Repository: markh794/mhvtl Branch: master Commit: e942484250dd Files: 155 Total size: 1.6 MB Directory structure: gitextract_1xsiiwpt/ ├── .clang-format ├── .github/ │ └── workflows/ │ └── codeql.yml ├── .gitignore ├── COPYING ├── ChangeLog ├── INSTALL ├── Makefile ├── README ├── ccan/ │ └── ccan/ │ └── crc32c/ │ ├── _info │ ├── benchmark/ │ │ ├── Makefile │ │ └── bench.c │ ├── crc32c.c │ ├── crc32c.h │ └── test/ │ ├── api-crc32c.c │ └── run-crc32c.c ├── config.mk ├── doc/ │ ├── 4_library_example/ │ │ ├── device.conf │ │ ├── library_contents.10 │ │ ├── library_contents.20 │ │ ├── library_contents.30 │ │ ├── library_contents.40 │ │ └── mhvtl.conf │ └── index.html ├── etc/ │ ├── .gitignore │ ├── Makefile │ ├── generate_device_conf.in │ ├── generate_library_contents.in │ ├── library_contents.sample │ ├── mhvtl-load-modules.service.in │ ├── mhvtl.conf.in │ ├── mhvtl.target │ ├── vtllibrary@.service.in │ └── vtltape@.service.in ├── include/ │ ├── common/ │ │ ├── logging.h │ │ ├── mhvtl_scsi.h │ │ ├── vtl_common.h │ │ ├── vtl_u.h │ │ └── vtltape_pem.h │ ├── mhvtl_log.h │ ├── mode.h │ ├── smc.h │ ├── spc.h │ ├── ssc.h │ ├── utils/ │ │ ├── be_byteshift.h │ │ ├── lzoconf.h │ │ ├── lzodefs.h │ │ ├── mhvtl_list.h │ │ ├── mhvtl_update.h │ │ ├── minilzo.h │ │ ├── q.h │ │ ├── security_protocol.h │ │ └── subprocess.h │ ├── vtlcart.h │ └── vtllib.h ├── kernel/ │ ├── .gitignore │ ├── Makefile │ ├── backport.h │ ├── config.sh │ ├── fetch.c │ ├── fetch24.c │ ├── fetch26.c │ ├── fetch27.c │ ├── fetch50.c │ └── mhvtl.c ├── man/ │ ├── Makefile │ ├── device.conf.5.in │ ├── dump_tape.1.in │ ├── edit_tape.1.in │ ├── generate_device_conf.1.in │ ├── generate_library_contents.1.in │ ├── library_contents.5.in │ ├── make_vtl_media.1.in │ ├── mhvtl.conf.5.in │ ├── mhvtl_kernel_mod_build.1.in │ ├── mktape.1.in │ ├── preload_tape.1.in │ ├── tapeexerciser.1.in │ ├── update_device.conf.1.in │ ├── vtlcmd.1.in │ ├── vtllibrary.1.in │ └── vtltape.1.in ├── mhvtl-utils.spec ├── scripts/ │ ├── 70-persistent-generic.rules │ ├── 70-persistent-tape.rules │ ├── Makefile │ ├── NetBackup/ │ │ ├── drive_stats.pl │ │ └── vlt_endeject_notify.pl │ ├── build-persistent-tape-rules.sh │ ├── centos_configure.sh │ ├── checkarch.sh │ ├── checkpatch.pl │ ├── lio_target_passthru.sh │ ├── mhvtl-1.4.ebuild │ ├── rescan-scsi-bus.sh │ ├── start-mhvtl-scst.sh │ ├── stgt-target-setup.conf │ ├── test_lbp.sh │ └── update_device.conf.in ├── tcopy/ │ ├── Makefile │ ├── pathnames.h │ ├── tcopy.1 │ └── tcopy.c ├── todo ├── usr/ │ ├── .gitignore │ ├── Makefile │ ├── cmd/ │ │ ├── dump_messageQ.c │ │ ├── edit_tape.c │ │ ├── make_scsi_dev │ │ ├── make_vtl_media.in │ │ ├── mhvtl-device-conf-generator.c │ │ ├── mhvtl_kernel_mod_build.in │ │ ├── mktape.c │ │ ├── tape_util.c │ │ ├── tapeexerciser.c │ │ ├── vtlcmd.c │ │ ├── vtllibrary.c │ │ └── vtltape.c │ ├── mhvtl_io.c │ ├── mhvtl_log.c │ ├── mode.c │ ├── pm/ │ │ ├── ait_pm.c │ │ ├── default_smc_pm.c │ │ ├── default_ssc_pm.c │ │ ├── hp_smc_pm.c │ │ ├── hp_ultrium_pm.c │ │ ├── ibm_03592_pm.c │ │ ├── ibm_smc_pm.c │ │ ├── overland_pm.c │ │ ├── quantum_dlt_pm.c │ │ ├── scalar_pm.c │ │ ├── spectra_pm.c │ │ ├── stk9x40_pm.c │ │ ├── stklxx_pm.c │ │ ├── t10000_pm.c │ │ └── ult3580_pm.c │ ├── smc.c │ ├── spc.c │ ├── ssc.c │ ├── utils/ │ │ ├── README.LZO │ │ ├── crc32c.c │ │ ├── mhvtl_update.c │ │ ├── minilzo.c │ │ ├── q.c │ │ ├── reed-solomon.c │ │ ├── subprocess.c │ │ └── validate_crc.c │ ├── vtl_cart_type.c │ ├── vtlcart.c │ ├── vtlcart_v1.c │ ├── vtlcart_v1_mtr.c │ └── vtllib.c ├── vagrant/ │ ├── README.MD │ ├── Vagrantfile │ └── install.sh └── webgui/ └── index.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ BasedOnStyle: LLVM IndentWidth: 4 UseTab: Always TabWidth: 4 ColumnLimit: 0 # --- Includes --- IncludeBlocks: Preserve SortIncludes: false # --- Initializers --- AllowShortBlocksOnASingleLine: Always AllowShortCaseLabelsOnASingleLine: true AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: true AllowShortLoopsOnASingleLine: true # --- Alignement --- AlignConsecutiveAssignments: true AlignConsecutiveDeclarations: true AlignConsecutiveMacros: true BinPackArguments: true BinPackParameters: true ================================================ FILE: .github/workflows/codeql.yml ================================================ # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '43 6 * * 5' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'cpp' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 ================================================ FILE: .gitignore ================================================ *.ko *.o *~ /cscope.* /kernel/.*.ko.cmd /kernel/.*.o.cmd /kernel/.tmp_versions/ /kernel/mhvtl.mod.c /kernel/Module.symvers /kernel/modules.order /kernel/config.h /scripts/update_device.conf /man/*.1 /man/*.5 /tcopy/tcopy local.mk mhvtl_kernel.tgz /usr/mhvtl_kernel_mod_build # Vagrant specific files /vagrant/.vagrant /vagrant/*.log ================================================ FILE: COPYING ================================================ Note that the GPL below is copyrighted by the Free Software Foundation, but the instance of code that it refers to (the 'mhvtl' user-space and kernel) is copyrighted by me and others who actually wrote it. Also note that the only valid version of the GPL as far as the kernel is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. Mark Harvey ---------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ================================================ FILE: ChangeLog ================================================ * Tue Mar 10 17:30:00 AEST 2026 Mark Harvey - A hugh patch set to add LTFS support. Many thanks to QuentinLoriaux * Tue Mar 10 16:30:00 AEST 2026 Mark Harvey - mhvtl.ko: Various updates to keep track of recent mainline changes - add GET/SET TIMESTAMP OP code support - fix for Logical Block Protection * Mon Mar 10 12:11:00 AEST 2025 Mark Harvey - Remove reference to Nutanix as I no longer work there - Implement Logical Block Protection for LTO emulations - mhvtl.ko: Update to support recent kernel changes - more 32/64bit cast fixes (Raspberry-Pi) & x86_64 - Fix issue with helper script creating WORM media (Thanks Gaurav Malhotra) - Initial LTO-9 emulation (Thanks Gwen Dawes) - daemon exit strategy change - reduce possibility of shutdown during inflight I/O - Fix READ POSITION - Implement VHF log page - Reduce calls to fflush() unless in debug mode * Fri Mar 10 16:30:00 AEST 2023 Mark Harvey - review of all utilities and use '-m ' the standard option - Add support for 'INQUIRY_DATA_HAS_CHANGED' - Added 'Extended Inquiry Data VPD page for Ultrium - Add SELinux ACL so systemctl scripts will work if SELinux is enforcing - mhvtl.ko: Various improvements to suit recent kernel changes (thanks Lee Duncan and Tamas Revesz) - mhvtl.ko: Change initiator ID from 15 to -1 (resolves conflict if target also configured with ID 15) - mhvtl.ko: Remove printk in favour of pr_* calls - pre-package mhvtl.ko source into /usr/lib/firmware/mhvtl/ and add wrapper script 'mhvtl_kernel_mod_build' to extract, compile and install - Various 64bit/32bit casts fixed. - Updated Vagrant to use AlmaLinux, RockyLinux, CentOS, OpenSuse, Ubuntu * Thu Mar 10 10:30:00 AEST 2022 Mark Harvey - Updated default drives in library 10 from LTO4/LTO5 to LTO6 & LTO8 - Added initial support for Data Integrity Validation (via Logical Block Protection) - IBM LTO6,LTO7 & LTO8 with this release. - mhvtl.ko: Various updates to match the latest kernels - Additional checks for Vagrant build on CentOS, Ubuntu and OpenSUSE - Improve tape load/unload handshake to make it more robust - Improve handling of missing slot numbers in library_contents.xx - Makefile - Move variables to a common location (config.mk) - SEND DIAGNOSTICS: Implement basic sanity check for SSC devices - VERIFY(6) op code support - Update install instructions with sg3_utils vs sg3-utils package names - LTO-6 media load to match LTO standards (LTO8 cannot read LTO6 media) - New utility 'preload_tape' bypassing need to write via /dev/st* - Add missing 'ENCRYPTION capable' bit for LTO6, 7 & 8 media - systemd device config: Best effort, log errors via klog - Obligatory spelling fixes - Bump version to 1.7 : Beginning of LBP, VERIFY(6) and SEND DIAGNOSTICS support * Thu Oct 07 10:40:00 AEST 2021 Mark Harvey - rename list.h to avoid generic name conflict - rename scsi.h to avoid generic name conflict - rename log.h to avoid generic name conflict - Exclude __builtin_cpu_supports() on non x86_64 CPU types - Fix spelling of retrieving - mhvtl.ko: Define SG_SEGMENT_SZ only if not defined - Increase default kmem_cache bounce buffer size to 64k - mhvtl.ko: kernel module oops on PPC - mhvtl.ko: Log errors if they occur - mhvtl.ko: Correct compiler warning about printf var sizes - mhvtl.ko: Simplify file_inode() - mhvtl.ko: Use _safe version of list_for_each_entry - mhvtl.ko: Limit number of outstanding queued commands - mhvtl.ko: Log minor number with other ioctl info - mhvtl.ko: clean up noisy logging at level 2 - mhvtl.ko: Initialise outstanding op struct before adding to list - mktape: Set default version string based from MHVTL_VERSION - Update LTO-8 media denisty codes - Security Protocol OUT: Fix null pointer check - Fix routine to extract barcode from string - Fix typos in mktape man page - dump_tape: Fix segfault due to local variable conflict - Use bounce buffer if tape block size is larger than request buffer - Remove unused scsi_host_template field - mhvtl.ko: Compile on RedHat 8.x kernel - mhvtl.ko: reinstate HAVE_UNLOCKED_IOCTL - mhvtl.ko: Remove reference to DRIVER_SENSE - Update function names with 'mhvtl_' prefix - Update kernel /sys/ location to suit new pseudo name space - OOM: /proc//oom_adj is deprecated. Using oom_score_adj. - systemd: Update Makefile to include systemd install path - mhvtl.spec: Update to build a package on CentOS8 * Tue Mar 10 09:00:00 AEST 2020 Mark Harvey - Fix kernel module oops/panic due to race between placing OP ready for user space before all variable were initialised - Add ccan version of crc32c routine which includes CPU optimisations for sse4.2 capabile CPUs * Sun Oct 06 12:20:00 AEST 2019 Mark Harvey - Fix kernel copy_to_user()/copy_from_user() to use white-listed buffer - Fix kernel compile for 5.0+ kernels * Wed Apr 10 10:30:30 AEST 2019 Mark Marvey - Fix long standing bug on media unload - Added CRC32 checks for each block written/read - allow kernel module to compile on 5.0 kernels - Add example on how to configure LIO pscsi (passthru) driver with mhVTL - Fix parsing args in dump_tape * Thu Mar 28 14:30:30 AEST 2019 Mark Marvey - Create virtual media on rpm install - Fixed a couple typos in spec file (one from several years ago) - Don’t overwrite /etc/mhvtl/* but install as ‘rpmnew’ - Replace SUSE specific rpm macros with hand crafted ’systemctl’ commands. - systems-generators dir needed munging for CentOS7 - Include ‘ChangeLog’ as part of the tar ball - the time/date build on man pages is dependant * Thu Nov 29 15:42:24 PST 2018 Lee Duncan - Many changes to support systemd: - Removed /etc/init.d/mhvtl script - Config files are now generated at install time, not the first time the service is run - Created service files and templates, and a generator - Updated INSTALL file with sytemd info - removed build_library_config and mhvtl init script - Updated man pages and doc/index.html - Updated make files - created generate_device_conf and generate_library_contents, called at 'make install' time, to take the place of what the init file used to do - updated SPEC file for systemd - updated example scripts - fixed asprintf calls to check return values (to make compiler happy) - added some arguments and better arg parsing to vtltape and vtllibray - moved some .gitignore entries around, to be more local - Bumped version to 1.5-5 - Fixed config file error in library_contents.* where VERSION was missing * Thu Sep 7 2014 Mark Harvey - Bumped version to 1.5-2 - Fix vtllibrary segfault using 'default' emulation - Correct slot mappings for L180/L700 emulation - Correct slot mappings for SL500 emulation - Increase max serial number length to 15 chars - More accurate emulation of IBM TS3100/3200 inquiry page bits - Fix bug with 'edit_tape' not understanding the 'null' media type * Thu May 1 2014 Mark Harvey - Bumped version to 1.5-1 - Fix bug with vpd pages 0x80 & 0x83 in library personality modules. * Sun Apr 13 2014 Mark Harvey - Bumped version to 1.5-0 - Introduce library personality modules - limited emulations - Resolve bug with more than 1260 slots - Rename mhvtl bus type so scsi_debug & mhvtl can load together. * Sun Oct 20 2013 Mark Harvey - Bumped version to 1.4-10 - Various cleanups due to static code check 'smatch' - Fix TapeAlert bit offsets - Return bit/byte offset for sense INVALID FIELD IN CDB/PARAMS - Fix returned data size when 0 data is requested (kernel module fix) - SSC - Fix op code 0Fh (read reverse) incorrectly set to 'reserve' - SMC - Ability to keep library media change persistant across restarts - SSC - Implement 'read media serial number' op code - Fix 'make_vtl_media' script to understand IBM 03592 media type * Thu Aug 29 2013 Mark Harvey - Bumped version to 1.4-9 - SSC -> MODE SELECT overhaul. - Sanitize delay values introduced in 1.4-8 - daemons now open /dev/mhvtlXX nodes in exclusive mode - Code cleanup based on Valgrind - memory leaks and unused file handles - Correctly set SCSI revision field for T10000B - SMC -> Adjust behaviour if 'Scalar' library defined set 'BarC' field in inquiry page and pad "Full Firmware Revision" field with compile time/date * Sat Jun 29 2013 Mark Harvey - Bumped version to 1.4-8 - Add additional fields in MAM for multi-partition support capability - MODE SELECT fixes - Solaris 11.1 st mode select - Make virtual media more resilient against disk full conditions - Add additional error checking between kernel and userspace ioctl - Add ability to introduce delays in load/unload/position/thread/rewind op codes - Add 16byte CDB locate command support - Minor kernel module tweeks - increase timeout & error checking on ioctl() * Fri Mar 22 2013 Mark Harvey - Bumped version to 1.4-7 - Add DLT7000/DLT8000 emulation - Record 'last load' data in MAM - New utility 'edit_tape' which will allow modificiation of media 'meta data' - Prevent and log possibility of READ ELEMENT STATUS overflowing data buffer - Set 'POWERON-RESET' if library re-configured via HUP signal - Update 'minor' number from 'unsigned char' to 'unsigned int' which removes max number of device nodes at 256 - Rework READ ELEMENT STATUS - Simplify code and correct bug where incorrect byte count returned. - Add 'support' for specifying 9x40 and DLT media via barcode suffix - Update kernel module to prevent overflow if DEF_MAX_MINOR_NO reached. - Update kernel module to allow up to 1024 minor numbers - Update kernel module vers to 0.18.12, vers date 20130310-0 - off-by-one fix of compression mode page - Fix 'READ POSITION' ssc op code - Helper scripts for NetBackup added - drive_stats.pl (parse 'scsi_command' output and email on tape unload) - vlt_endeject_notify.pl (NetBackup Vault helper) * Sat Jan 31 2013 Mark Harvey - Bumped version to 1.4-6 - Return correct TAPE CAPACITY log page - Fix overflow TAPE CAPACITY log page - Fix potential vtltape segfault when 'block position back' - New utility 'edit_tape' to update virtual media metadata * Sat Jan 12 2013 Mark Harvey - Bumped version to 1.4-5 - Added support for LTO-6 - Fix EOM handling - Fix data returned in TAPE CAPACITY log page * Thu Sep 13 2012 Mark Harvey - Bumped version to 1.4-4 - Fix a build issue on older (SLES9) linux distributions - Silence a compiler warning about uninitialised var 'month' in quantum PM - Note: 1.4-2 & 1.4-3 never made it as released packages. * Wed Sep 12 2012 Mark Harvey - Bumped version to 1.4-3 - Add 'performance' counter to vtllibrary - Fix off-by-one error in LOG SELECT - Implement LOG reset via PCR bit in LOG SELECT * Sat Sep 01 2012 Mark Harvey - Bumped version to 1.4-2 - Add 'performance' counter to each SCSI op code logged * Wed Aug 08 2012 Mark Harvey - Bumped version to 1.4-1 - Bug fix: Fix dump_tape to understand new library subdirectory format Fix vtlcmd to understand new library subdirectory format * Wed Aug 01 2012 Mark Harvey - Bumped version to 1.4-0 - Allow 'default' personality module to load all media R/W - Add IBM half-hight INQUIRY string match - vtltape: Update log entry regarding media capacity depending on capabilities - vtltape: Make the backoff algrithm value configurable - Add SDLT600 & SDLT320 personality module - mhvtl.ko: Silence install 'depmod' error - Move standard inquiry info into memory structure - Default media belonging to a library in a subdir - Remove kmod-mhvtl.spec - Rename 'mhvtl' package as 'mhvtl-utils' - Use ELRepo spec file - Move 'make install' from RPM to Makefile * Fri Jun 15 2012 Mark Harvey - Bumped version to 1.3-0 - Move 'on disk' unique functions into its own shared lib (libvtlcart.so) - HP Ultrium VPD page data update to match HP documentation - IBM Ultrium VPD page 0xC0 & 0xC1 update to match IBM documentation - Fix SPIN return status - Add helper functions for moving media within library (in prep for library partition manager) - Add ability to set/clear APPEND ONLY status from user (vtlcmd) * Wed Apr 04 2012 Mark Harvey - Bumped version to 1.2-3 - Fix segfault in AIT4 when attempting to access Security Protocol IN/OUT - No need to fail in virtual media parent directory exists - Add more descriptive debug messages for log/mode page lookups * Thu Mar 22 2012 Mark Harvey - Bumped version to 1.2-2 - Changed defaults so fifo is not enabled. If fifo not used, it will block writing process (i.e. hang daemons) - Improved MODE SELECT / SENSE to support 'changeable field bitmaps - Started implementing ALLOW OVERWRITE support (incomplete) - Fixed INQUIRY string match for STK T10000A * Sat Jan 21 2012 Mark Harvey - Bumped version to 1.2-1 - Fix memory leak in lzo decompression routine. * Fri Jan 20 2012 Mark Harvey - Bumped version to 1.2-0 - Ability to specify zlib or lzo compression. via 'vtlcmd' at runtime. 'vtlcmd <> compression lzo' 'vtlcmd <> compression zlib' /etc/mhvtl/device.conf entry ' Compression type: lzo' ' Compression type: zlib' * Sat Dec 24 2011 Mark Harvey - Bumped version to 1.1-1 - Clean up IPC resources when finished with them - Simplify fifo reference counting - Start including Scientific Linux patches (Still need to include kmod RPM) - Fix 'make install' on Ubuntu - Various log message cleanups * Sun Oct 09 2011 Mark Harvey - Bumped version to 1.1 - Added real time state notification. '-f ' (Set in build_library_config) Defaults to all daemons using the same 'named pipe' Each entry is prefixed with ':' and text to the change of state. - MODE SENSE for LTO devices update the 'media type' field. - LOG SENSE page 0x0c (Sequential Access Device Page) updates with valid data * Sun Sep 11 2011 Mark Harvey - Bumped version to 1.0.1 - Added mode page 25h (vendor specific) for IBM LTO3/4/5 This allows the Windows IBM Tape Driver to load correctly (connected via iSCSI) - Attempt to correct file permission/ownership when media is created manually - A HUP signal to the vtllibrary daemon will cause it to re-read its config from the /etc/mhvtl/library_contents.XX file. (So you can change the slot config without having to re-start the daemon). * Sun Sep 04 2011 Mark Harvey - Bumped version to 1.00.00 - Re-worked MODE SENSE/SELECT data structures into a linked list. - Re-worked LOG SENSE/SELECT data structures into a linked list. - Added support for sub-page MODE information. - Added STK 9x40 drive emulations. - Removed dead code from kernel driver. - Add working REPORT DENSITY support to the SSC. This change required the personality modules to define the list of supported densities. The media density is defined at 'mktape' time. Hence, the optional 'media load' rules in device.conf are now redundent and not used. BUT: The option to load any media into any drive is also gone. * Sat Jun 25 2011 Mark Harvey - Bumped version to 0.18.17 - Improvements - Kernel module compile warning since 2.6.33 - Bug fixes - Test MAP port open before moving media - Fix buffer overflow in vtllibrary (product_id) - Return correct sense from space op code * Sun May 22 2011 Mark Harvey - Bumped version to 0.18.16 - Improvements - Cleaning media behaves more like real (IBM LTO4) drive. - Implement OPEN/CLOSE IMPORT/EXPORT element OP code (Thanks Sebastian) - Kernel module support for 2.6.39 (Thanks Sebastian) - Bug fixes - SPACE op code - Space to end-of-data fixed (Thanks Sebastian) This fixes an issue triggered using Oracle Backup - REQUEST SENSE - Return correct data. - SPOUT - Return check_condition on some error paths - Cleaning Media - Return 'not ready' instead of 'good' when loaded - Don't write FILEMARKS to WORM or Cleaning media * Wed May 04 2011 Mark Harvey - Bumped version to 0.18.15 - Improvements - Implemented 'Personality Module' for each drive type (many cleanups due to 'PM' change fallout thanks to Sebastian) - Increase max barcode length to 16 chars - Bug fixes - Inquiry no longer incorrectly reports support for TrmIOP and CmdQue - Fixed block read/writes corruption if 'multiple blocks' specified - Fix Device Capabilities mode page - Don't advertise EXCHANGE MEDIUM support * Thu Mar 17 2011 Mark Harvey - Bumped version to 0.18.14 - Bug fixes: - kernel module build fixes for Linux kernel 2.6.37 and greater - Improvements - Add STK T10000C media/drive support - Always log 'fatal' errors - Cleanup to mhvtl.spec - Catch signals to prevent daemon terminating early. * Thu Jan 11 2011 Mark Harvey - Bumped version to 0.18.13 - Bug fixes: - SMC read element status. Return correct length. - SMC read element status. Handle request for 'any' slot type. - SSC log page. Byte-swap Bytes Read/Written. - vtltape: Return ILLEGAL REQUEST for unsupported OP codes. - Density code updates. - Increased sense buffer from 38 bytes to 96 bytes. * Wed Nov 17 2010 Mark Harvey - Bumped version to 0.18.12 - Bug fixes: - Silence 'which setuidgid' test - Fix compile on 2.6.34 kernel - ERASE only from BOT - Test SPACE '0' blocks/filemarks before moving - Fix REQUEST SENSE returned size - Implement SILI bit test - Check if ERASE WORM media is allowed - Only support SPIN/SPOUT for LTO-4/5, T10K and 3592E05/6 drive types - Fix REQUEST SENSE page length. Previously only returning 8 bytes. - Improvements - Re-organised code in vtllibrary to use a big jump table - Fix LTO5 media density reporting - Match HP ULTRIUM 4-SCSI - Ability to create DDS specific media (still need to find correct density codes for DDS) * Thu Sep 23 2010 Mark Harvey - Bumped version to 0.18.11 - Bug fix: Off-by-one if a re-position & then overwrite a filemark. Caused TSM & HP Dataprotector all sorts of problems. - Export kernel module 'major' number via /sys/bus/pseudo/drivers/mhvtl/major - Remove external script to create device nodes. Each daemon creates its own device node at startup. Uses 'kernel module' major no. sys interface. - Fix potential NULL pointer usage in REQUEST SENSE - VPD page 0x83 NAA field fix - VPD page length fix (only 0x50 bytes in size not 0x52) - Hopefully removed last reference to /proc - Use setuidgid if availble instead of 'su $USER -c ' - Add kernel module RPM spec file for RedHat - Add Gentoo build * Wed Sep 01 2010 Mark Harvey - Bumped version to 0.18.10 - Correct incorrect usage of & vs && - Fix TapeAlert. Error where the flags were not being set due to an earlier change. - Add extra verbage around positioning information. Trying to track down a problem with HP Dataprotector when it spaces back a block. - Make tape media & drive type loading logs more readable. - New utility 'tapeexerciser' which I've been using to track down the Dataprotector issue. A simple & basic 'st ioctl()' test utility. - Fix 'is vtl running' logic in rc script. Was broken if USER == root * Wed Jul 09 2010 Mark Harvey - Bumped version to 0.18.9 - Fix WORM media support - Bug report from Albert Pauw - Complete LTO5 media support * Wed Jun 23 2010 Mark Harvey - Bumped version to 0.18.8 - Several Security Protocol IN updates - thanks Albert Pauw * Return certificate data * Correct length for 'KEY FORMATS' * Correct length for SPIN SUPPORTED PAGES - Fix kernel compile on RedHat AS4 - Media/drive matching now 'dynamic' and defined in device.conf - Added man page for device.conf - Fix media corruption when media is 'formatted' - Add LTO5 & SDLT-S4 drive/media types - Handle INQUIRY correctly after media change (return SAM_STAT_GOOD) - Updated rc script so all devices created on Target & LUN. i.e. Don't use channel. Some application software has trouble if only the channel is unique. * Sat May 08 2010 Mark Harvey - Bumped version to 0.18.7 - Allow media sizes larger than 2G on 32bit platforms - Implement STK vendor unique op code 'LOAD DISPLAY' -> logs via syslog. - Fix core dump on invalid data in NAA strings. - Support VENDOR ID for SMC device with embedded spaces * Thu May 02 2010 Mark Harvey - Bumped version to 0.18.6 - Support VENDOR ID with embedded spaces - Fix 'vtlcmd list map' - Fix import of media via MAP (off-by-one) - Return TapeCapacity LOG SENSE in bytes/KB/MB depending on drive type - Honour MAP status (return error if MAP is open and robot attempts to move media in/out of MAP) - General cleanup of kernel module (thanks to Herbert Stadler) - Relax dependencies on /proc in faviour of /sys * Thu Apr 01 2010 Mark Harvey - Bumped version to 0.18.5 - Silence warning regarding local_irq_save() - Thanks to Norm Lunda - Re-work param parsing used by vtlcmd - Thanks to Herbert Stadler - Fix homepage URL * Fri Mar 05 2010 Mark Harvey - Bumped version to 0.18.4 - Fix syntax in rc script. Reported by Nabil That's what I get for rushing a fix out (ELEMENT STATUS) without testing everything.. * Thu Mar 04 2010 Mark Harvey - Bumped version to 0.18.3 - Test media mounted before REWIND (TSM seems to ignore previous TUR) - Make creation of media home dir more robust - Add queue depth callback - Defaults to 32. - Conversion script of old config files to new format - Fix problem with size of ELEMENT STATUS DATA length. Thanks to Norm Lunde. * Thu Jan 28 2010 Mark Harvey - Bumped version to 0.18.2 - Add support for multiple libraries on single host - pack MAM struct so it's 1024 bytes on both 32 & 64bit hosts - Updated URL after google moved it * Wed Dec 16 2009 Mark Harvey - Bumped version to 0.16.11 - Add tests for media/drive type. i.e. Only allow LTO media in LTO drives. - New defination for 'Compression:' in device.conf (Thanks Kevan Rehm) ' Compression: factor [1-9] enabled [0|1]' Where factor : 1 Fastest compression -> 9 Best compression enabled : 0 => Compression disabled, 1 => Compression enabled * Tue Dec 15 2009 Mark Harvey - Bumped version to 0.18.1 - Fix data silent data corruption. - Add tests for media/drive type. i.e. Only allow LTO media in LTO drives. - Increased max number of LUNs from 7 to 32 - Changed ' Compression: factor X enabled Y' to same as 0.16 branch. ' Compression: factor [1-9] enabled [0|1]' Where factor : 1 Fastest compression -> 9 Best compression enabled : 0 => Compression disabled, 1 => Compression enabled * Tue Dec 01 2009 Mark Harvey - Bumped version to 0.18.0 - Kevan Rehm reworked media format. The 'old' format suffered from performance problems. The larger the defined tape capacity, the longer it took to seek/position due to the sequential walk of header structures. The new tape format consists of three files. .data - Data file. .indx - Arrary of one header structure per written tape block or filemark. .meta - MAM, followed by meta header structure, followed by variable length of filemark block numbers Each Physical Cartridge Label (PCL) is located under its own directory MHVTL_HOME_PATH// * Sun Nov 29 2009 Mark Harvey - Bumped version to 0.16.9 - Check string length in device.conf and abort if too long. - Compression now working. Set up via MODE SELECT op code. Set default compression in device.conf 'Compression: X' Where 0 is off, 9 is max. - Defaults with SPECTRA/PYTHON library - Fix kernel module compile on 2.6.31+ (Ubuntu 9.10) - Fix kernel silent data corruption - Set kernel ENABLE_CLUSTERING to enable > 512k blocks on x86_64 - SPIN/SPOUT: Return correct list of supported pages. * Tue Nov 10 2009 Mark Harvey - Bumped version to 0.16.8 - Reworked READ ELEMENT STATUS (Thanks to Kevan Rehm) - Fix EOD header size. - Makefile rework. Honour PREFIX & DESTDIR. User/group name defined. Home paths for data files & config files defined. Add compression support (disabled by default). * Thu Oct 9 2009 Mark Harvey - Bumped version to 0.16.7 Fix REPORT ELMEMENT STATUS for All elements. TSM was core dumping. Reported and fixed by Bernardo Clavijo. Re-worked Media Access Port. Now requires 'vtlcmd library open map' before media can be removed and 'vtlcmd library close map' for the Medium Transport Element to place media into the MAP. * Thu Oct 1 2009 Mark Harvey - Bumped version to 0.16.6 Fixed output of 'dump_tape' utility * Wed Sep 16 2009 Mark Harvey - Bumped version to 0.16.5 Fixed verbosity test in MHVTL_DBG macro (again) * Tue Sep 15 2009 Mark Harvey - Bumped version to 0.16.4 More logging cleanups Fixed verbosity test in MHVTL_DBG macro * Wed Sep 02 2009 Mark Harvey - Bumped version to 0.16.3 Reworked logging using a macros MHVTL_DBG. Increased max block size to kernel max (from 256k) * Sat Aug 15 2009 Mark Harvey - Bumped version to 0.16.2 Changed/updated load MAP() functionality. Previous implementation re-read config file and parsed the MAP slots. This is 'interactive' using the vtlcmd command. e.g. vtlcmd library load map BARCODE Corrected NAA field in VPD page 0x83. This was a hard coded string. Now reads entry from /etc/mhvtl/device.conf ' NAA: 11:22:33:44:ab:cd:ef:03' (8 octet value) Remove dependency on sg3_utils * Wed Aug 05 2009 Mark Harvey - Bumped version to 0.16.1 Fixed kernel module oops on lu removal - Many thanks to Jean-Francois Added loadMap() to vtllibrary. * Tue Jul 09 2009 Mark Harvey - Bumped version to 0.16.0 - Moved INQUIRY into userspace. Re-jigged all helper scripts. Still need to do dynamic config of : vpd pages & mode pages. - Added pseudo encryption support (SPIN/SPOUT op codes) - kernel module will oops if unloaded and re-loaded.. * Fri Jan 02 2009 Mark Harvey - Bumped version to 0.15.10 - Removed kfifo from data path for SCSI OP codes which originate at the target - Add simple SCSI Persistent Reserve - Major change to kernel module. Replaced queued_command_arr[] with linked list * Thu Nov 27 2008 Mark Harvey - Bumped version to 0.15.9 - Re-fixed WRITE ATTRIBUTE bug forcing a rewind of media. - Fixed build for PPC64 platform - Add special reserved barcode 'NOBAR' to indicate a slot is full but contains no barcode. * Fri Nov 21 2008 Mark Harvey - Bumped version to 0.15.8 - Added initial SECURITY PROTOCOL code * Tue Nov 19 2008 Mark Harvey - Bumped version to 0.15.7 - Merge READ ATTRIBUTES fixes from Raymond Gilson * Sun Nov 16 2008 Mark Harvey - Bumped version to 0.15.6 - Fixed bug where WRITE ATTRIBUTE was causing media to rewind. - Increase default buffer size of SMC from 512k to 1024k - Ability to handle more (twice the) slots * Fri Nov 14 2008 Mark Harvey - Bumped version to 0.15.5 - sg structure changed between 2.6.23 and 2.6.24 causing the kernel module to no longer build on latest kernel. * Fri Apr 04 2008 Mark Harvey - Bumped version to 0.15.4 - Kernel module change. Default type is SDLT600 instead of IBM TD3 as there is confusion on the windows side of things regarding IBM Drivers vs IBM for Tivoli vs Symantec Tape Drivers. Maybe the QUANTUM SDLT600 will behave better ?? Only time will tell... * Fri Mar 28 2008 Mark Harvey - Bumped version to 0.15.3 - Return 'block descriptor data' on a MODE SENSE 'page 0' instead of an error. * Mon Mar 10 2008 Mark Harvey - Bumped version to 0.15.2 Re-org of the source code. - Placed user-space code in directory ./usr - Moved kernel drivers from ./kernel-driver/linux-2.6 to ./kernel Yet another 'tunable' option. Set the firmware release to "string" by # echo "5400" > /sys/bus/pseudo/drivers/vtl/firmware * Thu Mar 06 2008 Mark Harvey - Bumped version to 0.15.1 Ability to define default media size in /etc/vtl/vtl.conf * Wed Mar 05 2008 Mark Harvey - Name change as 'vtl' is was deemed too generic. Renamed to 'mhvtl' as this is being used by Symantec's Roseville office and is as good a name as any. - With the new name comes a new version 0.15.0 * Tue Feb 19 2008 Mark Harvey - Bumped version to 0.14.1 Cleaned up compile time warnings on x86_64 platform. Added sg_utils and zlib as RPM 'requires' packages. * Thu Feb 14 2008 Mark Harvey - Bumped version to 0.14.0 - With the ability to define device serial numbers, I thought it was time to increase vers from 0.12 to 0.14 - Cleaned up helper scripts. * Fri Feb 08 2008 Mark Harvey - Bumped vers to 0.12.37 - Added ability to set serial number via new utility 'vtl_set_sn -q X' The serial number is read from /etc/vtl/library_config for each 'Drive X:' e.g. Drive 2: SN20034 Drive 3: SN20035 If there is no serial number defined in library_config file, and the serial prefix has been set, then this will be used. Otherwise the old & trusted method of calculating based on HBA#, ID# & LUN. * Wed Feb 06 2008 Mark Harvey - Bumped vers to 0.12.36 - Added another config file /etc/vtl/vtl.conf - Added ability to set a serial number prefix for devices. - Added ability to set the buffer size used by SSC devices. - Added ability to set/clear logging levels within vtl.conf * Sat Feb 02 2008 Mark Harvey - Bumped vers to 0.12-35 - Fix post uninstall check for group & passwd entries before attempting to run groupdel/userdel * Sat Jan 08 2008 Mark Harvey - Bumped vers to 0.12-34 Changes to kernel module & rc scripts. - Default kernel module load reporting the library only. - The rc scripts now update the number of required tape devices depending on the contents of /etc/vtl/library_contents - Using the max_luns or num_tgts the library can consist of different drives or all the same drive type. - Deleted x86_64 patch as it is no longer needed. * Fri Jan 04 2008 Mark Harvey - Reserved vers 0.12-33 A special build with only 5 IBM drives. * Wed Dec 19 2007 Mark Harvey - Bumped vers to 0.12-32 - Changed user 'vtl' home directory to /opt/vtl Otherwise there can be problems starting the vtl s/w if /home is an automount and can't mount /home. - Changed kernel module Makefile To compile on Debian or Ubuntu systems "make ubuntu=1" To compile on SuSE or RedHat systems "make" * Tue Oct 16 2007 Mark Harvey - Bumped vers to 0.12-31 - No code changes. As sysdup has crashed and management have decided not to replaced the two failed drives in the RAID 5 system, I've changed the home of this project to my google homepage. * Tue Oct 16 2007 Mark Harvey - Bumped vers to 0.12-30 - vtl kernel module: - bumped to 0.12.14 20071015-0 - Another bug fix in the kernel module. This time copying data from user-space in an unsafe manner. Last reason for kernel oops ?? - Make library module startup more robust by clearing out message Q at start time. - Set vtl startup to 'on' at package install time. * Wed Oct 10 2007 Mark Harvey - Bumped vers to 0.12-28 - vtl kernel module: - bumped to 0.12.14 20071010-0 - Many updates to error handling condition. * Wed Sep 26 2007 Mark Harvey - Bumped vers to 0.12-26 - vtl kernel module: - bumped to 0.12.14 20070926-0 Moved memory alloc from devInfoReg() to vtl_slave_alloc() - I now don't get those horrible "Debug: sleeping function called from invalid context" * Tue Sep 25 2007 Mark Harvey - Bumped vers to 0.12-25 - Resolved an issue where virtual media was being corrupted when performing erase operation. * Sat Sep 22 2007 Mark Harvey - Bumped vers to 0.12-24 - On corrupt media load, return NOT READY/MEDIUM FORMAT CORRUPT * Fri Sep 21 2007 Mark Harvey - Bumped vers to 0.12-23 - vtl kernel module bug fix - resolved a race condition with my usage of copy_to_user()/copy_from_user() and c_ioctl() routines. Thanks to Ray Schafer for finding and being able to reproduce race. * Fri Aug 24 2007 Mark Harvey - Bumped vers to 0.12-22 - Set correct directory ownership and permissions at post install time for /opt/vtl * Wed Aug 01 2007 Mark Harvey - Bumped vers to 0.12-21 - Corrected warnings identified by sparse * Sat Apr 07 2007 Mark Harvey - Bumped vers to 0.12-20 - Calls to tune kernel behaviour of out-of-memory always return 'success'. Found out the hard way, earlier kernel versions do not support this feature. - Added check for return status from build_library_config in rc script * Sat Mar 31 2007 Mark Harvey - Bumped vers to 0.12-19 - Added conditional x86_64/amd64 to vtl.spec so it builds correctly on x86_64 platforms. * Wed Mar 28 2007 Mark Harvey - Bumped vers to 0.12-18 - Improved (slightly) checking of MAM header on media open. At least made it 32/64 bit friendly. * Thu Mar 24 2007 Mark Harvey - Bumped vers to 0.12-17 - Added 'Out Of Memory killer tuning to both vtltape and vtllibrary. - Added 'tags' as a target in Makefile, i.e. 'make tags' TAGS will be removed with a 'make distclean' * Thu Mar 13 2007 Mark Harvey - Bumped vers to 0.12-16 - Added -D_LARGEFILE64 switch to Makefile to allow a more portable way of opening files > 2G. There should be no need for the x86_64 patch any more. Needs testing. * Thu Feb 22 2007 Mark Harvey - Bumped vers to 0.12-14 - Added 'ERASE(6)' command as a result of bug report from Laurent Dongradi. NetBackup "bplabel -erase -l " command failing. * Thu Sep 07 2006 Mark Harvey - Bumped vers to 0.12-13 - Just updated some time/date in kernel module. Nothing major. * Tue Sep 05 2006 Mark Harvey - Bumped vers to 0.12-12 Shared library (libvxscsi.so) conflict with Symantec VRTSvxvm-common-4.1.20 so I've renamed mine to libvtlscsi.so Should build correctly on an x86_64 RPM system now. * Thu Aug 31 2006 Mark Harvey - Bumped vers to 0.12-10 (skipped numbering between -5 & -10 Changed interface between user-space and kernel, hence need kernel module version 0.12.10 The change is to support 'auto sense', where the sense buffer is sent straight after the data (if sense data is valid). Need to fix: Way of specifying size of sense buffer. Currently defined in kernel src (vtl.c) and user-space header vx.h. It would be nice to have this defined in one place only. - Updated rc script to check kernel version before starting user-space daemons. * Wed Jul 05 2006 Mark Harvey - Bumped vers to 0.12-5 - Updated man pages to reflect name changes from vxtape/vxlibrary to vtltape/vtllibrary - Removed any vxtape/vxlibrary binaries from build process * Tue Jul 04 2006 Mark Harvey - Bumped vers to 0.12-4 - Fixed (Well tested) kernel makefile compile on SLES 9 and suse 9.3 i.e. Kernel versions 2.6.5-7.191-smp & 2.6.11.4-21.12-smp Hacked scsi.h include file and added #if KERNEL_VERSION conditional compile around kfifo.[ch] if below 2.6.10 - Attempt to improve install/startup on RedHat AS4 + SLES9 * Sun Jul 02 2006 Mark Harvey - Bumped vers to 0.12-0 - Forked this version from main tree. Main branch is 0.13.x which will continue to be work-in-progress for removal of kfifo routines. - 0.12-x is to be current 'stable' branch. * Fri May 19 2006 Mark Harvey - Bumped vers to 0.10-13 - Added TapeUsage & TapeCapacity log parameters. Even thou I'm specifying these pages are not supported, BackupExec still probes for them and barfs on the scsi error returned. * Thu May 18 2006 Mark Harvey - Bumped vers to 0.10-12 - Re-worked the READ ELEMENT STATUS command with the idea of adding the extra options which appear to be required by BackupExec. * Wed May 10 2006 Mark Harvey - Bumped vers to 0.10-10 - Fixed bug introduced in vtllibrary where I broke the inventory function. * Tue May 09 2006 Mark Harvey - Changed poll of 'getting SCSI command' to use ioctl instead of kfifo. Requires kernel module dated > 20060503-0 * Sun Apr 30 2006 Mark Harvey - Fixed typo in rc script where trying to set permissions on /dev/vx? instead of /dev/vtl? * Sat Apr 29 2006 Mark Harvey - Changed logic on WIRTE-FILEMARK SCSI command. Now only attempts to write filemarks if greater the 0 filemarks specified. - Added check for SuSE-release and use '{user|group}add --system' switch otherwise {user|group}add without the --system switch. - Bumped version to 0.10-6 * Thu Apr 27 2006 Mark Harvey - Seek to EndOfData bug where the tape daemon would loop over the last block and never return. * Sat Apr 5 2006 Mark Harvey - Changed the kernel module from 'vx_tape' to vtl (no real impact to user space - daemons) however changing /dev/ entries from /dev/vx? to /dev/vtl due to name - space clash with VERITAS StorageFoundation (VxVM/VxFS) * Sat Apr 1 2006 Mark Harvey - Bumped version to 0.9 as this has the start of a working Solaris port. * Thu Mar 24 2006 Mark Harvey - Added extra logging for mode_sense command if verbose > 2 * Thu Mar 10 2006 Mark Harvey - Start of Solaris kernel port. - Moved kernel driver into seperate subdir. - Added some 'ifdef Solaris' to source code. * Thu Feb 22 2006 Mark Harvey - Added check to make sure RPM_BUILD_ROOT is not / when attempting to remove. - Shutdown daemons before package removal * Thu Feb 16 2006 Mark Harvey - Added ALLOW/DENY MEDIUM REMOVAL SCSI cmd to vxlibrary daemon. - Added Extend/Retract checking to MOVE MEDIUM SCSI cmds. * Thu Feb 14 2006 Mark Harvey - Made RPM create system account of 'vtl' & group of 'vtl' before install. * Thu Feb 11 2006 Mark Harvey - bump vers from 0.7 to 0.8 - Rename /opt/vx_media to /opt/vtl - Rename /etc/vxlibrary to /etc/vtl - RPM install now creates vtl user & group - rc script now installs a default library config file in /etc/vtl/ * Thu Feb 2 2006 Mark Harvey - Teach me for not testing fully. Resolved bug where no data was written. * Thu Feb 2 2006 Mark Harvey - Improvements to handling of cleaning media. * Wed Jan 3 2006 Mark Harvey - Added a new man page vtl, which aims to document how this package hangs together. - Corrected a couple of hard-coded paths in rc scripts * Wed Jan 3 2006 Mark Harvey - Moved source man pages into separate man directory. - Updated version of user-mode utilities to that of the kernel module (v0.7) * Tue Jan 3 2006 Mark Harvey - Added man pages. ================================================ FILE: INSTALL ================================================ mh virtual tape & library system. Instructions assume reader is familiar with the kernel build process. There are two sections of code to build. - The kernel module - The user-space daemons. Kernel module mhvtl: ================== 1) Make sure the kernel-devel package to match your running kernel is installed. e.g. RedHat AS 4 & 5: sh> rpm -qa|grep kernel kernel-2.6.9-34.0.1.EL kernel-devel-2.6.9-34.0.1.EL kernel-2.6.9-5.EL kernel-devel-2.6.9-5.EL kernel-utils-2.4-13.1.80 SLES 9 & 10: sh> rpm -qa|grep kernel 2) Extract the mhvtl source code. sh> tar xvfz mhvtl-2012-06-15.tgz 3) Change directory into the kernel driver source. sh> cd mhvtl-1.3/kernel/ sh> make sh> sudo make install User space daemons: =================== Pre-req for a running mhvtl - sg3_utils (http://sg.danny.cz/sg/sg3_utils.html) Pre-req to build/compile userspace: - zlib-devel - lzo-devel * To build an RPM =============== sh> sudo cp mhvtl-YYYY-MM-DD.tar.gz /usr/src/packages/SOURCE/ sh> cd /usr/src/packages/SOURCE sh> sudo rpmbuild -tb mhvtl-YYYY-MM-DD.tar.gz ... sh> sudo rpm -Uvh /usr/src/packages/RPMS//mhvtl-1.3-z..rpm ... - Note: For RedHat, replace 'packages' with 'redhat' * To build from tar archive (Debian / Ubuntu): ============================================ apt-get install lsscsi apt-get install sg3-utils Note: we used to create a user and group called "vtl" to try to limit the possible system hazards of having root access. But we now feel that normal Linux permissions and security controls are sufficient. Also note: The package sg3-utils has a different name based on the distribution used. Most distributions (Red Hat/Suse/Centos) use an underscore (sg3_utils). Debian based distributions (Debian/Ubuntu/Mint use a hyphen (sg3-utils). Now build user space daemons: From the parent directory where you extracted the source. sh> cd mhvtl-1.3 Build the binaries sh> make Install the systemd scripts and binaries and tell systemd: sh> sudo make install sh> sudo systemctl daemon-reload Enable and start the mhvtl target service: sh> sudo systemctl enable mhvtl.target sh> sudo systemctl start mhvtl.target And make sure it worked: sh> sudo systemctl status mhvtl.target You can test that your vtl* daemons are running with: sh> ps -ax | fgrep vtl Test: Note: Make sure the 'mtx' & 'lsscsi' utilities are installed The virtual devices are attached to HBA #5 in this example. e.g. # lsscsi -g [0:0:0:0] disk ATA WDC WD1200BEVS-0 02.0 /dev/sda /dev/sg0 [3:0:0:0] cd/dvd Optiarc DVD RW AD-7910A 1.D1 /dev/sr0 /dev/sg1 [5:0:0:0] mediumx SPECTRA PYTHON 5500 /dev/sch0 /dev/sg6 [5:0:1:0] tape QUANTUM SDLT600 5500 /dev/st0 /dev/sg2 [5:0:2:0] tape QUANTUM SDLT600 5500 /dev/st1 /dev/sg3 [5:0:3:0] tape IBM ULT3580-TD4 5500 /dev/st2 /dev/sg4 [5:0:4:0] tape IBM ULT3580-TD4 5500 /dev/st3 /dev/sg5 # mtx -f /dev/sg6 status Storage Changer /dev/sg6:4 Drives, 37 Slots ( 4 Import/Export ) Data Transfer Element 0:Empty Data Transfer Element 1:Empty Data Transfer Element 2:Empty Data Transfer Element 3:Empty Storage Element 1:Full :VolumeTag=SDLT01S3 Storage Element 2:Full :VolumeTag=SDLT02S3 Storage Element 3:Full :VolumeTag=SDLT03S3 Storage Element 4:Full :VolumeTag=SDLT04S3 Storage Element 5:Full :VolumeTag=SDLT05S3 Storage Element 6:Full :VolumeTag=SDLT06S3 Storage Element 7:Full :VolumeTag=SDLT07S3 Storage Element 8:Full :VolumeTag=SDLT08S3 Storage Element 9:Full :VolumeTag=SDLT09S3 Storage Element 10:Empty Storage Element 11:Full :VolumeTag=ULT001L1 Storage Element 12:Full :VolumeTag=ULT002L2 Storage Element 13:Full :VolumeTag=ULT003L3 Storage Element 14:Full :VolumeTag=ULT004L4 Storage Element 15:Full :VolumeTag=ULT005L1 Storage Element 16:Full :VolumeTag=ULT006L2 Storage Element 17:Full :VolumeTag=ULT007L3 Storage Element 18:Full :VolumeTag=ULT008L4 Storage Element 19:Full :VolumeTag=ULT009L1 Storage Element 20:Empty Storage Element 21:Full :VolumeTag=8MM001X4 Storage Element 22:Full :VolumeTag=8MM002X4 Storage Element 23:Full :VolumeTag=8MM003X4 Storage Element 24:Full :VolumeTag=8MM004X4 Storage Element 25:Empty Storage Element 26:Empty Storage Element 27:Empty Storage Element 28:Empty Storage Element 29:Empty Storage Element 30:Empty Storage Element 31:Full :VolumeTag=CLN001L1 Storage Element 32:Full :VolumeTag=CLN002L1 Storage Element 33:Full :VolumeTag=CLN003L1 Storage Element 34 IMPORT/EXPORT:Empty Storage Element 35 IMPORT/EXPORT:Empty Storage Element 36 IMPORT/EXPORT:Empty Storage Element 37 IMPORT/EXPORT:Empty Enjoy. Please feel free in letting me know if this works for you. Bug fixes and suggestions always welcome. markh794@gmail.com ================================================ FILE: Makefile ================================================ # # This makefile needs to be invoked as follows: # #make # # Here, options include: # # all to build all utilities # clean to clean up all intermediate files # kernel to build kernel module # include config.mk PARENTDIR = mhvtl-$(VER) CHECK_CC = cgcc CHECK_CC_FLAGS = '$(CHECK_CC) -Wbitwise -Wno-return-void -no-compile $(ARCH)' TAR_FILE := mhvtl-$(shell date +%F)-$(VERSION).$(EXTRAVERSION).tgz MAKE_VTL_MEDIA = usr/bin/make_vtl_media export PREFIX DESTDIR TOPDIR CFLAGS=-Wall -g -O2 -D_LARGEFILE64_SOURCE $(RPM_OPT_FLAGS) CLFLAGS=-shared all: usr etc scripts scripts: patch $(MAKE) -C scripts etc: patch $(MAKE) -C etc usr: patch $(MAKE) -C usr kernel: patch $(MAKE) -C kernel .PHONY:check check: ARCH=$(shell sh scripts/checkarch.sh) check: CC=$(CHECK_CC_FLAGS) $(MAKE) all patch: install: all [ -d $(DESTDIR)$(MHVTL_HOME_PATH) ] || mkdir -p $(DESTDIR)$(MHVTL_HOME_PATH) $(MAKE) -C usr install $(MAKE) -C scripts install $(MAKE) -i -C etc install $(MAKE) -C man install $(MAKE) -C kernel install ifeq ($(ROOTUID),YES) systemctl daemon-reload endif tape: # now ensure VTL media is setup env LD_LIBRARY_PATH=$(DESTDIR)$(LIBDIR) \ $(MAKE_VTL_MEDIA) \ --config-dir=$(DESTDIR)$(MHVTL_CONFIG_PATH) \ --home-dir=$(DESTDIR)$(MHVTL_HOME_PATH) \ --mktape-path=usr/bin .PHONY:uninstall uninstall: $(MAKE) -C usr uninstall $(MAKE) -C scripts uninstall $(MAKE) -i -C etc uninstall $(MAKE) -C man uninstall $(MAKE) -C kernel uninstall ifeq ($(ROOTUID),YES) systemctl daemon-reload endif tar: distclean test -d ../$(PARENTDIR) || ln -s $(TOPDIR) ../$(PARENTDIR) (cd kernel; tar --transform='s|.*/||' \ -cfz ../mhvtl_kernel.tgz * ../include/common/*) (cd ..; tar cvzf $(TAR_FILE) --exclude='.git*' \ $(PARENTDIR)/man \ $(PARENTDIR)/doc \ $(PARENTDIR)/kernel \ $(PARENTDIR)/usr \ $(PARENTDIR)/etc/ \ $(PARENTDIR)/scripts/ \ $(PARENTDIR)/ccan/ \ $(PARENTDIR)/tcopy/ \ $(PARENTDIR)/include \ $(PARENTDIR)/Makefile \ $(PARENTDIR)/config.mk \ $(PARENTDIR)/README \ $(PARENTDIR)/INSTALL \ $(PARENTDIR)/ChangeLog \ $(PARENTDIR)/mhvtl_kernel.tgz \ $(PARENTDIR)/mhvtl-utils.spec) $(RM) ../$(PARENTDIR) .PHONY:tags tags: $(MAKE) -C usr tags $(MAKE) -C kernel tags # ========== Cleaning ========== .PHONY:clean clean: $(MAKE) -C usr clean $(MAKE) -C etc clean $(MAKE) -C scripts clean $(MAKE) -C man clean $(MAKE) -C kernel clean $(RM) mhvtl_kernel.tgz .PHONY: distclean distclean: $(MAKE) -C usr distclean $(MAKE) -C etc distclean $(MAKE) -C scripts distclean $(MAKE) -C kernel distclean $(MAKE) -C man distclean $(RM) mhvtl_kernel.tgz $(RM) ../$(TAR_FILE) ================================================ FILE: README ================================================ mhvtl: A Virtual Tape & Library system. This package is composed of a kernel module (mhvtl) which is a pseudo HBA The vtl is basically a stripped down scsi_debug kernel module + a char dev 'back end' to pass the SCSI commands thru to user space daemons. It is the user space daemons responsibility to respond and process the SCSI commands. See the INSTALL file for compile and install instructions. This has been my first attempt at kernel level programming. Suggestions and comments always welcome. It has also been many years since I have done any c programming. Please be gentle.. Mark Harvey markh794@gmail.com ================================================ FILE: ccan/ccan/crc32c/_info ================================================ #include "config.h" #include #include /** * crc32c - routine for Castagnoli CRC (crc32c) of bytes * * Cyclic Redundancy Check routine, optimized for x86-64. Reasonably fast * checksum routines, but not suitable for cryptographic use. * * They are useful for simple error detection, eg. a 32-bit CRC will * detect a single error burst of up to 32 bits. * * Example: * #include * #include * #include * * // Given "123456789" outputs 0xe3069283 * int main(int argc, char *argv[]) * { * if (argc != 2) { * fprintf(stderr, "Usage: %s \n" * "Prints 32 bit CRC of the string\n", argv[0]); * exit(1); * } * printf("0x%08x\n", crc32c(0, argv[1], strlen(argv[1]))); * exit(0); * } * * License: MIT * Author: Mark Adler * Maintainer: Rusty Russell */ int main(int argc, char *argv[]) { if (argc != 2) return 1; if (strcmp(argv[1], "depends") == 0) { printf("ccan/compiler\n"); return 0; } return 1; } ================================================ FILE: ccan/ccan/crc32c/benchmark/Makefile ================================================ CCANDIR=../../.. CFLAGS=-Wall -Werror -O3 -I$(CCANDIR) -flto #CFLAGS=-Wall -Werror -g3 -I$(CCANDIR) LDFLAGS := -flto -O3 all: bench CCAN_OBJS:=ccan-tal.o ccan-tal-grab_file.o ccan-noerr.o ccan-take.o ccan-time.o bench: bench.o $(CCAN_OBJS) clean: rm -f bench *.o ccan-time.o: $(CCANDIR)/ccan/time/time.c $(CC) $(CFLAGS) -c -o $@ $< ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c $(CC) $(CFLAGS) -c -o $@ $< ccan-take.o: $(CCANDIR)/ccan/take/take.c $(CC) $(CFLAGS) -c -o $@ $< ccan-noerr.o: $(CCANDIR)/ccan/noerr/noerr.c $(CC) $(CFLAGS) -c -o $@ $< ccan-tal-grab_file.o: $(CCANDIR)/ccan/tal/grab_file/grab_file.c $(CC) $(CFLAGS) -c -o $@ $< ================================================ FILE: ccan/ccan/crc32c/benchmark/bench.c ================================================ #include #include #include #include #include #include #define RUNS 65536 int main(int argc, char *argv[]) { void *p; struct timeabs start, end; size_t len, runs; uint64_t sums = 0; bool sw = false, hw = false; if (argv[1]) { if (streq(argv[1], "--software")) { sw = true; argv++; argc--; } else if (streq(argv[1], "--hardware")) { hw = true; argv++; argc--; } } if (argc < 2 || (runs = atol(argv[1])) == 0) errx(1, "Usage: bench []"); p = grab_file(NULL, argv[2]); if (!p) err(1, "Reading %s", argv[2] ? argv[2] : ""); len = tal_count(p) - 1; start = time_now(); if (sw) { for (size_t i = 0; i < runs; i++) sums += crc32c_sw(0, p, len); } else if (hw) { for (size_t i = 0; i < runs; i++) sums += crc32c_hw(0, p, len); } else { for (size_t i = 0; i < runs; i++) sums += crc32c(0, p, len); } end = time_now(); assert(sums % runs == 0); printf("%u usec for %zu bytes, sum=%08x\n", (int)time_to_usec(time_divide(time_between(end, start), runs)), len, (unsigned int)(sums / runs)); return 0; } ================================================ FILE: ccan/ccan/crc32c/crc32c.c ================================================ /* MIT (BSD) license - see LICENSE file for details */ /* crc32c.c -- compute CRC-32C using the Intel crc32 instruction * Copyright (C) 2013, 2015 Mark Adler * Version 1.3 31 Dec 2015 Mark Adler */ /* This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Mark Adler madler@alumni.caltech.edu */ /* Use hardware CRC instruction on Intel SSE 4.2 processors. This computes a CRC-32C, *not* the CRC-32 used by Ethernet and zip, gzip, etc. A software version is provided as a fall-back, as well as for speed comparisons. */ /* Version history: 1.0 10 Feb 2013 First version 1.1 1 Aug 2013 Correct comments on why three crc instructions in parallel 1.2 1 Nov 2015 Add const qualifier to avoid compiler warning Load entire input into memory (test code) Argument gives number of times to repeat (test code) Argument < 0 forces software implementation (test code) 1.3 31 Dec 2015 Check for Intel architecture using compiler macro Support big-endian processors in software calculation Add header for external use */ #include "crc32c.h" #include #include static uint32_t crc32c_sw(uint32_t crc, void const *buf, size_t len); /* CRC-32C (iSCSI) polynomial in reversed bit order. */ #define POLY 0x82f63b78 #ifdef __x86_64__ /* Hardware CRC-32C for Intel and compatible processors. */ /* Multiply a matrix times a vector over the Galois field of two elements, GF(2). Each element is a bit in an unsigned integer. mat must have at least as many entries as the power of two for most significant one bit in vec. */ static inline uint32_t gf2_matrix_times(uint32_t *mat, uint32_t vec) { uint32_t sum = 0; while (vec) { if (vec & 1) sum ^= *mat; vec >>= 1; mat++; } return sum; } /* Multiply a matrix by itself over GF(2). Both mat and square must have 32 rows. */ static inline void gf2_matrix_square(uint32_t *square, uint32_t *mat) { for (unsigned n = 0; n < 32; n++) square[n] = gf2_matrix_times(mat, mat[n]); } /* Construct an operator to apply len zeros to a crc. len must be a power of two. If len is not a power of two, then the result is the same as for the largest power of two less than len. The result for len == 0 is the same as for len == 1. A version of this routine could be easily written for any len, but that is not needed for this application. */ static void crc32c_zeros_op(uint32_t *even, size_t len) { uint32_t odd[32]; /* odd-power-of-two zeros operator */ /* put operator for one zero bit in odd */ odd[0] = POLY; /* CRC-32C polynomial */ uint32_t row = 1; for (unsigned n = 1; n < 32; n++) { odd[n] = row; row <<= 1; } /* put operator for two zero bits in even */ gf2_matrix_square(even, odd); /* put operator for four zero bits in odd */ gf2_matrix_square(odd, even); /* first square will put the operator for one zero byte (eight zero bits), in even -- next square puts operator for two zero bytes in odd, and so on, until len has been rotated down to zero */ do { gf2_matrix_square(even, odd); len >>= 1; if (len == 0) return; gf2_matrix_square(odd, even); len >>= 1; } while (len); /* answer ended up in odd -- copy to even */ for (unsigned n = 0; n < 32; n++) even[n] = odd[n]; } /* Take a length and build four lookup tables for applying the zeros operator for that length, byte-by-byte on the operand. */ static void crc32c_zeros(uint32_t zeros[][256], size_t len) { uint32_t op[32]; crc32c_zeros_op(op, len); for (unsigned n = 0; n < 256; n++) { zeros[0][n] = gf2_matrix_times(op, n); zeros[1][n] = gf2_matrix_times(op, n << 8); zeros[2][n] = gf2_matrix_times(op, n << 16); zeros[3][n] = gf2_matrix_times(op, n << 24); } } /* Apply the zeros operator table to crc. */ static inline uint32_t crc32c_shift(uint32_t zeros[][256], uint32_t crc) { return zeros[0][crc & 0xff] ^ zeros[1][(crc >> 8) & 0xff] ^ zeros[2][(crc >> 16) & 0xff] ^ zeros[3][crc >> 24]; } /* Block sizes for three-way parallel crc computation. LONG and SHORT must both be powers of two. The associated string constants must be set accordingly, for use in constructing the assembler instructions. */ #define LONG 8192 #define LONGx1 "8192" #define LONGx2 "16384" #define SHORT 256 #define SHORTx1 "256" #define SHORTx2 "512" /* Tables for hardware crc that shift a crc by LONG and SHORT zeros. */ static bool crc32c_once_hw; static uint32_t crc32c_long[4][256]; static uint32_t crc32c_short[4][256]; /* Initialize tables for shifting crcs. */ static void crc32c_init_hw(void) { crc32c_once_hw = true; crc32c_zeros(crc32c_long, LONG); crc32c_zeros(crc32c_short, SHORT); } /* Compute CRC-32C using the Intel hardware instruction. */ static uint32_t crc32c_hw(uint32_t crc, void const *buf, size_t len) { /* populate shift tables the first time through */ if (!crc32c_once_hw) crc32c_init_hw(); /* pre-process the crc */ crc = ~crc; uint64_t crc0 = crc; /* 64-bits for crc32q instruction */ /* compute the crc for up to seven leading bytes to bring the data pointer to an eight-byte boundary */ unsigned char const *next = buf; while (len && ((uintptr_t)next & 7) != 0) { __asm__("crc32b\t" "(%1), %0" : "=r"(crc0) : "r"(next), "0"(crc0)); next++; len--; } /* compute the crc on sets of LONG*3 bytes, executing three independent crc instructions, each on LONG bytes -- this is optimized for the Nehalem, Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a throughput of one crc per cycle, but a latency of three cycles */ while (len >= LONG*3) { uint64_t crc1 = 0; uint64_t crc2 = 0; unsigned char const * const end = next + LONG; do { __asm__("crc32q\t" "(%3), %0\n\t" "crc32q\t" LONGx1 "(%3), %1\n\t" "crc32q\t" LONGx2 "(%3), %2" : "=r"(crc0), "=r"(crc1), "=r"(crc2) : "r"(next), "0"(crc0), "1"(crc1), "2"(crc2)); next += 8; } while (next < end); crc0 = crc32c_shift(crc32c_long, crc0) ^ crc1; crc0 = crc32c_shift(crc32c_long, crc0) ^ crc2; next += LONG*2; len -= LONG*3; } /* do the same thing, but now on SHORT*3 blocks for the remaining data less than a LONG*3 block */ while (len >= SHORT*3) { uint64_t crc1 = 0; uint64_t crc2 = 0; unsigned char const * const end = next + SHORT; do { __asm__("crc32q\t" "(%3), %0\n\t" "crc32q\t" SHORTx1 "(%3), %1\n\t" "crc32q\t" SHORTx2 "(%3), %2" : "=r"(crc0), "=r"(crc1), "=r"(crc2) : "r"(next), "0"(crc0), "1"(crc1), "2"(crc2)); next += 8; } while (next < end); crc0 = crc32c_shift(crc32c_short, crc0) ^ crc1; crc0 = crc32c_shift(crc32c_short, crc0) ^ crc2; next += SHORT*2; len -= SHORT*3; } /* compute the crc on the remaining eight-byte units less than a SHORT*3 block */ { unsigned char const * const end = next + (len - (len & 7)); while (next < end) { __asm__("crc32q\t" "(%1), %0" : "=r"(crc0) : "r"(next), "0"(crc0)); next += 8; } len &= 7; } /* compute the crc for up to seven trailing bytes */ while (len) { __asm__("crc32b\t" "(%1), %0" : "=r"(crc0) : "r"(next), "0"(crc0)); next++; len--; } /* return a post-processed crc */ return ~crc0; } /* Compute a CRC-32C. If the crc32 instruction is available, use the hardware version. Otherwise, use the software version. */ uint32_t crc32c(uint32_t crc, void const *buf, size_t len) { return cpu_supports("sse4.2") ? crc32c_hw(crc, buf, len) : crc32c_sw(crc, buf, len); } #else /* !__x86_64__ */ uint32_t crc32c(uint32_t crc, void const *buf, size_t len) { return crc32c_sw(crc, buf, len); } #endif /* Construct table for software CRC-32C little-endian calculation. */ static bool crc32c_once_little; static uint32_t crc32c_table_little[8][256]; static void crc32c_init_sw_little(void) { for (unsigned n = 0; n < 256; n++) { uint32_t crc = n; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc32c_table_little[0][n] = crc; } for (unsigned n = 0; n < 256; n++) { uint32_t crc = crc32c_table_little[0][n]; for (unsigned k = 1; k < 8; k++) { crc = crc32c_table_little[0][crc & 0xff] ^ (crc >> 8); crc32c_table_little[k][n] = crc; } } crc32c_once_little = true; } /* Compute a CRC-32C in software assuming a little-endian architecture, constructing the required table if that hasn't already been done. */ static uint32_t crc32c_sw_little(uint32_t crc, void const *buf, size_t len) { unsigned char const *next = buf; if (!crc32c_once_little) crc32c_init_sw_little(); crc = ~crc; while (len && ((uintptr_t)next & 7) != 0) { crc = crc32c_table_little[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); len--; } if (len >= 8) { uint64_t crcw = crc; do { crcw ^= *(uint64_t const *)next; crcw = crc32c_table_little[7][crcw & 0xff] ^ crc32c_table_little[6][(crcw >> 8) & 0xff] ^ crc32c_table_little[5][(crcw >> 16) & 0xff] ^ crc32c_table_little[4][(crcw >> 24) & 0xff] ^ crc32c_table_little[3][(crcw >> 32) & 0xff] ^ crc32c_table_little[2][(crcw >> 40) & 0xff] ^ crc32c_table_little[1][(crcw >> 48) & 0xff] ^ crc32c_table_little[0][crcw >> 56]; next += 8; len -= 8; } while (len >= 8); crc = crcw; } while (len) { crc = crc32c_table_little[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); len--; } return ~crc; } /* Swap the bytes in a uint64_t. (Only for big-endian.) */ #if defined(__has_builtin) || (defined(__GNUC__) && \ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) # define swap __builtin_bswap64 #else static inline uint64_t swap(uint64_t x) { x = ((x << 8) & 0xff00ff00ff00ff00) | ((x >> 8) & 0xff00ff00ff00ff); x = ((x << 16) & 0xffff0000ffff0000) | ((x >> 16) & 0xffff0000ffff); return (x << 32) | (x >> 32); } #endif /* Construct tables for software CRC-32C big-endian calculation. */ static bool crc32c_once_big; static uint32_t crc32c_table_big_byte[256]; static uint64_t crc32c_table_big[8][256]; static void crc32c_init_sw_big(void) { for (unsigned n = 0; n < 256; n++) { uint32_t crc = n; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc32c_table_big_byte[n] = crc; } for (unsigned n = 0; n < 256; n++) { uint32_t crc = crc32c_table_big_byte[n]; crc32c_table_big[0][n] = swap(crc); for (unsigned k = 1; k < 8; k++) { crc = crc32c_table_big_byte[crc & 0xff] ^ (crc >> 8); crc32c_table_big[k][n] = swap(crc); } } crc32c_once_big = true; } /* Compute a CRC-32C in software assuming a big-endian architecture, constructing the required tables if that hasn't already been done. */ static uint32_t crc32c_sw_big(uint32_t crc, void const *buf, size_t len) { unsigned char const *next = buf; if (!crc32c_once_big) crc32c_init_sw_big(); crc = ~crc; while (len && ((uintptr_t)next & 7) != 0) { crc = crc32c_table_big_byte[(crc ^ *next++) & 0xff] ^ (crc >> 8); len--; } if (len >= 8) { uint64_t crcw = swap(crc); do { crcw ^= *(uint64_t const *)next; crcw = crc32c_table_big[0][crcw & 0xff] ^ crc32c_table_big[1][(crcw >> 8) & 0xff] ^ crc32c_table_big[2][(crcw >> 16) & 0xff] ^ crc32c_table_big[3][(crcw >> 24) & 0xff] ^ crc32c_table_big[4][(crcw >> 32) & 0xff] ^ crc32c_table_big[5][(crcw >> 40) & 0xff] ^ crc32c_table_big[6][(crcw >> 48) & 0xff] ^ crc32c_table_big[7][(crcw >> 56)]; next += 8; len -= 8; } while (len >= 8); crc = swap(crcw); } while (len) { crc = crc32c_table_big_byte[(crc ^ *next++) & 0xff] ^ (crc >> 8); len--; } return ~crc; } /* Table-driven software CRC-32C. This is about 15 times slower than using the hardware instructions. Determine the endianess of the processor and proceed accordingly. Ideally the endianess will be determined at compile time, in which case the unused functions and tables for the other endianess will be removed by the optimizer. If not, then the proper routines and tables will be used, even if the endianess is changed mid-stream. (Yes, there are processors that permit that -- go figure.) */ static uint32_t crc32c_sw(uint32_t crc, void const *buf, size_t len) { static int const little = 1; if (*(char const *)&little) return crc32c_sw_little(crc, buf, len); else return crc32c_sw_big(crc, buf, len); } ================================================ FILE: ccan/ccan/crc32c/crc32c.h ================================================ /* Licensed under MIT - see LICENSE file for details */ #ifndef CCAN_CRC32C_H #define CCAN_CRC32C_H #include #include /** * crc32c - Castagnoli 32 bit crc of string of bytes * @start_crc: the initial crc (usually 0) * @buf: pointer to bytes * @size: length of buffer * * If you don't know what crc32 to use, use this one: it's the best. * * @Article{castagnoli-crc, * author = { Guy Castagnoli and Stefan Braeuer and Martin Herrman}, * title = {{Optimization of Cyclic Redundancy-Check Codes with 24 * and 32 Parity Bits}}, * journal = IEEE Transactions on Communication, * year = {1993}, * volume = {41}, * number = {6}, * pages = {}, * month = {June}, *} * 32 bit CRC checksum using polynomial * X^32+X^28+X^27+X^26+X^25+X^23+X^22+X^20+X^19+X^18+X^14+X^13+X^11+X^10+X^9+X^8+X^6+X^0. * * You can calculate the CRC of non-contiguous arrays by passing @start_crc * as 0 the first time, and the current crc result from then on. * * Example: * #include * ... * // Check that iovec has the crc we expect (Castagnoli version) * static bool check_crc(uint32_t expected, const struct iovec *iov, int l) * { * uint32_t crc = 0; * while (l >= 0) { * crc = crc32c(crc, iov->iov_base, iov->iov_len); * iov++; * } * return crc == expected; * } */ uint32_t crc32c(uint32_t start_crc, const void *buf, size_t size); #endif /* CCAN_CRC32C_H */ ================================================ FILE: ccan/ccan/crc32c/test/api-crc32c.c ================================================ /* Test vectors from https://tools.ietf.org/html/rfc3720#appendix-B.4 */ #include #include #include #define BSWAP_32(val) \ ((((uint32_t)(val) & 0x000000ff) << 24) \ | (((uint32_t)(val) & 0x0000ff00) << 8) \ | (((uint32_t)(val) & 0x00ff0000) >> 8) \ | (((uint32_t)(val) & 0xff000000) >> 24)) #if HAVE_LITTLE_ENDIAN #define BE32_TO_CPU(le_val) BSWAP_32((uint32_t)le_val) #else #define BE32_TO_CPU(le_val) ((uint32_t)(le_val)) #endif int main(void) { unsigned char m[48]; plan_tests(5); /* 32 bytes of zeroes: Byte: 0 1 2 3 0: 00 00 00 00 ... 28: 00 00 00 00 CRC: aa 36 91 8a */ memset(m, 0, 32); ok1(crc32c(0, m, 32) == BE32_TO_CPU(0xaa36918a)); /* 32 bytes of ones: Byte: 0 1 2 3 0: ff ff ff ff ... 28: ff ff ff ff CRC: 43 ab a8 62 */ memset(m, 0xff, 32); ok1(crc32c(0, m, 32) == BE32_TO_CPU(0x43aba862)); /* 32 bytes of incrementing 00..1f: Byte: 0 1 2 3 0: 00 01 02 03 ... 28: 1c 1d 1e 1f CRC: 4e 79 dd 46 */ for (size_t i = 0; i < 32; i++) m[i] = i; ok1(crc32c(0, m, 32) == BE32_TO_CPU(0x4e79dd46)); /* 32 bytes of decrementing 1f..00: Byte: 0 1 2 3 0: 1f 1e 1d 1c ... 28: 03 02 01 00 CRC: 5c db 3f 11 */ for (size_t i = 0; i < 32; i++) m[i] = 31 - i; ok1(crc32c(0, m, 32) == BE32_TO_CPU(0x5cdb3f11)); /* An iSCSI - SCSI Read (10) Command PDU Byte: 0 1 2 3 0: 01 c0 00 00 4: 00 00 00 00 8: 00 00 00 00 12: 00 00 00 00 16: 14 00 00 00 20: 00 00 04 00 24: 00 00 00 14 28: 00 00 00 18 32: 28 00 00 00 36: 00 00 00 00 40: 02 00 00 00 44: 00 00 00 00 CRC: 56 3a 96 d9 */ memset(m, 0, sizeof(m)); m[0] = 0x01; m[1] = 0xc0; m[16] = 0x14; m[22] = 0x04; m[27] = 0x14; m[31] = 0x18; m[32] = 0x28; m[40] = 0x02; ok1(crc32c(0, m, sizeof(m)) == BE32_TO_CPU(0x563a96d9)); return exit_status(); } ================================================ FILE: ccan/ccan/crc32c/test/run-crc32c.c ================================================ /* Test vectors from https://tools.ietf.org/html/rfc3720#appendix-B.4 */ /* Get access to sw version explicitly */ #include #include #include #define BSWAP_32(val) \ ((((uint32_t)(val) & 0x000000ff) << 24) \ | (((uint32_t)(val) & 0x0000ff00) << 8) \ | (((uint32_t)(val) & 0x00ff0000) >> 8) \ | (((uint32_t)(val) & 0xff000000) >> 24)) #if HAVE_LITTLE_ENDIAN #define BE32_TO_CPU(le_val) BSWAP_32((uint32_t)le_val) #else #define BE32_TO_CPU(le_val) ((uint32_t)(le_val)) #endif int main(void) { unsigned char m[48]; plan_tests(5); /* 32 bytes of zeroes: Byte: 0 1 2 3 0: 00 00 00 00 ... 28: 00 00 00 00 CRC: aa 36 91 8a */ memset(m, 0, 32); ok1(crc32c_sw(0, m, 32) == BE32_TO_CPU(0xaa36918a)); /* 32 bytes of ones: Byte: 0 1 2 3 0: ff ff ff ff ... 28: ff ff ff ff CRC: 43 ab a8 62 */ memset(m, 0xff, 32); ok1(crc32c_sw(0, m, 32) == BE32_TO_CPU(0x43aba862)); /* 32 bytes of incrementing 00..1f: Byte: 0 1 2 3 0: 00 01 02 03 ... 28: 1c 1d 1e 1f CRC: 4e 79 dd 46 */ for (size_t i = 0; i < 32; i++) m[i] = i; ok1(crc32c_sw(0, m, 32) == BE32_TO_CPU(0x4e79dd46)); /* 32 bytes of decrementing 1f..00: Byte: 0 1 2 3 0: 1f 1e 1d 1c ... 28: 03 02 01 00 CRC: 5c db 3f 11 */ for (size_t i = 0; i < 32; i++) m[i] = 31 - i; ok1(crc32c_sw(0, m, 32) == BE32_TO_CPU(0x5cdb3f11)); /* An iSCSI - SCSI Read (10) Command PDU Byte: 0 1 2 3 0: 01 c0 00 00 4: 00 00 00 00 8: 00 00 00 00 12: 00 00 00 00 16: 14 00 00 00 20: 00 00 04 00 24: 00 00 00 14 28: 00 00 00 18 32: 28 00 00 00 36: 00 00 00 00 40: 02 00 00 00 44: 00 00 00 00 CRC: 56 3a 96 d9 */ memset(m, 0, sizeof(m)); m[0] = 0x01; m[1] = 0xc0; m[16] = 0x14; m[22] = 0x04; m[27] = 0x14; m[31] = 0x18; m[32] = 0x28; m[40] = 0x02; ok1(crc32c_sw(0, m, sizeof(m)) == BE32_TO_CPU(0x563a96d9)); return exit_status(); } ================================================ FILE: config.mk ================================================ TOPDIR ?= $(CURDIR) VER ?= $(shell awk '/Version/ {print $$2}' $(TOPDIR)/mhvtl-utils.spec) REL ?= $(shell awk -F'[ %]' '/Release/ {print $$2}' $(TOPDIR)/mhvtl-utils.spec) EXTRAVERSION ?= $(shell awk '/define minor/ {print $$3}' $(TOPDIR)/mhvtl-utils.spec) FIRMWAREDIR ?= $(shell awk '/^%.*_firmwarepath/ {print $$3}' $(TOPDIR)/mhvtl-utils.spec) GITHASH ?= $(if $(shell test -d $(TOPDIR)/.git && echo 1),commit:\ $(shell git show -s --format=%h)) GITDATE ?= $(if $(shell test -d $(TOPDIR)/.git && echo 1),$(shell git show -s --format=%aI)) VERSION ?= $(VER) PREFIX ?= /usr MANDIR ?= /share/man MHVTL_HOME_PATH ?= /opt/mhvtl MHVTL_CONFIG_PATH ?= /etc/mhvtl SYSTEMD_GENERATOR_DIR ?= /lib/systemd/system-generators SYSTEMD_SERVICE_DIR ?= /lib/systemd/system ifeq ($(shell whoami),root) ROOTUID = YES endif ifeq ($(shell grep lib64$ /etc/ld.so.conf /etc/ld.so.conf.d/* | wc -l),0) LIBDIR ?= $(PREFIX)/lib else LIBDIR ?= $(PREFIX)/lib64 endif -include $(TOPDIR)/local.mk HOME_PATH = $(subst /,\/,$(MHVTL_HOME_PATH)) CONFIG_PATH = $(subst /,\/,$(MHVTL_CONFIG_PATH)) ================================================ FILE: doc/4_library_example/device.conf ================================================ VERSION: 3 # VPD page format: # ... # NAA format is an 8 hex byte value seperated by ':' # Note: NAA is part of inquiry VPD 0x83 # # Each 'record' is sperated by one (or more) blank lines. # Each 'record' starts at column 1 Library: 10 CHANNEL: 0 TARGET: 1 LUN: 0 Vendor identification: SPECTRA Product identification: PYTHON Product revision level: 5500 Unit serial number: XYZZY_10 NAA: 10:22:33:44:ab:cd:ef:00 Drive: 11 CHANNEL: 0 TARGET: 1 LUN: 1 Library ID: 10 Slot: 1 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_11 NAA: 10:22:33:44:ab:cd:ef:01 VPD: b0 04 00 02 01 00 Drive: 12 CHANNEL: 0 TARGET: 1 LUN: 2 Library ID: 10 Slot: 2 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_12 NAA: 10:22:33:44:ab:cd:ef:02 VPD: b0 04 00 02 01 00 Drive: 13 CHANNEL: 0 TARGET: 1 LUN: 3 Library ID: 10 Slot: 3 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_13 NAA: 10:22:33:44:ab:cd:ef:03 VPD: b0 04 00 02 01 00 Drive: 14 CHANNEL: 0 TARGET: 1 LUN: 4 Library ID: 10 Slot: 4 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_14 NAA: 10:22:33:44:ab:cd:ef:04 VPD: b0 04 00 02 01 00 Drive: 15 CHANNEL: 0 TARGET: 1 LUN: 5 Library ID: 10 Slot: 5 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_15 NAA: 10:22:33:44:ab:cd:ef:05 VPD: b0 04 00 02 01 00 Drive: 16 CHANNEL: 0 TARGET: 1 LUN: 6 Library ID: 10 Slot: 6 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_16 NAA: 10:22:33:44:ab:cd:ef:06 VPD: b0 04 00 02 01 00 Drive: 17 CHANNEL: 0 TARGET: 1 LUN: 7 Library ID: 10 Slot: 7 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_17 NAA: 10:22:33:44:ab:cd:ef:07 VPD: b0 04 00 02 01 00 Drive: 18 CHANNEL: 0 TARGET: 1 LUN: 8 Library ID: 10 Slot: 8 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_18 NAA: 10:22:33:44:ab:cd:ef:08 VPD: b0 04 00 02 01 00 Drive: 19 CHANNEL: 0 TARGET: 1 LUN: 9 Library ID: 10 Slot: 9 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_19 NAA: 10:22:33:44:ab:cd:ef:09 VPD: b0 04 00 02 01 00 Library: 20 CHANNEL: 0 TARGET: 1 LUN: 10 Vendor identification: SPECTRA Product identification: PYTHON Product revision level: 5500 Unit serial number: XYZZY_20 NAA: 20:22:33:44:ab:cd:ef:00 Drive: 21 CHANNEL: 0 TARGET: 1 LUN: 11 Library ID: 20 Slot: 1 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_21 NAA: 20:22:33:44:ab:cd:ef:01 VPD: b0 04 00 02 01 00 Drive: 22 CHANNEL: 0 TARGET: 1 LUN: 12 Library ID: 20 Slot: 2 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_22 NAA: 20:22:33:44:ab:cd:ef:02 VPD: b0 04 00 02 01 00 Drive: 23 CHANNEL: 0 TARGET: 1 LUN: 13 Library ID: 20 Slot: 3 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_23 NAA: 20:22:33:44:ab:cd:ef:03 VPD: b0 04 00 02 01 00 Drive: 24 CHANNEL: 0 TARGET: 1 LUN: 14 Library ID: 20 Slot: 4 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_24 NAA: 20:22:33:44:ab:cd:ef:04 VPD: b0 04 00 02 01 00 Drive: 25 CHANNEL: 0 TARGET: 1 LUN: 15 Library ID: 20 Slot: 5 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_25 NAA: 20:22:33:44:ab:cd:ef:05 VPD: b0 04 00 02 01 00 Drive: 26 CHANNEL: 0 TARGET: 1 LUN: 16 Library ID: 20 Slot: 6 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_26 NAA: 20:22:33:44:ab:cd:ef:06 VPD: b0 04 00 02 01 00 Drive: 27 CHANNEL: 0 TARGET: 1 LUN: 17 Library ID: 20 Slot: 7 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_27 NAA: 20:22:33:44:ab:cd:ef:07 VPD: b0 04 00 02 01 00 Drive: 28 CHANNEL: 0 TARGET: 1 LUN: 18 Library ID: 20 Slot: 8 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_28 NAA: 20:22:33:44:ab:cd:ef:08 VPD: b0 04 00 02 01 00 Drive: 29 CHANNEL: 0 TARGET: 1 LUN: 19 Library ID: 20 Slot: 9 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_29 NAA: 20:22:33:44:ab:cd:ef:09 VPD: b0 04 00 02 01 00 Library: 30 CHANNEL: 0 TARGET: 1 LUN: 20 Vendor identification: SPECTRA Product identification: PYTHON Product revision level: 5500 Unit serial number: XYZZY_30 NAA: 30:22:33:44:ab:cd:ef:00 Drive: 31 CHANNEL: 0 TARGET: 1 LUN: 21 Library ID: 30 Slot: 1 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_31 NAA: 30:22:33:44:ab:cd:ef:01 VPD: b0 04 00 02 01 00 Drive: 32 CHANNEL: 0 TARGET: 1 LUN: 22 Library ID: 30 Slot: 2 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_32 NAA: 30:22:33:44:ab:cd:ef:02 VPD: b0 04 00 02 01 00 Drive: 33 CHANNEL: 0 TARGET: 1 LUN: 23 Library ID: 30 Slot: 3 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_33 NAA: 30:22:33:44:ab:cd:ef:03 VPD: b0 04 00 02 01 00 Drive: 34 CHANNEL: 0 TARGET: 1 LUN: 24 Library ID: 30 Slot: 4 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_34 NAA: 30:22:33:44:ab:cd:ef:04 VPD: b0 04 00 02 01 00 Drive: 35 CHANNEL: 0 TARGET: 1 LUN: 25 Library ID: 30 Slot: 5 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_35 NAA: 30:22:33:44:ab:cd:ef:05 VPD: b0 04 00 02 01 00 Drive: 36 CHANNEL: 0 TARGET: 1 LUN: 26 Library ID: 30 Slot: 6 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_36 NAA: 30:22:33:44:ab:cd:ef:06 VPD: b0 04 00 02 01 00 Drive: 37 CHANNEL: 0 TARGET: 1 LUN: 27 Library ID: 30 Slot: 7 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_37 NAA: 30:22:33:44:ab:cd:ef:07 VPD: b0 04 00 02 01 00 Drive: 38 CHANNEL: 0 TARGET: 1 LUN: 28 Library ID: 30 Slot: 8 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_38 NAA: 30:22:33:44:ab:cd:ef:08 VPD: b0 04 00 02 01 00 Drive: 39 CHANNEL: 0 TARGET: 1 LUN: 29 Library ID: 30 Slot: 9 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_39 NAA: 30:22:33:44:ab:cd:ef:09 VPD: b0 04 00 02 01 00 Library: 40 CHANNEL: 0 TARGET: 1 LUN: 30 Vendor identification: SPECTRA Product identification: PYTHON Product revision level: 5500 Unit serial number: XYZZY_40 NAA: 40:22:33:44:ab:cd:ef:00 Drive: 41 CHANNEL: 0 TARGET: 1 LUN: 31 Library ID: 40 Slot: 1 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_41 NAA: 40:22:33:44:ab:cd:ef:01 VPD: b0 04 00 02 01 00 Drive: 42 CHANNEL: 0 TARGET: 2 LUN: 0 Library ID: 40 Slot: 2 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_42 NAA: 40:22:33:44:ab:cd:ef:02 VPD: b0 04 00 02 01 00 Drive: 43 CHANNEL: 0 TARGET: 2 LUN: 1 Library ID: 40 Slot: 3 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_43 NAA: 40:22:33:44:ab:cd:ef:03 VPD: b0 04 00 02 01 00 Drive: 44 CHANNEL: 0 TARGET: 2 LUN: 2 Library ID: 40 Slot: 4 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_44 NAA: 40:22:33:44:ab:cd:ef:04 VPD: b0 04 00 02 01 00 Drive: 45 CHANNEL: 0 TARGET: 2 LUN: 3 Library ID: 40 Slot: 5 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_45 NAA: 40:22:33:44:ab:cd:ef:05 VPD: b0 04 00 02 01 00 Drive: 46 CHANNEL: 0 TARGET: 2 LUN: 4 Library ID: 40 Slot: 6 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_46 NAA: 40:22:33:44:ab:cd:ef:06 VPD: b0 04 00 02 01 00 Drive: 47 CHANNEL: 0 TARGET: 2 LUN: 5 Library ID: 40 Slot: 7 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_47 NAA: 40:22:33:44:ab:cd:ef:07 VPD: b0 04 00 02 01 00 Drive: 48 CHANNEL: 0 TARGET: 2 LUN: 6 Library ID: 40 Slot: 8 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_48 NAA: 40:22:33:44:ab:cd:ef:08 VPD: b0 04 00 02 01 00 Drive: 49 CHANNEL: 0 TARGET: 2 LUN: 7 Library ID: 40 Slot: 9 Vendor identification: QUANTUM Product identification: SDLT600 Product revision level: 5500 Unit serial number: XYZZY_49 NAA: 40:22:33:44:ab:cd:ef:09 VPD: b0 04 00 02 01 00 ================================================ FILE: doc/4_library_example/library_contents.10 ================================================ VERSION: 2 Drive 1: Drive 2: Drive 3: Drive 4: Drive 5: Drive 6: Drive 7: Drive 8: Drive 9: Picker 1: MAP 1: MAP 2: MAP 3: MAP 4: # Slot 1 - ?, no gaps # Slot N: [barcode] # [barcode] # a barcode is comprised of three fields: [Leading] [identifier] [Trailing] # Leading "CLN" -- cleaning tape # Leading "W" -- WORM tape # Leading "NOBAR" -- will appear to have no barcode # If the barcode is at least 8 character long, then the last two characters are Trailing # Trailing "S3" - SDLT600 # Trailing "X4" - AIT-4 # Trailing "L1" - LTO 1 # Trailing "TA" - T10000+ # Trailing "JA" - 3592+ # Trailing "JB" - 3592E05+ # Trailing "JW" - WORM 3592+ # Trailing "JX" - WORM 3592E05+ # Slot 01: L10001S3 Slot 02: L10002S3 Slot 03: L10003S3 Slot 04: L10004S3 Slot 05: L10005S3 Slot 06: L10006S3 Slot 07: L10007S3 Slot 08: L10008S3 Slot 09: L10009S3 Slot 10: L10010S3 Slot 11: L10011S3 Slot 12: L10012S3 Slot 13: L10013S3 Slot 14: L10014S3 Slot 15: L10015S3 Slot 16: L10016S3 Slot 17: L10017S3 Slot 18: L10018S3 Slot 19: L10019S3 Slot 20: L10020S3 Slot 21: L10021S3 Slot 22: L10022S3 Slot 23: L10023S3 Slot 24: L10024S3 Slot 25: L10025S3 Slot 26: L10026S3 Slot 27: L10027S3 Slot 28: L10028S3 Slot 29: L10029S3 Slot 30: L10030S3 Slot 31: L10031S3 Slot 32: L10032S3 Slot 33: L10033S3 Slot 34: L10034S3 Slot 35: L10035S3 Slot 36: L10036S3 Slot 37: L10037S3 Slot 38: L10038S3 Slot 39: L10039S3 Slot 40: L10040S3 Slot 41: L10041S3 Slot 42: L10042S3 Slot 43: L10043S3 Slot 44: L10044S3 Slot 45: L10045S3 Slot 46: L10046S3 Slot 47: L10047S3 Slot 48: L10048S3 Slot 49: L10049S3 Slot 50: L10050S3 ================================================ FILE: doc/4_library_example/library_contents.20 ================================================ VERSION: 2 Drive 1: Drive 2: Drive 3: Drive 4: Drive 5: Drive 6: Drive 7: Drive 8: Drive 9: Picker 1: MAP 1: MAP 2: MAP 3: MAP 4: # Slot 1 - ?, no gaps # Slot N: [barcode] # [barcode] # a barcode is comprised of three fields: [Leading] [identifier] [Trailing] # Leading "CLN" -- cleaning tape # Leading "W" -- WORM tape # Leading "NOBAR" -- will appear to have no barcode # If the barcode is at least 8 character long, then the last two characters are Trailing # Trailing "S3" - SDLT600 # Trailing "X4" - AIT-4 # Trailing "L1" - LTO 1 # Trailing "TA" - T10000+ # Trailing "JA" - 3592+ # Trailing "JB" - 3592E05+ # Trailing "JW" - WORM 3592+ # Trailing "JX" - WORM 3592E05+ # Slot 01: L20001S3 Slot 02: L20002S3 Slot 03: L20003S3 Slot 04: L20004S3 Slot 05: L20005S3 Slot 06: L20006S3 Slot 07: L20007S3 Slot 08: L20008S3 Slot 09: L20009S3 Slot 10: L20010S3 Slot 11: L20011S3 Slot 12: L20012S3 Slot 13: L20013S3 Slot 14: L20014S3 Slot 15: L20015S3 Slot 16: L20016S3 Slot 17: L20017S3 Slot 18: L20018S3 Slot 19: L20019S3 Slot 20: L20020S3 Slot 21: L20021S3 Slot 22: L20022S3 Slot 23: L20023S3 Slot 24: L20024S3 Slot 25: L20025S3 Slot 26: L20026S3 Slot 27: L20027S3 Slot 28: L20028S3 Slot 29: L20029S3 Slot 30: L20030S3 Slot 31: L20031S3 Slot 32: L20032S3 Slot 33: L20033S3 Slot 34: L20034S3 Slot 35: L20035S3 Slot 36: L20036S3 Slot 37: L20037S3 Slot 38: L20038S3 Slot 39: L20039S3 Slot 40: L20040S3 Slot 41: L20041S3 Slot 42: L20042S3 Slot 43: L20043S3 Slot 44: L20044S3 Slot 45: L20045S3 Slot 46: L20046S3 Slot 47: L20047S3 Slot 48: L20048S3 Slot 49: L20049S3 Slot 50: L20050S3 ================================================ FILE: doc/4_library_example/library_contents.30 ================================================ VERSION: 2 Drive 1: Drive 2: Drive 3: Drive 4: Drive 5: Drive 6: Drive 7: Drive 8: Drive 9: Picker 1: MAP 1: MAP 2: MAP 3: MAP 4: # Slot 1 - ?, no gaps # Slot N: [barcode] # [barcode] # a barcode is comprised of three fields: [Leading] [identifier] [Trailing] # Leading "CLN" -- cleaning tape # Leading "W" -- WORM tape # Leading "NOBAR" -- will appear to have no barcode # If the barcode is at least 8 character long, then the last two characters are Trailing # Trailing "S3" - SDLT600 # Trailing "X4" - AIT-4 # Trailing "L1" - LTO 1 # Trailing "TA" - T10000+ # Trailing "JA" - 3592+ # Trailing "JB" - 3592E05+ # Trailing "JW" - WORM 3592+ # Trailing "JX" - WORM 3592E05+ # Slot 01: L30001S3 Slot 02: L30002S3 Slot 03: L30003S3 Slot 04: L30004S3 Slot 05: L30005S3 Slot 06: L30006S3 Slot 07: L30007S3 Slot 08: L30008S3 Slot 09: L30009S3 Slot 10: L30010S3 Slot 11: L30011S3 Slot 12: L30012S3 Slot 13: L30013S3 Slot 14: L30014S3 Slot 15: L30015S3 Slot 16: L30016S3 Slot 17: L30017S3 Slot 18: L30018S3 Slot 19: L30019S3 Slot 20: L30020S3 Slot 21: L30021S3 Slot 22: L30022S3 Slot 23: L30023S3 Slot 24: L30024S3 Slot 25: L30025S3 Slot 26: L30026S3 Slot 27: L30027S3 Slot 28: L30028S3 Slot 29: L30029S3 Slot 30: L30030S3 Slot 31: L30031S3 Slot 32: L30032S3 Slot 33: L30033S3 Slot 34: L30034S3 Slot 35: L30035S3 Slot 36: L30036S3 Slot 37: L30037S3 Slot 38: L30038S3 Slot 39: L30039S3 Slot 40: L30040S3 Slot 41: L30041S3 Slot 42: L30042S3 Slot 43: L30043S3 Slot 44: L30044S3 Slot 45: L30045S3 Slot 46: L30046S3 Slot 47: L30047S3 Slot 48: L30048S3 Slot 49: L30049S3 Slot 50: L30050S3 ================================================ FILE: doc/4_library_example/library_contents.40 ================================================ VERSION: 2 Drive 1: Drive 2: Drive 3: Drive 4: Drive 5: Drive 6: Drive 7: Drive 8: Drive 9: Picker 1: MAP 1: MAP 2: MAP 3: MAP 4: # Slot 1 - ?, no gaps # Slot N: [barcode] # [barcode] # a barcode is comprised of three fields: [Leading] [identifier] [Trailing] # Leading "CLN" -- cleaning tape # Leading "W" -- WORM tape # Leading "NOBAR" -- will appear to have no barcode # If the barcode is at least 8 character long, then the last two characters are Trailing # Trailing "S3" - SDLT600 # Trailing "X4" - AIT-4 # Trailing "L1" - LTO 1 # Trailing "TA" - T10000+ # Trailing "JA" - 3592+ # Trailing "JB" - 3592E05+ # Trailing "JW" - WORM 3592+ # Trailing "JX" - WORM 3592E05+ # Slot 01: L40001S3 Slot 02: L40002S3 Slot 03: L40003S3 Slot 04: L40004S3 Slot 05: L40005S3 Slot 06: L40006S3 Slot 07: L40007S3 Slot 08: L40008S3 Slot 09: L40009S3 Slot 10: L40010S3 Slot 11: L40011S3 Slot 12: L40012S3 Slot 13: L40013S3 Slot 14: L40014S3 Slot 15: L40015S3 Slot 16: L40016S3 Slot 17: L40017S3 Slot 18: L40018S3 Slot 19: L40019S3 Slot 20: L40020S3 Slot 21: L40021S3 Slot 22: L40022S3 Slot 23: L40023S3 Slot 24: L40024S3 Slot 25: L40025S3 Slot 26: L40026S3 Slot 27: L40027S3 Slot 28: L40028S3 Slot 29: L40029S3 Slot 30: L40030S3 Slot 31: L40031S3 Slot 32: L40032S3 Slot 33: L40033S3 Slot 34: L40034S3 Slot 35: L40035S3 Slot 36: L40036S3 Slot 37: L40037S3 Slot 38: L40038S3 Slot 39: L40039S3 Slot 40: L40040S3 Slot 41: L40041S3 Slot 42: L40042S3 Slot 43: L40043S3 Slot 44: L40044S3 Slot 45: L40045S3 Slot 46: L40046S3 Slot 47: L40047S3 Slot 48: L40048S3 Slot 49: L40049S3 Slot 50: L40050S3 ================================================ FILE: doc/4_library_example/mhvtl.conf ================================================ # Home directory for config file(s) MHVTL_CONFIG_PATH=/etc/mhvtl # Default media capacity (5 G) CAPACITY=5000 # Set default verbosity [0|1] VERBOSE=0 # Set kernel module debugging [0|1] VTL_DEBUG=0 ================================================ FILE: doc/index.html ================================================

Welcome to Mark Harvey's home page


Current projects:


Virtual Tape Library:

Virtual Tape Library consists of several components.

  • LLD - A low level driver implemented as a kernel module - vx_tape
  • Target devices - Daemons vtltape(1) and vtllibrary(1) which implement SCSI target device(s) in user-space
  • Utility commands mktape(1), vtlcmd(1)
  • And setup script make_vtl_media(1)

The kernel module is based on the scsi_debug kernel module (http://www.torque.net/sg/sdebug26.html). vx_tape is a pseudo HBA (LLD) reporting it has 1 x SMC target (StorageTek L700) and 8 x SSC targets (4 x IBM Ultrium-TD3 and 4 x Quantum SuperDLT600) attached.

A char device back-end has been included with the vx_tape LLD driver This allows data and SCSI commands to be passed from the LLD to user-mode daemons (SCSI targets) which constently poll the driver and process any outstanding SCSI commands.

vtltape

vtltape(1) is the usermode SSC target daemon which writes/reads data to data files in the /opt/vtl directory (if a virtual tape has been loaded). The virtual tape files include a Medium Auxiliary Memory (MAM) data structure to store persistent data (number of tape loads, total data written/read, media type etc).

vtllibrary

vtllibrary(1) is the usermode SMC target daemon which reads its configuration from the file /etc/vtl/library_contents(5) at startup. The number of storage slots are built dynamically when the daemon starts. Hence changing the number of storage slots and media access slots are a matter of modifing the file contents and restarting the vtllibrary(1) daemon. All 'library' commands are performed on data structures in memory ONLY.

vtlcmd

A utility vtlcmd(1) is used to administrator the daemons vtltape(1) and vtllibrary.
Message queue (key 0x4d61726b) is used to pass messages between vtlcmd(1), vtllibrary(1) and vtltape(1)

When a SCSI 'move medium' from a storage slot to a tape drive is requested, the media location is updated in vtllibrary(1) data structures, and the barcode of the media id is passed via the message queue to the vtltape(1) daemon in question. A file open of the barcode is attempted in the /opt/vtl directory and if successful, the vtltape(1) daemon will now return a ASC/ASCQ 0x0/0x0 to any Test Unit Ready requests. An unload SCSI command to the tape drive will close the data file.

Media can be moved out of the VTL via the Media Access Port. Once media is logically moved into the MAP slots, the MAP entries can be cleared using the vtlcmd:

# vtlcmd library empty map

Examples

vx_tape is registered as HBA #3:
# lsscsi -g
[0:0:0:0]    disk    MAXTOR   ATLAS10K4_36SCA  DFM0  /dev/sda  /dev/sg0
[0:0:6:0]    process PE/PV    1x3 SCSI BP      1.1   -         /dev/sg1
[2:0:0:0]    disk    SEAGATE  ST336607FC       0003  /dev/sdb  /dev/sg2
[2:0:1:0]    disk    SEAGATE  ST336607FC       0003  /dev/sdc  /dev/sg3
[2:0:2:0]    mediumx ATL      1500             6.0   -         /dev/sg4
[2:0:2:1]    tape    QUANTUM  SuperDLT1        2323  /dev/st0  /dev/sg5
[2:0:2:2]    tape    QUANTUM  SuperDLT1        2323  /dev/st1  /dev/sg6
[2:0:2:3]    process ATTO     310-LV           1.42  -         /dev/sg7
[3:0:0:0]    mediumx STK      L700             vx_0  -         /dev/sg8
[4:0:0:0]    tape    IBM      ULT3580-TD3      5400  /dev/st2  /dev/sg9
[4:0:0:1]    tape    IBM      ULT3580-TD3      5400  /dev/st3  /dev/sg10
[4:0:0:2]    tape    IBM      ULT3580-TD3      5400  /dev/st4  /dev/sg11
[4:0:0:3]    tape    IBM      ULT3580-TD3      5400  /dev/st5  /dev/sg12
[4:0:0:4]    tape    QUANTUM  SDLT600          5400  /dev/st6  /dev/sg13
[4:0:0:5]    tape    QUANTUM  SDLT600          5400  /dev/st7  /dev/sg14
[4:0:0:6]    tape    QUANTUM  SDLT600          5400  /dev/st8  /dev/sg15
[4:0:0:7]    tape    QUANTUM  SDLT600          5400  /dev/st9  /dev/sg16
NetBackup Utilities:
# tpautoconf -r
TPAC45 STK     L700            vx_0 8000 -1 -1 -1 -1 /dev/sg/h3c0t0l0 - -

# tpautoconf -t
TPAC45 IBM     ULT3580-TD3     5400 8001 -1 -1 -1 -1 /dev/st/nh3c0t0l1 - TapeAlert enabled
TPAC45 IBM     ULT3580-TD3     5400 8002 -1 -1 -1 -1 /dev/st/nh3c0t0l2 - TapeAlert enabled
TPAC45 IBM     ULT3580-TD3     5400 8003 -1 -1 -1 -1 /dev/st/nh3c0t0l3 - TapeAlert enabled
TPAC45 IBM     ULT3580-TD3     5400 8004 -1 -1 -1 -1 /dev/st/nh3c0t0l4 - TapeAlert enabled
TPAC45 QUANTUM SDLT600         5400 8005 -1 -1 -1 -1 /dev/st/nh3c0t0l5 - TapeAlert enabled
TPAC45 QUANTUM SDLT600         5400 8006 -1 -1 -1 -1 /dev/st/nh3c0t0l6 - TapeAlert enabled
TPAC45 QUANTUM SDLT600         5400 8007 -1 -1 -1 -1 /dev/st/nh3c0t0l7 - TapeAlert enabled
TPAC45 QUANTUM SDLT600         5400 8008 -1 -1 -1 -1 /dev/st/nh3c0t0l8 - TapeAlert enabled

volmgr/bin/scan output

Robot Inventory:

Using mtx

Using robtest

A sample /etc/vtl/library_contents can be viewed via this link.

TapeAlert

TapeAlert flags can be set using the vtlcmd(1)

e.g. vtlcmd [index|library] TapeAlert [flags]

Where index is the message Q offset (or string 'library') associated with the drive.

e.g. To set flag 14h (Clean now) the 20th bit (14h) needs to be set:
Pull out the binary to decimal calculator
1000 0000 0000 0000 0000 (20 bits) => hex => 80000

# vtlcmd 1 TapeAlert 80000

A listing of TapeAlert flags can be found at t10 home page for SSC devices Annex A.

Code vtlcmd value Flag Type Flag Type Recommended application client message Probable cause
01h 1 Read warning O W The tape drive is having problems reading data. No data has been lost, but there has been a reduction in the performance of the tape. The drive is having severe trouble reading.
02h 2 Write warning O W The tape drive is having problems writing data. No data has been lost, but there has been a reduction in the capacity of the tape. The drive is having severe trouble writing.
03h 4 Hard error M W The operation has stopped because an error has occurred while reading or writing data that the drive cannot correct. The drive had a hard read or write error.
04h 8 Media M C Your data is at risk:
  1. Copy any data you require from this tape.
  2. Do not use this tape again.
  3. Restart the operation with a different tape.
Media can no longer be written/read, or performance is severely degraded.
05h 10 Read failure M C The tape is damaged or the drive is faulty. Call the tape drive supplier help line. The drive can no longer read data from the tape.
06h 20 Write failure M C The tape is from a faulty batch or the tape drive is faulty:
  1. Use a good tape to test the drive.
  2. If the problem persists, call the tape drive supplier help line.
The drive can no longer write data to the tape.
07h 40 Media life O W The tape cartridge has reached the end of its calculated useful life:
  1. Copy any data you need to another tape.
  2. Discard the old tape.
The media has exceeded its specified life.
08h 80 Not data grade O W The cartridge is not data-grade. Any data you write to the tape is at risk. Replace the cartridge with a data-grade tape. The drive has not been able to read the MRSa stripes.
09h 100 Write protect O C You are trying to write to a write protected cartridge. Remove the write protection or use another tape. Write command is attempted to a write protected tape.
0Ah 200 No removal O I You cannot eject the cartridge because the tape drive is in use. Wait until the operation is complete before ejecting the cartridge. Manual or software unload attempted when prevent media removal is on.
0Bh 400 Cleaning media O I The tape in the drive is a cleaning cartridge. Cleaning tape loaded into drive.
0Ch 800 Unsupported format O I You have tried to load a cartridge of a type that is not supported by this drive. Attempted load of unsupported tape format (e.g., DDS2 in DDS1 drive).
0Dh 1000 Recoverable mechanical cartridge failure O C The operation has failed because the tape in the drive has experienced a mechanical failure:
  1. Discard the old tape.
  2. Restart the operation with a different tape.
Tape snapped/cut or other cartridge mechanical failure in the drive where medium can be de-mounted.
0Eh 2000 Unrecoverable mechanical cartridge failure O C The operation has failed because the tape in the drive has experienced a mechanical failure:
  1. Do not attempt to extract the tape cartridge.
  2. Call the tape drive supplier help line.
Tape snapped/cut or other cartridge mechanical failure in the drive where medium cannot be de-mounted.
0Fh 4000 Memory chip in cartridge failure O W The memory in the tape cartridge has failed, which reduces performance. Do not use the cartridge for further write operations. Memory chip failed in cartridge.
10h 8000 Forced eject O C The operation has failed because the tape cartridge was manually de-mounted while the tape drive was actively writing or reading. Manual or forced eject while drive actively writing or reading.
11h 10000 Read only format O W You have loaded a cartridge of a type that is read-only in this drive. The cartridge will appear as write protected. Media loaded that is read-only format.
12h 20000 Tape directory corrupted on load O W The tape directory on the tape cartridge has been corrupted. File search performance will be degraded. The tape directory can be rebuilt by reading all the data on the cartridge. Tape drive powered down with tape loaded, or permanent error prevented the tape directory being updated.
13h 40000 Nearing media life O I The tape cartridge is nearing the end of its calculated life. It is recommended that you:
  1. Use another tape cartridge for your next backup.
  2. Store this tape cartridge in a safe place in case you need to restore data from it.
Media may have exceeded its specified number of passes.
14h 80000 Clean now O C The tape drive needs cleaning:
  1. If the operation has stopped, eject the tape and clean the drive.
  2. If the operation has not stopped, wait for it to finish and then clean the drive.
Check the tape drive users manual for device specific cleaning instructions.
The drive thinks it has a head clog or needs cleaning.
15h 100000 Clean periodic O W The tape drive is due for routine cleaning:
  1. Wait for the current operation to finish.
  2. Then use a cleaning cartridge.
Check the tape drive users manual for device specific cleaning instructions. The drive is ready for a periodic cleaning.
16h 200000 Expired cleaning media O C The last cleaning cartridge used in the tape drive has worn out:
  1. Discard the worn out cleaning cartridge.
  2. Wait for the current operation to finish.
  3. Then use a new cleaning cartridge.
The cleaning tape has expired.
17h 400000 Invalid cleaning tape O C The last cleaning cartridge used in the tape drive was an invalid type:
  1. Do not use this cleaning cartridge in this drive.
  2. Wait for the current operation to finish.
  3. Then use a valid cleaning cartridge.
Invalid cleaning tape type used.
18h 800000 Retension requested O W The tape drive has requested a retension operation. The drive is having severe trouble reading or writing, that will be resolved by a retension cycle.
19h 1000000 Dual-port interface error O W A redundant interface port on the tape drive has failed. Failure of one interface port in a dual-port configuration (i.e., Fibre Channel)
1Ah 2000000 Cooling fan failure O W A tape drive cooling fan has failed. Fan failure inside tape drive mechanism or tape drive enclosure.
1Bh 4000000 Power supply failure O W A redundant power supply has failed inside the tape drive enclosure. Check the enclosure users manual for instructions on replacing the failed power supply. Redundant PSU failure inside the tape drive enclosure or rack subsystem.
1Ch 8000000 Power consumption O W The tape drive power consumption is outside the specified range. Power consumption of the tape drive is outside specified range.
1Dh 10000000 Drive maintenance O W Preventive maintenance of the tape drive is required. Check the tape drive users manual for device specific preventive maintenance tasks or call the tape drive supplier help line. The drive requires preventive maintenance (not cleaning).
1Eh 20000000 Hardware A O C The tape drive has a hardware fault:
  1. Eject the tape or magazine.
  2. Reset the drive.
  3. Restart the operation.
The drive has a hardware fault that requires reset to recover.
1Fh 40000000 Hardware B M C The tape drive has a hardware fault:
  1. Turn the tape drive off and then on again.
  2. Restart the operation.
  3. If the problem persists, call the tape drive supplier help line.
The drive has a hardware fault that is not read/write related or requires a power cycle to recover.
20h 80000000 Interface O W The tape drive has a problem with the application client interface:
  1. Check the cables and cable connections.
  2. Restart the operation.
The drive has identified an interface fault.
21h 100000000 Eject media O C The operation has failed:
  1. Eject the tape or magazine.
  2. Insert the tape or magazine again.
  3. Restart the operation.
Error recovery action.
22h 200000000 Download fail O W The firmware download has failed because you have tried to use the incorrect firmware for this tape drive. Obtain the correct firmware and try again. Firmware download failed.
23h 400000000 Drive humidity O W Environmental conditions inside the tape drive are outside the specified humidity range. Drive humidity limits exceeded.
24h 800000000 Drive temperature O W Environmental conditions inside the tape drive are outside the specified temperature range. Cooling problem.
25h 1000000000 Drive voltage O W The voltage supply to the tape drive is outside the specified range. Drive voltage limits exceeded.
26h 2000000000 Predictive failure O C A hardware failure of the tape drive is predicted. Call the tape drive supplier help line. Predictive failure of drive hardware.
27h 4000000000 Diagnostics required O W The tape drive may have a hardware fault. Run extended diagnostics to verify and diagnose the problem. Check the tape drive users manual for device specific instructions on running extended diagnostic tests. The drive may have a hardware fault that may be identified by extended diagnostics (i.e., SEND DIAGNOSTIC command).
28h - 2Eh Obsolete
2Fh - 31h Rsvd
32h 4000000000000 Lost statistics O W Media statistics have been lost at some time in the past. Drive or library powered on with tape loaded.
33h 8000000000000 Tape directory invalid at unload O W The tape directory on the tape cartridge just unloaded has been corrupted. File search performance will be degraded. The tape directory can be rebuilt by reading all the data. Error preventing the tape directory being updated on unload.
34h 10000000000000 Tape system area write failure O C The tape just unloaded could not write its system area successfully:
  1. Copy data to another tape cartridge.
  2. Discard the old cartridge.
Write errors while writing the system area on unload.
35h 20000000000000 Tape system area read failure O C The tape system area could not be read successfully at load time:
  1. Copy data to another tape cartridge.
Read errors while reading the system area on load.
36h 40000000000000 No start of data O C The start of data could not be found on the tape: 1. Check that you are using the correct format tape. 2. Discard the tape or return the tape to your supplier. Tape damaged, bulk erased, or incorrect format.
37h 80000000000000 Loading failure O C The operation has failed because the media cannot be loaded and threaded.
  1. Remove the cartridge, inspect it as specified in the product manual, and retry the operation.
  2. If the problem persists, call the tape drive supplier help line.
The drive is unable to load the media and thread the tape.
38h 100000000000000 Unrecoverable unload failure O C The operation has failed because the medium cannot be unloaded:
  1. Do not attempt to extract the tape cartridge.
  2. Call the tape driver supplier help line.
The drive is unable to unload the medium.
39h 200000000000000 Automation interface failure O C The tape drive has a problem with the automation interface:
  1. Check the power to the automation system.
  2. Check the cables and cable connections.
  3. Call the supplier help line if problem persists.
The drive has identified an interface fault.
3Ah 400000000000000 Firmware failure O W The tape drive has reset itself due to a detected firmware fault. If problem persists, call the supplier help line. Firmware bug.
3Bh 800000000000000 WORM Medium - Integrity Check Failed O W The tape drive has detected an inconsistency during the WORM medium integrity checks. Someone may have tampered with the cartridge. Someone has tampered with the WORM medium.
3Ch 1000000000000000 WORM Medium - Overwrite Attempted O W An attempt had been made to overwrite user data on a WORM medium:
  1. If a WORM medium was used inadvertently, replace it with a normal data medium.
  2. If a WORM medium was used intentionally:
    a) Check that the software application is compatible with the WORM medium format you are using.
    b) Check that the medium is bar-coded correctly for WORM.
The application software does not recognize the medium as WORM.
3Dh Rsvd
3Eh Rsvd
3Fh Rsvd
40h Rsvd

Download

Just the user-mode utilities packaged as an RPM: vtl-0.15-0.i686.rpm (~50Kbytes)

Source code for user-mode utilities + kernel module: vtl-0.15-0.src.rpm (~100Kbytes)


Please email Mark Harvey any questions and/or corrections.

$Id: index.html,v 1.9.2.1 2008-03-06 07:28:54 markh Exp $

================================================ FILE: etc/.gitignore ================================================ mhvtl.conf device.conf library_contents.10 library_contents.30 vtllibrary@.service vtltape@.service mhvtl-load-modules.service generate_device_conf generate_library_contents ================================================ FILE: etc/Makefile ================================================ # # etc/Makefile # CURDIR = "../" include ../config.mk MHVTL_CONF_FILE = mhvtl.conf DEVICE_CONF_FILE = device.conf LIB_CONTENTS_FILES = library_contents.10 library_contents.30 CONFIGS = $(MHVTL_CONF_FILE) $(DEVICE_CONF_FILE) $(LIB_CONTENTS_FILES) GENERATE_DEVICE_CONF = generate_device_conf GENERATE_LIB_CONTENTS = generate_library_contents GENERATORS = $(GENERATE_DEVICE_CONF) $(GENERATE_LIB_CONTENTS) SERVICES = $(patsubst %.in,%,$(wildcard *.service*)) MHVTL_TARGET = mhvtl.target all: $(SERVICES) $(CONFIGS) %: %.in sed -e s'/@CONF_PATH@/$(CONFIG_PATH)/' \ -e s'/@HOME_PATH@/$(HOME_PATH)/' $< > $@ $(GENERATE_DEVICE_CONF): $(GENERATE_DEVICE_CONF).in sed -e s'/@CONF_PATH@/$(CONFIG_PATH)/' \ -e s'/@HOME_PATH@/$(HOME_PATH)/' $< > $@ chmod 755 $@ $(GENERATE_LIB_CONTENTS): $(GENERATE_LIB_CONTENTS).in sed -e s'/@CONF_PATH@/$(CONFIG_PATH)/' \ -e s'/@HOME_PATH@/$(HOME_PATH)/' $< > $@ chmod 755 $@ $(DEVICE_CONF_FILE): $(GENERATE_DEVICE_CONF) bash ./$(GENERATE_DEVICE_CONF) --force --home-dir=$(MHVTL_HOME_PATH) --override-home $(LIB_CONTENTS_FILES): $(GENERATE_LIB_CONTENTS) $(DEVICE_CONF_FILE) bash ./$(GENERATE_LIB_CONTENTS) --force --config=. .PHONY: distclean distclean: clean .PHONY: clean clean: $(RM) $(GENERATED_FILES) $(RM) $(DEVICE_CONF_FILE) $(LIB_CONTENTS_FILES) $(DESTDIR)$(SYSTEMD_SERVICE_DIR): install -d -m 755 $@ $(DESTDIR)$(MHVTL_CONFIG_PATH): install -d -m 755 $@ # Installing services $(DESTDIR)$(SYSTEMD_SERVICE_DIR)/%.service: %.service | $(DESTDIR)$(SYSTEMD_SERVICE_DIR) install -m 644 $< $@ semanage fcontext -a -t systemd_unit_file_t \'$@\' $(DESTDIR)$(SYSTEMD_SERVICE_DIR)/%.target: %.target | $(DESTDIR)$(SYSTEMD_SERVICE_DIR) install -m 644 $< $@ semanage fcontext -a -t systemd_unit_file_t \'$@\' # Installing generator of config files $(DESTDIR)/usr/bin/generate_%: generate_% install -m 755 $< $@ # save backup copies of config files $(DESTDIR)$(MHVTL_CONFIG_PATH)/%: % | $(DESTDIR)$(MHVTL_CONFIG_PATH) install -m 644 -b -S .orig $< $@ .PHONY: install install: all \ $(addprefix $(DESTDIR)$(SYSTEMD_SERVICE_DIR)/,$(SERVICES) $(MHVTL_TARGET)) \ $(addprefix $(DESTDIR)$(MHVTL_CONFIG_PATH)/,$(CONFIGS)) \ $(addprefix $(DESTDIR)/usr/bin/,$(GENERATORS)) restorecon -R -v $(DESTDIR)$(SYSTEMD_SERVICE_DIR) ================================================ FILE: etc/generate_device_conf.in ================================================ #!/bin/bash # # generate_device_conf -- generate device.conf output to stdout # # This script generates the device.conf file, # and is used at installation time. This is done so that # administrators/users can modify or copy this script and # change their tape device configuration. # DEVICE_CONF='device.conf' PROG_NAME="$0" MHVTL_HOME_PATH=@HOME_PATH@ DEST_DIR='.' FORCE='' OVERRIDE='' usage() { echo "Usage: $PROG_NAME: [OPTIONS] -- generate a $DEVICE_CONF file" echo "where OPTIONS are from:" echo " [-h|--help] -- print this message and exit" echo " [-H|--home-dir MHVTL_HOME_PATH]" echo " -- home path [default $MHVTL_HOME_PATH]" echo " [-D|--dest-dir DIR] -- destination dir [default $DEST_DIR]" echo " [-f|--force] -- overwrite files if present" echo " [-o|--override-home] -- override requirement that home dir exists" echo "" echo "To create a new $DEVICE_CONF file, edit this script and run it again" } # # generate the static comments at the start of the file # generate_static_comments() { if (( $# != 0 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi cat << CONF_START VERSION: 5 # VPD page format: # ... # NAA format is an 8 hex byte value seperated by ':' # Note: NAA is part of inquiry VPD 0x83 # # Each 'record' is separated by one (or more) blank lines. # Each 'record' starts at column 1 # Serial num max len is 10. # Compression: factor X enabled 0|1 # Where X is zlib compression factor 1 = Fastest compression # 9 = Best compression # enabled 0 == off, 1 == on # # fifo: /var/tmp/mhvtl # If enabled, data must be read from fifo, otherwise daemon will block # trying to write. # e.g. cat /var/tmp/mhvtl (in another terminal) CONF_START } # add_library(DevID, Channel, Target, Lun, Vend, Prod, ProdRev, S/No) add_library() { if (( $# != 8 )) ; then echo "internal error: expected 8 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 VENDOR_ID="$5" PROD_ID="$6" UNIT_SN="$8" printf "Library: %02d CHANNEL: %02d TARGET: %02d LUN: %02d\n" \ $ID $CH $TARGET $LUN echo " Vendor identification: $VENDOR_ID" echo " Product identification: $PROD_ID" echo " Unit serial number: $UNIT_SN" printf " NAA: %02d:22:33:44:ab:%02d:%02d:%02d\n" \ $ID $CH $TARGET $LUN echo " Home directory: $MHVTL_HOME_PATH" echo " PERSIST: False" echo " Backoff: 400" echo "# fifo: /var/tmp/mhvtl" echo "" } # add_drive(DevID, Channel, Target, Lun, Vend, Prod, ProdRev, S/No, LibID, Slot, Density) add_drive() { if (( $# != 11 )) ; then echo "internal error: expected 11 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 VENDORID=$5 PRODUCTID=$6 PRODUCTREV=$7 UNITSERNO=$8 LIB=$9 # get arg 10 & 11 shift 9 SLOT=$1 DENSITY=$2 printf "Drive: %02d CHANNEL: %02d TARGET: %02d LUN: %02d\n" \ $ID $CH $TARGET $LUN printf " Library ID: %02d Slot: %02d\n" \ $LIB $SLOT echo " Vendor identification: $VENDORID" echo " Product identification: $PRODUCTID" echo " Product revision level: $PRODUCTREV" echo " Unit serial number: $UNITSERNO" printf " NAA: %02d:22:33:44:ab:%02d:%02d:%02d\n" \ $LIB $CH $TARGET $LUN echo " Compression: factor 1 enabled 1" echo " Compression type: lzo" echo " Backoff: 400" echo "# fifo: /var/tmp/mhvtl" echo "" } # add_ibm_ultrium_drive(DevID, Channel, Target, Lun, S/No, LibID, Slot) add_ibm_ultrium_drive() { if (( $# != 7 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 UNITSERNO=$5 LIB=$6 SLOT=$7 VENDORID="IBM" PRODUCTID="ULT3580-TD1" PRODUCTREV="550V" DENSITY="42" add_drive $ID $CH $TARGET $LUN $VENDORID $PRODUCTID $PRODUCTREV $UNITSERNO $LIB $SLOT $DENSITY } # add_ibm_ultrium_2_drive(DevID, Channel, Target, Lun, S/No, LibID, Slot) add_ibm_ultrium_2_drive() { if (( $# != 7 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 UNITSERNO=$5 LIB=$6 SLOT=$7 VENDORID="IBM" PRODUCTID="ULT3580-TD2" PRODUCTREV="550V" DENSITY="44" add_drive $ID $CH $TARGET $LUN $VENDORID $PRODUCTID $PRODUCTREV $UNITSERNO $LIB $SLOT $DENSITY } # add_ibm_ultrium_3_drive(DevID, Channel, Target, Lun, S/No, LibID, Slot) add_ibm_ultrium_3_drive() { if (( $# != 7 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 UNITSERNO=$5 LIB=$6 SLOT=$7 VENDORID="IBM" PRODUCTID="ULT3580-TD3" PRODUCTREV="550V" DENSITY="46" add_drive $ID $CH $TARGET $LUN $VENDORID $PRODUCTID $PRODUCTREV $UNITSERNO $LIB $SLOT $DENSITY } # add_ibm_ultrium_4_drive(DevID, Channel, Target, Lun, S/No, LibID, Slot) add_ibm_ultrium_4_drive() { if (( $# != 7 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 UNITSERNO=$5 LIB=$6 SLOT=$7 VENDORID="IBM" PRODUCTID="ULT3580-TD4" PRODUCTREV="550V" DENSITY="48" add_drive $ID $CH $TARGET $LUN $VENDORID $PRODUCTID $PRODUCTREV $UNITSERNO $LIB $SLOT $DENSITY } # add_ibm_ultrium_5_drive(DevID, Channel, Target, Lun, S/No, LibID, Slot) add_ibm_ultrium_5_drive() { if (( $# != 7 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 UNITSERNO=$5 LIB=$6 SLOT=$7 VENDORID="IBM" PRODUCTID="ULT3580-TD5" PRODUCTREV="550V" DENSITY="50" add_drive $ID $CH $TARGET $LUN $VENDORID $PRODUCTID $PRODUCTREV $UNITSERNO $LIB $SLOT $DENSITY } # add_ibm_ultrium_6_drive(DevID, Channel, Target, Lun, S/No, LibID, Slot) add_ibm_ultrium_6_drive() { if (( $# != 7 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 UNITSERNO=$5 LIB=$6 SLOT=$7 VENDORID="IBM" PRODUCTID="ULT3580-TD6" #### According to IBM LTO SCSI reference: REV is built of # "ABCD" - so 2160 decodes as Year 2022 (2), Jan (1), Day (6th), Version '0' # A = last digit char of year # B = Month, 1 - 9, A, B, C # C = Day, 1-9, A-V # D = Version PRODUCTREV="2160" DENSITY="50" add_drive $ID $CH $TARGET $LUN $VENDORID $PRODUCTID $PRODUCTREV $UNITSERNO $LIB $SLOT $DENSITY } # add_ibm_ultrium_7_drive(DevID, Channel, Target, Lun, S/No, LibID, Slot) add_ibm_ultrium_7_drive() { if (( $# != 7 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 UNITSERNO=$5 LIB=$6 SLOT=$7 VENDORID="IBM" PRODUCTID="ULT3580-TD7" #### According to IBM LTO SCSI reference: REV is built of # "ABCD" - so 2160 decodes as Year 2022 (2), Jan (1), Day (6th), Version '0' # A = last digit char of year # B = Month, 1 - 9, A, B, C # C = Day, 1-9, A-V # D = Version PRODUCTREV="2160" DENSITY="50" add_drive $ID $CH $TARGET $LUN $VENDORID $PRODUCTID $PRODUCTREV $UNITSERNO $LIB $SLOT $DENSITY } # add_ibm_ultrium_8_drive(DevID, Channel, Target, Lun, S/No, LibID, Slot) add_ibm_ultrium_8_drive() { if (( $# != 7 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 UNITSERNO=$5 LIB=$6 SLOT=$7 VENDORID="IBM" PRODUCTID="ULT3580-TD8" #### According to IBM LTO SCSI reference: REV is built of # "ABCD" - so 2160 decodes as Year 2022 (2), Jan (1), Day (6th), Version '0' # A = last digit char of year # B = Month, 1 - 9, A, B, C # C = Day, 1-9, A-V # D = Version PRODUCTREV="2160" DENSITY="50" add_drive $ID $CH $TARGET $LUN $VENDORID $PRODUCTID $PRODUCTREV $UNITSERNO $LIB $SLOT $DENSITY } # add_stk_t10kb_drive(DevID, Channel, Target, Lun, S/No, LibID, Slot) add_stk_t10kb_drive() { if (( $# != 7 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi ID=$1 CH=$2 TARGET=$3 LUN=$4 UNITSERNO=$5 LIB=$6 SLOT=$7 VENDORID="STK" PRODUCTID="T10000B" PRODUCTREV="550V" DENSITY="50" add_drive $ID $CH $TARGET $LUN $VENDORID $PRODUCTID $PRODUCTREV $UNITSERNO $LIB $SLOT $DENSITY } # add_library_contents_10(void) -- add the robot "10" with 4 drives add_library_contents_10() { if (( $# != 0 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi # index channel target LUN Vendor ProdID ProdRev S/No add_library 10 0 0 0 "STK" "L700" "550V" "XYZZY_A" # index channel target LUN S/No Lib# Slot add_ibm_ultrium_8_drive 11 0 1 0 "XYZZY_A1" 10 1 add_ibm_ultrium_8_drive 12 0 2 0 "XYZZY_A2" 10 2 add_ibm_ultrium_6_drive 13 0 3 0 "XYZZY_A3" 10 3 add_ibm_ultrium_6_drive 14 0 4 0 "XYZZY_A4" 10 4 } # add_library_contents_30(void) -- add the robot "30" with 4 drives add_library_contents_30() { if (( $# != 0 )) ; then echo "internal error: expected 0 arguments, got $#" 1>&2 exit 1 fi # index channel target LUN Vendor ProdID ProdRev S/No add_library 30 0 8 0 "STK" "L80" "550V" "XYZZY_B" # index channel target LUN S/No Lib# Slot add_stk_t10kb_drive 31 0 9 0 "XYZZY_B1" 30 1 add_stk_t10kb_drive 32 0 10 0 "XYZZY_B2" 30 2 add_stk_t10kb_drive 33 0 11 0 "XYZZY_B3" 30 3 add_stk_t10kb_drive 34 0 12 0 "XYZZY_B4" 30 4 } # # start of script # TEMP=$(getopt -o 'hH:D:fo' --long 'help,home-dir:,dest-dir:,force,override-home' -n "$PROG_NAME" -- "$@") if [[ $? -ne 0 ]] ; then usage exit 1 fi eval set - "$TEMP" unset TEMP while true; do case "$1" in '-h'|'--help') usage exit 0 ;; '-H'|'--home-dir') MHVTL_HOME_PATH="$2" shift 2 continue ;; '-D'|'--dest-dir') DEST_DIR="$2" shift 2 continue ;; '-f'|'--force') FORCE='1' shift continue ;; '-o'|'--override-home') OVERRIDE='1' shift continue ;; '--') shift break ;; *) echo "internal error: unknown arg: $1" 1>&2 exit 1 esac done # should be no more arguments if [[ $# -gt 0 ]] ; then echo "error: too many arguments" usage exit 1 fi # verify a home path make sense # (for use in the device.conf we generate) if [[ ! -d "$MHVTL_HOME_PATH" ]] ; then if [[ -z "$OVERRIDE" ]] ; then echo "error: $MHVTL_HOME_PATH not a directory" 1>&2 usage exit 1 fi fi # ensure we do not step on earlier config file if [[ -r $DEST_DIR/$DEVICE_CONF ]] ; then if [[ -z "$FORCE" ]] ; then echo "error: already exists: $DEST_DIR/$DEVICE_CONF" 1>&2 exit 1 fi fi echo '===>' "Generating: $DEST_DIR/$DEVICE_CONF ..." ( generate_static_comments add_library_contents_10 add_library_contents_30 ) > $DEST_DIR/$DEVICE_CONF ================================================ FILE: etc/generate_library_contents.in ================================================ #!/bin/bash # # generate_library_contents -- generate library_contents.* files # # This script generates the library_contents. (where # number defaults to 10 and 30) files in our configuration # directory. This is normally only done at installation time, # but having this script makes it possible for administrators/ # users to customize the configuration if desired. # DEVICE_CONF='device.conf' PROG_NAME="$0" FORCE='' MHVTL_CONFIG_PATH='@CONF_PATH@' DEST_DIR='.' usage() { echo "Usage: $PROG_NAME: [OPTIONS] -- generate" 'library_contents.*' "files from $DEVICE_CONF" echo "where OPTIONS are from:" echo " [-h|--help] -- print this message and exit" echo " [-C|--config-dir CONFIG_DIR] -- specificy config dir [default $MHVTL_CONFIG_PATH]" echo " [-D|--dest-dir DIRECTORY] -- specificy destination dir [default $DEST_DIR]" echo " [-f|--force] -- overwrite files if present" } create_library_file() { if (( $# != 1 )) ; then echo "create_library_file internal error: expected 1 argument, got $#" 1>&2 exit 1 fi LIBID=$1 echo "VERSION: 2" echo "" # Count number of drives in this library DRV_COUNT=$(grep -c "Library ID: $LIBID" $MHVTL_CONFIG_PATH/$DEVICE_CONF) # Add a 'Drive X:' for each drive for a in $(seq 1 $DRV_COUNT) ; do printf "Drive %d:\n" $a done # create the static (non-changing, comment) part of the file cat <&2 exit 1 esac done # should be no more arguments if [[ $# -gt 0 ]] ; then echo "error: too many arguments" 1>&2 usage exit 1 fi if [[ ! -d $MHVTL_CONFIG_PATH ]] ; then echo "error: mhvtl config path not present: $MHVTL_CONFIG_PATH" 1>&2 usage exit 1 fi if [[ ! -r $MHVTL_CONFIG_PATH/$DEVICE_CONF ]] ; then echo "error: need device config file: $MHVTL_CONFIG_PATH/$DEVICE_CONF" 1>&2 usage exit 1 fi # get a list of our libraries LIB_ID_LIST=$(awk '/^Library:/ {print $2}' $MHVTL_CONFIG_PATH/$DEVICE_CONF) for id in $LIB_ID_LIST ; do ID_FILE="$DEST_DIR/library_contents.$id" if [[ -r $ID_FILE ]] ; then if [[ -z "$FORCE" ]] ; then echo "error: already exists: $ID_FILE" 1>&2 exit 1 fi fi echo '===>' "Generating: $ID_FILE ..." create_library_file $id > $ID_FILE done exit 0 ================================================ FILE: etc/library_contents.sample ================================================ # Define how many tape drives you want in the vtl.. Drive 1: Drive 2: Drive 3: Drive 4: Drive 5: Drive 6: Drive 7: Drive 8: # Place holder for the robotic arm. Not really used. Picker 1: # Media Access Port # (mailslots, Cartridge Access Port, ) # Again, define how many MAPs this vtl will contain. MAP 1: MAP 2: MAP 3: MAP 4: # And the 'big' on, define your media and in which slot contains media. # When the rc script is started, all media listed here will be created # using the default media capacity. Slot 1: ULT001L1 Slot 2: ULT002L1 Slot 3: ULT003L1 Slot 4: ULT004L1 Slot 5: ULT005L1 Slot 6: ULT006L1 Slot 7: ULT007L1 Slot 8: ULT008L1 Slot 9: ULT009L1 Slot 10: ULT010L1 Slot 11: SDLT01L1 Slot 12: SDLT02L1 Slot 13: SDLT03L1 Slot 14: SDLT04L1 Slot 15: SDLT05L1 Slot 16: SDLT06L1 Slot 17: SDLT07L1 Slot 18: SDLT08L1 Slot 19: SDLT09L1 Slot 20: SDLT10L1 Slot 21: Slot 22: Slot 23: Slot 24: Slot 25: Slot 26: Slot 27: Slot 28: Slot 29: Slot 30: Slot 31: CLN001L1 Slot 32: CLN002L1 ================================================ FILE: etc/mhvtl-load-modules.service.in ================================================ [Unit] Description=Load mhvtl modules Documentation=man:man:vtltape(1) man:man:vtllibrary(1) Before=mhvtl.target PartOf=mhvtl.target Conflicts=shutdown.target Before=shutdown.target [Service] Type=oneshot RemainAfterExit=yes Environment=VTL_DEBUG="0" EnvironmentFile=-@CONF_PATH@/mhvtl.conf ExecStart=/sbin/modprobe mhvtl opts=${VTL_DEBUG} ExecStart=/sbin/modprobe sg [Install] WantedBy=mhvtl.target ================================================ FILE: etc/mhvtl.conf.in ================================================ # # mhvtl.conf # # Home directory for tape emulation files MHVTL_HOME_PATH=@HOME_PATH@ # Default media capacity (500 M) CAPACITY=500 # Set default verbosity [0|1|2|3] VERBOSE=1 # Set kernel module debugging [0|1] VTL_DEBUG=0 # vtltape and vtllibrary debugging [Blank or -d] #DAEMON_DEBUG=-d ================================================ FILE: etc/mhvtl.target ================================================ [Unit] Description=mhvtl service allowing to start/stop all vtltape@.service and vtllibrary@.service instances at once Documentation=man:man:vtltape(1) man:man:vtllibrary(1) [Install] WantedBy=multi-user.target ================================================ FILE: etc/vtllibrary@.service.in ================================================ [Unit] Documentation=man:vtllibrary(1) man:vtlcmd(1) Description=Robot Library Daemon for Virtual Tape & Robot Library Before=mhvtl.target Requires=mhvtl-load-modules.service After=mhvtl-load-modules.service PartOf=mhvtl.target [Service] Type=simple Environment=VERBOSE=0 Environment=DAEMON_DEBUG= EnvironmentFile=-@CONF_PATH@/mhvtl.conf ExecStart=/usr/bin/vtllibrary -F -q%i -v${VERBOSE} ${DAEMON_DEBUG} ExecStop=/usr/bin/vtlcmd %i exit KillMode=none ExecReload=/usr/bin/kill -HUP $MAINPID [Install] WantedBy=mhvtl.target ================================================ FILE: etc/vtltape@.service.in ================================================ [Unit] Documentation=man:vtltape(1) man:vtlcmd(1) Description=Tape Daemon for Virtual Tape & Robot Library Before=mhvtl.target Requires=mhvtl-load-modules.service After=mhvtl-load-modules.service PartOf=mhvtl.target [Service] Type=simple Environment=VERBOSE=0 Environment=DAEMON_DEBUG= EnvironmentFile=-@CONF_PATH@/mhvtl.conf ExecStart=/usr/bin/vtltape -F -q%i -v${VERBOSE} ${DAEMON_DEBUG} ExecStop=/usr/bin/vtlcmd %i exit KillMode=none [Install] WantedBy=mhvtl.target ================================================ FILE: include/common/logging.h ================================================ /* * logging macros * * Copyright (C) 2005-2025 Mark Harvey markh794@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #ifndef _LOGGING_H_ #define _LOGGING_H_ #include #include #define MHVTL_OPT_NOISE 0x11 #ifdef MHVTL_DEBUG extern char mhvtl_driver_name[]; extern uint8_t debug; extern uint8_t verbose; #define MHVTL_DBG_NO_FUNC(lvl, format, arg...) \ do { \ if (debug) \ printf("%s: " format "\n", \ mhvtl_driver_name, ##arg); \ else if ((verbose) >= (lvl)) \ syslog(LOG_DAEMON | LOG_INFO, format, ##arg); \ } while (0) #define MHVTL_ERR(format, arg...) \ do { \ if (debug) { \ printf("%s: ERROR: %s(): " format "\n", \ mhvtl_driver_name, __func__, ##arg); \ fflush(NULL); \ } else { \ syslog(LOG_DAEMON | LOG_ERR, "ERROR: %s(): line: %d," format, \ __func__, __LINE__, ##arg); \ } \ } while (0) #define MHVTL_LOG(format, arg...) \ do { \ if (debug) { \ printf("%s: %s(): " format "\n", \ mhvtl_driver_name, __func__, ##arg); \ fflush(NULL); \ } else { \ syslog(LOG_DAEMON | LOG_ERR, "%s(): " format, \ __func__, ##arg); \ } \ } while (0) #define MHVTL_DBG(lvl, format, arg...) \ do { \ if (debug) \ printf("%s: %s(): " format "\n", \ mhvtl_driver_name, __func__, ##arg); \ else if ((verbose) >= (lvl)) \ syslog(LOG_DAEMON | LOG_INFO, "%s(): " format, \ __func__, ##arg); \ } while (0) #define MHVTL_DBG_PRT_CDB(lvl, cmd) \ do { \ if (debug) { \ mhvtl_prt_cdb((lvl), (cmd)); \ } else if ((verbose) >= (lvl)) { \ mhvtl_prt_cdb((lvl), (cmd)); \ } \ } while (0) #else #define MHVTL_DBG(lvl, s...) #define MHVTL_DBG_NO_FUNC(lvl, s...) #define MHVTL_DBG_PRT_CDB(lvl, cmd) #define MHVTL_ERR(format, arg...) \ do { \ syslog(LOG_DAEMON | LOG_ERR, "ERROR: %s(): " format, \ __func__, ##arg); \ } while (0) #define MHVTL_LOG(format, arg...) \ do { \ syslog(LOG_DAEMON | LOG_ERR, "%s(): " format, \ __func__, ##arg); \ } while (0) #endif /* MHVTL_DEBUG */ #endif /* _LOGGING_H_ */ ================================================ FILE: include/common/mhvtl_scsi.h ================================================ /* * The userspace tape/library header file for the vtl virtual * tape kernel module. * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _SCSI_SCSI_H #define _SCSI_SCSI_H /* * Device types */ #define TYPE_DISK 0x00 #define TYPE_TAPE 0x01 #define TYPE_PROCESSOR 0x03 #define TYPE_WORM 0x04 #define TYPE_ROM 0x05 #define TYPE_SCANNER 0x06 #define TYPE_MOD 0x07 #define TYPE_MEDIUM_CHANGER 0x08 #define TYPE_ENCLOSURE 0x0d #define TYPE_NO_LUN 0x7f /* * SCSI Architectual model (SAM) Status codes. */ #define SAM_STAT_GOOD 0x00 #define SAM_STAT_CHECK_CONDITION 0x02 #define SAM_STAT_CONDITION_MET 0x04 #define SAM_STAT_BUSY 0x08 #define SAM_STAT_INTERMEDIATE 0x10 #define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 #define SAM_STAT_RESERVATION_CONFLICT 0x18 #define SAM_STAT_COMMAND_TERMINATED 0x22 #define SAM_STAT_QUEUE_FULL 0x28 /* * SENSE keys */ #define NO_SENSE 0x00 #define RECOVERED_ERROR 0x01 #define NOT_READY 0x02 #define MEDIUM_ERROR 0x03 #define HARDWARE_ERROR 0x04 #define ILLEGAL_REQUEST 0x05 #define UNIT_ATTENTION 0x06 #define DATA_PROTECT 0x07 #define BLANK_CHECK 0x08 #define VOLUME_OVERFLOW 0x0d /* * OP Codes */ #define ALLOW_MEDIUM_REMOVAL 0x1e #define ALLOW_OVERWRITE 0x82 #define ACCESS_CONTROL_IN 0x86 #define ACCESS_CONTROL_OUT 0x87 #define EXTENDED_COPY 0x83 #define A3_SA 0xa3 #define A4_SA 0xa4 #define ERASE_6 0x19 #define ERASE_16 0x93 #define FORMAT_UNIT 0x04 #define EXCHANGE_MEDIUM 0xa6 #define INITIALIZE_ELEMENT_STATUS 0x07 #define INITIALIZE_ELEMENT_STATUS_WITH_RANGE 0xE7 #define INQUIRY 0x12 #define LOAD_DISPLAY 0x06 /* STK T10000 specific */ #define LOCATE_10 0x2b #define LOCATE_16 0x92 #define MODE_SENSE 0x1a #define MODE_SENSE_10 0x5a #define MODE_SELECT 0x15 #define MODE_SELECT_10 0x55 #define MOVE_MEDIUM 0xa5 #define LOG_SELECT 0x4c #define LOG_SENSE 0x4d #define PERSISTENT_RESERVE_IN 0x5e #define PERSISTENT_RESERVE_OUT 0x5f #define READ_6 0x08 #define READ_10 0x28 #define READ_12 0xa8 #define READ_16 0x88 #define READ_ATTRIBUTE 0x8c #define READ_BLOCK_LIMITS 0x05 #define READ_BUFFER 0x3c #define READ_ELEMENT_STATUS 0xb8 #define READ_MEDIA_SERIAL_NUMBER 0xab #define READ_POSITION 0x34 #define READ_REVERSE_6 0x0f #define READ_REVERSE_16 0x81 #define RECEIVE_DIAGNOSTIC 0x1c #define RECOVER_BUFFERED_DATA 0x14 #define RELEASE 0x17 #define RELEASE_10 0x57 #define REPORT_DENSITY 0x44 #define REPORT_ELEMENT_INFORMATION 0x9e #define REPORT_LUNS 0xa0 #define REQUEST_SENSE 0x03 #define RESERVE 0x16 #define RESERVE_10 0x56 #define REZERO_UNIT 0x01 #define SECURITY_PROTOCOL_IN 0xa2 #define SECURITY_PROTOCOL_OUT 0xb5 #define SEND_DIAGNOSTIC 0x1d #define SET_CAPACITY 0x0b #define SPACE 0x11 #define SPACE_16 0x91 #define START_STOP 0x1b #define TEST_UNIT_READY 0x00 #define VERIFY_6 0x13 #define VERIFY_16 0x8f #define WRITE_6 0x0a #define WRITE_10 0x2a #define WRITE_12 0xaa #define WRITE_16 0x8a #define WRITE_ATTRIBUTE 0x8d #define WRITE_BUFFER 0x3b #define WRITE_FILEMARKS 0x10 #define WRITE_FILEMARKS_16 0x80 /* Service codes */ #define FORCED_EJECT 0x1f #define MANAGEMENT_PROTOCOL_IN 0x10 #define MANAGEMENT_PROTOCOL_OUT 0x10 #define CHANGE_ALIASES 0x0b #define REPORT_ALIASES 0x0b #define REPORT_SUPPORTED_OPCODES 0x0c #define REPORT_TIMESTAMP 0x0f #define SET_TIMESTAMP 0x0f /* No Sense Errors */ #define NO_ADDITIONAL_SENSE 0x0000 #define E_MARK 0x0001 #define E_EOM 0x0002 #define E_BOM 0x0004 #define E_END_OF_DATA 0x0005 #define E_PROGRAMMABLE_EARLY_WARNING 0x0007 #define E_OP_IN_PROGRESS 0x0016 #define E_DRIVE_REQUIRES_CLEANING 0x8282 /* Recovered Errors */ #define E_WRITE_ERROR 0x0c00 #define E_READ_ERROR 0x1100 #define E_RECOVERED_WITH_RETRYS 0x1701 #define E_MEDIA_LOAD_EJECT_ERROR 0x5300 #define E_FAILURE_PREDICTION 0x5d00 /* Not ready */ #define E_CAUSE_NOT_REPORTABLE 0x0400 #define E_BECOMING_READY 0x0401 #define E_INITIALIZING_REQUIRED 0x0402 #define E_OFFLINE 0x0412 #define E_EARLY_WARNING_EOM 0x2c98 #define E_CLEANING_CART_INSTALLED 0x3003 #define E_CLEANING_FAILURE 0x3007 #define E_MEDIUM_NOT_PRESENT 0x3a00 #define E_MAP_OPEN 0x3a02 #define E_LOGICAL_UNIT_NOT_CONFIG 0x3e00 /* Medium Errors */ #define E_WRITE_ERROR 0x0c00 #define E_UNRECOVERED_READ 0x1100 #define E_RECORDED_ENTITY_NOT_FOUND 0x1400 #define E_UNKNOWN_FORMAT 0x3001 #define E_IMCOMPATIBLE_FORMAT 0x3002 #define E_MEDIUM_FMT_CORRUPT 0x3100 #define E_SEQUENTIAL_POSITION_ERR 0x3b00 #define E_WRITE_APPEND_ERR 0x5000 #define E_CARTRIDGE_FAULT 0x5200 /* Hardware Failure */ #define E_COMPRESSION_CHECK 0x0c04 #define E_LOGICAL_BLOCK_GUARD_FAILED 0x1001 #define E_DECOMPRESSION_CRC 0x110d #define E_MANUAL_INTERVENTION_REQ 0x0403 #define E_HARDWARE_FAILURE 0x4000 #define E_INTERNAL_TARGET_FAILURE 0x4400 #define E_ERASE_FAILURE 0x5100 /* Illegal Request */ #define E_PARAMETER_LIST_LENGTH_ERR 0x1a00 #define E_INVALID_OP_CODE 0x2000 #define E_ILLEGAL_COMMAND_WHEN_NOT_APPEND 0x200C #define E_INVALID_ELEMENT_ADDR 0x2101 #define E_INVALID_FIELD_IN_CDB 0x2400 #define E_LUN_NOT_SUPPORTED 0x2500 #define E_INVALID_FIELD_IN_PARMS 0x2600 #define E_MEDIUM_INCOMPATIBLE 0x3000 #define E_SAVING_PARMS_UNSUP 0x3900 #define E_SEQUENTIAL_POSITIONING_ERROR 0x3b00 #define E_MEDIUM_DEST_FULL 0x3b0d #define E_MEDIUM_SRC_EMPTY 0x3b0e #define E_POSITION_PAST_BOM 0x3b0c #define E_MEDIUM_REMOVAL_PREVENTED 0x5302 #define E_BAD_MICROCODE_DETECTED 0x8283 /* Unit Attention */ #define E_NOT_READY_TO_TRANSITION 0x2800 #define E_IMPORT_ELEMENT_ACCESSED 0x2801 #define E_POWERON_RESET 0x2900 #define E_I_T_NEXUS_LOSS 0x2907 #define E_MODE_PARAMETERS_CHANGED 0x2a01 #define E_MICROCODE_DOWNLOADED 0x3f01 #define E_FAILURE_PREDICTION_FALSE 0x5dff #define E_MODE_PARAMETERS_CHANGED 0x2a01 #define E_TIMESTAMP_CHANGED 0x2a10 #define E_INQUIRY_DATA_HAS_CHANGED 0x3f03 /* Data Protect */ #define E_WRITE_PROTECT 0x2700 #define E_MEDIUM_OVERWRITE_ATTEMPT 0x300c #define E_UNABLE_TO_DECRYPT 0x7401 #define E_UNENCRYPTED_DATA 0x7402 #define E_INCORRECT_KEY 0x7403 /* Suppress Incorrect Length Indicator */ #define SILI 0x2 /* Fixed block format */ #define FIXED_BLOCK 0x1 /* Sense Data format bits & pieces */ /* Incorrect Length Indicator */ #define SD_CURRENT_INFORMATION_FIXED 0x70 #define SD_VALID 0x80 #define SD_FILEMARK 0x80 #define SD_EOM 0x40 #define SD_ILI 0x20 /* Sense - field pointer sense key specific data - SPC4 - 4.5.2.4 */ #define SKSV 0x80 #define CD 0x40 #define BPV 0x08 /* MODE PAGE */ #define MODE_RW_ERROR_RECOVER 0x01 #define MODE_DISCONNECT_RECONNECT 0x02 #define MODE_CONTROL 0x0a #define MODE_DATA_COMPRESSION 0x0f #define MODE_DEVICE_CONFIGURATION 0x10 #define MODE_MEDIUM_PARTITION 0x11 #define MODE_POWER_CONDITION 0x1a #define MODE_INFORMATION_EXCEPTION 0x1c #define MODE_MEDIUM_CONFIGURATION 0x1d #define MODE_ELEMENT_ADDRESS 0x1d #define MODE_TRANSPORT_GEOMETRY 0x1e #define MODE_DEVICE_CAPABILITIES 0x1f #define MODE_VENDOR_SPECIFIC_24H 0x24 #define MODE_VENDOR_SPECIFIC_25H 0x25 #define MODE_BEHAVIOR_CONFIGURATION 0x2f #define MODE_ENCRYPTION_MODE 0x30 #define MODE_AIT_DEVICE_CONFIGURATION 0x31 #define TA_NONE 0x00 #define TA_READ 0x01 #define TA_WRITE 0x02 #define TA_HARD 0x04 #define TA_MEDIA 0x08 #define TA_READ_FAILURE 0x10 #define TA_WRITE_FAILURE 0X20 #define TA_MEDIA_EOL 0x40 #define TA_NOT_DATA_GRADE 0X80 #define TA_WRITE_PROTECT 0X100 #define TA_DRIVE_IN_USE 0x200 #define TA_CLEANING_MEDIA 0x400 #define TA_MEDIA_NOT_SUPPORTED 0x800 #define TA_RECOV_CART_FAILURE 0x1000 #define TA_UNRECOV_CART_FAILURE 0x2000 #define TA_MEM_CHIP_FAILURE 0x4000 #define TA_TAPE_UNMOUNTED 0x8000 #define TA_MEDIA_RO 0x10000 #define TA_MEDIA_DIR_CORRUPT 0x20000 #define TA_MEDIA_NEAR_EOL 0x40000 #define TA_DRIVE_NEEDS_CLEANING 0x80000 #define TA_CLEAN_PERIODIC 0x100000 #define TA_EXPIRED_CLEANING_CART 0x200000 #define TA_INVALID_CLEANING_CART 0x400000 #define TA_RETENTION_REQUESTED 0x800000 #define TA_INTERFACE_PORT_ERROR 0x1000000 #define TA_COOLING_FAN_FAILURE 0x2000000 #define TA_PSU_FAILURE 0x4000000 #define TA_PSU_POWER_CONSUMPTION 0x8000000 #define TA_PREVENTIVE_MAINTENANCE 0x10000000 #define TA_HARDWARE_A_FAULT 0x20000000 #define TA_HARDWARE_B_FAULT 0x40000000 #define TA_INTERFACE_FAULT 0x80000000 #define TA_MOUNT_FAILURE 0x100000000ull #define TA_INCORRECT_FIRMWARE 0x200000000ull #define TA_HUMIDITY_CONDITIONS 0x400000000ull #define TA_TEMPERATURE_CONDITIONS 0x800000000ull #define TA_DRIVE_VOLTAGE 0x1000000000ull #define TA_PREDICTIVE_FAILURE 0x2000000000ull #define TA_DIAGNOSTICS_REQUIRED 0x4000000000ull #define TA_LOST_STATISTICS 0x2000000000000ull #define TA_TAPE_DIR_INVALID 0x4000000000000ull #define TA_SYSTEM_AREA_WRITE_FAILURE 0x8000000000000ull #define TA_SYSTEM_AREA_READ_FAILURE 0x10000000000000ull #define TA_NO_START_OF_DATA 0x20000000000000ull #define TA_LOAD_FAILURE 0x40000000000000ull #define TA_UNRECOV_UNLOAD_FAILURE 0x80000000000000ull #define TA_AUTOMATION_FAILURE 0x100000000000000ull #define TA_FIRMWARE_FAILURE 0x200000000000000ull #define TA_WORM_INTEGRITY_FAILED 0x400000000000000ull #define TA_WORM_OVERWRITE_ATTEMPTED 0x800000000000000ull #endif /* _SCSI_SCSI_H */ ================================================ FILE: include/common/vtl_common.h ================================================ /* Common stuff for kernel and usr programs */ #ifndef VTL_COMMON_H #define VTL_COMMON_H #define SENSE_BUF_SIZE 96 /* Max cdb size */ #define MAX_COMMAND_SIZE 16 #define VTL_IDLE 0x00 #define VTL_QUEUE_CMD 0xfe /* ioctl defines */ #define VX_TAPE_ONLINE 0x80 #define VTL_POLL_AND_GET_HEADER 0x200 #define VTL_GET_DATA 0x201 #define VTL_PUT_DATA 0x203 #define VTL_REMOVE_LU 0x205 #define VENDOR_ID_LEN 8 #define PRODUCT_ID_LEN 16 #define PRODUCT_REV_LEN 4 struct mhvtl_header { unsigned long long serialNo; unsigned char cdb[MAX_COMMAND_SIZE]; unsigned char *buf; }; struct mhvtl_ds { void *data; unsigned int sz; unsigned long long serialNo; void *sense_buf; unsigned char sam_stat; }; struct mhvtl_ctl { unsigned int channel; unsigned int id; unsigned int lun; }; #if !defined(FALSE) #define FALSE 0 #endif #if !defined(TRUE) #define TRUE 1 #endif #endif /* VTL_COMMON_H */ ================================================ FILE: include/common/vtl_u.h ================================================ /* * vtl_u.h */ #define NETLINK_VTL 22 struct mhvtl_event { u32 tid; aligned_u64 sid; aligned_u64 serial_no; u32 cid; u32 state; } ================================================ FILE: include/common/vtltape_pem.h ================================================ static char *certificate = "-----BEGIN CERTIFICATE-----\n" "MIID9zCCA2CgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBtDELMAkGA1UEBhMCTlox\n" "EzARBgNVBAcTCldlbGxpbmd0b24xPDA6BgNVBAoTM1JlYWxseSBJcnJlc3BvbnNp\n" "YmxlIEF1dGhvcmlzYXRpb24gQXV0aG9yaXR5IChSSUFBKTEWMBQGA1UECxMNQ2Vy\n" "dC1zdGFtcGluZzEZMBcGA1UEAxMQSmFja292IGFsLVRyYWRlczEfMB0GCSqGSIb3\n" "DQEJARYQbm9uZUBmYWtlLmRvbWFpbjAeFw0wMjAxMTYwNTA5NTlaFw0xMjAxMTQw\n" "NTA5NTlaMIG0MQswCQYDVQQGEwJOWjETMBEGA1UEBxMKV2VsbGluZ3RvbjE8MDoG\n" "A1UEChMzUmVhbGx5IElycmVzcG9uc2libGUgQXV0aG9yaXNhdGlvbiBBdXRob3Jp\n" "dHkgKFJJQUEpMRYwFAYDVQQLEw1DZXJ0LXN0YW1waW5nMRkwFwYDVQQDExBKYWNr\n" "b3YgYWwtVHJhZGVzMR8wHQYJKoZIhvcNAQkBFhBub25lQGZha2UuZG9tYWluMIGf\n" "MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7QdDfFIrJn3X24hKmpkyk3TG0Ivxd\n" "K2wWmDPXq1wjr8lUTwrA6hM5Ba9N36jLieWpXhviLOWu9DBza5GmtgCuXloATKTC\n" "94xOdKHlciTVujG3wDlLDB5e710Kar84nnj6VueL1RyZ0bmP5PANa4mbGW9Tqc7J\n" "CkBTTW2y9d0SgQIDAQABo4IBFTCCAREwHQYDVR0OBBYEFEn7RXISxMzhRaHTCJ6V\n" "xCxtVT8XMIHhBgNVHSMEgdkwgdaAFEn7RXISxMzhRaHTCJ6VxCxtVT8XoYG6pIG3\n" "MIG0MQswCQYDVQQGEwJOWjETMBEGA1UEBxMKV2VsbGluZ3RvbjE8MDoGA1UEChMz\n" "UmVhbGx5IElycmVzcG9uc2libGUgQXV0aG9yaXNhdGlvbiBBdXRob3JpdHkgKFJJ\n" "QUEpMRYwFAYDVQQLEw1DZXJ0LXN0YW1waW5nMRkwFwYDVQQDExBKYWNrb3YgYWwt\n" "VHJhZGVzMR8wHQYJKoZIhvcNAQkBFhBub25lQGZha2UuZG9tYWluggEAMAwGA1Ud\n" "EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYQo95V/NY+eKxYxkhibZiUQygph+\n" "gTfgbDG20MsnH6+8//w5ArHauFCgDrf0P2VyACgq+N4pBTWFGaAaLwbjKy9HCe2E\n" "j9C91tO1CqDS4MJkDB5AP13FTkK6fP1ZCiTQranOAp3DlGWTTWsFVyW5kVfQ9diS\n" "ZOyJZ9Fit5XM2X0=\n" "-----END CERTIFICATE-----\n"; ================================================ FILE: include/mhvtl_log.h ================================================ /* * This handles any SCSI OP 'log sense / log select' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #ifndef MHVTL_LOG_H #define MHVTL_LOG_H #include #include "mhvtl_list.h" struct lu_phy_attr; typedef void (*init_pg_fn)(void *log_ptr); /* * Process the LOG_SENSE page definitions */ #define SUPPORTED_LOG_PAGES 0x00 #define BUFFER_UNDER_OVER_RUN 0x01 #define WRITE_ERROR_COUNTER 0x02 #define READ_ERROR_COUNTER 0x03 #define READ_REVERSE_ERROR_COUNTER 0x04 #define VERIFY_ERROR_COUNTER 0x05 #define NON_MEDIUM_ERROR_COUNTER 0x06 #define LAST_n_ERROR 0x07 #define FORMAT_STATUS 0x08 #define LAST_n_DEFERRED_ERROR 0x0b #define SEQUENTIAL_ACCESS_DEVICE 0x0c #define TEMPERATURE_PAGE 0x0d #define START_STOP_CYCLE_COUNTER 0x0e #define APPLICATION_CLIENT 0x0f #define SELFTEST_RESULTS 0x10 #define DEVICE_STATUS 0x11 #define VOLUME_STATISTICS 0x17 #define TAPE_ALERT 0x2e #define INFORMATIONAL_EXCEPTIONS 0x2f #define TAPE_USAGE 0x30 #define TAPE_CAPACITY 0x31 #define DATA_COMPRESSION 0x32 #define PERFORMANCE_CHARACTERISTICS 0x37 #define NO_SUBPAGE 0x00 struct log_pg_list { struct list_head siblings; char *description; int log_page_num; int log_subpage_num; int size; void *p; }; /* Log Page header */ struct log_pg_header { uint8_t pcode; uint8_t res; uint16_t len; } __attribute__((packed)); /* Page Code header struct. */ struct pc_header { uint8_t head0; uint8_t head1; uint8_t flags; uint8_t len; } __attribute__((packed)); /* Vendor Specific : 0x32 (Taken from IBM Ultrium doco) */ struct DataCompression_pg { struct log_pg_header pcode_head; struct pc_header h_ReadCompressionRatio; uint16_t ReadCompressionRatio; struct pc_header h_WriteCompressionRatio; uint16_t WriteCompressionRatio; struct pc_header h_MBytesToServer; uint32_t MBytesToServer; struct pc_header h_BytesToServer; uint32_t BytesToServer; struct pc_header h_MBytesReadFromTape; uint32_t MBytesReadFromTape; struct pc_header h_BytesReadFromTape; uint32_t BytesReadFromTape; struct pc_header h_MBytesFromServer; uint32_t MBytesFromServer; struct pc_header h_BytesFromServer; uint32_t BytesFromServer; struct pc_header h_MBytesWrittenToTape; uint32_t MBytesWrittenToTape; struct pc_header h_BytesWrittenToTape; uint32_t BytesWrittenToTape; } __attribute__((packed)); /* Buffer Under/Over Run log page - 0x01 : SPC-3 (7.2.3) */ struct BufferUnderOverRun_pg { struct log_pg_header pcode_head; } __attribute__((packed)); struct TapeUsage_pg { struct log_pg_header pcode_head; struct pc_header flagNo01; uint32_t volumeMounts; struct pc_header flagNo02; uint64_t volumeDatasetsWritten; struct pc_header flagNo03; uint32_t volWriteRetries; struct pc_header flagNo04; uint16_t volWritePerms; struct pc_header flagNo05; uint16_t volSuspendedWrites; struct pc_header flagNo06; uint16_t volFatalSuspendedWrites; struct pc_header flagNo07; uint64_t volDatasetsRead; struct pc_header flagNo08; uint32_t volReadRetries; struct pc_header flagNo09; uint16_t volReadPerms; struct pc_header flagNo10; uint16_t volSuspendedReads; struct pc_header flagNo11; uint16_t volFatalSuspendedReads; } __attribute__((packed)); /* Self-Test Results - 0x10 * */ struct selfTest_data { /* data in log parameters of selftest results */ uint8_t self_test_code_results; uint8_t self_test_number; uint16_t accumulated_power_on_hours; uint64_t address_of_first_failure; uint8_t sense_key; uint8_t asc; uint8_t ascq; uint8_t vendor_specific; } __attribute__((packed)); struct SelfTestResults_pg { struct log_pg_header pcode_head; struct pc_header h_result01; struct selfTest_data data01; struct pc_header h_result02; struct selfTest_data data02; struct pc_header h_result03; struct selfTest_data data03; struct pc_header h_result04; struct selfTest_data data04; struct pc_header h_result05; struct selfTest_data data05; struct pc_header h_result06; struct selfTest_data data06; struct pc_header h_result07; struct selfTest_data data07; struct pc_header h_result08; struct selfTest_data data08; struct pc_header h_result09; struct selfTest_data data09; struct pc_header h_result0a; struct selfTest_data data0a; struct pc_header h_result0b; struct selfTest_data data0b; struct pc_header h_result0c; struct selfTest_data data0c; struct pc_header h_result0d; struct selfTest_data data0d; struct pc_header h_result0e; struct selfTest_data data0e; struct pc_header h_result0f; struct selfTest_data data0f; struct pc_header h_result10; struct selfTest_data data10; struct pc_header h_result11; struct selfTest_data data11; struct pc_header h_result12; struct selfTest_data data12; struct pc_header h_result13; struct selfTest_data data13; struct pc_header h_result14; struct selfTest_data data14; } __attribute__((packed)); struct DeviceStatus_pg { struct log_pg_header pcode_head; struct pc_header h_vhf; struct __attribute__((packed)) { struct __attribute__((packed)) { uint8_t DINIT : 1; /* Device Initialized - 0 not initialised*/ uint8_t CRQRD : 1; /* Cleaning required - before media is mounted (required) */ uint8_t CRQST : 1; /* Cleaning required - non urgent (request) */ uint8_t WRTP : 1; /* Physical Write Protect */ uint8_t CMPR : 1; /* Compression enabled */ uint8_t MACC : 1; /* MAM accessible */ uint8_t HIU : 1; /* Host Initiated Unload */ uint8_t PAMR : 1; /* Prevent/Allow Media Removal */ } b4; struct __attribute__((packed)) { uint8_t MOUNTED : 1; /* Medium mounted */ uint8_t MTHRD : 1; /* Medium Threaded */ uint8_t MSTD : 1; /* Medium Seated */ uint8_t RSVD_2 : 1; uint8_t MPRSNT : 1; /* Medium Present */ uint8_t RAA : 1; /* Robotic access allowed */ uint8_t RSVD_1 : 1; uint8_t INXTN : 1; /* In Transition - other bits in byte 5 not stable */ } b5; uint8_t b6; /* DT Device Activity */ struct __attribute__((packed)) { uint8_t TAFC : 1; /* TapeAlert state flag changed */ uint8_t INITFC : 1; /* Interface changed */ uint8_t RRQST : 1; /* Recovery requested */ uint8_t ESR : 1; /* Encryption Service Requested */ uint8_t EPP : 1; /* Encryption Parameters Present */ uint8_t TDDEC : 1; /* Tape Diagnostic data entry created */ uint8_t RSVD : 1; uint8_t VS : 1; /* Always 0 */ } b7; } vhf; /* Very High Frequency data */ } __attribute__((packed)); struct TapeCapacity_pg { struct log_pg_header pcode_head; struct pc_header flagNo01; uint32_t partition0remaining; struct pc_header flagNo02; uint32_t partition1remaining; struct pc_header flagNo03; uint32_t partition0maximum; struct pc_header flagNo04; uint32_t partition1maximum; } __attribute__((packed)); /* Volume Statistics - 0x17 * SSC-4 */ struct partition_record_header { uint8_t len; uint8_t reserved; uint16_t partition_no; } __attribute__((packed)); struct partition_record_size4 { struct partition_record_header header; uint32_t data; } __attribute__((packed)); struct partition_record_size6 { struct partition_record_header header; uint8_t data[6]; } __attribute__((packed)); struct VolumeStatistics_pg { struct log_pg_header pcode_head; struct pc_header h_PageValid; uint8_t PageValid; struct pc_header h_VolumeMounts; uint32_t VolumeMounts; struct pc_header h_VolumeDatasetsWritten; uint64_t VolumeDatasetsWritten; struct pc_header h_RecoveredWriteDataErrors; uint32_t RecoveredWriteDataErrors; struct pc_header h_UnrecoveredWriteDataErrors; uint16_t UnrecoveredWriteDataErrors; struct pc_header h_WriteServoErrors; uint16_t WriteServoErrors; struct pc_header h_UnrecoveredWriteServoErrors; uint16_t UnrecoveredWriteServoErrors; struct pc_header h_VolumeDatasetsRead; uint64_t VolumeDatasetsRead; struct pc_header h_RecoveredReadErrors; uint32_t RecoveredReadErrors; struct pc_header h_UnrecoveredReadErrors; uint16_t UnrecoveredReadErrors; struct pc_header h_LastMountUnrecoveredWriteErrors; uint16_t LastMountUnrecoveredWriteErrors; struct pc_header h_LastMountUnrecoveredReadErrors; uint16_t LastMountUnrecoveredReadErrors; struct pc_header h_LastMountMBWritten; uint32_t LastMountMBWritten; struct pc_header h_LastMountMBRead; uint32_t LastMountMBRead; struct pc_header h_LifetimeMBWritten; uint64_t LifetimeMBWritten; struct pc_header h_LifetimeMBRead; uint64_t LifetimeMBRead; struct pc_header h_LastLoadWriteCompressionRatio; uint16_t LastLoadWriteCompressionRatio; struct pc_header h_LastLoadReadCompressionRatio; uint16_t LastLoadReadCompressionRatio; struct pc_header h_MediumMountTime; uint8_t MediumMountTime[6]; struct pc_header h_MediumReadyTime; uint8_t MediumReadyTime[6]; struct pc_header h_TotalNativeCapacity; uint32_t TotalNativeCapacity; struct pc_header h_TotalUsedNativeCapacity; uint32_t TotalUsedNativeCapacity; struct pc_header h_AppDesignCapacity; uint32_t AppDesignCapacity; struct pc_header h_VolumeLifetimeRemaining; uint8_t VolumeLifetimeRemaining; struct pc_header h_VolumeSerialNumber; uint8_t VolumeSerialNumber[32]; struct pc_header h_TapeLotIdentifier; uint8_t TapeLotIdentifier[8]; struct pc_header h_VolumeBarcode; uint8_t VolumeBarcode[32]; struct pc_header h_VolumeManufacturer; uint8_t VolumeManufacturer[8]; struct pc_header h_VolumeLicenseCode; uint8_t VolumeLicenseCode[4]; struct pc_header h_VolumePersonality; uint8_t VolumePersonality[9]; struct pc_header h_WriteProtect; uint8_t WriteProtect; struct pc_header h_WORM; uint8_t WORM; struct pc_header h_TempExceeded; uint8_t TempExceeded; struct pc_header h_BOMPasses; uint32_t BOMPasses; struct pc_header h_MOTPasses; uint32_t MOTPasses; /* size depends on actual nb of partitions => we put max size by default and adapt dynamically later */ struct pc_header h_FirstEncryptedLogicalObj; struct partition_record_size6 FirstEncryptedLogicalObj[MAX_PARTITIONS]; struct pc_header h_FirstUnencryptedLogicalObj; struct partition_record_size6 FirstUnencryptedLogicalObj[MAX_PARTITIONS]; struct pc_header h_ApproxNativeCapacityPartition; struct partition_record_size4 ApproxNativeCapacityPartition[MAX_PARTITIONS]; struct pc_header h_ApproxUsedNativeCapacityPartition; struct partition_record_size4 ApproxUsedNativeCapacityPartition[MAX_PARTITIONS]; struct pc_header h_RemainingCapacityToEWPartition; struct partition_record_size4 RemainingCapacityToEWPartition[MAX_PARTITIONS]; } __attribute__((packed)); struct TapeAlert_flag { struct pc_header flag; uint8_t value; } __attribute__((packed)); /* Tape Alert Log Page - 0x2E * SSC-3 (8.2.3) */ struct TapeAlert_pg { struct log_pg_header pcode_head; struct TapeAlert_flag TapeAlert[64]; } __attribute__((packed)); /* Temperature Log Page - 0x0d * SPC-3 (7.2.13) */ struct Temperature_pg { struct log_pg_header pcode_head; struct pc_header header; uint16_t temperature; } __attribute__((packed)); /* Write/Read/Read Reverse * Error Counter log page - 0x02, 0x03, 0x04 * SPC-3 (7.2.4) */ struct ErrorCounter_pg { struct log_pg_header pcode_head; struct pc_header h_err_correctedWODelay; uint32_t err_correctedWODelay; struct pc_header h_err_correctedWDelay; uint32_t err_correctedWDelay; struct pc_header h_totalReTry; uint32_t totalReTry; struct pc_header h_totalErrorsCorrected; uint32_t totalErrorsCorrected; struct pc_header h_correctAlgorithm; uint32_t correctAlgorithm; struct pc_header h_bytesProcessed; uint64_t bytesProcessed; struct pc_header h_uncorrectedErrors; uint32_t uncorrectedErrors; struct pc_header h_readErrorsSinceLast; uint32_t readErrorsSinceLast; struct pc_header h_totalRawReadError; uint32_t totalRawReadError; struct pc_header h_totalDropoutError; uint32_t totalDropoutError; struct pc_header h_totalServoTracking; uint32_t totalServoTracking; } __attribute__((packed)); /* Sequential-Access * Device log page - 0x0C * SSC-3 (Ch 8.2.2) */ struct SequentialAccessDevice_pg { struct log_pg_header pcode_head; struct pc_header h_writeDataB4; uint64_t writeDataB4Compression; /* Write. Bytes from initiator */ struct pc_header h_writeData_Af; uint64_t writeDataAfCompression; /* Write. Bytes written to media */ struct pc_header h_readData_b4; uint64_t readDataB4Compression; /* Read. Bytes read from media */ struct pc_header h_readData_Af; uint64_t readDataAfCompression; /* Read. Bytes to initiator */ struct pc_header h_bop_eod; uint32_t capacity_bop_eod; /* Native capacity BOT to EOD */ struct pc_header h_bop_ew; uint32_t capacity_bop_ew; /* Native capacity BOP to EW */ struct pc_header h_ew_leop; uint32_t capacity_ew_leop; /* Native capacity EW and * Logical End Of Partition */ struct pc_header h_bop_curr; uint32_t capacity_bop_curr; /* Native capacity BOP to curr pos */ struct pc_header h_buffer; uint32_t capacity_buffer; /* Native capacity in buffer */ struct pc_header h_cleaning; uint64_t TapeAlert; struct pc_header h_mbytes_processed; uint32_t mbytes_processed; /* MB since cleaning */ struct pc_header h_load_cycle; uint32_t load_cycle; /* Total number of load cycles over drive lifetime*/ struct pc_header h_clean; uint32_t clean_cycle; /* Total number of cleans over drive lifetime */ } __attribute__((packed)); /* Performance Characteristics Log Page - 0x37 * Sample */ struct PerformanceCharacteristics_pg { struct log_pg_header pcode_head; struct pc_header h_DriveEfficiency; uint8_t DriveEfficiency; } __attribute__((packed)); void set_current_state(int s); int get_tape_load_status(); void set_tape_load_status(int s); void set_lp_11_macc(int flag); void set_lp11_medium_present(int flag); /* Update LogPage 11 'Medium Present' bit */ void set_lp11_compression(int flag); /* Update LogPage 11 compression bit */ void set_lp_11_wp(int flag); void setTapeAlert(struct TapeAlert_pg *, uint64_t); /* in vtllib.c, never used */ void initTapeAlert(struct TapeAlert_pg *); void dealloc_all_log_pages(struct lu_phy_attr *lu); void update_VolumeStatistics(struct VolumeStatistics_pg *pg, struct priv_lu_ssc *lu_priv); int update_TapeAlert(uint64_t flags); int set_TapeAlert(uint64_t flags); void update_TapeUsage(struct TapeUsage_pg *b); void update_TapeCapacity(struct TapeCapacity_pg *pg); void update_SequentialAccessDevice(struct SequentialAccessDevice_pg *sa); struct log_pg_list *lookup_log_pg(struct list_head *l, uint8_t page, uint8_t subpage); int alloc_log_page(struct lu_phy_attr *lu, uint8_t page, uint8_t subpage, init_pg_fn init_log_pg, size_t pg_size); int add_log_write_err_counter(struct lu_phy_attr *lu); int add_log_read_err_counter(struct lu_phy_attr *lu); int add_log_sequential_access(struct lu_phy_attr *lu); int add_log_temperature_page(struct lu_phy_attr *lu); int add_log_selftest_results(struct lu_phy_attr *lu); int add_log_volume_statistics(struct lu_phy_attr *lu); int add_log_tape_alert(struct lu_phy_attr *lu); int add_log_tape_usage(struct lu_phy_attr *lu); int add_log_tape_capacity(struct lu_phy_attr *lu); int add_log_data_compression(struct lu_phy_attr *lu); int add_log_device_status(struct lu_phy_attr *lu); int add_log_performance_characteristics(struct lu_phy_attr *lu); extern const char *log_page_desc[0x38]; #endif /* MHVTL_LOG_H */ ================================================ FILE: include/mode.h ================================================ /* * This handles any SCSI OP 'mode sense / mode select' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #ifndef MODE_H #define MODE_H #include struct lu_phy_attr; int add_mode_page_rw_err_recovery(struct lu_phy_attr *lu); int add_mode_disconnect_reconnect(struct lu_phy_attr *lu); int add_mode_control(struct lu_phy_attr *lu); int add_mode_control_extension(struct lu_phy_attr *lu); int add_mode_control_data_protection(struct lu_phy_attr *lu); int add_mode_data_compression(struct lu_phy_attr *lu); int add_mode_device_configuration(struct lu_phy_attr *lu); int add_mode_device_configuration_extension(struct lu_phy_attr *lu); int add_mode_medium_partition(struct lu_phy_attr *lu); int add_mode_power_condition(struct lu_phy_attr *lu); int add_mode_information_exception(struct lu_phy_attr *lu); int add_mode_medium_configuration(struct lu_phy_attr *lu); int add_mode_ait_device_configuration(struct lu_phy_attr *lu); int add_mode_ult_encr_mode_pages(struct lu_phy_attr *lu); int add_mode_vendor_25h_mode_pages(struct lu_phy_attr *lu); int add_mode_encryption_mode_attribute(struct lu_phy_attr *lu); int add_mode_behavior_configuration(struct lu_phy_attr *lu); int add_mode_device_capabilities(struct lu_phy_attr *lu); int add_mode_transport_geometry(struct lu_phy_attr *lu); int add_mode_element_address_assignment(struct lu_phy_attr *lu); int update_prog_early_warning(struct lu_phy_attr *lu); int update_logical_block_protection(struct lu_phy_attr *lu, uint8_t *buf); uint8_t set_lbp(struct scsi_cmd *cmd, uint8_t *buf, int len); void dealloc_all_mode_pages(struct lu_phy_attr *lu); int add_smc_mode_page_drive_configuration(struct lu_phy_attr *lu); void set_mode_compression(struct scsi_cmd *cmd, uint8_t *p); void set_device_configuration(struct scsi_cmd *cmd, uint8_t *p); uint8_t set_device_configuration_extension(struct scsi_cmd *cmd, uint8_t *p); void set_medium_partition(struct scsi_cmd *cmd, uint8_t *p); #endif /* MODE_H */ ================================================ FILE: include/smc.h ================================================ #ifndef SMC_H #define SMC_H #include struct scsi_cmd; struct s_info; /* Element type codes */ #define ANY 0 #define MEDIUM_TRANSPORT 1 #define STORAGE_ELEMENT 2 #define MAP_ELEMENT 3 #define DATA_TRANSFER 4 #define CAP_CLOSED 1 #define CAP_OPEN 0 #define OPERATOR 1 #define ROBOT_ARM 0 struct smc_personality_template { char *name; uint32_t library_has_map : 1; uint32_t library_has_barcode_reader : 1; uint32_t library_has_playground : 1; uint32_t dvcid_serial_only : 1; uint32_t no_dvcid_flag : 1; uint32_t start_drive; uint32_t start_picker; uint32_t start_map; uint32_t start_storage; uint32_t dvcid_len; struct lu_phy_attr *lu; }; uint8_t smc_allow_removal(struct scsi_cmd *cmd); uint8_t smc_initialize_element_status(struct scsi_cmd *cmd); uint8_t smc_initialize_element_status_with_range(struct scsi_cmd *cmd); uint8_t smc_log_sense(struct scsi_cmd *cmd); uint8_t smc_move_medium(struct scsi_cmd *cmd); uint8_t smc_read_element_status(struct scsi_cmd *cmd); uint8_t smc_rezero(struct scsi_cmd *cmd); uint8_t smc_open_close_import_export_element(struct scsi_cmd *cmd); int get_cart_type(char *barcode); void update_home_dir(long my_id); /* for the 'get_cart_type()' function only */ int slotOccupied(struct s_info *s); void setImpExpStatus(struct s_info *s, int flg); void setSlotEmpty(struct s_info *s); void unload_drive_on_shutdown(struct s_info *src, struct s_info *dest); void init_slot_info(struct lu_phy_attr *lu); void init_stkl20(struct lu_phy_attr *lu); void init_stklxx(struct lu_phy_attr *lu); void init_stkslxx(struct lu_phy_attr *lu); void init_default_smc(struct lu_phy_attr *lu); void init_scalar_smc(struct lu_phy_attr *lu); void init_spectra_logic_smc(struct lu_phy_attr *lu); void init_spectra_215_smc(struct lu_phy_attr *lu); void init_spectra_gator_smc(struct lu_phy_attr *lu); void init_ibmts3100(struct lu_phy_attr *lu); void init_ibm3584(struct lu_phy_attr *lu); void init_hp_eml_smc(struct lu_phy_attr *lu); void init_hp_msl_smc(struct lu_phy_attr *lu); void init_overland_smc(struct lu_phy_attr *lu); void smc_personality_module_register(struct smc_personality_template *pm); void setAccessStatus(struct s_info *s, int flg); #endif /* SMC_H */ ================================================ FILE: include/spc.h ================================================ #ifndef SPC_H #define SPC_H #include struct scsi_cmd; /* Variables for simple, single initiator, SCSI Reservation system */ extern uint64_t SPR_Reservation_Key; extern uint32_t SPR_Reservation_Generation; extern uint8_t SPR_Reservation_Type; uint8_t resp_spc_pro(uint8_t *cdb, struct mhvtl_ds *dbuf_p); uint8_t resp_spc_pri(uint8_t *cdb, struct mhvtl_ds *dbuf_p); uint8_t spc_illegal_op(struct scsi_cmd *cmd); uint8_t spc_inquiry(struct scsi_cmd *cmd); uint8_t spc_log_select(struct scsi_cmd *cmd); uint8_t spc_log_sense(struct scsi_cmd *cmd); uint8_t spc_mode_select(struct scsi_cmd *cmd); uint8_t spc_mode_sense(struct scsi_cmd *cmd); uint8_t spc_recv_diagnostics(struct scsi_cmd *cmd); uint8_t spc_release(struct scsi_cmd *cmd); uint8_t spc_request_sense(struct scsi_cmd *cmd); uint8_t spc_reserve(struct scsi_cmd *cmd); uint8_t spc_send_diagnostics(struct scsi_cmd *cmd); uint8_t spc_tur(struct scsi_cmd *cmd); uint8_t spc_read_buffer(struct scsi_cmd *cmd); #endif /* SPC_H */ ================================================ FILE: include/ssc.h ================================================ #ifndef SSC_H #define SSC_H #include #include #include #include "mhvtl_list.h" #include "q.h" struct mhvtl_ds; struct scsi_cmd; #define ENCR_C 1 /* Device supports Encryption */ #define ENCR_E 4 /* Encryption is enabled */ #define ENCR_IN_SUPPORT_PAGES 0 #define ENCR_OUT_SUPPORT_PAGES 1 #define ENCR_CAPABILITIES 0x10 #define ENCR_KEY_FORMATS 0x11 #define ENCR_KEY_MGT_CAPABILITIES 0x12 #define ENCR_DATA_ENCR_STATUS 0x20 #define ENCR_NEXT_BLK_ENCR_STATUS 0x21 #define ENCR_SET_DATA_ENCRYPTION 0x10 #define EARLY_WARNING_SZ (1024 * 1024 * 2) /* 2M EW size */ #define PROG_EARLY_WARNING_SZ (1024 * 1024 * 3) /* 3M Prog EW size */ #define MAX_DELAY_LOAD 20 #define MAX_DELAY_UNLOAD 20 #define MAX_DELAY_THREAD 20 #define MAX_DELAY_POSITION 20 #define MAX_DELAY_REWIND 30 #define LBP_RSCRC 1 #define LBP_CRC32C 2 struct name_to_media_info { char *name; int media_type; int mode_media_type; int media_density; }; struct ssc_personality_template { char *name; int drive_type; uint32_t drive_supports_append_only_mode : 1; uint32_t drive_supports_early_warning : 1; uint32_t drive_supports_prog_early_warning : 1; uint32_t drive_supports_WORM : 1; /* Write Once Read Many */ uint32_t drive_supports_SPR : 1; /* SCSI Persistent Reservation */ uint32_t drive_supports_SP : 1; /* Security Protocol */ uint32_t drive_supports_LBP : 2; /* 0 - No, 1- Reed-Solomon only, 2 - RSCRC and CRC32C */ uint32_t drive_ANSI_VERSION : 5; struct density_info *native_drive_density; struct name_to_media_info *media_handling; struct lu_phy_attr *lu; /* Read check if this block contains valid encryption keys */ uint8_t (*valid_encryption_blk)(struct scsi_cmd *cmd); /* Write check if the media supports encryption */ uint8_t (*valid_encryption_media)(struct scsi_cmd *cmd); /* SPIN page 20 -> Encryption Capabilities */ int (*encryption_capabilities)(struct scsi_cmd *cmd); /* SPOUT -> Validation KAD info is correct */ int (*kad_validation)(int encrypt_mode, int akad, int ukad); /* Update mode page for encryption capabilities */ uint8_t (*update_encryption_mode)(struct list_head *m, void *p, int mode); /* Media access, check if any write restrictions */ uint8_t (*check_restrictions)(struct scsi_cmd *cmd); /* enable/disable compression */ uint8_t (*clear_compression)(struct list_head *l); uint8_t (*set_compression)(struct list_head *l, int level); /* enable/disable WORM */ uint8_t (*clear_WORM)(struct list_head *l); uint8_t (*set_WORM)(struct list_head *l); /* Cleaning media mount calls into here */ uint8_t (*cleaning_media)(void *priv); /* Called on load/unload - where var load = 0 on unload, 1 on load */ uint8_t (*media_load)(struct lu_phy_attr *lu, int load); }; enum delay_list { DELAY_UNDEFINED, /* Must be first */ DELAY_LOAD, DELAY_THREAD, DELAY_POSITION, DELAY_REWIND, DELAY_UNLOAD, DELAY_LAST_ITEM /* Flag end of list */ }; /* Load capabilities - density_status bits */ #define LOAD_INVALID 1 #define LOAD_RW 2 #define LOAD_RO 4 #define LOAD_WORM 8 #define LOAD_ENCRYPT 0x10 #define LOAD_FAIL 0x20 #define LOAD_CLEANING 0x40 #define LOAD_NOACCESS 0x80 /* Loads but can not read/write */ #define CLEAN_MOUNT_STAGE1 1 #define CLEAN_MOUNT_STAGE2 2 #define CLEAN_MOUNT_STAGE3 3 struct media_details { struct list_head siblings; unsigned int media_type; /* Media Type */ unsigned int load_capability; /* RO, RW, invalid or fail mount */ }; struct priv_lu_ssc { int bufsize; int load_status; /* Tape load state: Unloaded, loading, loaded */ uint32_t inLibrary : 1; /* This tape drive is 'assigned' as part of a library */ uint32_t I_am_SPC_2_Reserved : 1; /* Variables for simple, single initiator, SCSI Reservation system */ uint32_t append_only_mode : 1; uint32_t MediaWriteProtect : 1; uint32_t LBP_method : 2; /* Logical Block Protection method 0: off, 1: Reed-Solomon CRC, 2: CRC32C */ uint32_t LBP_W : 1; /* Logical Block Protection during writes */ uint32_t LBP_R : 1; /* Logical Block Protection during reads */ char *barcode; /* Barcode of tape in the drive - NULL if nothing is in mouth of drive */ uint8_t sam_status; uint8_t allow_overwrite; /* Default value read from config file */ uint8_t configCompressionFactor; uint8_t configCompressionEnabled; uint8_t compressionType; /* lzo or zlib compression */ loff_t capacity_unit; loff_t early_warning_sz; loff_t prog_early_warning_sz; loff_t early_warning_position; loff_t prog_early_warning_position; /* Pointer into Device config mode page */ uint8_t *compressionFactor; int *OK_2_write; struct MAM *mamp; uint64_t allow_overwrite_block; /* Used by 'allow overwrite' op code */ uint64_t max_capacity; /* save MAM.max_capacity here for quick access */ uint64_t bytesRead_M; /* Bytes read from media */ uint64_t bytesRead_I; /* Bytes read and sent to initiator */ uint64_t bytesWritten_M; /* Bytes written to media (compressed) */ uint64_t bytesWritten_I; /* Bytes recevied from initiator */ /* Allow to insert delays into op codes */ int delay_load; int delay_unload; int delay_thread; int delay_position; int delay_rewind; struct blk_header *c_pos; uint32_t KEY_INSTANCE_COUNTER; uint32_t DECRYPT_MODE; uint32_t ENCRYPT_MODE; struct encryption *app_encr_info; struct list_head supported_media_list; unsigned char mediaSerialNo[34]; /* cleaning_media_state - Only used for cleaning media status.. Using signal / alarm which will increment this value thru: 0/NULL (unmonted) 1 (mounted) -> sense: "Cleaning cartridge installed" 2 (mounted) -> sense: "Logical unit not ready" 3 (mounted) -> sense: "Cause not reportable" */ volatile sig_atomic_t *cleaning_media_state; char *state_msg; /* Custom State message */ struct q_entry r_entry; /* IPC message queue */ struct ssc_personality_template *pm; /* Personality Module */ }; /* cdb[1] bits for verify_6 op code */ struct verify_6_bits { uint8_t FIXED : 1; /* FIXED block */ uint8_t BYTCMP : 1; /* Byte Compare */ uint8_t IMMED : 1; /* Immediate */ uint8_t VBF : 1; /* Verify by Filemarks */ uint8_t VLBPM : 1; /* Verify Logical Block Protection Method */ uint8_t VTE : 1; /* Verify To End-of-data */ }; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ struct read_position_information_short { uint8_t BPEW : 1; /* Beyond Early Warning - 0: The LOCU is set to 1, the PEWS field in MP 0x10:01 is set to 0, 1: logical object location is in a PEWZ or on EOP side of EW */ uint8_t PERR : 1; /* Position Error - 0: An overflow has not occurred, 1: An overflow has occurred - should use the LONG FORM (06h) instead */ uint8_t LOLU : 1; /* Logical object location is unknown - 0: Block position is exact, 1: Block position is estimate */ uint8_t resvd : 1; /* Reserved */ uint8_t BYCU : 1; /* Byte Count Unknown - 0: Byte count is exact, 1: Byte count is an estimate */ uint8_t LOCU : 1; /* Logical Object Count Unknown - 0: Block count is exact, 1: Block count is an estimate */ uint8_t EOP : 1; /* End of Partition 0: Not between early warning and end of partition, 1: Positioned between early warning and end of partition */ uint8_t BOP : 1; /* Beginning of Partition 0: current logical position is not at beginning, 1: At beginning of partition */ } __attribute__((packed)); struct read_position_information_long { uint8_t BPEW : 1; /* Beyond Early Warning - 0: The LOCU is set to 1, the PEWS field in MP 0x10:01 is set to 0, 1: logical object location is in a PEWZ or on EOP side of EW */ uint8_t rsvd_1 : 1; /* Reserved 1 bit */ uint8_t LONU : 1; /* Logical Object Number Unknown - 0: Logical Object Number and Partition number contains exact info, 1: LON is estimate */ uint8_t MPU : 1; /* Mark Position Unknown - 0: Filemark count is exact, 1: Filemark count is an estimate */ uint8_t rsvd_0 : 2; /* Reserved 2 bits */ uint8_t EOP : 1; /* End of Partition 0: Not between early warning and end of partition, 1: Positioned between early warning and end of partition */ uint8_t BOP : 1; /* Beginning of Partition 0: current logical position is not at beginning, 1: At beginning of partition */ } __attribute__((packed)); struct read_position_information_extended { uint8_t BPEW : 1; /* Beyond Early Warning - 0: The LOCU is set to 1, the PEWS field in MP 0x10:01 is set to 0, 1: logical object location is in a PEWZ or on EOP side of EW */ uint8_t PERR : 1; /* Position Error - 0: An overflow has not occurred, 1: An overflow has occurred - should use the LONG FORM (06h) instead */ uint8_t LOLU : 1; /* Logical object location is unknown - 0: Block position is exact, 1: Block position is estimate */ uint8_t resvd : 1; /* Reserved */ uint8_t BYCU : 1; /* Byte Count Unknown - 0: Byte count is exact, 1: Byte count is an estimate */ uint8_t LOCU : 1; /* Logical Object Count Unknown - 0: Block count is exact, 1: Block count is an estimate */ uint8_t EOP : 1; /* End of Partition 0: Not between early warning and end of partition, 1: Positioned between early warning and end of partition */ uint8_t BOP : 1; /* Beginning of Partition 0: current logical position is not at beginning, 1: At beginning of partition */ } __attribute__((packed)); #else struct read_position_information_short { uint8_t BOP : 1; /* Beginning of Partition 0: current logical position is not at beginning, 1: At beginning of partition */ uint8_t EOP : 1; /* End of Partition 0: Not between early warning and end of partition, 1: Positioned between early warning and end of partition */ uint8_t LOCU : 1; /* Logical Object Count Unknown - 0: Block count is exact, 1: Block count is an estimate */ uint8_t BYCU : 1; /* Byte Count Unknown - 0: Byte count is exact, 1: Byte count is an estimate */ uint8_t resvd : 1; /* Reserved */ uint8_t LOLU : 1; /* Logical object location is unknown - 0: Block position is exact, 1: Block position is estimate */ uint8_t PERR : 1; /* Position Error - 0: An overflow has not occurred, 1: An overflow has occurred - should use the LONG FORM (06h) instead */ uint8_t BPEW : 1; /* Beyond Early Warning - 0: The LOCU is set to 1, the PEWS field in MP 0x10:01 is set to 0, 1: logical object location is in a PEWZ or on EOP side of EW */ } __attribute__((packed)); struct read_position_information_long { uint8_t BOP : 1; /* Beginning of Partition 0: current logical position is not at beginning, 1: At beginning of partition */ uint8_t EOP : 1; /* End of Partition 0: Not between early warning and end of partition, 1: Positioned between early warning and end of partition */ uint8_t rsvd_0 : 2; /* Reserved 2 bits */ uint8_t MPU : 1; /* Mark Position Unknown - 0: Filemark count is exact, 1: Filemark count is an estimate */ uint8_t LONU : 1; /* Logical Object Number Unknown - 0: Logical Object Number and Partition number contains exact info, 1: LON is estimate */ uint8_t rsvd_1 : 1; /* Reserved 1 bit */ uint8_t BPEW : 1; /* Beyond Early Warning - 0: The LOCU is set to 1, the PEWS field in MP 0x10:01 is set to 0, 1: logical object location is in a PEWZ or on EOP side of EW */ } __attribute__((packed)); struct read_position_information_extended { uint8_t BOP : 1; /* Beginning of Partition 0: current logical position is not at beginning, 1: At beginning of partition */ uint8_t EOP : 1; /* End of Partition 0: Not between early warning and end of partition, 1: Positioned between early warning and end of partition */ uint8_t LOCU : 1; /* Logical Object Count Unknown - 0: Block count is exact, 1: Block count is an estimate */ uint8_t BYCU : 1; /* Byte Count Unknown - 0: Byte count is exact, 1: Byte count is an estimate */ uint8_t resvd : 1; /* Reserved */ uint8_t LOLU : 1; /* Logical object location is unknown - 0: Block position is exact, 1: Block position is estimate */ uint8_t PERR : 1; /* Position Error - 0: An overflow has not occurred, 1: An overflow has occurred - should use the LONG FORM (06h) instead */ uint8_t BPEW : 1; /* Beyond Early Warning - 0: The LOCU is set to 1, the PEWS field in MP 0x10:01 is set to 0, 1: logical object location is in a PEWZ or on EOP side of EW */ } __attribute__((packed)); #endif /* Byte order */ void ssc_personality_module_register(struct ssc_personality_template *pm); int readBlock(uint8_t *buf, uint32_t request_sz, int sili, int lbp, uint8_t *sam_stat); int writeBlock(struct scsi_cmd *cmd, uint32_t request_sz); uint8_t ssc_a3_service_action(struct scsi_cmd *cmd); uint8_t ssc_a4_service_action(struct scsi_cmd *cmd); uint8_t ssc_allow_overwrite(struct scsi_cmd *cmd); uint8_t ssc_allow_prevent_removal(struct scsi_cmd *cmd); uint8_t ssc_erase(struct scsi_cmd *cmd); uint8_t ssc_format_medium(struct scsi_cmd *cmd); uint8_t ssc_load_display(struct scsi_cmd *cmd); uint8_t ssc_log_select(struct scsi_cmd *cmd); uint8_t ssc_log_sense(struct scsi_cmd *cmd); uint8_t ssc_mode_select(struct scsi_cmd *cmd); uint8_t ssc_pr_in(struct scsi_cmd *cmd); uint8_t ssc_pr_out(struct scsi_cmd *cmd); uint8_t ssc_read_6(struct scsi_cmd *cmd); uint8_t ssc_read_attributes(struct scsi_cmd *cmd); uint8_t ssc_read_block_limits(struct scsi_cmd *cmd); uint8_t ssc_read_display(struct scsi_cmd *cmd); uint8_t ssc_read_media_sn(struct scsi_cmd *cmd); uint8_t ssc_read_position(struct scsi_cmd *cmd); uint8_t ssc_release(struct scsi_cmd *cmd); uint8_t ssc_report_density_support(struct scsi_cmd *cmd); uint8_t ssc_reserve(struct scsi_cmd *cmd); uint8_t ssc_rewind(struct scsi_cmd *cmd); uint8_t ssc_locate(struct scsi_cmd *cmd); uint8_t ssc_send_diagnostics(struct scsi_cmd *cmd); uint8_t ssc_recv_diagnostics(struct scsi_cmd *cmd); uint8_t ssc_space_6(struct scsi_cmd *cmd); uint8_t ssc_space_16(struct scsi_cmd *cmd); uint8_t ssc_spin(struct scsi_cmd *cmd); uint8_t ssc_spout(struct scsi_cmd *cmd); uint8_t ssc_load_unload(struct scsi_cmd *cmd); uint8_t ssc_tur(struct scsi_cmd *cmd); uint8_t ssc_verify_6(struct scsi_cmd *cmd); uint8_t ssc_write_6(struct scsi_cmd *cmd); uint8_t ssc_write_attributes(struct scsi_cmd *cmd); uint8_t ssc_write_filemarks(struct scsi_cmd *cmd); uint8_t ssc_set_capacity(struct scsi_cmd *cmd); /* Initialisation of personality modules */ void init_ait1_ssc(struct lu_phy_attr *lu); void init_ait2_ssc(struct lu_phy_attr *lu); void init_ait3_ssc(struct lu_phy_attr *lu); void init_ait4_ssc(struct lu_phy_attr *lu); void init_default_ssc(struct lu_phy_attr *lu); void init_t10kA_ssc(struct lu_phy_attr *lu); void init_t10kB_ssc(struct lu_phy_attr *lu); void init_t10kC_ssc(struct lu_phy_attr *lu); void init_9840A_ssc(struct lu_phy_attr *lu); void init_9840B_ssc(struct lu_phy_attr *lu); void init_9840C_ssc(struct lu_phy_attr *lu); void init_9840D_ssc(struct lu_phy_attr *lu); void init_9940A_ssc(struct lu_phy_attr *lu); void init_9940B_ssc(struct lu_phy_attr *lu); void init_ult3580_td1(struct lu_phy_attr *lu); void init_ult3580_td2(struct lu_phy_attr *lu); void init_ult3580_td3(struct lu_phy_attr *lu); void init_ult3580_td4(struct lu_phy_attr *lu); void init_ult3580_td5(struct lu_phy_attr *lu); void init_ult3580_td6(struct lu_phy_attr *lu); void init_ult3580_td7(struct lu_phy_attr *lu); void init_ult3580_td8(struct lu_phy_attr *lu); void init_ult3580_td9(struct lu_phy_attr *lu); void init_hp_ult_1(struct lu_phy_attr *lu); void init_hp_ult_2(struct lu_phy_attr *lu); void init_hp_ult_3(struct lu_phy_attr *lu); void init_hp_ult_4(struct lu_phy_attr *lu); void init_hp_ult_5(struct lu_phy_attr *lu); void init_hp_ult_6(struct lu_phy_attr *lu); void init_hp_ult_7(struct lu_phy_attr *lu); void init_hp_ult_8(struct lu_phy_attr *lu); void init_3592_j1a(struct lu_phy_attr *lu); void init_3592_E05(struct lu_phy_attr *lu); void init_3592_E06(struct lu_phy_attr *lu); void init_3592_E07(struct lu_phy_attr *lu); void init_dlt7000_ssc(struct lu_phy_attr *lu); void init_dlt8000_ssc(struct lu_phy_attr *lu); void init_sdlt320_ssc(struct lu_phy_attr *lu); void init_sdlt600_ssc(struct lu_phy_attr *lu); void register_ops(struct lu_phy_attr *lu, int op, void *f, void *g, void *h); uint8_t valid_encryption_blk(struct scsi_cmd *cmd); uint8_t check_restrictions(struct scsi_cmd *cmd); void init_default_ssc_mode_pages(struct list_head *l); uint8_t resp_spin(struct scsi_cmd *cmd); uint8_t resp_spout(struct scsi_cmd *cmd); int resp_write_attribute(struct scsi_cmd *cmd); int resp_read_attribute(struct scsi_cmd *cmd); int resp_report_density(struct priv_lu_ssc *lu_ssc, uint8_t media, struct mhvtl_ds *dbuf_p); void resp_space(int64_t count, int code, uint8_t *sam_stat); int loadTape(char *barcode, uint8_t *sam_stat); void unloadTape(int update_library, uint8_t *sam_stat); void delay_opcode(int what, int value); void set_timestamp(uint8_t source, uint64_t ts); void memset_ssc_buf(struct scsi_cmd *cmd, uint64_t alloc_len); #endif /* SSC_H */ ================================================ FILE: include/utils/be_byteshift.h ================================================ #ifndef _LINUX_UNALIGNED_BE_BYTESHIFT_H #define _LINUX_UNALIGNED_BE_BYTESHIFT_H #include static inline uint16_t __get_unaligned_be16(const uint8_t *p) { return p[0] << 8 | p[1]; } static inline uint32_t __get_unaligned_be32(const uint8_t *p) { return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; } static inline uint64_t __get_unaligned_be64(const uint8_t *p) { return (uint64_t)__get_unaligned_be32(p) << 32 | __get_unaligned_be32(p + 4); } static inline void __put_unaligned_be16(uint16_t val, uint8_t *p) { *p++ = val >> 8; *p++ = val; } static inline void __put_unaligned_be32(uint32_t val, uint8_t *p) { __put_unaligned_be16(val >> 16, p); __put_unaligned_be16(val, p + 2); } static inline void __put_unaligned_be64(uint64_t val, uint8_t *p) { __put_unaligned_be32(val >> 32, p); __put_unaligned_be32(val, p + 4); } static inline uint16_t get_unaligned_be16(const void *p) { return __get_unaligned_be16((const uint8_t *)p); } static inline uint32_t get_unaligned_be24(const uint8_t *p) { return p[0] << 16 | p[1] << 8 | p[2]; } static inline uint32_t get_unaligned_be32(const void *p) { return __get_unaligned_be32((const uint8_t *)p); } static inline uint64_t get_unaligned_be48(const void *p) { return (uint64_t)__get_unaligned_be32(p) << 32 | __get_unaligned_be16(p + 4); } static inline uint64_t get_unaligned_be64(const void *p) { return __get_unaligned_be64((const uint8_t *)p); } static inline void put_unaligned_be16(uint16_t val, void *p) { __put_unaligned_be16(val, (uint8_t *)p); } static inline void put_unaligned_be24(uint32_t val, void *p) { ((uint8_t *)p)[0] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[2] = val & 0xff; } static inline void put_unaligned_be32(uint32_t val, void *p) { __put_unaligned_be32(val, (uint8_t *)p); } static inline void put_unaligned_be48(uint64_t val, void *p) { __put_unaligned_be32(val >> 32, (uint8_t *)p); __put_unaligned_be16(val, p + 4); } static inline void put_unaligned_be64(uint64_t val, void *p) { __put_unaligned_be64(val, (uint8_t *)p); } #endif /* _LINUX_UNALIGNED_BE_BYTESHIFT_H */ ================================================ FILE: include/utils/lzoconf.h ================================================ /* lzoconf.h -- configuration of the LZO data compression library This file is part of the LZO real-time data compression library. Copyright (C) 1996-2015 Markus Franz Xaver Johannes Oberhumer All Rights Reserved. The LZO library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The LZO 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 General Public License for more details. You should have received a copy of the GNU General Public License along with the LZO library; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Markus F.X.J. Oberhumer http://www.oberhumer.com/opensource/lzo/ */ #ifndef __LZOCONF_H_INCLUDED #define __LZOCONF_H_INCLUDED 1 #define LZO_VERSION 0x2090 #define LZO_VERSION_STRING "2.09" #define LZO_VERSION_DATE "Feb 04 2015" /* internal Autoconf configuration file - only used when building LZO */ #if defined(LZO_HAVE_CONFIG_H) #include #endif #include #include /*********************************************************************** // LZO requires a conforming ************************************************************************/ #if !defined(CHAR_BIT) || (CHAR_BIT != 8) #error "invalid CHAR_BIT" #endif #if !defined(UCHAR_MAX) || !defined(USHRT_MAX) || !defined(UINT_MAX) || !defined(ULONG_MAX) #error "check your compiler installation" #endif #if (USHRT_MAX < 1) || (UINT_MAX < 1) || (ULONG_MAX < 1) #error "your limits.h macros are broken" #endif /* get OS and architecture defines */ #ifndef __LZODEFS_H_INCLUDED #include "lzodefs.h" #endif #ifdef __cplusplus extern "C" { #endif /*********************************************************************** // some core defines ************************************************************************/ /* memory checkers */ #if !defined(__LZO_CHECKER) #if defined(__BOUNDS_CHECKING_ON) #define __LZO_CHECKER 1 #elif defined(__CHECKER__) #define __LZO_CHECKER 1 #elif defined(__INSURE__) #define __LZO_CHECKER 1 #elif defined(__PURIFY__) #define __LZO_CHECKER 1 #endif #endif /*********************************************************************** // integral and pointer types ************************************************************************/ /* lzo_uint must match size_t */ #if !defined(LZO_UINT_MAX) #if (LZO_ABI_LLP64) #if (LZO_OS_WIN64) typedef unsigned __int64 lzo_uint; typedef __int64 lzo_int; #define LZO_TYPEOF_LZO_INT LZO_TYPEOF___INT64 #else typedef lzo_ullong_t lzo_uint; typedef lzo_llong_t lzo_int; #define LZO_TYPEOF_LZO_INT LZO_TYPEOF_LONG_LONG #endif #define LZO_SIZEOF_LZO_INT 8 #define LZO_UINT_MAX 0xffffffffffffffffull #define LZO_INT_MAX 9223372036854775807LL #define LZO_INT_MIN (-1LL - LZO_INT_MAX) #elif (LZO_ABI_IP32L64) /* MIPS R5900 */ typedef unsigned int lzo_uint; typedef int lzo_int; #define LZO_SIZEOF_LZO_INT LZO_SIZEOF_INT #define LZO_TYPEOF_LZO_INT LZO_TYPEOF_INT #define LZO_UINT_MAX UINT_MAX #define LZO_INT_MAX INT_MAX #define LZO_INT_MIN INT_MIN #elif (ULONG_MAX >= LZO_0xffffffffL) typedef unsigned long lzo_uint; typedef long lzo_int; #define LZO_SIZEOF_LZO_INT LZO_SIZEOF_LONG #define LZO_TYPEOF_LZO_INT LZO_TYPEOF_LONG #define LZO_UINT_MAX ULONG_MAX #define LZO_INT_MAX LONG_MAX #define LZO_INT_MIN LONG_MIN #else #error "lzo_uint" #endif #endif /* The larger type of lzo_uint and lzo_uint32_t. */ #if (LZO_SIZEOF_LZO_INT >= 4) #define lzo_xint lzo_uint #else #define lzo_xint lzo_uint32_t #endif typedef int lzo_bool; /* sanity checks */ LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int) == LZO_SIZEOF_LZO_INT) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint) == LZO_SIZEOF_LZO_INT) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_xint) >= sizeof(lzo_uint)) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_xint) >= sizeof(lzo_uint32_t)) #ifndef __LZO_MMODEL #define __LZO_MMODEL /*empty*/ #endif /* no typedef here because of const-pointer issues */ #define lzo_bytep unsigned char __LZO_MMODEL * #define lzo_charp char __LZO_MMODEL * #define lzo_voidp void __LZO_MMODEL * #define lzo_shortp short __LZO_MMODEL * #define lzo_ushortp unsigned short __LZO_MMODEL * #define lzo_intp lzo_int __LZO_MMODEL * #define lzo_uintp lzo_uint __LZO_MMODEL * #define lzo_xintp lzo_xint __LZO_MMODEL * #define lzo_voidpp lzo_voidp __LZO_MMODEL * #define lzo_bytepp lzo_bytep __LZO_MMODEL * #define lzo_int8_tp lzo_int8_t __LZO_MMODEL * #define lzo_uint8_tp lzo_uint8_t __LZO_MMODEL * #define lzo_int16_tp lzo_int16_t __LZO_MMODEL * #define lzo_uint16_tp lzo_uint16_t __LZO_MMODEL * #define lzo_int32_tp lzo_int32_t __LZO_MMODEL * #define lzo_uint32_tp lzo_uint32_t __LZO_MMODEL * #if defined(lzo_int64_t) #define lzo_int64_tp lzo_int64_t __LZO_MMODEL * #define lzo_uint64_tp lzo_uint64_t __LZO_MMODEL * #endif /* Older LZO versions used to support ancient systems and memory models * such as 16-bit MSDOS with __huge pointers or Cray PVP, but these * obsolete configurations are not supported any longer. */ #if defined(__LZO_MMODEL_HUGE) #error "__LZO_MMODEL_HUGE memory model is unsupported" #endif #if (LZO_MM_PVP) #error "LZO_MM_PVP memory model is unsupported" #endif #if (LZO_SIZEOF_INT < 4) #error "LZO_SIZEOF_INT < 4 is unsupported" #endif #if (__LZO_UINTPTR_T_IS_POINTER) #error "__LZO_UINTPTR_T_IS_POINTER is unsupported" #endif LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(int) >= 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint) >= 4) /* Strange configurations where sizeof(lzo_uint) != sizeof(size_t) should * work but have not received much testing lately, so be strict here. */ LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint) == sizeof(size_t)) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint) == sizeof(ptrdiff_t)) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint) == sizeof(lzo_uintptr_t)) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(void *) == sizeof(lzo_uintptr_t)) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(char *) == sizeof(lzo_uintptr_t)) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long *) == sizeof(lzo_uintptr_t)) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(void *) == sizeof(lzo_voidp)) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(char *) == sizeof(lzo_bytep)) /*********************************************************************** // function types ************************************************************************/ /* name mangling */ #if !defined(__LZO_EXTERN_C) #ifdef __cplusplus #define __LZO_EXTERN_C extern "C" #else #define __LZO_EXTERN_C extern #endif #endif /* calling convention */ #if !defined(__LZO_CDECL) #define __LZO_CDECL __lzo_cdecl #endif /* DLL export information */ #if !defined(__LZO_EXPORT1) #define __LZO_EXPORT1 /*empty*/ #endif #if !defined(__LZO_EXPORT2) #define __LZO_EXPORT2 /*empty*/ #endif /* __cdecl calling convention for public C and assembly functions */ #if !defined(LZO_PUBLIC) #define LZO_PUBLIC(r) __LZO_EXPORT1 r __LZO_EXPORT2 __LZO_CDECL #endif #if !defined(LZO_EXTERN) #define LZO_EXTERN(r) __LZO_EXTERN_C LZO_PUBLIC(r) #endif #if !defined(LZO_PRIVATE) #define LZO_PRIVATE(r) static r __LZO_CDECL #endif /* function types */ typedef int(__LZO_CDECL *lzo_compress_t)(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, lzo_uintp dst_len, lzo_voidp wrkmem); typedef int(__LZO_CDECL *lzo_decompress_t)(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, lzo_uintp dst_len, lzo_voidp wrkmem); typedef int(__LZO_CDECL *lzo_optimize_t)(lzo_bytep src, lzo_uint src_len, lzo_bytep dst, lzo_uintp dst_len, lzo_voidp wrkmem); typedef int(__LZO_CDECL *lzo_compress_dict_t)(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, lzo_uintp dst_len, lzo_voidp wrkmem, const lzo_bytep dict, lzo_uint dict_len); typedef int(__LZO_CDECL *lzo_decompress_dict_t)(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, lzo_uintp dst_len, lzo_voidp wrkmem, const lzo_bytep dict, lzo_uint dict_len); /* Callback interface. Currently only the progress indicator ("nprogress") * is used, but this may change in a future release. */ struct lzo_callback_t; typedef struct lzo_callback_t lzo_callback_t; #define lzo_callback_p lzo_callback_t __LZO_MMODEL * /* malloc & free function types */ typedef lzo_voidp(__LZO_CDECL *lzo_alloc_func_t)(lzo_callback_p self, lzo_uint items, lzo_uint size); typedef void(__LZO_CDECL *lzo_free_func_t)(lzo_callback_p self, lzo_voidp ptr); /* a progress indicator callback function */ typedef void(__LZO_CDECL *lzo_progress_func_t)(lzo_callback_p, lzo_uint, lzo_uint, int); struct lzo_callback_t { /* custom allocators (set to 0 to disable) */ lzo_alloc_func_t nalloc; /* [not used right now] */ lzo_free_func_t nfree; /* [not used right now] */ /* a progress indicator callback function (set to 0 to disable) */ lzo_progress_func_t nprogress; /* INFO: the first parameter "self" of the nalloc/nfree/nprogress * callbacks points back to this struct, so you are free to store * some extra info in the following variables. */ lzo_voidp user1; lzo_xint user2; lzo_xint user3; }; /*********************************************************************** // error codes and prototypes ************************************************************************/ /* Error codes for the compression/decompression functions. Negative * values are errors, positive values will be used for special but * normal events. */ #define LZO_E_OK 0 #define LZO_E_ERROR (-1) #define LZO_E_OUT_OF_MEMORY (-2) /* [lzo_alloc_func_t failure] */ #define LZO_E_NOT_COMPRESSIBLE (-3) /* [not used right now] */ #define LZO_E_INPUT_OVERRUN (-4) #define LZO_E_OUTPUT_OVERRUN (-5) #define LZO_E_LOOKBEHIND_OVERRUN (-6) #define LZO_E_EOF_NOT_FOUND (-7) #define LZO_E_INPUT_NOT_CONSUMED (-8) #define LZO_E_NOT_YET_IMPLEMENTED (-9) /* [not used right now] */ #define LZO_E_INVALID_ARGUMENT (-10) #define LZO_E_INVALID_ALIGNMENT (-11) /* pointer argument is not properly aligned */ #define LZO_E_OUTPUT_NOT_CONSUMED (-12) #define LZO_E_INTERNAL_ERROR (-99) #ifndef lzo_sizeof_dict_t #define lzo_sizeof_dict_t ((unsigned)sizeof(lzo_bytep)) #endif /* lzo_init() should be the first function you call. * Check the return code ! * * lzo_init() is a macro to allow checking that the library and the * compiler's view of various types are consistent. */ #define lzo_init() __lzo_init_v2(LZO_VERSION, (int)sizeof(short), (int)sizeof(int), \ (int)sizeof(long), (int)sizeof(lzo_uint32_t), (int)sizeof(lzo_uint), \ (int)lzo_sizeof_dict_t, (int)sizeof(char *), (int)sizeof(lzo_voidp), \ (int)sizeof(lzo_callback_t)) LZO_EXTERN(int) __lzo_init_v2(unsigned, int, int, int, int, int, int, int, int, int); /* version functions (useful for shared libraries) */ LZO_EXTERN(unsigned) lzo_version(void); LZO_EXTERN(const char *) lzo_version_string(void); LZO_EXTERN(const char *) lzo_version_date(void); LZO_EXTERN(const lzo_charp) _lzo_version_string(void); LZO_EXTERN(const lzo_charp) _lzo_version_date(void); /* string functions */ LZO_EXTERN(int) lzo_memcmp(const lzo_voidp a, const lzo_voidp b, lzo_uint len); LZO_EXTERN(lzo_voidp) lzo_memcpy(lzo_voidp dst, const lzo_voidp src, lzo_uint len); LZO_EXTERN(lzo_voidp) lzo_memmove(lzo_voidp dst, const lzo_voidp src, lzo_uint len); LZO_EXTERN(lzo_voidp) lzo_memset(lzo_voidp buf, int c, lzo_uint len); /* checksum functions */ LZO_EXTERN(lzo_uint32_t) lzo_adler32(lzo_uint32_t c, const lzo_bytep buf, lzo_uint len); LZO_EXTERN(lzo_uint32_t) lzo_crc32(lzo_uint32_t c, const lzo_bytep buf, lzo_uint len); LZO_EXTERN(const lzo_uint32_tp) lzo_get_crc32_table(void); /* misc. */ LZO_EXTERN(int) _lzo_config_check(void); typedef union { lzo_voidp a00; lzo_bytep a01; lzo_uint a02; lzo_xint a03; lzo_uintptr_t a04; void *a05; unsigned char *a06; unsigned long a07; size_t a08; ptrdiff_t a09; #if defined(lzo_int64_t) lzo_uint64_t a10; #endif } lzo_align_t; /* align a char pointer on a boundary that is a multiple of 'size' */ LZO_EXTERN(unsigned) __lzo_align_gap(const lzo_voidp p, lzo_uint size); #define LZO_PTR_ALIGN_UP(p, size) \ ((p) + (lzo_uint)__lzo_align_gap((const lzo_voidp)(p), (lzo_uint)(size))) /*********************************************************************** // deprecated macros - only for backward compatibility ************************************************************************/ /* deprecated - use 'lzo_bytep' instead of 'lzo_byte *' */ #define lzo_byte unsigned char /* deprecated type names */ #define lzo_int32 lzo_int32_t #define lzo_uint32 lzo_uint32_t #define lzo_int32p lzo_int32_t __LZO_MMODEL * #define lzo_uint32p lzo_uint32_t __LZO_MMODEL * #define LZO_INT32_MAX LZO_INT32_C(2147483647) #define LZO_UINT32_MAX LZO_UINT32_C(4294967295) #if defined(lzo_int64_t) #define lzo_int64 lzo_int64_t #define lzo_uint64 lzo_uint64_t #define lzo_int64p lzo_int64_t __LZO_MMODEL * #define lzo_uint64p lzo_uint64_t __LZO_MMODEL * #define LZO_INT64_MAX LZO_INT64_C(9223372036854775807) #define LZO_UINT64_MAX LZO_UINT64_C(18446744073709551615) #endif /* deprecated types */ typedef union { lzo_bytep a; lzo_uint b; } __lzo_pu_u; typedef union { lzo_bytep a; lzo_uint32_t b; } __lzo_pu32_u; /* deprecated defines */ #if !defined(LZO_SIZEOF_LZO_UINT) #define LZO_SIZEOF_LZO_UINT LZO_SIZEOF_LZO_INT #endif #if defined(LZO_CFG_COMPAT) #define __LZOCONF_H 1 #if defined(LZO_ARCH_I086) #define __LZO_i386 1 #elif defined(LZO_ARCH_I386) #define __LZO_i386 1 #endif #if defined(LZO_OS_DOS16) #define __LZO_DOS 1 #define __LZO_DOS16 1 #elif defined(LZO_OS_DOS32) #define __LZO_DOS 1 #elif defined(LZO_OS_WIN16) #define __LZO_WIN 1 #define __LZO_WIN16 1 #elif defined(LZO_OS_WIN32) #define __LZO_WIN 1 #endif #define __LZO_CMODEL /*empty*/ #define __LZO_DMODEL /*empty*/ #define __LZO_ENTRY __LZO_CDECL #define LZO_EXTERN_CDECL LZO_EXTERN #define LZO_ALIGN LZO_PTR_ALIGN_UP #define lzo_compress_asm_t lzo_compress_t #define lzo_decompress_asm_t lzo_decompress_t #endif /* LZO_CFG_COMPAT */ #ifdef __cplusplus } /* extern "C" */ #endif #endif /* already included */ /* vim:set ts=4 sw=4 et: */ ================================================ FILE: include/utils/lzodefs.h ================================================ /* lzodefs.h -- architecture, OS and compiler specific defines This file is part of the LZO real-time data compression library. Copyright (C) 1996-2015 Markus Franz Xaver Johannes Oberhumer All Rights Reserved. The LZO library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The LZO 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 General Public License for more details. You should have received a copy of the GNU General Public License along with the LZO library; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Markus F.X.J. Oberhumer http://www.oberhumer.com/opensource/lzo/ */ #ifndef __LZODEFS_H_INCLUDED #define __LZODEFS_H_INCLUDED 1 #if defined(__CYGWIN32__) && !defined(__CYGWIN__) # define __CYGWIN__ __CYGWIN32__ #endif #if 1 && defined(__INTERIX) && defined(__GNUC__) && !defined(_ALL_SOURCE) # define _ALL_SOURCE 1 #endif #if defined(__mips__) && defined(__R5900__) # if !defined(__LONG_MAX__) # define __LONG_MAX__ 9223372036854775807L # endif #endif #if !defined(LZO_CFG_NO_DISABLE_WUNDEF) #if defined(__ARMCC_VERSION) # pragma diag_suppress 193 #elif defined(__clang__) && defined(__clang_minor__) # pragma clang diagnostic ignored "-Wundef" #elif defined(__INTEL_COMPILER) # pragma warning(disable: 193) #elif defined(__KEIL__) && defined(__C166__) # pragma warning disable = 322 #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && !defined(__PATHSCALE__) # if ((__GNUC__-0) >= 5 || ((__GNUC__-0) == 4 && (__GNUC_MINOR__-0) >= 2)) # pragma GCC diagnostic ignored "-Wundef" # endif #elif defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__MWERKS__) # if ((_MSC_VER-0) >= 1300) # pragma warning(disable: 4668) # endif #endif #endif #if 0 && defined(__POCC__) && defined(_WIN32) # if (__POCC__ >= 400) # pragma warn(disable: 2216) # endif #endif #if 0 && defined(__WATCOMC__) # if (__WATCOMC__ >= 1050) && (__WATCOMC__ < 1060) # pragma warning 203 9 # endif #endif #if defined(__BORLANDC__) && defined(__MSDOS__) && !defined(__FLAT__) # pragma option -h #endif #if !(LZO_CFG_NO_DISABLE_WCRTNONSTDC) #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE 1 #endif #ifndef _CRT_NONSTDC_NO_WARNINGS #define _CRT_NONSTDC_NO_WARNINGS 1 #endif #ifndef _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_DEPRECATE 1 #endif #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS 1 #endif #endif #if 0 #define LZO_0xffffUL 0xfffful #define LZO_0xffffffffUL 0xfffffffful #else #define LZO_0xffffUL 65535ul #define LZO_0xffffffffUL 4294967295ul #endif #define LZO_0xffffL LZO_0xffffUL #define LZO_0xffffffffL LZO_0xffffffffUL #if (LZO_0xffffL == LZO_0xffffffffL) # error "your preprocessor is broken 1" #endif #if (16ul * 16384ul != 262144ul) # error "your preprocessor is broken 2" #endif #if 0 #if (32767 >= 4294967295ul) # error "your preprocessor is broken 3" #endif #if (65535u >= 4294967295ul) # error "your preprocessor is broken 4" #endif #endif #if defined(__COUNTER__) # ifndef LZO_CFG_USE_COUNTER # define LZO_CFG_USE_COUNTER 1 # endif #else # undef LZO_CFG_USE_COUNTER #endif #if (UINT_MAX == LZO_0xffffL) #if defined(__ZTC__) && defined(__I86__) && !defined(__OS2__) # if !defined(MSDOS) # define MSDOS 1 # endif # if !defined(_MSDOS) # define _MSDOS 1 # endif #elif 0 && defined(__VERSION) && defined(MB_LEN_MAX) # if (__VERSION == 520) && (MB_LEN_MAX == 1) # if !defined(__AZTEC_C__) # define __AZTEC_C__ __VERSION # endif # if !defined(__DOS__) # define __DOS__ 1 # endif # endif #endif #endif #if defined(_MSC_VER) && defined(M_I86HM) && (UINT_MAX == LZO_0xffffL) # define ptrdiff_t long # define _PTRDIFF_T_DEFINED 1 #endif #if (UINT_MAX == LZO_0xffffL) # undef __LZO_RENAME_A # undef __LZO_RENAME_B # if defined(__AZTEC_C__) && defined(__DOS__) # define __LZO_RENAME_A 1 # elif defined(_MSC_VER) && defined(MSDOS) # if (_MSC_VER < 600) # define __LZO_RENAME_A 1 # elif (_MSC_VER < 700) # define __LZO_RENAME_B 1 # endif # elif defined(__TSC__) && defined(__OS2__) # define __LZO_RENAME_A 1 # elif defined(__MSDOS__) && defined(__TURBOC__) && (__TURBOC__ < 0x0410) # define __LZO_RENAME_A 1 # elif defined(__PACIFIC__) && defined(DOS) # if !defined(__far) # define __far far # endif # if !defined(__near) # define __near near # endif # endif # if defined(__LZO_RENAME_A) # if !defined(__cdecl) # define __cdecl cdecl # endif # if !defined(__far) # define __far far # endif # if !defined(__huge) # define __huge huge # endif # if !defined(__near) # define __near near # endif # if !defined(__pascal) # define __pascal pascal # endif # if !defined(__huge) # define __huge huge # endif # elif defined(__LZO_RENAME_B) # if !defined(__cdecl) # define __cdecl _cdecl # endif # if !defined(__far) # define __far _far # endif # if !defined(__huge) # define __huge _huge # endif # if !defined(__near) # define __near _near # endif # if !defined(__pascal) # define __pascal _pascal # endif # elif (defined(__PUREC__) || defined(__TURBOC__)) && defined(__TOS__) # if !defined(__cdecl) # define __cdecl cdecl # endif # if !defined(__pascal) # define __pascal pascal # endif # endif # undef __LZO_RENAME_A # undef __LZO_RENAME_B #endif #if (UINT_MAX == LZO_0xffffL) #if defined(__AZTEC_C__) && defined(__DOS__) # define LZO_BROKEN_CDECL_ALT_SYNTAX 1 #elif defined(_MSC_VER) && defined(MSDOS) # if (_MSC_VER < 600) # define LZO_BROKEN_INTEGRAL_CONSTANTS 1 # endif # if (_MSC_VER < 700) # define LZO_BROKEN_INTEGRAL_PROMOTION 1 # define LZO_BROKEN_SIZEOF 1 # endif #elif defined(__PACIFIC__) && defined(DOS) # define LZO_BROKEN_INTEGRAL_CONSTANTS 1 #elif defined(__TURBOC__) && defined(__MSDOS__) # if (__TURBOC__ < 0x0150) # define LZO_BROKEN_CDECL_ALT_SYNTAX 1 # define LZO_BROKEN_INTEGRAL_CONSTANTS 1 # define LZO_BROKEN_INTEGRAL_PROMOTION 1 # endif # if (__TURBOC__ < 0x0200) # define LZO_BROKEN_SIZEOF 1 # endif # if (__TURBOC__ < 0x0400) && defined(__cplusplus) # define LZO_BROKEN_CDECL_ALT_SYNTAX 1 # endif #elif (defined(__PUREC__) || defined(__TURBOC__)) && defined(__TOS__) # define LZO_BROKEN_CDECL_ALT_SYNTAX 1 # define LZO_BROKEN_SIZEOF 1 #endif #endif #if defined(__WATCOMC__) && (__WATCOMC__ < 900) # define LZO_BROKEN_INTEGRAL_CONSTANTS 1 #endif #if defined(_CRAY) && defined(_CRAY1) # define LZO_BROKEN_SIGNED_RIGHT_SHIFT 1 #endif #define LZO_PP_STRINGIZE(x) #x #define LZO_PP_MACRO_EXPAND(x) LZO_PP_STRINGIZE(x) #define LZO_PP_CONCAT0() /*empty*/ #define LZO_PP_CONCAT1(a) a #define LZO_PP_CONCAT2(a,b) a ## b #define LZO_PP_CONCAT3(a,b,c) a ## b ## c #define LZO_PP_CONCAT4(a,b,c,d) a ## b ## c ## d #define LZO_PP_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e #define LZO_PP_CONCAT6(a,b,c,d,e,f) a ## b ## c ## d ## e ## f #define LZO_PP_CONCAT7(a,b,c,d,e,f,g) a ## b ## c ## d ## e ## f ## g #define LZO_PP_ECONCAT0() LZO_PP_CONCAT0() #define LZO_PP_ECONCAT1(a) LZO_PP_CONCAT1(a) #define LZO_PP_ECONCAT2(a,b) LZO_PP_CONCAT2(a,b) #define LZO_PP_ECONCAT3(a,b,c) LZO_PP_CONCAT3(a,b,c) #define LZO_PP_ECONCAT4(a,b,c,d) LZO_PP_CONCAT4(a,b,c,d) #define LZO_PP_ECONCAT5(a,b,c,d,e) LZO_PP_CONCAT5(a,b,c,d,e) #define LZO_PP_ECONCAT6(a,b,c,d,e,f) LZO_PP_CONCAT6(a,b,c,d,e,f) #define LZO_PP_ECONCAT7(a,b,c,d,e,f,g) LZO_PP_CONCAT7(a,b,c,d,e,f,g) #define LZO_PP_EMPTY /*empty*/ #define LZO_PP_EMPTY0() /*empty*/ #define LZO_PP_EMPTY1(a) /*empty*/ #define LZO_PP_EMPTY2(a,b) /*empty*/ #define LZO_PP_EMPTY3(a,b,c) /*empty*/ #define LZO_PP_EMPTY4(a,b,c,d) /*empty*/ #define LZO_PP_EMPTY5(a,b,c,d,e) /*empty*/ #define LZO_PP_EMPTY6(a,b,c,d,e,f) /*empty*/ #define LZO_PP_EMPTY7(a,b,c,d,e,f,g) /*empty*/ #if 1 #define LZO_CPP_STRINGIZE(x) #x #define LZO_CPP_MACRO_EXPAND(x) LZO_CPP_STRINGIZE(x) #define LZO_CPP_CONCAT2(a,b) a ## b #define LZO_CPP_CONCAT3(a,b,c) a ## b ## c #define LZO_CPP_CONCAT4(a,b,c,d) a ## b ## c ## d #define LZO_CPP_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e #define LZO_CPP_CONCAT6(a,b,c,d,e,f) a ## b ## c ## d ## e ## f #define LZO_CPP_CONCAT7(a,b,c,d,e,f,g) a ## b ## c ## d ## e ## f ## g #define LZO_CPP_ECONCAT2(a,b) LZO_CPP_CONCAT2(a,b) #define LZO_CPP_ECONCAT3(a,b,c) LZO_CPP_CONCAT3(a,b,c) #define LZO_CPP_ECONCAT4(a,b,c,d) LZO_CPP_CONCAT4(a,b,c,d) #define LZO_CPP_ECONCAT5(a,b,c,d,e) LZO_CPP_CONCAT5(a,b,c,d,e) #define LZO_CPP_ECONCAT6(a,b,c,d,e,f) LZO_CPP_CONCAT6(a,b,c,d,e,f) #define LZO_CPP_ECONCAT7(a,b,c,d,e,f,g) LZO_CPP_CONCAT7(a,b,c,d,e,f,g) #endif #define __LZO_MASK_GEN(o,b) (((((o) << ((b)-!!(b))) - (o)) << 1) + (o)*!!(b)) #if 1 && defined(__cplusplus) # if !defined(__STDC_CONSTANT_MACROS) # define __STDC_CONSTANT_MACROS 1 # endif # if !defined(__STDC_LIMIT_MACROS) # define __STDC_LIMIT_MACROS 1 # endif #endif #if defined(__cplusplus) # define LZO_EXTERN_C extern "C" # define LZO_EXTERN_C_BEGIN extern "C" { # define LZO_EXTERN_C_END } #else # define LZO_EXTERN_C extern # define LZO_EXTERN_C_BEGIN /*empty*/ # define LZO_EXTERN_C_END /*empty*/ #endif #if !defined(__LZO_OS_OVERRIDE) #if (LZO_OS_FREESTANDING) # define LZO_INFO_OS "freestanding" #elif (LZO_OS_EMBEDDED) # define LZO_INFO_OS "embedded" #elif 1 && defined(__IAR_SYSTEMS_ICC__) # define LZO_OS_EMBEDDED 1 # define LZO_INFO_OS "embedded" #elif defined(__CYGWIN__) && defined(__GNUC__) # define LZO_OS_CYGWIN 1 # define LZO_INFO_OS "cygwin" #elif defined(__EMX__) && defined(__GNUC__) # define LZO_OS_EMX 1 # define LZO_INFO_OS "emx" #elif defined(__BEOS__) # define LZO_OS_BEOS 1 # define LZO_INFO_OS "beos" #elif defined(__Lynx__) # define LZO_OS_LYNXOS 1 # define LZO_INFO_OS "lynxos" #elif defined(__OS400__) # define LZO_OS_OS400 1 # define LZO_INFO_OS "os400" #elif defined(__QNX__) # define LZO_OS_QNX 1 # define LZO_INFO_OS "qnx" #elif defined(__BORLANDC__) && defined(__DPMI32__) && (__BORLANDC__ >= 0x0460) # define LZO_OS_DOS32 1 # define LZO_INFO_OS "dos32" #elif defined(__BORLANDC__) && defined(__DPMI16__) # define LZO_OS_DOS16 1 # define LZO_INFO_OS "dos16" #elif defined(__ZTC__) && defined(DOS386) # define LZO_OS_DOS32 1 # define LZO_INFO_OS "dos32" #elif defined(__OS2__) || defined(__OS2V2__) # if (UINT_MAX == LZO_0xffffL) # define LZO_OS_OS216 1 # define LZO_INFO_OS "os216" # elif (UINT_MAX == LZO_0xffffffffL) # define LZO_OS_OS2 1 # define LZO_INFO_OS "os2" # else # error "check your limits.h header" # endif #elif defined(__WIN64__) || defined(_WIN64) || defined(WIN64) # define LZO_OS_WIN64 1 # define LZO_INFO_OS "win64" #elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32) || defined(__WINDOWS_386__) # define LZO_OS_WIN32 1 # define LZO_INFO_OS "win32" #elif defined(__MWERKS__) && defined(__INTEL__) # define LZO_OS_WIN32 1 # define LZO_INFO_OS "win32" #elif defined(__WINDOWS__) || defined(_WINDOWS) || defined(_Windows) # if (UINT_MAX == LZO_0xffffL) # define LZO_OS_WIN16 1 # define LZO_INFO_OS "win16" # elif (UINT_MAX == LZO_0xffffffffL) # define LZO_OS_WIN32 1 # define LZO_INFO_OS "win32" # else # error "check your limits.h header" # endif #elif defined(__DOS__) || defined(__MSDOS__) || defined(_MSDOS) || defined(MSDOS) || (defined(__PACIFIC__) && defined(DOS)) # if (UINT_MAX == LZO_0xffffL) # define LZO_OS_DOS16 1 # define LZO_INFO_OS "dos16" # elif (UINT_MAX == LZO_0xffffffffL) # define LZO_OS_DOS32 1 # define LZO_INFO_OS "dos32" # else # error "check your limits.h header" # endif #elif defined(__WATCOMC__) # if defined(__NT__) && (UINT_MAX == LZO_0xffffL) # define LZO_OS_DOS16 1 # define LZO_INFO_OS "dos16" # elif defined(__NT__) && (__WATCOMC__ < 1100) # define LZO_OS_WIN32 1 # define LZO_INFO_OS "win32" # elif defined(__linux__) || defined(__LINUX__) # define LZO_OS_POSIX 1 # define LZO_INFO_OS "posix" # else # error "please specify a target using the -bt compiler option" # endif #elif defined(__palmos__) # define LZO_OS_PALMOS 1 # define LZO_INFO_OS "palmos" #elif defined(__TOS__) || defined(__atarist__) # define LZO_OS_TOS 1 # define LZO_INFO_OS "tos" #elif defined(macintosh) && !defined(__arm__) && !defined(__i386__) && !defined(__ppc__) && !defined(__x64_64__) # define LZO_OS_MACCLASSIC 1 # define LZO_INFO_OS "macclassic" #elif defined(__VMS) # define LZO_OS_VMS 1 # define LZO_INFO_OS "vms" #elif (defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__) # define LZO_OS_CONSOLE 1 # define LZO_OS_CONSOLE_PS2 1 # define LZO_INFO_OS "console" # define LZO_INFO_OS_CONSOLE "ps2" #elif defined(__mips__) && defined(__psp__) # define LZO_OS_CONSOLE 1 # define LZO_OS_CONSOLE_PSP 1 # define LZO_INFO_OS "console" # define LZO_INFO_OS_CONSOLE "psp" #else # define LZO_OS_POSIX 1 # define LZO_INFO_OS "posix" #endif #if (LZO_OS_POSIX) # if defined(_AIX) || defined(__AIX__) || defined(__aix__) # define LZO_OS_POSIX_AIX 1 # define LZO_INFO_OS_POSIX "aix" # elif defined(__FreeBSD__) # define LZO_OS_POSIX_FREEBSD 1 # define LZO_INFO_OS_POSIX "freebsd" # elif defined(__hpux__) || defined(__hpux) # define LZO_OS_POSIX_HPUX 1 # define LZO_INFO_OS_POSIX "hpux" # elif defined(__INTERIX) # define LZO_OS_POSIX_INTERIX 1 # define LZO_INFO_OS_POSIX "interix" # elif defined(__IRIX__) || defined(__irix__) # define LZO_OS_POSIX_IRIX 1 # define LZO_INFO_OS_POSIX "irix" # elif defined(__linux__) || defined(__linux) || defined(__LINUX__) # define LZO_OS_POSIX_LINUX 1 # define LZO_INFO_OS_POSIX "linux" # elif defined(__APPLE__) && defined(__MACH__) # if ((__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__-0) >= 20000) # define LZO_OS_POSIX_DARWIN 1040 # define LZO_INFO_OS_POSIX "darwin_iphone" # elif ((__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__-0) >= 1040) # define LZO_OS_POSIX_DARWIN __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ # define LZO_INFO_OS_POSIX "darwin" # else # define LZO_OS_POSIX_DARWIN 1 # define LZO_INFO_OS_POSIX "darwin" # endif # define LZO_OS_POSIX_MACOSX LZO_OS_POSIX_DARWIN # elif defined(__minix__) || defined(__minix) # define LZO_OS_POSIX_MINIX 1 # define LZO_INFO_OS_POSIX "minix" # elif defined(__NetBSD__) # define LZO_OS_POSIX_NETBSD 1 # define LZO_INFO_OS_POSIX "netbsd" # elif defined(__OpenBSD__) # define LZO_OS_POSIX_OPENBSD 1 # define LZO_INFO_OS_POSIX "openbsd" # elif defined(__osf__) # define LZO_OS_POSIX_OSF 1 # define LZO_INFO_OS_POSIX "osf" # elif defined(__solaris__) || defined(__sun) # if defined(__SVR4) || defined(__svr4__) # define LZO_OS_POSIX_SOLARIS 1 # define LZO_INFO_OS_POSIX "solaris" # else # define LZO_OS_POSIX_SUNOS 1 # define LZO_INFO_OS_POSIX "sunos" # endif # elif defined(__ultrix__) || defined(__ultrix) # define LZO_OS_POSIX_ULTRIX 1 # define LZO_INFO_OS_POSIX "ultrix" # elif defined(_UNICOS) # define LZO_OS_POSIX_UNICOS 1 # define LZO_INFO_OS_POSIX "unicos" # else # define LZO_OS_POSIX_UNKNOWN 1 # define LZO_INFO_OS_POSIX "unknown" # endif #endif #endif #if (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) # if (UINT_MAX != LZO_0xffffL) # error "unexpected configuration - check your compiler defines" # endif # if (ULONG_MAX != LZO_0xffffffffL) # error "unexpected configuration - check your compiler defines" # endif #endif #if (LZO_OS_DOS32 || LZO_OS_OS2 || LZO_OS_WIN32 || LZO_OS_WIN64) # if (UINT_MAX != LZO_0xffffffffL) # error "unexpected configuration - check your compiler defines" # endif # if (ULONG_MAX != LZO_0xffffffffL) # error "unexpected configuration - check your compiler defines" # endif #endif #if defined(CIL) && defined(_GNUCC) && defined(__GNUC__) # define LZO_CC_CILLY 1 # define LZO_INFO_CC "Cilly" # if defined(__CILLY__) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__CILLY__) # else # define LZO_INFO_CCVER "unknown" # endif #elif 0 && defined(SDCC) && defined(__VERSION__) && !defined(__GNUC__) # define LZO_CC_SDCC 1 # define LZO_INFO_CC "sdcc" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(SDCC) #elif defined(__PATHSCALE__) && defined(__PATHCC_PATCHLEVEL__) # define LZO_CC_PATHSCALE (__PATHCC__ * 0x10000L + (__PATHCC_MINOR__-0) * 0x100 + (__PATHCC_PATCHLEVEL__-0)) # define LZO_INFO_CC "Pathscale C" # define LZO_INFO_CCVER __PATHSCALE__ # if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # define LZO_CC_PATHSCALE_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # endif #elif defined(__INTEL_COMPILER) && ((__INTEL_COMPILER-0) > 0) # define LZO_CC_INTELC __INTEL_COMPILER # define LZO_INFO_CC "Intel C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__INTEL_COMPILER) # if defined(_MSC_VER) && ((_MSC_VER-0) > 0) # define LZO_CC_INTELC_MSC _MSC_VER # elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # define LZO_CC_INTELC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # endif #elif defined(__POCC__) && defined(_WIN32) # define LZO_CC_PELLESC 1 # define LZO_INFO_CC "Pelles C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__POCC__) #elif defined(__ARMCC_VERSION) && defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # if defined(__GNUC_PATCHLEVEL__) # define LZO_CC_ARMCC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # else # define LZO_CC_ARMCC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) # endif # define LZO_CC_ARMCC __ARMCC_VERSION # define LZO_INFO_CC "ARM C Compiler" # define LZO_INFO_CCVER __VERSION__ #elif defined(__clang__) && defined(__llvm__) && defined(__VERSION__) # if defined(__clang_major__) && defined(__clang_minor__) && defined(__clang_patchlevel__) # define LZO_CC_CLANG (__clang_major__ * 0x10000L + (__clang_minor__-0) * 0x100 + (__clang_patchlevel__-0)) # else # define LZO_CC_CLANG 0x010000L # endif # if defined(_MSC_VER) && ((_MSC_VER-0) > 0) # define LZO_CC_CLANG_MSC _MSC_VER # elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # define LZO_CC_CLANG_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # endif # define LZO_INFO_CC "clang" # define LZO_INFO_CCVER __VERSION__ #elif defined(__llvm__) && defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # if defined(__GNUC_PATCHLEVEL__) # define LZO_CC_LLVM_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # else # define LZO_CC_LLVM_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) # endif # define LZO_CC_LLVM LZO_CC_LLVM_GNUC # define LZO_INFO_CC "llvm-gcc" # define LZO_INFO_CCVER __VERSION__ #elif defined(__ACK__) && defined(_ACK) # define LZO_CC_ACK 1 # define LZO_INFO_CC "Amsterdam Compiler Kit C" # define LZO_INFO_CCVER "unknown" #elif defined(__ARMCC_VERSION) && !defined(__GNUC__) # define LZO_CC_ARMCC __ARMCC_VERSION # define LZO_CC_ARMCC_ARMCC __ARMCC_VERSION # define LZO_INFO_CC "ARM C Compiler" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__ARMCC_VERSION) #elif defined(__AZTEC_C__) # define LZO_CC_AZTECC 1 # define LZO_INFO_CC "Aztec C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__AZTEC_C__) #elif defined(__CODEGEARC__) # define LZO_CC_CODEGEARC 1 # define LZO_INFO_CC "CodeGear C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__CODEGEARC__) #elif defined(__BORLANDC__) # define LZO_CC_BORLANDC 1 # define LZO_INFO_CC "Borland C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__BORLANDC__) #elif defined(_CRAYC) && defined(_RELEASE) # define LZO_CC_CRAYC 1 # define LZO_INFO_CC "Cray C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_RELEASE) #elif defined(__DMC__) && defined(__SC__) # define LZO_CC_DMC 1 # define LZO_INFO_CC "Digital Mars C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__DMC__) #elif defined(__DECC) # define LZO_CC_DECC 1 # define LZO_INFO_CC "DEC C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__DECC) #elif (defined(__ghs) || defined(__ghs__)) && defined(__GHS_VERSION_NUMBER) && ((__GHS_VERSION_NUMBER-0) > 0) # define LZO_CC_GHS 1 # define LZO_INFO_CC "Green Hills C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__GHS_VERSION_NUMBER) # if defined(_MSC_VER) && ((_MSC_VER-0) > 0) # define LZO_CC_GHS_MSC _MSC_VER # elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # define LZO_CC_GHS_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # endif #elif defined(__HIGHC__) # define LZO_CC_HIGHC 1 # define LZO_INFO_CC "MetaWare High C" # define LZO_INFO_CCVER "unknown" #elif defined(__HP_aCC) && ((__HP_aCC-0) > 0) # define LZO_CC_HPACC __HP_aCC # define LZO_INFO_CC "HP aCC" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__HP_aCC) #elif defined(__IAR_SYSTEMS_ICC__) # define LZO_CC_IARC 1 # define LZO_INFO_CC "IAR C" # if defined(__VER__) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__VER__) # else # define LZO_INFO_CCVER "unknown" # endif #elif defined(__IBMC__) && ((__IBMC__-0) > 0) # define LZO_CC_IBMC __IBMC__ # define LZO_INFO_CC "IBM C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__IBMC__) #elif defined(__IBMCPP__) && ((__IBMCPP__-0) > 0) # define LZO_CC_IBMC __IBMCPP__ # define LZO_INFO_CC "IBM C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__IBMCPP__) #elif defined(__KEIL__) && defined(__C166__) # define LZO_CC_KEILC 1 # define LZO_INFO_CC "Keil C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__C166__) #elif defined(__LCC__) && defined(_WIN32) && defined(__LCCOPTIMLEVEL) # define LZO_CC_LCCWIN32 1 # define LZO_INFO_CC "lcc-win32" # define LZO_INFO_CCVER "unknown" #elif defined(__LCC__) # define LZO_CC_LCC 1 # define LZO_INFO_CC "lcc" # if defined(__LCC_VERSION__) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__LCC_VERSION__) # else # define LZO_INFO_CCVER "unknown" # endif #elif defined(__MWERKS__) && ((__MWERKS__-0) > 0) # define LZO_CC_MWERKS __MWERKS__ # define LZO_INFO_CC "Metrowerks C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__MWERKS__) #elif (defined(__NDPC__) || defined(__NDPX__)) && defined(__i386) # define LZO_CC_NDPC 1 # define LZO_INFO_CC "Microway NDP C" # define LZO_INFO_CCVER "unknown" #elif defined(__PACIFIC__) # define LZO_CC_PACIFICC 1 # define LZO_INFO_CC "Pacific C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PACIFIC__) #elif defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) # if defined(__PGIC_PATCHLEVEL__) # define LZO_CC_PGI (__PGIC__ * 0x10000L + (__PGIC_MINOR__-0) * 0x100 + (__PGIC_PATCHLEVEL__-0)) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PGIC__) "." LZO_PP_MACRO_EXPAND(__PGIC_MINOR__) "." LZO_PP_MACRO_EXPAND(__PGIC_PATCHLEVEL__) # else # define LZO_CC_PGI (__PGIC__ * 0x10000L + (__PGIC_MINOR__-0) * 0x100) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PGIC__) "." LZO_PP_MACRO_EXPAND(__PGIC_MINOR__) ".0" # endif # define LZO_INFO_CC "Portland Group PGI C" #elif defined(__PGI) && (defined(__linux__) || defined(__WIN32__)) # define LZO_CC_PGI 1 # define LZO_INFO_CC "Portland Group PGI C" # define LZO_INFO_CCVER "unknown" #elif defined(__PUREC__) && defined(__TOS__) # define LZO_CC_PUREC 1 # define LZO_INFO_CC "Pure C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PUREC__) #elif defined(__SC__) && defined(__ZTC__) # define LZO_CC_SYMANTECC 1 # define LZO_INFO_CC "Symantec C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SC__) #elif defined(__SUNPRO_C) # define LZO_INFO_CC "SunPro C" # if ((__SUNPRO_C-0) > 0) # define LZO_CC_SUNPROC __SUNPRO_C # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SUNPRO_C) # else # define LZO_CC_SUNPROC 1 # define LZO_INFO_CCVER "unknown" # endif #elif defined(__SUNPRO_CC) # define LZO_INFO_CC "SunPro C" # if ((__SUNPRO_CC-0) > 0) # define LZO_CC_SUNPROC __SUNPRO_CC # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SUNPRO_CC) # else # define LZO_CC_SUNPROC 1 # define LZO_INFO_CCVER "unknown" # endif #elif defined(__TINYC__) # define LZO_CC_TINYC 1 # define LZO_INFO_CC "Tiny C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TINYC__) #elif defined(__TSC__) # define LZO_CC_TOPSPEEDC 1 # define LZO_INFO_CC "TopSpeed C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TSC__) #elif defined(__WATCOMC__) # define LZO_CC_WATCOMC 1 # define LZO_INFO_CC "Watcom C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__WATCOMC__) #elif defined(__TURBOC__) # define LZO_CC_TURBOC 1 # define LZO_INFO_CC "Turbo C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TURBOC__) #elif defined(__ZTC__) # define LZO_CC_ZORTECHC 1 # define LZO_INFO_CC "Zortech C" # if ((__ZTC__-0) == 0x310) # define LZO_INFO_CCVER "0x310" # else # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__ZTC__) # endif #elif defined(__GNUC__) && defined(__VERSION__) # if defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) # define LZO_CC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # elif defined(__GNUC_MINOR__) # define LZO_CC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) # else # define LZO_CC_GNUC (__GNUC__ * 0x10000L) # endif # define LZO_INFO_CC "gcc" # define LZO_INFO_CCVER __VERSION__ #elif defined(_MSC_VER) && ((_MSC_VER-0) > 0) # define LZO_CC_MSC _MSC_VER # define LZO_INFO_CC "Microsoft C" # if defined(_MSC_FULL_VER) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_MSC_VER) "." LZO_PP_MACRO_EXPAND(_MSC_FULL_VER) # else # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_MSC_VER) # endif #else # define LZO_CC_UNKNOWN 1 # define LZO_INFO_CC "unknown" # define LZO_INFO_CCVER "unknown" #endif #if (LZO_CC_GNUC) && defined(__OPEN64__) # if defined(__OPENCC__) && defined(__OPENCC_MINOR__) && defined(__OPENCC_PATCHLEVEL__) # define LZO_CC_OPEN64 (__OPENCC__ * 0x10000L + (__OPENCC_MINOR__-0) * 0x100 + (__OPENCC_PATCHLEVEL__-0)) # define LZO_CC_OPEN64_GNUC LZO_CC_GNUC # endif #endif #if (LZO_CC_GNUC) && defined(__PCC__) # if defined(__PCC__) && defined(__PCC_MINOR__) && defined(__PCC_MINORMINOR__) # define LZO_CC_PCC (__PCC__ * 0x10000L + (__PCC_MINOR__-0) * 0x100 + (__PCC_MINORMINOR__-0)) # define LZO_CC_PCC_GNUC LZO_CC_GNUC # endif #endif #if 0 && (LZO_CC_MSC && (_MSC_VER >= 1200)) && !defined(_MSC_FULL_VER) # error "LZO_CC_MSC: _MSC_FULL_VER is not defined" #endif #if !defined(__LZO_ARCH_OVERRIDE) && !(LZO_ARCH_GENERIC) && defined(_CRAY) # if (UINT_MAX > LZO_0xffffffffL) && defined(_CRAY) # if defined(_CRAYMPP) || defined(_CRAYT3D) || defined(_CRAYT3E) # define LZO_ARCH_CRAY_MPP 1 # elif defined(_CRAY1) # define LZO_ARCH_CRAY_PVP 1 # endif # endif #endif #if !defined(__LZO_ARCH_OVERRIDE) #if (LZO_ARCH_GENERIC) # define LZO_INFO_ARCH "generic" #elif (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) # define LZO_ARCH_I086 1 # define LZO_INFO_ARCH "i086" #elif defined(__aarch64__) # define LZO_ARCH_ARM64 1 # define LZO_INFO_ARCH "arm64" #elif defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA) # define LZO_ARCH_ALPHA 1 # define LZO_INFO_ARCH "alpha" #elif (LZO_ARCH_CRAY_MPP) && (defined(_CRAYT3D) || defined(_CRAYT3E)) # define LZO_ARCH_ALPHA 1 # define LZO_INFO_ARCH "alpha" #elif defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64) # define LZO_ARCH_AMD64 1 # define LZO_INFO_ARCH "amd64" #elif defined(__arm__) || defined(_M_ARM) # define LZO_ARCH_ARM 1 # define LZO_INFO_ARCH "arm" #elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCARM__) # define LZO_ARCH_ARM 1 # define LZO_INFO_ARCH "arm" #elif (UINT_MAX <= LZO_0xffffL) && defined(__AVR__) # define LZO_ARCH_AVR 1 # define LZO_INFO_ARCH "avr" #elif defined(__avr32__) || defined(__AVR32__) # define LZO_ARCH_AVR32 1 # define LZO_INFO_ARCH "avr32" #elif defined(__bfin__) # define LZO_ARCH_BLACKFIN 1 # define LZO_INFO_ARCH "blackfin" #elif (UINT_MAX == LZO_0xffffL) && defined(__C166__) # define LZO_ARCH_C166 1 # define LZO_INFO_ARCH "c166" #elif defined(__cris__) # define LZO_ARCH_CRIS 1 # define LZO_INFO_ARCH "cris" #elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCEZ80__) # define LZO_ARCH_EZ80 1 # define LZO_INFO_ARCH "ez80" #elif defined(__H8300__) || defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) # define LZO_ARCH_H8300 1 # define LZO_INFO_ARCH "h8300" #elif defined(__hppa__) || defined(__hppa) # define LZO_ARCH_HPPA 1 # define LZO_INFO_ARCH "hppa" #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386) # define LZO_ARCH_I386 1 # define LZO_ARCH_IA32 1 # define LZO_INFO_ARCH "i386" #elif (LZO_CC_ZORTECHC && defined(__I86__)) # define LZO_ARCH_I386 1 # define LZO_ARCH_IA32 1 # define LZO_INFO_ARCH "i386" #elif (LZO_OS_DOS32 && LZO_CC_HIGHC) && defined(_I386) # define LZO_ARCH_I386 1 # define LZO_ARCH_IA32 1 # define LZO_INFO_ARCH "i386" #elif defined(__ia64__) || defined(__ia64) || defined(_M_IA64) # define LZO_ARCH_IA64 1 # define LZO_INFO_ARCH "ia64" #elif (UINT_MAX == LZO_0xffffL) && defined(__m32c__) # define LZO_ARCH_M16C 1 # define LZO_INFO_ARCH "m16c" #elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCM16C__) # define LZO_ARCH_M16C 1 # define LZO_INFO_ARCH "m16c" #elif defined(__m32r__) # define LZO_ARCH_M32R 1 # define LZO_INFO_ARCH "m32r" #elif (LZO_OS_TOS) || defined(__m68k__) || defined(__m68000__) || defined(__mc68000__) || defined(__mc68020__) || defined(_M_M68K) # define LZO_ARCH_M68K 1 # define LZO_INFO_ARCH "m68k" #elif (UINT_MAX == LZO_0xffffL) && defined(__C251__) # define LZO_ARCH_MCS251 1 # define LZO_INFO_ARCH "mcs251" #elif (UINT_MAX == LZO_0xffffL) && defined(__C51__) # define LZO_ARCH_MCS51 1 # define LZO_INFO_ARCH "mcs51" #elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICC8051__) # define LZO_ARCH_MCS51 1 # define LZO_INFO_ARCH "mcs51" #elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000) # define LZO_ARCH_MIPS 1 # define LZO_INFO_ARCH "mips" #elif (UINT_MAX == LZO_0xffffL) && defined(__MSP430__) # define LZO_ARCH_MSP430 1 # define LZO_INFO_ARCH "msp430" #elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICC430__) # define LZO_ARCH_MSP430 1 # define LZO_INFO_ARCH "msp430" #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR) # define LZO_ARCH_POWERPC 1 # define LZO_INFO_ARCH "powerpc" #elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x) # define LZO_ARCH_S390 1 # define LZO_INFO_ARCH "s390" #elif defined(__sh__) || defined(_M_SH) # define LZO_ARCH_SH 1 # define LZO_INFO_ARCH "sh" #elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8) # define LZO_ARCH_SPARC 1 # define LZO_INFO_ARCH "sparc" #elif defined(__SPU__) # define LZO_ARCH_SPU 1 # define LZO_INFO_ARCH "spu" #elif (UINT_MAX == LZO_0xffffL) && defined(__z80) # define LZO_ARCH_Z80 1 # define LZO_INFO_ARCH "z80" #elif (LZO_ARCH_CRAY_PVP) # if defined(_CRAYSV1) # define LZO_ARCH_CRAY_SV1 1 # define LZO_INFO_ARCH "cray_sv1" # elif (_ADDR64) # define LZO_ARCH_CRAY_T90 1 # define LZO_INFO_ARCH "cray_t90" # elif (_ADDR32) # define LZO_ARCH_CRAY_YMP 1 # define LZO_INFO_ARCH "cray_ymp" # else # define LZO_ARCH_CRAY_XMP 1 # define LZO_INFO_ARCH "cray_xmp" # endif #else # define LZO_ARCH_UNKNOWN 1 # define LZO_INFO_ARCH "unknown" #endif #endif #if !defined(LZO_ARCH_ARM_THUMB2) #if (LZO_ARCH_ARM) # if defined(__ARM_ARCH_ISA_THUMB) # if ((__ARM_ARCH_ISA_THUMB)+0 >= 2) # define LZO_ARCH_ARM_THUMB2 1 # endif # elif 1 && defined(__thumb2__) # define LZO_ARCH_ARM_THUMB2 1 # elif 1 && defined(__TARGET_ARCH_THUMB) && ((__TARGET_ARCH_THUMB)+0 >= 4) # define LZO_ARCH_ARM_THUMB2 1 # endif #endif #endif #if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_DOS32 || LZO_OS_OS2) # error "FIXME - missing define for CPU architecture" #endif #if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_WIN32) # error "FIXME - missing LZO_OS_WIN32 define for CPU architecture" #endif #if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_WIN64) # error "FIXME - missing LZO_OS_WIN64 define for CPU architecture" #endif #if (LZO_OS_OS216 || LZO_OS_WIN16) # define LZO_ARCH_I086PM 1 #elif 1 && (LZO_OS_DOS16 && defined(BLX286)) # define LZO_ARCH_I086PM 1 #elif 1 && (LZO_OS_DOS16 && defined(DOSX286)) # define LZO_ARCH_I086PM 1 #elif 1 && (LZO_OS_DOS16 && LZO_CC_BORLANDC && defined(__DPMI16__)) # define LZO_ARCH_I086PM 1 #endif #if (LZO_ARCH_AMD64 && !LZO_ARCH_X64) # define LZO_ARCH_X64 1 #elif (!LZO_ARCH_AMD64 && LZO_ARCH_X64) && defined(__LZO_ARCH_OVERRIDE) # define LZO_ARCH_AMD64 1 #endif #if (LZO_ARCH_ARM64 && !LZO_ARCH_AARCH64) # define LZO_ARCH_AARCH64 1 #elif (!LZO_ARCH_ARM64 && LZO_ARCH_AARCH64) && defined(__LZO_ARCH_OVERRIDE) # define LZO_ARCH_ARM64 1 #endif #if (LZO_ARCH_I386 && !LZO_ARCH_X86) # define LZO_ARCH_X86 1 #elif (!LZO_ARCH_I386 && LZO_ARCH_X86) && defined(__LZO_ARCH_OVERRIDE) # define LZO_ARCH_I386 1 #endif #if (LZO_ARCH_AMD64 && !LZO_ARCH_X64) || (!LZO_ARCH_AMD64 && LZO_ARCH_X64) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_ARM64 && !LZO_ARCH_AARCH64) || (!LZO_ARCH_ARM64 && LZO_ARCH_AARCH64) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_I386 && !LZO_ARCH_X86) || (!LZO_ARCH_I386 && LZO_ARCH_X86) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_ARM_THUMB1 && !LZO_ARCH_ARM) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_ARM_THUMB2 && !LZO_ARCH_ARM) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_ARM_THUMB1 && LZO_ARCH_ARM_THUMB2) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_I086PM && !LZO_ARCH_I086) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_I086) # if (UINT_MAX != LZO_0xffffL) # error "unexpected configuration - check your compiler defines" # endif # if (ULONG_MAX != LZO_0xffffffffL) # error "unexpected configuration - check your compiler defines" # endif #endif #if (LZO_ARCH_I386) # if (UINT_MAX != LZO_0xffffL) && defined(__i386_int16__) # error "unexpected configuration - check your compiler defines" # endif # if (UINT_MAX != LZO_0xffffffffL) && !defined(__i386_int16__) # error "unexpected configuration - check your compiler defines" # endif # if (ULONG_MAX != LZO_0xffffffffL) # error "unexpected configuration - check your compiler defines" # endif #endif #if (LZO_ARCH_AMD64 || LZO_ARCH_I386) # if !defined(LZO_TARGET_FEATURE_SSE2) # if defined(__SSE2__) # define LZO_TARGET_FEATURE_SSE2 1 # elif defined(_MSC_VER) && (defined(_M_IX86_FP) && ((_M_IX86_FP)+0 >= 2)) # define LZO_TARGET_FEATURE_SSE2 1 # elif (LZO_CC_INTELC_MSC || LZO_CC_MSC) && defined(_M_AMD64) # define LZO_TARGET_FEATURE_SSE2 1 # endif # endif # if !defined(LZO_TARGET_FEATURE_SSSE3) # if (LZO_TARGET_FEATURE_SSE2) # if defined(__SSSE3__) # define LZO_TARGET_FEATURE_SSSE3 1 # elif defined(_MSC_VER) && defined(__AVX__) # define LZO_TARGET_FEATURE_SSSE3 1 # endif # endif # endif # if !defined(LZO_TARGET_FEATURE_SSE4_2) # if (LZO_TARGET_FEATURE_SSSE3) # if defined(__SSE4_2__) # define LZO_TARGET_FEATURE_SSE4_2 1 # endif # endif # endif # if !defined(LZO_TARGET_FEATURE_AVX) # if (LZO_TARGET_FEATURE_SSSE3) # if defined(__AVX__) # define LZO_TARGET_FEATURE_AVX 1 # endif # endif # endif # if !defined(LZO_TARGET_FEATURE_AVX2) # if (LZO_TARGET_FEATURE_AVX) # if defined(__AVX2__) # define LZO_TARGET_FEATURE_AVX2 1 # endif # endif # endif #endif #if (LZO_TARGET_FEATURE_SSSE3 && !(LZO_TARGET_FEATURE_SSE2)) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_TARGET_FEATURE_SSE4_2 && !(LZO_TARGET_FEATURE_SSSE3)) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_TARGET_FEATURE_AVX && !(LZO_TARGET_FEATURE_SSSE3)) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_TARGET_FEATURE_AVX2 && !(LZO_TARGET_FEATURE_AVX)) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_ARM) # if !defined(LZO_TARGET_FEATURE_NEON) # if defined(__ARM_NEON) && ((__ARM_NEON)+0) # define LZO_TARGET_FEATURE_NEON 1 # elif 1 && defined(__ARM_NEON__) && ((__ARM_NEON__)+0) # define LZO_TARGET_FEATURE_NEON 1 # elif 1 && defined(__TARGET_FEATURE_NEON) && ((__TARGET_FEATURE_NEON)+0) # define LZO_TARGET_FEATURE_NEON 1 # endif # endif #elif (LZO_ARCH_ARM64) # if !defined(LZO_TARGET_FEATURE_NEON) # if 1 # define LZO_TARGET_FEATURE_NEON 1 # endif # endif #endif #if 0 #elif !defined(__LZO_MM_OVERRIDE) #if (LZO_ARCH_I086) #if (UINT_MAX != LZO_0xffffL) # error "unexpected configuration - check your compiler defines" #endif #if defined(__TINY__) || defined(M_I86TM) || defined(_M_I86TM) # define LZO_MM_TINY 1 #elif defined(__HUGE__) || defined(_HUGE_) || defined(M_I86HM) || defined(_M_I86HM) # define LZO_MM_HUGE 1 #elif defined(__SMALL__) || defined(M_I86SM) || defined(_M_I86SM) || defined(SMALL_MODEL) # define LZO_MM_SMALL 1 #elif defined(__MEDIUM__) || defined(M_I86MM) || defined(_M_I86MM) # define LZO_MM_MEDIUM 1 #elif defined(__COMPACT__) || defined(M_I86CM) || defined(_M_I86CM) # define LZO_MM_COMPACT 1 #elif defined(__LARGE__) || defined(M_I86LM) || defined(_M_I86LM) || defined(LARGE_MODEL) # define LZO_MM_LARGE 1 #elif (LZO_CC_AZTECC) # if defined(_LARGE_CODE) && defined(_LARGE_DATA) # define LZO_MM_LARGE 1 # elif defined(_LARGE_CODE) # define LZO_MM_MEDIUM 1 # elif defined(_LARGE_DATA) # define LZO_MM_COMPACT 1 # else # define LZO_MM_SMALL 1 # endif #elif (LZO_CC_ZORTECHC && defined(__VCM__)) # define LZO_MM_LARGE 1 #else # error "unknown LZO_ARCH_I086 memory model" #endif #if (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) #define LZO_HAVE_MM_HUGE_PTR 1 #define LZO_HAVE_MM_HUGE_ARRAY 1 #if (LZO_MM_TINY) # undef LZO_HAVE_MM_HUGE_ARRAY #endif #if (LZO_CC_AZTECC || LZO_CC_PACIFICC || LZO_CC_ZORTECHC) # undef LZO_HAVE_MM_HUGE_PTR # undef LZO_HAVE_MM_HUGE_ARRAY #elif (LZO_CC_DMC || LZO_CC_SYMANTECC) # undef LZO_HAVE_MM_HUGE_ARRAY #elif (LZO_CC_MSC && defined(_QC)) # undef LZO_HAVE_MM_HUGE_ARRAY # if (_MSC_VER < 600) # undef LZO_HAVE_MM_HUGE_PTR # endif #elif (LZO_CC_TURBOC && (__TURBOC__ < 0x0295)) # undef LZO_HAVE_MM_HUGE_ARRAY #endif #if (LZO_ARCH_I086PM) && !(LZO_HAVE_MM_HUGE_PTR) # if (LZO_OS_DOS16) # error "unexpected configuration - check your compiler defines" # elif (LZO_CC_ZORTECHC) # else # error "unexpected configuration - check your compiler defines" # endif #endif #ifdef __cplusplus extern "C" { #endif #if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0200)) extern void __near __cdecl _AHSHIFT(void); # define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) #elif (LZO_CC_DMC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) extern void __near __cdecl _AHSHIFT(void); # define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) #elif (LZO_CC_MSC || LZO_CC_TOPSPEEDC) extern void __near __cdecl _AHSHIFT(void); # define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) #elif (LZO_CC_TURBOC && (__TURBOC__ >= 0x0295)) extern void __near __cdecl _AHSHIFT(void); # define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) #elif ((LZO_CC_AZTECC || LZO_CC_PACIFICC || LZO_CC_TURBOC) && LZO_OS_DOS16) # define LZO_MM_AHSHIFT 12 #elif (LZO_CC_WATCOMC) extern unsigned char _HShift; # define LZO_MM_AHSHIFT ((unsigned) _HShift) #else # error "FIXME - implement LZO_MM_AHSHIFT" #endif #ifdef __cplusplus } #endif #endif #elif (LZO_ARCH_C166) #if !defined(__MODEL__) # error "FIXME - LZO_ARCH_C166 __MODEL__" #elif ((__MODEL__) == 0) # define LZO_MM_SMALL 1 #elif ((__MODEL__) == 1) # define LZO_MM_SMALL 1 #elif ((__MODEL__) == 2) # define LZO_MM_LARGE 1 #elif ((__MODEL__) == 3) # define LZO_MM_TINY 1 #elif ((__MODEL__) == 4) # define LZO_MM_XTINY 1 #elif ((__MODEL__) == 5) # define LZO_MM_XSMALL 1 #else # error "FIXME - LZO_ARCH_C166 __MODEL__" #endif #elif (LZO_ARCH_MCS251) #if !defined(__MODEL__) # error "FIXME - LZO_ARCH_MCS251 __MODEL__" #elif ((__MODEL__) == 0) # define LZO_MM_SMALL 1 #elif ((__MODEL__) == 2) # define LZO_MM_LARGE 1 #elif ((__MODEL__) == 3) # define LZO_MM_TINY 1 #elif ((__MODEL__) == 4) # define LZO_MM_XTINY 1 #elif ((__MODEL__) == 5) # define LZO_MM_XSMALL 1 #else # error "FIXME - LZO_ARCH_MCS251 __MODEL__" #endif #elif (LZO_ARCH_MCS51) #if !defined(__MODEL__) # error "FIXME - LZO_ARCH_MCS51 __MODEL__" #elif ((__MODEL__) == 1) # define LZO_MM_SMALL 1 #elif ((__MODEL__) == 2) # define LZO_MM_LARGE 1 #elif ((__MODEL__) == 3) # define LZO_MM_TINY 1 #elif ((__MODEL__) == 4) # define LZO_MM_XTINY 1 #elif ((__MODEL__) == 5) # define LZO_MM_XSMALL 1 #else # error "FIXME - LZO_ARCH_MCS51 __MODEL__" #endif #elif (LZO_ARCH_CRAY_PVP) # define LZO_MM_PVP 1 #else # define LZO_MM_FLAT 1 #endif #if (LZO_MM_COMPACT) # define LZO_INFO_MM "compact" #elif (LZO_MM_FLAT) # define LZO_INFO_MM "flat" #elif (LZO_MM_HUGE) # define LZO_INFO_MM "huge" #elif (LZO_MM_LARGE) # define LZO_INFO_MM "large" #elif (LZO_MM_MEDIUM) # define LZO_INFO_MM "medium" #elif (LZO_MM_PVP) # define LZO_INFO_MM "pvp" #elif (LZO_MM_SMALL) # define LZO_INFO_MM "small" #elif (LZO_MM_TINY) # define LZO_INFO_MM "tiny" #else # error "unknown memory model" #endif #endif #if !defined(__lzo_gnuc_extension__) #if (LZO_CC_GNUC >= 0x020800ul) # define __lzo_gnuc_extension__ __extension__ #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_gnuc_extension__ __extension__ #elif (LZO_CC_IBMC >= 600) # define __lzo_gnuc_extension__ __extension__ #else #endif #endif #if !defined(__lzo_gnuc_extension__) # define __lzo_gnuc_extension__ /*empty*/ #endif #if !defined(lzo_has_builtin) #if (LZO_CC_CLANG) && defined(__has_builtin) # define lzo_has_builtin __has_builtin #endif #endif #if !defined(lzo_has_builtin) # define lzo_has_builtin(x) 0 #endif #if !defined(lzo_has_attribute) #if (LZO_CC_CLANG) && defined(__has_attribute) # define lzo_has_attribute __has_attribute #endif #endif #if !defined(lzo_has_attribute) # define lzo_has_attribute(x) 0 #endif #if !defined(lzo_has_declspec_attribute) #if (LZO_CC_CLANG) && defined(__has_declspec_attribute) # define lzo_has_declspec_attribute __has_declspec_attribute #endif #endif #if !defined(lzo_has_declspec_attribute) # define lzo_has_declspec_attribute(x) 0 #endif #if !defined(lzo_has_feature) #if (LZO_CC_CLANG) && defined(__has_feature) # define lzo_has_feature __has_feature #endif #endif #if !defined(lzo_has_feature) # define lzo_has_feature(x) 0 #endif #if !defined(lzo_has_extension) #if (LZO_CC_CLANG) && defined(__has_extension) # define lzo_has_extension __has_extension #elif (LZO_CC_CLANG) && defined(__has_feature) # define lzo_has_extension __has_feature #endif #endif #if !defined(lzo_has_extension) # define lzo_has_extension 0 #endif #if !defined(LZO_CFG_USE_NEW_STYLE_CASTS) && defined(__cplusplus) && 0 # if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) # define LZO_CFG_USE_NEW_STYLE_CASTS 0 # elif (LZO_CC_INTELC && (__INTEL_COMPILER < 1200)) # define LZO_CFG_USE_NEW_STYLE_CASTS 0 # else # define LZO_CFG_USE_NEW_STYLE_CASTS 1 # endif #endif #if !defined(LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_CFG_USE_NEW_STYLE_CASTS 0 #endif #if !defined(__cplusplus) # if defined(LZO_CFG_USE_NEW_STYLE_CASTS) # undef LZO_CFG_USE_NEW_STYLE_CASTS # endif # define LZO_CFG_USE_NEW_STYLE_CASTS 0 #endif #if !defined(LZO_REINTERPRET_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_REINTERPRET_CAST(t,e) (reinterpret_cast (e)) # endif #endif #if !defined(LZO_REINTERPRET_CAST) # define LZO_REINTERPRET_CAST(t,e) ((t) (e)) #endif #if !defined(LZO_STATIC_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_STATIC_CAST(t,e) (static_cast (e)) # endif #endif #if !defined(LZO_STATIC_CAST) # define LZO_STATIC_CAST(t,e) ((t) (e)) #endif #if !defined(LZO_STATIC_CAST2) # define LZO_STATIC_CAST2(t1,t2,e) LZO_STATIC_CAST(t1, LZO_STATIC_CAST(t2, e)) #endif #if !defined(LZO_UNCONST_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_UNCONST_CAST(t,e) (const_cast (e)) # elif (LZO_HAVE_MM_HUGE_PTR) # define LZO_UNCONST_CAST(t,e) ((t) (e)) # elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_UNCONST_CAST(t,e) ((t) ((void *) ((lzo_uintptr_t) ((const void *) (e))))) # endif #endif #if !defined(LZO_UNCONST_CAST) # define LZO_UNCONST_CAST(t,e) ((t) ((void *) ((const void *) (e)))) #endif #if !defined(LZO_UNCONST_VOLATILE_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_UNCONST_VOLATILE_CAST(t,e) (const_cast (e)) # elif (LZO_HAVE_MM_HUGE_PTR) # define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) (e)) # elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) ((volatile void *) ((lzo_uintptr_t) ((volatile const void *) (e))))) # endif #endif #if !defined(LZO_UNCONST_VOLATILE_CAST) # define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) ((volatile void *) ((volatile const void *) (e)))) #endif #if !defined(LZO_UNVOLATILE_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_UNVOLATILE_CAST(t,e) (const_cast (e)) # elif (LZO_HAVE_MM_HUGE_PTR) # define LZO_UNVOLATILE_CAST(t,e) ((t) (e)) # elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_UNVOLATILE_CAST(t,e) ((t) ((void *) ((lzo_uintptr_t) ((volatile void *) (e))))) # endif #endif #if !defined(LZO_UNVOLATILE_CAST) # define LZO_UNVOLATILE_CAST(t,e) ((t) ((void *) ((volatile void *) (e)))) #endif #if !defined(LZO_UNVOLATILE_CONST_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_UNVOLATILE_CONST_CAST(t,e) (const_cast (e)) # elif (LZO_HAVE_MM_HUGE_PTR) # define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) (e)) # elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) ((const void *) ((lzo_uintptr_t) ((volatile const void *) (e))))) # endif #endif #if !defined(LZO_UNVOLATILE_CONST_CAST) # define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) ((const void *) ((volatile const void *) (e)))) #endif #if !defined(LZO_PCAST) # if (LZO_HAVE_MM_HUGE_PTR) # define LZO_PCAST(t,e) ((t) (e)) # endif #endif #if !defined(LZO_PCAST) # define LZO_PCAST(t,e) LZO_STATIC_CAST(t, LZO_STATIC_CAST(void *, e)) #endif #if !defined(LZO_CCAST) # if (LZO_HAVE_MM_HUGE_PTR) # define LZO_CCAST(t,e) ((t) (e)) # endif #endif #if !defined(LZO_CCAST) # define LZO_CCAST(t,e) LZO_STATIC_CAST(t, LZO_STATIC_CAST(const void *, e)) #endif #if !defined(LZO_ICONV) # define LZO_ICONV(t,e) LZO_STATIC_CAST(t, e) #endif #if !defined(LZO_ICAST) # define LZO_ICAST(t,e) LZO_STATIC_CAST(t, e) #endif #if !defined(LZO_ITRUNC) # define LZO_ITRUNC(t,e) LZO_STATIC_CAST(t, e) #endif #if !defined(__lzo_cte) # if (LZO_CC_MSC || LZO_CC_WATCOMC) # define __lzo_cte(e) ((void)0,(e)) # elif 1 # define __lzo_cte(e) ((void)0,(e)) # endif #endif #if !defined(__lzo_cte) # define __lzo_cte(e) (e) #endif #if !defined(LZO_BLOCK_BEGIN) # define LZO_BLOCK_BEGIN do { # define LZO_BLOCK_END } while __lzo_cte(0) #endif #if !defined(LZO_UNUSED) # if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0600)) # define LZO_UNUSED(var) ((void) &var) # elif (LZO_CC_BORLANDC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PELLESC || LZO_CC_TURBOC) # define LZO_UNUSED(var) if (&var) ; else # elif (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x030200ul)) # define LZO_UNUSED(var) ((void) &var) # elif (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_UNUSED(var) ((void) var) # elif (LZO_CC_MSC && (_MSC_VER < 900)) # define LZO_UNUSED(var) if (&var) ; else # elif (LZO_CC_KEILC) # define LZO_UNUSED(var) {extern int lzo_unused__[1-2*!(sizeof(var)>0)]; (void)lzo_unused__;} # elif (LZO_CC_PACIFICC) # define LZO_UNUSED(var) ((void) sizeof(var)) # elif (LZO_CC_WATCOMC) && defined(__cplusplus) # define LZO_UNUSED(var) ((void) var) # else # define LZO_UNUSED(var) ((void) &var) # endif #endif #if !defined(LZO_UNUSED_FUNC) # if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0600)) # define LZO_UNUSED_FUNC(func) ((void) func) # elif (LZO_CC_BORLANDC || LZO_CC_NDPC || LZO_CC_TURBOC) # define LZO_UNUSED_FUNC(func) if (func) ; else # elif (LZO_CC_CLANG || LZO_CC_LLVM) # define LZO_UNUSED_FUNC(func) ((void) &func) # elif (LZO_CC_MSC && (_MSC_VER < 900)) # define LZO_UNUSED_FUNC(func) if (func) ; else # elif (LZO_CC_MSC) # define LZO_UNUSED_FUNC(func) ((void) &func) # elif (LZO_CC_KEILC || LZO_CC_PELLESC) # define LZO_UNUSED_FUNC(func) {extern int lzo_unused__[1-2*!(sizeof((int)func)>0)]; (void)lzo_unused__;} # else # define LZO_UNUSED_FUNC(func) ((void) func) # endif #endif #if !defined(LZO_UNUSED_LABEL) # if (LZO_CC_CLANG >= 0x020800ul) # define LZO_UNUSED_LABEL(l) (__lzo_gnuc_extension__ ((void) ((const void *) &&l))) # elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_INTELC || LZO_CC_WATCOMC) # define LZO_UNUSED_LABEL(l) if __lzo_cte(0) goto l # else # define LZO_UNUSED_LABEL(l) switch (0) case 1:goto l # endif #endif #if !defined(LZO_DEFINE_UNINITIALIZED_VAR) # if 0 # define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var # elif 0 && (LZO_CC_GNUC) # define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var = var # else # define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var = init # endif #endif #if !defined(__lzo_inline) #if (LZO_CC_TURBOC && (__TURBOC__ <= 0x0295)) #elif defined(__cplusplus) # define __lzo_inline inline #elif defined(__STDC_VERSION__) && (__STDC_VERSION__-0 >= 199901L) # define __lzo_inline inline #elif (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0550)) # define __lzo_inline __inline #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) # define __lzo_inline __inline__ #elif (LZO_CC_DMC) # define __lzo_inline __inline #elif (LZO_CC_GHS) # define __lzo_inline __inline__ #elif (LZO_CC_IBMC >= 600) # define __lzo_inline __inline__ #elif (LZO_CC_INTELC) # define __lzo_inline __inline #elif (LZO_CC_MWERKS && (__MWERKS__ >= 0x2405)) # define __lzo_inline __inline #elif (LZO_CC_MSC && (_MSC_VER >= 900)) # define __lzo_inline __inline #elif (LZO_CC_SUNPROC >= 0x5100) # define __lzo_inline __inline__ #endif #endif #if defined(__lzo_inline) # ifndef __lzo_HAVE_inline # define __lzo_HAVE_inline 1 # endif #else # define __lzo_inline /*empty*/ #endif #if !defined(__lzo_forceinline) #if (LZO_CC_GNUC >= 0x030200ul) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #elif (LZO_CC_IBMC >= 700) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) # define __lzo_forceinline __forceinline #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #elif (LZO_CC_MSC && (_MSC_VER >= 1200)) # define __lzo_forceinline __forceinline #elif (LZO_CC_PGI >= 0x0d0a00ul) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #elif (LZO_CC_SUNPROC >= 0x5100) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #endif #endif #if defined(__lzo_forceinline) # ifndef __lzo_HAVE_forceinline # define __lzo_HAVE_forceinline 1 # endif #else # define __lzo_forceinline __lzo_inline #endif #if !defined(__lzo_noinline) #if 1 && (LZO_ARCH_I386) && (LZO_CC_GNUC >= 0x040000ul) && (LZO_CC_GNUC < 0x040003ul) # define __lzo_noinline __attribute__((__noinline__,__used__)) #elif (LZO_CC_GNUC >= 0x030200ul) # define __lzo_noinline __attribute__((__noinline__)) #elif (LZO_CC_IBMC >= 700) # define __lzo_noinline __attribute__((__noinline__)) #elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 600)) # define __lzo_noinline __declspec(noinline) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) # define __lzo_noinline __attribute__((__noinline__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_noinline __attribute__((__noinline__)) #elif (LZO_CC_MSC && (_MSC_VER >= 1300)) # define __lzo_noinline __declspec(noinline) #elif (LZO_CC_MWERKS && (__MWERKS__ >= 0x3200) && (LZO_OS_WIN32 || LZO_OS_WIN64)) # if defined(__cplusplus) # else # define __lzo_noinline __declspec(noinline) # endif #elif (LZO_CC_PGI >= 0x0d0a00ul) # define __lzo_noinline __attribute__((__noinline__)) #elif (LZO_CC_SUNPROC >= 0x5100) # define __lzo_noinline __attribute__((__noinline__)) #endif #endif #if defined(__lzo_noinline) # ifndef __lzo_HAVE_noinline # define __lzo_HAVE_noinline 1 # endif #else # define __lzo_noinline /*empty*/ #endif #if (__lzo_HAVE_forceinline || __lzo_HAVE_noinline) && !(__lzo_HAVE_inline) # error "unexpected configuration - check your compiler defines" #endif #if !defined(__lzo_static_inline) #if (LZO_CC_IBMC) # define __lzo_static_inline __lzo_gnuc_extension__ static __lzo_inline #endif #endif #if !defined(__lzo_static_inline) # define __lzo_static_inline static __lzo_inline #endif #if !defined(__lzo_static_forceinline) #if (LZO_CC_IBMC) # define __lzo_static_forceinline __lzo_gnuc_extension__ static __lzo_forceinline #endif #endif #if !defined(__lzo_static_forceinline) # define __lzo_static_forceinline static __lzo_forceinline #endif #if !defined(__lzo_static_noinline) #if (LZO_CC_IBMC) # define __lzo_static_noinline __lzo_gnuc_extension__ static __lzo_noinline #endif #endif #if !defined(__lzo_static_noinline) # define __lzo_static_noinline static __lzo_noinline #endif #if !defined(__lzo_c99_extern_inline) #if defined(__GNUC_GNU_INLINE__) # define __lzo_c99_extern_inline __lzo_inline #elif defined(__GNUC_STDC_INLINE__) # define __lzo_c99_extern_inline extern __lzo_inline #elif defined(__STDC_VERSION__) && (__STDC_VERSION__-0 >= 199901L) # define __lzo_c99_extern_inline extern __lzo_inline #endif #if !defined(__lzo_c99_extern_inline) && (__lzo_HAVE_inline) # define __lzo_c99_extern_inline __lzo_inline #endif #endif #if defined(__lzo_c99_extern_inline) # ifndef __lzo_HAVE_c99_extern_inline # define __lzo_HAVE_c99_extern_inline 1 # endif #else # define __lzo_c99_extern_inline /*empty*/ #endif #if !defined(__lzo_may_alias) #if (LZO_CC_GNUC >= 0x030400ul) # define __lzo_may_alias __attribute__((__may_alias__)) #elif (LZO_CC_CLANG >= 0x020900ul) # define __lzo_may_alias __attribute__((__may_alias__)) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 1210)) && 0 # define __lzo_may_alias __attribute__((__may_alias__)) #elif (LZO_CC_PGI >= 0x0d0a00ul) && 0 # define __lzo_may_alias __attribute__((__may_alias__)) #endif #endif #if defined(__lzo_may_alias) # ifndef __lzo_HAVE_may_alias # define __lzo_HAVE_may_alias 1 # endif #else # define __lzo_may_alias /*empty*/ #endif #if !defined(__lzo_noreturn) #if (LZO_CC_GNUC >= 0x020700ul) # define __lzo_noreturn __attribute__((__noreturn__)) #elif (LZO_CC_IBMC >= 700) # define __lzo_noreturn __attribute__((__noreturn__)) #elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) # define __lzo_noreturn __declspec(noreturn) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 600)) # define __lzo_noreturn __attribute__((__noreturn__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_noreturn __attribute__((__noreturn__)) #elif (LZO_CC_MSC && (_MSC_VER >= 1200)) # define __lzo_noreturn __declspec(noreturn) #elif (LZO_CC_PGI >= 0x0d0a00ul) # define __lzo_noreturn __attribute__((__noreturn__)) #endif #endif #if defined(__lzo_noreturn) # ifndef __lzo_HAVE_noreturn # define __lzo_HAVE_noreturn 1 # endif #else # define __lzo_noreturn /*empty*/ #endif #if !defined(__lzo_nothrow) #if (LZO_CC_GNUC >= 0x030300ul) # define __lzo_nothrow __attribute__((__nothrow__)) #elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) && defined(__cplusplus) # define __lzo_nothrow __declspec(nothrow) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 900)) # define __lzo_nothrow __attribute__((__nothrow__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_nothrow __attribute__((__nothrow__)) #elif (LZO_CC_MSC && (_MSC_VER >= 1200)) && defined(__cplusplus) # define __lzo_nothrow __declspec(nothrow) #endif #endif #if defined(__lzo_nothrow) # ifndef __lzo_HAVE_nothrow # define __lzo_HAVE_nothrow 1 # endif #else # define __lzo_nothrow /*empty*/ #endif #if !defined(__lzo_restrict) #if (LZO_CC_GNUC >= 0x030400ul) # define __lzo_restrict __restrict__ #elif (LZO_CC_IBMC >= 800) && !defined(__cplusplus) # define __lzo_restrict __restrict__ #elif (LZO_CC_IBMC >= 1210) # define __lzo_restrict __restrict__ #elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 600)) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 600)) # define __lzo_restrict __restrict__ #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM) # define __lzo_restrict __restrict__ #elif (LZO_CC_MSC && (_MSC_VER >= 1400)) # define __lzo_restrict __restrict #elif (LZO_CC_PGI >= 0x0d0a00ul) # define __lzo_restrict __restrict__ #endif #endif #if defined(__lzo_restrict) # ifndef __lzo_HAVE_restrict # define __lzo_HAVE_restrict 1 # endif #else # define __lzo_restrict /*empty*/ #endif #if !defined(__lzo_alignof) #if (LZO_CC_ARMCC || LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) # define __lzo_alignof(e) __alignof__(e) #elif (LZO_CC_GHS) && !defined(__cplusplus) # define __lzo_alignof(e) __alignof__(e) #elif (LZO_CC_IBMC >= 600) # define __lzo_alignof(e) (__lzo_gnuc_extension__ __alignof__(e)) #elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 700)) # define __lzo_alignof(e) __alignof__(e) #elif (LZO_CC_MSC && (_MSC_VER >= 1300)) # define __lzo_alignof(e) __alignof(e) #elif (LZO_CC_SUNPROC >= 0x5100) # define __lzo_alignof(e) __alignof__(e) #endif #endif #if defined(__lzo_alignof) # ifndef __lzo_HAVE_alignof # define __lzo_HAVE_alignof 1 # endif #endif #if !defined(__lzo_struct_packed) #if (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) #elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020700ul)) #elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) && defined(__cplusplus) #elif (LZO_CC_PCC && (LZO_CC_PCC < 0x010100ul)) #elif (LZO_CC_SUNPROC && (LZO_CC_SUNPROC < 0x5110)) && !defined(__cplusplus) #elif (LZO_CC_GNUC >= 0x030400ul) && !(LZO_CC_PCC_GNUC) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) # define __lzo_struct_packed(s) struct s { # define __lzo_struct_packed_end() } __attribute__((__gcc_struct__,__packed__)); # define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__gcc_struct__,__packed__)); #elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || (LZO_CC_PGI >= 0x0d0a00ul) || (LZO_CC_SUNPROC >= 0x5100)) # define __lzo_struct_packed(s) struct s { # define __lzo_struct_packed_end() } __attribute__((__packed__)); # define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__packed__)); #elif (LZO_CC_IBMC >= 700) # define __lzo_struct_packed(s) __lzo_gnuc_extension__ struct s { # define __lzo_struct_packed_end() } __attribute__((__packed__)); # define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__packed__)); #elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) # define __lzo_struct_packed(s) __pragma(pack(push,1)) struct s { # define __lzo_struct_packed_end() } __pragma(pack(pop)); #elif (LZO_CC_WATCOMC && (__WATCOMC__ >= 900)) # define __lzo_struct_packed(s) _Packed struct s { # define __lzo_struct_packed_end() }; #endif #endif #if defined(__lzo_struct_packed) && !defined(__lzo_struct_packed_ma) # define __lzo_struct_packed_ma(s) __lzo_struct_packed(s) #endif #if defined(__lzo_struct_packed_end) && !defined(__lzo_struct_packed_ma_end) # define __lzo_struct_packed_ma_end() __lzo_struct_packed_end() #endif #if !defined(__lzo_byte_struct) #if defined(__lzo_struct_packed) # define __lzo_byte_struct(s,n) __lzo_struct_packed(s) unsigned char a[n]; __lzo_struct_packed_end() # define __lzo_byte_struct_ma(s,n) __lzo_struct_packed_ma(s) unsigned char a[n]; __lzo_struct_packed_ma_end() #elif (LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_PGI || (LZO_CC_SUNPROC >= 0x5100)) # define __lzo_byte_struct(s,n) struct s { unsigned char a[n]; } __attribute__((__packed__)); # define __lzo_byte_struct_ma(s,n) struct s { unsigned char a[n]; } __lzo_may_alias __attribute__((__packed__)); #endif #endif #if defined(__lzo_byte_struct) && !defined(__lzo_byte_struct_ma) # define __lzo_byte_struct_ma(s,n) __lzo_byte_struct(s,n) #endif #if !defined(__lzo_struct_align16) && (__lzo_HAVE_alignof) #if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x030000ul)) #elif (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) #elif (LZO_CC_CILLY || LZO_CC_PCC) #elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) # define __lzo_struct_align16(s) struct __declspec(align(16)) s { # define __lzo_struct_align16_end() }; # define __lzo_struct_align32(s) struct __declspec(align(32)) s { # define __lzo_struct_align32_end() }; # define __lzo_struct_align64(s) struct __declspec(align(64)) s { # define __lzo_struct_align64_end() }; #elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || (LZO_CC_IBMC >= 700) || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_struct_align16(s) struct s { # define __lzo_struct_align16_end() } __attribute__((__aligned__(16))); # define __lzo_struct_align32(s) struct s { # define __lzo_struct_align32_end() } __attribute__((__aligned__(32))); # define __lzo_struct_align64(s) struct s { # define __lzo_struct_align64_end() } __attribute__((__aligned__(64))); #endif #endif #if !defined(__lzo_union_um) #if (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) #elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020700ul)) #elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) && defined(__cplusplus) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER < 810)) #elif (LZO_CC_PCC && (LZO_CC_PCC < 0x010100ul)) #elif (LZO_CC_SUNPROC && (LZO_CC_SUNPROC < 0x5110)) && !defined(__cplusplus) #elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || (LZO_CC_PGI >= 0x0d0a00ul) || (LZO_CC_SUNPROC >= 0x5100)) # define __lzo_union_am(s) union s { # define __lzo_union_am_end() } __lzo_may_alias; # define __lzo_union_um(s) union s { # define __lzo_union_um_end() } __lzo_may_alias __attribute__((__packed__)); #elif (LZO_CC_IBMC >= 700) # define __lzo_union_am(s) __lzo_gnuc_extension__ union s { # define __lzo_union_am_end() } __lzo_may_alias; # define __lzo_union_um(s) __lzo_gnuc_extension__ union s { # define __lzo_union_um_end() } __lzo_may_alias __attribute__((__packed__)); #elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) # define __lzo_union_um(s) __pragma(pack(push,1)) union s { # define __lzo_union_um_end() } __pragma(pack(pop)); #elif (LZO_CC_WATCOMC && (__WATCOMC__ >= 900)) # define __lzo_union_um(s) _Packed union s { # define __lzo_union_um_end() }; #endif #endif #if !defined(__lzo_union_am) # define __lzo_union_am(s) union s { # define __lzo_union_am_end() }; #endif #if !defined(__lzo_constructor) #if (LZO_CC_GNUC >= 0x030400ul) # define __lzo_constructor __attribute__((__constructor__,__used__)) #elif (LZO_CC_GNUC >= 0x020700ul) # define __lzo_constructor __attribute__((__constructor__)) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) # define __lzo_constructor __attribute__((__constructor__,__used__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_constructor __attribute__((__constructor__)) #endif #endif #if defined(__lzo_constructor) # ifndef __lzo_HAVE_constructor # define __lzo_HAVE_constructor 1 # endif #endif #if !defined(__lzo_destructor) #if (LZO_CC_GNUC >= 0x030400ul) # define __lzo_destructor __attribute__((__destructor__,__used__)) #elif (LZO_CC_GNUC >= 0x020700ul) # define __lzo_destructor __attribute__((__destructor__)) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) # define __lzo_destructor __attribute__((__destructor__,__used__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_destructor __attribute__((__destructor__)) #endif #endif #if defined(__lzo_destructor) # ifndef __lzo_HAVE_destructor # define __lzo_HAVE_destructor 1 # endif #endif #if (__lzo_HAVE_destructor) && !(__lzo_HAVE_constructor) # error "unexpected configuration - check your compiler defines" #endif #if !defined(__lzo_likely) && !defined(__lzo_unlikely) #if (LZO_CC_GNUC >= 0x030200ul) # define __lzo_likely(e) (__builtin_expect(!!(e),1)) # define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) #elif (LZO_CC_IBMC >= 1010) # define __lzo_likely(e) (__builtin_expect(!!(e),1)) # define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) #elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 800)) # define __lzo_likely(e) (__builtin_expect(!!(e),1)) # define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_likely(e) (__builtin_expect(!!(e),1)) # define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) #endif #endif #if defined(__lzo_likely) # ifndef __lzo_HAVE_likely # define __lzo_HAVE_likely 1 # endif #else # define __lzo_likely(e) (e) #endif #if defined(__lzo_very_likely) # ifndef __lzo_HAVE_very_likely # define __lzo_HAVE_very_likely 1 # endif #else # define __lzo_very_likely(e) __lzo_likely(e) #endif #if defined(__lzo_unlikely) # ifndef __lzo_HAVE_unlikely # define __lzo_HAVE_unlikely 1 # endif #else # define __lzo_unlikely(e) (e) #endif #if defined(__lzo_very_unlikely) # ifndef __lzo_HAVE_very_unlikely # define __lzo_HAVE_very_unlikely 1 # endif #else # define __lzo_very_unlikely(e) __lzo_unlikely(e) #endif #if !defined(__lzo_loop_forever) # if (LZO_CC_IBMC) # define __lzo_loop_forever() LZO_BLOCK_BEGIN for (;;) { ; } LZO_BLOCK_END # else # define __lzo_loop_forever() do { ; } while __lzo_cte(1) # endif #endif #if !defined(__lzo_unreachable) #if (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x020800ul)) && lzo_has_builtin(__builtin_unreachable) # define __lzo_unreachable() __builtin_unreachable(); #elif (LZO_CC_GNUC >= 0x040500ul) # define __lzo_unreachable() __builtin_unreachable(); #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 1300)) && 1 # define __lzo_unreachable() __builtin_unreachable(); #endif #endif #if defined(__lzo_unreachable) # ifndef __lzo_HAVE_unreachable # define __lzo_HAVE_unreachable 1 # endif #else # if 0 # define __lzo_unreachable() ((void)0); # else # define __lzo_unreachable() __lzo_loop_forever(); # endif #endif #if !defined(lzo_unused_funcs_impl) # if 1 && (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || (LZO_CC_GNUC >= 0x020700ul) || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) # define lzo_unused_funcs_impl(r,f) static r __attribute__((__unused__)) f # elif 1 && (LZO_CC_BORLANDC || LZO_CC_GNUC) # define lzo_unused_funcs_impl(r,f) static r f # else # define lzo_unused_funcs_impl(r,f) __lzo_static_forceinline r f # endif #endif #ifndef __LZO_CTA_NAME #if (LZO_CFG_USE_COUNTER) # define __LZO_CTA_NAME(a) LZO_PP_ECONCAT2(a,__COUNTER__) #else # define __LZO_CTA_NAME(a) LZO_PP_ECONCAT2(a,__LINE__) #endif #endif #if !defined(LZO_COMPILE_TIME_ASSERT_HEADER) # if (LZO_CC_AZTECC || LZO_CC_ZORTECHC) # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-!(e)]; LZO_EXTERN_C_END # elif (LZO_CC_DMC || LZO_CC_SYMANTECC) # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1u-2*!(e)]; LZO_EXTERN_C_END # elif (LZO_CC_TURBOC && (__TURBOC__ == 0x0295)) # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-!(e)]; LZO_EXTERN_C_END # elif (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020900ul)) && defined(__cplusplus) # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN int __LZO_CTA_NAME(lzo_cta_f__)(int [1-2*!(e)]); LZO_EXTERN_C_END # elif (LZO_CC_GNUC) && defined(__CHECKER__) && defined(__SPARSE_CHECKER__) # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN enum {__LZO_CTA_NAME(lzo_cta_e__)=1/!!(e)} __attribute__((__unused__)); LZO_EXTERN_C_END # else # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-2*!(e)]; LZO_EXTERN_C_END # endif #endif #if !defined(LZO_COMPILE_TIME_ASSERT) # if (LZO_CC_AZTECC) # define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-!(e)];} # elif (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x030000ul)) # define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)] __attribute__((__unused__));} # elif (LZO_CC_DMC || LZO_CC_PACIFICC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) # define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; # elif (LZO_CC_GNUC) && defined(__CHECKER__) && defined(__SPARSE_CHECKER__) # define LZO_COMPILE_TIME_ASSERT(e) {(void) (0/!!(e));} # elif (LZO_CC_GNUC >= 0x040700ul) && (LZO_CFG_USE_COUNTER) && defined(__cplusplus) # define LZO_COMPILE_TIME_ASSERT(e) {enum {__LZO_CTA_NAME(lzo_cta_e__)=1/!!(e)} __attribute__((__unused__));} # elif (LZO_CC_GNUC >= 0x040700ul) # define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)] __attribute__((__unused__));} # elif (LZO_CC_MSC && (_MSC_VER < 900)) # define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; # elif (LZO_CC_TURBOC && (__TURBOC__ == 0x0295)) # define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; # else # define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)];} # endif #endif LZO_COMPILE_TIME_ASSERT_HEADER(1 == 1) #if defined(__cplusplus) extern "C" { LZO_COMPILE_TIME_ASSERT_HEADER(2 == 2) } #endif LZO_COMPILE_TIME_ASSERT_HEADER(3 == 3) #if (LZO_ARCH_I086 || LZO_ARCH_I386) && (LZO_OS_DOS16 || LZO_OS_DOS32 || LZO_OS_OS2 || LZO_OS_OS216 || LZO_OS_WIN16 || LZO_OS_WIN32 || LZO_OS_WIN64) # if (LZO_CC_GNUC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PACIFICC) # elif (LZO_CC_DMC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) # define __lzo_cdecl __cdecl # define __lzo_cdecl_atexit /*empty*/ # define __lzo_cdecl_main __cdecl # if (LZO_OS_OS2 && (LZO_CC_DMC || LZO_CC_SYMANTECC)) # define __lzo_cdecl_qsort __pascal # elif (LZO_OS_OS2 && (LZO_CC_ZORTECHC)) # define __lzo_cdecl_qsort _stdcall # else # define __lzo_cdecl_qsort __cdecl # endif # elif (LZO_CC_WATCOMC) # define __lzo_cdecl __cdecl # else # define __lzo_cdecl __cdecl # define __lzo_cdecl_atexit __cdecl # define __lzo_cdecl_main __cdecl # define __lzo_cdecl_qsort __cdecl # endif # if (LZO_CC_GNUC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PACIFICC || LZO_CC_WATCOMC) # elif (LZO_OS_OS2 && (LZO_CC_DMC || LZO_CC_SYMANTECC)) # define __lzo_cdecl_sighandler __pascal # elif (LZO_OS_OS2 && (LZO_CC_ZORTECHC)) # define __lzo_cdecl_sighandler _stdcall # elif (LZO_CC_MSC && (_MSC_VER >= 1400)) && defined(_M_CEE_PURE) # define __lzo_cdecl_sighandler __clrcall # elif (LZO_CC_MSC && (_MSC_VER >= 600 && _MSC_VER < 700)) # if defined(_DLL) # define __lzo_cdecl_sighandler _far _cdecl _loadds # elif defined(_MT) # define __lzo_cdecl_sighandler _far _cdecl # else # define __lzo_cdecl_sighandler _cdecl # endif # else # define __lzo_cdecl_sighandler __cdecl # endif #elif (LZO_ARCH_I386) && (LZO_CC_WATCOMC) # define __lzo_cdecl __cdecl #elif (LZO_ARCH_M68K && LZO_OS_TOS && (LZO_CC_PUREC || LZO_CC_TURBOC)) # define __lzo_cdecl cdecl #endif #if !defined(__lzo_cdecl) # define __lzo_cdecl /*empty*/ #endif #if !defined(__lzo_cdecl_atexit) # define __lzo_cdecl_atexit /*empty*/ #endif #if !defined(__lzo_cdecl_main) # define __lzo_cdecl_main /*empty*/ #endif #if !defined(__lzo_cdecl_qsort) # define __lzo_cdecl_qsort /*empty*/ #endif #if !defined(__lzo_cdecl_sighandler) # define __lzo_cdecl_sighandler /*empty*/ #endif #if !defined(__lzo_cdecl_va) # define __lzo_cdecl_va __lzo_cdecl #endif #if !(LZO_CFG_NO_WINDOWS_H) #if !defined(LZO_HAVE_WINDOWS_H) #if (LZO_OS_CYGWIN || (LZO_OS_EMX && defined(__RSXNT__)) || LZO_OS_WIN32 || LZO_OS_WIN64) # if (LZO_CC_WATCOMC && (__WATCOMC__ < 1000)) # elif ((LZO_OS_WIN32 && defined(__PW32__)) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x030000ul))) # elif ((LZO_OS_CYGWIN || defined(__MINGW32__)) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x025f00ul))) # else # define LZO_HAVE_WINDOWS_H 1 # endif #endif #endif #endif #ifndef LZO_SIZEOF_SHORT #if defined(SIZEOF_SHORT) # define LZO_SIZEOF_SHORT (SIZEOF_SHORT) #elif defined(__SIZEOF_SHORT__) # define LZO_SIZEOF_SHORT (__SIZEOF_SHORT__) #endif #endif #ifndef LZO_SIZEOF_INT #if defined(SIZEOF_INT) # define LZO_SIZEOF_INT (SIZEOF_INT) #elif defined(__SIZEOF_INT__) # define LZO_SIZEOF_INT (__SIZEOF_INT__) #endif #endif #ifndef LZO_SIZEOF_LONG #if defined(SIZEOF_LONG) # define LZO_SIZEOF_LONG (SIZEOF_LONG) #elif defined(__SIZEOF_LONG__) # define LZO_SIZEOF_LONG (__SIZEOF_LONG__) #endif #endif #ifndef LZO_SIZEOF_LONG_LONG #if defined(SIZEOF_LONG_LONG) # define LZO_SIZEOF_LONG_LONG (SIZEOF_LONG_LONG) #elif defined(__SIZEOF_LONG_LONG__) # define LZO_SIZEOF_LONG_LONG (__SIZEOF_LONG_LONG__) #endif #endif #ifndef LZO_SIZEOF___INT16 #if defined(SIZEOF___INT16) # define LZO_SIZEOF___INT16 (SIZEOF___INT16) #endif #endif #ifndef LZO_SIZEOF___INT32 #if defined(SIZEOF___INT32) # define LZO_SIZEOF___INT32 (SIZEOF___INT32) #endif #endif #ifndef LZO_SIZEOF___INT64 #if defined(SIZEOF___INT64) # define LZO_SIZEOF___INT64 (SIZEOF___INT64) #endif #endif #ifndef LZO_SIZEOF_VOID_P #if defined(SIZEOF_VOID_P) # define LZO_SIZEOF_VOID_P (SIZEOF_VOID_P) #elif defined(__SIZEOF_POINTER__) # define LZO_SIZEOF_VOID_P (__SIZEOF_POINTER__) #endif #endif #ifndef LZO_SIZEOF_SIZE_T #if defined(SIZEOF_SIZE_T) # define LZO_SIZEOF_SIZE_T (SIZEOF_SIZE_T) #elif defined(__SIZEOF_SIZE_T__) # define LZO_SIZEOF_SIZE_T (__SIZEOF_SIZE_T__) #endif #endif #ifndef LZO_SIZEOF_PTRDIFF_T #if defined(SIZEOF_PTRDIFF_T) # define LZO_SIZEOF_PTRDIFF_T (SIZEOF_PTRDIFF_T) #elif defined(__SIZEOF_PTRDIFF_T__) # define LZO_SIZEOF_PTRDIFF_T (__SIZEOF_PTRDIFF_T__) #endif #endif #define __LZO_LSR(x,b) (((x)+0ul) >> (b)) #if !defined(LZO_SIZEOF_SHORT) # if (LZO_ARCH_CRAY_PVP) # define LZO_SIZEOF_SHORT 8 # elif (USHRT_MAX == LZO_0xffffL) # define LZO_SIZEOF_SHORT 2 # elif (__LZO_LSR(USHRT_MAX,7) == 1) # define LZO_SIZEOF_SHORT 1 # elif (__LZO_LSR(USHRT_MAX,15) == 1) # define LZO_SIZEOF_SHORT 2 # elif (__LZO_LSR(USHRT_MAX,31) == 1) # define LZO_SIZEOF_SHORT 4 # elif (__LZO_LSR(USHRT_MAX,63) == 1) # define LZO_SIZEOF_SHORT 8 # elif (__LZO_LSR(USHRT_MAX,127) == 1) # define LZO_SIZEOF_SHORT 16 # else # error "LZO_SIZEOF_SHORT" # endif #endif LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_SHORT == sizeof(short)) #if !defined(LZO_SIZEOF_INT) # if (LZO_ARCH_CRAY_PVP) # define LZO_SIZEOF_INT 8 # elif (UINT_MAX == LZO_0xffffL) # define LZO_SIZEOF_INT 2 # elif (UINT_MAX == LZO_0xffffffffL) # define LZO_SIZEOF_INT 4 # elif (__LZO_LSR(UINT_MAX,7) == 1) # define LZO_SIZEOF_INT 1 # elif (__LZO_LSR(UINT_MAX,15) == 1) # define LZO_SIZEOF_INT 2 # elif (__LZO_LSR(UINT_MAX,31) == 1) # define LZO_SIZEOF_INT 4 # elif (__LZO_LSR(UINT_MAX,63) == 1) # define LZO_SIZEOF_INT 8 # elif (__LZO_LSR(UINT_MAX,127) == 1) # define LZO_SIZEOF_INT 16 # else # error "LZO_SIZEOF_INT" # endif #endif LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_INT == sizeof(int)) #if !defined(LZO_SIZEOF_LONG) # if (ULONG_MAX == LZO_0xffffffffL) # define LZO_SIZEOF_LONG 4 # elif (__LZO_LSR(ULONG_MAX,7) == 1) # define LZO_SIZEOF_LONG 1 # elif (__LZO_LSR(ULONG_MAX,15) == 1) # define LZO_SIZEOF_LONG 2 # elif (__LZO_LSR(ULONG_MAX,31) == 1) # define LZO_SIZEOF_LONG 4 # elif (__LZO_LSR(ULONG_MAX,39) == 1) # define LZO_SIZEOF_LONG 5 # elif (__LZO_LSR(ULONG_MAX,63) == 1) # define LZO_SIZEOF_LONG 8 # elif (__LZO_LSR(ULONG_MAX,127) == 1) # define LZO_SIZEOF_LONG 16 # else # error "LZO_SIZEOF_LONG" # endif #endif LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_LONG == sizeof(long)) #if !defined(LZO_SIZEOF_LONG_LONG) && !defined(LZO_SIZEOF___INT64) #if (LZO_SIZEOF_LONG > 0 && LZO_SIZEOF_LONG < 8) # if defined(__LONG_MAX__) && defined(__LONG_LONG_MAX__) # if (LZO_CC_GNUC >= 0x030300ul) # if ((__LONG_MAX__-0) == (__LONG_LONG_MAX__-0)) # define LZO_SIZEOF_LONG_LONG LZO_SIZEOF_LONG # elif (__LZO_LSR(__LONG_LONG_MAX__,30) == 1) # define LZO_SIZEOF_LONG_LONG 4 # endif # endif # endif #endif #endif #if !defined(LZO_SIZEOF_LONG_LONG) && !defined(LZO_SIZEOF___INT64) #if (LZO_SIZEOF_LONG > 0 && LZO_SIZEOF_LONG < 8) #if (LZO_ARCH_I086 && LZO_CC_DMC) #elif (LZO_CC_CILLY) && defined(__GNUC__) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_SIZEOF_LONG_LONG 8 #elif ((LZO_OS_WIN32 || LZO_OS_WIN64 || defined(_WIN32)) && LZO_CC_MSC && (_MSC_VER >= 1400)) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_OS_WIN64 || defined(_WIN64)) # define LZO_SIZEOF___INT64 8 #elif (LZO_ARCH_I386 && (LZO_CC_DMC)) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_ARCH_I386 && (LZO_CC_SYMANTECC && (__SC__ >= 0x700))) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_ARCH_I386 && (LZO_CC_INTELC && defined(__linux__))) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_ARCH_I386 && (LZO_CC_MWERKS || LZO_CC_PELLESC || LZO_CC_PGI || LZO_CC_SUNPROC)) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_ARCH_I386 && (LZO_CC_INTELC || LZO_CC_MSC)) # define LZO_SIZEOF___INT64 8 #elif ((LZO_OS_WIN32 || defined(_WIN32)) && (LZO_CC_MSC)) # define LZO_SIZEOF___INT64 8 #elif (LZO_ARCH_I386 && (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0520))) # define LZO_SIZEOF___INT64 8 #elif (LZO_ARCH_I386 && (LZO_CC_WATCOMC && (__WATCOMC__ >= 1100))) # define LZO_SIZEOF___INT64 8 #elif (LZO_CC_GHS && defined(__LLONG_BIT) && ((__LLONG_BIT-0) == 64)) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_CC_WATCOMC && defined(_INTEGRAL_MAX_BITS) && ((_INTEGRAL_MAX_BITS-0) == 64)) # define LZO_SIZEOF___INT64 8 #elif (LZO_OS_OS400 || defined(__OS400__)) && defined(__LLP64_IFC__) # define LZO_SIZEOF_LONG_LONG 8 #elif (defined(__vms) || defined(__VMS)) && ((__INITIAL_POINTER_SIZE-0) == 64) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_CC_SDCC) && (LZO_SIZEOF_INT == 2) #elif 1 && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) # define LZO_SIZEOF_LONG_LONG 8 #endif #endif #endif #if defined(__cplusplus) && (LZO_CC_GNUC) # if (LZO_CC_GNUC < 0x020800ul) # undef LZO_SIZEOF_LONG_LONG # endif #endif #if (LZO_CFG_NO_LONG_LONG) # undef LZO_SIZEOF_LONG_LONG #elif defined(__NO_LONG_LONG) # undef LZO_SIZEOF_LONG_LONG #elif defined(_NO_LONGLONG) # undef LZO_SIZEOF_LONG_LONG #endif #if !defined(LZO_WORDSIZE) #if (LZO_ARCH_ALPHA) # define LZO_WORDSIZE 8 #elif (LZO_ARCH_AMD64) # define LZO_WORDSIZE 8 #elif (LZO_ARCH_AVR) # define LZO_WORDSIZE 1 #elif (LZO_ARCH_H8300) # if defined(__NORMAL_MODE__) # define LZO_WORDSIZE 4 # elif defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) # define LZO_WORDSIZE 4 # else # define LZO_WORDSIZE 2 # endif #elif (LZO_ARCH_I086) # define LZO_WORDSIZE 2 #elif (LZO_ARCH_IA64) # define LZO_WORDSIZE 8 #elif (LZO_ARCH_M16C) # define LZO_WORDSIZE 2 #elif (LZO_ARCH_SPU) # define LZO_WORDSIZE 4 #elif (LZO_ARCH_Z80) # define LZO_WORDSIZE 1 #elif (LZO_SIZEOF_LONG == 8) && ((defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__)) # define LZO_WORDSIZE 8 #elif (LZO_OS_OS400 || defined(__OS400__)) # define LZO_WORDSIZE 8 #elif (defined(__vms) || defined(__VMS)) && (__INITIAL_POINTER_SIZE+0 == 64) # define LZO_WORDSIZE 8 #endif #endif #if !defined(LZO_SIZEOF_VOID_P) #if defined(__ILP32__) || defined(__ILP32) || defined(_ILP32) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(int) == 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 4) # define LZO_SIZEOF_VOID_P 4 #elif defined(__ILP64__) || defined(__ILP64) || defined(_ILP64) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(int) == 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 8) # define LZO_SIZEOF_VOID_P 8 #elif defined(__LLP64__) || defined(__LLP64) || defined(_LLP64) || defined(_WIN64) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 4) # define LZO_SIZEOF_VOID_P 8 #elif defined(__LP64__) || defined(__LP64) || defined(_LP64) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 8) # define LZO_SIZEOF_VOID_P 8 #elif (LZO_ARCH_AVR) # define LZO_SIZEOF_VOID_P 2 #elif (LZO_ARCH_C166 || LZO_ARCH_MCS51 || LZO_ARCH_MCS251 || LZO_ARCH_MSP430) # define LZO_SIZEOF_VOID_P 2 #elif (LZO_ARCH_H8300) # if defined(__NORMAL_MODE__) # define LZO_SIZEOF_VOID_P 2 # elif defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) # define LZO_SIZEOF_VOID_P 4 # else # define LZO_SIZEOF_VOID_P 2 # endif # if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x040000ul)) && (LZO_SIZEOF_INT == 4) # define LZO_SIZEOF_SIZE_T LZO_SIZEOF_INT # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_INT # endif #elif (LZO_ARCH_I086) # if (LZO_MM_TINY || LZO_MM_SMALL || LZO_MM_MEDIUM) # define LZO_SIZEOF_VOID_P 2 # elif (LZO_MM_COMPACT || LZO_MM_LARGE || LZO_MM_HUGE) # define LZO_SIZEOF_VOID_P 4 # else # error "invalid LZO_ARCH_I086 memory model" # endif #elif (LZO_ARCH_M16C) # if defined(__m32c_cpu__) || defined(__m32cm_cpu__) # define LZO_SIZEOF_VOID_P 4 # else # define LZO_SIZEOF_VOID_P 2 # endif #elif (LZO_ARCH_SPU) # define LZO_SIZEOF_VOID_P 4 #elif (LZO_ARCH_Z80) # define LZO_SIZEOF_VOID_P 2 #elif (LZO_SIZEOF_LONG == 8) && ((defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__)) # define LZO_SIZEOF_VOID_P 4 #elif (LZO_OS_OS400 || defined(__OS400__)) # if defined(__LLP64_IFC__) # define LZO_SIZEOF_VOID_P 8 # define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG # else # define LZO_SIZEOF_VOID_P 16 # define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG # endif #elif (defined(__vms) || defined(__VMS)) && (__INITIAL_POINTER_SIZE+0 == 64) # define LZO_SIZEOF_VOID_P 8 # define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG #endif #endif #if !defined(LZO_SIZEOF_VOID_P) # define LZO_SIZEOF_VOID_P LZO_SIZEOF_LONG #endif LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_VOID_P == sizeof(void *)) #if !defined(LZO_SIZEOF_SIZE_T) #if (LZO_ARCH_I086 || LZO_ARCH_M16C) # define LZO_SIZEOF_SIZE_T 2 #endif #endif #if !defined(LZO_SIZEOF_SIZE_T) # define LZO_SIZEOF_SIZE_T LZO_SIZEOF_VOID_P #endif #if defined(offsetof) LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_SIZE_T == sizeof(size_t)) #endif #if !defined(LZO_SIZEOF_PTRDIFF_T) #if (LZO_ARCH_I086) # if (LZO_MM_TINY || LZO_MM_SMALL || LZO_MM_MEDIUM || LZO_MM_HUGE) # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_VOID_P # elif (LZO_MM_COMPACT || LZO_MM_LARGE) # if (LZO_CC_BORLANDC || LZO_CC_TURBOC) # define LZO_SIZEOF_PTRDIFF_T 4 # else # define LZO_SIZEOF_PTRDIFF_T 2 # endif # else # error "invalid LZO_ARCH_I086 memory model" # endif #endif #endif #if !defined(LZO_SIZEOF_PTRDIFF_T) # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_SIZE_T #endif #if defined(offsetof) LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_PTRDIFF_T == sizeof(ptrdiff_t)) #endif #if !defined(LZO_WORDSIZE) # define LZO_WORDSIZE LZO_SIZEOF_VOID_P #endif #if (LZO_ABI_NEUTRAL_ENDIAN) # undef LZO_ABI_BIG_ENDIAN # undef LZO_ABI_LITTLE_ENDIAN #elif !(LZO_ABI_BIG_ENDIAN) && !(LZO_ABI_LITTLE_ENDIAN) #if (LZO_ARCH_ALPHA) && (LZO_ARCH_CRAY_MPP) # define LZO_ABI_BIG_ENDIAN 1 #elif (LZO_ARCH_IA64) && (LZO_OS_POSIX_LINUX || LZO_OS_WIN64) # define LZO_ABI_LITTLE_ENDIAN 1 #elif (LZO_ARCH_ALPHA || LZO_ARCH_AMD64 || LZO_ARCH_BLACKFIN || LZO_ARCH_CRIS || LZO_ARCH_I086 || LZO_ARCH_I386 || LZO_ARCH_MSP430) # define LZO_ABI_LITTLE_ENDIAN 1 #elif (LZO_ARCH_AVR32 || LZO_ARCH_M68K || LZO_ARCH_S390 || LZO_ARCH_SPU) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && defined(__IAR_SYSTEMS_ICC__) && defined(__LITTLE_ENDIAN__) # if (__LITTLE_ENDIAN__ == 1) # define LZO_ABI_LITTLE_ENDIAN 1 # else # define LZO_ABI_BIG_ENDIAN 1 # endif #elif 1 && defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) # define LZO_ABI_LITTLE_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM) && defined(__ARM_BIG_ENDIAN) && ((__ARM_BIG_ENDIAN)+0) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM) && defined(__ARMEB__) && !defined(__ARMEL__) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM) && defined(__ARMEL__) && !defined(__ARMEB__) # define LZO_ABI_LITTLE_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM && LZO_CC_ARMCC_ARMCC) # if defined(__BIG_ENDIAN) && defined(__LITTLE_ENDIAN) # error "unexpected configuration - check your compiler defines" # elif defined(__BIG_ENDIAN) # define LZO_ABI_BIG_ENDIAN 1 # else # define LZO_ABI_LITTLE_ENDIAN 1 # endif # define LZO_ABI_LITTLE_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM64) && defined(__ARM_BIG_ENDIAN) && ((__ARM_BIG_ENDIAN)+0) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM64) && defined(__AARCH64EB__) && !defined(__AARCH64EL__) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM64) && defined(__AARCH64EL__) && !defined(__AARCH64EB__) # define LZO_ABI_LITTLE_ENDIAN 1 #elif 1 && (LZO_ARCH_MIPS) && defined(__MIPSEB__) && !defined(__MIPSEL__) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && (LZO_ARCH_MIPS) && defined(__MIPSEL__) && !defined(__MIPSEB__) # define LZO_ABI_LITTLE_ENDIAN 1 #endif #endif #if (LZO_ABI_BIG_ENDIAN) && (LZO_ABI_LITTLE_ENDIAN) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ABI_BIG_ENDIAN) # define LZO_INFO_ABI_ENDIAN "be" #elif (LZO_ABI_LITTLE_ENDIAN) # define LZO_INFO_ABI_ENDIAN "le" #elif (LZO_ABI_NEUTRAL_ENDIAN) # define LZO_INFO_ABI_ENDIAN "neutral" #endif #if (LZO_SIZEOF_INT == 1 && LZO_SIZEOF_LONG == 2 && LZO_SIZEOF_VOID_P == 2) # define LZO_ABI_I8LP16 1 # define LZO_INFO_ABI_PM "i8lp16" #elif (LZO_SIZEOF_INT == 2 && LZO_SIZEOF_LONG == 2 && LZO_SIZEOF_VOID_P == 2) # define LZO_ABI_ILP16 1 # define LZO_INFO_ABI_PM "ilp16" #elif (LZO_SIZEOF_INT == 2 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 4) # define LZO_ABI_LP32 1 # define LZO_INFO_ABI_PM "lp32" #elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 4) # define LZO_ABI_ILP32 1 # define LZO_INFO_ABI_PM "ilp32" #elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 8 && LZO_SIZEOF_SIZE_T == 8) # define LZO_ABI_LLP64 1 # define LZO_INFO_ABI_PM "llp64" #elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 8) # define LZO_ABI_LP64 1 # define LZO_INFO_ABI_PM "lp64" #elif (LZO_SIZEOF_INT == 8 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 8) # define LZO_ABI_ILP64 1 # define LZO_INFO_ABI_PM "ilp64" #elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 4) # define LZO_ABI_IP32L64 1 # define LZO_INFO_ABI_PM "ip32l64" #endif #if 0 #elif !defined(__LZO_LIBC_OVERRIDE) #if (LZO_LIBC_NAKED) # define LZO_INFO_LIBC "naked" #elif (LZO_LIBC_FREESTANDING) # define LZO_INFO_LIBC "freestanding" #elif (LZO_LIBC_MOSTLY_FREESTANDING) # define LZO_INFO_LIBC "mfreestanding" #elif (LZO_LIBC_ISOC90) # define LZO_INFO_LIBC "isoc90" #elif (LZO_LIBC_ISOC99) # define LZO_INFO_LIBC "isoc99" #elif (LZO_CC_ARMCC_ARMCC) && defined(__ARMCLIB_VERSION) # define LZO_LIBC_ISOC90 1 # define LZO_INFO_LIBC "isoc90" #elif defined(__dietlibc__) # define LZO_LIBC_DIETLIBC 1 # define LZO_INFO_LIBC "dietlibc" #elif defined(_NEWLIB_VERSION) # define LZO_LIBC_NEWLIB 1 # define LZO_INFO_LIBC "newlib" #elif defined(__UCLIBC__) && defined(__UCLIBC_MAJOR__) && defined(__UCLIBC_MINOR__) # if defined(__UCLIBC_SUBLEVEL__) # define LZO_LIBC_UCLIBC (__UCLIBC_MAJOR__ * 0x10000L + (__UCLIBC_MINOR__-0) * 0x100 + (__UCLIBC_SUBLEVEL__-0)) # else # define LZO_LIBC_UCLIBC 0x00090bL # endif # define LZO_INFO_LIBC "uc" "libc" #elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) # define LZO_LIBC_GLIBC (__GLIBC__ * 0x10000L + (__GLIBC_MINOR__-0) * 0x100) # define LZO_INFO_LIBC "glibc" #elif (LZO_CC_MWERKS) && defined(__MSL__) # define LZO_LIBC_MSL __MSL__ # define LZO_INFO_LIBC "msl" #elif 1 && defined(__IAR_SYSTEMS_ICC__) # define LZO_LIBC_ISOC90 1 # define LZO_INFO_LIBC "isoc90" #else # define LZO_LIBC_DEFAULT 1 # define LZO_INFO_LIBC "default" #endif #endif #if (LZO_ARCH_I386 && (LZO_OS_DOS32 || LZO_OS_WIN32) && (LZO_CC_DMC || LZO_CC_INTELC || LZO_CC_MSC || LZO_CC_PELLESC)) # define LZO_ASM_SYNTAX_MSC 1 #elif (LZO_OS_WIN64 && (LZO_CC_DMC || LZO_CC_INTELC || LZO_CC_MSC || LZO_CC_PELLESC)) #elif (LZO_ARCH_I386 && LZO_CC_GNUC && (LZO_CC_GNUC == 0x011f00ul)) #elif (LZO_ARCH_I386 && (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC || LZO_CC_PATHSCALE)) # define LZO_ASM_SYNTAX_GNUC 1 #elif (LZO_ARCH_AMD64 && (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC || LZO_CC_PATHSCALE)) # define LZO_ASM_SYNTAX_GNUC 1 #elif (LZO_CC_GNUC) # define LZO_ASM_SYNTAX_GNUC 1 #endif #if (LZO_ASM_SYNTAX_GNUC) #if (LZO_ARCH_I386 && LZO_CC_GNUC && (LZO_CC_GNUC < 0x020000ul)) # define __LZO_ASM_CLOBBER "ax" # define __LZO_ASM_CLOBBER_LIST_CC /*empty*/ # define __LZO_ASM_CLOBBER_LIST_CC_MEMORY /*empty*/ # define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ #elif (LZO_CC_INTELC && (__INTEL_COMPILER < 1000)) # define __LZO_ASM_CLOBBER "memory" # define __LZO_ASM_CLOBBER_LIST_CC /*empty*/ # define __LZO_ASM_CLOBBER_LIST_CC_MEMORY : "memory" # define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ #else # define __LZO_ASM_CLOBBER "cc", "memory" # define __LZO_ASM_CLOBBER_LIST_CC : "cc" # define __LZO_ASM_CLOBBER_LIST_CC_MEMORY : "cc", "memory" # define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ #endif #endif #if (LZO_ARCH_ALPHA) # define LZO_OPT_AVOID_UINT_INDEX 1 #elif (LZO_ARCH_AMD64) # define LZO_OPT_AVOID_INT_INDEX 1 # define LZO_OPT_AVOID_UINT_INDEX 1 # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # ifndef LZO_OPT_UNALIGNED64 # define LZO_OPT_UNALIGNED64 1 # endif #elif (LZO_ARCH_ARM) # if defined(__ARM_FEATURE_UNALIGNED) # if ((__ARM_FEATURE_UNALIGNED)+0) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # endif # elif 1 && (LZO_ARCH_ARM_THUMB2) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # elif 1 && defined(__TARGET_ARCH_ARM) && ((__TARGET_ARCH_ARM)+0 >= 7) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # elif 1 && defined(__TARGET_ARCH_ARM) && ((__TARGET_ARCH_ARM)+0 >= 6) && (defined(__TARGET_PROFILE_A) || defined(__TARGET_PROFILE_R)) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # endif #elif (LZO_ARCH_ARM64) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # ifndef LZO_OPT_UNALIGNED64 # define LZO_OPT_UNALIGNED64 1 # endif #elif (LZO_ARCH_CRIS) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif #elif (LZO_ARCH_I386) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif #elif (LZO_ARCH_IA64) # define LZO_OPT_AVOID_INT_INDEX 1 # define LZO_OPT_AVOID_UINT_INDEX 1 # define LZO_OPT_PREFER_POSTINC 1 #elif (LZO_ARCH_M68K) # define LZO_OPT_PREFER_POSTINC 1 # define LZO_OPT_PREFER_PREDEC 1 # if defined(__mc68020__) && !defined(__mcoldfire__) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # endif #elif (LZO_ARCH_MIPS) # define LZO_OPT_AVOID_UINT_INDEX 1 #elif (LZO_ARCH_POWERPC) # define LZO_OPT_PREFER_PREINC 1 # define LZO_OPT_PREFER_PREDEC 1 # if (LZO_ABI_BIG_ENDIAN) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # if (LZO_WORDSIZE == 8) # ifndef LZO_OPT_UNALIGNED64 # define LZO_OPT_UNALIGNED64 1 # endif # endif # endif #elif (LZO_ARCH_S390) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # if (LZO_WORDSIZE == 8) # ifndef LZO_OPT_UNALIGNED64 # define LZO_OPT_UNALIGNED64 1 # endif # endif #elif (LZO_ARCH_SH) # define LZO_OPT_PREFER_POSTINC 1 # define LZO_OPT_PREFER_PREDEC 1 #endif #ifndef LZO_CFG_NO_INLINE_ASM #if (LZO_ABI_NEUTRAL_ENDIAN) || (LZO_ARCH_GENERIC) # define LZO_CFG_NO_INLINE_ASM 1 #elif (LZO_CC_LLVM) # define LZO_CFG_NO_INLINE_ASM 1 #endif #endif #if (LZO_CFG_NO_INLINE_ASM) # undef LZO_ASM_SYNTAX_MSC # undef LZO_ASM_SYNTAX_GNUC # undef __LZO_ASM_CLOBBER # undef __LZO_ASM_CLOBBER_LIST_CC # undef __LZO_ASM_CLOBBER_LIST_CC_MEMORY # undef __LZO_ASM_CLOBBER_LIST_EMPTY #endif #ifndef LZO_CFG_NO_UNALIGNED #if (LZO_ABI_NEUTRAL_ENDIAN) || (LZO_ARCH_GENERIC) # define LZO_CFG_NO_UNALIGNED 1 #endif #endif #if (LZO_CFG_NO_UNALIGNED) # undef LZO_OPT_UNALIGNED16 # undef LZO_OPT_UNALIGNED32 # undef LZO_OPT_UNALIGNED64 #endif #if defined(__LZO_INFOSTR_MM) #elif (LZO_MM_FLAT) && (defined(__LZO_INFOSTR_PM) || defined(LZO_INFO_ABI_PM)) # define __LZO_INFOSTR_MM "" #elif defined(LZO_INFO_MM) # define __LZO_INFOSTR_MM "." LZO_INFO_MM #else # define __LZO_INFOSTR_MM "" #endif #if defined(__LZO_INFOSTR_PM) #elif defined(LZO_INFO_ABI_PM) # define __LZO_INFOSTR_PM "." LZO_INFO_ABI_PM #else # define __LZO_INFOSTR_PM "" #endif #if defined(__LZO_INFOSTR_ENDIAN) #elif defined(LZO_INFO_ABI_ENDIAN) # define __LZO_INFOSTR_ENDIAN "." LZO_INFO_ABI_ENDIAN #else # define __LZO_INFOSTR_ENDIAN "" #endif #if defined(__LZO_INFOSTR_OSNAME) #elif defined(LZO_INFO_OS_CONSOLE) # define __LZO_INFOSTR_OSNAME LZO_INFO_OS "." LZO_INFO_OS_CONSOLE #elif defined(LZO_INFO_OS_POSIX) # define __LZO_INFOSTR_OSNAME LZO_INFO_OS "." LZO_INFO_OS_POSIX #else # define __LZO_INFOSTR_OSNAME LZO_INFO_OS #endif #if defined(__LZO_INFOSTR_LIBC) #elif defined(LZO_INFO_LIBC) # define __LZO_INFOSTR_LIBC "." LZO_INFO_LIBC #else # define __LZO_INFOSTR_LIBC "" #endif #if defined(__LZO_INFOSTR_CCVER) #elif defined(LZO_INFO_CCVER) # define __LZO_INFOSTR_CCVER " " LZO_INFO_CCVER #else # define __LZO_INFOSTR_CCVER "" #endif #define LZO_INFO_STRING \ LZO_INFO_ARCH __LZO_INFOSTR_MM __LZO_INFOSTR_PM __LZO_INFOSTR_ENDIAN \ " " __LZO_INFOSTR_OSNAME __LZO_INFOSTR_LIBC " " LZO_INFO_CC __LZO_INFOSTR_CCVER #if !(LZO_CFG_SKIP_LZO_TYPES) #if (!(LZO_SIZEOF_SHORT+0 > 0 && LZO_SIZEOF_INT+0 > 0 && LZO_SIZEOF_LONG+0 > 0)) # error "missing defines for sizes" #endif #if (!(LZO_SIZEOF_PTRDIFF_T+0 > 0 && LZO_SIZEOF_SIZE_T+0 > 0 && LZO_SIZEOF_VOID_P+0 > 0)) # error "missing defines for sizes" #endif #define LZO_TYPEOF_CHAR 1u #define LZO_TYPEOF_SHORT 2u #define LZO_TYPEOF_INT 3u #define LZO_TYPEOF_LONG 4u #define LZO_TYPEOF_LONG_LONG 5u #define LZO_TYPEOF___INT8 17u #define LZO_TYPEOF___INT16 18u #define LZO_TYPEOF___INT32 19u #define LZO_TYPEOF___INT64 20u #define LZO_TYPEOF___INT128 21u #define LZO_TYPEOF___INT256 22u #define LZO_TYPEOF___MODE_QI 33u #define LZO_TYPEOF___MODE_HI 34u #define LZO_TYPEOF___MODE_SI 35u #define LZO_TYPEOF___MODE_DI 36u #define LZO_TYPEOF___MODE_TI 37u #define LZO_TYPEOF_CHAR_P 129u #if !defined(lzo_llong_t) #if (LZO_SIZEOF_LONG_LONG+0 > 0) __lzo_gnuc_extension__ typedef long long lzo_llong_t__; __lzo_gnuc_extension__ typedef unsigned long long lzo_ullong_t__; # define lzo_llong_t lzo_llong_t__ # define lzo_ullong_t lzo_ullong_t__ #endif #endif #if !defined(lzo_int16e_t) #if (LZO_SIZEOF_LONG == 2) # define lzo_int16e_t long # define lzo_uint16e_t unsigned long # define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_LONG #elif (LZO_SIZEOF_INT == 2) # define lzo_int16e_t int # define lzo_uint16e_t unsigned int # define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_INT #elif (LZO_SIZEOF_SHORT == 2) # define lzo_int16e_t short int # define lzo_uint16e_t unsigned short int # define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_SHORT #elif 1 && !(LZO_CFG_TYPE_NO_MODE_HI) && (LZO_CC_CLANG || (LZO_CC_GNUC >= 0x025f00ul) || LZO_CC_LLVM) typedef int lzo_int16e_hi_t__ __attribute__((__mode__(__HI__))); typedef unsigned int lzo_uint16e_hi_t__ __attribute__((__mode__(__HI__))); # define lzo_int16e_t lzo_int16e_hi_t__ # define lzo_uint16e_t lzo_uint16e_hi_t__ # define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF___MODE_HI #elif (LZO_SIZEOF___INT16 == 2) # define lzo_int16e_t __int16 # define lzo_uint16e_t unsigned __int16 # define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF___INT16 #else #endif #endif #if defined(lzo_int16e_t) # define LZO_SIZEOF_LZO_INT16E_T 2 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16e_t) == 2) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16e_t) == LZO_SIZEOF_LZO_INT16E_T) #endif #if !defined(lzo_int32e_t) #if (LZO_SIZEOF_LONG == 4) # define lzo_int32e_t long int # define lzo_uint32e_t unsigned long int # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_LONG #elif (LZO_SIZEOF_INT == 4) # define lzo_int32e_t int # define lzo_uint32e_t unsigned int # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_INT #elif (LZO_SIZEOF_SHORT == 4) # define lzo_int32e_t short int # define lzo_uint32e_t unsigned short int # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_SHORT #elif (LZO_SIZEOF_LONG_LONG == 4) # define lzo_int32e_t lzo_llong_t # define lzo_uint32e_t lzo_ullong_t # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_LONG_LONG #elif 1 && !(LZO_CFG_TYPE_NO_MODE_SI) && (LZO_CC_CLANG || (LZO_CC_GNUC >= 0x025f00ul) || LZO_CC_LLVM) && (__INT_MAX__+0 > 2147483647L) typedef int lzo_int32e_si_t__ __attribute__((__mode__(__SI__))); typedef unsigned int lzo_uint32e_si_t__ __attribute__((__mode__(__SI__))); # define lzo_int32e_t lzo_int32e_si_t__ # define lzo_uint32e_t lzo_uint32e_si_t__ # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___MODE_SI #elif 1 && !(LZO_CFG_TYPE_NO_MODE_SI) && (LZO_CC_GNUC >= 0x025f00ul) && defined(__AVR__) && (__LONG_MAX__+0 == 32767L) typedef int lzo_int32e_si_t__ __attribute__((__mode__(__SI__))); typedef unsigned int lzo_uint32e_si_t__ __attribute__((__mode__(__SI__))); # define lzo_int32e_t lzo_int32e_si_t__ # define lzo_uint32e_t lzo_uint32e_si_t__ # define LZO_INT32_C(c) (c##LL) # define LZO_UINT32_C(c) (c##ULL) # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___MODE_SI #elif (LZO_SIZEOF___INT32 == 4) # define lzo_int32e_t __int32 # define lzo_uint32e_t unsigned __int32 # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___INT32 #else #endif #endif #if defined(lzo_int32e_t) # define LZO_SIZEOF_LZO_INT32E_T 4 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32e_t) == 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32e_t) == LZO_SIZEOF_LZO_INT32E_T) #endif #if !defined(lzo_int64e_t) #if (LZO_SIZEOF___INT64 == 8) # if (LZO_CC_BORLANDC) && !(LZO_CFG_TYPE_PREFER___INT64) # define LZO_CFG_TYPE_PREFER___INT64 1 # endif #endif #if (LZO_SIZEOF_INT == 8) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) # define lzo_int64e_t int # define lzo_uint64e_t unsigned int # define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_INT #elif (LZO_SIZEOF_LONG == 8) # define lzo_int64e_t long int # define lzo_uint64e_t unsigned long int # define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_LONG #elif (LZO_SIZEOF_LONG_LONG == 8) && !(LZO_CFG_TYPE_PREFER___INT64) # define lzo_int64e_t lzo_llong_t # define lzo_uint64e_t lzo_ullong_t # define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_LONG_LONG # if (LZO_CC_BORLANDC) # define LZO_INT64_C(c) ((c) + 0ll) # define LZO_UINT64_C(c) ((c) + 0ull) # elif 0 # define LZO_INT64_C(c) (__lzo_gnuc_extension__ (c##LL)) # define LZO_UINT64_C(c) (__lzo_gnuc_extension__ (c##ULL)) # else # define LZO_INT64_C(c) (c##LL) # define LZO_UINT64_C(c) (c##ULL) # endif #elif (LZO_SIZEOF___INT64 == 8) # define lzo_int64e_t __int64 # define lzo_uint64e_t unsigned __int64 # define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF___INT64 # if (LZO_CC_BORLANDC) # define LZO_INT64_C(c) ((c) + 0i64) # define LZO_UINT64_C(c) ((c) + 0ui64) # else # define LZO_INT64_C(c) (c##i64) # define LZO_UINT64_C(c) (c##ui64) # endif #else #endif #endif #if defined(lzo_int64e_t) # define LZO_SIZEOF_LZO_INT64E_T 8 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64e_t) == 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64e_t) == LZO_SIZEOF_LZO_INT64E_T) #endif #if !defined(lzo_int32l_t) #if defined(lzo_int32e_t) # define lzo_int32l_t lzo_int32e_t # define lzo_uint32l_t lzo_uint32e_t # define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_LZO_INT32E_T # define LZO_TYPEOF_LZO_INT32L_T LZO_TYPEOF_LZO_INT32E_T #elif (LZO_SIZEOF_INT >= 4) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) # define lzo_int32l_t int # define lzo_uint32l_t unsigned int # define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_INT # define LZO_TYPEOF_LZO_INT32L_T LZO_SIZEOF_INT #elif (LZO_SIZEOF_LONG >= 4) # define lzo_int32l_t long int # define lzo_uint32l_t unsigned long int # define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_LONG # define LZO_TYPEOF_LZO_INT32L_T LZO_SIZEOF_LONG #else # error "lzo_int32l_t" #endif #endif #if 1 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32l_t) >= 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32l_t) == LZO_SIZEOF_LZO_INT32L_T) #endif #if !defined(lzo_int64l_t) #if defined(lzo_int64e_t) # define lzo_int64l_t lzo_int64e_t # define lzo_uint64l_t lzo_uint64e_t # define LZO_SIZEOF_LZO_INT64L_T LZO_SIZEOF_LZO_INT64E_T # define LZO_TYPEOF_LZO_INT64L_T LZO_TYPEOF_LZO_INT64E_T #else #endif #endif #if defined(lzo_int64l_t) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64l_t) >= 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64l_t) == LZO_SIZEOF_LZO_INT64L_T) #endif #if !defined(lzo_int32f_t) #if (LZO_SIZEOF_SIZE_T >= 8) # define lzo_int32f_t lzo_int64l_t # define lzo_uint32f_t lzo_uint64l_t # define LZO_SIZEOF_LZO_INT32F_T LZO_SIZEOF_LZO_INT64L_T # define LZO_TYPEOF_LZO_INT32F_T LZO_TYPEOF_LZO_INT64L_T #else # define lzo_int32f_t lzo_int32l_t # define lzo_uint32f_t lzo_uint32l_t # define LZO_SIZEOF_LZO_INT32F_T LZO_SIZEOF_LZO_INT32L_T # define LZO_TYPEOF_LZO_INT32F_T LZO_TYPEOF_LZO_INT32L_T #endif #endif #if 1 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32f_t) >= 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32f_t) == LZO_SIZEOF_LZO_INT32F_T) #endif #if !defined(lzo_int64f_t) #if defined(lzo_int64l_t) # define lzo_int64f_t lzo_int64l_t # define lzo_uint64f_t lzo_uint64l_t # define LZO_SIZEOF_LZO_INT64F_T LZO_SIZEOF_LZO_INT64L_T # define LZO_TYPEOF_LZO_INT64F_T LZO_TYPEOF_LZO_INT64L_T #else #endif #endif #if defined(lzo_int64f_t) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64f_t) >= 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64f_t) == LZO_SIZEOF_LZO_INT64F_T) #endif #if !defined(lzo_intptr_t) #if 1 && (LZO_OS_OS400 && (LZO_SIZEOF_VOID_P == 16)) # define __LZO_INTPTR_T_IS_POINTER 1 typedef char * lzo_intptr_t; typedef char * lzo_uintptr_t; # define lzo_intptr_t lzo_intptr_t # define lzo_uintptr_t lzo_uintptr_t # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_VOID_P # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_CHAR_P #elif (LZO_CC_MSC && (_MSC_VER >= 1300) && (LZO_SIZEOF_VOID_P == 4) && (LZO_SIZEOF_INT == 4)) typedef __w64 int lzo_intptr_t; typedef __w64 unsigned int lzo_uintptr_t; # define lzo_intptr_t lzo_intptr_t # define lzo_uintptr_t lzo_uintptr_t # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_INT # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_INT #elif (LZO_SIZEOF_SHORT == LZO_SIZEOF_VOID_P) && (LZO_SIZEOF_INT > LZO_SIZEOF_VOID_P) # define lzo_intptr_t short # define lzo_uintptr_t unsigned short # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_SHORT # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_SHORT #elif (LZO_SIZEOF_INT >= LZO_SIZEOF_VOID_P) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) # define lzo_intptr_t int # define lzo_uintptr_t unsigned int # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_INT # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_INT #elif (LZO_SIZEOF_LONG >= LZO_SIZEOF_VOID_P) # define lzo_intptr_t long # define lzo_uintptr_t unsigned long # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_LONG # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_LONG #elif (LZO_SIZEOF_LZO_INT64L_T >= LZO_SIZEOF_VOID_P) # define lzo_intptr_t lzo_int64l_t # define lzo_uintptr_t lzo_uint64l_t # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_LZO_INT64L_T # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_LZO_INT64L_T #else # error "lzo_intptr_t" #endif #endif #if 1 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_intptr_t) >= sizeof(void *)) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_intptr_t) == sizeof(lzo_uintptr_t)) #endif #if !defined(lzo_word_t) #if defined(LZO_WORDSIZE) && (LZO_WORDSIZE+0 > 0) #if (LZO_WORDSIZE == LZO_SIZEOF_LZO_INTPTR_T) && !(__LZO_INTPTR_T_IS_POINTER) # define lzo_word_t lzo_uintptr_t # define lzo_sword_t lzo_intptr_t # define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LZO_INTPTR_T # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_LZO_INTPTR_T #elif (LZO_WORDSIZE == LZO_SIZEOF_LONG) # define lzo_word_t unsigned long # define lzo_sword_t long # define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LONG # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_LONG #elif (LZO_WORDSIZE == LZO_SIZEOF_INT) # define lzo_word_t unsigned int # define lzo_sword_t int # define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_INT # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_INT #elif (LZO_WORDSIZE == LZO_SIZEOF_SHORT) # define lzo_word_t unsigned short # define lzo_sword_t short # define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_SHORT # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_SHORT #elif (LZO_WORDSIZE == 1) # define lzo_word_t unsigned char # define lzo_sword_t signed char # define LZO_SIZEOF_LZO_WORD_T 1 # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_CHAR #elif (LZO_WORDSIZE == LZO_SIZEOF_LZO_INT64L_T) # define lzo_word_t lzo_uint64l_t # define lzo_sword_t lzo_int64l_t # define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LZO_INT64L_T # define LZO_TYPEOF_LZO_WORD_T LZO_SIZEOF_LZO_INT64L_T #elif (LZO_ARCH_SPU) && (LZO_CC_GNUC) #if 0 typedef unsigned lzo_word_t __attribute__((__mode__(__V16QI__))); typedef int lzo_sword_t __attribute__((__mode__(__V16QI__))); # define lzo_word_t lzo_word_t # define lzo_sword_t lzo_sword_t # define LZO_SIZEOF_LZO_WORD_T 16 # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF___MODE_V16QI #endif #else # error "lzo_word_t" #endif #endif #endif #if 1 && defined(lzo_word_t) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_word_t) == LZO_WORDSIZE) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_sword_t) == LZO_WORDSIZE) #endif #if 1 #define lzo_int8_t signed char #define lzo_uint8_t unsigned char #define LZO_SIZEOF_LZO_INT8_T 1 #define LZO_TYPEOF_LZO_INT8_T LZO_TYPEOF_CHAR LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int8_t) == 1) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int8_t) == sizeof(lzo_uint8_t)) #endif #if defined(lzo_int16e_t) #define lzo_int16_t lzo_int16e_t #define lzo_uint16_t lzo_uint16e_t #define LZO_SIZEOF_LZO_INT16_T LZO_SIZEOF_LZO_INT16E_T #define LZO_TYPEOF_LZO_INT16_T LZO_TYPEOF_LZO_INT16E_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16_t) == 2) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16_t) == sizeof(lzo_uint16_t)) #endif #if defined(lzo_int32e_t) #define lzo_int32_t lzo_int32e_t #define lzo_uint32_t lzo_uint32e_t #define LZO_SIZEOF_LZO_INT32_T LZO_SIZEOF_LZO_INT32E_T #define LZO_TYPEOF_LZO_INT32_T LZO_TYPEOF_LZO_INT32E_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32_t) == 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32_t) == sizeof(lzo_uint32_t)) #endif #if defined(lzo_int64e_t) #define lzo_int64_t lzo_int64e_t #define lzo_uint64_t lzo_uint64e_t #define LZO_SIZEOF_LZO_INT64_T LZO_SIZEOF_LZO_INT64E_T #define LZO_TYPEOF_LZO_INT64_T LZO_TYPEOF_LZO_INT64E_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64_t) == 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64_t) == sizeof(lzo_uint64_t)) #endif #if 1 #define lzo_int_least32_t lzo_int32l_t #define lzo_uint_least32_t lzo_uint32l_t #define LZO_SIZEOF_LZO_INT_LEAST32_T LZO_SIZEOF_LZO_INT32L_T #define LZO_TYPEOF_LZO_INT_LEAST32_T LZO_TYPEOF_LZO_INT32L_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least32_t) >= 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least32_t) == sizeof(lzo_uint_least32_t)) #endif #if defined(lzo_int64l_t) #define lzo_int_least64_t lzo_int64l_t #define lzo_uint_least64_t lzo_uint64l_t #define LZO_SIZEOF_LZO_INT_LEAST64_T LZO_SIZEOF_LZO_INT64L_T #define LZO_TYPEOF_LZO_INT_LEAST64_T LZO_TYPEOF_LZO_INT64L_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least64_t) >= 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least64_t) == sizeof(lzo_uint_least64_t)) #endif #if 1 #define lzo_int_fast32_t lzo_int32f_t #define lzo_uint_fast32_t lzo_uint32f_t #define LZO_SIZEOF_LZO_INT_FAST32_T LZO_SIZEOF_LZO_INT32F_T #define LZO_TYPEOF_LZO_INT_FAST32_T LZO_TYPEOF_LZO_INT32F_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast32_t) >= 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast32_t) == sizeof(lzo_uint_fast32_t)) #endif #if defined(lzo_int64f_t) #define lzo_int_fast64_t lzo_int64f_t #define lzo_uint_fast64_t lzo_uint64f_t #define LZO_SIZEOF_LZO_INT_FAST64_T LZO_SIZEOF_LZO_INT64F_T #define LZO_TYPEOF_LZO_INT_FAST64_T LZO_TYPEOF_LZO_INT64F_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast64_t) >= 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast64_t) == sizeof(lzo_uint_fast64_t)) #endif #if !defined(LZO_INT16_C) # if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 2) # define LZO_INT16_C(c) ((c) + 0) # define LZO_UINT16_C(c) ((c) + 0U) # elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 2) # define LZO_INT16_C(c) ((c) + 0L) # define LZO_UINT16_C(c) ((c) + 0UL) # elif (LZO_SIZEOF_INT >= 2) # define LZO_INT16_C(c) (c) # define LZO_UINT16_C(c) (c##U) # elif (LZO_SIZEOF_LONG >= 2) # define LZO_INT16_C(c) (c##L) # define LZO_UINT16_C(c) (c##UL) # else # error "LZO_INT16_C" # endif #endif #if !defined(LZO_INT32_C) # if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 4) # define LZO_INT32_C(c) ((c) + 0) # define LZO_UINT32_C(c) ((c) + 0U) # elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 4) # define LZO_INT32_C(c) ((c) + 0L) # define LZO_UINT32_C(c) ((c) + 0UL) # elif (LZO_SIZEOF_INT >= 4) # define LZO_INT32_C(c) (c) # define LZO_UINT32_C(c) (c##U) # elif (LZO_SIZEOF_LONG >= 4) # define LZO_INT32_C(c) (c##L) # define LZO_UINT32_C(c) (c##UL) # elif (LZO_SIZEOF_LONG_LONG >= 4) # define LZO_INT32_C(c) (c##LL) # define LZO_UINT32_C(c) (c##ULL) # else # error "LZO_INT32_C" # endif #endif #if !defined(LZO_INT64_C) && defined(lzo_int64l_t) # if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 8) # define LZO_INT64_C(c) ((c) + 0) # define LZO_UINT64_C(c) ((c) + 0U) # elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 8) # define LZO_INT64_C(c) ((c) + 0L) # define LZO_UINT64_C(c) ((c) + 0UL) # elif (LZO_SIZEOF_INT >= 8) # define LZO_INT64_C(c) (c) # define LZO_UINT64_C(c) (c##U) # elif (LZO_SIZEOF_LONG >= 8) # define LZO_INT64_C(c) (c##L) # define LZO_UINT64_C(c) (c##UL) # else # error "LZO_INT64_C" # endif #endif #endif #endif /* already included */ /* vim:set ts=4 sw=4 et: */ ================================================ FILE: include/utils/mhvtl_list.h ================================================ #ifndef __LIST_H__ #define __LIST_H__ #include /* taken from linux kernel */ #undef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER) #else #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) #endif #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type, member) ); }) struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) {&(name), &(name)} #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) static inline int list_empty(const struct list_head *head) { return head->next == head; } #define list_entry(ptr, type, member) \ container_of(ptr, type, member) #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) static inline void __list_add(struct list_head *newhead, struct list_head *prev, struct list_head *next) { next->prev = newhead; newhead->next = next; newhead->prev = prev; prev->next = newhead; } static inline void list_add(struct list_head *newhead, struct list_head *head) { __list_add(newhead, head, head->next); } static inline void list_add_tail(struct list_head *newhead, struct list_head *head) { __list_add(newhead, head->prev, head); } static inline void __list_del(struct list_head *prev, struct list_head *next) { next->prev = prev; prev->next = next; } static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = entry->prev = NULL; } static inline void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); } static inline void __list_splice(const struct list_head *list, struct list_head *prev, struct list_head *next) { struct list_head *first = list->next; struct list_head *last = list->prev; first->prev = prev; prev->next = first; last->next = next; next->prev = last; } static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head, head->next); INIT_LIST_HEAD(list); } } #endif ================================================ FILE: include/utils/mhvtl_update.h ================================================ /* * Structures and Functions to update the tape format and mam format to new versions * * Copyright (C) 2005-2025 Mark Harvey markh794@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef MHVTL_UPDATE_H #define MHVTL_UPDATE_H int try_extract_mam(char *currentPCL); int try_update_mam(char *currentPCL); int try_update_tape(char *currentPCL); #endif /* MHVTL_UPDATE_H */ ================================================ FILE: include/utils/minilzo.h ================================================ /* minilzo.h -- mini subset of the LZO real-time data compression library This file is part of the LZO real-time data compression library. Copyright (C) 1996-2015 Markus Franz Xaver Johannes Oberhumer All Rights Reserved. The LZO library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The LZO 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 General Public License for more details. You should have received a copy of the GNU General Public License along with the LZO library; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Markus F.X.J. Oberhumer http://www.oberhumer.com/opensource/lzo/ */ /* * NOTE: * the full LZO package can be found at * http://www.oberhumer.com/opensource/lzo/ */ #ifndef __MINILZO_H_INCLUDED #define __MINILZO_H_INCLUDED 1 #define MINILZO_VERSION 0x2090 #if defined(__LZOCONF_H_INCLUDED) # error "you cannot use both LZO and miniLZO" #endif /* internal Autoconf configuration file - only used when building miniLZO */ #ifdef MINILZO_HAVE_CONFIG_H # include #endif #include #include #ifndef __LZODEFS_H_INCLUDED #include "lzodefs.h" #endif #undef LZO_HAVE_CONFIG_H #include "lzoconf.h" #if !defined(LZO_VERSION) || (LZO_VERSION != MINILZO_VERSION) # error "version mismatch in header files" #endif #ifdef __cplusplus extern "C" { #endif /*********************************************************************** // ************************************************************************/ /* Memory required for the wrkmem parameter. * When the required size is 0, you can also pass a NULL pointer. */ #define LZO1X_MEM_COMPRESS LZO1X_1_MEM_COMPRESS #define LZO1X_1_MEM_COMPRESS ((lzo_uint32_t) (16384L * lzo_sizeof_dict_t)) #define LZO1X_MEM_DECOMPRESS (0) /* compression */ LZO_EXTERN(int) lzo1x_1_compress ( const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, lzo_uintp dst_len, lzo_voidp wrkmem ); /* decompression */ LZO_EXTERN(int) lzo1x_decompress ( const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, lzo_uintp dst_len, lzo_voidp wrkmem /* NOT USED */ ); /* safe decompression with overrun testing */ LZO_EXTERN(int) lzo1x_decompress_safe ( const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, lzo_uintp dst_len, lzo_voidp wrkmem /* NOT USED */ ); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* already included */ /* vim:set ts=4 sw=4 et: */ ================================================ FILE: include/utils/q.h ================================================ /* * q.h -- Message queue for vtltape/vtllibrary * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * Message key - My 2 seconds of fame :-) */ #ifndef _Q_H_ #define _Q_H_ #define MAXTEXTLEN 1024 struct q_msg { long snd_id; char text[MAXTEXTLEN + 1]; }; #define QKEY (key_t)0x4d61726b /* Identifying key for queue */ #define QPERM 0660 /* Permissions for queue */ #define MAXOBN sizeof(struct q_msg) /* Maximum length of message for Q. */ #define MAXPRIOR 1024 /* max priority level */ #define VTLCMD_Q 32768 /* Priority for vtlcmd */ struct q_entry { long rcv_id; struct q_msg msg; }; int enter(char *, long rcv_id); int send_msg(char *cmd, long rcv_id); int serve(void); int init_queue(void); extern long my_id; /* Message strings passed between vtllibrary & vtltape */ #define msg_not_occupied "Not occupied" #define msg_occupied "occupied" #define msg_unload_ok "Unloaded OK" #define msg_load_failed "Load failed" #define msg_load_ok "Loaded OK" #define msg_mount_state "mount_state" #define msg_eject "eject" #define msg_set_empty "set_empty" #endif /* _Q_H_ */ ================================================ FILE: include/utils/security_protocol.h ================================================ #ifndef _SECURITY_PROTOCOL_H_ #define _SECURITY_PROTOCOL_H_ /* * Security Protocol IN/OUT * * Based on spc4r14 / ssc3r04a * */ #include /* * Security Algorithm Code */ #define HMAC_KDF_SHA1 0x0020002 #define HMAC_KDF_SHA256 0x0020005 #define HMAC_KDF_SHA384 0x0020006 #define HMAC_KDF_SHA512 0x0020007 #define KDF_AES_128_XCBC 0x0020004 /* * Security Association * spc4r14 5.13.2 */ struct sa { uint32_t ac_sai; uint32_t ds_sai; uint32_t timeout; uint64_t ac_sqn; uint64_t ds_sqn; uint8_t ac_nonce[64]; uint8_t ds_nonce[64]; uint8_t key_seed[64]; uint32_t kdf_id; uint8_t keymat[1024]; uint16_t usage_type; uint8_t usage_data[1024]; uint8_t mgmt_data[1024]; }; #endif /* _SECURITY_PROTOCOL_H_ */ ================================================ FILE: include/utils/subprocess.h ================================================ int run_command(char *command, int timeout); ================================================ FILE: include/vtlcart.h ================================================ /* * Describes the header layout of each tape 'block' * * $Id: vtlcart.h,v 1.1.2.1 2006-08-06 07:58:44 markh Exp $ * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _VTLCART_H_ #define _VTLCART_H_ #include struct MAM; /* Block type definitions */ #define B_DATA 11 #define B_FILEMARK 3 #define B_EOD 5 /* End of data */ #define B_NOOP 8 /* No Operation - fake it */ #define BLKHDR_FLG_ZLIB_COMPRESSED 0x01 #define BLKHDR_FLG_ENCRYPTED 0x02 #define BLKHDR_FLG_LZO_COMPRESSED 0x04 #define BLKHDR_FLG_CRC 0x08 #define TAPE_FMT_VERSION 6 #define ENCR_KEY_MAX_LEN 32 struct encryption { uint32_t key_length; uint32_t ukad_length; uint32_t akad_length; uint32_t pad; uint8_t key[ENCR_KEY_MAX_LEN]; uint8_t ukad[ENCR_KEY_MAX_LEN]; uint8_t akad[ENCR_KEY_MAX_LEN]; }; /* * Header before each block of data in 'file' * * block_type -> See above 'Block type definitions' * blk_size -> Uncompressed size of data block * (Specifies capacity of tape (used in BOT header) in Mbytes. * disk_blk_size -> Amount of space block takes up in 'file' * uncomp_crc -> CRC of the uncompressed data * encryption.key_length -> what length was the key used to 'encrypt' this block * encryption.ukad_length -> what length was the ukad used to 'encrypt' this block * encryption.akad_length -> what length was the akad used to 'encrypt' this block * encryption.key -> what key was used to 'encrypt' this block * encryption.ukad -> what ukad was used to 'encrypt' this block * encryption.kkad -> what akad was used to 'encrypt' this block */ struct blk_header { uint32_t blk_type; uint32_t blk_flags; uint32_t blk_number; uint32_t blk_size; uint32_t disk_blk_size; uint32_t uncomp_crc; struct encryption blk_encryption_info; uint32_t partition_id; uint32_t reserved; // for 64 bit alignment /* * Add other things right here... * Be careful to keep data 64bit aligned */ }; /* Default tape size specified in Mbytes */ #define DEFAULT_TAPE_SZ 8000 /* * Medium Density Code * * This value is returned in the first byte [0] of the Mode Sense 'Block Descriptor' */ /* A good list can be found her: https://github.com/iustin/mt-st/blob/fbfd923faad0d6f613415f4de747833fb6b4a465/mt.c#L136 */ #define medium_density_code_unknown 0x20 #define medium_density_code_lto1 0x40 #define medium_density_code_lto2 0x42 #define medium_density_code_lto3 0x44 #define medium_density_code_lto4 0x46 #define medium_density_code_lto5 0x58 #define medium_density_code_lto6 0x5a #define medium_density_code_lto7 0x5c #define medium_density_code_lto7 0x5c #define medium_density_code_lto8_m8 0x5d #define medium_density_code_lto8 0x5e #define medium_density_code_lto9 0x60 #define medium_density_code_j1a 0x51 #define medium_density_code_e05 0x52 #define medium_density_code_e06 0x53 #define medium_density_code_e07 0x54 #define medium_density_code_j1a_ENCR 0x71 #define medium_density_code_e05_ENCR 0x72 #define medium_density_code_e06_ENCR 0x73 #define medium_density_code_e07_ENCR 0x74 #define medium_density_code_ait1 0x30 #define medium_density_code_ait2 0x31 #define medium_density_code_ait3 0x32 #define medium_density_code_ait4 0x33 #define medium_density_code_10kA 0x4a #define medium_density_code_10kB 0x4b #define medium_density_code_10kC 0x4c #define medium_density_code_9840A 0x42 #define medium_density_code_9840B 0x42 #define medium_density_code_9840C 0x45 #define medium_density_code_9840D 0x46 #define medium_density_code_9940A 0x43 #define medium_density_code_9940B 0x44 #define medium_density_code_dlt2 0x16 /* X3.193-1990 */ #define medium_density_code_dlt3_2G 0x17 /* 2.6G */ #define medium_density_code_dlt3_6G 0x18 /* 6G */ #define medium_density_code_dlt3_10G 0x19 /* 10G */ #define medium_density_code_dlt3 0x19 /* default to 10G density */ #define medium_density_code_dlt4_20G 0x1a #define medium_density_code_dlt4_35G 0x1b #define medium_density_code_dlt4 0x1b /* Default to 35G density */ #define medium_density_code_sdlt 0x90 #define medium_density_code_220 0x48 #define medium_density_code_320 0x49 #define medium_density_code_600 0x4a #define medium_density_code_s4 0x4b #define medium_density_code_QIC 0x11 #define medium_density_code_DDS1 0x13 #define medium_density_code_DDS2 0x24 #define medium_density_code_DDS3 0x25 #define medium_density_code_DDS4 0x26 #define medium_density_code_DDS5 0x47 /* Used by MODE SENSE 'media type' in Mode Parameter Header */ #define media_type_unknown 0x00 #define media_type_dlt_clean 0x81 #define media_type_dlt1 0x82 #define media_type_dlt3 0x83 #define media_type_dlt3xt 0x84 #define media_type_dlt4 0x85 #define media_type_lto1_data 0x18 #define media_type_lto2_data 0x28 #define media_type_lto3_data 0x38 #define media_type_lto3_worm 0x3C #define media_type_lto4_data 0x48 #define media_type_lto4_worm 0x4C #define media_type_lto5_data 0x58 #define media_type_lto5_worm 0x5c #define media_type_lto6_data 0x68 #define media_type_lto6_worm 0x6c #define media_type_lto7_data 0x78 #define media_type_lto7_worm 0x7c #define media_type_lto8_data 0x88 #define media_type_lto8_worm 0x8c #define media_type_lto9_data 0x98 #define media_type_lto9_worm 0x9c #define media_type_hp_lto_data 0x00 #define media_type_hp_lto_worm 0x01 #define media_type_hp_lto_cd 0x80 #define LZO 1 /* Using lzo compression libraries */ #define ZLIB 2 /* Using zlib compression libraries */ /* The remainder of this file defines the interface between the tape drive software and the implementation of a tape cartridge as one or more disk files. */ int create_tape(const char *pcl, uint8_t *sam_stat); int load_partition(const char *pcl, uint8_t *sam_stat, uint8_t error_check, uint8_t partition_number); int load_tape(const char *pcl, uint8_t *sam_stat); void unload_tape(uint8_t *sam_stat); int rewind_tape(uint8_t *sam_stat); int position_to_eod(uint8_t *sam_stat); int position_to_block(uint32_t blk_no, uint8_t *sam_stat); int position_blocks_forw(uint64_t count, uint8_t *sam_stat); int position_blocks_back(uint64_t count, uint8_t *sam_stat); int position_filemarks_forw(uint64_t count, uint8_t *sam_stat); int position_filemarks_back(uint64_t count, uint8_t *sam_stat); int change_partition(uint8_t partition_number); uint32_t read_tape_block(uint8_t *buf, uint32_t size, uint8_t *sam_stat); int write_filemarks(uint32_t count, uint8_t *sam_stat); int write_tape_block(const uint8_t *buf, uint32_t uncomp_size, uint32_t comp_size, const struct encryption *cp, uint8_t comp_type, uint8_t null_type, uint32_t crc, uint8_t *sam_stat); int format_partition(uint8_t *sam_stat); int format_tape(uint8_t *sam_stat); int write_mam(int mam_fd, int mhvtl_fd); int read_mam(int mam_fd, int mhvtl_fd, struct MAM *mamp); int rewriteMAM(uint8_t *sam_stat); uint64_t current_tape_offset(void); uint64_t current_tape_block(void); uint64_t last_block(uint8_t partition_number); uint64_t block_from_filemark(uint8_t partition_number, uint32_t filemark); uint64_t count_filemarks(int64_t count); void print_raw_header(void); void print_filemark_count(void); void print_metadata(void); void cart_deinit(void); #endif /* _VTLCART_H_ */ ================================================ FILE: include/vtllib.h ================================================ /* * The shared library libvtlscsi.so function defs * * Copyright (C) 2005-2025 Mark Harvey markh794@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _VTLLIB_H_ #define _VTLLIB_H_ #ifndef Solaris #include #include #endif #include #include #include #include #include "vtl_common.h" #include "mhvtl_list.h" #include "vtlcart.h" #ifndef MHVTL_CONFIG_PATH #define MHVTL_CONFIG_PATH "/etc/mhvtl" #endif #ifndef MHVTL_HOME_PATH /* Where all the tape data files belong */ #define MHVTL_HOME_PATH "/opt/vtl" #endif /* http://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/ */ #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)])) #define min(x, y) ({ \ typeof(x) _x = (x); \ typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x < _y ? _x : _y; }) #define max(x, y) ({ \ typeof(x) _x = (x); \ typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x > _y ? _x : _y; }) #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define likely(x) __builtin_expect((x), 1) #define unlikely(x) __builtin_expect((x), 0) #define STATUS_OK 0 #define STATUS_QUEUE_CMD 0xfe #define SCSI_SN_LEN 16 #define MAX_BARCODE_LEN 16 #define LEFT_JUST_16_STR "%-16.16s" #define MAX_INQ_ARR_SZ 64 #define MALLOC_SZ 512 #define TAPE_UNLOADED 0 #define TAPE_LOADED 1 #define TAPE_LOADING 2 #define MIN_SLEEP_TIME 5 #define DEFLT_BACKOFF_VALUE 400 #define HOME_DIR_PATH_SZ 1024 #define CONF_DIR_PATH_SZ 1024 #define CONF_FILE_SZ 1024 + 256 #define MAX_PARTITIONS 4 typedef enum { DEVICE_CONF, LIBCONTENTS, LIBCONTENTS_PERSIST, } conf_file; /* * Medium Type Definitions */ #define MEDIA_TYPE_DATA 0 #define MEDIA_TYPE_WORM 1 #define MEDIA_TYPE_NULL 2 #define MEDIA_TYPE_DIAGNOSTIC 3 #define MEDIA_TYPE_FIRMWARE 4 #define MEDIA_TYPE_CLEAN 6 #define MHVTL_NO_COMPRESSION 0 /* status definitions (byte[2] in the element descriptor) */ #define STATUS_Full 0x01 #define STATUS_ImpExp 0x02 #define STATUS_Except 0x04 #define STATUS_Access 0x08 #define STATUS_ExEnab 0x10 #define STATUS_InEnab 0x20 #define STATUS_Reserved6 0x40 #define STATUS_Reserved7 0x80 /* internal_status definitions: */ #define INSTATUS_NO_BARCODE 0x01 #define VOLTAG_LEN 36 /* size of voltag area in RES descriptor */ #define VPD_83_SZ 50 #define VPD_86_SZ 0x3c #define VPD_B0_SZ 4 #define VPD_B1_SZ SCSI_SN_LEN #define VPD_B2_SZ 8 #define VPD_B5_SZ 8 #define VPD_C0_SZ 0x28 struct smc_type_slot { char type; uint32_t start; uint32_t number; }; struct mode { struct list_head siblings; uint8_t pcode; /* Page code */ uint8_t subpcode; /* Sub page code */ int32_t pcodeSize; /* Size of page code data. */ uint8_t *pcodePointerBitMap; /* bitmap for changeable data */ uint8_t *pcodePointer; /* Pointer to page code data */ char *description; /* ASCII text 'description' */ }; /* v2 of the tape media * Between BOT & blk #1, is the MAM (Medium Auxiliary Memory) */ #define MAM_VERSION 4 enum MAM_attribute_idx { /* 0x0000 – 0x03ff : Device */ MAM_REMAINING_CAPACITY, MAM_MAX_CAPACITY, MAM_TAPE_ALERT, MAM_LOAD_COUNT, MAM_MAM_SPACE_REMAINING, MAM_ASSIGNING_ORG_1, MAM_FORMATTED_DENSITY_CODE, MAM_INITIALIZATION_COUNT, /* 0x008 Not supported */ MAM_VOLUME_CHANGE_REFERENCE, MAM_DEV_MAKE_SERIAL_LAST_LOAD, MAM_DEV_MAKE_SERIAL_LAST_LOAD1, MAM_DEV_MAKE_SERIAL_LAST_LOAD2, MAM_DEV_MAKE_SERIAL_LAST_LOAD3, MAM_WRITTEN_IN_MEDIUM_LIFE, MAM_READ_IN_MEDIUM_LIFE, MAM_WRITTEN_IN_LAST_LOAD, MAM_READ_IN_LAST_LOAD, /* 0x0400 – 0x07ff : Medium */ MAM_MEDIUM_MANUFACTURER, MAM_MEDIUM_SERIAL_NUMBER, MAM_MEDIUM_LENGTH, MAM_MEDIUM_WIDTH, MAM_ASSIGNING_ORG_2, MAM_MEDIUM_DENSITY_CODE, MAM_MEDIUM_MANUFACTURE_DATE, MAM_MAM_CAPACITY, MAM_MEDIUM_TYPE, MAM_MEDIUM_TYPE_INFORMATION, /* 0x0800 – 0x0bff : Host */ MAM_APPLICATION_VENDOR, MAM_APPLICATION_NAME, MAM_APPLICATION_VERSION, MAM_USER_MEDIUM_TEXT_LABEL, MAM_DATE_TIME_LAST_WRITTEN, MAM_LOCALIZATION_IDENTIFIER, MAM_BARCODE, MAM_OWNING_HOST_TEXTUAL_NAME, MAM_MEDIA_POOL, MAM_APPLICATION_FORMAT_VERSION, MAM_VOLUME_COHERENCY_INFORMATION, /* 0x0c00 - 0x0fff - Device - Vendor Specific */ /* 0x1000 - 0x13ff - Medium - Vendor Specific */ /* 0x1400 - 0x17ff - Host - Vendor Specific */ MAM_VOLUME_LOCK, MAM_ATTRIBUTE_END, }; enum MHVTL_attribute_idx { MAM_MHVTL_RECORD_DIRTY, MAM_MHVTL_FLAGS, MAM_MHVTL_MAX_PARTITIONS, MAM_MHVTL_NUM_PARTITIONS, MAM_MHVTL_MEDIA_TYPE, /* media_info */ MAM_MHVTL_MEDIAINFO_BITS_PER_MM, MAM_MHVTL_MEDIAINFO_TRACKS, MAM_MHVTL_MEDIAINFO_DENSITY_NAME, MAM_MHVTL_MEDIAINFO_DESCRIPTION, MAM_MHVTL_ATTRIBUTE_END, }; struct MAM_attr { uint16_t attribute_id; uint16_t length; uint8_t read_only; /* 0: Not RO 1: RO */ uint8_t format; /* 0: binary 1: ASCII 2: TEXT */ void *value; }; struct MHVTL_attr { uint16_t attribute_id; uint16_t length; void *value; }; struct MAM { struct MAM_attr attributes[MAM_ATTRIBUTE_END + 1]; struct MHVTL_attr mhvtl_attr[MAM_MHVTL_ATTRIBUTE_END + 1]; uint32_t tape_fmt_version; uint32_t mam_fmt_version; /* Device (0x0000 - 0x03ff) */ uint64_t remaining_capacity; uint64_t max_capacity; uint64_t TapeAlert; uint64_t LoadCount; uint64_t MAMSpaceRemaining; uint8_t AssigningOrganization_1[8]; uint8_t FormattedDensityCode; uint8_t InitializationCount[2]; uint32_t VolumeChangeReference; uint8_t DevMakeSerialLastLoad[40]; uint8_t DevMakeSerialLastLoad1[40]; uint8_t DevMakeSerialLastLoad2[40]; uint8_t DevMakeSerialLastLoad3[40]; uint64_t WrittenInMediumLife; uint64_t ReadInMediumLife; uint64_t WrittenInLastLoad; uint64_t ReadInLastLoad; /* Medium (0x0400 - 0x07ff) */ uint8_t MediumManufacturer[8]; uint8_t MediumSerialNumber[32]; uint32_t MediumLength; uint32_t MediumWidth; uint8_t AssigningOrganization_2[8]; uint8_t MediumDensityCode; uint8_t MediumManufactureDate[12]; uint64_t MAMCapacity; uint8_t MediumType; /* 0 -> Data, 1 -> WORM, 6 -> Clean */ uint16_t MediumTypeInformation; /* If Clean, max mount */ /* Host (0x0800 - 0x0bff) */ uint8_t ApplicationVendor[8]; uint8_t ApplicationName[32]; uint8_t ApplicationVersion[8]; uint8_t UserMediumTextLabel[160]; uint8_t DateTimeLastWritten[12]; uint8_t LocalizationIdentifier; uint8_t Barcode[32]; uint8_t OwningHostTextualName[80]; uint8_t MediaPool[160]; uint8_t ApplicationFormatVersion[16]; uint8_t VolumeCoherencyInformation[46]; /* 0x0c00 - 0x0fff - Device - Vendor Specific */ /* 0x1000 - 0x13ff - Medium - Vendor Specific */ /* 0x1400 - 0x17ff - Host - Vendor Specific */ uint8_t VolumeLock; /* mhvtl attributes */ uint8_t record_dirty; /* 0 = Record clean, non-zero umount failed. */ uint16_t Flags; uint8_t MediaType; /* LTO1, LTO2, AIT etc (Media_Type_list) */ uint8_t max_partitions; uint8_t num_partitions; struct uniq_media_info { uint32_t bits_per_mm; uint16_t tracks; char density_name[8]; char description[32]; } media_info; }; #define MAM_FLAGS_ENCRYPTION_FORMAT 0x0001 #define MAM_FLAGS_MEDIA_WRITE_PROTECT 0x0002 #define PCODE_SHIFT 7 #define PCODE_OFFSET(x) (x & ((1 << PCODE_SHIFT) - 1)) struct lu_phy_attr; /* Vital Product Data (VPD) */ struct vpd { uint16_t sz; uint8_t *data; }; enum drive_type_list { drive_undefined, drive_LTO1, drive_LTO2, drive_LTO3, drive_LTO4, drive_LTO5, drive_LTO6, drive_LTO7, drive_LTO8, drive_3592_J1A, drive_3592_E05, drive_3592_E06, drive_3592_E07, drive_DDS1, drive_DDS2, drive_DDS3, drive_DDS4, drive_DDS5, drive_AIT1, drive_AIT2, drive_AIT3, drive_AIT4, drive_10K_A, drive_10K_B, drive_10K_C, drive_DLT7K, drive_DLT8K, drive_SDLT, drive_SDLT220, drive_SDLT320, drive_SDLT600, drive_SDLT_S4, drive_9840_A, drive_9840_B, drive_9840_C, drive_9840_D, drive_9940_A, drive_9940_B, drive_UNKNOWN /* Always last */ }; /* Uniquely define each media type known */ enum Media_Type_list { Media_undefined, Media_LTO1, Media_LTO1_CLEAN, Media_LTO2, Media_LTO2_CLEAN, Media_LTO3, Media_LTO3_CLEAN, Media_LTO3_WORM, Media_LTO4, Media_LTO4_CLEAN, Media_LTO4_WORM, Media_LTO5, Media_LTO5_CLEAN, Media_LTO5_WORM, Media_LTO6, Media_LTO6_CLEAN, Media_LTO6_WORM, Media_LTO7, Media_LTO7_CLEAN, Media_LTO7_WORM, Media_LTO8, Media_LTO8_CLEAN, Media_LTO8_WORM, Media_LTO9, Media_LTO9_CLEAN, Media_LTO9_WORM, Media_3592_JA, Media_3592_JA_CLEAN, Media_3592_JW, Media_3592_JB, Media_3592_JB_CLEAN, Media_3592_JX, Media_3592_JX_CLEAN, Media_3592_JK, /* E07 */ Media_3592_JK_CLEAN, /* E07 */ Media_AIT1, Media_AIT1_CLEAN, Media_AIT2, Media_AIT2_CLEAN, Media_AIT3, Media_AIT3_CLEAN, Media_AIT4, Media_AIT4_CLEAN, Media_AIT4_WORM, Media_T10KA, Media_T10KA_CLEAN, Media_T10KA_WORM, Media_T10KB, Media_T10KB_CLEAN, Media_T10KB_WORM, Media_T10KC, Media_T10KC_CLEAN, Media_T10KC_WORM, Media_DLT2, Media_DLT2_CLEAN, Media_DLT3, Media_DLT3_CLEAN, Media_DLT4, Media_DLT4_CLEAN, Media_SDLT, Media_SDLT_CLEAN, Media_SDLT220, Media_SDLT220_CLEAN, Media_SDLT320, Media_SDLT320_CLEAN, Media_SDLT600, Media_SDLT600_CLEAN, Media_SDLT600_WORM, Media_SDLT_S4, Media_SDLT_S4_CLEAN, Media_SDLT_S4_WORM, Media_DDS1, Media_DDS1_CLEAN, Media_DDS2, Media_DDS2_CLEAN, Media_DDS3, Media_DDS3_CLEAN, Media_DDS4, Media_DDS4_CLEAN, Media_DDS5, Media_DDS5_CLEAN, Media_9840A, Media_9840A_CLEAN, Media_9840B, Media_9840B_CLEAN, Media_9840C, Media_9840C_CLEAN, Media_9840D, Media_9840D_CLEAN, Media_9940A, Media_9940A_CLEAN, Media_9940B, Media_9940B_CLEAN, Media_UNKNOWN /* always last */ }; struct scsi_cmd { uint8_t *scb; /* SCSI Command Block */ int scb_len; int cdev; /* filepointer to char dev */ useconds_t pollInterval; /* Poor mans Performance counter */ struct mhvtl_ds *dbuf_p; struct lu_phy_attr *lu; }; #define SCSI_OP(opcode, fn) \ [opcode] = { \ .cmd_perform = fn, \ } #define SCSI_OP_RANGE(lo_op, hi_op, fn) \ [lo_op... hi_op] = { \ .cmd_perform = fn, \ } struct device_type_operations { uint8_t (*cmd_perform)(struct scsi_cmd *cmd); int (*pre_cmd_perform)(struct scsi_cmd *cmd, void *p); int (*post_cmd_perform)(struct scsi_cmd *cmd, void *p); }; struct device_type_template { struct device_type_operations ops[256]; }; #define MAX_INQUIRY_SZ 256 /* Logical Unit information */ struct lu_phy_attr { char ptype; char mode_media_type; char online; char inquiry[MAX_INQUIRY_SZ]; char vendor_id[VENDOR_ID_LEN + 1]; char product_id[PRODUCT_ID_LEN + 1]; char lu_serial_no[SCSI_SN_LEN]; struct list_head den_list; struct list_head mode_pg; struct list_head log_pg; struct device_type_template *scsi_ops; uint8_t *naa; struct vpd *lu_vpd[1 << (PCODE_SHIFT + 1)]; FILE *fifo_fd; char *fifoname; int fifo_flag; int persist; /* Save changes across restarts */ uint8_t *sense_p; /* Pointer to sense buffer */ void *lu_private; /* Private data struct per lu */ }; /* Drive Info */ struct d_info { struct list_head siblings; char inq_vendor_id[10]; char inq_product_id[18]; char inq_product_rev[6]; char inq_product_sno[12]; long drv_id; /* drive's send_msg queue ID */ char online; /* Physical status of drive */ int SCSI_BUS; int SCSI_ID; int SCSI_LUN; char load_status; /* Tape is 'loaded' by drive */ struct s_info *slot; }; struct m_info { /* Media Info */ struct list_head siblings; uint32_t last_location; char barcode[MAX_BARCODE_LEN + 1]; uint8_t media_domain; uint8_t media_type; uint8_t cart_type; uint8_t internal_status; /* internal states */ }; struct s_info { /* Slot Info */ struct list_head siblings; uint32_t slot_location; uint32_t last_location; struct d_info *drive; struct m_info *media; /* Additional Sense Code & Additional Sense Code Qualifier */ uint16_t asc_ascq; uint8_t status; /* Used for MAP status. */ /* 1 Media Transport, 2 Storage, 3 MAP, 4 Data transfer */ uint8_t element_type; uint8_t media_domain; /* L700 */ uint8_t media_type; /* L700 */ }; #define DEF_SMC_PRIV_STATE_MSG_LENGTH 64 struct smc_priv { uint32_t bufsize; struct list_head drive_list; struct list_head slot_list; struct list_head media_list; int commandtimeout; /* Timeout for 'movecommand' */ int num_drives; int num_picker; int num_map; int num_storage; char cap_closed; char *state_msg; /* Custom State message */ char *movecommand; /* 3rd party command to call */ struct smc_personality_template *pm; }; struct density_info { uint32_t bits_per_mm; uint16_t media_width; uint16_t tracks; uint32_t capacity; uint16_t density; char assigning_org[9]; char density_name[9]; char description[20]; }; struct supported_density_list { struct list_head siblings; struct density_info *density_info; int rw; }; extern uint8_t sense[SENSE_BUF_SIZE]; /* Sense Specific Data - SPC4.5.5.2.4 * For those sense keys where the invalid byte/field is known */ struct s_sd { uint8_t byte0; uint16_t field_pointer; }; /* Used by Mode Sense - if set, return block descriptor */ extern uint8_t modeBlockDescriptor[8]; enum MHVTL_STATE { MHVTL_STATE_INIT, MHVTL_STATE_IDLE, /* Drive operation states */ MHVTL_STATE_UNLOADED, MHVTL_STATE_LOADING, MHVTL_STATE_LOADING_CLEAN, MHVTL_STATE_LOADING_WORM, MHVTL_STATE_LOADED, MHVTL_STATE_LOADED_IDLE, MHVTL_STATE_LOAD_FAILED, MHVTL_STATE_REWIND, MHVTL_STATE_POSITIONING, MHVTL_STATE_LOCATE, MHVTL_STATE_READING, MHVTL_STATE_WRITING, MHVTL_STATE_UNLOADING, MHVTL_STATE_ERASE, MHVTL_STATE_VERIFY, /* Library operation states */ MHVTL_STATE_MOVING_DRIVE_2_SLOT, MHVTL_STATE_MOVING_SLOT_2_DRIVE, MHVTL_STATE_MOVING_DRIVE_2_MAP, MHVTL_STATE_MOVING_MAP_2_DRIVE, MHVTL_STATE_MOVING_SLOT_2_MAP, MHVTL_STATE_MOVING_MAP_2_SLOT, MHVTL_STATE_MOVING_DRIVE_2_DRIVE, MHVTL_STATE_MOVING_SLOT_2_SLOT, MHVTL_STATE_OPENING_MAP, MHVTL_STATE_CLOSING_MAP, MHVTL_STATE_INVENTORY, MHVTL_STATE_INITIALISE_ELEMENTS, MHVTL_STATE_ONLINE, MHVTL_STATE_OFFLINE, MHVTL_STATE_UNKNOWN, }; int get_config(char *buf, conf_file conf, long my_id); void init_mam(struct MAM* mamp); int check_reset(uint8_t *); int check_inquiry_data_has_changed(uint8_t *); void reset_device(void); void set_inquiry_data_changed(void); void sam_unit_attention(uint16_t ascq, uint8_t *sam_stat); void sam_not_ready(uint16_t ascq, uint8_t *sam_stat); void sam_illegal_request(uint16_t ascq, struct s_sd *sd, uint8_t *sam_stat); void sam_medium_error(uint16_t ascq, uint8_t *sam_stat); void sam_blank_check(uint16_t ascq, uint8_t *sam_stat); void sam_data_protect(uint16_t ascq, uint8_t *sam_stat); void sam_hardware_error(uint16_t ascq, uint8_t *sam_stat); void sam_no_sense(uint8_t key, uint16_t ascq, uint8_t *sam_stat); void resp_log_select(uint8_t *, uint8_t *); int resp_read_position_long(loff_t, uint8_t *, uint8_t *); int resp_read_position(loff_t, uint8_t *, uint8_t *); uint32_t resp_read_media_serial(uint8_t *, uint8_t *, uint8_t *); int resp_mode_sense(uint8_t *, uint8_t *, struct mode *, uint8_t, uint8_t *); struct mode *lookup_mode_pg(struct list_head *l, uint8_t pcode, uint8_t subpcode); int resp_read_block_limits(struct mhvtl_ds *dbuf_p, int sz); void hex_dump(uint8_t *, int); void *zalloc(int sz); int chrdev_open(const char *name, unsigned minor); int chrdev_create(unsigned minor); void chrdev_delete(unsigned minor); int oom_adjust(void); int open_fifo(FILE **fifo_fd, char *fifoname); void status_change(FILE *fifo_fd, int current_status, int my_id, char **msg); char *readline(char *s, int len, FILE *f); void blank_fill(uint8_t *dest, char *src, int len); void log_opcode(char *opcode, struct scsi_cmd *cmd); struct vpd *alloc_vpd(uint16_t sz); void dealloc_vpd(struct vpd *pg); void cleanup_density_support(struct list_head *l); pid_t add_lu(unsigned minor, struct mhvtl_ctl *ctl); void completeSCSICommand(int, struct mhvtl_ds *ds); int retrieve_CDB_data(int cdev, struct mhvtl_ds *dbuf_p); int check_for_running_daemons(unsigned minor); int free_lock(unsigned minor); void mhvtl_prt_cdb(int l, struct scsi_cmd *cmd); void checkstrlen(char *s, unsigned int len, int linecount); extern int device_type_register(struct lu_phy_attr *lu, struct device_type_template *t); void process_fifoname(struct lu_phy_attr *lu, char *s, int flag); uint8_t clear_WORM(struct list_head *l); uint8_t set_WORM(struct list_head *l); uint8_t clear_compression_mode_pg(struct list_head *l); uint8_t set_compression_mode_pg(struct list_head *l, int lvl); void rmnl(char *s, unsigned char c, int len); void truncate_spaces(char *s, int maxlen); char *get_version(void); void update_vpd_80(struct lu_phy_attr *lu, void *p); void update_vpd_83(struct lu_phy_attr *lu, void *p); void update_vpd_86(struct lu_phy_attr *lu, void *p); void update_vpd_b0(struct lu_phy_attr *lu, void *p); void update_vpd_b1(struct lu_phy_attr *lu, void *p); void update_vpd_b2(struct lu_phy_attr *lu, void *p); void update_vpd_c0(struct lu_phy_attr *lu, void *p); void update_vpd_c1(struct lu_phy_attr *lu, void *p); int get_fifo_count(void); int dec_fifo_count(void); int inc_fifo_count(void); void cleanup_msg(void); int add_density_support(struct list_head *l, struct density_info *di, int rw); int add_drive_media_list(struct lu_phy_attr *lu, int status, char *s); void find_media_home_directory(char *config_directory, long lib_id); unsigned int set_media_params(struct MAM *mamp, char *density); char *slot_type_str(int type); void init_smc_log_pages(struct lu_phy_attr *lu); void init_smc_mode_pages(struct lu_phy_attr *lu); void bubbleSort(int *array, int size); void sort_library_slot_type(struct lu_phy_attr *lu, struct smc_type_slot *type); void ymd(int *year, int *month, int *day, int *hh, int *min, int *sec); void opcode_6_params(struct scsi_cmd *cmd, int *num, int *sz); /* ======== Global variables ========*/ /* In vtllib.c */ extern struct MAM mam; extern struct priv_lu_ssc lu_ssc; extern struct lu_phy_attr lunit; extern struct encryption app_encryption_state; /* Stores the encryption info the application sent us */ extern int current_state; /* Last status sent to fifo */ extern int lbp_rscrc_be; /* Logical Block Protection: RS-CRC big-endian */ extern int OK_to_write; extern uint8_t sense[SENSE_BUF_SIZE]; extern uint8_t modeBlockDescriptor[8]; /* Used by Mode Sense - if set, return block descriptor */ extern char home_directory[HOME_DIR_PATH_SZ + 1]; extern uint8_t verbose; extern uint8_t debug; extern long my_id; /* In vtlcart.c */ extern struct blk_header *c_pos; /* current position, declared and initialised in vtlcart.c */ #endif /* _VTLLIB_H_ */ ================================================ FILE: kernel/.gitignore ================================================ TAGS *.cmd *.mod ================================================ FILE: kernel/Makefile ================================================ # # $Id: Makefile,v 1.2.2.3 2006-08-30 06:35:14 markh Exp $ # # CC=/usr/bin/gcc # EXTRA_CFLAGS += -I$(SRC)/../include EXTRA_CFLAGS += -DMHVTL_DEBUG ccflags-y += -I$(M)/../include/common -DMHVTL_DEBUG obj-m := mhvtl.o vtl-objs := mhvtl.o V ?= $(shell uname -r) ifeq ($(KDIR),) ifneq ($(wildcard /lib/modules/$(V)/build),) KDIR := /lib/modules/$(V)/build else ifneq ($(wildcard /usr/src/linux-headers-$(V)),) KDIR := /usr/src/linux-headers-$(V) endif endif endif export KDIR TOPDIR ?= $(CURDIR)/.. FIRMWAREDIR ?= $(shell awk '/^%.*_firmwarepath/ {print $$3}' $(TOPDIR)/mhvtl-utils.spec) PWD := $(shell pwd) all: mhvtl.ko ../mhvtl_kernel.tgz mhvtl.ko: config.h $(MAKE) -C $(KDIR) M=$(PWD) modules config.h: config.sh ./config.sh install: all [ -d $(DESTDIR)$(FIRMWAREDIR)/mhvtl ] || mkdir -p $(DESTDIR)$(FIRMWAREDIR)/mhvtl install -m 755 ../mhvtl_kernel.tgz $(DESTDIR)$(FIRMWAREDIR)/mhvtl/ install -o root -g root -m 644 mhvtl.ko /lib/modules/$(V)/kernel/drivers/scsi/ depmod -a uninstall: distclean rm -f /lib/modules/$(V)/kernel/drivers/scsi/mhvtl.ko $(DESTDIR)$(FIRMWAREDIR)/mhvtl/mhvtl_kernel.tgz depmod -a ../mhvtl_kernel.tgz: $(wildcard ./*.c) $(wildcard ./*.h) $(wildcard ../include/common/*.h) \ config.sh Makefile tar --sort=name --mtime=@1 --format=gnu --transform='s|.*/||' -czf $@ $^ install-bkup: cp mhvtl.ko ~/mhvtl-$(V)_`uname -m`.ko tags: ctags -R * # ========== Cleaning ========== .PHONY: clean clean: $(RM) mhvtl.ko $(RM) *.o $(RM) TAGS $(RM) config.h .PHONY: distclean distclean: clean $(RM) .*.o.d \ mhvtl.mod.c \ Modules.symvers \ Module.symvers \ .mhvtl.ko.cmd \ .mhvtl.o.cmd \ .mhvtl.mod.o.cmd \ .event.o.cmd \ .event.o.d \ .Module.symvers.cmd \ .mhvtl.mod.cmd \ .modules.order.cmd \ mhvtl.mod \ Module.markers \ modules.order \ mhvtl.ko.unsigned \ .mhvtl.ko.unsigned.cmd \ config.h $(RM) -r .tmp_versions ================================================ FILE: kernel/backport.h ================================================ /* * Include wrappers for older kernels as interfaces change */ #include "config.h" #ifndef SG_SEGMENT_SZ #define SG_SEGMENT_SZ 65536 #endif #ifndef slab_flags_t typedef unsigned __bitwise slab_flags_t; #endif /* * Copied kmem_cache_create_usercopy() from scst project */ #if !defined(HAVE_KMEM_CACHE_CREATE_USERCOPY) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) static inline struct kmem_cache *kmem_cache_create_usercopy(const char *name, unsigned int size, unsigned int align, slab_flags_t flags, unsigned int useroffset, unsigned int usersize, void (*ctor)(void *)) { return kmem_cache_create(name, size, align, flags, ctor, NULL); } #elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) static inline struct kmem_cache *kmem_cache_create_usercopy(const char *name, unsigned int size, unsigned int align, slab_flags_t flags, unsigned int useroffset, unsigned int usersize, void (*ctor)(void *)) { return kmem_cache_create(name, size, align, flags, ctor); } #endif #endif #if !defined(HAVE_FILE_INODE) /* * See also patch "new helper: file_inode(file)" (commit ID * 496ad9aa8ef448058e36ca7a787c61f2e63f0f54). */ static inline struct inode *file_inode(struct file *f) { return f->f_path.dentry->d_inode; } #endif #if !defined(HAVE_SYSFS_EMIT) /* https://patches.linaro.org/project/stable/patch/20210305120853.392925382@linuxfoundation.org/ */ /** * sysfs_emit - scnprintf equivalent, aware of PAGE_SIZE buffer. * @buf: start of PAGE_SIZE buffer. * @fmt: format * @...: optional arguments to @format * * * Returns number of characters written to @buf. */ static int sysfs_emit(char *buf, const char *fmt, ...) { va_list args; int len; if (WARN(!buf || offset_in_page(buf), "invalid sysfs_emit: buf:%p\n", buf)) return 0; va_start(args, fmt); len = vscnprintf(buf, PAGE_SIZE, fmt, args); va_end(args); return len; } #endif #if !defined(USE_TIMER_DELETE_NOT_DEL_TIMER) #define timer_delete_sync del_timer_sync #endif /* * 6.16 kernel change, from "from_timer()" to "timer_container_of()" in timer.h. */ #if !defined(FROM_TIMER_NOW_TIMER_CONTAINER_OF) #define timer_container_of from_timer #endif ================================================ FILE: kernel/config.sh ================================================ #!/usr/bin/env bash # vim: tabstop=4 shiftwidth=4 expandtab colorcolumn=80 foldmethod=marker : # uncomment the next line to enable script debugging # set -x # make sure we have the kernel directory defined if [ -z "${KDIR}" ] ; then echo "error: you must supply environment variable KDIR" 1>&2 echo " or you do not have the kernel-devel installed" 1>&2 exit 1 fi # # "syms" is an associative array, where the "key" # is a symbol that we try to find (using grep) in # the "value", i.e. we try to find the string # "sysfs_emit" in the include file sysfs.h. Based # on that, we create a cpp #define or #undef. # declare -A syms syms[kmem_cache_create_usercopy]='slab.h' syms[file_inode]='fs.h' syms[sysfs_emit]='sysfs.h' output='config.h' kparent="${KDIR%/*}" # # use the "fs.h" to determine where the kernel headers are located # if [ -e "${KDIR}/include/linux/fs.h" ] then hdrs="${KDIR}/include" elif [ -e "${kparent}/source/include/linux/fs.h" ] then hdrs="${kparent}/source/include" else echo "Cannot infer kernel headers location" 1>&2 exit 1 fi rm -f "${output}" cat <"${output}" /* Autogenerated by kernel/config.sh - do not edit */ #ifndef _MHVTL_KERNEL_CONFIG_H #define _MHVTL_KERNEL_CONFIG_H EOF # # start checking for compatability issues # # # first, check the symbols in our associative array # for sym in ${!syms[@]} do grep -q "${sym}" "${hdrs}/linux/${syms[$sym]}" if [ $? -eq 0 ] then printf '#define HAVE_%s\n' \ "$( echo "${sym}" | tr [:lower:] [:upper:] )" >> "${output}" else printf '#undef HAVE_%s\n' \ "$( echo "${sym}" | tr [:lower:] [:upper:] )" >> "${output}" fi done # # do we have a "genhd.h" present? # if [ -e "${hdrs}/linux/genhd.h" ]; then echo "#define HAVE_GENHD" else echo "#undef HAVE_GENHD" fi >> "${output}" # # see if "struct file_operations" has member "unlocked_ioctl" # (otherwise, just "ioctl") # syms[file_operations]='fs.h' if grep -q unlocked_ioctl "${hdrs}/linux/fs.h"; then echo "#ifndef HAVE_UNLOCKED_IOCTL" echo "#define HAVE_UNLOCKED_IOCTL" echo "#endif" else echo "#undef HAVE_UNLOCKED_IOCTL" fi >> "${output}" # # check for the scsi queue command taking one or two args # str=$( grep 'rc = func_name##_lck' ${hdrs}/scsi/scsi_host.h ) if [[ "$str" == *,* ]] ; then echo "#undef QUEUECOMMAND_LCK_ONE_ARG" else echo "#ifndef QUEUECOMMAND_LCK_ONE_ARG" echo "#define QUEUECOMMAND_LCK_ONE_ARG" echo "#endif" fi >> "${output}" # # check for DEFINE_SEMAPHORE() taking an argument or not # if grep -q 'DEFINE_SEMAPHORE(_name, _n)' "${hdrs}/linux/semaphore.h"; then echo "#ifndef DEFINE_SEMAPHORE_HAS_NUMERIC_ARG" echo "#define DEFINE_SEMAPHORE_HAS_NUMERIC_ARG" echo "#endif" else echo "#undef DEFINE_SEMAPHORE_HAS_NUMERIC_ARG" fi >> "${output}" # # check if scsi_host_template argument to scsi_host_alloc # is const # if grep -F -q 'extern struct Scsi_Host *scsi_host_alloc(const' \ "${hdrs}/scsi/scsi_host.h"; then # the first argument to scsi_host_alloc needs to be a "const" echo "#ifndef DEFINE_CONST_STRUCT_SCSI_HOST_TEMPLATE" echo "#define DEFINE_CONST_STRUCT_SCSI_HOST_TEMPLATE" echo "#endif" else echo "#undef DEFINE_CONST_STRUCT_SCSI_HOST_TEMPLATE" fi >> "${output}" # # We need to find the definition for "struct bus_type", so that we # can if the "match" member of this struct, which points to a function, # has a 2nd argument that is const or not. # # The pattern to find (if "const" is needed) pat='int (*match)(struct device *dev, const struct device_driver *drv);' # First, find the file bus_type_def_file=$(grep -Rl 'struct bus_type {' ${hdrs}) : {bus_type_def_file:="not-found"} # Now check for the 2nd argument needs a "const" if [ -r "$bus_type_def_file" ] && grep -F -q "$pat" "$bus_type_def_file"; then # the second argument needs a "const" definition echo "#ifndef DEFINE_CONST_STRUCT_DEVICE_DRIVER" echo "#define DEFINE_CONST_STRUCT_DEVICE_DRIVER" echo "#endif" else echo "#undef DEFINE_CONST_STRUCT_DEVICE_DRIVER" fi >> "${output}" # # check if slave_configure has been renamed to sdev_configure # pat='int (* sdev_configure)(struct scsi_device *, struct queue_limits *lim);' if grep -F -q "$pat" "${hdrs}/scsi/scsi_host.h"; then echo "#ifndef DEFINE_QUEUE_LIMITS_SCSI_DEV_CONFIGURE" echo "#define DEFINE_QUEUE_LIMITS_SCSI_DEV_CONFIGURE" echo "#endif" else echo "#undef DEFINE_QUEUE_LIMITS_SCSI_DEV_CONFIGURE" fi >> "${output}" # # check if del_timer_sync() has been renamed to timer_delete() # if grep -F -q 'int timer_delete_sync(' "${hdrs}/linux/timer.h"; then echo "#ifndef USE_TIMER_DELETE_NOT_DEL_TIMER" echo "#define USE_TIMER_DELETE_NOT_DEL_TIMER" echo "#endif" else echo "#undef USE_TIMER_DELETE_NOT_DEL_TIMER" fi >> "${output}" # # has "from_timer()" been renamed to "timer_container_of()"? # if grep -F -q 'timer_container_of(' "${hdrs}/linux/timer.h"; then echo "#ifndef FROM_TIMER_NOW_TIMER_CONTAINER_OF" echo "#define FROM_TIMER_NOW_TIMER_CONTAINER_OF" echo "#endif" else echo "#undef FROM_TIMER_NOW_TIMER_CONTAINER_OF" fi >> "${output}" printf '\n\n#endif /* _MHVTL_KERNEL_CONFIG_H */\n' >> "${output}" ================================================ FILE: kernel/fetch.c ================================================ /* * Copy data from SCSI command buffer to device buffer * (SCSI command buffer -> user space) * * Returns number of bytes fetched into 'arr'/FIFO or -1 if error. */ static int mhvtl_fetch_to_dev_buffer(struct scsi_cmnd *scp, char __user *arr, int max_arr_len) { int k, req_len, act_len, len, active; int retval; void *kaddr; void *kaddr_off; struct scatterlist *sg; if (0 == scp->request_bufflen) return 0; if (NULL == scp->request_buffer) return -1; if (NULL == arr) { pr_crit("%s, userspace pointer is NULL\n", __func__); WARN_ON(1); } if (!((scp->sc_data_direction == DMA_BIDIRECTIONAL) || (scp->sc_data_direction == DMA_TO_DEVICE))) return -1; if (0 == scp->use_sg) { req_len = scp->request_bufflen; act_len = (req_len < max_arr_len) ? req_len : max_arr_len; if (copy_to_user(arr, scp->request_buffer, act_len)) return -1; return act_len; } sg = (struct scatterlist *)scp->request_buffer; for (k = 0, req_len = 0, active = 0; k < scp->use_sg; ++k, ++sg) { kaddr = (unsigned char *)kmap(sg->page); if (NULL == kaddr) return -1; kaddr_off = (unsigned char *)kaddr + sg->offset; len = sg->length; if ((req_len + len) > max_arr_len) { len = max_arr_len - req_len; active = 1; } retval = copy_to_user(arr + req_len, kaddr_off, len); kunmap(sg->page); if (retval) { pr_err("mhvtl: %s[%d] failed to copy_to_user()\n", __func__, __LINE__); return -1; } if (active) return req_len + len; req_len += sg->length; } return req_len; } /* * fill_from_user_buffer : Retrieves data from user-space into SCSI * buffer(s) Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */ static int mhvtl_fill_from_user_buffer(struct scsi_cmnd *scp, char __user *arr, int arr_len) { int k, req_len, act_len, len, active; int retval; void *kaddr; void *kaddr_off; struct scatterlist *sg; if (0 == scp->request_bufflen) return 0; if (NULL == scp->request_buffer) return DID_ERROR << 16; if (!((scp->sc_data_direction == DMA_BIDIRECTIONAL) || (scp->sc_data_direction == DMA_FROM_DEVICE))) return DID_ERROR << 16; if (0 == scp->use_sg) { req_len = scp->request_bufflen; act_len = (req_len < arr_len) ? req_len : arr_len; if (copy_from_user(scp->request_buffer, arr, act_len)) pr_err("%s[%d]: failed to copy_from_user()\n", __func__, __LINE__); scp->resid = req_len - act_len; return 0; } active = 1; sg = (struct scatterlist *)scp->request_buffer; for (k = 0, req_len = 0, act_len = 0; k < scp->use_sg; ++k, ++sg) { if (active) { kaddr = (unsigned char *)kmap(sg->page); if (NULL == kaddr) return DID_ERROR << 16; kaddr_off = (unsigned char *)kaddr + sg->offset; len = sg->length; if ((req_len + len) > arr_len) { active = 0; len = arr_len - req_len; } retval = copy_from_user(kaddr_off, arr + req_len, len); kunmap(sg->page); if (retval) { pr_err("mhvtl: %s[%d] failed to copy_from_user()\n", __func__, __LINE__); return -1; } act_len += len; } req_len += sg->length; } scp->resid = req_len - act_len; return 0; } /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */ static int mhvtl_fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, int arr_len) { int k, req_len, act_len, len, active; void *kaddr; void *kaddr_off; struct scatterlist *sg; if (0 == scp->request_bufflen) return 0; if (NULL == scp->request_buffer) return DID_ERROR << 16; if (!((scp->sc_data_direction == DMA_BIDIRECTIONAL) || (scp->sc_data_direction == DMA_FROM_DEVICE))) return DID_ERROR << 16; if (0 == scp->use_sg) { req_len = scp->request_bufflen; act_len = (req_len < arr_len) ? req_len : arr_len; memcpy(scp->request_buffer, arr, act_len); scp->resid = req_len - act_len; return 0; } active = 1; sg = (struct scatterlist *)scp->request_buffer; for (k = 0, req_len = 0, act_len = 0; k < scp->use_sg; ++k, ++sg) { if (active) { kaddr = (unsigned char *) kmap_atomic(sg->page, KM_USER0); if (NULL == kaddr) return DID_ERROR << 16; kaddr_off = (unsigned char *)kaddr + sg->offset; len = sg->length; if ((req_len + len) > arr_len) { active = 0; len = arr_len - req_len; } memcpy(kaddr_off, arr + req_len, len); kunmap_atomic(kaddr, KM_USER0); act_len += len; } req_len += sg->length; } scp->resid = req_len - act_len; return 0; } ================================================ FILE: kernel/fetch24.c ================================================ /* * Copy data from SCSI command buffer to device buffer * (SCSI command buffer -> user space) * * Returns number of bytes fetched into 'arr'/FIFO or -1 if error. */ static int mhvtl_fetch_to_dev_buffer(struct scsi_cmnd *scp, char __user *arr, int max_arr_len) { int k, req_len, act_len, len, active; int retval; void *kaddr; void *kaddr_off; struct scatterlist *sg; if (0 == scp->request_bufflen) return 0; if (NULL == scp->request_buffer) return -1; if (NULL == arr) { pr_err("%s, userspace pointer is NULL\n", __func__); WARN_ON(1); } if (!((scp->sc_data_direction == DMA_BIDIRECTIONAL) || (scp->sc_data_direction == DMA_TO_DEVICE))) return -1; if (0 == scp->use_sg) { req_len = scp->request_bufflen; act_len = (req_len < max_arr_len) ? req_len : max_arr_len; if (copy_to_user(arr, scp->request_buffer, act_len)) return -1; return act_len; } active = 1; req_len = 0; act_len = 0; scsi_for_each_sg(scp, sg, scp->use_sg, k) { if (active) { kaddr = (unsigned char *)kmap(sg_page(sg)); if (NULL == kaddr) return DID_ERROR << 16; kaddr_off = (unsigned char *)kaddr + sg->offset; len = sg->length; if ((req_len + len) > max_arr_len) { active = 0; len = max_arr_len - req_len; } retval = copy_to_user(arr + act_len, kaddr_off, len); kunmap(sg_page(sg)); if (retval) { pr_err("mhvtl: %s[%d] failed to " "copy_to_user()\n", __func__, __LINE__); return -1; } act_len += len; } req_len += sg->length; } if (scp->resid) scp->resid -= act_len; else scp->resid = req_len - act_len; return 0; } /* * fill_from_user_buffer : Retrieves data from user-space into SCSI * buffer(s) Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */ static int mhvtl_fill_from_user_buffer(struct scsi_cmnd *scp, char __user *arr, int arr_len) { int k, req_len, act_len, len, active; int retval; void *kaddr; void *kaddr_off; struct scatterlist *sg; if (0 == scp->request_bufflen) return 0; if (NULL == scp->request_buffer) return DID_ERROR << 16; if (!((scp->sc_data_direction == DMA_BIDIRECTIONAL) || (scp->sc_data_direction == DMA_FROM_DEVICE))) return DID_ERROR << 16; if (0 == scp->use_sg) { req_len = scp->request_bufflen; act_len = (req_len < arr_len) ? req_len : arr_len; if (copy_from_user(scp->request_buffer, arr, act_len)) pr_err("%s[%d]: failed to copy_from_user()\n", __func__, __LINE__); scp->resid = req_len - act_len; return 0; } active = 1; req_len = 0; act_len = 0; scsi_for_each_sg(scp, sg, scp->use_sg, k) { if (active) { kaddr = (unsigned char *)kmap(sg_page(sg)); if (NULL == kaddr) return DID_ERROR << 16; kaddr_off = (unsigned char *)kaddr + sg->offset; len = sg->length; if ((req_len + len) > arr_len) { active = 0; len = arr_len - req_len; } retval = copy_from_user(kaddr_off, arr + req_len, len); kunmap(sg_page(sg)); if (retval) { pr_err("mhvtl: %s[%d] failed to copy_from_user()\n", __func__, __LINE__); return -1; } act_len += len; } req_len += sg->length; } if (scp->resid) scp->resid -= act_len; else scp->resid = req_len - act_len; return 0; } /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */ static int mhvtl_fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, int arr_len) { int k, req_len, act_len, len, active; void *kaddr; void *kaddr_off; struct scatterlist *sg; if (0 == scp->request_bufflen) return 0; if (NULL == scp->request_buffer) return DID_ERROR << 16; if (!((scp->sc_data_direction == DMA_BIDIRECTIONAL) || (scp->sc_data_direction == DMA_FROM_DEVICE))) return DID_ERROR << 16; if (0 == scp->use_sg) { req_len = scp->request_bufflen; act_len = (req_len < arr_len) ? req_len : arr_len; memcpy(scp->request_buffer, arr, act_len); scp->resid = req_len - act_len; return 0; } active = 1; req_len = act_len = 0; scsi_for_each_sg(scp, sg, scp->use_sg, k) { if (active) { kaddr = (unsigned char *) kmap_atomic(sg_page(sg), KM_USER0); if (NULL == kaddr) return DID_ERROR << 16; kaddr_off = (unsigned char *)kaddr + sg->offset; len = sg->length; if ((req_len + len) > arr_len) { active = 0; len = arr_len - req_len; } memcpy(kaddr_off, arr + req_len, len); kunmap_atomic(kaddr, KM_USER0); act_len += len; } req_len += sg->length; } if (scp->resid) scp->resid -= act_len; else scp->resid = req_len - act_len; return 0; } ================================================ FILE: kernel/fetch26.c ================================================ /* * Routines based on linux/lib/scatterlist.c */ /** * sg_copy_buffer - Copy data between a linear buffer and an SG list * @sgl: The SG list * @nents: Number of SG entries * @buf: Where to copy from * @buflen: The number of bytes to copy * @to_buffer: transfer direction (non zero == from an sg list to a * buffer, 0 == from a buffer to an sg list * * Returns the number of copied bytes. * **/ static size_t mhvtl_sg_copy_user(struct scatterlist *sgl, unsigned int nents, __user void *buf, size_t buflen, int to_buffer) { struct scatterlist *sg; size_t buf_off = 0; int i; int ret; for_each_sg(sgl, sg, nents, i) { struct page *page; int n = 0; unsigned int sg_off = sg->offset; unsigned int sg_copy = sg->length; if (sg_copy > buflen) sg_copy = buflen; buflen -= sg_copy; while (sg_copy > 0) { unsigned int page_copy; void *p; page_copy = PAGE_SIZE - sg_off; if (page_copy > sg_copy) page_copy = sg_copy; page = nth_page(sg_page(sg), n); p = kmap_atomic(page, KM_BIO_SRC_IRQ); if (to_buffer) ret = copy_to_user(buf + buf_off, p + sg_off, page_copy); else { ret = copy_from_user(p + sg_off, buf + buf_off, page_copy); flush_kernel_dcache_page(page); } kunmap_atomic(p, KM_BIO_SRC_IRQ); buf_off += page_copy; sg_off += page_copy; if (sg_off == PAGE_SIZE) { sg_off = 0; n++; } sg_copy -= page_copy; } if (!buflen) break; } return buf_off; } size_t mhvtl_copy_from_user(struct scatterlist *sgl, unsigned int nents, char __user *buf, size_t buflen) { return mhvtl_sg_copy_user(sgl, nents, buf, buflen, 0); } size_t mhvtl_copy_to_user(struct scatterlist *sgl, unsigned int nents, char __user *buf, size_t buflen) { return mhvtl_sg_copy_user(sgl, nents, buf, buflen, 1); } /* * Copy data from SCSI command buffer to device buffer * (SCSI command buffer -> user space) * * Returns number of bytes fetched into 'arr' or -1 if error. */ static int mhvtl_fetch_to_dev_buffer(struct scsi_cmnd *scp, char __user *arr, int len) { struct scsi_data_buffer *sdb = scsi_out(scp); if (!scsi_bufflen(scp)) return 0; if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_TO_DEVICE)) return -1; return mhvtl_copy_to_user(sdb->table.sgl, sdb->table.nents, arr, len); } /* * fill_from_user_buffer : Retrieves data from user-space into SCSI * buffer(s) Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */ static int mhvtl_fill_from_user_buffer(struct scsi_cmnd *scp, char __user *arr, int arr_len) { int act_len; struct scsi_data_buffer *sdb = scsi_in(scp); if (!sdb->length) return 0; if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE)) return DID_ERROR << 16; act_len = mhvtl_copy_from_user(sdb->table.sgl, sdb->table.nents, arr, arr_len); if (sdb->resid) sdb->resid -= act_len; else sdb->resid = scsi_bufflen(scp) - act_len; return 0; } /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */ static int mhvtl_fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, int arr_len) { int act_len; struct scsi_data_buffer *sdb = scsi_in(scp); if (!sdb->length) return 0; if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE)) return DID_ERROR << 16; act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents, arr, arr_len); if (sdb->resid) sdb->resid -= act_len; else sdb->resid = scsi_bufflen(scp) - act_len; return 0; } ================================================ FILE: kernel/fetch27.c ================================================ /** * mhvtl_sg_copy_user - Copy data between user-space linear buffer and an SG list * @sgl: The SG list * @nents: Number of SG entries * @buf: Where to copy from * @buflen: The number of bytes to copy * @to_buffer: Transfer direction (non zero == from an sg list to a buffer, * 0 == from a buffer to an sg list * * Returns number of copied bytes * * Taken in whole from scatterlist.c */ static size_t mhvtl_sg_copy_user(struct scatterlist *sgl, unsigned int nents, __user void *buf, size_t buflen, int to_buffer) { unsigned int offset = 0; struct sg_mapping_iter miter; /* Do not use SG_MITER_ATOMIC flag on the sg_miter_start() call */ unsigned int sg_flags = 0; unsigned int rem; void *kmem_user; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 30) if (to_buffer) sg_flags |= SG_MITER_FROM_SG; else sg_flags |= SG_MITER_TO_SG; #endif kmem_user = kmem_cache_alloc(sgp, 0); if (!kmem_user) return offset; sg_miter_start(&miter, sgl, nents, sg_flags); while (sg_miter_next(&miter) && offset < buflen) { unsigned int len; len = min(miter.length, buflen - offset); if (len > SG_SEGMENT_SZ) { pr_warn("scatter-gather segment size larger than SG_SEGMENT_SZ (%d > %d)", len, SG_SEGMENT_SZ); goto abort_early; } if (to_buffer) { memcpy(kmem_user, miter.addr, len); rem = copy_to_user(buf + offset, kmem_user, len); } else { rem = copy_from_user(kmem_user, buf + offset, len); memcpy(miter.addr, kmem_user, len); flush_kernel_dcache_page(miter.page); } if (rem) pr_debug("mhvtl: %s(): " "copy_%s_user() failed, rem %ld, buf 0x%llx, " "miter.addr 0x%llx, len %d\n", __func__, (to_buffer) ? "to" : "from", (long)rem, (unsigned long long)(buf + offset), (unsigned long long)miter.addr, len); offset += len; } abort_early: sg_miter_stop(&miter); kmem_cache_free(sgp, kmem_user); return offset; } static size_t mhvtl_copy_from_user(struct scatterlist *sgl, unsigned int nents, char __user *buf, size_t buflen) { return mhvtl_sg_copy_user(sgl, nents, buf, buflen, 0); } static size_t mhvtl_copy_to_user(struct scatterlist *sgl, unsigned int nents, char __user *buf, size_t buflen) { return mhvtl_sg_copy_user(sgl, nents, buf, buflen, 1); } /* * Copy data from SCSI command buffer to device buffer * (SCSI command buffer -> user space) * * Returns number of bytes fetched into 'arr' or -1 if error. */ static int mhvtl_fetch_to_dev_buffer(struct scsi_cmnd *scp, char __user *arr, int len) { struct scsi_data_buffer *sdb = scsi_out(scp); if (!scsi_bufflen(scp)) return 0; if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_TO_DEVICE)) return -1; return mhvtl_copy_to_user(sdb->table.sgl, sdb->table.nents, arr, len); } /* * fill_from_user_buffer : Retrieves data from user-space into SCSI * buffer(s) Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */ static int mhvtl_fill_from_user_buffer(struct scsi_cmnd *scp, char __user *arr, int arr_len) { int act_len; struct scsi_data_buffer *sdb = scsi_in(scp); if (!sdb->length) return 0; if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE)) return DID_ERROR << 16; act_len = mhvtl_copy_from_user(sdb->table.sgl, sdb->table.nents, arr, arr_len); if (sdb->resid) sdb->resid -= act_len; else sdb->resid = scsi_bufflen(scp) - act_len; return 0; } /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */ static int mhvtl_fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, int arr_len) { int act_len; struct scsi_data_buffer *sdb = scsi_in(scp); if (!sdb->length) return 0; if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE)) return DID_ERROR << 16; act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents, arr, arr_len); if (sdb->resid) sdb->resid -= act_len; else sdb->resid = scsi_bufflen(scp) - act_len; return 0; } ================================================ FILE: kernel/fetch50.c ================================================ /** * mhvtl_sg_copy_user - Copy data between user-space linear buffer and an SG list * @sgl: The SG list * @nents: Number of SG entries * @buf: Where to copy from * @buflen: The number of bytes to copy * @to_buffer: Transfer direction (non zero == from an sg list to a buffer, * 0 == from a buffer to an sg list * * Returns number of copied bytes * * Taken in whole from scatterlist.c */ static size_t mhvtl_sg_copy_user(struct scatterlist *sgl, unsigned int nents, __user void *buf, size_t buflen, int to_buffer) { unsigned int offset = 0; struct sg_mapping_iter miter; /* Do not use SG_MITER_ATOMIC flag on the sg_miter_start() call */ unsigned int sg_flags = 0; unsigned int rem; void *kmem_user; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 30) if (to_buffer) sg_flags |= SG_MITER_FROM_SG; else sg_flags |= SG_MITER_TO_SG; #endif kmem_user = kmem_cache_alloc(sgp, 0); if (!kmem_user) return offset; sg_miter_start(&miter, sgl, nents, sg_flags); while (sg_miter_next(&miter) && offset < buflen) { unsigned int len; len = min(miter.length, buflen - offset); if (unlikely(len > SG_SEGMENT_SZ)) { pr_warn("scatter-gather segment size larger than SG_SEGMENT_SZ (%d > %d)", len, SG_SEGMENT_SZ); goto abort_early; } /* Since user copy 'white list' - need to copy into 'kmem_buffer' first */ if (to_buffer) { memcpy(kmem_user, miter.addr, len); rem = copy_to_user(buf + offset, kmem_user, len); } else { rem = copy_from_user(kmem_user, buf + offset, len); memcpy(miter.addr, kmem_user, len); flush_dcache_page(miter.page); } if (rem) pr_warn("mhvtl: %s(): " "copy_%s_user() failed, rem %ld, buf 0x%pS, " "miter.addr 0x%pS, len %d\n", __func__, (to_buffer) ? "to" : "from", (long)rem, (buf + offset), miter.addr, len); offset += len; } abort_early: sg_miter_stop(&miter); kmem_cache_free(sgp, kmem_user); return offset; } static size_t mhvtl_copy_from_user(struct scatterlist *sgl, unsigned int nents, char __user *buf, size_t buflen) { return mhvtl_sg_copy_user(sgl, nents, buf, buflen, 0); } static size_t mhvtl_copy_to_user(struct scatterlist *sgl, unsigned int nents, char __user *buf, size_t buflen) { return mhvtl_sg_copy_user(sgl, nents, buf, buflen, 1); } /* * Copy data from SCSI command buffer to device buffer * (SCSI command buffer -> user space) * * Returns number of bytes fetched into 'arr' or -1 if error. */ static int mhvtl_fetch_to_dev_buffer(struct scsi_cmnd *scp, char __user *arr, int len) { if (!scsi_bufflen(scp)) return 0; if (scp->sc_data_direction != DMA_TO_DEVICE) return -1; return mhvtl_copy_to_user(scsi_sglist(scp), scsi_sg_count(scp), arr, len); } /* * fill_from_user_buffer : Retrieves data from user-space into SCSI * buffer(s) Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */ static int mhvtl_fill_from_user_buffer(struct scsi_cmnd *scp, char __user *arr, int arr_len) { int act_len; struct scsi_data_buffer *sdb = &scp->sdb; if (!sdb->length) return 0; if (scp->sc_data_direction != DMA_FROM_DEVICE) return DID_ERROR << 16; act_len = mhvtl_copy_from_user(sdb->table.sgl, sdb->table.nents, arr, arr_len); scsi_set_resid(scp, scsi_bufflen(scp) - act_len); return 0; } /* Build SCSI "data-in" buffer. Returns 0 if ok else (DID_ERROR << 16). */ static int mhvtl_fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, int arr_len) { int act_len; struct scsi_data_buffer *sdb = &scp->sdb; if (!sdb->length) return 0; if (scp->sc_data_direction != DMA_FROM_DEVICE) return DID_ERROR << 16; act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents, arr, arr_len); scsi_set_resid(scp, scsi_bufflen(scp) - act_len); return 0; } ================================================ FILE: kernel/mhvtl.c ================================================ /* * linux/kernel/vtl.c * vvvvvvvvvvvvvvvvvvvvvvv Original vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv * Copyright (C) 1992 Eric Youngdale * Simulate a host adapter with 2 disks attached. Do a lot of checking * to make sure that we are not getting blocks mixed up, and PANIC if * anything out of the ordinary is seen. * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * * For documentation see http://sg.danny.cz/sg/sdebug26.html * * D. Gilbert (dpg) work for Magneto-Optical device test [20010421] * dpg: work for devfs large number of disks [20010809] * forked for lk 2.5 series [20011216, 20020101] * use vmalloc() more inquiry+mode_sense [20020302] * Patrick Mansfield max_luns+scsi_level [20021031] * Mike Anderson sysfs work [20021118] * dpg: change style of boot options to "vtl.num_tgts=2" and * module options to "modprobe vtl num_tgts=2" [20021221] * * Mark Harvey 2005 - 2025 * * markh794@gmail.com * * Pinched wholesale from scsi_debug.[ch] * * Hacked to represent SCSI tape drives & Library. * * Registered char driver to handle data to user space daemon. * Idea is for user space daemons (vtltape & vtllibrary) to emulate * and process the SCSI SSC/SMC device command set. * * I've used it for testing NetBackup - but there is no reason any * other backup utility could not use it as well. * * Modification History: * 2010-04-18 hstadler - some source code revision in mhvtl_init, * mhvtl_exit, some return code checking * * */ #define pr_fmt(fmt) "%s: %s(): " fmt, KBUILD_MODNAME, __func__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef LINUX_VERSION_CODE #include #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) #include #endif #ifndef _SCSI_H #define _SCSI_H #include #include #include #include #include struct Scsi_Host; struct scsi_cmnd; struct scsi_device; struct scsi_target; struct scatterlist; #endif /* _SCSI_H */ #include "vtl_common.h" #include "backport.h" #if defined(HAVE_GENHD) #include #endif #include #include /* version of scsi_debug I started from #define VTL_VERSION "1.75" */ #ifndef MHVTL_VERSION #define MHVTL_VERSION "0.18.40" #endif static const char *mhvtl_version_date = "20260506-0"; static const char mhvtl_driver_name[] = "mhvtl"; /* Additional Sense Code (ASC) used */ #define INVALID_FIELD_IN_CDB 0x24 #define VTL_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */ #ifndef SCSI_MAX_SG_CHAIN_SEGMENTS #define SCSI_MAX_SG_CHAIN_SEGMENTS SG_ALL #endif #define TIMEOUT_FOR_USER_DAEMON 50000 /* Default values for driver parameters */ #define DEF_NUM_HOST 1 #define DEF_NUM_TGTS 0 #define DEF_MAX_LUNS 32 #define DEF_OPTS 1 /* Default to verbose logging */ /* bit mask values for mhvtl_opts */ #define VTL_OPT_NOISE 3 #ifndef MHVTL_DEBUG #define MHVTL_DBG_PRT_CDB(lvl, s...) #else #define MHVTL_DBG_PRT_CDB(lvl, sn, s, len) \ { \ if ((mhvtl_opts & VTL_OPT_NOISE) >= (lvl)) { \ pr_info("(%llu) %d bytes", (long long unsigned)sn, len); \ switch (len) { \ case 6: \ pr_cont(" %02x %02x %02x %02x %02x %02x", \ s[0], s[1], s[2], s[3], \ s[4], s[5]); \ break; \ case 8: \ pr_cont(" %02x %02x %02x %02x %02x %02x %02x %02x", \ s[0], s[1], s[2], s[3], \ s[4], s[5], s[6], s[7]); \ break; \ case 10: \ pr_cont(" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", \ s[0], s[1], s[2], s[3], \ s[4], s[5], s[6], s[7], \ s[8], s[9]); \ break; \ case 12: \ pr_cont(" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", \ s[0], s[1], s[2], s[3], \ s[4], s[5], s[6], s[7], \ s[8], s[9], s[10], s[11]); \ break; \ case 16: \ default: \ pr_cont(" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", \ s[0], s[1], s[2], s[3], \ s[4], s[5], s[6], s[7], \ s[8], s[9], s[10], s[11], \ s[12], s[13], s[14], s[15]); \ break; \ } \ } \ } #endif /* MHVTL_DEBUG */ /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1) * or "peripheral device" addressing (value 0) */ #define SAM2_LUN_ADDRESS_METHOD 0 /* Major number assigned to vtl driver => 0 means to ask for one */ static int mhvtl_major = 0; #define DEF_MAX_MINOR_NO 1024 /* Max number of minor nos. this driver will handle */ #define VTL_CANQUEUE 1 /* needs to be >= 1 */ #define VTL_MAX_CMD_LEN 16 static struct kmem_cache *dsp; static struct kmem_cache *sgp; static int mhvtl_add_host = DEF_NUM_HOST; static int mhvtl_max_luns = DEF_MAX_LUNS; static int mhvtl_num_tgts = DEF_NUM_TGTS; /* targets per host */ static int mhvtl_opts = DEF_OPTS; static int mhvtl_cmnd_count = 0; static unsigned long long serial_number; struct mhvtl_lu_info { struct list_head lu_sibling; unsigned char sense_buff[SENSE_BUF_SIZE]; /* weak nexus */ unsigned int channel; unsigned int target; unsigned int lun; unsigned int minor; struct mhvtl_hba_info *mhvtl_hba; struct scsi_device *sdev; spinlock_t sdev_lock; char reset; struct list_head cmd_list; /* list of outstanding cmds for this lu */ spinlock_t cmd_list_lock; }; static struct mhvtl_lu_info *devp[DEF_MAX_MINOR_NO]; struct mhvtl_hba_info { struct list_head hba_sibling; /* List of adapters */ struct list_head lu_list; /* List of lu */ struct Scsi_Host *shost; struct device dev; }; #define to_mhvtl_hba(d) \ container_of(d, struct mhvtl_hba_info, dev) static LIST_HEAD(mhvtl_hba_list); /* dll of adapters */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) static spinlock_t mhvtl_hba_list_lock = __SPIN_LOCK_UNLOCKED(mhvtl_hba_list_lock); #else static spinlock_t mhvtl_hba_list_lock = SPIN_LOCK_UNLOCKED; #endif typedef void (*done_funct_t)(struct scsi_cmnd *); /* mhvtl_queued_cmd-> state */ enum cmd_state { CMD_STATE_FREE = 0, CMD_STATE_QUEUED, CMD_STATE_IN_USE, }; struct mhvtl_queued_cmd { int state; struct timer_list cmnd_timer; done_funct_t done_funct; struct scsi_cmnd *a_cmnd; int scsi_result; struct mhvtl_header op_header; struct list_head queued_sibling; unsigned long long serial_number; }; static int num_aborts = 0; static int num_dev_resets = 0; static int num_bus_resets = 0; static int num_host_resets = 0; static int mhvtl_driver_probe(struct device *); static int mhvtl_driver_remove(struct device *); static struct bus_type mhvtl_pseudo_lld_bus; static struct device_driver mhvtl_driverfs_driver = { .name = mhvtl_driver_name, .bus = &mhvtl_pseudo_lld_bus, .probe = mhvtl_driver_probe, .remove = mhvtl_driver_remove, }; /* function declarations */ static int mhvtl_resp_report_luns(struct scsi_cmnd *SCpnt, struct mhvtl_lu_info *lu); static int mhvtl_fill_from_user_buffer(struct scsi_cmnd *scp, char __user *arr, int arr_len); static int mhvtl_fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, int arr_len); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) static void mhvtl_timer_intr_handler(struct timer_list *indx); #else static void mhvtl_timer_intr_handler(unsigned long indx); #endif static struct mhvtl_lu_info *devInfoReg(struct scsi_device *sdp); static void mk_sense_buffer(struct mhvtl_lu_info *lu, int key, int asc, int asq); static void mhvtl_stop_all_queued(void); static int do_create_driverfs_files(void); static void do_remove_driverfs_files(void); static int mhvtl_add_adapter(void); static void mhvtl_remove_adapter(void); static int mhvtl_sdev_alloc(struct scsi_device *); #ifdef DEFINE_QUEUE_LIMITS_SCSI_DEV_CONFIGURE static int mhvtl_sdev_configure(struct scsi_device *, struct queue_limits *lim); #else static int mhvtl_sdev_configure(struct scsi_device *); #endif static void mhvtl_sdev_destroy(struct scsi_device *); #if LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 9) #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 19, 0) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) static int mhvtl_change_queue_depth(struct scsi_device *sdev, int qdepth); #else static int mhvtl_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason); #endif #endif #ifdef QUEUECOMMAND_LCK_ONE_ARG static int mhvtl_queuecommand_lck(struct scsi_cmnd *); #else static int mhvtl_queuecommand_lck(struct scsi_cmnd *, done_funct_t done); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) static int mhvtl_b_ioctl(struct scsi_device *, unsigned int, void __user *); #else static int mhvtl_b_ioctl(struct scsi_device *, int, void __user *); #endif static long mhvtl_c_ioctl(struct file *, unsigned int, unsigned long); static int mhvtl_c_ioctl_bkl(struct inode *, struct file *, unsigned int, unsigned long); static int mhvtl_abort(struct scsi_cmnd *); static int mhvtl_bus_reset(struct scsi_cmnd *); static int mhvtl_device_reset(struct scsi_cmnd *); static int mhvtl_host_reset(struct scsi_cmnd *); static const char *mhvtl_info(struct Scsi_Host *); static int mhvtl_open(struct inode *, struct file *); static int mhvtl_release(struct inode *, struct file *); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) static DEF_SCSI_QCMD(mhvtl_queuecommand) #endif static struct device mhvtl_pseudo_primary; #ifdef DEFINE_CONST_STRUCT_SCSI_HOST_TEMPLATE static const struct scsi_host_template mhvtl_driver_template = { #else static struct scsi_host_template mhvtl_driver_template = { #endif .name = "VTL", .info = mhvtl_info, #ifdef DEFINE_QUEUE_LIMITS_SCSI_DEV_CONFIGURE .sdev_init = mhvtl_sdev_alloc, .sdev_configure = mhvtl_sdev_configure, .sdev_destroy = mhvtl_sdev_destroy, #else .slave_alloc = mhvtl_sdev_alloc, .slave_configure = mhvtl_sdev_configure, .slave_destroy = mhvtl_sdev_destroy, #endif .ioctl = mhvtl_b_ioctl, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) .queuecommand = mhvtl_queuecommand, #else .queuecommand = mhvtl_queuecommand_lck, #endif #if LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 9) .change_queue_depth = mhvtl_change_queue_depth, #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) .ordered_tag = 1, #endif #endif .eh_abort_handler = mhvtl_abort, .eh_bus_reset_handler = mhvtl_bus_reset, .eh_device_reset_handler = mhvtl_device_reset, .eh_host_reset_handler = mhvtl_host_reset, .can_queue = VTL_CANQUEUE, .this_id = -1, .proc_name = mhvtl_driver_name, .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS, .cmd_per_lun = 1, .max_sectors = 4096, #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) .use_clustering = ENABLE_CLUSTERING, #else .dma_boundary = PAGE_SIZE - 1, #endif .module = THIS_MODULE, }; static const struct file_operations mhvtl_fops = { .owner = THIS_MODULE, #if defined(HAVE_UNLOCKED_IOCTL) .unlocked_ioctl = mhvtl_c_ioctl, #else .ioctl = mhvtl_c_ioctl_bkl, #endif .open = mhvtl_open, .release = mhvtl_release, }; #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 17, 0) #include "fetch50.c" #elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 26) #include "fetch27.c" #elif LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 26) #include "fetch26.c" #elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 23) #include "fetch24.c" #else #include "fetch.c" #endif /********************************************************************** * misc functions to handle queuing SCSI commands **********************************************************************/ /* * mhvtl_schedule_resp() - handle SCSI commands that are processed from the * queuecommand() interface. i.e. No callback to done() * outside the queuecommand() function. * * Any SCSI command handled directly by the kernel driver * will use this. */ static int mhvtl_schedule_resp(struct scsi_cmnd *SCpnt, struct mhvtl_lu_info *lu, done_funct_t done, int scsi_result) { if ((VTL_OPT_NOISE & mhvtl_opts) && SCpnt) { if (scsi_result) { struct scsi_device *sdp = SCpnt->device; pr_info(" <%u %u %u %llu> non-zero result=0x%x\n", sdp->host->host_no, sdp->channel, sdp->id, (unsigned long long)sdp->lun, scsi_result); } } if (SCpnt && lu) { /* simulate autosense by this driver */ if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff)) memcpy(SCpnt->sense_buffer, lu->sense_buff, (SCSI_SENSE_BUFFERSIZE > SENSE_BUF_SIZE) ? SENSE_BUF_SIZE : SCSI_SENSE_BUFFERSIZE); } if (SCpnt) SCpnt->result = scsi_result; if (done) done(SCpnt); return 0; } /********************************************************************** * SCSI data handling routines **********************************************************************/ static int mhvtl_resp_write_to_user(struct scsi_cmnd *SCpnt, void __user *up, int count) { int fetched; fetched = mhvtl_fetch_to_dev_buffer(SCpnt, up, count); if (fetched < count) { pr_err(" cdb indicated=%d, IO sent=%d bytes\n", count, fetched); return -EIO; } return 0; } static void mhvtl_debug_queued_list(struct mhvtl_lu_info *lu) { unsigned long iflags = 0; struct mhvtl_queued_cmd *sqcp, *n; int k = 0; spin_lock_irqsave(&lu->cmd_list_lock, iflags); list_for_each_entry_safe(sqcp, n, &lu->cmd_list, queued_sibling) { if (sqcp->state) { if (sqcp->a_cmnd) { pr_info("%d entry in use " "SCpnt: %p, SCSI result: %d, done: %p, " "Serial No: %lld\n", k, sqcp->a_cmnd, sqcp->scsi_result, sqcp->done_funct, sqcp->serial_number); } else { pr_info("%d entry in use " "SCpnt: %p, SCSI result: %d, done: %p\n", k, sqcp->a_cmnd, sqcp->scsi_result, sqcp->done_funct); } } else pr_info("entry free %d\n", k); k++; } spin_unlock_irqrestore(&lu->cmd_list_lock, iflags); pr_info("found %d entr%s\n", k, (k == 1) ? "y" : "ies"); } static struct mhvtl_hba_info *mhvtl_get_hba_entry(void) { struct mhvtl_hba_info *mhvtl_hba; spin_lock(&mhvtl_hba_list_lock); if (list_empty(&mhvtl_hba_list)) mhvtl_hba = NULL; else mhvtl_hba = list_entry(mhvtl_hba_list.prev, struct mhvtl_hba_info, hba_sibling); spin_unlock(&mhvtl_hba_list_lock); return mhvtl_hba; } static void mhvtl_dump_queued_list(void) { struct mhvtl_lu_info *lu, *__lu; struct mhvtl_hba_info *mhvtl_hba; mhvtl_hba = mhvtl_get_hba_entry(); if (!mhvtl_hba) return; /* Now that the work list is split per lu, we have to check each * lu to see if we can find the serial number in question */ list_for_each_entry_safe(lu, __lu, &mhvtl_hba->lu_list, lu_sibling) { pr_debug("Channel %d, ID %d, LUN %d\n", lu->channel, lu->target, lu->lun); mhvtl_debug_queued_list(lu); } } /********************************************************* * Generic interface to queue SCSI cmd to userspace daemon *********************************************************/ /* * mhvtl_q_cmd returns success if we successfully added the SCSI * cmd to the queued_list * * - Set state to indicate that the SCSI cmnd is ready for processing. */ static int mhvtl_q_cmd(struct scsi_cmnd *scp, done_funct_t done, struct mhvtl_lu_info *lu) { unsigned long iflags = 0; struct mhvtl_header *vheadp; struct mhvtl_queued_cmd *sqcp; sqcp = kmalloc(sizeof(*sqcp), GFP_ATOMIC); if (!sqcp) { pr_err("kmalloc failed %ld bytes\n", sizeof(*sqcp)); return 1; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) timer_setup(&sqcp->cmnd_timer, mhvtl_timer_intr_handler, 0); #else init_timer(&sqcp->cmnd_timer); sqcp->cmnd_timer.function = mhvtl_timer_intr_handler; #endif sqcp->a_cmnd = scp; sqcp->scsi_result = 0; sqcp->done_funct = done; sqcp->cmnd_timer.expires = jiffies + TIMEOUT_FOR_USER_DAEMON; add_timer(&sqcp->cmnd_timer); vheadp = &sqcp->op_header; vheadp->serialNo = serial_number; sqcp->serial_number = serial_number; /* Make sure serial_number can't wrap to '0' */ if (unlikely(serial_number < 2)) serial_number = 2; serial_number++; memcpy(vheadp->cdb, scp->cmnd, scp->cmd_len); /* Set flag. * Next ioctl() poll by user-daemon will check this state. */ sqcp->state = CMD_STATE_QUEUED; spin_lock_irqsave(&lu->cmd_list_lock, iflags); list_add_tail(&sqcp->queued_sibling, &lu->cmd_list); spin_unlock_irqrestore(&lu->cmd_list_lock, iflags); if ((mhvtl_opts & VTL_OPT_NOISE) >= 2) mhvtl_dump_queued_list(); return 0; } /********************************************************************** * Main interface from SCSI mid level **********************************************************************/ static int _mhvtl_queuecommand_lck(struct scsi_cmnd *SCpnt, done_funct_t done) { unsigned char *cmd = (unsigned char *)SCpnt->cmnd; int errsts = 0; struct mhvtl_lu_info *lu = NULL; if (done == NULL) return 0; /* assume mid level reprocessing command */ if (cmd) { MHVTL_DBG_PRT_CDB(1, serial_number, cmd, SCpnt->cmd_len); } if (SCpnt->device->id == mhvtl_driver_template.this_id) { pr_err("initiator's id used as target!\n"); return mhvtl_schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16); } if (SCpnt->device->lun >= mhvtl_max_luns) { pr_err("Max luns exceeded\n"); return mhvtl_schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16); } lu = devInfoReg(SCpnt->device); if (NULL == lu) { pr_err("Could not find lu\n"); return mhvtl_schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16); } switch (*cmd) { case REPORT_LUNS: /* mandatory, ignore unit attention */ errsts = mhvtl_resp_report_luns(SCpnt, lu); break; /* All commands down the list are handled by a user-space daemon */ default: /* Pass on to user space daemon to process */ errsts = mhvtl_q_cmd(SCpnt, done, lu); if (!errsts) return 0; break; } return mhvtl_schedule_resp(SCpnt, lu, done, errsts); } #ifdef QUEUECOMMAND_LCK_ONE_ARG static int mhvtl_queuecommand_lck(struct scsi_cmnd *SCpnt) { void (*done)(struct scsi_cmnd *) = scsi_done; return _mhvtl_queuecommand_lck(SCpnt, done); } #else static int mhvtl_queuecommand_lck(struct scsi_cmnd *SCpnt, done_funct_t done) { return _mhvtl_queuecommand_lck(SCpnt, done); } #endif /* FIXME: I don't know what version this inline routine was introduced */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 9) /* RedHat 4 appears to define 'scsi_get_tag_type' but doesn't understand * change_queue_depth * Disabling for kernel 2.6.9 (RedHat AS 4) */ #define MSG_SIMPLE_TAG 0x20 #define MSG_ORDERED_TAG 0x22 /** * scsi_get_tag_type - get the type of tag the device supports * @sdev: the scsi device * * Notes: * If the drive only supports simple tags, returns MSG_SIMPLE_TAG * if it supports all tag types, returns MSG_ORDERED_TAG. */ static inline int scsi_get_tag_type(struct scsi_device *sdev) { if (!sdev->tagged_supported) return 0; if (sdev->ordered_tags) return MSG_ORDERED_TAG; if (sdev->simple_tags) return MSG_SIMPLE_TAG; return 0; } #endif /* RedHat 4 appears to define 'scsi_get_tag_type' but doesn't understand * change_queue_depth * Disabling for kernel 2.6.9 (RedHat AS 4) */ #if LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 9) #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 19, 0) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) static int mhvtl_change_queue_depth(struct scsi_device *sdev, int qdepth) #else static int mhvtl_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) #endif { pr_info("queue depth now %d\n", qdepth); if (qdepth < 1) qdepth = 1; else if (qdepth > sdev->host->cmd_per_lun) qdepth = sdev->host->cmd_per_lun; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); #else scsi_change_queue_depth(sdev, qdepth); #endif return sdev->queue_depth; } #endif static struct mhvtl_queued_cmd *lookup_sqcp(struct mhvtl_lu_info *lu, unsigned long serialNo) { unsigned long iflags; struct mhvtl_queued_cmd *sqcp; spin_lock_irqsave(&lu->cmd_list_lock, iflags); list_for_each_entry(sqcp, &lu->cmd_list, queued_sibling) { if (sqcp->state && (sqcp->serial_number == serialNo)) { spin_unlock_irqrestore(&lu->cmd_list_lock, iflags); return sqcp; } } spin_unlock_irqrestore(&lu->cmd_list_lock, iflags); return NULL; } /* * Block device ioctl */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) static int mhvtl_b_ioctl(struct scsi_device *sdp, unsigned int cmd, void __user *arg) #else static int mhvtl_b_ioctl(struct scsi_device *sdp, int cmd, void __user *arg) #endif { pr_debug("cmd=0x%x\n", cmd); return -ENOTTY; } #define MHVTL_RLUN_ARR_SZ 128 static int mhvtl_resp_report_luns(struct scsi_cmnd *scp, struct mhvtl_lu_info *lu) { unsigned int alloc_len; int lun_cnt, i, upper; unsigned char *cmd = (unsigned char *)scp->cmnd; int select_report = (int)cmd[2]; struct scsi_lun *one_lun; unsigned char arr[MHVTL_RLUN_ARR_SZ]; alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); if ((alloc_len < 16) || (select_report > 2)) { mk_sense_buffer(lu, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return SAM_STAT_CHECK_CONDITION; } /* can produce response with up to 16k luns (lun 0 to lun 16383) */ memset(arr, 0, MHVTL_RLUN_ARR_SZ); lun_cnt = mhvtl_max_luns; arr[2] = ((sizeof(struct scsi_lun) * lun_cnt) >> 8) & 0xff; arr[3] = (sizeof(struct scsi_lun) * lun_cnt) & 0xff; lun_cnt = min((int)((MHVTL_RLUN_ARR_SZ - 8) / sizeof(struct scsi_lun)), lun_cnt); one_lun = (struct scsi_lun *)&arr[8]; for (i = 0; i < lun_cnt; i++) { upper = (i >> 8) & 0x3f; if (upper) one_lun[i].scsi_lun[0] = (upper | (SAM2_LUN_ADDRESS_METHOD << 6)); one_lun[i].scsi_lun[1] = i & 0xff; } return mhvtl_fill_from_dev_buffer(scp, arr, min((int)alloc_len, MHVTL_RLUN_ARR_SZ)); } static void __mhvtl_remove_sqcp(struct mhvtl_queued_cmd *sqcp) { list_del(&sqcp->queued_sibling); kfree(sqcp); } static void mhvtl_remove_sqcp(struct mhvtl_lu_info *lu, struct mhvtl_queued_cmd *sqcp) { unsigned long iflags; spin_lock_irqsave(&lu->cmd_list_lock, iflags); __mhvtl_remove_sqcp(sqcp); spin_unlock_irqrestore(&lu->cmd_list_lock, iflags); } /* When timer goes off this function is called. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) static void mhvtl_timer_intr_handler(struct timer_list *t) { struct mhvtl_queued_cmd *sqcp = timer_container_of(sqcp, t, cmnd_timer); unsigned long long indx = sqcp->serial_number; #else static void mhvtl_timer_intr_handler(unsigned long indx) { struct mhvtl_queued_cmd *sqcp = NULL; #endif struct mhvtl_lu_info *lu; struct mhvtl_hba_info *mhvtl_hba; mhvtl_hba = mhvtl_get_hba_entry(); if (!mhvtl_hba) return; /* Now that the work list is split per lu, we have to check each * lu to see if we can find the serial number in question */ list_for_each_entry(lu, &mhvtl_hba->lu_list, lu_sibling) { sqcp = lookup_sqcp(lu, indx); if (sqcp) break; } if (!sqcp) { pr_err("Unexpected interrupt, indx %ld\n", (unsigned long)indx); return; } sqcp->state = CMD_STATE_FREE; if (sqcp->done_funct) { sqcp->a_cmnd->result = sqcp->scsi_result; sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */ } sqcp->done_funct = NULL; mhvtl_remove_sqcp(lu, sqcp); } static int mhvtl_sdev_alloc(struct scsi_device *sdp) { struct mhvtl_hba_info *mhvtl_hba; struct mhvtl_lu_info *lu = (struct mhvtl_lu_info *)sdp->hostdata; pr_debug("<%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, (unsigned long long)sdp->lun); if (lu) return 0; mhvtl_hba = *(struct mhvtl_hba_info **)sdp->host->hostdata; if (!mhvtl_hba) { pr_err("Host info NULL\n"); return -1; } list_for_each_entry(lu, &mhvtl_hba->lu_list, lu_sibling) { if ((lu->channel == sdp->channel) && (lu->target == sdp->id) && (lu->lun == sdp->lun)) { pr_debug("line %d found matching lu\n", __LINE__); return 0; } } return -1; } #ifdef DEFINE_QUEUE_LIMITS_SCSI_DEV_CONFIGURE static int mhvtl_sdev_configure(struct scsi_device *sdp, struct queue_limits *lim) #else static int mhvtl_sdev_configure(struct scsi_device *sdp) #endif { struct mhvtl_lu_info *lu; pr_debug("<%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, (unsigned long long)sdp->lun); if (sdp->host->max_cmd_len != VTL_MAX_CMD_LEN) sdp->host->max_cmd_len = VTL_MAX_CMD_LEN; lu = devInfoReg(sdp); sdp->hostdata = lu; if (sdp->host->cmd_per_lun) #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) scsi_adjust_queue_depth(sdp, VTL_TAGGED_QUEUING, sdp->host->cmd_per_lun); #else scsi_change_queue_depth(sdp, sdp->host->cmd_per_lun); #endif return 0; } static void mhvtl_sdev_destroy(struct scsi_device *sdp) { struct mhvtl_lu_info *lu = (struct mhvtl_lu_info *)sdp->hostdata; pr_notice("<%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, (unsigned long long)sdp->lun); if (lu) { pr_debug("Removing lu structure, minor %d\n", lu->minor); /* make this slot avaliable for re-use */ devp[lu->minor] = NULL; kfree(sdp->hostdata); sdp->hostdata = NULL; } } static struct mhvtl_lu_info *devInfoReg(struct scsi_device *sdp) { struct mhvtl_hba_info *mhvtl_hba; struct mhvtl_lu_info *lu = (struct mhvtl_lu_info *)sdp->hostdata; if (lu) return lu; mhvtl_hba = *(struct mhvtl_hba_info **)sdp->host->hostdata; if (!mhvtl_hba) { pr_err("Host info NULL\n"); return NULL; } list_for_each_entry(lu, &mhvtl_hba->lu_list, lu_sibling) { if ((lu->channel == sdp->channel) && (lu->target == sdp->id) && (lu->lun == sdp->lun)) return lu; } return NULL; } static void mk_sense_buffer(struct mhvtl_lu_info *lu, int key, int asc, int asq) { unsigned char *sbuff; sbuff = lu->sense_buff; memset(sbuff, 0, SENSE_BUF_SIZE); sbuff[0] = 0x70; /* fixed, current */ sbuff[2] = key; sbuff[7] = 0xa; /* implies 18 byte sense buffer */ sbuff[12] = asc; sbuff[13] = asq; pr_notice(" [key,asc,ascq]: [0x%x,0x%x,0x%x]\n", key, asc, asq); } static int mhvtl_device_reset(struct scsi_cmnd *SCpnt) { struct mhvtl_lu_info *lu; pr_notice("Device reset called\n"); ++num_dev_resets; if (SCpnt) { lu = devInfoReg(SCpnt->device); if (lu) lu->reset = 1; } return SUCCESS; } static int mhvtl_bus_reset(struct scsi_cmnd *SCpnt) { struct mhvtl_hba_info *mhvtl_hba; struct mhvtl_lu_info *lu; struct scsi_device *sdp; struct Scsi_Host *hp; pr_notice("Bus reset called\n"); ++num_bus_resets; if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) { mhvtl_hba = *(struct mhvtl_hba_info **)hp->hostdata; if (mhvtl_hba) { list_for_each_entry(lu, &mhvtl_hba->lu_list, lu_sibling) lu->reset = 1; } } return SUCCESS; } static int mhvtl_host_reset(struct scsi_cmnd *SCpnt) { struct mhvtl_hba_info *mhvtl_hba; struct mhvtl_lu_info *lu; pr_notice("Host reset called\n"); ++num_host_resets; spin_lock(&mhvtl_hba_list_lock); list_for_each_entry(mhvtl_hba, &mhvtl_hba_list, hba_sibling) { list_for_each_entry(lu, &mhvtl_hba->lu_list, lu_sibling) lu->reset = 1; } spin_unlock(&mhvtl_hba_list_lock); mhvtl_stop_all_queued(); return SUCCESS; } /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */ static int mhvtl_stop_queued_cmnd(struct scsi_cmnd *SCpnt) { int found = 0; unsigned long iflags; struct mhvtl_queued_cmd *sqcp, *n; struct mhvtl_lu_info *lu; lu = devInfoReg(SCpnt->device); spin_lock_irqsave(&lu->cmd_list_lock, iflags); list_for_each_entry_safe(sqcp, n, &lu->cmd_list, queued_sibling) { if (sqcp->state && (SCpnt == sqcp->a_cmnd)) { timer_delete_sync(&sqcp->cmnd_timer); sqcp->state = CMD_STATE_FREE; sqcp->a_cmnd = NULL; found = 1; __mhvtl_remove_sqcp(sqcp); break; } } spin_unlock_irqrestore(&lu->cmd_list_lock, iflags); return found; } /* Deletes (stops) timers of all queued commands */ static void mhvtl_stop_all_queued(void) { unsigned long iflags; struct mhvtl_queued_cmd *sqcp, *n; struct mhvtl_hba_info *mhvtl_hba; struct mhvtl_lu_info *lu; mhvtl_hba = mhvtl_get_hba_entry(); if (!mhvtl_hba) return; list_for_each_entry(lu, &mhvtl_hba->lu_list, lu_sibling) { spin_lock_irqsave(&lu->cmd_list_lock, iflags); list_for_each_entry_safe(sqcp, n, &lu->cmd_list, queued_sibling) { if (sqcp->state && sqcp->a_cmnd) { timer_delete_sync(&sqcp->cmnd_timer); sqcp->state = CMD_STATE_FREE; sqcp->a_cmnd = NULL; __mhvtl_remove_sqcp(sqcp); } } spin_unlock_irqrestore(&lu->cmd_list_lock, iflags); } } static int mhvtl_abort(struct scsi_cmnd *SCpnt) { pr_notice("Abort called\n"); ++num_aborts; mhvtl_stop_queued_cmnd(SCpnt); return SUCCESS; } /* SLES 9 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 6) struct scsi_device *__scsi_add_device(struct Scsi_Host *hpnt, uint channel, uint id, uint lun, char *p) { return scsi_add_device(hpnt, channel, id, lun); } #endif /* * According to scsi_mid_low_api.txt * * A call from LLD scsi_add_device() will result in SCSI mid layer * -> sdev_alloc() * -> sdev_configure() */ static int mhvtl_add_device(unsigned int minor, struct mhvtl_ctl *ctl) { struct Scsi_Host *hpnt; struct mhvtl_hba_info *mhvtl_hba; struct mhvtl_lu_info *lu; struct scsi_device *tmp_sdev; int error = 0; if (devp[minor]) { pr_notice("device struct already in place\n"); return error; } mhvtl_hba = mhvtl_get_hba_entry(); if (!mhvtl_hba) { pr_err("mhvtl_ost info struct is NULL\n"); return -ENOTTY; } pr_debug("mhvtl_hba_info struct is %p\n", mhvtl_hba); hpnt = mhvtl_hba->shost; if (!hpnt) { pr_notice("scsi host structure is NULL\n"); return -ENOTTY; } pr_debug("scsi_host struct is %p\n", hpnt); lu = kmalloc(sizeof(*lu), GFP_KERNEL); if (!lu) { pr_err("line %d - out of memory attempting to kmalloc %ld bytes\n", __LINE__, sizeof(*lu)); return -ENOMEM; } memset(lu, 0, sizeof(*lu)); lu->minor = minor; lu->channel = ctl->channel; lu->target = ctl->id; lu->lun = ctl->lun; lu->mhvtl_hba = mhvtl_hba; lu->reset = 0; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) spin_lock_init(&lu->cmd_list_lock); spin_lock_init(&lu->sdev_lock); #else lu->cmd_list_lock = SPIN_LOCK_UNLOCKED; lu->sdev_lock = SPIN_LOCK_UNLOCKED; #endif /* List of queued SCSI op codes associated with this device */ INIT_LIST_HEAD(&lu->cmd_list); lu->sense_buff[0] = 0x70; lu->sense_buff[7] = 0xa; devp[minor] = lu; spin_lock(&mhvtl_hba_list_lock); list_add_tail(&lu->lu_sibling, &mhvtl_hba->lu_list); spin_unlock(&mhvtl_hba_list_lock); pr_debug("Added lu: %p to devp[%d]\n", lu, minor); tmp_sdev = __scsi_add_device(hpnt, ctl->channel, ctl->id, ctl->lun, NULL); if (IS_ERR(tmp_sdev)) { tmp_sdev = NULL; error = -ENODEV; } spin_lock(&lu->sdev_lock); lu->sdev = tmp_sdev; spin_unlock(&lu->sdev_lock); return error; } /* Set 'perm' (4th argument) to 0 to disable module_param's definition * of sysfs parameters (which module_param doesn't yet support). * Sysfs parameters defined explicitly below. */ module_param_named(opts, mhvtl_opts, int, 0); /* perm=0644 */ MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert + Mark Harvey"); MODULE_DESCRIPTION("SCSI vtl adapter driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(MHVTL_VERSION); MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->..."); static char mhvtl_parm_info[256]; static const char *mhvtl_info(struct Scsi_Host *shp) { sprintf(mhvtl_parm_info, "%s: version %s [%s], " "opts=0x%x", mhvtl_driver_name, MHVTL_VERSION, mhvtl_version_date, mhvtl_opts); return mhvtl_parm_info; } static ssize_t opts_show(struct device_driver *ddp, char *buf) { return sysfs_emit(buf, "0x%x\n", mhvtl_opts); } static ssize_t opts_store(struct device_driver *ddp, const char *buf, size_t count) { int opts; char work[20]; if (1 == sscanf(buf, "%10s", work)) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) if (0 == strncasecmp(work, "0x", 2)) { #else if (0 == strnicmp(work, "0x", 2)) { #endif if (1 == sscanf(&work[2], "%x", &opts)) goto opts_done; } else { if (1 == sscanf(work, "%d", &opts)) goto opts_done; } } return -EINVAL; opts_done: mhvtl_opts = opts; mhvtl_cmnd_count = 0; return count; } static ssize_t major_show(struct device_driver *ddp, char *buf) { return sysfs_emit(buf, "%d\n", mhvtl_major); } static ssize_t add_lu_store(struct device_driver *ddp, const char *buf, size_t count) { int retval; unsigned int minor; struct mhvtl_ctl ctl; char str[512]; if (strncmp(buf, "add", 3)) { pr_err("Invalid command: %s\n", buf); return count; } retval = sscanf(buf, "%s %u %d %d %d", str, &minor, &ctl.channel, &ctl.id, &ctl.lun); pr_debug("Calling 'mhvtl_add_device(minor: %u," " Channel: %d, ID: %d, LUN: %d)\n", minor, ctl.channel, ctl.id, ctl.lun); retval = mhvtl_add_device(minor, &ctl); return count; } #ifdef DRIVER_ATTR static DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, opts_show, opts_store); static DRIVER_ATTR(major, S_IRUGO, major_show, NULL); static DRIVER_ATTR(add_lu, S_IWUSR | S_IWGRP, NULL, add_lu_store); #else static DRIVER_ATTR_RW(opts); static DRIVER_ATTR_RO(major); static DRIVER_ATTR_WO(add_lu); #endif static int do_create_driverfs_files(void) { int ret; ret = driver_create_file(&mhvtl_driverfs_driver, &driver_attr_add_lu); ret |= driver_create_file(&mhvtl_driverfs_driver, &driver_attr_opts); ret |= driver_create_file(&mhvtl_driverfs_driver, &driver_attr_major); return ret; } static void do_remove_driverfs_files(void) { driver_remove_file(&mhvtl_driverfs_driver, &driver_attr_major); driver_remove_file(&mhvtl_driverfs_driver, &driver_attr_opts); driver_remove_file(&mhvtl_driverfs_driver, &driver_attr_add_lu); } static int __init mhvtl_init(void) { int ret; memset(&devp, 0, sizeof(devp)); serial_number = 2; /* Start at something other than 0 */ mhvtl_major = register_chrdev(mhvtl_major, "mhvtl", &mhvtl_fops); if (mhvtl_major < 0) { pr_crit("Can't get major number\n"); goto register_chrdev_error; } ret = device_register(&mhvtl_pseudo_primary); if (ret < 0) { pr_crit("Device_register error: %d\n", ret); goto device_register_error; } ret = bus_register(&mhvtl_pseudo_lld_bus); if (ret < 0) { pr_crit("Bus_register error: %d\n", ret); goto bus_register_error; } ret = driver_register(&mhvtl_driverfs_driver); if (ret < 0) { pr_crit("Driver_register error: %d\n", ret); goto driver_register_error; } ret = do_create_driverfs_files(); if (ret < 0) { pr_crit("Driver_create_file error: %d\n", ret); goto do_create_driverfs_error; } mhvtl_add_host = 0; if (mhvtl_add_adapter()) { pr_crit("mhvtl_add_adapter failed\n"); goto mhvtl_add_adapter_error; } pr_debug("Built %d host%s\n", mhvtl_add_host, (mhvtl_add_host == 1) ? "" : "s"); dsp = (struct kmem_cache *)kmem_cache_create_usercopy("mhvtl_ds_cache", sizeof(struct mhvtl_ds), 0, SLAB_HWCACHE_ALIGN, 0, sizeof(struct mhvtl_ds), NULL); if (!dsp) { pr_err("Unable to create ds cache"); goto mhvtl_kmem_cache_error; } sgp = (struct kmem_cache *)kmem_cache_create_usercopy("mhvtl_sg_cache", SG_SEGMENT_SZ, 0, SLAB_HWCACHE_ALIGN, 0, SG_SEGMENT_SZ, NULL); if (!sgp) { pr_err("Unable to create sg cache (size %d)", (int)SG_SEGMENT_SZ); goto mhvtl_kmem_cache_error; } else { pr_info("kmem_cache_user_copy: page size: %d", (int)SG_SEGMENT_SZ); } pr_debug("Starting serial_number: %lld", serial_number); return 0; mhvtl_kmem_cache_error: mhvtl_remove_adapter(); mhvtl_add_adapter_error: do_remove_driverfs_files(); do_create_driverfs_error: driver_unregister(&mhvtl_driverfs_driver); driver_register_error: bus_unregister(&mhvtl_pseudo_lld_bus); bus_register_error: device_unregister(&mhvtl_pseudo_primary); device_register_error: unregister_chrdev(mhvtl_major, "mhvtl"); register_chrdev_error: return -EFAULT; } static void __exit mhvtl_exit(void) { int k; mhvtl_stop_all_queued(); for (k = mhvtl_add_host; k; k--) mhvtl_remove_adapter(); if (mhvtl_add_host != 0) pr_err("mhvtl_remove_adapter error at line %d\n", __LINE__); do_remove_driverfs_files(); driver_unregister(&mhvtl_driverfs_driver); bus_unregister(&mhvtl_pseudo_lld_bus); device_unregister(&mhvtl_pseudo_primary); unregister_chrdev(mhvtl_major, "mhvtl"); kmem_cache_destroy(dsp); kmem_cache_destroy(sgp); } device_initcall(mhvtl_init); module_exit(mhvtl_exit); static void mhvtl_pseudo_release(struct device *dev) { pr_notice("Called\n"); } static struct device mhvtl_pseudo_primary = { #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 30) .init_name = "mhvtl_pseudo", #else .bus_id = "mhvtl_pseudo", #endif .release = mhvtl_pseudo_release, }; static int mhvtl_lld_bus_match(struct device *dev, #ifdef DEFINE_CONST_STRUCT_DEVICE_DRIVER const #endif struct device_driver *dev_driver) { return 1; } static struct bus_type mhvtl_pseudo_lld_bus = { .name = "mhvtl", .match = mhvtl_lld_bus_match, }; static void mhvtl_release_adapter(struct device *dev) { struct mhvtl_hba_info *mhvtl_hba; mhvtl_hba = to_mhvtl_hba(dev); kfree(mhvtl_hba); } /* Simplified from original. * * Changed so it only adds one hba instance and no logical units */ static int mhvtl_add_adapter(void) { int error = 0; struct mhvtl_hba_info *mhvtl_hba; mhvtl_hba = kmalloc(sizeof(*mhvtl_hba), GFP_KERNEL); if (!mhvtl_hba) { pr_err("Unable to kmalloc %ld bytes of memory at line %d\n", sizeof(*mhvtl_hba), __LINE__); return -ENOMEM; } memset(mhvtl_hba, 0, sizeof(*mhvtl_hba)); INIT_LIST_HEAD(&mhvtl_hba->lu_list); spin_lock(&mhvtl_hba_list_lock); list_add_tail(&mhvtl_hba->hba_sibling, &mhvtl_hba_list); spin_unlock(&mhvtl_hba_list_lock); mhvtl_hba->dev.bus = &mhvtl_pseudo_lld_bus; mhvtl_hba->dev.parent = &mhvtl_pseudo_primary; mhvtl_hba->dev.release = &mhvtl_release_adapter; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 30) dev_set_name(&mhvtl_hba->dev, "adapter%d", mhvtl_add_host); #else sprintf(mhvtl_hba->dev.bus_id, "adapter%d", mhvtl_add_host); #endif error = device_register(&mhvtl_hba->dev); if (error) { kfree(mhvtl_hba); return error; } mhvtl_add_host++; return error; } static void mhvtl_remove_adapter(void) { struct mhvtl_hba_info *mhvtl_hba = NULL; spin_lock(&mhvtl_hba_list_lock); if (!list_empty(&mhvtl_hba_list)) { mhvtl_hba = list_entry(mhvtl_hba_list.prev, struct mhvtl_hba_info, hba_sibling); list_del(&mhvtl_hba->hba_sibling); } spin_unlock(&mhvtl_hba_list_lock); if (!mhvtl_hba) return; device_unregister(&mhvtl_hba->dev); --mhvtl_add_host; } static int mhvtl_driver_probe(struct device *dev) { int error = 0; struct mhvtl_hba_info *mhvtl_hba; struct Scsi_Host *hpnt; mhvtl_hba = to_mhvtl_hba(dev); hpnt = scsi_host_alloc(&mhvtl_driver_template, sizeof(*mhvtl_hba)); if (NULL == hpnt) { pr_err("scsi_register failed\n"); error = -ENODEV; return error; } mhvtl_hba->shost = hpnt; *((struct mhvtl_hba_info **)hpnt->hostdata) = mhvtl_hba; if ((hpnt->this_id >= 0) && (mhvtl_num_tgts > hpnt->this_id)) hpnt->max_id = mhvtl_num_tgts + 1; else hpnt->max_id = mhvtl_num_tgts; hpnt->max_lun = mhvtl_max_luns; error = scsi_add_host(hpnt, &mhvtl_hba->dev); if (error) { pr_err("scsi_add_host failed\n"); error = -ENODEV; scsi_host_put(hpnt); } else scsi_scan_host(hpnt); return error; } static int mhvtl_driver_remove(struct device *dev) { struct list_head *lh, *lh_sf; struct mhvtl_hba_info *mhvtl_hba; struct mhvtl_lu_info *lu; mhvtl_hba = to_mhvtl_hba(dev); if (!mhvtl_hba) { pr_err("Unable to locate host info\n"); return -ENODEV; } scsi_remove_host(mhvtl_hba->shost); list_for_each_safe(lh, lh_sf, &mhvtl_hba->lu_list) { lu = list_entry(lh, struct mhvtl_lu_info, lu_sibling); list_del(&lu->lu_sibling); kfree(lu); } scsi_host_put(mhvtl_hba->shost); mhvtl_hba->shost = NULL; return 0; } /* ******************************************************************* * Char device driver routines ******************************************************************* */ static int mhvtl_get_user_data(unsigned int minor, char __user *arg) { struct mhvtl_queued_cmd *sqcp = NULL; struct mhvtl_ds *ds; int ret = 0; unsigned char __user *up; size_t sz; ds = kmem_cache_alloc(dsp, 0); if (!ds) return -EFAULT; if (copy_from_user((u8 *)ds, (u8 *)arg, sizeof(struct mhvtl_ds))) { ret = -EFAULT; goto ret_err; } pr_debug(" data Cmd S/No : %lld\n", (unsigned long long)ds->serialNo); pr_debug(" data pointer : %p\n", ds->data); pr_debug(" data sz : %d\n", ds->sz); pr_debug(" SAM status : %d (0x%02x)\n", ds->sam_stat, ds->sam_stat); up = ds->data; sz = ds->sz; sqcp = lookup_sqcp(devp[minor], ds->serialNo); if (!sqcp) { ret = -ENOTTY; goto ret_err; } ret = mhvtl_resp_write_to_user(sqcp->a_cmnd, up, sz); ret_err: kmem_cache_free(dsp, ds); return ret; } static int mhvtl_put_user_data(unsigned int minor, char __user *arg) { struct mhvtl_queued_cmd *sqcp = NULL; struct mhvtl_ds *ds; int ret = 0; uint8_t *s; ds = kmem_cache_alloc(dsp, 0); if (!ds) { pr_err("Failed to allocate kmem_cache\n"); ret = -EFAULT; goto give_up; } if (copy_from_user((u8 *)ds, (u8 *)arg, sizeof(struct mhvtl_ds))) { pr_err("Failed to copy from user %ld bytes", (unsigned long)sizeof(struct mhvtl_ds)); ret = -EFAULT; goto give_up; } pr_debug(" data Cmd S/No : %lld\n", (unsigned long long)ds->serialNo); pr_debug(" data pointer : %p\n", ds->data); pr_debug(" data sz : %d\n", ds->sz); pr_debug(" SAM status : %d (0x%02x)\n", ds->sam_stat, ds->sam_stat); sqcp = lookup_sqcp(devp[minor], ds->serialNo); if (!sqcp) { pr_err("Callback function not found for SCSI cmd s/no. %lld, minor: %d\n", (unsigned long long)ds->serialNo, minor); ret = 1; /* report busy to mid level */ goto give_up; } ret = mhvtl_fill_from_user_buffer(sqcp->a_cmnd, ds->data, ds->sz); if (ds->sam_stat) { /* Auto-sense */ sqcp->a_cmnd->result = ds->sam_stat; if (copy_from_user(sqcp->a_cmnd->sense_buffer, ds->sense_buf, SENSE_BUF_SIZE)) pr_err("Failed to retrieve autosense data\n"); sqcp->a_cmnd->sense_buffer[0] |= 0x70; /* force valid sense */ s = sqcp->a_cmnd->sense_buffer; pr_debug("Auto-Sense returned [key/ASC/ASCQ] " "[%02x %02x %02x]\n", s[2], s[12], s[13]); } else { sqcp->a_cmnd->result = DID_OK << 16; } timer_delete_sync(&sqcp->cmnd_timer); if (sqcp->done_funct) sqcp->done_funct(sqcp->a_cmnd); else pr_err("FATAL, line %d: SCSI done_funct callback => NULL\n", __LINE__); mhvtl_remove_sqcp(devp[minor], sqcp); ret = 0; give_up: kmem_cache_free(dsp, ds); return ret; } static int send_mhvtl_header(unsigned int minor, char __user *arg) { struct mhvtl_header *vheadp; struct mhvtl_queued_cmd *sqcp, *n; int ret = 0; list_for_each_entry_safe(sqcp, n, &devp[minor]->cmd_list, queued_sibling) { if (sqcp->state == CMD_STATE_QUEUED) { vheadp = &sqcp->op_header; if (copy_to_user((u8 *)arg, (u8 *)vheadp, sizeof(struct mhvtl_header))) { ret = -EFAULT; goto give_up; } /* Found an outstanding cmd to send */ sqcp->state = CMD_STATE_IN_USE; ret = VTL_QUEUE_CMD; /* Can only send one header at a time */ goto give_up; } } give_up: return ret; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) #if defined(DEFINE_SEMAPHORE_HAS_NUMERIC_ARG) static DEFINE_SEMAPHORE(tmp_mutex, 1); #else static DEFINE_SEMAPHORE(tmp_mutex); #endif /* DEFINE_SEMAPHORE_HAS_NUMERIC_ARG */ #else static DECLARE_MUTEX(tmp_mutex); #endif static int mhvtl_remove_lu(unsigned int minor, char __user *arg) { struct mhvtl_ctl ctl; struct mhvtl_hba_info *mhvtl_hba; struct mhvtl_lu_info *lu, *n; struct scsi_device *baksdev; int ret = -ENODEV; baksdev = NULL; down(&tmp_mutex); if (copy_from_user((u8 *)&ctl, (u8 *)arg, sizeof(ctl))) { ret = -EFAULT; goto give_up; } mhvtl_hba = mhvtl_get_hba_entry(); if (!mhvtl_hba) { ret = 0; goto give_up; } pr_debug("ioctl to remove device <%02d %02d %02d>, hba: %p\n", ctl.channel, ctl.id, ctl.lun, mhvtl_hba); spin_lock(&mhvtl_hba_list_lock); list_for_each_entry_safe(lu, n, &mhvtl_hba->lu_list, lu_sibling) { if ((lu->channel == ctl.channel) && (lu->target == ctl.id) && (lu->lun == ctl.lun)) { pr_debug("line %d found matching lu\n", __LINE__); list_del(&lu->lu_sibling); devp[minor] = NULL; spin_lock(&lu->sdev_lock); if (lu->sdev) { baksdev = lu->sdev; spin_unlock(&lu->sdev_lock); break; } spin_unlock(&lu->sdev_lock); } } spin_unlock(&mhvtl_hba_list_lock); if (baksdev) { scsi_remove_device(baksdev); scsi_device_put(baksdev); } ret = 0; give_up: up(&tmp_mutex); return ret; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) static DEFINE_MUTEX(ioctl_mutex); #endif static long mhvtl_c_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; struct inode *inode = file_inode(file); if (!inode) { pr_err("Unable to obtain inode - inode is null\n"); return -ENODEV; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) mutex_lock(&ioctl_mutex); #else lock_kernel(); #endif ret = mhvtl_c_ioctl_bkl(inode, file, cmd, arg); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) mutex_unlock(&ioctl_mutex); #else unlock_kernel(); #endif return ret; } /* * char device ioctl entry point */ static int mhvtl_c_ioctl_bkl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor = iminor(inode); int ret; if (minor >= DEF_MAX_MINOR_NO) { /* Check limit minor no. */ return -ENODEV; } ret = 0; switch (cmd) { case VTL_POLL_AND_GET_HEADER: if (!devp[minor]) { put_user(0, (unsigned int *)arg); ret = 0; break; } ret = send_mhvtl_header(minor, (char __user *)arg); break; case VTL_GET_DATA: pr_debug("ioctl(VTL_GET_DATA)\n"); ret = mhvtl_get_user_data(minor, (char __user *)arg); break; case VTL_PUT_DATA: pr_debug("ioctl(VTL_PUT_DATA)\n"); ret = mhvtl_put_user_data(minor, (char __user *)arg); break; case VTL_REMOVE_LU: pr_debug("ioctl(VTL_REMOVE_LU)\n"); ret = mhvtl_remove_lu(minor, (char __user *)arg); break; default: ret = -ENOTTY; break; } return ret; } static int mhvtl_release(struct inode *inode, struct file *filp) { unsigned int minor = iminor(inode); pr_debug("lu for minor %u Release\n", minor); return 0; } static int mhvtl_open(struct inode *inode, struct file *filp) { unsigned int minor = iminor(inode); pr_debug("mhvtl%u: opened\n", minor); return 0; } ================================================ FILE: man/Makefile ================================================ # # This makefile needs to be invoked as follows: # #make # # Here, options include: # # all to build all utilities # clean to clean up all intermediate files # # # Makefile magic # $@ is a variable that expands to the name of the file being built # $< is a variable that expands to the naem of the source file # @ at the beginning of the first line tell make not to echo the commands as it run it. # CURDIR = "../" include ../config.mk MONTH = $(shell date -r ../ChangeLog +%B) YEAR = $(shell date -r ../ChangeLog +%Y) MAN_PATH = $(DESTDIR)$(PREFIX)$(MANDIR) MAN1_PAGES = $(patsubst %.1.in,%.1,$(wildcard *.1.in)) MAN5_PAGES = $(patsubst %.5.in,%.5,$(wildcard *.5.in)) all: $(MAN1_PAGES) $(MAN5_PAGES) # Create man pages in local folder %: %.in sed -e s'/@VERSION@/$(VERSION)/' \ -e s'/@MONTH@/$(MONTH)/' \ -e s'/@YEAR@/$(YEAR)/' \ -e s'/@CONFIG_PATH@/$(CONFIG_PATH)/' \ -e s'/@HOME_PATH@/$(HOME_PATH)/' $< > $@ # Directories $(MAN_PATH)/man1: install -d -m 755 $@ $(MAN_PATH)/man5: install -d -m 755 $@ # installation $(MAN_PATH)/man1/%.1: %.1 | $(MAN_PATH)/man1 install $< $@ $(MAN_PATH)/man5/%.5: %.5 | $(MAN_PATH)/man5 install $< $@ .PHONY:install install: $(addprefix $(MAN_PATH)/man1/,$(MAN1_PAGES)) \ $(addprefix $(MAN_PATH)/man5/,$(MAN5_PAGES)) .PHONY: uninstall uninstall: rm -f $(addprefix $(MAN_PATH)/man1/,$(MAN1_PAGES)) \ $(addprefix $(MAN_PATH)/man5/,$(MAN5_PAGES)) # ========== Cleaning ========== .PHONY:clean clean: rm -f *.1 *.5 .PHONY:distclean distclean: rm -f *.1 *.5 ================================================ FILE: man/device.conf.5.in ================================================ .TH device.conf "5" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME device.conf \- Configuration file for .BR vtllibrary(1) and .BR vtltape(1) .SH DESCRIPTION .\" Add any additional description here .PP Each configured device contains a unique entry in device.conf Each section starts at column 1 and is terminated by a blank line. .IP e.g. Library: 10 CHANNEL: 00 TARGET: 00 LUN: 00 Vendor identification: SPECTRA Product identification: PYTHON Product revision level: 550V Unit serial number: XYZZY_A NAA: 10:22:33:44:ab:00:00:00 Home directory: @HOME_PATH@/Spectra fifo: /var/tmp/mhvtl PERSIST: True .PP Where descriptor .B Library: or .B Drive: starts each section The descriptor is followed by a unique ID (a decimal number) On the same line following the descriptor is the SCSI corrordinates for the device described. .B Vendor identification, .B Product identification, .B Product revision level and .B Unit serial number are common fields across all device types. .B NAA field is used by some backup software (e.g. Legato) as means to uniquely identify devices. It is used by INQUIRY VPD page 0x83. Most software uses the .B Unit serial number as the unique identifier. .B Compression: factor .B X enabled .B Y Where .PP .B X is 1 through 9. .IP 1 is fastest compression, 9 is best compression. .PP .B Y .IP 1: compression is enabled, 0: compression is disabled. .PP .B Compression type: zlib or lzo .PP .B Backoff: Value between 10 and 10000. Default is 1000. This value is added to existing 'usleep' time in between ioctl polls. If there is work to do, the usleep time is reset to 10. .PP .B Home directory: /some/where/with/space .PP Specify a parent directory for the virtual media associated with this library. Only in valid ^Library: entries (not for ^Tape: entries) .PP .B fifo: /some/where/for/named/pipe .PP If fifo: is defined, (near) real time state information will be available for external applications by reading from this named pipe. .PP Note: the '-f fifo' switch on daemon startup has higher precedence .PP .B PERSIST: True | False If PERSIST is set to 'Yes' or 'True' - enable saving state on shutdown. A file 'library_contents.XX.persist' will be created on shutdown to save persistent state. On startup - Default to read the library_contents.XX.persist if the file exists. Fall back to library_contents.XX if no .persist file exists. .SH AUTHOR Written by Mark Harvey .SH BUGS .RS This man page. .RE .RS No version information. .RE .RS Config file not xml. .RE .RS .BR vtllibrary(1) and .BR vtltape(1) needs to be restarted to read any changes made. .RE .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2005 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR library_contents(5) .BR make_vtl_media(1), .BR mktape(1), .BR vtlcmd(1), .BR vtllibrary(1), .BR vtltape(1) ================================================ FILE: man/dump_tape.1.in ================================================ .TH dump_tape "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME dump_tape \- Utility to dump tape contents for virtual media in VTL. .SH SYNOPSIS .B dump_tape .B [ \-h ] .br .B dump_tape .B [ \fIOPTIONS\fR] .SH DESCRIPTION .\" Add any additional description here .PP Dump the contents of the specified PCL. If a library is specified, look there, else look through all of the libraries. .SH OPTIONS .TP .B \-h display usage information and exit .TP .B \-v be verbose .TP .B \-d print debugging information .TP .BR \-l lib_no \fB\-l n\fR Where lib_no is the library index number (default library index numbers 10 & 30). .TP \fB\-m PCL\fR where PCL is the Physical Cartridge Label (barcode). This is required. .TP .B \-D dump data .SH AUTHOR Written by Mark Harvey .SH BUGS Needs to be made user friendly. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2005 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR make_vtl_media(1), .BR library_contents(5), .BR vtlcmd(1), .BR vtllibrary(1), .BR vtltape(1), .BR edit_tape(1), .BR preload_tape(1) ================================================ FILE: man/edit_tape.1.in ================================================ .TH edit_tape "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME edit_tape \- Utility to update meta data virtual media for the VTL. .SH SYNOPSIS .B edit_tape .B [ \-h ] .B [ \fIoptions \fR] .B ... .SH DESCRIPTION .\" Add any additional description here .PP Create and initialise new media for the Virtual Tape Library. Media is created in the /opt/vtl/ directory. This path is hard coded. .SH OPTIONS .TP \fB\-h\fR display this help and exit .TP \fB\-m PCL\fR where PCL is the Physical Cartridge Label (barcode). .TP \fB\-w on|off\fR Turn on|off media write-protect flag .TP \fB\-s size\fR where size is the capacity of the virtual media - Size is defined in 'megabytes'. .TP \fB\-t type\fR The media 'type' which can be: "data" , "WORM" (Write Once Read Many) or "clean" (cleaning cartridge) .TP \fB\-d density\fR Media density. Can be one of LTO1, LTO2, LTO3, LTO4, LTO5, LTO6, LTO7, LTO8, LTO9, SDLT1, SDLT2, SDLT3, SDLT4, AIT1, AIT2, AIT3, AIT4, T10KA, T10KB, T10KC, 9840A, 9840B, 9840C, 9840D, 9940A, 9940B, J1A, E05 and E06 The 'J1A, E05 and E06' media densities refer to the IBM 03592 media types. .SH AUTHOR Written by Mark Harvey .SH BUGS Needs to be made user friendly. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2005 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR make_vtl_media(1), .BR library_contents(5), .BR vtlcmd(1), .BR vtllibrary(1), .BR vtltape(1) .BR mktape(1) ================================================ FILE: man/generate_device_conf.1.in ================================================ .TH generate_device_conf "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME generate_device_conf \- Utility to generate the device.conf file .SH SYNOPSIS .B generate_device_conf -h|--help -- print help message and exit .br .BI generate_device_conf OPTIONS .SH DESCRIPTION .PP This script creates the device.conf file, from scratch. Edit this script and run it to generate a non-default configuration. .PP This script is normally run at software installation time, but can be edited then run again to regenerate the .B device.conf file again. .PP .I OPTIONS are from: .TP \fB\-H\fR, \fB\-\-home-dir\fR \fIHOME_DIR\fR Set the .I mhvtl home directory [default .BR @HOME_PATH@ . .TP \fB\-D\fR, \fB\-\-dest-dir\fR \fIDIR\fR Set the destination directory [default .BR . ] .TP \fB\-f\fR, \fB\-\-force\fR Overwrite existing .B device.conf if needed. .TP \fB\-o\fR, \fB\-\-override-home\fR Allow script to continue even if home directory is not present. .SH AUTHOR Written by Mark Harvey. .SH BUGS Needs to be made user friendly and more verbose. And it would be nice to be able to change the device configuration file without editing this script. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2018 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR device.conf(5) , .BR make_vtl_media(1) , .BR library_contents(5) , .BR vtlcmd(1) , .BR vtllibrary(1) , .BR vtltape(1) , .BR edit_tape(1) , .BR generate_library_contents(1) ================================================ FILE: man/generate_library_contents.1.in ================================================ .TH generate_library_contents "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME generate_library_contents \- Utility to generate the lib_contents.* files from device.conf .SH SYNOPSIS \fBgenerate_library_contents -h|--help\fR -- print help usage and exit, or .br .B generate_library_contents .I [OPTIONS] .SH DESCRIPTION Where .I OPTIONS are from: .TP \fB\-C\fR, \fB\-\-config-dir\fR=\fIDIR\fR Set config directory [defaults to .BR @CONF_PATH@ ] .TP \fB\-D\fR, \fB\-\-dest-dir\fR=\fIDIR\fR Set the destination directory [defaults to .BR . ] .TP \fB\-f\fR, \fB\-\-force\fR Write over existing library_contents.* files, if needed. .PP This script creates the library_contents.* files from the .B device.conf file. It is normally run at system installation time, but can be called to regenerate these files if .B device.conf has changed. .PP .SH AUTHOR Written by Mark Harvey. Updated by Lee Duncan. .SH BUGS Needs to be made user friendly and more verbose. And perhaps this should be integrated with .BR generate_device_conf(1) . .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2018 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR device.conf(5) , .BR make_vtl_media(1) , .BR library_contents(5) , .BR vtlcmd(1) , .BR vtllibrary(1) , .BR vtltape(1) , .BR edit_tape(1) , .BR generate_device_conf(1) ================================================ FILE: man/library_contents.5.in ================================================ .TH library_contents "5" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME library_contents \- Configuration file for .BR vtllibrary(1) .SH DESCRIPTION .\" Add any additional description here .PP Configuration file for vtllibrary daemon (part of Virtual Tape Library) One line per entry. Contains the following sections, Only the 'Slot' section should be populated with additional information (barcodes). The other sections are used as place holders. e.g. If the number of MAP slots are to be changed within the VTL, change the number of entries in this configuration file. .IP "Drive #: " Note: Set the serial number of the drive in device.conf .IP "Picker #:" .IP "MAP #:" .IP "Slot #: [Barcode]" Where [Barcode] can be any ASCII string from 1 to 12 chars in length. If there is no Barcode defined for the Slot number, the slot is taken to be empty. .SH AUTHOR Written by Mark Harvey .SH BUGS .RS This man page. .RE .RS No version information. .RE .RS Config file not xml. .RE .RS .BR vtllibrary(1) needs to be restarted to read any changes made. .RE .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2005 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR make_vtl_media(1), .BR mktape(1), .BR vtlcmd(1), .BR vtllibrary(1), .BR vtltape(1) .BR device.conf(5) ================================================ FILE: man/make_vtl_media.1.in ================================================ .TH make_vtl_media "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME make_vtl_media \- create the library database from lib_contents.* file(s) .SH SYNOPSIS \fBmake_vtl_media \-h|\-\-help\fR -- print help message and exit, or .br .B make_vtl_media .I [OPTIONS] .SH DESCRIPTION .PP Where .I OPTIONS are from: .TP \fB\-H\fR, \fB\-\-home-dir\fR=\fIDIR\fR Set the .I mhvtl home directory [default .BR @HOME_PATH@ ] .TP \fB\-C\fR, \fB\-\-config-dir\fR=\fIDIR\fR Set the config directory [default .BR @CONFIG_PATH@ ] .TP \fB\-f\fR, \fB\-\-force\fR Overwrite existing database files, if needed. .TP \fB\-c\fR, \fB\-\-create-dir\fR Create the home directory, if needed. .PP This program is invoked initialy when the software is installed, but can also be ran later to recreate the tape libraray database. It does so by reading the .I device.conf and .I library_contents.* files in our configuration directory, and finding the .B Slot entries in those files, extracting the .I barcode found for each slot. .PP .IP "If \fIbarcode\fR starts with W" Create as Write Once Read Many (WORM) media type. .IP "If \fIbarcode\fR starts with CLN" Create media type as cleaning cartridge. .IP "Otherwise" Create the media as a data cartridge. .IP Attempts to use the chars 7 and 8 to work out the media type (LTO1/2/3/4/5/6/7, T10K, AIT etc) See .B mktape(1) for more details on media density. .IP Feel free to replace this script with one that better suits your needs. .PP If the configuration is regenerated then the .B mhvtl.target service should be restarted. .SH AUTHOR Written by Mark Harvey .SH BUGS Not documented. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2019 Free Software Foundation, Inc. .P This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR library_contents(5) , .BR mktape(1) , .BR vtlcmd(1) , .BR vtllibrary(1) , .BR vtltape(1) , .BR generate_device_conf(1) , .BR generate_library_contents(1) ================================================ FILE: man/mhvtl.conf.5.in ================================================ .TH mhvtl.conf "5" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME mhvtl.conf \- Configuration file for .B vtllibrary(1) and .B vtltape(1) .SH DESCRIPTION .\" Add any additional description here .PP Configuration file to define startup behaviour of the virtual tape library. .TP MHVTL_HOME_PATH=@HOME_PATH@ The location where .I mhvtl stores its tape .IR database . .TP CAPACITY=500 Defines default capacity any new media will be created (in Megabytes). .B make_vtl_media(1) uses this value and passes to .IR mktape . .TP VERBOSE=1 Set verbosity to level [0|1|2|3] for daemon logging level at startup. Refer to .B vtlcmd(1) to adjust logging level at runtime. .TP VTL_DEBUG=0 Set to [0|1]. to disable/enable kernel module logging. Can be enabled/disabled at runtime via: .IP "echo 0 > /sys/bus/pseduo/drivers/mhvtl/opts" to disable, or .IP "echo 1 > /sys/bus/pseudo/drivers/mhvtl/opts" to enable logging. .TP DAEMON_DEBUG='' Set to .I '' to disable daemon debugging, or .I '-d' to enable it. .SH AUTHOR Written by Mark Harvey .SH BUGS This man page. .PP Config file not xml. .PP .B vtllibrary(1) needs to be restarted to read any changes made. (By design). .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2019 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR mktape(1) , .BR vtlcmd(1) , .BR vtllibrary(1) , .BR vtltape(1) ================================================ FILE: man/mhvtl_kernel_mod_build.1.in ================================================ .TH mhvtl_kernel_mod_build "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME mhvtl_kernel_mod_build \- Simple wrapper script to extract mhvtl.ko source and compile .SH SYNOPSIS \fBmhvtl_kernel_mod_build .br .SH DESCRIPTION .br This bash script simply extracts /usr/lib/firmware/mhvtl/mhvtl_kernel.tgz and compiles (make && sudo make install) .br The mhvtl.ko source is extracted into a unique tmp directory using 'mktemp()' .SH AUTHOR Written by Mark Harvey .SH BUGS Assumes the kernel-devel and gcc plus supporting packages are already installed. .SH "REPORTING BUGS" Report bugs to or log an issue at https://github.com/markh794/mhvtl.git .br Better yet, I'm happy to accept patches which fix bugs :) .SH COPYRIGHT Copyright \(co 2019 Free Software Foundation, Inc. .P This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR library_contents(5) , .BR mktape(1) , .BR vtlcmd(1) , .BR vtllibrary(1) , .BR vtltape(1) , .BR generate_device_conf(1) , .BR generate_library_contents(1) ================================================ FILE: man/mktape.1.in ================================================ .TH mktape "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME mktape \- Utility to create new/blank virtual media for the VTL. .SH SYNOPSIS .B mktape \-h .br .B mktape REQUIRED_PARAMS .BI [ OPTIONS ] .B ... .SH DESCRIPTION .PP Create and initialise new media for the Virtual Tape Library. Media is created in the .B @HOME_PATH@ directory by default. This can be over-ridden using the 'Home directory:' setting in .BR device.conf . .SH OPTIONS Options are from: .TP .B \-h display this help and exit .TP .B \-V Print version information and exit .TP .B \-v Be verbose .TP .B \-D Enter debugging mode (much each output) .TP \fB\-C\fR \fIconfig-dir\fR Use .I config-dir instead of the default of .BR @CONFIG_PATH@ . .PP Required options are: .TP \fB\-l\fR \fIn\fR Where .I n is the library index number (default library index numbers 10 & 30). The 'Home directory:' entry for the library specified is used as parent directory for media created. If the library index number does not exist, then a subdir under @HOME_PATH@/ is created and virtual media created in this directory. For example "mktape -l 20 -m ABC123 -s 1024 -t data -d LTO3" will create a virtual media .I ABC123 in @HOME_PATH@/20/ if no library index 20 defined in .BR device.conf . .TP \fB\-m\fR \fIPCL\fR where .I PCL is the Physical Cartridge Label (barcode). .TP \fB\-s\fR \fIsize\fR where .I size is the capacity of the virtual media - Size is defined in 'megabytes'. .TP \fB\-t\fR \fItype\fR The media .I type which can be: .BR data , .B WORM (Write Once Read Many), .B clean (cleaning cartridge) or .B NULL for media which does not save data. .PP The .B NULL is a special media type used primarily to test performance with disk writes kept to a minimum. Note: Metadata (512bytes per data block) is still written. .TP \fB\-d\fR \fIdensity\fR Media .IR density . Can be one of .BR LTO1 , .BR LTO2 , .BR LTO3 , .BR LTO4 , .BR LTO5 , .BR LTO6 , .BR LTO7 , .BR LTO8 , .BR LTO9 , .BR SDLT1 , .BR SDLT2 , .BR SDLT3 , .BR SDLT4 , .BR AIT1 , .BR AIT2 , .BR AIT3 , .BR AIT4 , .BR T10KA , .BR T10KB , .BR T10KC , .BR 9840A , .BR 9840B , .BR 9840C , .BR 9840D , .BR 9940A , .BR 9940B , .BR J1A , .BR E05 and .BR E06 . .PP The .BR J1A , .B E05 and .B E06 media densities refer to the IBM 03592 media types. .SH AUTHOR Written by Mark Harvey .SH BUGS Needs to be made user friendly. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2018 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR make_vtl_media(1) , .BR library_contents(5) , .BR vtlcmd(1) , .BR vtllibrary(1) , .BR vtltape(1) , .BR edit_tape(1) , .BR generate_device_conf(1) , .BR generate_library_contents(1) ================================================ FILE: man/preload_tape.1.in ================================================ .TH preload_tape "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME preload_tape \- Utility to write a file directly to virtual media. .SH SYNOPSIS .B preload_tape .B [ \-h ] .br .B preload_tape .B [ \fIOPTIONS\fR] .SH DESCRIPTION .\" Add any additional description here .PP Dump the contents of the specified PCL. If a library is specified, look there, else look through all of the libraries. .SH OPTIONS .TP .B \-h display usage information and exit .TP .B \-v be verbose .TP .B \-d print debugging information .TP .BR \-l lib_no \fB\-l n\fR Where lib_no is the library index number (default library index numbers 10 & 30). .TP \fB\-m PCL\fR where PCL is the Physical Cartridge Label (barcode). This is required. .TP \fB\-F \fR where is the source of the data to write 'in' virtual media format. .TP \fB\-b block_size\fR where block_size is the size of the 'tape block' - e.g. -b 65535 will write in 64k blocks .TP \fB\-c LZO|ZLIB|NONE\fR Compress data before writing in virtual media format. .TP .B .SH AUTHOR Written by Mark Harvey .SH BUGS Needs to be made user friendly. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2005 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR make_vtl_media(1), .BR library_contents(5), .BR vtlcmd(1), .BR dump_tape(1), .BR vtllibrary(1), .BR vtltape(1), .BR edit_tape(1) ================================================ FILE: man/tapeexerciser.1.in ================================================ .TH tapeexerciser "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME tapeexerciser \- Utility to exercise virtual media for the VTL. .SH SYNOPSIS .B tapeexerciser .BR \-f tape_device .SH DESCRIPTION .\" Add any additional description here .PP Exercise the specified tape drive path and exercise the tape found there. .PP .B NOTE: This will overwrite the contents of the tape media on the specified drive. .SH AUTHOR Written by Mark Harvey .SH BUGS Needs to be made user friendly. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2005 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR make_vtl_media(1), .BR library_contents(5), .BR vtlcmd(1), .BR vtllibrary(1), .BR vtltape(1) .BR edit_tape(1) ================================================ FILE: man/update_device.conf.1.in ================================================ .TH update_device.conf "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME update_device.conf \- Utility to update device.conf to version 4 .SH SYNOPSIS .B update_device.conf .SH DESCRIPTION .\" Add any additional description here .PP This script updates the device.conf file to version 4, and sets the library index to one more than the number of drives found. .SH AUTHOR Written by Mark Harvey .SH BUGS Needs to be made user friendly and more verbose. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2005 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR device.conf(5) .BR make_vtl_media(1), .BR library_contents(5), .BR vtlcmd(1), .BR vtllibrary(1), .BR vtltape(1) .BR edit_tape(1) ================================================ FILE: man/vtlcmd.1.in ================================================ .TH vtlcmd "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME vtlcmd \- user space utility to send commands to .BR vtltape(1), .BR vtllibrary(1), daemons. .SH SYNOPSIS .B vtlcmd 'Q number' .B [ \-h ] .B [ \fIcommand \fR] .B ... .SH DESCRIPTION .\" Add any additional description here .PP Where 'Q number' is the message queue number for the drive (see .BR vtltape(1) ) .TP \fB\-h\fR display this help and exit .SH COMMAND .IP verbose Toggle verbose flag. This will cycle verbose flag thru verbose settings 3 2 1 off .IP "load " Load media ID (barcode) - Used for stand-alone tape drive daemon. .IP "unload " Unload media ID (barcode) .IP "compression " Changes compression libraries used to compress each block of data. Valid for .B tape only. .IP "delay load x" Sets a delay for loading media to .B x seconds. Valid values 0 - 20. ssc target returns <02/04/01> 'NOT READY/BECOMMING READY' status in response to any 'Test Unit Ready' requested until timer expires. Valid for .B tape only. .IP "delay unload x" Sets a delay for unloading media to .B x seconds. Valid values 0 - 20. Valid for .B tape only. .IP "delay rewind x" Sets a delay for rewinding media to .B x seconds. Valid values 0 - 30. Valid for .B tape only. .IP "delay position x" Sets a delay for positioning media to .B x seconds. Valid values 0 - 20. Valid for .B tape only. .IP "delay thread x" Sets a delay for \'threading\' media to .B x seconds. Valid values 0 - 20. Valid for .B tape only. .IP "TapeAlert " Send a 64bit hex number, each bit corresponds to one TapeAlert flag as defined by t10.org. Where bit 0 is TapeAlert flag 1, and bit 63 is TapeAlert flag 64. .IP exit Send a terminate message to daemon. .IP online Valid for .B library only. Place library in 'online' mode. .IP offline Valid for .B library only. Take library 'offline'. .IP "add slot" Valid for .B library only. This will dynamically add an additional storage slot to the library. This is not persistent. .IP "list map" Valid for .B library only. Returns a list of media IDs in Media Access Port (MAP). .IP "open map" Valid for .B library only. Logically 'opens' the Media Access Port (MAP). .IP "close map" Valid for .B library only. Logically 'closes' the Media Access Port (MAP). MAP needs to be 'closed' before the Medium Transport Element can move media to/from MAP. .IP "empty map" Valid for .B library only. Clears media from Media Access Port (MAP). Only valid when MAP is in 'open' state .IP "load map " Valid for .B library only. Places logically into the Media Access Port (MAP). Note: needs to exist first, otherwise the command fails with suggestion to create media first. see .BR mktape(1) for creating media. .SH AUTHOR Written by Mark Harvey .SH BUGS Needs to be made user friendly. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2005 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR library_contents(5), .BR mktape(1), .BR vtllibrary(1), .BR vtltape(1) ================================================ FILE: man/vtllibrary.1.in ================================================ .TH vtllibrary "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME vtllibrary \- user space daemon to handle SCSI SMC commands for Virtual Tape Library. .SH SYNOPSIS \fBvtllibrary \-h\fR -- print a help message and exit, or .br .B vtllibrary .I [OPTIONS] \fB\-q\fR \fIQUEUE_ID\fR -- Emulate a tape library for queue \fIQUEUE_ID\fR. .SH DESCRIPTION .\" Add any additional description here This command emulates a tape library device, using queue .IR QUEUE_ID , which represent the message queue number used for this daemon. This number is derived from the .I device.conf where it must be unique. Media files can be created using .I OPTIONS are from: .TP .B \-d Enable debug logging (to stdout). Do not background the process, and set verbosity to level 9. .TP .BI \-v[ N ] Enable verbose logging (to syslog) at level \fIN\fR (default 1). .TP .B -F Run in the foreground, not as a daemon. .TP .BI \-f FIFO Near real time device state information will be available for external utilities by reading from this fifo. This switch has a higher precedence than the 'fifo:' entry in .BR device.conf . .PP The .B vtllibrary daemon simulates a tape library device, and is started by .BR systemd(1) , as needed, one invocation per Tape Library being simulated. The .B vtllibrary daemon registers each .B vtltape(1) via message queue. The .B vtllibrary reads @CONFIG_PATH@/library_contents.* and dynamically builds an internal structure of Storage slots and Media Access slots. All media movement is performed on internal structures only. The configuration file is never changed, i.e. a re-start of the daemon will result in the loss of any previous media movements. .P When media is moved to/from a "Data transfer element" (tape drive), a (un)load message is sent via the drive 'slot number' message Q number to load/unload the barcode. .SH FILES @CONFIG_PATH@/device.conf -- to find which \fIlibrary_contents.*\fR files to examine .br @CONFIG_PATH@/library_contents.* .SH AUTHOR Written by Mark Harvey .SH BUGS Does not implement the complete SCSI SMC-2/SMC-3 command set. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2019 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR library_contents(5) , .BR mktape(1) , .BR make_vtl_media(1) , .BR vtlcmd(1) , .BR vtltape(1) ================================================ FILE: man/vtltape.1.in ================================================ .TH vtltape "1" "@MONTH@ @YEAR@" "mhvtl @VERSION@" "User Commands" .SH NAME vtltape \- user space daemon to handle SCSI SSC commands for Virtual Tape Library. .SH SYNOPSIS \fBvtltape -h\fR -- print a help message and exit, or .br .B vtltape .I [OPTIONS] \fB-q\fR \fIQUEUE_ID\fR -- Emulate a tape drive for queue \fIQUEUE_ID\fR. .SH DESCRIPTION .\" Add any additional description here This command emulates a tape device, using queue .IR QUEUE_ID , which represent the message queue number used for this daemon. This number is derived from the .I device.conf where it must be unique. Media files can be created using .BR mktape(1) . .P .I OPTIONS are from: .TP .B \-d Enable debug logging (to stdout). Do not background the process, and set verbosity to level 9. .TP .BI \-v[ N ] Enable verbose logging (to syslog) at level \fIN\fR (default 1). .TP .BI \-f FIFO Near real time device state information will be available for external utilities by reading from this fifo. This switch has a higher precedence than the 'fifo:' entry in .BR device.conf . .SH AUTHOR Written by Mark Harvey .SH BUGS Does not implement the complete SCSI SSC-3 command set. .SH "REPORTING BUGS" Report bugs to .SH COPYRIGHT Copyright \(co 2019 Free Software Foundation, Inc. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR library_contents(5) , .BR make_vtl_media(1) , .BR mktape(1) , .BR vtlcmd(1) , .BR vtllibrary(1) ================================================ FILE: mhvtl-utils.spec ================================================ # Disable the building of the debug package(s). %define debug_package %{nil} # config.sh parses '_firmwarepath' to define FIRMWAREDIR in parent Makefile %global _firmwarepath /usr/lib/firmware # Compat path macros # pilfered from https://src.fedoraproject.org/rpms/snapd/blob/master/f/snapd.spec %{!?_systemdgeneratordir: %global _systemdgeneratordir %{_prefix}/lib/systemd/system-generators} %define _unpackaged_files_terminate_build 0 %define mhvtl_home_dir /opt/mhvtl Summary: Virtual tape library. kernel pseudo HBA driver + userspace daemons %define real_name mhvtl Name: mhvtl-utils %define real_version 2026-03-10 %define minor 0 Version: 1.8 Release: %{minor}%{?dist} License: GPL Group: System/Kernel URL: http://sites.google.com/site/linuxvtl2/ Source: mhvtl-%{real_version}.tgz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build-%(%{__id_u} -n) Recommends: lsscsi mtx mt-st Requires:sg3_utils Requires: policycoreutils Requires: tar >= 1.28 BuildRequires: systemd BuildRequires: systemd-rpm-macros BuildRequires: zlib-devel %{?systemd_requires} %{?systemd_ordering} Obsoletes: mhvtl <= %{version}-%{release} Provides: mhvtl = %{version}-%{release} %description A Virtual tape library and tape drives: Used to emulate hardware robot & tape drives: VTL consists of a pseudo HBA kernel driver and user-space daemons which function as the SCSI target. Communication between the kernel module and the daemons is achieved via /dev/mhvtl? device nodes. The kernel module is based on the scsi_debug driver. The SSC/SMC target daemons have been written from scratch. %prep %setup -n %{real_name}-%{version} %post /sbin/semanage fcontext -a -t systemd_unit_file_t %{_unitdir}/mhvtl-load-modules.service /sbin/semanage fcontext -a -t systemd_unit_file_t %{_unitdir}/vtllibrary@.service /sbin/semanage fcontext -a -t systemd_unit_file_t %{_unitdir}/vtltape@.service /sbin/semanage fcontext -a -t systemd_unit_file_t %{_unitdir}/mhvtl.target /sbin/restorecon -R -v %{_unitdir} /bin/systemctl daemon-reload %{service_add_post mhvtl.target mhvtl-load-modules.service vtllibrary@.service vtltape@.service} /bin/systemctl start mhvtl.target /bin/systemctl enable mhvtl.target make_vtl_media --config-dir=%{_sysconfdir}/mhvtl --home-dir=/opt/mhvtl --mktape-path=%{_bindir} %postun /sbin/ldconfig /bin/systemctl daemon-reload #%{service_del_postun mhvtl.target mhvtl-load-modules.service vtllibrary@.service vtltape@.service} %pre #%{service_add_pre mhvtl.target mhvtl-load-modules.service vtllibrary@.service vtltape@.service} %preun /bin/systemctl stop mhvtl.target /bin/systemctl disable mhvtl.target #%{service_del_preun mhvtl.target mhvtl-load-modules.service vtllibrary@.service vtltape@.service} %build make MHVTL_HOME_PATH=%{mhvtl_home_dir} VERSION=%{version} EXTRAVERSION=%{minor} \ SYSTEMD_GENERATOR_DIR=%{_systemdgeneratordir} \ SYSTEMD_SERVICE_DIR=%{_unitdir} %install %make_install \ MHVTL_HOME_PATH=%{mhvtl_home_dir} VERSION=%{version} EXTRAVERSION=%{minor} LIBDIR=%{_libdir} \ SYSTEMD_GENERATOR_DIR=%{_systemdgeneratordir} \ SYSTEMD_SERVICE_DIR=%{_unitdir} install -d -m 755 %{buildroot}%{_sbindir} ln -s %{_sbindir}/service %{buildroot}/%{_sbindir}/rc%{name} install -d -m 755 %{buildroot}/var/lib/%{name} %clean %{__rm} -rf %{buildroot} %files %defattr(-, root, root, 0755) %doc INSTALL README etc/library_contents.sample %doc %{_mandir}/man1/mktape.1* %doc %{_mandir}/man1/edit_tape.1* %doc %{_mandir}/man1/vtlcmd.1* %doc %{_mandir}/man1/vtllibrary.1* %doc %{_mandir}/man1/vtltape.1* %doc %{_mandir}/man1/preload_tape.1* %doc %{_mandir}/man1/dump_tape.1* %doc %{_mandir}/man1/make_vtl_media.1* %doc %{_mandir}/man1/mhvtl_kernel_mod_build.1* %doc %{_mandir}/man1/tapeexerciser.1* %doc %{_mandir}/man1/update_device.conf.1* %doc %{_mandir}/man1/generate_device_conf.1* %doc %{_mandir}/man1/generate_library_contents.1* %doc %{_mandir}/man5/device.conf.5* %doc %{_mandir}/man5/mhvtl.conf.5* %doc %{_mandir}/man5/library_contents.5* %{_bindir}/vtlcmd %{_bindir}/mktape %{_bindir}/edit_tape %{_bindir}/dump_tape %{_bindir}/preload_tape %{_bindir}/tapeexerciser %{_bindir}/make_vtl_media %{_bindir}/mhvtl_kernel_mod_build %{_bindir}/update_device.conf %{_bindir}/generate_device_conf %{_bindir}/generate_library_contents %{_libdir}/libvtlscsi.so %{_libdir}/libvtlcart.so %{_firmwarepath}/mhvtl/mhvtl_kernel.tgz %dir %{_sysconfdir}/mhvtl %config(noreplace) %{_sysconfdir}/mhvtl/mhvtl.conf %config(noreplace) %{_sysconfdir}/mhvtl/device.conf %config(noreplace) %{_sysconfdir}/mhvtl/library_contents.10 %config(noreplace) %{_sysconfdir}/mhvtl/library_contents.30 %{_systemdgeneratordir}/mhvtl-device-conf-generator %{_unitdir}/mhvtl-load-modules.service %{_unitdir}/vtllibrary@.service %{_unitdir}/vtltape@.service %{_unitdir}/mhvtl.target %dir %{mhvtl_home_dir} %ghost %{mhvtl_home_dir}/* %defattr(755, root, root, 0755) %{_bindir}/vtltape %{_bindir}/vtllibrary %defattr(-, root, root, 755) %dir /opt/mhvtl/ %changelog * Fri Mar 10 2023 Mark Harvey - 1.7-1 - Updated to release 1.7-1 (2023-03-10). * Thu Mar 10 2022 Mark Harvey - 1.7-0 - Updated to release 1.7-0 (2022-03-10). * Thu Oct 07 2021 Mark Harvey - 1.6-4 - Updated to release 1.6-4 (2021-10-07). * Tue Mar 03 2020 Mark Harvey - 1.6-3 - Updated to release 1.6-3 (2020-03-10). * Sun Oct 06 2019 Mark Harvey - 1.6-2 - Updated to release 1.6-2 (2019-10-06). * Thu Mar 10 2016 Mark Harvey - 1.5-4 - Updated to release 1.5-4 (2016-03-10). * Tue Apr 14 2015 Mark Harvey - 1.5-3 - Updated to release 1.5-3 (2015-04-14). * Sun Sep 7 2014 Mark Harvey - 1.5-2 - Updated to release 1.5-2 (2014-09-04). * Sun Apr 13 2014 Mark Harvey - 1.5-0 - Updated to release 1.5-0 (2014-04-13). * Sun Oct 20 2013 Mark Harvey - 1.4-10 - Updated to release 1.4-10 (2013-10-20). * Thu Aug 29 2013 Mark Harvey - 1.4-9 - Updated to release 1.4-9 (2013-08-29). * Sat Jun 29 2013 Mark Harvey - 1.4-8 - Updated to release 1.4-8 (2013-06-29). * Fri Mar 22 2013 Mark Harvey - 1.4-7 - Updated to release 1.4-7 (2013-03-22). * Thu Jan 31 2013 Mark Harvey - 1.4-6 - Updated to release 1.4-6 (2013-01-31). * Sat Jan 12 2013 Mark Harvey - 1.4-5 - Updated to release 1.4-5 (2013-01-12). * Mon Aug 13 2012 Mark Harvey - 1.4-4 - Updated to release 1.4-4 (2012-09-13). * Wed Aug 8 2012 Mark Harvey - 1.4-1 - Updated to release 1.4-1 (2012-08-08). * Wed Aug 1 2012 Mark Harvey - 1.4-0 - Updated to release 1.4 (2012-08-01). - install using Makefile * Thu Jun 21 2012 Dag Wieers - 1.3-1 - Updated to release 1.3 (2012-06-15). * Thu Aug 05 2010 Dag Wieers - 0.18-11 - Initial build of the kmod package. ================================================ FILE: scripts/70-persistent-generic.rules ================================================ # Media changer ACTION=="add|change", KERNEL=="sg[0-9]*", SUBSYSTEM=="scsi_generic", PROGRAM=="/lib/udev/scsi_id --page=0x83 --whitelisted -u --device=$tempnode", RESULT=="?IBM_03584L32_XYZZY_A00*", NAME="sg9" # Persistent name starting from 'sg6' ACTION=="add|change", KERNEL=="sg[0-9]*", SUBSYSTEM=="scsi_generic", PROGRAM=="/lib/udev/scsi_id --page=0x83 --whitelisted --device=$tempnode", RESULT=="350223344ff000100", NAME="sg6" ACTION=="add|change", KERNEL=="sg[0-9]*", SUBSYSTEM=="scsi_generic", PROGRAM=="/lib/udev/scsi_id --page=0x83 --whitelisted --device=$tempnode", RESULT=="350223344ff000200", NAME="sg7" ACTION=="add|change", KERNEL=="sg[0-9]*", SUBSYSTEM=="scsi_generic", PROGRAM=="/lib/udev/scsi_id --page=0x83 --whitelisted --device=$tempnode", RESULT=="350223344ff000300", NAME="sg8" ================================================ FILE: scripts/70-persistent-tape.rules ================================================ ACTION=="add|change", KERNEL=="st[0-9]*", SUBSYSTEM=="scsi_tape", ENV{ID_SERIAL}=="350223344ff000100", NAME="st0" ACTION=="add|change", KERNEL=="nst[0-9]*", SUBSYSTEM=="scsi_tape", ENV{ID_SERIAL}=="350223344ff000100", NAME="nst0" # ACTION=="add|change", KERNEL=="st[0-9]*", SUBSYSTEM=="scsi_tape", ENV{ID_SERIAL}=="350223344ff000200", NAME="st1" ACTION=="add|change", KERNEL=="nst[0-9]*", SUBSYSTEM=="scsi_tape", ENV{ID_SERIAL}=="350223344ff000200", NAME="nst1" # ACTION=="add|change", KERNEL=="st[0-9]*", SUBSYSTEM=="scsi_tape", ENV{ID_SERIAL}=="350223344ff000300", NAME="st2" ACTION=="add|change", KERNEL=="nst[0-9]*", SUBSYSTEM=="scsi_tape", ENV{ID_SERIAL}=="350223344ff000300", NAME="nst2" ================================================ FILE: scripts/Makefile ================================================ CURDIR = "../" include ../config.mk RCFILE = update_device.conf $(RCFILE): $(RCFILE).in sed -e s'/@CONF_PATH@/$(CONFIG_PATH)/' $< > $@ .PHONY: rcfile rcfile: $(RCFILE) .PHONY: install install: install -m 700 $(RCFILE) $(DESTDIR)$(PREFIX)/bin/ .PHONY: uninstall uninstall: $(RM) $(DESTDIR)$(PREFIX)/bin/$(RCFILE) # ========== Cleaning ========== .PHONY: clean clean: rm -f $(RCFILE) .PHONY: distclean distclean: rm -f $(RCFILE) ================================================ FILE: scripts/NetBackup/drive_stats.pl ================================================ #!/usr/bin/perl -w # # To be called from NetBackup volmgr/bin/drive_unmount_notify # Parses output of "scsi_command -log_dump" # # To collect hard & soft errors from drive(s) along with informaiton # about bytes sent/received by initiator & bytes written/read to/from media # # Released under GPL2 license ############################################################################## ## Modules ############################################################################### use strict; use POSIX qw(locale_h); ############################################################################# ## Variables ############################################################################# my $locale = setlocale(LC_CTYPE); my $line; my $tape_usage = 0; my $read_errors = 0; my $write_errors = 0; my $last_drive = 0; my $last_drive_value = 0; my $write_bytes_initiator; my $write_bytes_media; my $read_bytes_initiator; my $read_bytes_media; my $total_corrective_read_errors; my $total_read_retries; my $total_corrective_write_errors; my $total_write_retries; print "\n ===============\n"; while($line = <>) { $line =~ s/^M//g; # Strip any cr/lf to lf chomp($line); # Print header information if ($line =~ /^Inquiry/) { print $line . "\n"; } if ($line =~ /path/) { print $line . "\n"; } if ($line =~ /Serial_Number/) { print $line . "\n\n"; } # Search for Write Error log page (0x02) if ($line =~ /^\*\*\*Write\s+Error\s+Log\s+Page/) { $tape_usage = 0; $write_errors = 1; $read_errors = 0; } # Search for Read Error log page (0x03) if ($line =~ /^\*\*\*Read\s+Error\s+Log\s+Page/) { $tape_usage = 0; $write_errors = 0; $read_errors = 1; } # Search for Tape Usage log page (0x0C) if ($line =~ /^\*\*\*Log\s+page\s+\((.*)\)/) { if ($1 =~ /0x0c/) { # Found Tape Usage log page $tape_usage = 1; $write_errors = 0; $read_errors = 0; } else { # Not a log page we're interested in - reset all $tape_usage = 0; $write_errors = 0; $read_errors = 0; } } # last loop matched key.. This time around is the value if ($last_drive) { $last_drive_value = 1; } if ($line =~ /attribute\s0x020[abcd]/) { # Key match $last_drive = 1; $last_drive_value = 0; } if ($tape_usage) { if ($line =~ /parameter\s+code:\s+0x0000,\s+value:\s+(.*)/) { $write_bytes_initiator = hex("$1"); print "Bytes written by initiator : $write_bytes_initiator\n"; } if ($line =~ /parameter\s+code:\s+0x0001,\s+value:\s+(.*)/) { $write_bytes_media = hex("$1"); print "Bytes written to media : $write_bytes_media\n"; } if ($line =~ /parameter\s+code:\s+0x0002,\s+value:\s+(.*)/) { $read_bytes_media = hex("$1"); print "Bytes read from media : $read_bytes_media\n"; } if ($line =~ /parameter\s+code:\s+0x0003,\s+value:\s+(.*)/) { $read_bytes_initiator = hex("$1"); print "Bytes read by initiator : $read_bytes_initiator\n"; } } if ($read_errors) { if ($line =~ /parameter\s+code:\s+0x0003,\s+value:\s+(.*)/) { $total_corrective_read_errors = hex("$1"); print "Total Corrective read errors : $total_corrective_read_errors\n"; } if ($line =~ /parameter\s+code:\s+0x0004,\s+value:\s+(.*)/) { $total_read_retries = hex("$1"); print "Total read retries : $total_read_retries\n"; } } if ($write_errors) { if ($line =~ /parameter\s+code:\s+0x0003,\s+value:\s+(.*)/) { $total_corrective_write_errors = hex("$1"); print "Total Corrective write errors : $total_corrective_write_errors\n"; } if ($line =~ /parameter\s+code:\s+0x0004,\s+value:\s+(.*)/) { $total_write_retries = hex("$1"); print "Total write retries : $total_write_retries\n"; } } if ($last_drive_value) { print "Media previously mounted in : $line\n"; $last_drive_value = 0; $last_drive = 0; } } ================================================ FILE: scripts/NetBackup/vlt_endeject_notify.pl ================================================ #!/usr/bin/perl # vlt_endeject_notify # # This script is called after the vault job completes the eject processing for # the media to be vaulted. # # This script will empty the MAP after a vault eject and insert any tapes # "returning" from the vault into # # Netbackup provides 4 parameters to the script when executed from # a vault profile: # 1 = $robot_number - robot number # 2 = $vault_name - logical vault name # 3 = $vault_profile - profile name # 4 = $vault_session_id - vault session id # This script: # must be executable by the root user # must exit with 0 upon successful completion # must exit with a non zero error code upon failure # # CAUTION: writing anything to stdout or stderr will cause problems in the # calling program. # # This script closes STDOUT and STDERR and opens them as a pipe to the # mail command. # Any errors encountered will be in the output that is emailed. use strict; use warnings; my ($robot_number, $vault_name, $vault_profile, $vault_session_id) = @ARGV; my $mail_to = join ",", qw( admin1@example.com admin2@example.com ); my $vtlcmd_binary = "/usr/bin/vtlcmd"; my $vmquery_binary = "/usr/openv/volmgr/bin/vmquery"; my $vmupdate_binary = "/usr/openv/volmgr/bin/vmupdate"; my $robot_type = "tld"; # Netbackup robot type my $mhvtl_robot_number = 50; my $mail_subject = "Vault Eject for $vault_name"; my $mail_binary = find_mail_binary(); my $scratch_pool = "Scratch"; # Open STDOUT and STDERR as pipes to the mail command only if not running in an # interactive terminal if (not -t STDIN and not -t STDOUT) { close(STDOUT); close(STDERR); open(STDOUT, "|-", qq{$mail_binary -s "$mail_subject" "$mail_to"} ); open(STDERR, ">&", "STDOUT"); } list_map(); open_map(); empty_map(); insert_tapes(); close_map(); inventory_robot(); sub empty_map { print "Emptying MAP: "; system("$vtlcmd_binary $mhvtl_robot_number empty map"); if ($?) { die "Error emptying MAP: $!"; } } sub list_map { print "Current MAP "; system("$vtlcmd_binary $mhvtl_robot_number list map"); if ($?) { die "Error listing contents of MAP: $@"; } } sub open_map { print "Opening MAP: "; system("$vtlcmd_binary $mhvtl_robot_number open map"); if ($?) { die "Error opening MAP: $!"; } } sub close_map { print "Closing MAP: "; system("$vtlcmd_binary $mhvtl_robot_number close map"); if ($?) { die "Error closing MAP: $!"; } } sub inventory_robot { print "Inventorying robot:\n"; system("$vmupdate_binary -rt $robot_type -rn $robot_number -full -empty_map -use_barcode_rules"); if ($?) { die "Error inventorying robot: $!"; } } sub insert_tapes { # Get all tapes that are "outside" the library and have been returned to the # scratch volume pool my @returned_tapes = map { (split /\s+/, $_)[3] } grep { # This regex filters all lines that don't have the 12th field (Volume Pool) # as the Scratch Volume Pool /^(?:[^\s]+\s+){11}$scratch_pool/ } `$vmquery_binary -rt NONE -W`; if ($?) { die "Error running vmquery: $!"; } foreach my $tape_to_insert (@returned_tapes) { print "Loading Tape $tape_to_insert into MAP: "; system("$vtlcmd_binary $mhvtl_robot_number load map $tape_to_insert"); if ($?) { die "Error inserting tape into MAP: $!"; } } } # Taken from the sample netbackup vlt_endeject_notify script sub find_mail_binary { foreach my $mail_command ( qw( mailx Mail mail ) ) { foreach my $mail_dir ( qw( /usr/bin /usr/ucb /usr/sbin /bin /sbin ) ) { return "$mail_dir/$mail_command" if -e "$mail_dir/$mail_command"; } } die "No mail binary found!"; } ================================================ FILE: scripts/build-persistent-tape-rules.sh ================================================ #!/bin/bash # To 'view' what are valid fields we can use - Pick a tape drive you are interested in. using /dev/st0 here. # # udevadm info --name=/dev/st0 --query=all # P: /devices/pseudo_9/adapter0/host4/target4:0:1/4:0:1:0/scsi_tape/st0 # N: st0 # S: tape/by-id/scsi-350223344ab000100 # E: DEVLINKS=/dev/tape/by-id/scsi-350223344ab000100 # E: DEVNAME=/dev/st0 # E: DEVPATH=/devices/pseudo_9/adapter0/host4/target4:0:1/4:0:1:0/scsi_tape/st0 # E: ID_BUS=scsi # E: ID_MODEL=ULT3580-TD5 # E: ID_MODEL_ENC=ULT3580-TD5\x20\x20\x20\x20\x20 # E: ID_REVISION=0105 # E: ID_SCSI=1 # E: ID_SCSI_SERIAL=XYZZY_A1 # E: ID_SERIAL=350223344ab000100 # E: ID_SERIAL_SHORT=50223344ab000100 # E: ID_TYPE=tape # E: ID_VENDOR=IBM # E: ID_VENDOR_ENC=IBM\x20\x20\x20\x20\x20 # E: ID_WWN=0x50223344ab000100 # E: ID_WWN_WITH_EXTENSION=0x50223344ab000100 # E: MAJOR=9 # E: MINOR=0 # E: SUBSYSTEM=scsi_tape # E: USEC_INITIALIZED=421748 # E: nodmraid=1 # # Anything beginning with 'E' are environmental and can be used by/for 'ENV{}' for SN in `grep ^Drive -A6 /etc/mhvtl/device.conf | awk '/NAA:/ {print $2}' | sed -e "s/://g" -e "s/^[13]0/50/g"` do echo "ACTION==\"add|change\", KERNEL==\"nst*\", ENV{SUBSYSTEM}==\"scsi_tape\", ENV{ID_SERIAL_SHORT}==\"$SN\", SYMLINK+=\"tape/by-path/%E{ID_VENDOR}-%E{ID_MODEL}-%E{ID_SCSI_SERIAL}-nst\", MODE=\"0666\"" done ================================================ FILE: scripts/centos_configure.sh ================================================ #!/bin/sh ### Install script for auto install mhvtl on CentOS Linux 6.2 i386/x86_64 ### Version: 1.0.0 ### made by patrick ru ### mail: patrick.ru@hotmail.com ### Date: 2012.Mar.18 # prepair : pre-install centos linux 6.2 with minimal installation. # update the system yum update -y # close the selinux and the firewall chkconfig iptables off service iptables stop sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config /usr/sbin/setenforce 0 # install Git yum install -y git # install supported Rpms yum install -y mc ntp gcc gcc-c++ make kernel-devel zlib-devel sg3_utils lsscsi mt-st mtx lzo lzo-devel perl-Config-General # Create required directories mkdir -p /opt/mhvtl mkdir -p /etc/mhvtl # install mhvtl mkdir -p /usr/src/mhvtl cd /usr/src/mhvtl git init git pull http://github.com/markh794/mhvtl.git make distclean cd kernel/ make && make install cd .. make && make install systemctl enable mhvtl systemctl start mhvtl # install tgt mkdir /etc/tgt mkdir /usr/src/tgt cd /usr/src/tgt git init git pull http://github.com/fujita/tgt.git make && make install /usr/sbin/tgtd -d 1 # install mhvtl-gui yum install -y httpd php sudo sysstat cp /etc/sudoers /etc/sudoers.old sed -i '/Defaults requiretty/s/^/#/' /etc/sudoers echo "apache ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers chkconfig httpd on mkdir /var/www/html/mhvtl cd /var/www/html/mhvtl git init git pull http://github.com/niadev67/mhvtl-gui.git chown -R apache:apache ./ service httpd start touch /var/www/html/mhvtl/ENABLE_TGTD_SCSI_TARGET sh scripts/auto.iscsi.config.stgt.sh echo installation has finished! please use mhvtl-gui to install and config tgtd after config the mhvtl for running! ================================================ FILE: scripts/checkarch.sh ================================================ #!/bin/sh arch=`gcc -dumpmachine` case $arch in `echo $arch | grep x86_64`) echo -m64 ;; `echo $arch | grep "i[3-6]86"`) echo -m32 ;; *) echo ' Failed to parse your architecture. Please run $ make check32 or $ make check64 manually. ' exit 1 ;; esac ================================================ FILE: scripts/checkpatch.pl ================================================ #!/usr/bin/perl -w # (c) 2001, Dave Jones. (the file handling bit) # (c) 2005, Joel Schopp (the ugly bit) # (c) 2007,2008, Andy Whitcroft (new conditions, test suite) # (c) 2008-2010 Andy Whitcroft # Licensed under the terms of the GNU GPL License version 2 use strict; my $P = $0; $P =~ s@.*/@@g; my $V = '0.31'; use Getopt::Long qw(:config no_auto_abbrev); my $quiet = 0; my $tree = 0; my $chk_signoff = 1; my $chk_patch = 1; my $tst_only; my $emacs = 0; my $terse = 0; my $file = 0; my $check = 0; my $summary = 1; my $mailback = 0; my $summary_file = 0; my $root; my %debug; my $help = 0; sub help { my ($exitcode) = @_; print << "EOM"; Usage: $P [OPTION]... [FILE]... Version: $V Options: -q, --quiet quiet --no-tree run without a kernel tree --no-signoff do not check for 'Signed-off-by' line --patch treat FILE as patchfile (default) --emacs emacs compile window format --terse one line per report -f, --file treat FILE as regular source file --subjective, --strict enable more subjective tests --root=PATH PATH to the kernel tree root --no-summary suppress the per-file summary --mailback only produce a report in case of warnings/errors --summary-file include the filename in summary --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of 'values', 'possible', 'type', and 'attr' (default is all off) --test-only=WORD report only warnings/errors containing WORD literally -h, --help, --version display this help and exit When FILE is - read standard input. EOM exit($exitcode); } GetOptions( 'q|quiet+' => \$quiet, 'tree!' => \$tree, 'signoff!' => \$chk_signoff, 'patch!' => \$chk_patch, 'emacs!' => \$emacs, 'terse!' => \$terse, 'f|file!' => \$file, 'subjective!' => \$check, 'strict!' => \$check, 'root=s' => \$root, 'summary!' => \$summary, 'mailback!' => \$mailback, 'summary-file!' => \$summary_file, 'debug=s' => \%debug, 'test-only=s' => \$tst_only, 'h|help' => \$help, 'version' => \$help ) or help(1); help(0) if ($help); my $exit = 0; if ($#ARGV < 0) { print "$P: no input files\n"; exit(1); } my $dbg_values = 0; my $dbg_possible = 0; my $dbg_type = 0; my $dbg_attr = 0; for my $key (keys %debug) { ## no critic eval "\${dbg_$key} = '$debug{$key}';"; die "$@" if ($@); } my $rpt_cleaners = 0; if ($terse) { $emacs = 1; $quiet++; } if ($tree) { if (defined $root) { if (!top_of_kernel_tree($root)) { die "$P: $root: --root does not point at a valid tree\n"; } } else { if (top_of_kernel_tree('.')) { $root = '.'; } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && top_of_kernel_tree($1)) { $root = $1; } } if (!defined $root) { print "Must be run from the top-level dir. of a kernel tree\n"; exit(2); } } my $emitted_corrupt = 0; our $Ident = qr{ [A-Za-z_][A-Za-z\d_]* (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* }x; our $Storage = qr{extern|static|asmlinkage}; our $Sparse = qr{ __user| __kernel| __force| __iomem| __must_check| __init_refok| __kprobes| __ref }x; # Notes to $Attribute: # We need \b after 'init' otherwise 'initconst' will cause a false positive in a check our $Attribute = qr{ const| __percpu| __nocast| __safe| __bitwise__| __packed__| __packed2__| __naked| __maybe_unused| __always_unused| __noreturn| __used| __cold| __noclone| __deprecated| __read_mostly| __kprobes| __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)| ____cacheline_aligned| ____cacheline_aligned_in_smp| ____cacheline_internodealigned_in_smp| __weak }x; our $Modifier; our $Inline = qr{inline|__always_inline|noinline}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; our $Constant = qr{(?:[0-9]+|0x[0-9a-fA-F]+)[UL]*}; our $Assignment = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)}; our $Compare = qr{<=|>=|==|!=|<|>}; our $Operators = qr{ <=|>=|==|!=| =>|->|<<|>>|<|>|!|~| &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|% }x; our $NonptrType; our $Type; our $Declare; our $UTF8 = qr { [\x09\x0A\x0D\x20-\x7E] # ASCII | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 }x; our $typeTypedefs = qr{(?x: (?:__)?(?:u|s|be|le)(?:8|16|32|64)| atomic_t )}; our $logFunctions = qr{(?x: printk| [a-z]+_(emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)| WARN| panic| MODULE_[A-Z_]+ )}; our @typeList = ( qr{void}, qr{(?:unsigned\s+)?char}, qr{(?:unsigned\s+)?short}, qr{(?:unsigned\s+)?int}, qr{(?:unsigned\s+)?long}, qr{(?:unsigned\s+)?long\s+int}, qr{(?:unsigned\s+)?long\s+long}, qr{(?:unsigned\s+)?long\s+long\s+int}, qr{unsigned}, qr{float}, qr{double}, qr{bool}, qr{struct\s+$Ident}, qr{union\s+$Ident}, qr{enum\s+$Ident}, qr{${Ident}_t}, qr{${Ident}_handler}, qr{${Ident}_handler_fn}, ); our @modifierList = ( qr{fastcall}, ); our $allowed_asm_includes = qr{(?x: irq| memory )}; # memory.h: ARM has a custom one sub build_types { my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)"; $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; $NonptrType = qr{ (?:$Modifier\s+|const\s+)* (?: (?:typeof|__typeof__)\s*\(\s*\**\s*$Ident\s*\)| (?:$typeTypedefs\b)| (?:${all}\b) ) (?:\s+$Modifier|\s+const)* }x; $Type = qr{ $NonptrType (?:[\s\*]+\s*const|[\s\*]+|(?:\s*\[\s*\])+)? (?:\s+$Inline|\s+$Modifier)* }x; $Declare = qr{(?:$Storage\s+)?$Type}; } build_types(); $chk_signoff = 0 if ($file); my @dep_includes = (); my @dep_functions = (); my $removal = "Documentation/feature-removal-schedule.txt"; if ($tree && -f "$root/$removal") { open(my $REMOVE, '<', "$root/$removal") || die "$P: $removal: open failed - $!\n"; while (<$REMOVE>) { if (/^Check:\s+(.*\S)/) { for my $entry (split(/[, ]+/, $1)) { if ($entry =~ m@include/(.*)@) { push(@dep_includes, $1); } elsif ($entry !~ m@/@) { push(@dep_functions, $entry); } } } } close($REMOVE); } my @rawlines = (); my @lines = (); my $vname; for my $filename (@ARGV) { my $FILE; if ($file) { open($FILE, '-|', "diff -u /dev/null $filename") || die "$P: $filename: diff failed - $!\n"; } elsif ($filename eq '-') { open($FILE, '<&STDIN'); } else { open($FILE, '<', "$filename") || die "$P: $filename: open failed - $!\n"; } if ($filename eq '-') { $vname = 'Your patch'; } else { $vname = $filename; } while (<$FILE>) { chomp; push(@rawlines, $_); } close($FILE); if (!process($filename)) { $exit = 1; } @rawlines = (); @lines = (); } exit($exit); sub top_of_kernel_tree { my ($root) = @_; my @tree_check = ( "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", "README", "Documentation", "arch", "include", "drivers", "fs", "init", "ipc", "kernel", "lib", "scripts", ); foreach my $check (@tree_check) { if (! -e $root . '/' . $check) { return 0; } } return 1; } sub expand_tabs { my ($str) = @_; my $res = ''; my $n = 0; for my $c (split(//, $str)) { if ($c eq "\t") { $res .= ' '; $n++; for (; ($n % 8) != 0; $n++) { $res .= ' '; } next; } $res .= $c; $n++; } return $res; } sub copy_spacing { (my $res = shift) =~ tr/\t/ /c; return $res; } sub line_stats { my ($line) = @_; # Drop the diff line leader and expand tabs $line =~ s/^.//; $line = expand_tabs($line); # Pick the indent from the front of the line. my ($white) = ($line =~ /^(\s*)/); return (length($line), length($white)); } my $sanitise_quote = ''; sub sanitise_line_reset { my ($in_comment) = @_; if ($in_comment) { $sanitise_quote = '*/'; } else { $sanitise_quote = ''; } } sub sanitise_line { my ($line) = @_; my $res = ''; my $l = ''; my $qlen = 0; my $off = 0; my $c; # Always copy over the diff marker. $res = substr($line, 0, 1); for ($off = 1; $off < length($line); $off++) { $c = substr($line, $off, 1); # Comments we are wacking completly including the begin # and end, all to $;. if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { $sanitise_quote = '*/'; substr($res, $off, 2, "$;$;"); $off++; next; } if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { $sanitise_quote = ''; substr($res, $off, 2, "$;$;"); $off++; next; } if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { $sanitise_quote = '//'; substr($res, $off, 2, $sanitise_quote); $off++; next; } # A \ in a string means ignore the next character. if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && $c eq "\\") { substr($res, $off, 2, 'XX'); $off++; next; } # Regular quotes. if ($c eq "'" || $c eq '"') { if ($sanitise_quote eq '') { $sanitise_quote = $c; substr($res, $off, 1, $c); next; } elsif ($sanitise_quote eq $c) { $sanitise_quote = ''; } } #print "c<$c> SQ<$sanitise_quote>\n"; if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { substr($res, $off, 1, $;); } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { substr($res, $off, 1, $;); } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { substr($res, $off, 1, 'X'); } else { substr($res, $off, 1, $c); } } if ($sanitise_quote eq '//') { $sanitise_quote = ''; } # The pathname on a #include may be surrounded by '<' and '>'. if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { my $clean = 'X' x length($1); $res =~ s@\<.*\>@<$clean>@; # The whole of a #error is a string. } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { my $clean = 'X' x length($1); $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; } return $res; } sub ctx_statement_block { my ($linenr, $remain, $off) = @_; my $line = $linenr - 1; my $blk = ''; my $soff = $off; my $coff = $off - 1; my $coff_set = 0; my $loff = 0; my $type = ''; my $level = 0; my @stack = (); my $p; my $c; my $len = 0; my $remainder; while (1) { @stack = (['', 0]) if ($#stack == -1); #warn "CSB: blk<$blk> remain<$remain>\n"; # If we are about to drop off the end, pull in more # context. if ($off >= $len) { for (; $remain > 0; $line++) { last if (!defined $lines[$line]); next if ($lines[$line] =~ /^-/); $remain--; $loff = $len; $blk .= $lines[$line] . "\n"; $len = length($blk); $line++; last; } # Bail if there is no further context. #warn "CSB: blk<$blk> off<$off> len<$len>\n"; if ($off >= $len) { last; } } $p = $c; $c = substr($blk, $off, 1); $remainder = substr($blk, $off); #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; # Handle nested #if/#else. if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { push(@stack, [ $type, $level ]); } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { ($type, $level) = @{$stack[$#stack - 1]}; } elsif ($remainder =~ /^#\s*endif\b/) { ($type, $level) = @{pop(@stack)}; } # Statement ends at the ';' or a close '}' at the # outermost level. if ($level == 0 && $c eq ';') { last; } # An else is really a conditional as long as its not else if if ($level == 0 && $coff_set == 0 && (!defined($p) || $p =~ /(?:\s|\}|\+)/) && $remainder =~ /^(else)(?:\s|{)/ && $remainder !~ /^else\s+if\b/) { $coff = $off + length($1) - 1; $coff_set = 1; #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; } if (($type eq '' || $type eq '(') && $c eq '(') { $level++; $type = '('; } if ($type eq '(' && $c eq ')') { $level--; $type = ($level != 0)? '(' : ''; if ($level == 0 && $coff < $soff) { $coff = $off; $coff_set = 1; #warn "CSB: mark coff<$coff>\n"; } } if (($type eq '' || $type eq '{') && $c eq '{') { $level++; $type = '{'; } if ($type eq '{' && $c eq '}') { $level--; $type = ($level != 0)? '{' : ''; if ($level == 0) { if (substr($blk, $off + 1, 1) eq ';') { $off++; } last; } } $off++; } # We are truly at the end, so shuffle to the next line. if ($off == $len) { $loff = $len + 1; $line++; $remain--; } my $statement = substr($blk, $soff, $off - $soff + 1); my $condition = substr($blk, $soff, $coff - $soff + 1); #warn "STATEMENT<$statement>\n"; #warn "CONDITION<$condition>\n"; #print "coff<$coff> soff<$off> loff<$loff>\n"; return ($statement, $condition, $line, $remain + 1, $off - $loff + 1, $level); } sub statement_lines { my ($stmt) = @_; # Strip the diff line prefixes and rip blank lines at start and end. $stmt =~ s/(^|\n)./$1/g; $stmt =~ s/^\s*//; $stmt =~ s/\s*$//; my @stmt_lines = ($stmt =~ /\n/g); return $#stmt_lines + 2; } sub statement_rawlines { my ($stmt) = @_; my @stmt_lines = ($stmt =~ /\n/g); return $#stmt_lines + 2; } sub statement_block_size { my ($stmt) = @_; $stmt =~ s/(^|\n)./$1/g; $stmt =~ s/^\s*{//; $stmt =~ s/}\s*$//; $stmt =~ s/^\s*//; $stmt =~ s/\s*$//; my @stmt_lines = ($stmt =~ /\n/g); my @stmt_statements = ($stmt =~ /;/g); my $stmt_lines = $#stmt_lines + 2; my $stmt_statements = $#stmt_statements + 1; if ($stmt_lines > $stmt_statements) { return $stmt_lines; } else { return $stmt_statements; } } sub ctx_statement_full { my ($linenr, $remain, $off) = @_; my ($statement, $condition, $level); my (@chunks); # Grab the first conditional/block pair. ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); #print "F: c<$condition> s<$statement> remain<$remain>\n"; push(@chunks, [ $condition, $statement ]); if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { return ($level, $linenr, @chunks); } # Pull in the following conditional/block pairs and see if they # could continue the statement. for (;;) { ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); #print "C: c<$condition> s<$statement> remain<$remain>\n"; last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); #print "C: push\n"; push(@chunks, [ $condition, $statement ]); } return ($level, $linenr, @chunks); } sub ctx_block_get { my ($linenr, $remain, $outer, $open, $close, $off) = @_; my $line; my $start = $linenr - 1; my $blk = ''; my @o; my @c; my @res = (); my $level = 0; my @stack = ($level); for ($line = $start; $remain > 0; $line++) { next if ($rawlines[$line] =~ /^-/); $remain--; $blk .= $rawlines[$line]; # Handle nested #if/#else. if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { push(@stack, $level); } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { $level = $stack[$#stack - 1]; } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { $level = pop(@stack); } foreach my $c (split(//, $lines[$line])) { ##print "C<$c>L<$level><$open$close>O<$off>\n"; if ($off > 0) { $off--; next; } if ($c eq $close && $level > 0) { $level--; last if ($level == 0); } elsif ($c eq $open) { $level++; } } if (!$outer || $level <= 1) { push(@res, $rawlines[$line]); } last if ($level == 0); } return ($level, @res); } sub ctx_block_outer { my ($linenr, $remain) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); return @r; } sub ctx_block { my ($linenr, $remain) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); return @r; } sub ctx_statement { my ($linenr, $remain, $off) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); return @r; } sub ctx_block_level { my ($linenr, $remain) = @_; return ctx_block_get($linenr, $remain, 0, '{', '}', 0); } sub ctx_statement_level { my ($linenr, $remain, $off) = @_; return ctx_block_get($linenr, $remain, 0, '(', ')', $off); } sub ctx_locate_comment { my ($first_line, $end_line) = @_; # Catch a comment on the end of the line itself. my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); return $current_comment if (defined $current_comment); # Look through the context and try and figure out if there is a # comment. my $in_comment = 0; $current_comment = ''; for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { my $line = $rawlines[$linenr - 1]; #warn " $line\n"; if ($linenr == $first_line and $line =~ m@^.\s*\*@) { $in_comment = 1; } if ($line =~ m@/\*@) { $in_comment = 1; } if (!$in_comment && $current_comment ne '') { $current_comment = ''; } $current_comment .= $line . "\n" if ($in_comment); if ($line =~ m@\*/@) { $in_comment = 0; } } chomp($current_comment); return($current_comment); } sub ctx_has_comment { my ($first_line, $end_line) = @_; my $cmt = ctx_locate_comment($first_line, $end_line); ##print "LINE: $rawlines[$end_line - 1 ]\n"; ##print "CMMT: $cmt\n"; return ($cmt ne ''); } sub raw_line { my ($linenr, $cnt) = @_; my $offset = $linenr - 1; $cnt++; my $line; while ($cnt) { $line = $rawlines[$offset++]; next if (defined($line) && $line =~ /^-/); $cnt--; } return $line; } sub cat_vet { my ($vet) = @_; my ($res, $coded); $res = ''; while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { $res .= $1; if ($2 ne '') { $coded = sprintf("^%c", unpack('C', $2) + 64); $res .= $coded; } } $res =~ s/$/\$/; return $res; } my $av_preprocessor = 0; my $av_pending; my @av_paren_type; my $av_pend_colon; sub annotate_reset { $av_preprocessor = 0; $av_pending = '_'; @av_paren_type = ('E'); $av_pend_colon = 'O'; } sub annotate_values { my ($stream, $type) = @_; my $res; my $var = '_' x length($stream); my $cur = $stream; print "$stream\n" if ($dbg_values > 1); while (length($cur)) { @av_paren_type = ('E') if ($#av_paren_type < 0); print " <" . join('', @av_paren_type) . "> <$type> <$av_pending>" if ($dbg_values > 1); if ($cur =~ /^(\s+)/o) { print "WS($1)\n" if ($dbg_values > 1); if ($1 =~ /\n/ && $av_preprocessor) { $type = pop(@av_paren_type); $av_preprocessor = 0; } } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { print "CAST($1)\n" if ($dbg_values > 1); push(@av_paren_type, $type); $type = 'C'; } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { print "DECLARE($1)\n" if ($dbg_values > 1); $type = 'T'; } elsif ($cur =~ /^($Modifier)\s*/) { print "MODIFIER($1)\n" if ($dbg_values > 1); $type = 'T'; } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { print "DEFINE($1,$2)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); if ($2 ne '') { $av_pending = 'N'; } $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { print "UNDEF($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { print "PRE_START($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); push(@av_paren_type, $type); $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { print "PRE_RESTART($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $av_paren_type[$#av_paren_type]); $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:endif))/o) { print "PRE_END($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; # Assume all arms of the conditional end as this # one does, and continue as if the #endif was not here. pop(@av_paren_type); push(@av_paren_type, $type); $type = 'E'; } elsif ($cur =~ /^(\\\n)/o) { print "PRECONT($1)\n" if ($dbg_values > 1); } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { print "ATTR($1)\n" if ($dbg_values > 1); $av_pending = $type; $type = 'N'; } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { print "SIZEOF($1)\n" if ($dbg_values > 1); if (defined $2) { $av_pending = 'V'; } $type = 'N'; } elsif ($cur =~ /^(if|while|for)\b/o) { print "COND($1)\n" if ($dbg_values > 1); $av_pending = 'E'; $type = 'N'; } elsif ($cur =~/^(case)/o) { print "CASE($1)\n" if ($dbg_values > 1); $av_pend_colon = 'C'; $type = 'N'; } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { print "KEYWORD($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(\()/o) { print "PAREN('$1')\n" if ($dbg_values > 1); push(@av_paren_type, $av_pending); $av_pending = '_'; $type = 'N'; } elsif ($cur =~ /^(\))/o) { my $new_type = pop(@av_paren_type); if ($new_type ne '_') { $type = $new_type; print "PAREN('$1') -> $type\n" if ($dbg_values > 1); } else { print "PAREN('$1')\n" if ($dbg_values > 1); } } elsif ($cur =~ /^($Ident)\s*\(/o) { print "FUNC($1)\n" if ($dbg_values > 1); $type = 'V'; $av_pending = 'V'; } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { if (defined $2 && $type eq 'C' || $type eq 'T') { $av_pend_colon = 'B'; } elsif ($type eq 'E') { $av_pend_colon = 'L'; } print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); $type = 'V'; } elsif ($cur =~ /^($Ident|$Constant)/o) { print "IDENT($1)\n" if ($dbg_values > 1); $type = 'V'; } elsif ($cur =~ /^($Assignment)/o) { print "ASSIGN($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~/^(;|{|})/) { print "END($1)\n" if ($dbg_values > 1); $type = 'E'; $av_pend_colon = 'O'; } elsif ($cur =~/^(,)/) { print "COMMA($1)\n" if ($dbg_values > 1); $type = 'C'; } elsif ($cur =~ /^(\?)/o) { print "QUESTION($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(:)/o) { print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); substr($var, length($res), 1, $av_pend_colon); if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { $type = 'E'; } else { $type = 'N'; } $av_pend_colon = 'O'; } elsif ($cur =~ /^(\[)/o) { print "CLOSE($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { my $variant; print "OPV($1)\n" if ($dbg_values > 1); if ($type eq 'V') { $variant = 'B'; } else { $variant = 'U'; } substr($var, length($res), 1, $variant); $type = 'N'; } elsif ($cur =~ /^($Operators)/o) { print "OP($1)\n" if ($dbg_values > 1); if ($1 ne '++' && $1 ne '--') { $type = 'N'; } } elsif ($cur =~ /(^.)/o) { print "C($1)\n" if ($dbg_values > 1); } if (defined $1) { $cur = substr($cur, length($1)); $res .= $type x length($1); } } return ($res, $var); } sub possible { my ($possible, $line) = @_; my $notPermitted = qr{(?: ^(?: $Modifier| $Storage| $Type| DEFINE_\S+ )$| ^(?: goto| return| case| else| asm|__asm__| do )(?:\s|$)| ^(?:typedef|struct|enum)\b )}x; warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); if ($possible !~ $notPermitted) { # Check for modifiers. $possible =~ s/\s*$Storage\s*//g; $possible =~ s/\s*$Sparse\s*//g; if ($possible =~ /^\s*$/) { } elsif ($possible =~ /\s/) { $possible =~ s/\s*$Type\s*//g; for my $modifier (split(' ', $possible)) { if ($modifier !~ $notPermitted) { warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); push(@modifierList, $modifier); } } } else { warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); push(@typeList, $possible); } build_types(); } else { warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); } } my $prefix = ''; sub report { if (defined $tst_only && $_[0] !~ /\Q$tst_only\E/) { return 0; } my $line = $prefix . $_[0]; $line = (split('\n', $line))[0] . "\n" if ($terse); push(our @report, $line); return 1; } sub report_dump { our @report; } sub ERROR { if (report("ERROR: $_[0]\n")) { our $clean = 0; our $cnt_error++; } } sub WARN { if (report("WARNING: $_[0]\n")) { our $clean = 0; our $cnt_warn++; } } sub CHK { if ($check && report("CHECK: $_[0]\n")) { our $clean = 0; our $cnt_chk++; } } sub check_absolute_file { my ($absolute, $herecurr) = @_; my $file = $absolute; ##print "absolute<$absolute>\n"; # See if any suffix of this path is a path within the tree. while ($file =~ s@^[^/]*/@@) { if (-f "$root/$file") { ##print "file<$file>\n"; last; } } if (! -f _) { return 0; } # It is, so see if the prefix is acceptable. my $prefix = $absolute; substr($prefix, -length($file)) = ''; ##print "prefix<$prefix>\n"; if ($prefix ne ".../") { WARN("use relative pathname instead of absolute in changelog text\n" . $herecurr); } } sub process { my $filename = shift; my $linenr=0; my $prevline=""; my $prevrawline=""; my $stashline=""; my $stashrawline=""; my $length; my $indent; my $previndent=0; my $stashindent=0; our $clean = 1; my $signoff = 0; my $is_patch = 0; our @report = (); our $cnt_lines = 0; our $cnt_error = 0; our $cnt_warn = 0; our $cnt_chk = 0; # Trace the real file/line as we go. my $realfile = ''; my $realline = 0; my $realcnt = 0; my $here = ''; my $in_comment = 0; my $comment_edge = 0; my $first_line = 0; my $p1_prefix = ''; my $prev_values = 'E'; # suppression flags my %suppress_ifbraces; my %suppress_whiletrailers; my %suppress_export; # Pre-scan the patch sanitizing the lines. # Pre-scan the patch looking for any __setup documentation. # my @setup_docs = (); my $setup_docs = 0; sanitise_line_reset(); my $line; foreach my $rawline (@rawlines) { $linenr++; $line = $rawline; if ($rawline=~/^\+\+\+\s+(\S+)/) { $setup_docs = 0; if ($1 =~ m@Documentation/kernel-parameters.txt$@) { $setup_docs = 1; } #next; } if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $realline=$1-1; if (defined $2) { $realcnt=$3+1; } else { $realcnt=1+1; } $in_comment = 0; # Guestimate if this is a continuing comment. Run # the context looking for a comment "edge". If this # edge is a close comment then we must be in a comment # at context start. my $edge; my $cnt = $realcnt; for (my $ln = $linenr + 1; $cnt > 0; $ln++) { next if (defined $rawlines[$ln - 1] && $rawlines[$ln - 1] =~ /^-/); $cnt--; #print "RAW<$rawlines[$ln - 1]>\n"; last if (!defined $rawlines[$ln - 1]); if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { ($edge) = $1; last; } } if (defined $edge && $edge eq '*/') { $in_comment = 1; } # Guestimate if this is a continuing comment. If this # is the start of a diff block and this line starts # ' *' then it is very likely a comment. if (!defined $edge && $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) { $in_comment = 1; } ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; sanitise_line_reset($in_comment); } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { # Standardise the strings and chars within the input to # simplify matching -- only bother with positive lines. $line = sanitise_line($rawline); } push(@lines, $line); if ($realcnt > 1) { $realcnt-- if ($line =~ /^(?:\+| |$)/); } else { $realcnt = 0; } #print "==>$rawline\n"; #print "-->$line\n"; if ($setup_docs && $line =~ /^\+/) { push(@setup_docs, $line); } } $prefix = ''; $realcnt = 0; $linenr = 0; foreach my $line (@lines) { $linenr++; my $rawline = $rawlines[$linenr - 1]; #extract the line range in the file after the patch is applied if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $is_patch = 1; $first_line = $linenr + 1; $realline=$1-1; if (defined $2) { $realcnt=$3+1; } else { $realcnt=1+1; } annotate_reset(); $prev_values = 'E'; %suppress_ifbraces = (); %suppress_whiletrailers = (); %suppress_export = (); next; # track the line number as we move through the hunk, note that # new versions of GNU diff omit the leading space on completely # blank context lines so we need to count that too. } elsif ($line =~ /^( |\+|$)/) { $realline++; $realcnt-- if ($realcnt != 0); # Measure the line length and indent. ($length, $indent) = line_stats($rawline); # Track the previous line. ($prevline, $stashline) = ($stashline, $line); ($previndent, $stashindent) = ($stashindent, $indent); ($prevrawline, $stashrawline) = ($stashrawline, $rawline); #warn "line<$line>\n"; } elsif ($realcnt == 1) { $realcnt--; } my $hunk_line = ($realcnt != 0); #make up the handle for any error we report on this line $prefix = "$filename:$realline: " if ($emacs && $file); $prefix = "$filename:$linenr: " if ($emacs && !$file); $here = "#$linenr: " if (!$file); $here = "#$realline: " if ($file); # extract the filename as it passes if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@; } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@; $p1_prefix = $1; if (!$file && $tree && $p1_prefix ne '' && -e "$root/$p1_prefix") { WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); } if ($realfile =~ m@^include/asm/@) { ERROR("do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); } next; } $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); my $hereline = "$here\n$rawline\n"; my $herecurr = "$here\n$rawline\n"; my $hereprev = "$here\n$prevrawline\n$rawline\n"; $cnt_lines++ if ($realcnt != 0); # Check for incorrect file permissions if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { my $permhere = $here . "FILE: $realfile\n"; if ($realfile =~ /(Makefile|Kconfig|\.c|\.h|\.S|\.tmpl)$/) { ERROR("do not set execute permissions for source files\n" . $permhere); } } #check the patch for a signoff: if ($line =~ /^\s*signed-off-by:/i) { # This is a signoff, if ugly, so do not double report. $signoff++; if (!($line =~ /^\s*Signed-off-by:/)) { WARN("Signed-off-by: is the preferred form\n" . $herecurr); } if ($line =~ /^\s*signed-off-by:\S/i) { WARN("space required after Signed-off-by:\n" . $herecurr); } } # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("patch seems to be corrupt (line wrapped?)\n" . $herecurr) if (!$emitted_corrupt++); } # Check for absolute kernel paths. if ($tree) { while ($line =~ m{(?:^|\s)(/\S*)}g) { my $file = $1; if ($file =~ m{^(.*?)(?::\d+)+:?$} && check_absolute_file($1, $herecurr)) { # } else { check_absolute_file($file, $herecurr); } } } # UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php if (($realfile =~ /^$/ || $line =~ /^\+/) && $rawline !~ m/^$UTF8*$/) { my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); my $blank = copy_spacing($rawline); my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; my $hereptr = "$hereline$ptr\n"; ERROR("Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); } # ignore non-hunk lines and lines being removed next if (!$hunk_line || $line =~ /^-/); #trailing whitespace if ($line =~ /^\+.*\015/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; ERROR("DOS line endings\n" . $herevet); } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; ERROR("trailing whitespace\n" . $herevet); $rpt_cleaners = 1; } # check for Kconfig help text having a real description # Only applies when adding the entry originally, after that we do not have # sufficient context to determine whether it is indeed long enough. if ($realfile =~ /Kconfig/ && $line =~ /\+\s*(?:---)?help(?:---)?$/) { my $length = 0; my $cnt = $realcnt; my $ln = $linenr + 1; my $f; my $is_end = 0; while ($cnt > 0 && defined $lines[$ln - 1]) { $f = $lines[$ln - 1]; $cnt-- if ($lines[$ln - 1] !~ /^-/); $is_end = $lines[$ln - 1] =~ /^\+/; $ln++; next if ($f =~ /^-/); $f =~ s/^.//; $f =~ s/#.*//; $f =~ s/^\s+//; next if ($f =~ /^$/); if ($f =~ /^\s*config\s/) { $is_end = 1; last; } $length++; } WARN("please write a paragraph that describes the config symbol fully\n" . $herecurr) if ($is_end && $length < 4); #print "is_end<$is_end> length<$length>\n"; } # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); #80 column limit if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && $rawline !~ /^.\s*\*\s*\@$Ident\s/ && !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:|,|\)\s*;)\s*$/ || $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && $length > 80) { WARN("line over 80 characters\n" . $herecurr); } # check for spaces before a quoted newline if ($rawline =~ /^.*\".*\s\\n/) { WARN("unnecessary whitespace before a quoted newline\n" . $herecurr); } # check for adding lines without a newline. if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { WARN("adding a line without newline at end of file\n" . $herecurr); } # Blackfin: use hi/lo macros if ($realfile =~ m@arch/blackfin/.*\.S$@) { if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { my $herevet = "$here\n" . cat_vet($line) . "\n"; ERROR("use the LO() macro, not (... & 0xFFFF)\n" . $herevet); } if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { my $herevet = "$here\n" . cat_vet($line) . "\n"; ERROR("use the HI() macro, not (... >> 16)\n" . $herevet); } } # check we are in a valid source file C or perl if not then ignore this hunk next if ($realfile !~ /\.(h|c|pl)$/); # at the beginning of a line any tabs must come first and anything # more than 8 must use tabs. if ($rawline =~ /^\+\s* \t\s*\S/ || $rawline =~ /^\+\s* \s*/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; ERROR("code indent should use tabs where possible\n" . $herevet); $rpt_cleaners = 1; } # check for space before tabs. if ($rawline =~ /^\+/ && $rawline =~ / \t/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; WARN("please, no space before tabs\n" . $herevet); } # check for spaces at the beginning of a line. # Exceptions: # 1) within comments # 2) indented preprocessor commands # 3) hanging labels if ($rawline =~ /^\+ / && $line !~ /\+ *(?:$;|#|$Ident:)/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; WARN("please, no spaces at the start of a line\n" . $herevet); } # check we are in a valid C source file if not then ignore this hunk next if ($realfile !~ /\.(h|c)$/); # check for RCS/CVS revision markers if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); } # Blackfin: don't use __builtin_bfin_[cs]sync if ($line =~ /__builtin_bfin_csync/) { my $herevet = "$here\n" . cat_vet($line) . "\n"; ERROR("use the CSYNC() macro in asm/blackfin.h\n" . $herevet); } if ($line =~ /__builtin_bfin_ssync/) { my $herevet = "$here\n" . cat_vet($line) . "\n"; ERROR("use the SSYNC() macro in asm/blackfin.h\n" . $herevet); } # Check for potential 'bare' types my ($stat, $cond, $line_nr_next, $remain_next, $off_next, $realline_next); if ($realcnt && $line =~ /.\s*\S/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0); $stat =~ s/\n./\n /g; $cond =~ s/\n./\n /g; # Find the real next line. $realline_next = $line_nr_next; if (defined $realline_next && (!defined $lines[$realline_next - 1] || substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { $realline_next++; } my $s = $stat; $s =~ s/{.*$//s; # Ignore goto labels. if ($s =~ /$Ident:\*$/s) { # Ignore functions being called } elsif ($s =~ /^.\s*$Ident\s*\(/s) { } elsif ($s =~ /^.\s*else\b/s) { # declarations always start with types } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { my $type = $1; $type =~ s/\s+/ /g; possible($type, "A:" . $s); # definitions in global scope can only start with types } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { possible($1, "B:" . $s); } # any (foo ... *) is a pointer cast, and foo is a type while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { possible($1, "C:" . $s); } # Check for any sort of function declaration. # int foo(something bar, other baz); # void (*store_gdt)(x86_descr_ptr *); if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { my ($name_len) = length($1); my $ctx = $s; substr($ctx, 0, $name_len + 1, ''); $ctx =~ s/\)[^\)]*$//; for my $arg (split(/\s*,\s*/, $ctx)) { if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { possible($1, "D:" . $s); } } } } # # Checks which may be anchored in the context. # # Check for switch () and associated case and default # statements should be at the same indent. if ($line=~/\bswitch\s*\(.*\)/) { my $err = ''; my $sep = ''; my @ctx = ctx_block_outer($linenr, $realcnt); shift(@ctx); for my $ctx (@ctx) { my ($clen, $cindent) = line_stats($ctx); if ($ctx =~ /^\+\s*(case\s+|default:)/ && $indent != $cindent) { $err .= "$sep$ctx\n"; $sep = ''; } else { $sep = "[...]\n"; } } if ($err ne '') { ERROR("switch and case should be at the same indent\n$hereline$err"); } } # if/while/etc brace do not go on next line, unless defining a do while loop, # or if that brace on the next line is for something else if ($line =~ /(.*)\b((?:if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { my $pre_ctx = "$1$2"; my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); my $ctx_cnt = $realcnt - $#ctx - 1; my $ctx = join("\n", @ctx); my $ctx_ln = $linenr; my $ctx_skip = $realcnt; while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && defined $lines[$ctx_ln - 1] && $lines[$ctx_ln - 1] =~ /^-/)) { ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); $ctx_ln++; } #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { ERROR("that open brace { should be on the previous line\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); } if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && $ctx =~ /\)\s*\;\s*$/ && defined $lines[$ctx_ln - 1]) { my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); if ($nindent > $indent) { WARN("trailing semicolon indicates no statements, indent implies otherwise\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); } } } # Check relative indent for conditionals and blocks. if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { my ($s, $c) = ($stat, $cond); substr($s, 0, length($c), ''); # Make sure we remove the line prefixes as we have # none on the first line, and are going to readd them # where necessary. $s =~ s/\n./\n/gs; # Find out how long the conditional actually is. my @newlines = ($c =~ /\n/gs); my $cond_lines = 1 + $#newlines; # We want to check the first line inside the block # starting at the end of the conditional, so remove: # 1) any blank line termination # 2) any opening brace { on end of the line # 3) any do (...) { my $continuation = 0; my $check = 0; $s =~ s/^.*\bdo\b//; $s =~ s/^\s*{//; if ($s =~ s/^\s*\\//) { $continuation = 1; } if ($s =~ s/^\s*?\n//) { $check = 1; $cond_lines++; } # Also ignore a loop construct at the end of a # preprocessor statement. if (($prevline =~ /^.\s*#\s*define\s/ || $prevline =~ /\\\s*$/) && $continuation == 0) { $check = 0; } my $cond_ptr = -1; $continuation = 0; while ($cond_ptr != $cond_lines) { $cond_ptr = $cond_lines; # If we see an #else/#elif then the code # is not linear. if ($s =~ /^\s*\#\s*(?:else|elif)/) { $check = 0; } # Ignore: # 1) blank lines, they should be at 0, # 2) preprocessor lines, and # 3) labels. if ($continuation || $s =~ /^\s*?\n/ || $s =~ /^\s*#\s*?/ || $s =~ /^\s*$Ident\s*:/) { $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; if ($s =~ s/^.*?\n//) { $cond_lines++; } } } my (undef, $sindent) = line_stats("+" . $s); my $stat_real = raw_line($linenr, $cond_lines); # Check if either of these lines are modified, else # this is not this patch's fault. if (!defined($stat_real) || $stat !~ /^\+/ && $stat_real !~ /^\+/) { $check = 0; } if (defined($stat_real) && $cond_lines > 1) { $stat_real = "[...]\n$stat_real"; } #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; if ($check && (($sindent % 8) != 0 || ($sindent <= $indent && $s ne ''))) { WARN("suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); } } # Track the 'values' across context and added lines. my $opline = $line; $opline =~ s/^./ /; my ($curr_values, $curr_vars) = annotate_values($opline . "\n", $prev_values); $curr_values = $prev_values . $curr_values; if ($dbg_values) { my $outline = $opline; $outline =~ s/\t/ /g; print "$linenr > .$outline\n"; print "$linenr > $curr_values\n"; print "$linenr > $curr_vars\n"; } $prev_values = substr($curr_values, -1); #ignore lines not being added if ($line=~/^[^\+]/) {next;} # TEST: allow direct testing of the type matcher. if ($dbg_type) { if ($line =~ /^.\s*$Declare\s*$/) { ERROR("TEST: is type\n" . $herecurr); } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { ERROR("TEST: is not type ($1 is)\n". $herecurr); } next; } # TEST: allow direct testing of the attribute matcher. if ($dbg_attr) { if ($line =~ /^.\s*$Modifier\s*$/) { ERROR("TEST: is attr\n" . $herecurr); } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { ERROR("TEST: is not attr ($1 is)\n". $herecurr); } next; } # check for initialisation to aggregates open brace on the next line if ($line =~ /^.\s*{/ && $prevline =~ /(?:^|[^=])=\s*$/) { ERROR("that open brace { should be on the previous line\n" . $hereprev); } # # Checks which are anchored on the added line. # # check for malformed paths in #include statements (uses RAW line) if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { my $path = $1; if ($path =~ m{//}) { ERROR("malformed #include filename\n" . $herecurr); } } # no C99 // comments if ($line =~ m{//}) { ERROR("do not use C99 // comments\n" . $herecurr); } # Remove C99 comments. $line =~ s@//.*@@; $opline =~ s@//.*@@; # EXPORT_SYMBOL should immediately follow the thing it is exporting, consider # the whole statement. #print "APW <$lines[$realline_next - 1]>\n"; if (defined $realline_next && exists $lines[$realline_next - 1] && !defined $suppress_export{$realline_next} && ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { # Handle definitions which produce identifiers with # a prefix: # XXX(foo); # EXPORT_SYMBOL(something_foo); my $name = $1; if ($stat =~ /^.([A-Z_]+)\s*\(\s*($Ident)/ && $name =~ /^${Ident}_$2/) { #print "FOO C name<$name>\n"; $suppress_export{$realline_next} = 1; } elsif ($stat !~ /(?: \n.}\s*$| ^.DEFINE_$Ident\(\Q$name\E\)| ^.DECLARE_$Ident\(\Q$name\E\)| ^.LIST_HEAD\(\Q$name\E\)| ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() )/x) { #print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; $suppress_export{$realline_next} = 2; } else { $suppress_export{$realline_next} = 1; } } if (!defined $suppress_export{$linenr} && $prevline =~ /^.\s*$/ && ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { #print "FOO B <$lines[$linenr - 1]>\n"; $suppress_export{$linenr} = 2; } if (defined $suppress_export{$linenr} && $suppress_export{$linenr} == 2) { WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); } # check for global initialisers. if ($line =~ /^.$Type\s*$Ident\s*(?:\s+$Modifier)*\s*=\s*(0|NULL|false)\s*;/) { ERROR("do not initialise globals to 0 or NULL\n" . $herecurr); } # check for static initialisers. if ($line =~ /\bstatic\s.*=\s*(0|NULL|false)\s*;/) { ERROR("do not initialise statics to 0 or NULL\n" . $herecurr); } # check for static const char * arrays. if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { WARN("static const char * array should probably be static const char * const\n" . $herecurr); } # check for static char foo[] = "bar" declarations. if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { WARN("static char array declaration should probably be static const char\n" . $herecurr); } # check for declarations of struct pci_device_id if ($line =~ /\bstruct\s+pci_device_id\s+\w+\s*\[\s*\]\s*\=\s*\{/) { WARN("Use DEFINE_PCI_DEVICE_TABLE for struct pci_device_id\n" . $herecurr); } # check for new typedefs, only function parameters and sparse annotations # make sense. if ($line =~ /\btypedef\s/ && $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && $line !~ /\b$typeTypedefs\b/ && $line !~ /\b__bitwise(?:__|)\b/) { WARN("do not add new typedefs\n" . $herecurr); } # * goes on variable not on type # (char*[ const]) if ($line =~ m{\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\)}) { my ($from, $to) = ($1, $1); # Should start with a space. $to =~ s/^(\S)/ $1/; # Should not end with a space. $to =~ s/\s+$//; # '*'s should not have spaces between. while ($to =~ s/\*\s+\*/\*\*/) { } #print "from<$from> to<$to>\n"; if ($from ne $to) { ERROR("\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr); } } elsif ($line =~ m{\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident)}) { my ($from, $to, $ident) = ($1, $1, $2); # Should start with a space. $to =~ s/^(\S)/ $1/; # Should not end with a space. $to =~ s/\s+$//; # '*'s should not have spaces between. while ($to =~ s/\*\s+\*/\*\*/) { } # Modifiers should have spaces. $to =~ s/(\b$Modifier$)/$1 /; #print "from<$from> to<$to> ident<$ident>\n"; if ($from ne $to && $ident !~ /^$Modifier$/) { ERROR("\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr); } } # # no BUG() or BUG_ON() # if ($line =~ /\b(BUG|BUG_ON)\b/) { # print "Try to use WARN_ON & Recovery code rather than BUG() or BUG_ON()\n"; # print "$herecurr"; # $clean = 0; # } if ($line =~ /\bLINUX_VERSION_CODE\b/) { WARN("LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); } # check for uses of printk_ratelimit if ($line =~ /\bprintk_ratelimit\s*\(/) { WARN("Prefer printk_ratelimited or pr__ratelimited to printk_ratelimit\n" . $herecurr); } # printk should use KERN_* levels. Note that follow on printk's on the # same line do not need a level, so we use the current block context # to try and find and validate the current printk. In summary the current # printk includes all preceding printk's which have no newline on the end. # we assume the first bad printk is the one to report. if ($line =~ /\bprintk\((?!KERN_)\s*"/) { my $ok = 0; for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { #print "CHECK<$lines[$ln - 1]\n"; # we have a preceding printk if it ends # with "\n" ignore it, else it is to blame if ($lines[$ln - 1] =~ m{\bprintk\(}) { if ($rawlines[$ln - 1] !~ m{\\n"}) { $ok = 1; } last; } } if ($ok == 0) { WARN("printk() should include KERN_ facility level\n" . $herecurr); } } # function brace can't be on same line, except for #defines of do while, # or if closed on same line if (($line=~/$Type\s*$Ident\(.*\).*\s{/) and !($line=~/\#\s*define.*do\s{/) and !($line=~/}/)) { ERROR("open brace '{' following function declarations go on the next line\n" . $herecurr); } # open braces for enum, union and struct go on the same line. if ($line =~ /^.\s*{/ && $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { ERROR("open brace '{' following $1 go on the same line\n" . $hereprev); } # missing space after union, struct or enum definition if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) { WARN("missing space after $1 definition\n" . $herecurr); } # check for spacing round square brackets; allowed: # 1. with a type on the left -- int [] a; # 2. at the beginning of a line for slice initialisers -- [0...10] = 5, # 3. inside a curly brace -- = { [0...10] = 5 } while ($line =~ /(.*?\s)\[/g) { my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && $prefix !~ /{\s+$/) { ERROR("space prohibited before open square bracket '['\n" . $herecurr); } } # check for spaces between functions and their parentheses. while ($line =~ /($Ident)\s+\(/g) { my $name = $1; my $ctx_before = substr($line, 0, $-[1]); my $ctx = "$ctx_before$name"; # Ignore those directives where spaces _are_ permitted. if ($name =~ /^(?: if|for|while|switch|return|case| volatile|__volatile__| __attribute__|format|__extension__| asm|__asm__)$/x) { # cpp #define statements have non-optional spaces, ie # if there is a space between the name and the open # parenthesis it is simply not a parameter group. } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { # cpp #elif statement condition may start with a ( } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { # If this whole things ends with a type its most # likely a typedef for a function. } elsif ($ctx =~ /$Type$/) { } else { WARN("space prohibited between function name and open parenthesis '('\n" . $herecurr); } } # Check operator spacing. if (!($line=~/\#\s*include/)) { my $ops = qr{ <<=|>>=|<=|>=|==|!=| \+=|-=|\*=|\/=|%=|\^=|\|=|&=| =>|->|<<|>>|<|>|=|!|~| &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| \?|: }x; my @elements = split(/($ops|;)/, $opline); my $off = 0; my $blank = copy_spacing($opline); for (my $n = 0; $n < $#elements; $n += 2) { $off += length($elements[$n]); # Pick up the preceding and succeeding characters. my $ca = substr($opline, 0, $off); my $cc = ''; if (length($opline) >= ($off + length($elements[$n + 1]))) { $cc = substr($opline, $off + length($elements[$n + 1])); } my $cb = "$ca$;$cc"; my $a = ''; $a = 'V' if ($elements[$n] ne ''); $a = 'W' if ($elements[$n] =~ /\s$/); $a = 'C' if ($elements[$n] =~ /$;$/); $a = 'B' if ($elements[$n] =~ /(\[|\()$/); $a = 'O' if ($elements[$n] eq ''); $a = 'E' if ($ca =~ /^\s*$/); my $op = $elements[$n + 1]; my $c = ''; if (defined $elements[$n + 2]) { $c = 'V' if ($elements[$n + 2] ne ''); $c = 'W' if ($elements[$n + 2] =~ /^\s/); $c = 'C' if ($elements[$n + 2] =~ /^$;/); $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); $c = 'O' if ($elements[$n + 2] eq ''); $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); } else { $c = 'E'; } my $ctx = "${a}x${c}"; my $at = "(ctx:$ctx)"; my $ptr = substr($blank, 0, $off) . "^"; my $hereptr = "$hereline$ptr\n"; # Pull out the value of this operator. my $op_type = substr($curr_values, $off + 1, 1); # Get the full operator variant. my $opv = $op . substr($curr_vars, $off, 1); # Ignore operators passed as parameters. if ($op_type ne 'V' && $ca =~ /\s$/ && $cc =~ /^\s*,/) { # # Ignore comments # } elsif ($op =~ /^$;+$/) { # ; should have either the end of line or a space or \ after it } elsif ($op eq ';') { if ($ctx !~ /.x[WEBC]/ && $cc !~ /^\\/ && $cc !~ /^;/) { ERROR("space required after that '$op' $at\n" . $hereptr); } # // is a comment } elsif ($op eq '//') { # No spaces for: # -> # : when part of a bitfield } elsif ($op eq '->' || $opv eq ':B') { if ($ctx =~ /Wx.|.xW/) { ERROR("spaces prohibited around that '$op' $at\n" . $hereptr); } # , must have a space on the right. } elsif ($op eq ',') { if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { ERROR("space required after that '$op' $at\n" . $hereptr); } # '*' as part of a type definition -- reported already. } elsif ($opv eq '*_') { #warn "'*' is part of type\n"; # unary operators should have a space before and # none after. May be left adjacent to another # unary operator, or a cast } elsif ($op eq '!' || $op eq '~' || $opv eq '*U' || $opv eq '-U' || $opv eq '&U' || $opv eq '&&U') { if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { ERROR("space required before that '$op' $at\n" . $hereptr); } if ($op eq '*' && $cc =~/\s*$Modifier\b/) { # A unary '*' may be const } elsif ($ctx =~ /.xW/) { ERROR("space prohibited after that '$op' $at\n" . $hereptr); } # unary ++ and unary -- are allowed no space on one side. } elsif ($op eq '++' or $op eq '--') { if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { ERROR("space required one side of that '$op' $at\n" . $hereptr); } if ($ctx =~ /Wx[BE]/ || ($ctx =~ /Wx./ && $cc =~ /^;/)) { ERROR("space prohibited before that '$op' $at\n" . $hereptr); } if ($ctx =~ /ExW/) { ERROR("space prohibited after that '$op' $at\n" . $hereptr); } # << and >> may either have or not have spaces both sides } elsif ($op eq '<<' or $op eq '>>' or $op eq '&' or $op eq '^' or $op eq '|' or $op eq '+' or $op eq '-' or $op eq '*' or $op eq '/' or $op eq '%') { if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { ERROR("need consistent spacing around '$op' $at\n" . $hereptr); } # A colon needs no spaces before when it is # terminating a case value or a label. } elsif ($opv eq ':C' || $opv eq ':L') { if ($ctx =~ /Wx./) { ERROR("space prohibited before that '$op' $at\n" . $hereptr); } # All the others need spaces both sides. } elsif ($ctx !~ /[EWC]x[CWE]/) { my $ok = 0; # Ignore email addresses if (($op eq '<' && $cc =~ /^\S+\@\S+>/) || ($op eq '>' && $ca =~ /<\S+\@\S+$/)) { $ok = 1; } # Ignore ?: if (($opv eq ':O' && $ca =~ /\?$/) || ($op eq '?' && $cc =~ /^:/)) { $ok = 1; } if ($ok == 0) { ERROR("spaces required around that '$op' $at\n" . $hereptr); } } $off += length($elements[$n + 1]); } } # check for multiple assignments if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { CHK("multiple assignments should be avoided\n" . $herecurr); } ## # check for multiple declarations, allowing for a function declaration ## # continuation. ## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && ## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { ## ## # Remove any bracketed sections to ensure we do not ## # falsly report the parameters of functions. ## my $ln = $line; ## while ($ln =~ s/\([^\(\)]*\)//g) { ## } ## if ($ln =~ /,/) { ## WARN("declaring multiple variables together should be avoided\n" . $herecurr); ## } ## } #need space before brace following if, while, etc if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) || $line =~ /do{/) { ERROR("space required before the open brace '{'\n" . $herecurr); } # closing brace should have a space following it when it has anything # on the line if ($line =~ /}(?!(?:,|;|\)))\S/) { ERROR("space required after that close brace '}'\n" . $herecurr); } # check spacing on square brackets if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { ERROR("space prohibited after that open square bracket '['\n" . $herecurr); } if ($line =~ /\s\]/) { ERROR("space prohibited before that close square bracket ']'\n" . $herecurr); } # check spacing on parentheses if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && $line !~ /for\s*\(\s+;/) { ERROR("space prohibited after that open parenthesis '('\n" . $herecurr); } if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && $line !~ /for\s*\(.*;\s+\)/ && $line !~ /:\s+\)/) { ERROR("space prohibited before that close parenthesis ')'\n" . $herecurr); } #goto labels aren't indented, allow a single space however if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { WARN("labels should not be indented\n" . $herecurr); } # Return is not a function. if (defined($stat) && $stat =~ /^.\s*return(\s*)(\(.*);/s) { my $spacing = $1; my $value = $2; # Flatten any parentheses $value =~ s/\(/ \(/g; $value =~ s/\)/\) /g; while ($value =~ s/\[[^\{\}]*\]/1/ || $value !~ /(?:$Ident|-?$Constant)\s* $Compare\s* (?:$Ident|-?$Constant)/x && $value =~ s/\([^\(\)]*\)/1/) { } #print "value<$value>\n"; if ($value =~ /^\s*(?:$Ident|-?$Constant)\s*$/) { ERROR("return is not a function, parentheses are not required\n" . $herecurr); } elsif ($spacing !~ /\s+/) { ERROR("space required before the open parenthesis '('\n" . $herecurr); } } # Return of what appears to be an errno should normally be -'ve if ($line =~ /^.\s*return\s*(E[A-Z]*)\s*;/) { my $name = $1; if ($name ne 'EOF' && $name ne 'ERROR') { WARN("return of an errno should typically be -ve (return -$1)\n" . $herecurr); } } # Need a space before open parenthesis after if, while etc if ($line=~/\b(if|while|for|switch)\(/) { ERROR("space required before the open parenthesis '('\n" . $herecurr); } # Check for illegal assignment in if conditional -- and check for trailing # statements after the conditional. if ($line =~ /do\s*(?!{)/) { my ($stat_next) = ctx_statement_block($line_nr_next, $remain_next, $off_next); $stat_next =~ s/\n./\n /g; ##print "stat<$stat> stat_next<$stat_next>\n"; if ($stat_next =~ /^\s*while\b/) { # If the statement carries leading newlines, # then count those as offsets. my ($whitespace) = ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); my $offset = statement_rawlines($whitespace) - 1; $suppress_whiletrailers{$line_nr_next + $offset} = 1; } } if (!defined $suppress_whiletrailers{$linenr} && $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { my ($s, $c) = ($stat, $cond); if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { ERROR("do not use assignment in if condition\n" . $herecurr); } # Find out what is on the end of the line after the # conditional. substr($s, 0, length($c), ''); $s =~ s/\n.*//g; $s =~ s/$;//g; # Remove any comments if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && $c !~ /}\s*while\s*/) { # Find out how long the conditional actually is. my @newlines = ($c =~ /\n/gs); my $cond_lines = 1 + $#newlines; my $stat_real = ''; $stat_real = raw_line($linenr, $cond_lines) . "\n" if ($cond_lines); if (defined($stat_real) && $cond_lines > 1) { $stat_real = "[...]\n$stat_real"; } ERROR("trailing statements should be on next line\n" . $herecurr . $stat_real); } } # Check for bitwise tests written as boolean if ($line =~ / (?: (?:\[|\(|\&\&|\|\|) \s*0[xX][0-9]+\s* (?:\&\&|\|\|) | (?:\&\&|\|\|) \s*0[xX][0-9]+\s* (?:\&\&|\|\||\)|\]) )/x) { WARN("boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); } # if and else should not have general statements after it if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { my $s = $1; $s =~ s/$;//g; # Remove any comments if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { ERROR("trailing statements should be on next line\n" . $herecurr); } } # if should not continue a brace if ($line =~ /}\s*if\b/) { ERROR("trailing statements should be on next line\n" . $herecurr); } # case and default should not have general statements after them if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && $line !~ /\G(?: (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| \s*return\s+ )/xg) { ERROR("trailing statements should be on next line\n" . $herecurr); } # Check for }else {, these must be at the same # indent level to be relevant to each other. if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ and $previndent == $indent) { ERROR("else should follow close brace '}'\n" . $hereprev); } if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ and $previndent == $indent) { my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); # Find out what is on the end of the line after the # conditional. substr($s, 0, length($c), ''); $s =~ s/\n.*//g; if ($s =~ /^\s*;/) { ERROR("while should follow close brace '}'\n" . $hereprev); } } #studly caps, commented out until figure out how to distinguish between use of existing and adding new # if (($line=~/[\w_][a-z\d]+[A-Z]/) and !($line=~/print/)) { # print "No studly caps, use _\n"; # print "$herecurr"; # $clean = 0; # } #no spaces allowed after \ in define if ($line=~/\#\s*define.*\\\s$/) { WARN("Whitepspace after \\ makes next lines useless\n" . $herecurr); } #warn if is #included and is available (uses RAW line) if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\}) { my $file = "$1.h"; my $checkfile = "include/linux/$file"; if (-f "$root/$checkfile" && $realfile ne $checkfile && $1 !~ /$allowed_asm_includes/) { if ($realfile =~ m{^arch/}) { CHK("Consider using #include instead of \n" . $herecurr); } else { WARN("Use #include instead of \n" . $herecurr); } } } # multi-statement macros should be enclosed in a do while loop, grab the # first statement and ensure its the whole macro if its not enclosed # in a known good container if ($realfile !~ m@/vmlinux.lds.h$@ && $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { my $ln = $linenr; my $cnt = $realcnt; my ($off, $dstat, $dcond, $rest); my $ctx = ''; my $args = defined($1); # Find the end of the macro and limit our statement # search to that. while ($cnt > 0 && defined $lines[$ln - 1] && $lines[$ln - 1] =~ /^(?:-|..*\\$)/) { $ctx .= $rawlines[$ln - 1] . "\n"; $cnt-- if ($lines[$ln - 1] !~ /^-/); $ln++; } $ctx .= $rawlines[$ln - 1]; ($dstat, $dcond, $ln, $cnt, $off) = ctx_statement_block($linenr, $ln - $linenr + 1, 0); #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; # Extract the remainder of the define (if any) and # rip off surrounding spaces, and trailing \'s. $rest = ''; while ($off != 0 || ($cnt > 0 && $rest =~ /\\\s*$/)) { #print "ADDING cnt<$cnt> $off <" . substr($lines[$ln - 1], $off) . "> rest<$rest>\n"; if ($off != 0 || $lines[$ln - 1] !~ /^-/) { $rest .= substr($lines[$ln - 1], $off) . "\n"; $cnt--; } $ln++; $off = 0; } $rest =~ s/\\\n.//g; $rest =~ s/^\s*//s; $rest =~ s/\s*$//s; # Clean up the original statement. if ($args) { substr($dstat, 0, length($dcond), ''); } else { $dstat =~ s/^.\s*\#\s*define\s+$Ident\s*//; } $dstat =~ s/$;//g; $dstat =~ s/\\\n.//g; $dstat =~ s/^\s*//s; $dstat =~ s/\s*$//s; # Flatten any parentheses and braces while ($dstat =~ s/\([^\(\)]*\)/1/ || $dstat =~ s/\{[^\{\}]*\}/1/ || $dstat =~ s/\[[^\{\}]*\]/1/) { } my $exceptions = qr{ $Declare| module_param_named| MODULE_PARAM_DESC| DECLARE_PER_CPU| DEFINE_PER_CPU| __typeof__\(| union| struct| \.$Ident\s*=\s*| ^\"|\"$ }x; #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; if ($rest ne '' && $rest ne ',') { if ($rest !~ /while\s*\(/ && $dstat !~ /$exceptions/) { ERROR("Macros with multiple statements should be enclosed in a do - while loop\n" . "$here\n$ctx\n"); } } elsif ($ctx !~ /;/) { if ($dstat ne '' && $dstat !~ /^(?:$Ident|-?$Constant)$/ && $dstat !~ /$exceptions/ && $dstat !~ /^\.$Ident\s*=/ && $dstat =~ /$Operators/) { ERROR("Macros with complex values should be enclosed in parenthesis\n" . "$here\n$ctx\n"); } } } # make sure symbols are always wrapped with VMLINUX_SYMBOL() ... # all assignments may have only one of the following with an assignment: # . # ALIGN(...) # VMLINUX_SYMBOL(...) if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { WARN("vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); } # check for redundant bracing round if etc if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { my ($level, $endln, @chunks) = ctx_statement_full($linenr, $realcnt, 1); #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; if ($#chunks > 0 && $level == 0) { my $allowed = 0; my $seen = 0; my $herectx = $here . "\n"; my $ln = $linenr - 1; for my $chunk (@chunks) { my ($cond, $block) = @{$chunk}; # If the condition carries leading newlines, then count those as offsets. my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); my $offset = statement_rawlines($whitespace) - 1; #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; # We have looked at and allowed this specific line. $suppress_ifbraces{$ln + $offset} = 1; $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; $ln += statement_rawlines($block) - 1; substr($block, 0, length($cond), ''); $seen++ if ($block =~ /^\s*{/); #print "cond<$cond> block<$block> allowed<$allowed>\n"; if (statement_lines($cond) > 1) { #print "APW: ALLOWED: cond<$cond>\n"; $allowed = 1; } if ($block =~/\b(?:if|for|while)\b/) { #print "APW: ALLOWED: block<$block>\n"; $allowed = 1; } if (statement_block_size($block) > 1) { #print "APW: ALLOWED: lines block<$block>\n"; $allowed = 1; } } if ($seen && !$allowed) { WARN("braces {} are not necessary for any arm of this statement\n" . $herectx); } } } if (!defined $suppress_ifbraces{$linenr - 1} && $line =~ /\b(if|while|for|else)\b/) { my $allowed = 0; # Check the pre-context. if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { #print "APW: ALLOWED: pre<$1>\n"; $allowed = 1; } my ($level, $endln, @chunks) = ctx_statement_full($linenr, $realcnt, $-[0]); # Check the condition. my ($cond, $block) = @{$chunks[0]}; #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; if (defined $cond) { substr($block, 0, length($cond), ''); } if (statement_lines($cond) > 1) { #print "APW: ALLOWED: cond<$cond>\n"; $allowed = 1; } if ($block =~/\b(?:if|for|while)\b/) { #print "APW: ALLOWED: block<$block>\n"; $allowed = 1; } if (statement_block_size($block) > 1) { #print "APW: ALLOWED: lines block<$block>\n"; $allowed = 1; } # Check the post-context. if (defined $chunks[1]) { my ($cond, $block) = @{$chunks[1]}; if (defined $cond) { substr($block, 0, length($cond), ''); } if ($block =~ /^\s*\{/) { #print "APW: ALLOWED: chunk-1 block<$block>\n"; $allowed = 1; } } if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { my $herectx = $here . "\n";; my $cnt = statement_rawlines($block); for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n";; } WARN("braces {} are not necessary for single statement blocks\n" . $herectx); } } # don't include deprecated include files (uses RAW line) for my $inc (@dep_includes) { if ($rawline =~ m@^.\s*\#\s*include\s*\<$inc>@) { ERROR("Don't use <$inc>: see Documentation/feature-removal-schedule.txt\n" . $herecurr); } } # don't use deprecated functions for my $func (@dep_functions) { if ($line =~ /\b$func\b/) { ERROR("Don't use $func(): see Documentation/feature-removal-schedule.txt\n" . $herecurr); } } # no volatiles please my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { WARN("Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); } # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { CHK("if this code is redundant consider removing it\n" . $herecurr); } # check for needless kfree() checks if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { my $expr = $1; if ($line =~ /\bkfree\(\Q$expr\E\);/) { WARN("kfree(NULL) is safe this check is probably not required\n" . $hereprev); } } # check for needless usb_free_urb() checks if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { my $expr = $1; if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) { WARN("usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev); } } # prefer usleep_range over udelay if ($line =~ /\budelay\s*\(\s*(\w+)\s*\)/) { # ignore udelay's < 10, however if (! (($1 =~ /(\d+)/) && ($1 < 10)) ) { CHK("usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $line); } } # warn about unexpectedly long msleep's if ($line =~ /\bmsleep\s*\((\d+)\);/) { if ($1 < 20) { WARN("msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $line); } } # warn about #ifdefs in C files # if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { # print "#ifdef in C files should be avoided\n"; # print "$herecurr"; # $clean = 0; # } # warn about spacing in #ifdefs if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { ERROR("exactly one space required after that #$1\n" . $herecurr); } # check for spinlock_t definitions without a comment. if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { my $which = $1; if (!ctx_has_comment($first_line, $linenr)) { CHK("$1 definition without comment\n" . $herecurr); } } # check for memory barriers without a comment. if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { if (!ctx_has_comment($first_line, $linenr)) { CHK("memory barrier without comment\n" . $herecurr); } } # check of hardware specific defines if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { CHK("architecture specific defines should be avoided\n" . $herecurr); } # Check that the storage class is at the beginning of a declaration if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { WARN("storage class should be at the beginning of the declaration\n" . $herecurr) } # check the location of the inline attribute, that it is between # storage class and type. if ($line =~ /\b$Type\s+$Inline\b/ || $line =~ /\b$Inline\s+$Storage\b/) { ERROR("inline keyword should sit between storage class and type\n" . $herecurr); } # Check for __inline__ and __inline, prefer inline if ($line =~ /\b(__inline__|__inline)\b/) { WARN("plain inline is preferred over $1\n" . $herecurr); } # Check for __attribute__ packed, prefer __packed if ($line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { WARN("__packed is preferred over __attribute__((packed))\n" . $herecurr); } # check for sizeof(&) if ($line =~ /\bsizeof\s*\(\s*\&/) { WARN("sizeof(& should be avoided\n" . $herecurr); } # check for line continuations in quoted strings with odd counts of " if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { WARN("Avoid line continuations in quoted strings\n" . $herecurr); } # check for new externs in .c files. if ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) { my $function_name = $1; my $paren_space = $2; my $s = $stat; if (defined $cond) { substr($s, 0, length($cond), ''); } if ($s =~ /^\s*;/ && $function_name ne 'uninitialized_var') { WARN("externs should be avoided in .c files\n" . $herecurr); } if ($paren_space =~ /\n/) { WARN("arguments for function declarations should follow identifier\n" . $herecurr); } } elsif ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^.\s*extern\s+/) { WARN("externs should be avoided in .c files\n" . $herecurr); } # checks for new __setup's if ($rawline =~ /\b__setup\("([^"]*)"/) { my $name = $1; if (!grep(/$name/, @setup_docs)) { CHK("__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); } } # check for pointless casting of kmalloc return if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) { WARN("unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); } # check for multiple semicolons if ($line =~ /;\s*;\s*$/) { WARN("Statements terminations use 1 semicolon\n" . $herecurr); } # check for gcc specific __FUNCTION__ if ($line =~ /__FUNCTION__/) { WARN("__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr); } # check for semaphores initialized locked if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { WARN("consider using a completion\n" . $herecurr); } # recommend kstrto* over simple_strto* if ($line =~ /\bsimple_(strto.*?)\s*\(/) { WARN("consider using kstrto* in preference to simple_$1\n" . $herecurr); } # check for __initcall(), use device_initcall() explicitly please if ($line =~ /^.\s*__initcall\s*\(/) { WARN("please use device_initcall() instead of __initcall()\n" . $herecurr); } # check for various ops structs, ensure they are const. my $struct_ops = qr{acpi_dock_ops| address_space_operations| backlight_ops| block_device_operations| dentry_operations| dev_pm_ops| dma_map_ops| extent_io_ops| file_lock_operations| file_operations| hv_ops| ide_dma_ops| intel_dvo_dev_ops| item_operations| iwl_ops| kgdb_arch| kgdb_io| kset_uevent_ops| lock_manager_operations| microcode_ops| mtrr_ops| neigh_ops| nlmsvc_binding| pci_raw_ops| pipe_buf_operations| platform_hibernation_ops| platform_suspend_ops| proto_ops| rpc_pipe_ops| seq_operations| snd_ac97_build_ops| soc_pcmcia_socket_ops| stacktrace_ops| sysfs_ops| tty_operations| usb_mon_operations| wd_ops}x; if ($line !~ /\bconst\b/ && $line =~ /\bstruct\s+($struct_ops)\b/) { WARN("struct $1 should normally be const\n" . $herecurr); } # use of NR_CPUS is usually wrong # ignore definitions of NR_CPUS and usage to define arrays as likely right if ($line =~ /\bNR_CPUS\b/ && $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) { WARN("usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); } # check for %L{u,d,i} in strings my $string; while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { $string = substr($rawline, $-[1], $+[1] - $-[1]); $string =~ s/%%/__/g; if ($string =~ /(?mutex.\n" . $herecurr); } } if ($line =~ /debugfs_create_file.*S_IWUGO/ || $line =~ /DEVICE_ATTR.*S_IWUGO/ ) { WARN("Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); } # Check for memset with swapped arguments if ($line =~ /memset.*\,(\ |)(0x|)0(\ |0|)\);/) { ERROR("memset size is 3rd argument, not the second.\n" . $herecurr); } } # If we have no input at all, then there is nothing to report on # so just keep quiet. if ($#rawlines == -1) { exit(0); } # In mailback mode only produce a report in the negative, for # things that appear to be patches. if ($mailback && ($clean == 1 || !$is_patch)) { exit(0); } # This is not a patch, and we are are in 'no-patch' mode so # just keep quiet. if (!$chk_patch && !$is_patch) { exit(0); } if (!$is_patch) { ERROR("Does not appear to be a unified-diff format patch\n"); } if ($is_patch && $chk_signoff && $signoff == 0) { ERROR("Missing Signed-off-by: line(s)\n"); } print report_dump(); if ($summary && !($clean == 1 && $quiet == 1)) { print "$filename " if ($summary_file); print "total: $cnt_error errors, $cnt_warn warnings, " . (($check)? "$cnt_chk checks, " : "") . "$cnt_lines lines checked\n"; print "\n" if ($quiet == 0); } if ($quiet == 0) { # If there were whitespace errors which cleanpatch can fix # then suggest that. if ($rpt_cleaners) { print "NOTE: whitespace errors detected, you may wish to use scripts/cleanpatch or\n"; print " scripts/cleanfile\n\n"; $rpt_cleaners = 0; } } if ($clean == 1 && $quiet == 0) { print "$vname has no obvious style problems and is ready for submission.\n" } if ($clean == 0 && $quiet == 0) { print "$vname has style problems, please review. If any of these errors\n"; print "are false positives report them to the maintainer, see\n"; print "CHECKPATCH in MAINTAINERS.\n"; } return $clean; } ================================================ FILE: scripts/lio_target_passthru.sh ================================================ #!/bin/bash if [ $# -ne 1 ]; then echo "Usage: $0 initiator_iqn (to add to iSCSI ACL)" exit fi # Use $1 if defined, otherwise default to this IQN iniator addr INIT_IQN=${1:-iqn.1994-05.com.redhat:e284d58153b4} # Reference: # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=880576 IQN="iqn.2019-03.com.mhvtl:target" HBA=`lsscsi -H | awk '/mhvtl/ {print $1}' | sed -e 's/\[//' -e 's/\]//'` # Extract SCSI h:b:t:l of each dev SCSI_ADDR=`lsscsi $HBA | awk '{print $1}'` targetcli /iscsi/ create $IQN # Setup LIO backing store - Walk each device, extract /dev/sg path for dev in $SCSI_ADDR do read -r hba channel id lun <<< `echo $dev | awk -F: '{print $1,$2,$3,$4}' | sed -e 's/\[//' -e 's/\]//g'` # Extract the SCSI Passthru device (/dev/sg) of this h:b:t:l PASSTHRU=`lsscsi -g $hba $channel $id $lun | awk '{print $7}'` echo "hba: $hba, Channel: $channel, SCSI ID: $id, SCSI LUN: $lun - scsi passthru path: $PASSTHRU" MHVTL=$(printf "h%db%dt%dl%d" $hba $channel $id $lun) TLD="/sys/kernel/config/target/core/pscsi_0/mhVTL${MHVTL}" mkdir -p $TLD uuidgen > $TLD/wwn/vpd_unit_serial echo scsi_host_id=$hba,scsi_channel_id=$channel,scsi_target_id=$id,scsi_lun_id=$lun > $TLD/control echo $PASSTHRU > $TLD/udev_path echo 1 > $TLD/enable # Map LIO backing store to mhVTL /dev/sgXX path targetcli /iscsi/$IQN/tpg1/luns/ create /backstores/pscsi/mhVTL${MHVTL} # targetcli ls # sleep 2 done targetcli /iscsi/$IQN/tpg1/acls/ create $INIT_IQN targetcli ls # Open up TCP port 3260 through iptables # CentOS7 anyway.. firewall-cmd --add-port 3260/tcp ================================================ FILE: scripts/mhvtl-1.4.ebuild ================================================ # Copyright 1999-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: EAPI="2" inherit linux-mod eutils MY_P="${PN}-2012-09-13" DESCRIPTION="mhvtl module provides Virtual (SCSI) Tape Library" HOMEPAGE="http://sites.google.com/site/linuxvtl2" SRC_URI="http://sites.google.com/site/linuxvtl2/${MY_P}.tgz" LICENSE="GPL-2" SLOT="0" KEYWORDS="~amd64 ~x86" IUSE="doc" DEPEND=">=virtual/linux-sources-2.6.19 sys-fs/lsscsi sys-libs/zlib sys-libs/lzo sys-apps/sg3_utils" RDEPEND="" MODULE_NAMES="mhvtl(block:${S}/kernel:${S}/kernel)" BUILD_TARGETS="clean default" MHVTL_HOME_PATH=/var/spool/media/vtl pkg_setup() { CONFIG_CHECK="~BLK_DEV_SR ~CHR_DEV_SG" check_extra_config BUILD_PARAMS="KDIR=${KV_DIR}" linux-mod_pkg_setup } src_prepare() { epatch "${FILESDIR}/1.2-kerneldir.patch" epatch "${FILESDIR}/1.2-etc.patch" epatch "${FILESDIR}/1.2-make_vtl_media.patch" epatch "${FILESDIR}/1.2-mhvtl.patch" } src_compile() { emake clean || die linux-mod_src_compile || die "linux-mod_src_compile" emake MHVTL_HOME_PATH=${MHVTL_HOME_PATH} || die "emake failed" } src_install() { linux-mod_src_install || die "Error: installing module failed!" emake MHVTL_HOME_PATH=${MHVTL_HOME_PATH} DESTDIR=${D} install || die "emake failed" einfo "Generating udev rules ..." dodir /etc/udev/rules.d/ cat > "${D}"/etc/udev/rules.d/70-mhvtl.rules <<-EOF || die # do not edit this file, it will be overwritten on update # KERNEL=="mhvtl[0-9]*", MODE="0660", OWNER="root", GROUP="root" EOF newinitd "${FILESDIR}"/mhvtl.init.d mhvtl || die if use doc; then dohtml -r doc/* || die fi doman man/*.1 || die dodoc README INSTALL } pkg_postinst() { linux-mod_pkg_postinst } ================================================ FILE: scripts/rescan-scsi-bus.sh ================================================ #!/bin/bash # Skript to rescan SCSI bus, using the # scsi add-single-device mechanism # (c) 1998--2008 Kurt Garloff , GNU GPL v2 or later # (c) 2006--2008 Hannes Reinecke, GNU GPL v2 or later # $Id: rescan-scsi-bus.sh,v 1.48 2010/08/10 19:32:22 garloff Exp $ setcolor () { red="\e[0;31m" green="\e[0;32m" yellow="\e[0;33m" bold="\e[0;1m" norm="\e[0;0m" } unsetcolor () { red=""; green="" yellow=""; norm="" } # Output some text and return cursor to previous position # (only works for simple strings) # Stores length of string in LN and returns it print_and_scroll_back () { STRG="$1" LN=${#STRG} BK="" declare -i cntr=0 while test $cntr -lt $LN; do BK="$BK\e[D"; let cntr+=1; done echo -en "$STRG$BK" return $LN } # Overwrite a text of length $1 (fallback to $LN) with whitespace white_out () { BK=""; WH="" if test -n "$1"; then LN=$1; fi declare -i cntr=0 while test $cntr -lt $LN; do BK="$BK\e[D"; WH="$WH "; let cntr+=1; done echo -en "$WH$BK" } # Return hosts. sysfs must be mounted findhosts_26 () { hosts= for hostdir in /sys/class/scsi_host/host*; do hostno=${hostdir#/sys/class/scsi_host/host} if [ -f $hostdir/isp_name ] ; then hostname="qla2xxx" elif [ -f $hostdir/lpfc_drvr_version ] ; then hostname="lpfc" else hostname=`cat $hostdir/proc_name` fi hosts="$hosts $hostno" echo "Host adapter $hostno ($hostname) found." done if [ -z "$hosts" ] ; then echo "No SCSI host adapters found in sysfs" exit 1; fi hosts=`echo $hosts | sed 's/ /\n/g' | sort -n` } # Return hosts. /proc/scsi/HOSTADAPTER/? must exist findhosts () { hosts= for driverdir in /proc/scsi/*; do driver=${driverdir#/proc/scsi/} if test $driver = scsi -o $driver = sg -o $driver = dummy -o $driver = device_info; then continue; fi for hostdir in $driverdir/*; do name=${hostdir#/proc/scsi/*/} if test $name = add_map -o $name = map -o $name = mod_parm; then continue; fi num=$name driverinfo=$driver if test -r $hostdir/status; then num=$(printf '%d\n' `sed -n 's/SCSI host number://p' $hostdir/status`) driverinfo="$driver:$name" fi hosts="$hosts $num" echo "Host adapter $num ($driverinfo) found." done done } printtype () { local type=$1 case "$type" in 0) echo "Direct-Access " ;; 1) echo "Sequential-Access" ;; 2) echo "Printer " ;; 3) echo "Processor " ;; 4) echo "WORM " ;; 5) echo "CD-ROM " ;; 6) echo "Scanner " ;; 7) echo "Optical Device " ;; 8) echo "Medium Changer " ;; 9) echo "Communications " ;; 10) echo "Unknown " ;; 11) echo "Unknown " ;; 12) echo "RAID " ;; 13) echo "Enclosure " ;; 14) echo "Direct-Access-RBC" ;; *) echo "Unknown " ;; esac } print02i() { if [ "$1" = "*" ] ; then echo "00" else printf "%02i" "$1" fi } # Get /proc/scsi/scsi info for device $host:$channel:$id:$lun # Optional parameter: Number of lines after first (default = 2), # result in SCSISTR, return code 1 means empty. procscsiscsi () { if test -z "$1"; then LN=2; else LN=$1; fi CHANNEL=`print02i "$channel"` ID=`print02i "$id"` LUN=`print02i "$lun"` if [ -d /sys/class/scsi_device ]; then SCSIPATH="/sys/class/scsi_device/${host}:${channel}:${id}:${lun}" if [ -d "$SCSIPATH" ] ; then SCSISTR="Host: scsi${host} Channel: $CHANNEL Id: $ID Lun: $LUN" if [ "$LN" -gt 0 ] ; then IVEND=$(cat ${SCSIPATH}/device/vendor) IPROD=$(cat ${SCSIPATH}/device/model) IPREV=$(cat ${SCSIPATH}/device/rev) SCSIDEV=$(printf ' Vendor: %-08s Model: %-16s Rev: %-4s' "$IVEND" "$IPROD" "$IPREV") SCSISTR="$SCSISTR $SCSIDEV" fi if [ "$LN" -gt 1 ] ; then ILVL=$(cat ${SCSIPATH}/device/scsi_level) type=$(cat ${SCSIPATH}/device/type) ITYPE=$(printtype $type) SCSITMP=$(printf ' Type: %-16s ANSI SCSI revision: %02d' "$ITYPE" "$((ILVL - 1))") SCSISTR="$SCSISTR $SCSITMP" fi else return 1 fi else grepstr="scsi$host Channel: $CHANNEL Id: $ID Lun: $LUN" SCSISTR=`cat /proc/scsi/scsi | grep -A$LN -e"$grepstr"` fi if test -z "$SCSISTR"; then return 1; else return 0; fi } # Find sg device with 2.6 sysfs support sgdevice26 () { if test -e /sys/class/scsi_device/$host\:$channel\:$id\:$lun/device/generic; then SGDEV=`readlink /sys/class/scsi_device/$host\:$channel\:$id\:$lun/device/generic` SGDEV=`basename $SGDEV` else for SGDEV in /sys/class/scsi_generic/sg*; do DEV=`readlink $SGDEV/device` if test "${DEV##*/}" = "$host:$channel:$id:$lun"; then SGDEV=`basename $SGDEV`; return fi done SGDEV="" fi } # Find sg device with 2.4 report-devs extensions sgdevice24 () { if procscsiscsi 3; then SGDEV=`echo "$SCSISTR" | grep 'Attached drivers:' | sed 's/^ *Attached drivers: \(sg[0-9]*\).*/\1/'` fi } # Find sg device that belongs to SCSI device $host $channel $id $lun # and return in SGDEV sgdevice () { SGDEV= if test -d /sys/class/scsi_device; then sgdevice26 else DRV=`grep 'Attached drivers:' /proc/scsi/scsi 2>/dev/null` repdevstat=$((1-$?)) if [ $repdevstat = 0 ]; then echo "scsi report-devs 1" >/proc/scsi/scsi DRV=`grep 'Attached drivers:' /proc/scsi/scsi 2>/dev/null` if [ $? = 1 ]; then return; fi fi if ! `echo $DRV | grep 'drivers: sg' >/dev/null`; then modprobe sg fi sgdevice24 if [ $repdevstat = 0 ]; then echo "scsi report-devs 0" >/proc/scsi/scsi fi fi } # Test if SCSI device is still responding to commands testonline () { : testonline RC=0 if test ! -x /usr/bin/sg_turs; then return 0; fi sgdevice if test -z "$SGDEV"; then return 0; fi sg_turs /dev/$SGDEV >/dev/null 2>&1 RC=$? # Handle in progress of becoming ready and unit attention -- wait at max 11s declare -i ctr=0 if test $RC = 2 -o $RC = 6; then RMB=`sg_inq /dev/$SGDEV | grep 'RMB=' | sed 's/^.*RMB=\(.\).*$/\1/'` print_and_scroll_back "$host:$channel:$id:$lun $SGDEV ($RMB) " fi while test $RC = 2 -o $RC = 6 && test $ctr -le 8; do if test $RC = 2 -a "$RMB" != "1"; then echo -n "."; let $LN+=1; sleep 1 else usleep 20000; fi let ctr+=1 sg_turs /dev/$SGDEV >/dev/null 2>&1 RC=$? done if test $ctr != 0; then white_out; fi # echo -e "\e[A\e[A\e[A${yellow}Test existence of $SGDEV = $RC ${norm} \n\n\n" if test $RC = 1; then return $RC; fi # Reset RC (might be !=0 for passive paths) RC=0 # OK, device online, compare INQUIRY string INQ=`sg_inq $sg_len_arg /dev/$SGDEV 2>/dev/null` IVEND=`echo "$INQ" | grep 'Vendor identification:' | sed 's/^[^:]*: \(.*\)$/\1/'` IPROD=`echo "$INQ" | grep 'Product identification:' | sed 's/^[^:]*: \(.*\)$/\1/'` IPREV=`echo "$INQ" | grep 'Product revision level:' | sed 's/^[^:]*: \(.*\)$/\1/'` STR=`printf " Vendor: %-08s Model: %-16s Rev: %-4s" "$IVEND" "$IPROD" "$IPREV"` IPTYPE=`echo "$INQ" | sed -n 's/.* Device_type=\([0-9]*\) .*/\1/p'` IPQUAL=`echo "$INQ" | sed -n 's/ *PQual=\([0-9]*\) Device.*/\1/p'` if [ "$IPQUAL" != 0 ] ; then echo -e "\e[A\e[A\e[A\e[A${red}$SGDEV changed: ${bold}LU not available (PQual $IPQUAL)${norm} \n\n\n" return 2 fi TYPE=$(printtype $IPTYPE) procscsiscsi TMPSTR=`echo "$SCSISTR" | grep 'Vendor:'` if [ "$TMPSTR" != "$STR" ]; then echo -e "\e[A\e[A\e[A\e[A${red}$SGDEV changed: ${bold}\nfrom:${SCSISTR#* } \nto: $STR ${norm} \n\n\n" return 1 fi TMPSTR=`echo "$SCSISTR" | sed -n 's/.*Type: *\(.*\) *ANSI.*/\1/p'` if [ $TMPSTR != $TYPE ] ; then echo -e "\e[A\e[A\e[A\e[A${red}$SGDEV changed: ${bold}\nfrom:${TMPSTR} \nto: $TYPE ${norm} \n\n\n" return 1 fi return $RC } # Test if SCSI device $host $channen $id $lun exists # Outputs description from /proc/scsi/scsi (unless arg passed) # Returns SCSISTR (empty if no dev) testexist () { : testexist SCSISTR= if procscsiscsi && test -z "$1"; then echo "$SCSISTR" | head -n1 echo "$SCSISTR" | tail -n2 | pr -o4 -l1 fi } # Returns the list of existing channels per host chanlist () { local hcil local cil local chan local tmpchan for dev in /sys/class/scsi_device/${host}:* ; do [ -d $dev ] || continue; hcil=${dev##*/} cil=${hcil#*:} chan=${cil%%:*} for tmpchan in $channelsearch ; do if test "$chan" -eq $tmpchan ; then chan= fi done if test -n "$chan" ; then channelsearch="$channelsearch $chan" fi done if test -z "$channelsearch"; then channelsearch="0"; fi } # Returns the list of existing targets per host idlist () { local hcil local cil local il local target local tmpid for dev in /sys/class/scsi_device/${host}:${channel}:* ; do [ -d $dev ] || continue; hcil=${dev##*/} cil=${hcil#*:} il=${cil#*:} target=${il%%:*} for tmpid in $idsearch ; do if test "$target" -eq $tmpid ; then target= break fi done if test -n "$target" ; then idsearch="$idsearch $target" fi done } # Returns the list of existing LUNs from device $host $channel $id $lun # and returns list to stdout getluns() { sgdevice if test -z "$SGDEV"; then return; fi if test ! -x /usr/bin/sg_luns; then echo 0; return; fi LLUN=`sg_luns -d /dev/$SGDEV 2>/dev/null` if test $? != 0; then echo 0; return; fi echo "$LLUN" | sed -n 's/.*lun=\(.*\)/\1/p' } # Wait for udev to settle (create device nodes etc.) udevadm_settle() { if test -x /sbin/udevadm; then print_and_scroll_back " Calling udevadm settle (can take a while) " /sbin/udevadm settle white_out else usleep 20000 fi } # Perform scan on a single lun $host $channel $id $lun dolunscan() { SCSISTR= devnr="$host $channel $id $lun" echo "Scanning for device $devnr ... " printf "${yellow}OLD: $norm" testexist # Special case: lun 0 just got added (for reportlunscan), # so make sure we correctly treat it as new if test "$lun" = "0" -a "$1"; then SCSISTR="" printf "\r\e[A\e[A\e[A" fi : f $remove s $SCSISTR if test "$remove" -a "$SCSISTR"; then # Device exists: Test whether it's still online # (testonline returns 1 if it's gone or has changed) testonline RC=$? if test $RC != 0 -o ! -z "$forceremove"; then echo -en "\r\e[A\e[A\e[A${red}REM: " echo "$SCSISTR" | head -n1 echo -e "${norm}\e[B\e[B" if test -e /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device; then echo 1 > /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device/delete if test $RC -eq 1 -o $lun -eq 0 ; then # Try readding, should fail if device is gone echo "$channel $id $lun" > /sys/class/scsi_host/host${host}/scan fi # FIXME: Can we skip udevadm settle for removal? #udevadm_settle usleep 20000 else echo "scsi remove-single-device $devnr" > /proc/scsi/scsi if test $RC -eq 1 -o $lun -eq 0 ; then # Try readding, should fail if device is gone echo "scsi add-single-device $devnr" > /proc/scsi/scsi fi fi fi if test $RC = 0 -o "$forcerescan" ; then if test -e /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device; then echo 1 > /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device/rescan udevadm_settle fi fi printf "\r\e[A\e[A\e[A${yellow}OLD: $norm" testexist if test -z "$SCSISTR"; then printf "\r${red}DEL: $norm\r\n\n" let rmvd+=1; return 1 fi fi if test -z "$SCSISTR"; then # Device does not exist, try to add printf "\r${green}NEW: $norm" if test -e /sys/class/scsi_host/host${host}/scan; then echo "$channel $id $lun" > /sys/class/scsi_host/host${host}/scan 2> /dev/null udevadm_settle else echo "scsi add-single-device $devnr" > /proc/scsi/scsi fi testexist if test -z "$SCSISTR"; then # Device not present printf "\r\e[A"; # Optimization: if lun==0, stop here (only if in non-remove mode) if test $lun = 0 -a -z "$remove" -a $optscan = 1; then break; fi else let found+=1; fi fi } # Perform report lun scan on $host $channel $id using REPORT_LUNS doreportlun() { lun=0 SCSISTR= devnr="$host $channel $id $lun" echo -en "Scanning for device $devnr ...\r" lun0added= #printf "${yellow}OLD: $norm" # Phase one: If LUN0 does not exist, try to add testexist -q if test -z "$SCSISTR"; then # Device does not exist, try to add #printf "\r${green}NEW: $norm" if test -e /sys/class/scsi_host/host${host}/scan; then echo "$channel $id $lun" > /sys/class/scsi_host/host${host}/scan 2> /dev/null udevadm_settle else echo "scsi add-single-device $devnr" > /proc/scsi/scsi fi testexist -q if test -n "$SCSISTR"; then lun0added=1 #testonline else # Device not present # return # Find alternative LUN to send getluns to for dev in /sys/class/scsi_device/${host}:${channel}:${id}:*; do [ -d "$dev" ] || continue lun=${dev##*:} break done fi fi targetluns=`getluns` lunremove= #echo "getluns reports " $targetluns # Check existing luns for dev in /sys/class/scsi_device/${host}:${channel}:${id}:*; do [ -d "$dev" ] || continue lun=${dev##*:} newsearch= inlist= # OK, is existing $lun (still) in reported list for tmplun in $targetluns; do if test $tmplun -eq $lun ; then inlist=1 dolunscan $lun0added else newsearch="$newsearch $tmplun" fi done # OK, we have now done a lunscan on $lun and # $newsearch is the old $targetluns without $lun if [ -z "$inlist" ]; then # Stale lun lunremove="$lunremove $lun" fi # $lun removed from $lunsearch (echo for whitespace cleanup) targetluns=`echo $newsearch` done # Add new ones and check stale ones for lun in $targetluns $lunremove; do dolunscan $lun0added done } # Perform search (scan $host) dosearch () { if test -z "$channelsearch" ; then chanlist fi for channel in $channelsearch; do if test -z "$idsearch" ; then idlist fi for id in $idsearch; do if test -z "$lunsearch" ; then doreportlun else for lun in $lunsearch; do dolunscan done fi done done } expandlist () { list=$1 result="" first=${list%%,*} rest=${list#*,} while test ! -z "$first"; do beg=${first%%-*}; if test "$beg" = "$first"; then result="$result $beg"; else end=${first#*-} result="$result `seq $beg $end`" fi test "$rest" = "$first" && rest="" first=${rest%%,*} rest=${rest#*,} done echo $result } # main if test @$1 = @--help -o @$1 = @-h -o @$1 = @-?; then echo "Usage: rescan-scsi-bus.sh [options] [host [host ...]]" echo "Options:" echo " -l activates scanning for LUNs 0--7 [default: 0]" echo " -L NUM activates scanning for LUNs 0--NUM [default: 0]" echo " -w scan for target device IDs 0--15 [default: 0--7]" echo " -c enables scanning of channels 0 1 [default: 0 / all detected ones]" echo " -r enables removing of devices [default: disabled]" echo " -i issue a FibreChannel LIP reset [default: disabled]" echo "--remove: same as -r" echo "--issue-lip: same as -i" echo "--forcerescan: Rescan existing devices" echo "--forceremove: Remove and readd every device (DANGEROUS)" echo "--nooptscan: don't stop looking for LUNs is 0 is not found" echo "--color: use coloured prefixes OLD/NEW/DEL" echo "--hosts=LIST: Scan only host(s) in LIST" echo "--channels=LIST: Scan only channel(s) in LIST" echo "--ids=LIST: Scan only target ID(s) in LIST" echo "--luns=LIST: Scan only lun(s) in LIST" echo "--sync/nosync: Issue a sync / no sync [default: sync if remove]" echo "--attachpq3: Tell kernel to attach sg to LUN 0 that reports PQ=3" echo "--reportlun2: Tell kernel to try REPORT_LUN even on SCSI2 devices" echo "--largelun: Tell kernel to support LUNs > 7 even on SCSI2 devs" echo "--sparselun: Tell kernel to support sparse LUN numbering" echo " Host numbers may thus be specified either directly on cmd line (deprecated) or" echo " or with the --hosts=LIST parameter (recommended)." echo "LIST: A[-B][,C[-D]]... is a comma separated list of single values and ranges" echo " (No spaces allowed.)" exit 0 fi if test ! -d /sys/class/scsi_host/ -a ! -d /proc/scsi/; then echo "Error: SCSI subsystem not active" exit 1 fi # Make sure sg is there modprobe sg >/dev/null 2>&1 if test -x /usr/bin/sg_inq; then sg_version=$(sg_inq -V 2>&1 | cut -d " " -f 3) sg_version=${sg_version##0.} #echo "\"$sg_version\"" if [ -z "$sg_version" -o "$sg_version" -lt 70 ] ; then sg_len_arg="-36" else sg_len_arg="--len=36" fi fi # defaults unsetcolor lunsearch= opt_idsearch=`seq 0 7` opt_channelsearch= remove= forceremove= optscan=1 sync=1 declare -i scan_flags=0 if test -d /sys/class/scsi_host; then findhosts_26 else findhosts fi # Scan options opt="$1" while test ! -z "$opt" -a -z "${opt##-*}"; do opt=${opt#-} case "$opt" in l) lunsearch=`seq 0 7` ;; L) lunsearch=`seq 0 $2`; shift ;; w) opt_idsearch=`seq 0 15` ;; c) opt_channelsearch="0 1" ;; r) remove=1 ;; i) lipreset=1 ;; -remove) remove=1 ;; -forcerescan) remove=1; forcerescan=1 ;; -forceremove) remove=1; forceremove=1 ;; -hosts=*) arg=${opt#-hosts=}; hosts=`expandlist $arg` ;; -channels=*) arg=${opt#-channels=};opt_channelsearch=`expandlist $arg` ;; -ids=*) arg=${opt#-ids=}; opt_idsearch=`expandlist $arg` ;; -luns=*) arg=${opt#-luns=}; lunsearch=`expandlist $arg` ;; -color) setcolor ;; -nooptscan) optscan=0 ;; -issue-lip) lipreset=1 ;; -sync) sync=2 ;; -nosync) sync=0 ;; -attachpq3) scan_flags=$(($scan_flags|0x1000000)) ;; -reportlun2) scan_flags=$(($scan_flags|0x20000)) ;; -largelun) scan_flags=$(($scan_flags|0x200)) ;; -sparselun) scan_flags=$((scan_flags|0x40)) ;; *) echo "Unknown option -$opt !" ;; esac shift opt="$1" done # Hosts given ? if test "@$1" != "@"; then hosts=$* fi if [ -d /sys/class/scsi_host -a ! -w /sys/class/scsi_host ]; then echo "You need to run scsi-rescan-bus.sh as root" exit 2 fi if test "$sync" = 1 -a "$remove" = 1; then sync=2; fi if test "$sync" = 2; then echo "Syncing file systems"; sync; fi if test -w /sys/module/scsi_mod/parameters/default_dev_flags -a $scan_flags != 0; then OLD_SCANFLAGS=`cat /sys/module/scsi_mod/parameters/default_dev_flags` NEW_SCANFLAGS=$(($OLD_SCANFLAGS|$scan_flags)) if test "$OLD_SCANFLAGS" != "$NEW_SCANFLAGS"; then echo -n "Temporarily setting kernel scanning flags from " printf "0x%08x to 0x%08x\n" $OLD_SCANFLAGS $NEW_SCANFLAGS echo $NEW_SCANFLAGS > /sys/module/scsi_mod/parameters/default_dev_flags else unset OLD_SCANFLAGS fi fi echo "Scanning SCSI subsystem for new devices" test -z "$remove" || echo " and remove devices that have disappeared" declare -i found=0 declare -i rmvd=0 for host in $hosts; do echo -n "Scanning host $host " if test -e /sys/class/fc_host/host$host ; then # It's pointless to do a target scan on FC if test -n "$lipreset" ; then echo 1 > /sys/class/fc_host/host$host/issue_lip 2> /dev/null; fi # Always trigger a rescan for FC to update channels and targets echo "- - -" > /sys/class/scsi_host/host$host/scan 2> /dev/null; channelsearch= idsearch= udevadm_settle else channelsearch=$opt_channelsearch idsearch=$opt_idsearch fi [ -n "$channelsearch" ] && echo -n "channels $channelsearch " echo -n "for " if [ -n "$idsearch" ] ; then echo -n " SCSI target IDs " $idsearch else echo -n " all SCSI target IDs" fi if [ -n "$lunsearch" ] ; then echo ", LUNs " $lunsearch else echo ", all LUNs" fi dosearch done if test -n "$OLD_SCANFLAGS"; then echo $OLD_SCANFLAGS > /sys/module/scsi_mod/parameters/default_dev_flags fi echo "$found new device(s) found. " echo "$rmvd device(s) removed. " ================================================ FILE: scripts/start-mhvtl-scst.sh ================================================ #!/bin/sh modprobe mpt2sas modprobe pl2303 modprobe scst modprobe scst-vdisk modprobe scst_tape modprobe scst_changer # for iSCSI modprobe scsi_transport_iscsi modprobe iscsi-scst iscsi-scstd iscsid # for FC modprobe qla2x00tgt modprobe scst_user modprobe scst_local /opt/fast/application/fileio/fileio_tgt_gpu -o -e 1 -b 4096 gpu01 gpu -- -o -s $((1000*1024*1024*1024)) $(/opt/fast/scripts/diskmapper.sh /opt/fast/scripts/disks_shelf_2.lst) & sleep 3 echo "add gpu01 0" > /sys/kernel/scst_tgt/targets/scst_local/scst_local_tgt/luns/mgmt DEVICE=`lsscsi | grep gpu01 | awk '{print $6;}'` echo "Formatting GPU Device: $DEVICE" mkfs.ext4 $DEVICE mount $DEVICE /mnt mkdir -p /mnt/mhvtl-data chown vtl:vtl /mnt/mhvtl-data ln -s /mnt/mhvtl-data /opt/mhvtl make_vtl_media systemctl start mhvtl sleep 5 # create SCST config ( cat << 'EOF' HANDLER dev_tape { DEVICE 7:0:1:0 DEVICE 7:0:2:0 } HANDLER dev_changer { DEVICE 7:0:0:0 } TARGET_DRIVER qla2x00t { enabled 1 TARGET 21:00:00:24:ff:05:7b:1a { LUN 1 7:0:0:0 LUN 2 7:0:1:0 LUN 3 7:0:2:0 enabled 1 } } EOF ) > /etc/scst.conf scstadmin -config ================================================ FILE: scripts/stgt-target-setup.conf ================================================ # This is a sample config file for tgt-admin. # By default, tgt-admin looks for its config file in /etc/tgt/targets.conf # Set the driver. If not specified, defaults to "iscsi". default-driver iscsi # Sample target with one LUN only. Defaults to allow access for all initiators: device-type pt bs-type sg backing-store /dev/sg2 backing-store /dev/sg3 allow-in-use yes ================================================ FILE: scripts/test_lbp.sh ================================================ #!/bin/bash #systemctl stop mhvtl #systemctl start mhvtl if [[ $EUID -ne 0 ]]; then echo "Sorry, this script needs to be run as root" exit 1 fi # Library source slot to move tape to/from SOURCE_SLOT=1 # Which drive are we testing - /etc/mhvtl/device.conf DRV_INDEX=18 while [[ $# -gt 0 ]]; do case $1 in -i|--index) if [ -z "$2" ]; then # Check for missing arg echo "Usage: Need to specify drive index" echo "e.g. $0 -i 11" exit 1 fi DRV_INDEX="$2" shift # past argument shift # past value ;; -s|--source) if [ -z "$2" ]; then # Check for missing arg echo "Usage: Need to specify source slot" echo "e.g. $0 -s 1" exit 1 fi SOURCE_SLOT="$2" shift # past argument shift # past value ;; *) shift # past argument ;; esac done i=`grep -A6 "^Drive: ${DRV_INDEX} " /etc/mhvtl/device.conf | awk '/Library/ {print $5}'` # Convert into hex - leading '0' typically means it's an octal value TARGET_DRIVE=$((16#${i}-1)) if [ ${TARGET_DRIVE} -lt 0 ]; then echo "Unable to find drive at index ${DRV_INDEX}... Exiting" echo "Perhaps provide drive index using \"-i \"" echo "e.g. $0 -i 11" exit fi read -r channel id lun <<< `grep "^Drive: ${DRV_INDEX} " /etc/mhvtl/device.conf | awk '{print $4,$6,$8}'` #echo "Channel: $channel, id: $id, lun: $lun" HBA=`lsscsi -H | awk '/mhvtl/ {print $1}' | sed -e 's/\[//g' -e 's/\]//g'` SG=`lsscsi -g ${HBA} ${channel} ${id} ${lun} | awk '{print $7}'` ST=`lsscsi -g ${HBA} ${channel} ${id} ${lun} | awk '{print $6}'` MTX=`lsscsi -g ${HBA} 0 0 0 | awk '{print $7}'` #echo "HBA: ${HBA}" #echo "st : ${ST}" #echo "sg : ${SG}" #echo "mtx : ${MTX}" echo "++ Moving tape slot: ${SOURCE_SLOT} to drive: ${TARGET_DRIVE}" mtx -f ${MTX} load ${SOURCE_SLOT} ${TARGET_DRIVE} vtlcmd ${DRV_INDEX} verbose vtlcmd ${DRV_INDEX} verbose echo echo "++ Checking status of ${ST}" mt -f ${ST} status echo CRC32C=2 RSCRC=1 LBP_W="40" LBP_R="80" LBP_RW="c0" ## Set LBP_W #sg_wr_mode -p 0x0a,0xf0 -d -c 0a,f0,00,1c,${CRC32C},4,${LBP_W},0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ${SG} ## Set LBP_R #sg_wr_mode -p 0x0a,0xf0 -d -c 0a,f0,00,1c,${CRC32C},4,${LBP_R},0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ${SG} # Set LBP_R & LBP_W echo -e "++ Enable LBP CRC32C RW\n" sg_wr_mode -p 0x0a,0xf0 -d -c 0a,f0,00,1c,${CRC32C},4,${LBP_RW},0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ${SG} echo -e "++ Reading 64k + 4 to /tmp/CRC32c.out" dd if=${ST} of=/tmp/CRC32c.out bs=65540 count=1 echo echo -e "++ Enable LBP Reed-Solomon CRC RW\n" sg_wr_mode -p 0x0a,0xf0 -d -c 0a,f0,00,1c,${RSCRC},4,${LBP_RW},0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ${SG} echo "++ Reading 64k + 4 to /tmp/RS-CRC.out" dd if=${ST} of=/tmp/RS-CRC.out bs=65540 count=1 echo # Turn off LBP echo -e "++ Turn off LBP\n" sg_wr_mode -p 0x0a,0xf0 -d -c 0a,f0,00,1c,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ${SG} echo "++ Reading 64k to /tmp/NO_LBP.out" dd if=${ST} of=/tmp/NO_LBP.out bs=65540 count=1 echo echo "Offline drive" mt -f ${ST} offline echo "Moving tape from drive: ${TARGET_DRIVE} to slot: ${SOURCE_SLOT}" mtx -f ${MTX} unload ${SOURCE_SLOT} ${TARGET_DRIVE} ================================================ FILE: scripts/update_device.conf.in ================================================ #!/usr/bin/perl -w # # Copyright (C) 2005 - 2025 Mark Harvey markh794@gmail.com # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # use strict; my $if; my $lib; my $slt = 1; my $num_drives = 0; my $old_device_conf = "@CONF_PATH@/device.conf"; my $new_device_conf = "@CONF_PATH@/device.conf_$$"; if (! -e $old_device_conf ) { die "Can not find $old_device_conf"; } # Confirm device.conf not current version.. # Also count number of drives. open(IF, " < $old_device_conf") || die "Can't open $old_device_conf"; while($if = ) { if ($if =~ /VERSION:\s+(\d+)/) { if ($1 == 4) { print "Nothing to upgrade.. Exiting\n"; exit 0; } } if ($if =~ /Library\sID:.*Slot:/) { # Found V4 type entry.. Exit exit 0; } if ($if =~ /^Drive/) { $num_drives++; } } close IF; # Now create new temporary device.conf $lib = $num_drives + 1; open(IF, " < $old_device_conf") || die "Can't open $old_device_conf for reading"; open(OF, " > $new_device_conf") || die "Can't open $new_device_conf for writing"; while($if = ) { # Update library 'index' with max drives + 1 $if =~ s/^Library:\s(\d+)/Library: $lib/g; if ($if =~ /VERSION:/) { print OF "VERSION: 4\n"; } else { print OF $if; } if ($if =~ /^Drive:\s(\d+)/) { print OF " Library ID: $lib Slot: $slt\n"; $slt++; } } close IF; close OF; # Now remove original config file and replace with new one unlink($old_device_conf); system("mv $new_device_conf $old_device_conf"); # Now 'move' library_contents to library_contents.$lib $new_device_conf = "@CONF_PATH@/library_contents.$lib"; $old_device_conf = "@CONF_PATH@/library_contents"; open(IF, " < $old_device_conf") || die "Can't open $old_device_conf for reading"; open(OF, " > $new_device_conf") || die "Can't open $new_device_conf for writing"; while($if = ) { print OF $if; } close IF; close OF; # Now remove original file(s) unlink($old_device_conf); exit 0; ================================================ FILE: tcopy/Makefile ================================================ # Makefile for linux port of Tcopy # By Nicholas Harbour, 2000 CURDIR = "../" include ../config.mk BINARY=tcopy MANPAGE=tcopy.1 BIN_PATH=$(PREFIX)/bin/ MAN_PATH=$(PREFIX)/$(MANDIR)/man1/ OBJS=tcopy.o CC=gcc OPTS=-Wall all: tcopy install tcopy: clean $(OBJS) $(CC) $(OPTS) -o $(BINARY) $(OBJS) install: tcopy cp $(BINARY) $(BIN_PATH) cp $(MANPAGE) $(MAN_PATH) clean: rm -f $(OBJS) tcopy ================================================ FILE: tcopy/pathnames.h ================================================ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 */ #define _PATH_DEFTAPE "/dev/nst0" ================================================ FILE: tcopy/tcopy.1 ================================================ .\" Copyright (c) 1985, 1990, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)tcopy.1 8.2 (Berkeley) 4/17/94 .\" $FreeBSD: src/usr.bin/tcopy/tcopy.1,v 1.7 2000/03/01 12:20:12 sheldonh Exp $ .\" .Dd April 17, 1994 .Dt TCOPY 1 .Os BSD 4.3 .Sh NAME .Nm tcopy .Nd copy and/or verify mag tapes .Sh SYNOPSIS .Nm .Op Fl cvx .Op Fl s Ar maxblk .Oo Ar src Op Ar dest .Oc .Sh DESCRIPTION .Nm Tcopy is designed to copy magnetic tapes. The only assumption made about the tape is that there are two tape marks at the end. .Nm Tcopy with only a source tape .Pf ( Ar /dev/nst0 by default) specified will print information about the sizes of records and tape files. If a destination is specified a copy will be made of the source tape. The blocking on the destination tape will be identical to that used on the source tape. Copying a tape will yield the same output as if just printing the sizes. .Pp Options: .Bl -tag -width s_maxblk .It Fl c Copy .Ar src to .Ar dest and then verify that the two tapes are identical. .It Fl s Ar maxblk Specify a maximum block size, .Ar maxblk . .It Fl v Given the two tapes, .ar src and .Ar dest verify that they are identical. .It Fl x Output all informational messages to the standard error. This option is useful when .Ar dest is .Pa /dev/stdout . .El .Sh SEE ALSO .Xr mtio 4 .Sh HISTORY The .Nm command appeared in .Bx 4.3 . .Sh BUGS Writting an image of a tape to a file does not preserve much more than the raw data. Block size(s) and tape EOF marks are lost which would otherwise be preserved in a tape-to-tape copy. EOD is determined by two sequential EOF marks with no data between. There are old systems which typically wrote three EOF's between tape files. .Xr tcopy 1 will erroneously stop copying early in this case. When using the copy/verify option \-c .Xr tcopy 1 does not rewind the tapes prior to start. A rewind is performed after writing prior to the verification stage. If one doesn't start at BOT then the comparison may not be of the intended data. ================================================ FILE: tcopy/tcopy.c ================================================ /* * Copyright (c) 1985, 1987, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* Linux port - Nicholas Harbour 2000 */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1985, 1987, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static const char sccsid[] = "@(#)tcopy.c 8.2 (Berkeley) 4/17/94"; #endif static const char rcsid[] = "$FreeBSD: src/usr.bin/tcopy/tcopy.c,v 1.2.2.4 1999/09/05 11:33:13 peter Exp $"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #define MAXREC (640 * 1024) #define NOCOUNT (-2) int filen, guesslen, maxblk = MAXREC; u_long lastrec, record, size, tsize; /*FILE *msg = stdout;*/ FILE *msg; /*njh 2000*/ void *getspace __P((int)); void intr __P((int)); static void usage __P((void)); void verify __P((int, int, char *)); void writeop __P((int, int)); int main(int argc, char *argv[]) { register int lastnread, nread, nw, inp, outp; enum {READ, VERIFY, COPY, COPYVERIFY} op = READ; sig_t oldsig; int ch, needeof; char *buff, *inf; msg = stdout; guesslen = 1; while ((ch = getopt(argc, argv, "cs:vx")) != -1) switch ((char)ch) { case 'c': op = COPYVERIFY; break; case 's': maxblk = atoi(optarg); if (maxblk <= 0) { warnx("illegal block size"); usage(); } guesslen = 0; break; case 'v': op = VERIFY; break; case 'x': msg = stderr; break; case '?': default: usage(); } argc -= optind; argv += optind; switch (argc) { case 0: if (op != READ) usage(); inf = _PATH_DEFTAPE; break; case 1: if (op != READ) usage(); inf = argv[0]; break; case 2: if (op == READ) op = COPY; inf = argv[0]; outp = open(argv[1], op == VERIFY ? O_RDONLY : op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE); if (outp < 0) err(3, "%s", argv[1]); break; default: usage(); } inp = open(inf, O_RDONLY, 0); if (inp < 0) err(1, "%s", inf); buff = getspace(maxblk); if (op == VERIFY) { verify(inp, outp, buff); exit(0); } oldsig = signal(SIGINT, SIG_IGN); if (oldsig != SIG_IGN) (void) signal(SIGINT, intr); needeof = 0; for (lastnread = NOCOUNT;;) { nread = read(inp, buff, maxblk); if (nread == -1) { while (errno == EINVAL && (maxblk -= 1024)) { nread = read(inp, buff, maxblk); if (nread >= 0) goto r1; } err(1, "read err, File %d, Record %ld", filen, record); } else if (nread != lastnread) { if (lastnread != 0 && lastnread != NOCOUNT) { if (lastrec == 0 && nread == 0) fprintf(msg, "%ld records\n", record); else if (record - lastrec > 1) fprintf(msg, "records %ld to %ld\n", lastrec, record); else fprintf(msg, "record %ld\n", lastrec); } if (nread != 0) fprintf(msg, "file %d: block size %d: ", filen, nread); (void) fflush(stdout); lastrec = record; } r1: guesslen = 0; if (nread > 0) { if (op == COPY || op == COPYVERIFY) { if (needeof) { writeop(outp, MTWEOF); needeof = 0; } nw = write(outp, buff, nread); if (nw != nread) { if (nw == -1) { warn("write err, File %d, " "Record %ld", filen, record); } else { warnx("write err, File %d, Record %ld", filen, record); warnx("write (%d) != read (%d)", nw, nread); } errx(5, "copy aborted"); } } size += nread; record++; } else { if (lastnread <= 0 && lastnread != NOCOUNT) { fprintf(msg, "eot\n"); break; } fprintf(msg, "file %d: eof after %lu records: %lu bytes\n", filen, record, size); needeof = 1; filen++; tsize += size; size = record = lastrec = 0; lastnread = 0; } lastnread = nread; } fprintf(msg, "total length: %lu bytes\n", tsize); (void)signal(SIGINT, oldsig); if (op == COPY || op == COPYVERIFY) { writeop(outp, MTWEOF); writeop(outp, MTWEOF); if (op == COPYVERIFY) { writeop(outp, MTREW); writeop(inp, MTREW); verify(inp, outp, buff); } } exit(0); } void verify(register int inp, register int outp, register char *outb) { register int eot, inmaxblk, inn, outmaxblk, outn; register char *inb; inb = getspace(maxblk); inmaxblk = outmaxblk = maxblk; for (eot = 0;; guesslen = 0) { inn = read(inp, inb, inmaxblk); if (inn == -1) { if (guesslen) while (errno == EINVAL && (inmaxblk -= 1024)) { inn = read(inp, inb, inmaxblk); if (inn >= 0) goto r1; } warn("read error"); break; } r1: outn = read(outp, outb, outmaxblk); if (outn == -1) { if (guesslen) while (errno == EINVAL && (outmaxblk -= 1024)) { outn = read(outp, outb, outmaxblk); if (outn >= 0) goto r2; } warn("read error"); break; } r2: if (inn != outn) { fprintf(msg, "%s: tapes have different block sizes; %d != %d.\n", "tcopy", inn, outn); break; } if (!inn) { if (eot++) { fprintf(msg, "tcopy: tapes are identical.\n"); return; } } else { if (bcmp(inb, outb, inn)) { fprintf(msg, "tcopy: tapes have different data.\n"); break; } eot = 0; } } exit(1); } void intr(int signo) { if (record) if (record - lastrec > 1) fprintf(msg, "records %ld to %ld\n", lastrec, record); else fprintf(msg, "record %ld\n", lastrec); fprintf(msg, "interrupt at file %d: record %ld\n", filen, record); fprintf(msg, "total length: %ld bytes\n", tsize + size); exit(1); } void *getspace(int blk) { void *bp; bp = malloc((size_t)blk); if (bp == NULL) errx(11, "no memory"); return bp; } void writeop(int fd, int type) { struct mtop op; op.mt_op = type; op.mt_count = (daddr_t)1; if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) err(6, "tape op"); } static void usage(void) { fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n"); exit(1); } ================================================ FILE: todo ================================================ - Review return values from functions and make consistent. Some functions return SAM_STAT_XXX, some return *sam_status - Major -> Add personality modules to library ================================ ================================================ FILE: usr/.gitignore ================================================ mhvtl-device-conf-generator dump_messageQ dump_tape edit_tape preload_tape libvtlcart.so libvtlscsi.so make_vtl_media mktape tapeexerciser vtlcmd vtllibrary vtltape TAGS *.d ================================================ FILE: usr/Makefile ================================================ # # This makefile needs to be invoked as follows: # #make # # Here, options include: # # all to build all utilities # clean to clean up all intermediate files # # # Makefile magic # $@ is a variable that expands to the name of the file being built # $< is a variable that expands to the name of the source file # @ at the beginning of the first line tell make not to echo the commands as it run it. # CURDIR = "../" include ../config.mk IFLAGS = -I ../include -I ../include/utils -I ../include/common -I ../ccan CFLAGS = -Wall -Wshadow -g -O2 -D_LARGEFILE64_SOURCE $(RPM_OPT_FLAGS) $(IFLAGS) CFLAGS += -MMD -MP # create header dependencies dynamically (.d files) CFLAGS += -std=gnu99 CFLAGS += -DMHVTL_VERSION=\"$(VERSION).$(EXTRAVERSION)\" CFLAGS += -DMHVTL_GITDATE=\"$(GITDATE)\" CFLAGS += -DMHVTL_GITHASH=\"$(GITHASH)\" CFLAGS += -D_GNU_SOURCE CFLAGS += -DMHVTL_DEBUG CFLAGS += -DMHVTL_HOME_PATH=\"$(MHVTL_HOME_PATH)\" CFLAGS += -DMHVTL_CONFIG_PATH=\"$(MHVTL_CONFIG_PATH)\" CFLAGS += -DSYSTEMD_SERVICE_DIR=\"$(SYSTEMD_SERVICE_DIR)\" CLFLAGS = -shared ${RPM_OPT_FLAGS} # Enable LZODEBUG #LZODEBUG = -DLZO_DEBUG LZODEBUG = # files that need to be generated GENERATED_FILES = $(patsubst cmd/%.in,bin/%,$(wildcard cmd/*.in)) BINARIES = $(patsubst cmd/%.c,bin/%,$(wildcard cmd/*.c)) bin/dump_tape LIBRARIES = libvtlscsi.so all: | bin all: $(LIBRARIES) $(BINARIES) $(GENERATED_FILES) validate_crc bin : install -d -m 755 $@ # ================== objects ================== # ------------ VTL objects VTL_SRC = $(wildcard ./*.c) VTL_DEP = $(VTL_SRC:.c=.d) -include $(VTL_DEP) %.o: %.c $(CC) $(CFLAGS) -o $@ -c $< mhvtl_log.o mode.o \ smc.o spc.o \ vtlcart.o vtllib.o: \ CFLAGS += -fpic # ------------ personality modules PM_SRC = $(wildcard pm/*.c) PM_DEP = $(PM_SRC:.c=.d) -include $(PM_DEP) pm/%.o: pm/%.c $(CC) $(CFLAGS) -o $@ -c $< # ------------ cmds CMD_SRC = $(wildcard cmd/*.c) CMD_DEP = $(CMD_SRC:.c=.d) -include $(CMD_DEP) cmd/%.o: cmd/%.c $(CC) $(CFLAGS) -o $@ -c $< cmd/tape_util.o: \ CFLAGS += -fPIC # ------------ utils UTILS_SRC = $(wildcard utils/*.c) UTILS_DEP = $(UTILS_SRC:.c=.d) -include $(UTILS_DEP) utils/%.o: utils/%.c $(CC) $(CFLAGS) -o $@ -c $< utils/minilzo.o: \ CFLAGS += $(LZODEBUG) utils/q.o utils/subprocess.o utils/mhvtl_update.o: \ CFLAGS += -fpic # ================== libs ================== libvtlscsi.so: vtllib.o mhvtl_log.o mode.o \ vtlcart.o \ spc.o smc.o \ utils/q.o \ utils/subprocess.o \ utils/mhvtl_update.o $(CC) $(CLFLAGS) -o $@ $^ -lpthread # ================== Commands and scripts ================== bin/validate_crc: utils/validate_crc.o utils/crc32c.o utils/reed-solomon.o @$(CC) $(CFLAGS) -o $@ $^ .PHONY: validate_crc validate_crc: bin/validate_crc @./bin/validate_crc bin/tapeexerciser: cmd/tapeexerciser.o $(CC) $(CFLAGS) -o $@ $^ DUMP_MESSAGEQ_OBJ = cmd/dump_messageQ.o bin/dump_messageQ: $(DUMP_MESSAGEQ_OBJ) libvtlscsi.so $(CC) $(CFLAGS) -o $@ $(DUMP_MESSAGEQ_OBJ) -L. -lvtlscsi VTLCMD_OBJ = cmd/vtlcmd.o bin/vtlcmd: $(VTLCMD_OBJ) libvtlscsi.so $(CC) $(CFLAGS) -o $@ $(VTLCMD_OBJ) -L. -lvtlscsi bin/preload_tape: bin/dump_tape @rm -f $@ ln -s $? $@ bin/tape_util: @true DUMP_TAPE_OBJ = cmd/tape_util.o \ mhvtl_io.o \ utils/minilzo.o \ utils/crc32c.o \ utils/reed-solomon.o \ pm/default_ssc_pm.o bin/dump_tape: $(DUMP_TAPE_OBJ) libvtlscsi.so $(CC) $(CFLAGS) -o $@ $(DUMP_TAPE_OBJ) -L. -lz -lvtlscsi MKTAPE_OBJ = cmd/mktape.o bin/mktape: $(MKTAPE_OBJ) libvtlscsi.so $(CC) $(CFLAGS) -o $@ $(MKTAPE_OBJ) -L. -lvtlscsi EDIT_TAPE_OBJ = cmd/edit_tape.o bin/edit_tape: $(EDIT_TAPE_OBJ) libvtlscsi.so $(CC) $(CFLAGS) -o $@ $(EDIT_TAPE_OBJ) -L. -lvtlscsi VTLLIBRARY_OBJ = cmd/vtllibrary.o \ vtl_cart_type.o \ pm/stklxx_pm.o \ pm/hp_smc_pm.o \ pm/overland_pm.o \ pm/spectra_pm.o \ pm/scalar_pm.o \ pm/ibm_smc_pm.o \ pm/default_smc_pm.o bin/vtllibrary: $(VTLLIBRARY_OBJ) libvtlscsi.so $(CC) $(CFLAGS) -o $@ $(VTLLIBRARY_OBJ) -L. -lvtlscsi VTLTAPE_OBJ = cmd/vtltape.o \ mhvtl_io.o ssc.o \ utils/minilzo.o \ utils/crc32c.o \ utils/reed-solomon.o \ pm/default_ssc_pm.o \ pm/ult3580_pm.o \ pm/hp_ultrium_pm.o \ pm/stk9x40_pm.o \ pm/quantum_dlt_pm.o \ pm/ait_pm.o \ pm/t10000_pm.o \ pm/ibm_03592_pm.o bin/vtltape: $(VTLTAPE_OBJ) libvtlscsi.so $(CC) $(CFLAGS) -o $@ $(VTLTAPE_OBJ) -lz -L. -lvtlscsi MHVTL_DEVICE_CONF_GENERATOR_OBJ = cmd/mhvtl-device-conf-generator.o bin/mhvtl-device-conf-generator: $(MHVTL_DEVICE_CONF_GENERATOR_OBJ) libvtlscsi.so $(CC) $(CFLAGS) -o $@ $(MHVTL_DEVICE_CONF_GENERATOR_OBJ) -L. -lvtlscsi bin/make_vtl_media: cmd/make_vtl_media.in sed -e s'/@CONF_PATH@/$(CONFIG_PATH)/' \ -e s'/@HOME_PATH@/$(HOME_PATH)/' $< > $@ chmod 755 $@ bin/mhvtl_kernel_mod_build: cmd/mhvtl_kernel_mod_build.in sed -e s'|@FIRMWAREDIR@|${FIRMWAREDIR}|' $< > $@ chmod 755 $@ .PHONY:clean clean: $(RM) *.d *.o *.so \ pm/*.d pm/*.o \ utils/*.d utils/*.o \ cmd/*.d cmd/*.o \ bin/* $(RM) TAGS # ========== command targets ========== $(DESTDIR)$(PREFIX)/bin: install -d -m 755 $@ $(DESTDIR)$(LIBDIR): install -d -m 755 $@ $(DESTDIR)$(LIBDIR)/%.so: %.so | $(DESTDIR)$(LIBDIR) install -m 755 $< $@ $(DESTDIR)$(PREFIX)/bin/%: bin/% | $(DESTDIR)$(PREFIX)/bin install -m 755 $< $@ $(DESTDIR)$(PREFIX)/bin/tape_util: bin/tape_util @true .PHONY:install install: all \ $(addprefix $(DESTDIR)$(PREFIX)/,$(BINARIES) $(GENERATED_FILES)) \ $(addprefix $(DESTDIR)$(LIBDIR)/,$(LIBRARIES)) @rm -f $(DESTDIR)$(PREFIX)/bin/preload_tape ln $(DESTDIR)$(PREFIX)/bin/dump_tape $(DESTDIR)$(PREFIX)/bin/preload_tape [ -d $(DESTDIR)$(SYSTEMD_GENERATOR_DIR) ] || install -d -m 755 $(DESTDIR)$(SYSTEMD_GENERATOR_DIR) install -m 755 bin/mhvtl-device-conf-generator $(DESTDIR)$(SYSTEMD_GENERATOR_DIR)/ restorecon -R -v $(DESTDIR)$(SYSTEMD_GENERATOR_DIR) ifeq ($(ROOTUID),YES) ldconfig endif .PHONY: uninstall uninstall: rm -f $(addprefix $(DESTDIR)$(PREFIX)/,$(BINARIES) $(GENERATED_FILES)) \ $(addprefix $(DESTDIR)$(LIBDIR)/,$(LIBRARIES)) \ $(DESTDIR)$(SYSTEMD_GENERATOR_DIR)/mhvtl-device-conf-generator restorecon -R -v $(DESTDIR)$(SYSTEMD_GENERATOR_DIR) ifeq ($(ROOTUID),YES) ldconfig endif .PHONY:tar tar: make -C ../ tar .PHONY:tags tags: etags -R *.[ch] ../kernel/*.h # ========== Cleaning ========== .PHONY:clean clean: $(RM) *.d *.o *.so \ pm/*.d pm/*.o \ utils/*.d utils/*.o \ cmd/*.d cmd/*.o \ bin/* $(RM) TAGS .PHONY:distclean distclean: clean ================================================ FILE: usr/cmd/dump_messageQ.c ================================================ /* * dump_messageQ - A utility to empty & examine a message queue * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * * Modification History: * 2010-03-31 hstadler - source code revision, argument checking * * Dump any existing data in the messageQ. */ #include #include #include #include #include #include "q.h" char mhvtl_driver_name[] = "dump_messageQ"; static void usage(char *prog) { fprintf(stdout, "Usage : %s [-h|-help]\n", prog); fprintf(stdout, "Version: %s\n\n", MHVTL_VERSION); fprintf(stdout, "Dumping message queue content of " "library/tape queue.\n"); fprintf(stdout, "Primarily used for debugging purposes.\n\n"); } int main(int argc, char **argv) { int r_qid; long mcounter = 0; int i; struct q_entry r_entry; my_id = 0; /* checking several positions of -h/-help */ for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-h")) { usage(argv[0]); exit(1); } if (!strcmp(argv[i], "-?")) { usage(argv[0]); exit(1); } if (!strcmp(argv[i], "/h")) { usage(argv[0]); exit(1); } if (!strcmp(argv[i], "/?")) { usage(argv[0]); exit(1); } if (!strcmp(argv[i], "-help")) { usage(argv[0]); exit(1); } } if (argc > 1) { printf("Invalid option: %s\n", argv[1]); printf("Try '%s -h' for more information\n", argv[0]); exit(1); } /* Initialize message queue as necessary */ r_qid = init_queue(); if (r_qid == -1) exit(1); while (msgrcv(r_qid, &r_entry, MAXOBN, 0, IPC_NOWAIT) > 0) { mcounter++; if (mcounter == 1) { printf("\nDump Message Queue Content\n\n"); printf("%6s %6s %6s %-55s\n", "MessNo", "RcvID", "SndID", "MessageText"); } printf("%6ld %6ld %6ld %-55s\n", mcounter, r_entry.rcv_id, r_entry.msg.snd_id, r_entry.msg.text); } if (mcounter == 0) printf("Message queue empty\n"); exit(0); } ================================================ FILE: usr/cmd/edit_tape.c ================================================ /* * edit_tape is portion of the mhVTL package. * * The vtl package consists of: * a kernel module (vlt.ko) - Currently on 2.6.x Linux kernel support. * SCSI target daemons for both SMC and SSC devices. * * Copyright (C) 2005 - 2025 Mark Harvey markh794@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include "be_byteshift.h" #include "vtlcart.h" #include "vtllib.h" #include "logging.h" #if defined _LARGEFILE64_SOURCE static void *largefile_support = "large file support"; #else static void *largefile_support = "No largefile support"; #endif /* The following variables are needed for the MHVTL_DBG() macro to work. */ char mhvtl_driver_name[] = "edit_tape"; static int wp = 0; /* Write protect flag */ #define WRITE_PROTECT_OFF 1 #define WRITE_PROTECT_ON 2 void usage(char *progname) { printf("Usage: %s -l lib -m PCL [-s size] [-t type] [-d density]" " [-w on|off]\n", progname); printf(" Where 'size' is in Megabytes\n"); printf(" 'lib' is Library number\n"); printf(" 'type' is data | clean | WORM\n"); printf(" 'PCL' is Physical Cartridge Label (barcode)\n"); printf(" '-w on|off' enable/disable write protect flag\n"); printf(" 'density' can be on of the following:\n"); printf(" AIT1 AIT2 AIT3 AIT4\n"); printf(" DDS1 DDS2 DDS3 DDS4\n"); printf(" DLT3 DLT4\n"); printf(" SDLT1 SDLT220 SDLT320 SDLT600\n"); printf(" LTO1 LTO2 LTO3 LTO4\n"); printf(" LTO5 LTO6 LTO7 LTO8\n"); printf(" LTO9\n"); printf(" T10KA T10KB T10KC\n"); printf(" 9840A 9840B 9840C 9840D\n"); printf(" 9940A 9940B\n"); printf(" J1A E05 E06\n\n"); } int main(int argc, char *argv[]) { char device_conf[CONF_FILE_SZ]; unsigned char sam_stat; char *progname = argv[0]; char *pcl = NULL; char *mediaType = NULL; char *mediaCapacity = NULL; char *density = NULL; uint64_t size; struct MAM new_mam; char *lib = NULL; int libno = 0; int indx; int rc; FILE *conf; char *b; /* Read from file into this buffer */ char *s; /* Somewhere for sscanf to store results */ if (get_config(device_conf, DEVICE_CONF, my_id) < 0) exit(1); if (argc < 2) { usage(progname); exit(1); } while (argc > 0) { if (argv[0][0] == '-') { switch (argv[0][1]) { case 'd': if (argc > 1) density = argv[1]; break; case 'l': if (argc > 1) { lib = argv[1]; } else { puts(" More args needed for -l\n"); exit(1); } break; case 'm': if (argc > 1) { pcl = argv[1]; } else { puts(" More args needed for -m\n"); exit(1); } break; case 's': if (argc > 1) { mediaCapacity = argv[1]; } else { puts(" More args needed for -s\n"); exit(1); } break; case 't': if (argc > 1) { mediaType = argv[1]; } else { puts(" More args needed for -t\n"); exit(1); } break; case 'V': printf("%s: version %s\n%s\n\n", progname, MHVTL_VERSION, (char *)largefile_support); break; case 'v': verbose++; break; case 'w': if (argc > 1) { if (!strncasecmp("yes", argv[1], 3)) wp = WRITE_PROTECT_ON; else if (!strncasecmp("on", argv[1], 3)) wp = WRITE_PROTECT_ON; else wp = WRITE_PROTECT_OFF; } else { puts(" More args needed for -m\n"); exit(1); } break; } } argv++; argc--; } if (pcl == NULL) { printf("Please supply a barcode (-m barcode)\n\n"); usage(progname); exit(1); } conf = fopen(device_conf, "r"); if (!conf) { printf("Can not open config file %s : %s", device_conf, strerror(errno)); perror("Can not open config file"); exit(1); } s = zalloc(MALLOC_SZ); if (!s) { perror("Could not allocate memory"); exit(1); } b = zalloc(MALLOC_SZ); if (!b) { perror("Could not allocate memory"); exit(1); } rc = ENOENT; if (lib) { sscanf(lib, "%d", &libno); printf("Looking for PCL: %s in library %d\n", pcl, libno); find_media_home_directory(NULL, libno); rc = load_tape(pcl, &sam_stat); } else { /* Walk thru all defined libraries looking for media */ while (readline(b, MALLOC_SZ, conf) != NULL) { if (b[0] == '#') /* Ignore comments */ continue; /* If found a library: Attempt to load media * Break out of loop if found. Otherwise try next lib. */ if (sscanf(b, "Library: %d CHANNEL:", &indx)) { find_media_home_directory(NULL, indx); rc = load_tape(pcl, &sam_stat); if (!rc) break; } } } fclose(conf); free(s); free(b); if (rc) { fprintf(stderr, "PCL %s cannot be dumped, " "load_tape() returned %d\n", pcl, rc); exit(1); } /* Copy media MAM into temp location */ memcpy(&new_mam, &mam, sizeof(mam)); size = 0L; if (mediaCapacity) { sscanf(mediaCapacity, "%" PRId64, &size); printf("New capacity for %s: %ldMB\n", pcl, (unsigned long)size); } if (mediaType) { if (!strncasecmp("clean", mediaType, 5)) { MHVTL_DBG(1, "Setting media type to CLEAN"); new_mam.MediumType = MEDIA_TYPE_CLEAN; new_mam.MediumTypeInformation = 20; } else if (!strncasecmp("data", mediaType, 4)) { MHVTL_DBG(1, "Setting media type to DATA"); new_mam.MediumType = MEDIA_TYPE_DATA; } else if (!strncasecmp("null", mediaType, 4)) { MHVTL_DBG(1, "Setting media type to NULL"); new_mam.MediumType = MEDIA_TYPE_NULL; } else if (!strncasecmp("WORM", mediaType, 4)) { MHVTL_DBG(1, "Setting media type to WORM"); new_mam.MediumType = MEDIA_TYPE_WORM; } else { printf("Unknown media type: %s\n", mediaType); usage(progname); exit(1); } } if (density) { printf("Setting density to %s\n", density); if (set_media_params(&new_mam, density)) { printf("Could not determine media density: %s\n", density); unload_tape(&sam_stat); exit(1); } } if (size) { put_unaligned_be64(size * 1048576, &new_mam.max_capacity); /* This will set incorrect value to start with but next media * Usage, this will be recalculated correctly */ put_unaligned_be64(size * 1048576, &new_mam.remaining_capacity); } switch (wp) { case WRITE_PROTECT_ON: new_mam.Flags |= MAM_FLAGS_MEDIA_WRITE_PROTECT; printf("Setting write-protect for %s\n", pcl); break; case WRITE_PROTECT_OFF: new_mam.Flags &= ~MAM_FLAGS_MEDIA_WRITE_PROTECT; printf("Turning off write-protect for %s\n", pcl); break; } put_unaligned_be64(new_mam.max_capacity - sizeof(struct MAM), &new_mam.MAMSpaceRemaining); memcpy(&mam, &new_mam, sizeof(mam)); rewriteMAM(&sam_stat); unload_tape(&sam_stat); exit(0); } ================================================ FILE: usr/cmd/make_scsi_dev ================================================ #!/usr/bin/perl -w # # * perl replacement for the VERITAS /usr/openv/volmgr/bin/make_scsi_dev # * # * Re-creates the /dev/sg/ & /dev/st/ symlinks back to the native linux # * device nodes. # * # * # * $Id: make_scsi_dev,v 1.2.2.1 2006-08-06 07:58:44 markh Exp $ # * # * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com # * # * This program is free software; you can redistribute it and/or modify # * it under the terms of the GNU General Public License as published by # * the Free Software Foundation; either version 2 of the License, or # * (at your option) any later version. # * # * This program is distributed in the hope that it will be useful, # * but WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. # use strict; my $line; my $type; my $hba; my $bus; my $target; my $lun; my $path; my $st; my $sg; system("rm -f /dev/st/*"); system("rm -f /dev/sg/*"); open(D, "lsscsi -g|") || die "Could not execute \'lsscsi -g\'\n"; while($line = ) { chomp $line; next unless ($line =~ /tape|mediumx/); # print "$line\n"; ($hba,$bus,$target,$lun,$type) = ($line =~ /(\d+):(\d+):(\d+):(\d+)]\s+([a-z]*)/); # print "HBA: $hba, BUS: $bus, ID: $target, LUN: $lun\n"; $path = "h" . $hba . "c" . $bus . "t" . $target . "l" . $lun; ($st) = ($line =~ /\/dev\/(st\d+)/); ($sg) = ($line =~ /\/dev\/(sg\d+)/); if($type =~ /tape/) { system("ln -s /dev/$st /dev/st/$path"); system("ln -s /dev/n$st /dev/st/n$path"); } system("ln -s /dev/$sg /dev/sg/$path"); } close (D); ================================================ FILE: usr/cmd/make_vtl_media.in ================================================ #!/bin/bash # # Designed to be called from mhvtl rc script # # * Copyright (C) 2005 - 2025 Mark Harvey markh794@gmail.com # * # * This program is free software; you can redistribute it and/or modify # * it under the terms of the GNU General Public License as published by # * the Free Software Foundation; either version 2 of the License, or # * (at your option) any later version. # * # * This program is distributed in the hope that it will be useful, # * but WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # TODO: # - ensure we are root? # DEVICE_CONF="device.conf" PROG_NAME="$0" CONFIG_FILE='@CONF_PATH@/mhvtl.conf' FORCE='' CREATE_DIR='' MKTAPE_PATH='' # print usage info usage() { echo "Usage: $PROG_NAME: [OPTIONS] -- generate the tape device database" echo "where OPTIONS are from:" echo " [-h|--help] -- print this message and exit" echo " [-H|--home-dir MHVTL_HOME_PATH]" echo " -- home path [default $MHVTL_HOME_PATH]" echo " [-C|--config-dir DIR] -- configuration dir [default @CONF_PATH@]" echo " [-f|--force] -- overwrite files if present" echo " [-c|--create-dir] -- mkdir MHVTL_HOME_PATH if needed" echo " [-m|--mktape-path MKTAPE] -- pass in dir where mktape lives [default uses PATH]" } # # set_media_type -- set global variable MEDIA_TYPE # set_media_type() { if [[ $# -ne 1 ]] ; then echo "internal error: wrong number of arguments" 1>&1 exit 1 fi type=$1 e0_regex='[A-Z0-9]{6,8}([J])([WXY])$' lto_regex='[A-Z0-9]{6,8}([L])([TUVW])$' # default to data type MEDIA_TYPE="data" if [[ $type =~ ^"W" ]]; then MEDIA_TYPE="WORM" elif [[ $type =~ ^"CLN" ]]; then MEDIA_TYPE="clean" # Match JW / JX as 'worm' media elif [[ $type =~ $e0_regex ]]; then MEDIA_TYPE="WORM" elif [[ $type =~ $lto_regex ]]; then MEDIA_TYPE="WORM" fi } # # set_density -- set global variable DENSITY # set_density() { if [[ $# -ne 1 ]] ; then echo "internal error: wrong number of arguments" 1>&1 exit 1 fi density=$1 # I'm sure there is a better method then this... # There is.. Thanks to Gavin Barnard. regex='[A-Z0-9]{6,8}([SLXTJD])([12345678ABKUVWXYZ])$' #regex='[A-Z0-9]{4,6}([SLXTJ])([123456ABWX])[0-9]*$' # for bacula DENSITY=UNKNOWN if [[ $density =~ $regex ]]; then #matches=${#BASH_REMATCH[*]} if [ ${BASH_REMATCH[1]} = 'L' ]; then if [ ${BASH_REMATCH[2]} = 'T' ]; then DENSITY="LTO3" elif [ ${BASH_REMATCH[2]} = 'U' ]; then DENSITY="LTO4" elif [ ${BASH_REMATCH[2]} = 'V' ]; then DENSITY="LTO5" elif [ ${BASH_REMATCH[2]} = 'W' ]; then DENSITY="LTO6" else DENSITY="LTO${BASH_REMATCH[2]}" fi elif [ ${BASH_REMATCH[1]} = 'D' ]; then if [ ${BASH_REMATCH[2]} = '7' ]; then DENSITY="DLT4" fi elif [ ${BASH_REMATCH[1]} = 'S' ]; then if [ ${BASH_REMATCH[2]} = '3' ]; then DENSITY="SDLT600" elif [ ${BASH_REMATCH[2]} = '2' ]; then DENSITY="SDLT320" elif [ ${BASH_REMATCH[2]} = '1' ]; then DENSITY="SDLT220" else DENSITY="SDLT" fi elif [ ${BASH_REMATCH[1]} = 'J' ]; then if [ ${BASH_REMATCH[2]} = 'A' ]; then DENSITY="J1A" elif [ ${BASH_REMATCH[2]} = 'B' ]; then DENSITY="E05" elif [ ${BASH_REMATCH[2]} = 'W' ]; then DENSITY="E05" elif [ ${BASH_REMATCH[2]} = 'X' ]; then DENSITY="E05" elif [ ${BASH_REMATCH[2]} = 'Y' ]; then DENSITY="E07" elif [ ${BASH_REMATCH[2]} = 'K' ]; then DENSITY="E07" fi elif [ ${BASH_REMATCH[1]} = 'X' ]; then DENSITY="AIT${BASH_REMATCH[2]}" elif [ ${BASH_REMATCH[1]} = 'T' ]; then if [ ${BASH_REMATCH[2]} = 'Z' ]; then DENSITY="9840A" elif [ ${BASH_REMATCH[2]} = 'Y' ]; then DENSITY="9840B" elif [ ${BASH_REMATCH[2]} = 'X' ]; then DENSITY="9840C" elif [ ${BASH_REMATCH[2]} = 'W' ]; then DENSITY="9840D" elif [ ${BASH_REMATCH[2]} = 'V' ]; then DENSITY="9940A" elif [ ${BASH_REMATCH[2]} = 'U' ]; then DENSITY="9940B" else DENSITY="T10K${BASH_REMATCH[2]}" fi fi fi } # # find_mktape -- set MKTAPE_PATH to path to 'mktape' or exit if not found # find_mktape_path() { if [[ $# -gt 0 ]] ; then echo "internal error: $0: no argument expected" 1>&2 exit 1 fi if [[ -s "$MKTAPE_PATH" ]] ; then # make sure supplied path works if [[ ! -x $MKTAPE_PATH/mktape ]] ; then echo "error: no 'mktape' at specified path: $MKTAPE_PATh" 1>&2 exit 1 fi else # we must find mktape in our PATH OIFS="$IFS" IFS=':' for d in $PATH; do if [[ -x $d/mktape ]] ; then MKTAPE_PATH="$d" IFS="$OIFS" return fi done echo "error: no 'mktape' found in PATH" 1>&2 exit 1 fi } ################################################################## ## Main starts here... ################################################################## TEMP=$(getopt -o 'hH:C:fcm:' --long 'help,home-dir:,config-dir:,force,create-dir,mktape-path:' -n "$PROG_NAME" -- "$@") if [[ $? -ne 0 ]] ; then usage exit 1 fi eval set - "$TEMP" unset TEMP while true; do case "$1" in '-h'|'--help') usage exit 0 ;; '-H'|'--home-dir') PARAM_MHVTL_HOME_PATH="$2" shift 2 continue ;; '-C'|'--config-dir') MHVTL_CONFIG_PATH="$2" shift 2 continue ;; '-f'|'--force') FORCE='1' shift continue ;; '-c'|'--create-dir') CREATE_DIR='1' shift continue ;; '-m'|'--mktape-path') MKTAPE_PATH="$2" shift 2 continue ;; '--') shift break ;; *) echo "internal error: unknown arg: $1" 1>&2 exit 1 esac done # should be no more arguments if [[ $# -gt 0 ]] ; then echo "error: too many arguments" usage exit 1 fi if [[ ! -z "$MHVTL_CONFIG_PATH" ]] ; then CONFIG_FILE="$MHVTL_CONFIG_PATH/mhvtl.conf" fi if [[ ! -r $CONFIG_FILE ]] ; then echo "error: config file not found: $CONFIG_FILE" 1>&2 exit 1 fi # get default capacity . $CONFIG_FILE # Set default capacity to 500M if not defined. CAPACITY=${CAPACITY:=500} # override home dir if passed in by user if [[ ! -z "$PARAM_MHVTL_HOME_PATH" ]] ; then MHVTL_HOME_PATH=$PARAM_MHVTL_HOME_PATH fi if [[ ! -d $MHVTL_HOME_PATH ]] ; then if [[ -z "$CREATE_DIR" ]] ; then echo "error: no such directory: $MHVTL_HOME_PATH" 1>&2 usage exit 1 fi echo "warning: creating directory: $MHVTL_HOME_PATH" 1>&2 mkdir -p $MHVTL_HOME_PATH chmod 755 $MHVTL_HOME_PATH fi # Create any media specified in library config. umask 022 if [[ ! -r $MHVTL_CONFIG_PATH/$DEVICE_CONF ]] ; then echo "error: not found: $MHVTL_CONFIG_PATH/$DEVICE_CONF" 1>&2 exit 1 fi # make sure we can find mktape find_mktape_path # for LIBCONTENTS in $MHVTL_CONFIG_PATH/library_contents.* echo '===>' "Reading from $MHVTL_CONFIG_PATH/$DEVICE_CONF file to find Tape Libraries and Drives ..." for LIBID in $(awk '/Library:/ {print $2}' $MHVTL_CONFIG_PATH/$DEVICE_CONF) ; do LIBCONTENTS="$MHVTL_CONFIG_PATH/library_contents.$LIBID" if [[ ! -r $LIBCONTENTS ]] ; then echo "error: not found: $LIBCONTENTS" 1>&2 echo "have you ran 'generate_library_contents'?" 1>&2 exit 1 fi echo '===>' "Scanning for 'Slot' entries in $LIBCONTENTS ..." for a in $(awk '/^Slot/ {print $3}' <$LIBCONTENTS|sort -u) ; do if [[ -d $MHVTL_HOME_PATH/$a ]] ; then if [[ -z "$FORCE" ]] ; then echo "Directory already exists: $MHVTL_HOME_PATH/$a: skipping" continue fi echo "warning: removing entry: $MHVTL_HOME_PATH/$a" rm -rf $MHVTL_HOME_PATH/$a fi echo '===>' "Creating entry: $MHVTL_HOME_PATH/$a ..." set_density $a set_media_type $a # pass in "-D[0-9]" for debugging, "-v" for verbosity $MKTAPE_PATH/mktape -v -D5 -l $LIBID -s $CAPACITY -t $MEDIA_TYPE -m $a -d $DENSITY \ -C $MHVTL_CONFIG_PATH -H $MHVTL_HOME_PATH res=$? [[ $res -eq 0 ]] || exit 1 done done ================================================ FILE: usr/cmd/mhvtl-device-conf-generator.c ================================================ /* * mhvtl-device-conf-generator -- systemd generator for mhvtl * * This program read device.conf and creates the appropriate systemd * unit files and dependencies * * NOTE: This program assumes the existance of systemd template * files for vtllibrary and vtltape */ #include #include #include #include #include #include #include #include #include "vtllib.h" #define MAX_LINE_WIDTH 1024 #ifndef MHVTL_CONFIG_PATH #define MHVTL_CONFIG_PATH "/etc/mhvtl" #endif extern char *__progname; static int debug_mode = 0; char mhvtl_driver_name[] = ""; struct vtl_info { int num; struct vtl_info *next; }; static struct vtl_info our_tapes = {.num = -1, .next = NULL}; static struct vtl_info our_libraries = {.num = -1, .next = NULL}; static struct vtl_info *last_tape = &our_tapes; static struct vtl_info *last_library = &our_libraries; /* * A very thin wrapper to print to the kernel log. Since when the device * config generator is run, syslog is not up yet, generally, nothing is up yet, * the only place really to print information that needs to be preserved to * is the kernel log. Since it is the only place, it is done here on a * best-effort basis. * For the description of the levels, see * https://www.kernel.org/doc/html/latest/core-api/printk-basics.html * Broadly speaking, 3 (ERR) or 2 (CRIT) should cover this tool's needs */ __attribute__((__format__(__printf__, 2, 3))) static void pr_klog(int level, const char *fmt, ...) { int fd; char buf[1024]; va_list ap; int rc; int saved_errno = errno; /* Don't clobber errno */ if (level < 0 || level > 7) level = 3; snprintf(buf, sizeof(buf), "<%d>", level); va_start(ap, fmt); vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, ap); va_end(ap); fd = open("/dev/kmsg", O_WRONLY); if (fd == -1) { if (debug_mode) (void)fprintf(stderr, "Cannot open /dev/kmsg: %s\n", strerror(errno)); errno = saved_errno; return; } rc = write(fd, buf, strlen(buf)); if (rc) {}; /* Silence warn_unused_result - not much to do if it fails */ close(fd); errno = saved_errno; return; } /* * return 1 if path is a writable directory, else return 0 */ static int is_writable_dir(char *path) { struct stat sb; int res = 0; /* default to "no" */ if (lstat(path, &sb) < 0) goto dun; if (!S_ISDIR(sb.st_mode)) goto dun; res = (access(path, W_OK) == 0); dun: if (debug_mode) (void)printf("DEBUG: %s is%s writable\n", path, res ? "" : " not"); return res; } /* * scan arguments and return the "normal_dir" if all goes well, * else return NULL */ static char *get_working_dir(int argc, char **argv) { char *normal_dir; /* skip program name */ argc--; argc++; if ((argc > 1) && !strcmp(argv[1], "-d")) { debug_mode++; argc--; argv++; } if (argc != 4) { if (debug_mode) (void)fprintf(stderr, "DEBUG: error: expected 3 arguments, got %d\n", argc); return NULL; } if (debug_mode) (void)printf("DEBUG: normal=%s, early=%s, late=%s\n", argv[1], argv[2], argv[3]); normal_dir = argv[1]; /* we care about normal dir, so make sure it's real */ if (!is_writable_dir(normal_dir)) { if (debug_mode) (void)fprintf(stderr, "DEBUG: error: not a writable dir: %s\n", normal_dir); return NULL; } return normal_dir; } /* * add to the end of the linked list of libraries * * return bool success (0 -> fail, else success) */ static int add_to_libraries(int lib_num) { struct vtl_info *info; if (debug_mode) (void)printf("DEBUG: add_to_libraries(%d): entering\n", lib_num); if (lib_num <= 0) return 0; info = malloc(sizeof(struct vtl_info)); if (info == NULL) { if (debug_mode) (void)fprintf(stderr, "DEBUG: error: no memory\n"); return 0; } info->num = lib_num; info->next = NULL; last_library->next = info; last_library = info; return 1; } /* * add to the end of the linked list of drives for a library * * return bool success (0 -> fail, else success) */ static int add_to_tapes(int tape_num) { struct vtl_info *info; if (debug_mode) (void)printf("DEBUG: add_to_tapes(%d): entering\n", tape_num); if (tape_num <= 0) return 0; info = malloc(sizeof(struct vtl_info)); if (info == NULL) { if (debug_mode) (void)fprintf(stderr, "DEBUG: error: no memory\n"); return 0; } info->num = tape_num; info->next = NULL; last_tape->next = info; last_tape = info; return 1; } /* * parse the device.conf configuration file into our_tape_library * * This file has "Library" entries, and it has "Drive" entries. The * default configuration file has two library entries, and each * of those is followed by a number of drive entriess that go with * that library. But do NOT assume the file will be sorted in * this order. But DO assume that a library will not be referenced * before it is mentioned by a drive that uses it */ static int parse_config_file(char *path) { FILE *fp = NULL; char line_buf[MAX_LINE_WIDTH + 1]; char *cp = NULL; int tape_num = -1; int library_num = -1; if (debug_mode) (void)fprintf(stderr, "DEBUG: parsing config file: %s\n", path); if ((fp = fopen(path, "r")) == NULL) { pr_klog(3, "%s: Cannot open config file %s: %s\n", __progname, path, strerror(errno)); if (debug_mode) (void)fprintf(stderr, "DEBUG: error: cannot open config file %s: %s\n", path, strerror(errno)); return -1; } while ((cp = fgets(line_buf, MAX_LINE_WIDTH, fp)) != NULL) { if (*cp == '\n') continue; // blank line if (*cp == '#') continue; // comment line if (*cp == ' ') continue; // the middle of some record if (strncmp(cp, "Library: ", 9) == 0) { /* found a Library entry */ library_num = strtol(cp + 9, NULL, 0); if (!add_to_libraries(library_num)) goto dun; } else if (strncmp(cp, "Drive: ", 7) == 0) { /* found a Drive entry */ tape_num = strtol(cp + 7, NULL, 0); if (!add_to_tapes(tape_num)) goto dun; } } dun: (void)fclose(fp); return 0; } int main(int argc, char **argv) { char device_conf[CONF_FILE_SZ]; char *working_dir; struct vtl_info *ip; const char dirname[] = "mhvtl.target.wants"; char *path; int rc; if (get_config(device_conf, DEVICE_CONF, my_id) < 0) exit(1); working_dir = get_working_dir(argc, argv); if (!working_dir) exit(1); /* * parse the device.conf config file * * for each library found: * - set up vtllibrary unit * - for each tape that uses that library * - set up vtltape for that tape unit */ /* put config in our_tape_library */ rc = parse_config_file(device_conf); if (rc == -1) exit(1); if (debug_mode) { (void)printf("DEBUG: Libraries:\n"); for (ip = our_libraries.next; ip != NULL; ip = ip->next) (void)printf("DEBUG: %d\n", ip->num); (void)printf("DEBUG: Tapes:\n"); for (ip = our_tapes.next; ip != NULL; ip = ip->next) (void)printf("DEBUG: %d\n", ip->num); } if (asprintf(&path, "%s/%s", working_dir, dirname) < 0) { perror("Could not allocate memory (for directory path)"); exit(1); } if (debug_mode) printf("DEBUG: creating dir: %s\n", path); if (mkdir(path, 0755) < 0) { if (debug_mode) (void)fprintf(stderr, "DEBUG: error: can't mkdir %s: %s\n", path, strerror(errno)); // clean up? exit(1); } free(path); if (debug_mode) printf("DEBUG: scanning libraries ...\n"); for (ip = our_libraries.next; ip != NULL; ip = ip->next) { char *from_path, *to_path; if ((asprintf(&from_path, "%s/%s/vtllibrary@%d.service", working_dir, dirname, ip->num) < 0) || (asprintf(&to_path, "%s/vtllibrary@.service", SYSTEMD_SERVICE_DIR) < 0)) { perror("Could not allocate memory (for vtllibrary template symlink)"); exit(1); } if (debug_mode) (void)fprintf(stderr, "DEBUG: creating symlink: %s => %s\n", from_path, to_path); if (symlink(to_path, from_path) < 0) { if (debug_mode) (void)fprintf(stderr, "DEBUG: error: can't create symlink (%d): %s => %s\n", errno, from_path, to_path); // clean up? exit(1); } free(from_path); free(to_path); } if (debug_mode) printf("DEBUG: scanning tapes ...\n"); for (ip = our_tapes.next; ip != NULL; ip = ip->next) { char *from_path, *to_path; if ((asprintf(&from_path, "%s/%s/vtltape@%d.service", working_dir, dirname, ip->num) < 0) || (asprintf(&to_path, "%s/vtltape@.service", SYSTEMD_SERVICE_DIR) < 0)) { perror("Could not allocate memory (for vtltape template symlink)"); exit(1); } if (debug_mode) (void)fprintf(stderr, "DEBUG: creating symlink: %s => %s\n", from_path, to_path); if (symlink(to_path, from_path) < 0) { if (debug_mode) (void)fprintf(stderr, "DEBUG: error: can't create symlink (%d): %s => %s\n", errno, from_path, to_path); // clean up? exit(1); } free(from_path); free(to_path); } exit(0); } ================================================ FILE: usr/cmd/mhvtl_kernel_mod_build.in ================================================ #!/bin/bash # Used to automate the building of the mhvtl.ko module # on a running system - without requiring the whole source BUILD=$(mktemp -u -t tmp.XXXXXXXXXX) module_source=@FIRMWAREDIR@/mhvtl/mhvtl_kernel.tgz if [ ! -d ${BUILD} ]; then mkdir -p ${BUILD} fi cd ${BUILD} tar xfz ${module_source} make && sudo make install rm -r ${BUILD} ================================================ FILE: usr/cmd/mktape.c ================================================ /* * mktape * */ #include #include #include #include #include #include #include "be_byteshift.h" #include "vtlcart.h" #include "vtllib.h" #if defined _LARGEFILE64_SOURCE static void *largefile_support = "large file support"; #else static void *largefile_support = "No largefile support"; #endif /* The following variables are needed for the MHVTL_DBG() macro to work. */ char mhvtl_driver_name[] = "mktape"; static void usage(char *progname) { printf("Usage: %s [OPTIONS] [REQUIRED-PARAMS]", progname); printf("Where OPTIONS are from:\n"); printf(" -h -- print this message and exit\n"); printf(" -v -- verbose\n"); printf(" -D[N] -- set debug level to N [1]\n"); printf(" -V -- print Version and exit\n"); printf(" -C config-dir -- override default config dir [ %s ]\n", MHVTL_CONFIG_PATH); printf(" -H home-dir -- override default home dir [ %s ]\n", MHVTL_HOME_PATH); printf("And REQUIRED-PARAMS are:\n"); printf(" -l lib -- set Library number\n"); printf(" -m PCL -- set Physical Cartrige Label (barcode)\n"); printf(" -s size -- set size in Megabytes\n"); printf(" -t type -- set to data, clean, WORM, or NULL\n"); printf(" -d density -- set density to one of:\n"); printf(" AIT1 AIT2 AIT3 AIT4\n"); printf(" DDS1 DDS2 DDS3 DDS4\n"); printf(" DLT3 DLT4\n"); printf(" SDLT1 SDLT220 SDLT320 SDLT600\n"); printf(" LTO1 LTO2 LTO3 LTO4\n"); printf(" LTO5 LTO6 LTO7 LTO8\n"); printf(" T10KA T10KB T10KC\n"); printf(" 9840A 9840B 9840C 9840D\n"); printf(" 9940A 9940B\n"); printf(" J1A E05 E06 E07\n"); printf("\n"); } int gettime() { const char *source_date_epoch = getenv("SOURCE_DATE_EPOCH"); time_t t; if (source_date_epoch == NULL || (t = (time_t)strtoll(source_date_epoch, NULL, 10)) <= 0) t = time(NULL); return t; } int main(int argc, char *argv[]) { unsigned char sam_stat; char *progname = argv[0]; char *pcl = NULL; char *mediaType = NULL; char *mediaCapacity = NULL; char *density = NULL; char *lib = NULL; uint64_t size; int libno; int opt; int res; char *param_config_dir = NULL; char *param_home_dir = NULL; char ver[64]; int major, minor; int ret; if (argc < 2) { fprintf(stderr, "error: not enough arguments\n"); usage(progname); exit(1); } while ((opt = getopt(argc, argv, "d:l:m:s:t:VvD::hC:H:")) != -1) { switch (opt) { case 'h': usage(progname); exit(0); case 'd': density = strdup(optarg); break; case 'l': lib = strdup(optarg); break; case 'm': pcl = strdup(optarg); break; case 's': mediaCapacity = strdup(optarg); break; case 't': mediaType = strdup(optarg); break; case 'C': param_config_dir = strdup(optarg); break; case 'H': param_home_dir = strdup(optarg); break; case 'V': printf("%s: version %s\n", progname, MHVTL_VERSION); printf("%s\n", (char *)largefile_support); exit(0); case 'D': if (optarg) debug = strtol(optarg, NULL, 0); else debug++; break; case 'v': verbose++; break; default: fprintf(stderr, "error: unknown option: %c\n", opt); usage(progname); exit(1); } } if (pcl == NULL) { fprintf(stderr, "error: Please supply a barcode (-m barcode)\n\n"); usage(progname); exit(1); } if (mediaCapacity == NULL) { fprintf(stderr, "error: Please supply media capacity (-s xx)\n\n"); usage(progname); exit(1); } if (mediaType == NULL) { fprintf(stderr, "error: Please supply cart type (-t data|clean|WORM|NULL)\n\n"); usage(progname); exit(1); } if (lib == NULL) { fprintf(stderr, "error: Please supply Library number (-l xx)\n\n"); usage(progname); exit(1); } if (density == NULL) { fprintf(stderr, "error: Please supply media density (-d xx)\n\n"); usage(progname); exit(1); } sscanf(mediaCapacity, "%" PRId64, &size); if (size == 0) size = 8000; sscanf(lib, "%d", &libno); if (!libno) { fprintf(stderr, "error: Invalid library number\n"); exit(1); } ret = sscanf(MHVTL_VERSION, "%d.%d", &major, &minor); sprintf(ver, "vtl-%d.%d ", major, minor); if (ret != 2) { printf("Warning: Attempting to retrieve version string from %s failed\n", MHVTL_VERSION); } if (param_home_dir) strncpy(home_directory, param_home_dir, HOME_DIR_PATH_SZ); else find_media_home_directory(param_config_dir, libno); if (strlen(pcl) > MAX_BARCODE_LEN) { fprintf(stderr, "error: Max barcode length (%d) exceeded\n\n", MAX_BARCODE_LEN); usage(progname); exit(1); } /* Initialize the contents of the MAM to be used for the new PCL. */ init_mam(&mam); mam.tape_fmt_version = TAPE_FMT_VERSION; mam.mam_fmt_version = MAM_VERSION; put_unaligned_be64(size * 1048576, &mam.max_capacity); put_unaligned_be64(size * 1048576, &mam.remaining_capacity); put_unaligned_be64(mam.max_capacity - sizeof(struct MAM), &mam.MAMSpaceRemaining); memcpy(&mam.MediumManufacturer, "linuxVTL", 8); memcpy(&mam.ApplicationVendor, &ver, 8); sprintf((char *)mam.ApplicationVersion, "%d", TAPE_FMT_VERSION); if (!strncmp("clean", mediaType, 5)) { mam.MediumType = MEDIA_TYPE_CLEAN; /* Cleaning cart */ mam.MediumTypeInformation = 20; /* Max cleaning loads */ } else if (!strncmp("NULL", mediaType, 4)) { mam.MediumType = MEDIA_TYPE_NULL; /* save metadata only */ } else if (!strncmp("WORM", mediaType, 4)) { mam.MediumType = MEDIA_TYPE_WORM; /* WORM cart */ } else { mam.MediumType = MEDIA_TYPE_DATA; /* Normal data cart */ } set_media_params(&mam, density); sprintf((char *)mam.MediumSerialNumber, "%s_%d", pcl, (int)gettime()); sprintf((char *)mam.MediumManufactureDate, "%d", (int)gettime()); sprintf((char *)mam.Barcode, "%-31s", pcl); /* Create the PCL using the initialized MAM. */ if (verbose) printf("Creating tape data ...\n"); res = create_tape(pcl, &sam_stat); exit(res); } ================================================ FILE: usr/cmd/tape_util.c ================================================ /* * Dump headers of 'tape' datafile * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include "minilzo.h" #include "be_byteshift.h" #include "mhvtl_scsi.h" #include "mhvtl_list.h" #include "vtl_common.h" #include "vtllib.h" #include "vtlcart.h" #include "q.h" #include "ssc.h" #define MEDIA_WRITABLE 0 #define MEDIA_READONLY 1 char mhvtl_driver_name[] = "tape_util"; static int dump_tape = 0; /* dual personality - dump_tape & preload_tape */ static char *progname; static void print_mam_info(void) { uint64_t size; uint64_t remaining; char size_mul; /* K/M/G/T/P multiplier */ char remain_mul; /* K/M/G/T/P multiplier */ int a; static const char mul[] = " KMGT"; size = get_unaligned_be64(&mam.max_capacity); remaining = get_unaligned_be64(&mam.remaining_capacity); size_mul = remain_mul = ' '; for (a = 0; a < 4; a++) { if (size > 5121) { size >>= 10; /* divide by 1024 */ size_mul = mul[a + 1]; } } for (a = 0; a < 4; a++) { if (remaining > 5121) { remaining >>= 10; /* divide by 1024 */ remain_mul = mul[a + 1]; } } printf("Media density code: 0x%02x\n", mam.MediumDensityCode); printf("Media type code : 0x%02x\n", mam.MediaType); printf("Media description : %s\n", mam.media_info.description); printf("Tape Capacity : %" PRId64 " (%" PRId64 " %cBytes)\n", get_unaligned_be64(&mam.max_capacity), size, size_mul); switch (mam.MediumType) { case MEDIA_TYPE_CLEAN: printf("Media type : Cleaning media\n"); break; case MEDIA_TYPE_DATA: printf("Media type : Normal data\n"); break; case MEDIA_TYPE_NULL: printf("Media type : NULL - Use with caution !!\n"); break; case MEDIA_TYPE_WORM: printf("Media type : Write Once Read Many (WORM)\n"); break; default: printf("Media type : Unknown - please report !!\n"); break; } printf("Media : %s\n", (mam.Flags & MAM_FLAGS_MEDIA_WRITE_PROTECT) ? "Write-protected" : "read-write"); printf("Remaining Tape Capacity : %" PRId64 " (%" PRId64 " %cBytes)\n", get_unaligned_be64(&mam.remaining_capacity), remaining, remain_mul); } static void init_lunit(struct lu_phy_attr *lu, struct priv_lu_ssc *priv_lu) { if (debug) printf("Entering %s() +++\n", __func__); memset(lu, 0, sizeof(struct lu_phy_attr)); lu->lu_private = priv_lu; lu->sense_p = sense; strncpy(lu->lu_serial_no, "ABC123", 7); strncpy(lu->vendor_id, "TAPE_UTL", 9); strncpy(lu->product_id, "xyzz", 5); INIT_LIST_HEAD(&lu->den_list); INIT_LIST_HEAD(&lu->mode_pg); INIT_LIST_HEAD(&lu->log_pg); } static void init_lu_ssc(struct priv_lu_ssc *lu_priv) { if (debug) printf("Entering %s() +++\n", __func__); memset(lu_priv, 0, sizeof(struct priv_lu_ssc)); lu_priv->bufsize = 2 * 1024 * 1024; lu_priv->load_status = TAPE_UNLOADED; lu_priv->inLibrary = 0; lu_priv->sam_status = SAM_STAT_GOOD; lu_priv->MediaWriteProtect = MEDIA_WRITABLE; lu_priv->capacity_unit = 1; lu_priv->configCompressionFactor = Z_BEST_SPEED; lu_priv->bytesRead_I = 0; lu_priv->bytesRead_M = 0; lu_priv->bytesWritten_I = 0; lu_priv->bytesWritten_M = 0; lu_priv->c_pos = c_pos; lu_priv->KEY_INSTANCE_COUNTER = 0; lu_priv->DECRYPT_MODE = 0; lu_priv->ENCRYPT_MODE = 0; lu_priv->app_encr_info = &app_encryption_state; lu_priv->OK_2_write = &OK_to_write; lu_priv->mamp = &mam; INIT_LIST_HEAD(&lu_priv->supported_media_list); lu_priv->pm = NULL; lu_priv->state_msg = NULL; lu_priv->delay_load = 0; lu_priv->delay_unload = 0; lu_priv->delay_thread = 0; lu_priv->delay_position = 0; lu_priv->delay_rewind = 0; } /* Fix me - make sure it's not a WORM, cleaning cart etc */ uint8_t check_restrictions(struct scsi_cmd *cmd) { if (debug) printf("Entering %s() +++\n", __func__); *lu_ssc.OK_2_write = 1; return 1; } uint8_t valid_encryption_blk(struct scsi_cmd *cmd) { if (debug) printf("Entering %s() +++\n", __func__); return TRUE; } void register_ops(struct lu_phy_attr *lu, int op, void *f, void *g, void *h) { if (debug) printf("Entering %s() +++\n", __func__); } void ssc_personality_module_register(struct ssc_personality_template *pm) { if (debug) printf("Entering %s() +++\n", __func__); lu_ssc.pm = pm; if (debug) printf("Exiting %s() ++\n", __func__); } static struct media_details *check_media_can_load(struct list_head *mdl, int mt) { struct media_details *m_detail; if (debug) printf("Entering %s() +++\n", __func__); if (debug) printf("Looking for media_type: 0x%02x\n", mt); list_for_each_entry(m_detail, mdl, siblings) { if (debug) printf("testing against m_detail->media_type (0x%02x)\n", m_detail->media_type); if (m_detail->media_type == (unsigned int)mt) return m_detail; } return NULL; } static int lookup_media_int(struct name_to_media_info *media_info, char *s) { unsigned int i; if (debug) printf("Entering %s() +++\n", __func__); if (debug) printf("looking for media type %s\n", s); for (i = 0; media_info[i].media_density != 0; i++) if (!strcmp(media_info[i].name, s)) return media_info[i].media_type; return Media_undefined; } int add_drive_media_list(struct lu_phy_attr *lu, int status, char *s) { struct priv_lu_ssc *lu_tape; struct media_details *m_detail; struct list_head *den_list; int media_type; if (debug) printf("Entering %s() +++\n", __func__); lu_tape = (struct priv_lu_ssc *)lu->lu_private; den_list = &lu_tape->supported_media_list; if (debug) printf("Adding %s, status: 0x%02x\n", s, status); media_type = lookup_media_int(lu_tape->pm->media_handling, s); m_detail = check_media_can_load(den_list, media_type); if (m_detail) { if (debug) printf("Existing status for %s, Load Capability: 0x%02x\n", s, m_detail->load_capability); m_detail->load_capability |= status; if (debug) printf("Updating entry for %s, new Load Capability: 0x%02x\n", s, m_detail->load_capability); } else { if (debug) printf("Adding new entry for %s\n", s); m_detail = zalloc(sizeof(struct media_details)); if (!m_detail) { printf("Failed to allocate %d bytes\n", (int)sizeof(m_detail)); return -ENOMEM; } m_detail->media_type = media_type; m_detail->load_capability = status; list_add_tail(&m_detail->siblings, den_list); } return 0; } static void set_compression(struct priv_lu_ssc *lu_priv, char *compression) { if (!strcasecmp(compression, "LZO")) { if (verbose) printf("Setting compression to LZO\n"); lu_priv->compressionType = LZO; } else if (!strcasecmp(compression, "ZLIB")) { if (verbose) printf("Setting compression to ZLIB\n"); lu_priv->compressionType = ZLIB; } else { if (verbose) printf("Setting compression to NONE\n"); lu_priv->compressionType = 0; } } static int write_tape(char *source_file, uint32_t block_size, char *compression, uint8_t *sam_stat) { uint8_t *b; int fd = -1; int rc = 0; int retval; size_t count = 1; struct scsi_cmd cmd; struct mhvtl_ds ds; void (*drive_init)(struct lu_phy_attr *) = init_default_ssc; if (debug) printf("Entering %s() +++\n", __func__); /* fake scsi_cmd & lu_private structures for write operations */ memset(&cmd, 0, sizeof(cmd)); memset(&ds, 0, sizeof(ds)); cmd.lu = &lunit; cmd.dbuf_p = &ds; ds.sense_buf = sense; lu_ssc.max_capacity = get_unaligned_be64(&mam.max_capacity); drive_init(&lunit); init_default_ssc(&lunit); if (verbose) { printf("Opening %s for reading\n", source_file); printf("Block size: %d\n", block_size); printf("Compression : %s\n", compression); printf("Tape max capacity is %" PRIu64 "\n", lu_ssc.max_capacity); } b = malloc(block_size); if (!b) return -ENOMEM; if (debug) printf("malloc(%d) successfully\n", block_size); ds.data = b; ds.sz = block_size; set_compression(lunit.lu_private, compression); fd = open(source_file, O_LARGEFILE); if (fd < 0) { printf("Could not open file '%s', returned error: %s\n", source_file, strerror(errno)); rc = -EBADF; goto abort; } while (count > 0) { count = read(fd, b, block_size); if (count > 0) { if (count < block_size) { printf("zeroing out remaining block: %" PRIu32 "\n", (uint32_t)(block_size - count)); memset(b + count, 0, block_size - count); /* Zero out remaining block */ } retval = writeBlock(&cmd, count); if (retval < count) { if (sense[2] == (VOLUME_OVERFLOW | SD_EOM) && sense[13] == E_EOM) { printf("No space left on media, hit EOM while writing\n"); } else { printf("Tried to write %d, only succeeded in writing %d, SAM status: 0x%02x 0x%02x 0x%02x\n", block_size, retval, sense[2], sense[12], sense[13]); } break; } } } write_filemarks(1, sam_stat); abort: close(fd); free(b); return rc; } static int read_data(uint8_t *sam_stat) { uint8_t *p; uint32_t ret; uint32_t requested_blk_size = c_pos->blk_size; printf("c_pos->blk_size: %d\n", c_pos->blk_size); if (requested_blk_size <= 0) { printf("Data size: %d - skipping read\n", requested_blk_size); return 0; } p = malloc(requested_blk_size); if (!p) { fprintf(stderr, "Unable to allocate %d bytes\n", requested_blk_size); return -ENOMEM; } ret = readBlock(p, requested_blk_size, 1, 0, sam_stat); if (verbose) { /* put option to display all data */ printf("Data:\n%.*sEnd of data\n", requested_blk_size, p); } if (ret != requested_blk_size) { printf("Requested %d bytes, received %d\n", requested_blk_size, ret); } free(p); puts("\n"); return ret; } static void usage(char *errmsg) { if (errmsg) printf("%s\n", errmsg); printf("Usage: %s OPTIONS\n", progname); printf("Where OPTIONS are from:\n"); printf(" -h Print this message and exit\n"); printf(" -d Enable debugging\n"); printf(" -v Be verbose\n"); if (dump_tape == 1) { printf(" -D Dump data\n"); printf(" -l lib_no Look in specified library\n"); printf(" -m pcl Look for specified PCL\n"); } else if (dump_tape == 2) { printf(" -l lib_no Look in specified library\n"); printf(" -m pcl Look for specified PCL\n"); printf(" -b tape block size\n"); printf(" -c Compression type (NONE|LZO|ZLIB)\n"); printf(" -F Filename to read data from\n"); } else { printf("\n\nNot sure of my personality (dump_tape or preload_tape)\n"); } exit(errmsg ? 1 : 0); } static void dump_tape_metadata(int dump_data, uint8_t *sam_stat) { if (lzo_init() != LZO_E_OK) { fprintf(stderr, "internal error - lzo_init() failed !!!\n"); fprintf(stderr, "(this usually indicates a compiler bug - try recompiling\nwithout optimizations, and enable '-DLZO_DEBUG' for diagnostics)\n"); exit(3); } print_mam_info(); print_filemark_count(); if (verbose) { printf("Dumping filemark meta info\n"); print_metadata(); } while (c_pos->blk_type != B_EOD) { print_raw_header(); if (dump_data) { read_data(sam_stat); } while (c_pos->blk_type == B_FILEMARK) { printf("Filemark at block : %u , skipping\n", c_pos->blk_number); position_blocks_forw(1, sam_stat); } } print_raw_header(); } int main(int argc, char *argv[]) { char device_conf[CONF_FILE_SZ]; uint8_t sam_stat; char *pcl = NULL; int rc; int libno = 0; int indx; int block_size = 0; int dump_data = FALSE; char *source_file = NULL; char *compression = NULL; FILE *conf; char *b; /* Read from file into this buffer */ char *s; /* Somewhere for sscanf to store results */ if (get_config(device_conf, DEVICE_CONF, my_id) < 0) exit(1); progname = argv[0]; if (strcasestr(progname, "dump_tape")) { dump_tape = 1; } else if (strcasestr(progname, "preload_tape")) { dump_tape = 2; } else { usage("program name not correct"); } if (argc < 2) usage("Not enough arguments"); while (argc > 1) { if (argv[1][0] == '-') { switch (argv[1][1]) { case 'h': usage(NULL); break; case 'd': debug++; verbose = 9; /* If debug, make verbose... */ break; case 'm': if (argc > 1) pcl = argv[2]; else usage("More args needed for -m"); break; case 'l': if (argc > 1) libno = atoi(argv[1]); else usage("More args needed for -l"); break; case 'D': dump_data = TRUE; break; case 'v': verbose++; break; case 'b': /* block size */ if (dump_tape == 2) { if (argc > 1) { block_size = atoi(argv[2]); } else { usage("-b requires additional block size"); } } else { usage("-b is not a supported option"); } break; case 'c': /* compression type (NONE/LZO/ZLIB) */ if (dump_tape == 2) { if (argc > 1) { compression = argv[2]; } else { usage("-c requires additional compression type"); } } else { usage("-c is not a supported option"); } break; case 'F': /* File to read from */ if (dump_tape == 2) { if (argc > 1) { source_file = argv[2]; } else { usage("-F requires additional filename"); } } else { usage("-F is not a supported option"); } break; default: usage("Unknown option"); break; } } argv++; argc--; } if (pcl == NULL) usage("No PCL number supplied"); if (dump_tape == 2) { if (source_file == NULL) usage("Need to specify the filename to read data from"); if (compression == NULL) usage("Need to specify the compression type NONE|LZO|ZLIB"); if (block_size == 0) usage("Need to specify a block size"); } #ifdef __x86_64__ if (verbose) { if (__builtin_cpu_supports("sse4.2")) { printf("crc32c using Intel sse4.2 hardware optimization\n"); } else { printf("crc32c not using Intel sse4.2 optimization\n"); } } #endif init_lu_ssc(&lu_ssc); init_lunit(&lunit, &lu_ssc); conf = fopen(device_conf, "r"); if (!conf) { fprintf(stderr, "Cannot open config file %s: %s\n", device_conf, strerror(errno)); exit(1); } s = malloc(MALLOC_SZ); if (!s) { perror("Could not allocate memory"); exit(1); } b = malloc(MALLOC_SZ); if (!b) { perror("Could not allocate memory"); exit(1); } rc = ENOENT; if (libno) { printf("Looking for PCL: %s in library %d\n", pcl, libno); find_media_home_directory(NULL, libno); rc = load_tape(pcl, &sam_stat); } else { /* Walk thru all defined libraries looking for media */ while (readline(b, MALLOC_SZ, conf) != NULL) { if (b[0] == '#') /* Ignore comments */ continue; /* If found a library: Attempt to load media * Break out of loop if found. Otherwise try next lib. */ if (sscanf(b, "Library: %d CHANNEL:", &indx)) { find_media_home_directory(NULL, indx); rc = load_tape(pcl, &sam_stat); if (!rc) break; } } } fclose(conf); free(s); free(b); if (rc) { fprintf(stderr, "PCL %s cannot be opened, " "load_tape() returned %d\n", pcl, rc); exit(1); } if (dump_tape == 1) { for (int k = 0; k < mam.num_partitions; k++) { printf("Dumping partition %d\n", k); change_partition(k); dump_tape_metadata(dump_data, &sam_stat); } unload_tape(&sam_stat); } else if (dump_tape == 2) { write_tape(source_file, block_size, compression, &sam_stat); } return 0; } ================================================ FILE: usr/cmd/tapeexerciser.c ================================================ /* * Put a tape device thru a set of exercises */ #include #include #include #include #include #include #include static int space_forward_filemark(int fd, int count) { struct mtop mtop; int err; printf("%s(%d)", __func__, count); mtop.mt_op = MTFSF; /* Forward Space over count Filemarks */ mtop.mt_count = count; err = ioctl(fd, MTIOCTOP, &mtop); if (err) printf(" %s", strerror(errno)); printf("\n"); return err; } static int space_back_filemark(int fd, int count) { struct mtop mtop; int err; printf("%s(%d)", __func__, count); mtop.mt_op = MTBSF; /* Back Space over count Filemarks */ mtop.mt_count = count; err = ioctl(fd, MTIOCTOP, &mtop); if (err) printf(" %s", strerror(errno)); printf("\n"); return err; } static int space_forward_block(int fd, int count) { struct mtop mtop; int err; printf("%s(%d)", __func__, count); mtop.mt_op = MTFSR; /* Forward count block */ mtop.mt_count = count; err = ioctl(fd, MTIOCTOP, &mtop); if (err) printf(" %s", strerror(errno)); printf("\n"); return err; } static int write_filemarks(int fd, int count) { struct mtop mtop; int err; printf("%s(%d)", __func__, count); mtop.mt_op = MTWEOF; mtop.mt_count = count; err = ioctl(fd, MTIOCTOP, &mtop); if (err) printf(" %s", strerror(errno)); printf("\n"); return err; } static int space_back_block(int fd, int count) { struct mtop mtop; int err; printf("%s(%d)", __func__, count); mtop.mt_op = MTBSR; /* Back count block */ mtop.mt_count = 1; err = ioctl(fd, MTIOCTOP, &mtop); if (err) printf(" %s", strerror(errno)); printf("\n"); return err; } static int set_compression(int fd, int state) { struct mtop mtop; int err; printf("%s()", __func__); mtop.mt_op = MTCOMPRESSION; /* Set compression */ mtop.mt_count = state; err = ioctl(fd, MTIOCTOP, &mtop); if (err) printf(" %s", strerror(errno)); printf("\n"); return err; } static int rewind_tape(int fd) { struct mtop mtop; int err; printf("%s()", __func__); mtop.mt_op = MTREW; mtop.mt_count = 1; err = ioctl(fd, MTIOCTOP, &mtop); if (err) printf(" %s", strerror(errno)); printf("\n"); return err; } static int read_block_position(int fd) { struct mtpos mtpos; int err; err = ioctl(fd, MTIOCPOS, &mtpos); if (err) { printf("%s: %s\n", __func__, strerror(errno)); return -1; } return mtpos.mt_blkno; } static int read_block(int fd, int size) { char *buf; int err; buf = malloc(size); if (!buf) return -ENOMEM; printf("%s(%d)", __func__, size); err = read(fd, buf, size); if (err < 0) printf(" %s", strerror(errno)); printf("\n"); free(buf); return err; } static int write_block(int fd, int size) { char *buf; int err; buf = malloc(size); if (!buf) return -ENOMEM; memset(buf, 0x45, size); printf("%s(%d)", __func__, size); err = write(fd, buf, size); if (err < 0) printf(" %s", strerror(errno)); printf("\n"); free(buf); return err; } static void usage(char *arg) { printf("Usage: %s -f tape_path\n", arg); printf(" WARNING: %s will overwrite the tape\n\n", arg); } void write_tape_pattern_1(int fd) { rewind_tape(fd); write_block(fd, 8 * 1024); write_block(fd, 64 * 1024); write_filemarks(fd, 1); write_block(fd, 64 * 1024); write_block(fd, 64 * 1024); write_block(fd, 64 * 1024); write_block(fd, 64 * 1024); write_block(fd, 64 * 1024); write_filemarks(fd, 0); /* Flush data */ write_filemarks(fd, 1); } void write_tape_pattern_2(int fd) { rewind_tape(fd); space_forward_filemark(fd, 1); space_forward_block(fd, 5); /* should be at end filemark */ write_block(fd, 128 * 1024); write_block(fd, 128 * 1024); write_block(fd, 128 * 1024); write_filemarks(fd, 1); write_block(fd, 128 * 1024); write_filemarks(fd, 1); write_block(fd, 128 * 1024); write_filemarks(fd, 2); } int read_test_1(int fd) { rewind_tape(fd); read_block(fd, 8 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_forward_filemark(fd, 3); printf("%s: Block %d\n", __func__, read_block_position(fd)); read_block(fd, 64 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_back_block(fd, 1); printf("%s: Block %d\n", __func__, read_block_position(fd)); read_block(fd, 32 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); return 0; } int read_test_2(int fd) { rewind_tape(fd); space_forward_filemark(fd, 3); read_block(fd, 64 * 1024); space_forward_block(fd, 1); read_block(fd, 64 * 1024); printf("%s: Did we error here ??\n", __func__); rewind_tape(fd); printf("%s: Block %d\n", __func__, read_block_position(fd)); printf("%s: Note: following space_forward_filemark(100000)" " should error\n", __func__); space_forward_filemark(fd, 100000); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_back_filemark(fd, 2); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_back_block(fd, 1); printf("%s: Block %d\n", __func__, read_block_position(fd)); read_block(fd, 64 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); return 0; } int read_test_3(int fd) { rewind_tape(fd); printf("%s: Block %d\n", __func__, read_block_position(fd)); read_block(fd, 8 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_forward_filemark(fd, 3); printf("%s: Block %d\n", __func__, read_block_position(fd)); read_block(fd, 64 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_back_block(fd, 1); printf("%s: Block %d\n", __func__, read_block_position(fd)); read_block(fd, 64 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); printf("%s: Note: following space_forward_block() should error\n", __func__); space_forward_block(fd, 1); printf("%s: Block %d\n", __func__, read_block_position(fd)); return 0; } int read_test_4(int fd) { rewind_tape(fd); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_forward_filemark(fd, 1); printf("*** Should be at block 3 ==> \t"); printf("%s: Block %d\n", __func__, read_block_position(fd)); rewind_tape(fd); printf("%s: Block %d\n", __func__, read_block_position(fd)); read_block(fd, 8 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_forward_filemark(fd, 1); printf("*** Should be at block 3 ==> \t"); printf("%s: Block %d\n", __func__, read_block_position(fd)); read_block(fd, 64 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_back_block(fd, 1); printf("%s: Block %d\n", __func__, read_block_position(fd)); read_block(fd, 64 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_forward_block(fd, 1); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_forward_filemark(fd, 1); printf("%s: Block %d\n", __func__, read_block_position(fd)); return 0; } int read_test_5(int fd) { rewind_tape(fd); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_forward_filemark(fd, 1); printf("*** Should be at block 3 ==> \t"); printf("%s: Block %d\n", __func__, read_block_position(fd)); rewind_tape(fd); printf("%s: Block %d\n", __func__, read_block_position(fd)); read_block(fd, 8 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); space_forward_filemark(fd, 1); printf("*** Should be at block 3 ==> \t"); printf("%s: Block %d\n", __func__, read_block_position(fd)); printf("Only reading 32k of a 64k block\n"); read_block(fd, 32 * 1024); printf("%s: Block %d\n", __func__, read_block_position(fd)); return 0; } int main(int argc, char *argv[]) { struct mtget mtstat; struct mtpos mtpos; char *dev = NULL; int tape_fd; int err; int count; if (argc != 3) { usage(argv[0]); exit(1); } /* checking several positions of -h/-help */ for (count = 1; count < argc; count++) { if (!strcmp(argv[count], "-f")) { if (argc < count + 1) { usage(argv[0]); exit(1); } dev = argv[count + 1]; } } if (!dev) { usage(argv[0]); exit(1); } memset(&mtstat, 0, sizeof(struct mtget)); memset(&mtpos, 0, sizeof(struct mtpos)); tape_fd = open(dev, O_RDWR); if (tape_fd < 0) { printf("Failed to open %s: %s\n", dev, strerror(errno)); exit(-1); } /* Disable Compression */ set_compression(tape_fd, 0); write_tape_pattern_1(tape_fd); write_tape_pattern_2(tape_fd); read_test_1(tape_fd); read_test_2(tape_fd); read_test_3(tape_fd); read_test_4(tape_fd); read_test_5(tape_fd); err = ioctl(tape_fd, MTIOCGET, &mtstat); /* Query device status */ if (err) { printf("%s: %s\n", __func__, strerror(errno)); exit(-1); } printf(" =======================\n"); printf(" Querying type drive\n"); printf(" =======================\n"); printf("Status from tape device: type: %s\n", (mtstat.mt_type == MT_ISSCSI1) ? "SCSI-1" : "SCSI-2"); printf("Position: fileno: %d, blockno: %d\n", mtstat.mt_fileno, mtstat.mt_blkno); printf("Block size: %d, density: 0x%02x\n", (unsigned int)mtstat.mt_dsreg & MT_ST_BLKSIZE_MASK, (unsigned int)mtstat.mt_dsreg >> MT_ST_DENSITY_SHIFT); printf("Drive is %sline\n", GMT_ONLINE(mtstat.mt_gstat) ? "On" : "Off"); printf("Drive is EOF: %s\n", GMT_EOF(mtstat.mt_gstat) ? "Yes" : "No"); printf("Drive is BOT: %s\n", GMT_BOT(mtstat.mt_gstat) ? "Yes" : "No"); printf("Drive is EOT: %s\n", GMT_EOT(mtstat.mt_gstat) ? "Yes" : "No"); printf("Drive is EOD: %s\n", GMT_EOD(mtstat.mt_gstat) ? "Yes" : "No"); printf("Drive is SM: %s\n", GMT_SM(mtstat.mt_gstat) ? "Yes" : "No"); printf("Drive is WRITE PROTECT: %s\n", GMT_WR_PROT(mtstat.mt_gstat) ? "Yes" : "No"); printf("Tape is %sloaded in the drive\n", GMT_DR_OPEN(mtstat.mt_gstat) ? "not " : ""); printf(" =======================\n\n"); close(tape_fd); exit(0); } ================================================ FILE: usr/cmd/vtlcmd.c ================================================ /* * vtlcmd - A utility to send a message queue to the vtltape/vtllibrary * userspace daemons * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * * Modification History: * 2010-03-15 hstadler - source code revision, argument checking * * * FIXME: Server & Client are writing in the same queue, it would be better * the client opens a "private" queue and the server writes the * answers in this queue. In this case nobody can disturb the answer * from the server. I think this is called connectionless protocol. * But this means a redesign of some c-sources. * */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include "q.h" #include "vtllib.h" char mhvtl_driver_name[] = "vtlcmd"; #define TYPE_UNKNOWN 0 #define TYPE_LIBRARY 1 #define TYPE_DRIVE 2 static void usage(char *prog) { fprintf(stderr, "Usage : %s [-h|-help]\n", prog); fprintf(stderr, "Version: %s %s %s\n", MHVTL_VERSION, MHVTL_GITHASH, MHVTL_GITDATE); fprintf(stderr, " Where 'DeviceNo' is the number" " associated with tape/library daemon\n\n"); fprintf(stderr, "Global commands:\n"); fprintf(stderr, " verbose -> To enable verbose logging\n"); /* fprintf(stderr, " debug -> To enable debug logging\n"); */ fprintf(stderr, " TapeAlert # -> 64bit TapeAlert mask (hex)\n"); fprintf(stderr, " InquiryDataChange -> Set LU state to indicate Inquiry Data Has Changed\n"); fprintf(stderr, " exit -> To shutdown tape/library " "daemon/device\n"); fprintf(stderr, "\nTape specific commands:\n"); fprintf(stderr, " Append Only [Yes|No] -> To 'load' media ID\n"); fprintf(stderr, " compression [zlib|lzo] -> Use zlib or lzo " "compression\n"); fprintf(stderr, " load ID -> To 'load' media ID\n"); fprintf(stderr, " unload ID -> To 'unload' media ID\n"); fprintf(stderr, " delay load n -> Set load delay to n seconds\n"); fprintf(stderr, " delay unload n -> Set unload delay to n seconds\n"); fprintf(stderr, " delay rewind n -> Set rewind delay to n seconds\n"); fprintf(stderr, " delay position n -> Set position delay to n seconds\n"); fprintf(stderr, " delay thread n -> Set thread delay to n seconds\n"); fprintf(stderr, "\nLibrary specific commands:\n"); fprintf(stderr, " add slot -> Add a slot to library\n"); fprintf(stderr, " online -> To enable library\n"); fprintf(stderr, " offline -> To take library offline\n"); fprintf(stderr, " list map -> To list map contents\n"); fprintf(stderr, " empty map -> To remove media from map\n"); fprintf(stderr, " open map -> Open map to allow media export\n"); fprintf(stderr, " close map -> Close map to allow media import\n"); fprintf(stderr, " load map ID -> Load media ID into map\n"); } /* check if media (tape) exists in directory (/opt/mhvtl/..) */ int check_media(int libno, char *barcode) { char currentMedia[1024]; int datafile; int path_len; find_media_home_directory(NULL, libno); path_len = snprintf((char *)currentMedia, ARRAY_SIZE(currentMedia), "%s/%s/data", home_directory, barcode); if (path_len >= ARRAY_SIZE(currentMedia)) { fprintf(stderr, "Warning: path to %s/%s/data truncated to %" PRIu32 " bytes", home_directory, barcode, (uint32_t)ARRAY_SIZE(currentMedia)); } datafile = open(currentMedia, O_RDWR | O_LARGEFILE); if (datafile < 0) { fprintf(stderr, "Could not open %s: %s\n", currentMedia, strerror(errno)); return 1; } close(datafile); return 0; } /* Display the answer from daemon/service */ void DisplayResponse(int msqid, char *s) { struct q_entry r_entry; if (msgrcv(msqid, &r_entry, MAXOBN, VTLCMD_Q, 0) > 0) printf("%s%s\n", s, r_entry.msg.text); } int ishex(char *str) { while (*str) { if (!isxdigit(*str)) return 0; str++; } return 1; } int isnumeric(char *str) { while (*str) { if (!isdigit(*str)) return 0; str++; } return 1; } void PrintErrorExit(char *prog, char *s) { fprintf(stderr, "Please check command, parameter \'%s\' wrong.\n\n", s); usage(prog); exit(1); } void Check_TapeAlert(int argc, char **argv) { if (argc > 3) { if (!ishex(argv[3])) { fprintf(stderr, "Value not hexadecimal: %s\n", argv[3]); exit(1); } if (argc == 4) return; PrintErrorExit(argv[0], "TapeAlert"); } PrintErrorExit(argv[0], "TapeAlert"); } void Check_Load(int argc, char **argv) { if (argc > 3) { if (!strcmp(argv[3], "map")) { if (argc == 5) return; PrintErrorExit(argv[0], "load map"); } if (argc == 4) return; PrintErrorExit(argv[0], "load"); } PrintErrorExit(argv[0], "load"); } void Check_delay(int argc, char **argv) { if (argc > 4) { if (argc == 5) { if (atoi(argv[4]) >= 0) return; PrintErrorExit(argv[0], "delay: Negative value"); } PrintErrorExit(argv[0], "delay"); } PrintErrorExit(argv[0], "delay"); } void Check_Unload(int argc, char **argv) { if (argc > 3) { if (argc == 4) return; PrintErrorExit(argv[0], "unload"); } PrintErrorExit(argv[0], "unload"); } void Check_Compression(int argc, char **argv) { if (argc > 3) { if (argc == 4) return; PrintErrorExit(argv[0], "compression"); } PrintErrorExit(argv[0], "compression : missing lzo or zlib"); } void Check_append_only(int argc, char **argv) { if (argc > 4) { if (argc == 5) return; PrintErrorExit(argv[0], "Append Only"); } PrintErrorExit(argv[0], "Append Only : missing Yes / No"); } void Check_List(int argc, char **argv) { if (argc != 4) PrintErrorExit(argv[0], "list map : too many args"); else if (strcmp(argv[3], "map")) PrintErrorExit(argv[0], "list map : Can only list map"); } void Check_Empty(int argc, char **argv) { if (argc > 3) { if (!strcmp(argv[3], "map")) { if (argc == 4) return; } PrintErrorExit(argv[0], "empty map"); } PrintErrorExit(argv[0], "empty map"); } void Check_Open(int argc, char **argv) { if (argc > 3) { if (!strcmp(argv[3], "map")) { if (argc == 4) return; } PrintErrorExit(argv[0], "open map"); } PrintErrorExit(argv[0], "open map"); } void Check_Close(int argc, char **argv) { if (argc > 3) { if (!strcmp(argv[3], "map")) { if (argc == 4) return; } PrintErrorExit(argv[0], "close map"); } PrintErrorExit(argv[0], "close map"); } void Check_Params(int argc, char **argv) { if (argc > 1) { if (!isnumeric(argv[1])) { fprintf(stderr, "DeviceNo not numeric: %s\n", argv[1]); exit(1); } if (argc > 2) { /* global commands */ if (!strcmp(argv[2], "verbose")) { if (argc == 3) return; PrintErrorExit(argv[0], "verbose"); } if (!strcmp(argv[2], "dump")) { if (argc == 3) return; PrintErrorExit(argv[0], "dump"); } if (!strcmp(argv[2], "debug")) { if (argc == 3) return; PrintErrorExit(argv[0], "debug"); } if (!strcmp(argv[2], "exit")) { if (argc == 3) return; PrintErrorExit(argv[0], "exit"); } if (!strncasecmp(argv[2], "InquiryDataChange", 17)) { return; } if (!strncasecmp(argv[2], "TapeAlert", 9)) { Check_TapeAlert(argc, argv); return; } /* Tape commands */ if (!strncasecmp(argv[2], "load", 4)) { Check_Load(argc, argv); return; } if (!strncasecmp(argv[2], "unload", 6)) { Check_Unload(argc, argv); return; } if (!strncasecmp(argv[2], "delay", 5)) { Check_delay(argc, argv); return; } if (!strncasecmp(argv[2], "compression", 11)) { Check_Compression(argc, argv); return; } if (!strncasecmp(argv[2], "Append", 6)) { Check_append_only(argc, argv); return; } /* Library commands */ if (!strcmp(argv[2], "add")) { if (argc == 4) return; PrintErrorExit(argv[0], "add slot"); } if (!strcmp(argv[2], "online")) { if (argc == 3) return; PrintErrorExit(argv[0], "online"); } if (!strcmp(argv[2], "offline")) { if (argc == 3) return; PrintErrorExit(argv[0], "offline"); } if (!strcmp(argv[2], "list")) { Check_List(argc, argv); return; } if (!strcmp(argv[2], "empty")) { Check_Empty(argc, argv); return; } if (!strcmp(argv[2], "open")) { Check_Open(argc, argv); return; } if (!strcmp(argv[2], "close")) { Check_Close(argc, argv); return; } PrintErrorExit(argv[0], "check param"); } PrintErrorExit(argv[0], ""); } PrintErrorExit(argv[0], ""); } /* Open a new queue (for answers from server) */ int CreateNewQueue(void) { long queue_id; /* Attempt to create a message queue */ queue_id = msgget(IPC_PRIVATE, IPC_CREAT | S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH); if (queue_id == -1) fprintf(stderr, "%s: %s\n", __func__, strerror(errno)); return queue_id; } /* Open an alreay opened queue (opened by server) */ int OpenExistingQueue(key_t key) { long queue_id; /* Attempt to open an existing message queue */ queue_id = msgget(key, 0); if (queue_id == -1) fprintf(stderr, "%s: %s\n", __func__, strerror(errno)); return queue_id; } /* Send command to queue */ int SendMsg(long ReceiverQid, long ReceiverMtyp, char *sndbuf) { struct q_entry s_entry; s_entry.rcv_id = ReceiverMtyp; s_entry.msg.snd_id = VTLCMD_Q; s_entry.msg.text[0] = '\0'; strncat(s_entry.msg.text, sndbuf, MAXTEXTLEN); int len = strlen(s_entry.msg.text) + 1 + sizeof(s_entry.msg.snd_id); if (msgsnd(ReceiverQid, &s_entry, len, 0) == -1) return -1; return 0; } int main(int argc, char **argv) { char device_conf[CONF_FILE_SZ]; FILE *conf; char b[1024]; int device_type = TYPE_UNKNOWN; long deviceNo, indx; int count; char buf[1024]; char *p; my_id = VTLCMD_Q; if (get_config(device_conf, DEVICE_CONF, my_id) < 0) exit(1); if ((argc < 2) || (argc > 6)) { usage(argv[0]); exit(1); } /* checking several positions of -h/-help */ for (count = 1; count < argc; count++) { if (!strcmp(argv[count], "-h")) { usage(argv[0]); exit(1); } if (!strcmp(argv[count], "/h")) { usage(argv[0]); exit(1); } if (!strcmp(argv[count], "-help")) { usage(argv[0]); exit(1); } if (!strcasecmp(argv[count], "-v")) { fprintf(stderr, "Version: %s %s %s\n", MHVTL_VERSION, MHVTL_GITHASH, MHVTL_GITDATE); exit(1); } } Check_Params(argc, argv); deviceNo = atol(argv[1]); if ((deviceNo < 0) || (deviceNo >= VTLCMD_Q)) { fprintf(stderr, "Invalid device number for " "tape/library: %s\n", argv[1]); exit(1); } conf = fopen(device_conf, "r"); if (!conf) { fprintf(stderr, "Can not open config file %s : %s\n", device_conf, strerror(errno)); exit(1); } p = buf; buf[0] = '\0'; /* While read in a line */ while (fgets(b, sizeof(b), conf) != NULL) { if (sscanf(b, "Drive: %ld ", &indx) == 1 && indx == deviceNo) { device_type = TYPE_DRIVE; break; } if (sscanf(b, "Library: %ld ", &indx) == 1 && indx == deviceNo) { device_type = TYPE_LIBRARY; break; } } fclose(conf); if (device_type == TYPE_UNKNOWN) { fprintf(stderr, "No tape/library (%s) configured with " "device number: %ld\n", device_conf, deviceNo); exit(1); } /* Concat all args into one string. * Bound each write by remaining buffer space so an oversized argv * cannot overflow buf[] (the outgoing message queue slot is * sizeof(buf) bytes). */ { size_t remaining = sizeof(buf); p = buf; buf[0] = '\0'; for (count = 2; count < argc; count++) { int n = snprintf(p, remaining, "%s ", argv[count]); if (n < 0 || (size_t)n >= remaining) { fprintf(stderr, "Command line too long " "(max %zu bytes)\n", sizeof(buf) - 1); exit(1); } p += n; remaining -= n; } } /* check if command to the specific device is allowed */ if (device_type == TYPE_LIBRARY) { if (!strncmp(buf, "online", 6)) { } else if (!strncmp(buf, "add slot", 8)) { } else if (!strncmp(buf, "offline", 7)) { } else if (!strncmp(buf, "open map", 8)) { } else if (!strncmp(buf, "close map", 9)) { } else if (!strncmp(buf, "empty map", 9)) { } else if (!strncmp(buf, "list map", 8)) { } else if (!strncmp(buf, "load map", 8)) { } else if (!strncmp(buf, "verbose", 7)) { } else if (!strncmp(buf, "debug", 5)) { } else if (!strncmp(buf, "exit", 4)) { } else if (!strncmp(buf, "TapeAlert", 9)) { } else if (!strncmp(buf, "InquiryDataChange", 17)) { } else { fprintf(stderr, "Command for library not allowed\n"); exit(1); } } if (device_type == TYPE_DRIVE) { if (!strncmp(buf, "load", 4)) { } else if (!strncmp(buf, "unload", 6)) { } else if (!strncmp(buf, "verbose", 7)) { } else if (!strncmp(buf, "debug", 5)) { } else if (!strncmp(buf, "dump", 4)) { } else if (!strncmp(buf, "exit", 4)) { } else if (!strncmp(buf, "compression", 11)) { } else if (!strncmp(buf, "TapeAlert", 9)) { } else if (!strncmp(buf, "InquiryDataChange", 17)) { } else if (!strncasecmp(buf, "append", 6)) { } else if (!strncasecmp(buf, "delay load", 10)) { } else if (!strncasecmp(buf, "delay unload", 12)) { } else if (!strncasecmp(buf, "delay rewind", 12)) { } else if (!strncasecmp(buf, "delay position", 14)) { } else if (!strncasecmp(buf, "delay thread", 12)) { } else { fprintf(stderr, "Command for tape not allowed\n"); exit(1); } } /* Check for the existance of a datafile first - abort if not there */ if (device_type == TYPE_LIBRARY) { if (!strcmp(argv[2], "load") && !strcmp(argv[3], "map")) { if (check_media(deviceNo, argv[4])) { fprintf(stderr, "Hint: Use command 'mktape' to " "create media first\n"); exit(1); } } } long ReceiverQid; ReceiverQid = OpenExistingQueue(QKEY); if (ReceiverQid == -1) { fprintf(stderr, "MessageQueue not available\n"); exit(1); } if (SendMsg(ReceiverQid, deviceNo, buf) < 0) { fprintf(stderr, "Message Queue Error: send message\n"); exit(1); } if (device_type == TYPE_LIBRARY) { if (!strcmp(argv[2], "add") && !strcmp(argv[3], "slot")) DisplayResponse(ReceiverQid, ""); if (!strcmp(argv[2], "open") && !strcmp(argv[3], "map")) DisplayResponse(ReceiverQid, ""); if (!strcmp(argv[2], "close") && !strcmp(argv[3], "map")) DisplayResponse(ReceiverQid, ""); if (!strcmp(argv[2], "empty") && !strcmp(argv[3], "map")) DisplayResponse(ReceiverQid, ""); if (!strcmp(argv[2], "list") && !strcmp(argv[3], "map")) DisplayResponse(ReceiverQid, "Contents: "); if (!strcmp(argv[2], "load") && !strcmp(argv[3], "map")) DisplayResponse(ReceiverQid, ""); } exit(0); } ================================================ FILE: usr/cmd/vtllibrary.c ================================================ /* * This daemon is the SCSI SMC target (Medium Changer) portion of the * vtl package. * * The vtl package consists of: * a kernel module (vlt.ko) - Currently on 2.6.x Linux kernel support. * SCSI target daemons for both SMC and SSC devices. * * Copyright (C) 2005 - 2025 Mark Harvey markh794@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * * 0.14 13 Feb 2008 * Since ability to define device serial number, increased ver from * 0.12 to 0.14 * * v0.12 -> Forked into 'stable' (0.12) and 'devel' (0.13). * My current thinking : This is a dead end anyway. * An iSCSI target done in user-space is now my perferred solution. * This means I don't have to do any kernel level drivers * and leaverage the hosts native iSCSI initiator. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vtl_common.h" #include "mhvtl_scsi.h" #include "mhvtl_list.h" #include "q.h" #include "logging.h" #include "vtllib.h" #include "spc.h" #include "smc.h" #include "mode.h" #include "be_byteshift.h" #include "mhvtl_log.h" char mhvtl_driver_name[] = "vtllibrary"; #define CAP_CLOSED 1 #define CAP_OPEN 0 #define OPERATOR 1 #define ROBOT_ARM 0 #define SMC_BUF_SIZE 1024 * 1024 /* Default size of buffer */ static uint8_t sam_status = 0; /* Non-zero if Sense-data is valid */ static long backoff; /* Backoff value for polling char device */ static struct smc_priv smc_slots; struct s_info *add_new_slot(struct lu_phy_attr *lu); static void usage(char *progname) { printf("Usage: %s [OPTIONS] -q \n", progname); printf("Where:\n"); printf(" '-q ' is the queue priority number\n"); printf("and where OPTIONS are from:\n"); printf(" '-d' enable debug mode -> Don't run as daemon\n"); printf(" 'v[N]' enable verbose syslog messages level N [1]\n"); printf(" '-f FIFO' use FIFO to report real-time data\n"); printf(" '-F' run in the foreground\n"); } #ifndef Solaris int ioctl(int, int, void *); #endif struct device_type_template smc_template = { .ops = { SCSI_OP_RANGE(0x00, 0xff, spc_illegal_op), /* 0x00 -> 0x0f */ SCSI_OP(0x00, spc_tur), SCSI_OP(0x01, smc_rezero), SCSI_OP(0x03, spc_request_sense), SCSI_OP(0x07, smc_initialize_element_status), /* 0x10 -> 0x1f */ SCSI_OP(0x12, spc_inquiry), SCSI_OP(0x15, spc_mode_select), SCSI_OP(0x16, spc_reserve), SCSI_OP(0x17, spc_release), SCSI_OP(0x1a, spc_mode_sense), SCSI_OP(0x1b, smc_open_close_import_export_element), SCSI_OP(0x1c, spc_recv_diagnostics), SCSI_OP(0x1d, spc_send_diagnostics), SCSI_OP(0x1e, smc_allow_removal), /* 0x20 -> 0x2f */ /* 0x30 -> 0x3f */ /* 0x40 -> 0x4f */ SCSI_OP(0x4c, spc_log_select), SCSI_OP(0x4d, smc_log_sense), /* 0x50 -> 0x5f */ SCSI_OP(0x55, spc_mode_select), SCSI_OP(0x56, spc_reserve), SCSI_OP(0x57, spc_release), SCSI_OP(0x5a, spc_mode_sense), /* 0x60 -> 0x6f */ /* 0x70 -> 0x7f */ /* 0x80 -> 0x8f */ /* 0x90 -> 0x9f */ /* 0xa0 -> 0xaf */ SCSI_OP(0xa0, spc_illegal_op), /* processed in the kernel module */ SCSI_OP(0xa5, smc_move_medium), /* 0xb0 -> 0xbf */ SCSI_OP(0xb8, smc_read_element_status), /* 0xc0 -> 0xcf */ /* 0xd0 -> 0xdf */ /* 0xe0 -> 0xef */ SCSI_OP(0xe7, smc_initialize_element_status_with_range), /* 0xf0 -> 0xff */ }}; __attribute__((constructor)) static void smc_init(void) { device_type_register(&lunit, &smc_template); } /* * Update ops[xx] with new/updated/custom function 'f' */ void register_ops(struct lu_phy_attr *lu, int op, void *f, void *g, void *h) { lu->scsi_ops->ops[op].cmd_perform = f; lu->scsi_ops->ops[op].pre_cmd_perform = g; lu->scsi_ops->ops[op].post_cmd_perform = h; } /* * * Process the SCSI command * * Called with: * cdev -> Char dev file handle, * cdb -> SCSI Command buffer pointer, * struct mhvtl_ds -> general purpose data structure... Need better name * * Return: * SAM status returned in struct mhvtl_ds.sam_stat */ static void processCommand(int cdev, uint8_t *cdb, struct mhvtl_ds *dbuf_p, useconds_t pollInterval) { int err = 0; struct scsi_cmd _cmd; struct scsi_cmd *cmd; cmd = &_cmd; cmd->scb = cdb; cmd->scb_len = 16; /* fixme */ cmd->dbuf_p = dbuf_p; cmd->lu = &lunit; cmd->pollInterval = pollInterval; cmd->cdev = cdev; MHVTL_DBG_PRT_CDB(1, cmd); switch (cdb[0]) { case INQUIRY: /* INQUIRY does not need to check PowerOn/reset, however the Inquiry Data may have changed */ if (check_inquiry_data_has_changed(&dbuf_p->sam_stat)) return; case REPORT_LUNS: case REQUEST_SENSE: case MODE_SELECT: dbuf_p->sam_stat = SAM_STAT_GOOD; break; default: if (cmd->lu->online == 0) { sam_not_ready(E_OFFLINE, &dbuf_p->sam_stat); return; } if (check_reset(&dbuf_p->sam_stat)) return; } /* Skip main op code processing if pre-cmd returns non-zero */ if (cmd->lu->scsi_ops->ops[cdb[0]].pre_cmd_perform) err = cmd->lu->scsi_ops->ops[cdb[0]].pre_cmd_perform(cmd, NULL); if (!err) dbuf_p->sam_stat = cmd->lu->scsi_ops->ops[cdb[0]].cmd_perform(cmd); /* Post op code processing regardless */ if (cmd->lu->scsi_ops->ops[cdb[0]].post_cmd_perform) cmd->lu->scsi_ops->ops[cdb[0]].post_cmd_perform(cmd, NULL); return; } /* * Respond to messageQ 'list map' by sending a list of PCLs to messageQ */ static void list_map(struct q_msg *msg) { struct list_head *slot_head = &smc_slots.slot_list; struct s_info *sp; char buf[MAXOBN]; char *c = buf; *c = '\0'; list_for_each_entry(sp, slot_head, siblings) { if (slotOccupied(sp) && sp->element_type == MAP_ELEMENT) { strncat(c, (char *)sp->media->barcode, MAX_BARCODE_LEN + 1); MHVTL_DBG(2, "MAP slot %d full", sp->slot_location); } else { MHVTL_DBG(2, "MAP slot %d empty", sp->slot_location); } } MHVTL_DBG(2, "map contents: %s", buf); send_msg(buf, msg->snd_id); } /* Check existing MAP & Storage slots for existing barcode */ int already_in_slot(char *barcode) { struct list_head *slot_head = &smc_slots.slot_list; struct s_info *sp = NULL; int len; len = strlen(barcode); list_for_each_entry(sp, slot_head, siblings) { if (slotOccupied(sp)) { if (!strncmp((char *)sp->media->barcode, barcode, len)) { MHVTL_DBG(3, "Match: %s %s", sp->media->barcode, barcode); return 1; } else MHVTL_DBG(3, "No match: %s %s", sp->media->barcode, barcode); } } return 0; } static struct s_info *locate_empty_map(void) { struct s_info *sp = NULL; struct list_head *slot_head = &smc_slots.slot_list; list_for_each_entry(sp, slot_head, siblings) { if (!slotOccupied(sp) && sp->element_type == MAP_ELEMENT) return sp; } return NULL; } static struct m_info *lookup_barcode(struct lu_phy_attr *lu, char *barcode) { struct list_head *media_list_head; struct m_info *m; int match; media_list_head = &((struct smc_priv *)lu->lu_private)->media_list; list_for_each_entry(m, media_list_head, siblings) { match = strncmp(m->barcode, barcode, MAX_BARCODE_LEN + 1); if (!match) { MHVTL_DBG(3, "Match barcodes: %s %s: %d", barcode, m->barcode, match); return m; } } return NULL; } static struct m_info *add_barcode(struct lu_phy_attr *lu, char *barcode) { struct list_head *media_list_head; struct m_info *m; if (strlen(barcode) > MAX_BARCODE_LEN) { MHVTL_ERR("Barcode \'%s\' exceeds max barcode lenght: %d", barcode, MAX_BARCODE_LEN); exit(1); } if (lookup_barcode(lu, barcode)) { MHVTL_ERR("Duplicate barcode %s.. Exiting", barcode); exit(1); } m = zalloc(sizeof(struct m_info)); if (!m) { MHVTL_ERR("Out of memory allocating memory for barcode %s", barcode); exit(-ENOMEM); } media_list_head = &((struct smc_priv *)lu->lu_private)->media_list; memset(m, 0, sizeof(struct m_info)); snprintf((char *)m->barcode, MAX_BARCODE_LEN + 1, LEFT_JUST_16_STR, barcode); m->barcode[MAX_BARCODE_LEN] = '\0'; m->cart_type = get_cart_type(barcode); if (!strncmp((char *)m->barcode, "NOBAR", 5)) m->internal_status = INSTATUS_NO_BARCODE; else m->internal_status = 0; list_add_tail(&m->siblings, media_list_head); return m; } /* Return zero - failed, non-zero - success */ static int load_map(struct q_msg *msg) { struct s_info *sp = NULL; struct m_info *mp = NULL; char *barcode; int i; int str_len; char *text = &msg->text[9]; /* skip past "load map " */ MHVTL_DBG(2, "Loading %s into MAP", text); if (smc_slots.cap_closed) { send_msg("MAP not opened", msg->snd_id); return 0; } str_len = strlen(text); barcode = NULL; for (i = 0; i < str_len; i++) if (isalnum(text[i])) { barcode = &text[i]; break; } /* No barcode - reject load */ if (!barcode) { send_msg("Bad barcode", msg->snd_id); return 0; } if (already_in_slot(barcode)) { send_msg("barcode already in library", msg->snd_id); return 0; } if (strlen(barcode) > MAX_BARCODE_LEN) { send_msg("barcode length too long", msg->snd_id); return 0; } sp = locate_empty_map(); if (sp) { mp = lookup_barcode(&lunit, barcode); if (!mp) mp = add_barcode(&lunit, barcode); snprintf((char *)mp->barcode, MAX_BARCODE_LEN + 1, LEFT_JUST_16_STR, barcode); mp->barcode[MAX_BARCODE_LEN] = '\0'; /* 1 = data, 2 = Clean */ mp->cart_type = get_cart_type(barcode); sp->status = STATUS_InEnab | STATUS_ExEnab | STATUS_Access | STATUS_ImpExp | STATUS_Full; /* Media placed by operator */ setImpExpStatus(sp, OPERATOR); sp->media = mp; sp->media->internal_status = 0; send_msg("OK", msg->snd_id); return 1; } send_msg("MAP Full", msg->snd_id); return 0; } static void open_map(struct q_msg *msg) { MHVTL_DBG(1, "Called"); current_state = MHVTL_STATE_OPENING_MAP; smc_slots.cap_closed = CAP_OPEN; send_msg("OK", msg->snd_id); } static void close_map(struct q_msg *msg) { MHVTL_DBG(1, "Called"); current_state = MHVTL_STATE_CLOSING_MAP; smc_slots.cap_closed = CAP_CLOSED; send_msg("OK", msg->snd_id); } /* add new slot && assignment && initialization memory */ static void add_storage_slot(struct q_msg *msg) { int buffer_size; int slt_no; char message[20]; struct s_info *sp1 = NULL; struct smc_priv *smc_p = lunit.lu_private; sp1 = add_new_slot(&lunit); smc_p->num_storage++; slt_no = smc_p->num_storage; sp1->element_type = STORAGE_ELEMENT; sp1->status = STATUS_Access; sp1->slot_location = slt_no + smc_p->pm->start_storage - 1; /* Slot status to Empty */ setSlotEmpty(sp1); init_smc_log_pages(&lunit); init_smc_mode_pages(&lunit); MHVTL_DBG(1, "add slot && init smc"); /* malloc a big enough buffer to fit worst case read element status */ buffer_size = (smc_slots.num_drives + smc_slots.num_picker + smc_slots.num_map + smc_slots.num_storage) * 80; buffer_size = max(SMC_BUF_SIZE, buffer_size); smc_slots.bufsize = buffer_size; MHVTL_DBG(1, "Setting buffer size to %d", buffer_size); sprintf(message, "slot=%d", slt_no); send_msg(message, msg->snd_id); return; } /* * Respond to messageQ 'empty map' by clearing 'ocuplied' status in map slots. * Return 0 on failure, non-zero - success. */ static int empty_map(struct q_msg *msg) { struct s_info *sp; struct list_head *slot_head = &smc_slots.slot_list; if (smc_slots.cap_closed) { MHVTL_DBG(1, "MAP slot empty failed - CAP Not open"); send_msg("Can't empty map while MAP is closed", msg->snd_id); return 0; } list_for_each_entry(sp, slot_head, siblings) { if (slotOccupied(sp) && sp->element_type == MAP_ELEMENT) { setSlotEmpty(sp); MHVTL_DBG(2, "MAP slot %d emptied", sp->slot_location - smc_slots.pm->start_map); } } send_msg("OK", msg->snd_id); return 1; } /* Extract the id of the tape sending notification a tape was ejected * Set the 'access' bit in the READ_ELEMENT_STATUS page */ static int set_access_bit(struct q_msg *msg) { struct s_info *sp; struct list_head *slot_head = &smc_slots.slot_list; list_for_each_entry(sp, slot_head, siblings) { if (slotOccupied(sp) && sp->element_type == DATA_TRANSFER) { if (sp->drive->drv_id == msg->snd_id) { setAccessStatus(sp, 1); MHVTL_DBG(2, "Enabling access bit for drive id %ld", sp->drive->drv_id); } } } return 0; } /* * Return 1, exit program */ static int processMessageQ(struct q_msg *msg) { MHVTL_DBG(1, "%ld: Received from sender id: %ld, msg : %s", my_id, msg->snd_id, msg->text); if (!strncmp(msg->text, "debug", 5)) { if (debug) { debug--; } else { debug++; verbose = 2; } } if (!strncmp(msg->text, "add slot", 8)) add_storage_slot(msg); if (!strncmp(msg->text, "empty map", 9)) empty_map(msg); if (!strncmp(msg->text, msg_eject, strlen(msg_eject))) set_access_bit(msg); if (!strncmp(msg->text, "exit", 4)) return 1; if (!strncmp(msg->text, "open map", 8)) open_map(msg); if (!strncmp(msg->text, "close map", 9)) close_map(msg); if (!strncmp(msg->text, "list map", 8)) list_map(msg); if (!strncmp(msg->text, "load map ", 9)) load_map(msg); if (!strncmp(msg->text, "InquiryDataChange", 17)) set_inquiry_data_changed(); if (!strncmp(msg->text, "offline", 7)) { current_state = MHVTL_STATE_OFFLINE; lunit.online = 0; } if (!strncmp(msg->text, "online", 6)) { current_state = MHVTL_STATE_ONLINE; lunit.online = 1; } if (!strncmp(msg->text, "TapeAlert", 9)) { uint64_t flg = TA_NONE; sscanf(msg->text, "TapeAlert %" PRIx64, &flg); set_TapeAlert(flg); } if (!strncmp(msg->text, "verbose", 7)) { if (verbose) verbose--; else verbose = 3; MHVTL_LOG("verbose: %s at level %d", verbose ? "enabled" : "disabled", verbose); } return 0; } static struct d_info *lookup_drive(struct lu_phy_attr *lu, int drive_no) { struct list_head *drive_list_head; struct d_info *d; uint32_t slot_offset; drive_list_head = &((struct smc_priv *)lu->lu_private)->drive_list; slot_offset = ((struct smc_priv *)lu->lu_private)->pm->start_drive; /* Drive numbering starts from 1, decrement slot_offset */ slot_offset--; list_for_each_entry(d, drive_list_head, siblings) { MHVTL_DBG(3, "Slot location: %d, offset + drive_no: %d", d->slot->slot_location, slot_offset + drive_no); if (d->slot->slot_location == slot_offset + drive_no) return d; } return NULL; } struct s_info *add_new_slot(struct lu_phy_attr *lu) { struct s_info *new; struct list_head *slot_list_head; slot_list_head = &((struct smc_priv *)lu->lu_private)->slot_list; new = zalloc(sizeof(struct s_info)); if (!new) { MHVTL_ERR("Could not allocate memory for new slot struct"); exit(-ENOMEM); } list_add_tail(&new->siblings, slot_list_head); return new; } /* Open device config file and update device information */ static void update_drive_details(struct lu_phy_attr *lu) { char device_conf[CONF_FILE_SZ]; FILE *conf; char *b; /* Read from file into this buffer */ char *s; /* Somewhere for sscanf to store results */ int slot; long drv_id, lib_id; struct d_info *dp; struct s_info *sp; struct smc_priv *smc_p = lu->lu_private; if (get_config(device_conf, DEVICE_CONF, my_id) < 0) exit(1); conf = fopen(device_conf, "r"); if (!conf) { MHVTL_DBG(1, "Can not open config file %s : %s", device_conf, strerror(errno)); perror("Can not open config file"); exit(1); } s = zalloc(MALLOC_SZ); if (!s) { perror("Could not allocate memory"); exit(1); } b = zalloc(MALLOC_SZ); if (!b) { perror("Could not allocate memory"); exit(1); } drv_id = -1; dp = NULL; /* While read in a line */ while (readline(b, MALLOC_SZ, conf) != NULL) { if (b[0] == '#') /* Ignore comments */ continue; if (sscanf(b, "Drive: %ld", &drv_id) > 0) continue; if (sscanf(b, " Library ID: %ld Slot: %d", &lib_id, &slot) == 2 && lib_id == my_id && drv_id >= 0) { MHVTL_DBG(2, "Found Drive %ld in slot %d", drv_id, slot); dp = lookup_drive(lu, slot); if (!dp) { MHVTL_LOG("WARNING: Creating new entry for %ld", drv_id); dp = zalloc(sizeof(struct d_info)); if (!dp) { MHVTL_ERR("Couldn't malloc memory"); exit(-ENOMEM); } sp = add_new_slot(lu); sp->element_type = DATA_TRANSFER; dp->slot = sp; sp->drive = dp; list_add_tail(&dp->siblings, &smc_p->drive_list); } MHVTL_DBG(3, "Updating drive id in slot %d to : %ld", dp->slot->slot_location, drv_id); dp->drv_id = drv_id; continue; } if (dp) { if (sscanf(b, " Unit serial number: %s", s) > 0) { strncpy(dp->inq_product_sno, s, 10); rmnl(dp->inq_product_sno, ' ', 10); } if (sscanf(b, " Product identification: %16c", s) > 0) { /* sscanf does not NULL terminate */ /* 25 is len of ' Product identification: ' */ s[strlen(b) - 25] = '\0'; strncpy(dp->inq_product_id, s, 16); dp->inq_product_id[16] = 0; MHVTL_DBG(3, "id: \'%s\', inq_product_id: \'%s\'", s, dp->inq_product_id); } if (sscanf(b, " Product revision level: %s", s) > 0) { strncpy(dp->inq_product_rev, s, 4); rmnl(dp->inq_product_rev, ' ', 4); } if (sscanf(b, " Vendor identification: %s", s) > 0) { strncpy(dp->inq_vendor_id, s, 8); rmnl(dp->inq_vendor_id, ' ', 8); } } if (strlen(b) == 1) { /* Blank line => Reset device pointer */ drv_id = -1; dp = NULL; } } free(b); free(s); fclose(conf); } /* * Return 0 - no address space conflict * Return 1 - overlap address with another slot type */ static int check_overflow(struct lu_phy_attr *lu, int slot, char type) { struct smc_priv *smc_p; int co; smc_p = lu->lu_private; co = 0; switch (type) { case MAP_ELEMENT: co = slot + smc_p->pm->start_map; if (smc_p->pm->start_map < smc_p->pm->start_storage && co > smc_p->pm->start_storage) { MHVTL_LOG("MAP: %d, overlaps with storage slot", slot); return 1; } if (smc_p->pm->start_map < smc_p->pm->start_picker && co > smc_p->pm->start_picker) { MHVTL_LOG("MAP: %d, overlaps with Picker slot", slot); return 1; } if (smc_p->pm->start_map < smc_p->pm->start_drive && co > smc_p->pm->start_drive) { MHVTL_LOG("MAP: %d, overlaps with Drives", slot); return 1; } break; case DATA_TRANSFER: co = slot + smc_p->pm->start_drive; if (smc_p->pm->start_drive < smc_p->pm->start_storage && co > smc_p->pm->start_storage) { MHVTL_LOG("Drive: %d, overlaps with storage slot", slot); return 1; } if (smc_p->pm->start_drive < smc_p->pm->start_picker && co > smc_p->pm->start_picker) { MHVTL_LOG("Drive: %d, overlaps with picker slot", slot); return 1; } if (smc_p->pm->start_drive < smc_p->pm->start_map && co > smc_p->pm->start_map) { MHVTL_LOG("Drive: %d, overlaps with MAP slot", slot); return 1; } break; case MEDIUM_TRANSPORT: co = slot + smc_p->pm->start_picker; if (smc_p->pm->start_picker < smc_p->pm->start_map && co > smc_p->pm->start_map) { MHVTL_LOG("Picker slot: %d overlaps with MAP", slot); return 1; } if (smc_p->pm->start_picker < smc_p->pm->start_drive && co > smc_p->pm->start_drive) { MHVTL_LOG("Picker slot: %d overlaps with drives", slot); return 1; } if (smc_p->pm->start_picker < smc_p->pm->start_storage && co > smc_p->pm->start_storage) { MHVTL_LOG("Picker slot: %d overlaps with Storage", slot); return 1; } break; case STORAGE_ELEMENT: co = slot + smc_p->pm->start_storage; if (smc_p->pm->start_storage < smc_p->pm->start_map && co > smc_p->pm->start_map) { MHVTL_LOG("Storage slot: %d, overlaps with MAP", slot); return 1; } if (smc_p->pm->start_storage < smc_p->pm->start_picker && co > smc_p->pm->start_picker) { MHVTL_LOG("Storage slot: %d, overlaps with picker", slot); return 1; } if (smc_p->pm->start_storage < smc_p->pm->start_drive && co > smc_p->pm->start_drive) { MHVTL_LOG("Storage slot: %d, overlaps with drives", slot); return 1; } break; } return 0; } void init_drive_slot(struct lu_phy_attr *lu, int slt, char *s) { struct s_info *sp = NULL; struct d_info *dp = NULL; struct smc_priv *smc_p = lu->lu_private; if (check_overflow(lu, slt, DATA_TRANSFER)) return; dp = lookup_drive(lu, slt); if (!dp) { dp = zalloc(sizeof(struct d_info)); if (!dp) { MHVTL_ERR("Couldn't malloc memory"); exit(-ENOMEM); } sp = add_new_slot(lu); sp->element_type = DATA_TRANSFER; dp->slot = sp; sp->drive = dp; list_add_tail(&dp->siblings, &smc_p->drive_list); } dp->slot->slot_location = slt + smc_p->pm->start_drive - 1; dp->slot->status = STATUS_Access; smc_p->num_drives++; if (strlen(s)) { strncpy(dp->inq_product_sno, s, 10); MHVTL_DBG(2, "Drive s/no: %s", s); } MHVTL_DBG(3, "Slot: %d, start_drive: %d, slot_location: %d", slt, smc_p->pm->start_drive, dp->slot->slot_location); } void init_map_slot(struct lu_phy_attr *lu, int slt, char *barcode) { struct s_info *sp = NULL; struct smc_priv *smc_p = lu->lu_private; if (check_overflow(lu, slt, MAP_ELEMENT)) return; sp = add_new_slot(lu); sp->element_type = MAP_ELEMENT; smc_p->num_map++; sp->slot_location = slt + smc_p->pm->start_map - 1; sp->status = STATUS_InEnab | STATUS_ExEnab | STATUS_Access | STATUS_ImpExp; if (strlen(barcode)) { MHVTL_DBG(2, "Barcode %s in MAP %d", barcode, slt); sp->media = add_barcode(lu, barcode); sp->status |= STATUS_Full; } } void init_transport_slot(struct lu_phy_attr *lu, int slt, char *barcode) { struct s_info *sp = NULL; struct smc_priv *smc_p = lu->lu_private; if (check_overflow(lu, slt, MEDIUM_TRANSPORT)) return; sp = add_new_slot(lu); sp->element_type = MEDIUM_TRANSPORT; smc_p->num_picker++; sp->slot_location = slt + smc_p->pm->start_picker - 1; sp->status = 0; if (strlen(barcode)) { MHVTL_DBG(2, "Barcode %s in Picker %d", barcode, slt); sp->media = add_barcode(lu, barcode); sp->slot_location = slt + smc_p->pm->start_picker - 1; sp->status |= STATUS_Full; } } void init_storage_slot(struct lu_phy_attr *lu, int slt, char *barcode) { struct s_info *sp = NULL; struct smc_priv *smc_p = lu->lu_private; if (check_overflow(lu, slt, STORAGE_ELEMENT)) return; sp = add_new_slot(lu); sp->element_type = STORAGE_ELEMENT; smc_p->num_storage++; sp->status = STATUS_Access; sp->slot_location = slt + smc_p->pm->start_storage - 1; if (strlen(barcode)) { MHVTL_DBG(2, "Barcode %s in slot %d", barcode, slt); sp->media = add_barcode(lu, barcode); /* Slot full */ sp->status |= STATUS_Full; } } static void __init_slot_info(struct lu_phy_attr *lu, int type) { char lib_conf[CONF_FILE_SZ]; FILE *ctrl; char *b; /* Read from file into this buffer */ char *s; /* Somewhere for sscanf to store results */ int slt; struct stat configstat; struct stat persiststat; int filestat; int start_slot = 1; /* Slot creation needs to start with 1 */ int z; filestat = -1; /* Default to .persist file does not exist */ /* Lets stat each (potential) file and identify the last one modified */ get_config(lib_conf, LIBCONTENTS_PERSIST, my_id); if (lu->persist) /* If enabled - stat .persist file */ filestat = stat(lib_conf, &persiststat); if (filestat < 0) { /* PERSIST is either disabled or .persist file does not exist * - Update config filename to master 'library_contents. */ get_config(lib_conf, LIBCONTENTS, my_id); } else { /* stat original config file */ get_config(lib_conf, LIBCONTENTS, my_id); filestat = stat(lib_conf, &configstat); if (filestat < 0) { /* Does not exist !! */ MHVTL_ERR("Can not stat config file %s: %s", lib_conf, strerror(errno)); exit(1); } if (configstat.st_mtime > persiststat.st_mtime) { /* Don't do anything - leave config filename alone */ MHVTL_DBG(1, "%s is newer than %s.persist file. " "Using %s instead", lib_conf, lib_conf, lib_conf); } else { /* Update the config file to library_contents..persist */ get_config(lib_conf, LIBCONTENTS_PERSIST, my_id); } } /* By the time we get here - * - If PERSIST is enabled * - We have stat'ed each file - so unless it's been removed within * last few millisecs we should be good. * - Filename will be latest modify date */ ctrl = fopen(lib_conf, "r"); if (!ctrl) { MHVTL_ERR("Can not open config file %s : %s", lib_conf, strerror(errno)); exit(1); } /* Log which config file is being used to read in data */ MHVTL_DBG(2, "Reading %s configuration information from %s", slot_type_str(type), lib_conf); /* Grab a couple of generic MALLOC_SZ buffers.. */ s = zalloc(MALLOC_SZ); if (!s) { perror("Could not allocate memory"); exit(1); } b = zalloc(MALLOC_SZ); if (!b) { perror("Could not allocate memory"); exit(1); } rewind(ctrl); while (readline(b, MALLOC_SZ, ctrl) != NULL) { if (b[0] == '#') /* Ignore comments */ continue; s[0] = '\0'; switch (type) { case DATA_TRANSFER: if (sscanf(b, "Drive %d: %s", &slt, s)) { if (slt > start_slot) { /* Config file has holes - fill in empty slots */ MHVTL_DBG(1, "Config file is missing Drive %d - Creating empty records up to drive %d", start_slot, slt); for (z = start_slot; z < slt; z++) { init_drive_slot(lu, z, ""); } } init_drive_slot(lu, slt, s); start_slot = slt + 1; } break; case MAP_ELEMENT: if (sscanf(b, "MAP %d: %s", &slt, s)) init_map_slot(lu, slt, s); break; case MEDIUM_TRANSPORT: if (sscanf(b, "Picker %d: %s", &slt, s)) init_transport_slot(lu, slt, s); break; case STORAGE_ELEMENT: if (sscanf(b, "Slot %d: %s", &slt, s)) { if (slt > start_slot) { /* Config file has holes - fill in empty slots */ MHVTL_DBG(1, "Config file is missing Slot %d - Creating empty slots up to %d", start_slot, slt); for (z = start_slot; z < slt; z++) { init_storage_slot(lu, z, ""); } } init_storage_slot(lu, slt, s); start_slot = slt + 1; } break; } } fclose(ctrl); free(b); free(s); } /* Linked list data needs to be built in slot order */ void init_slot_info(struct lu_phy_attr *lu) { int i; struct smc_type_slot arr[4]; sort_library_slot_type(lu, &arr[0]); for (i = 0; i < 4; i++) __init_slot_info(lu, arr[i].type); } /* Return original slot location if empty */ static struct s_info *previous_storage_slot(struct s_info *s, struct list_head *slot_head) { struct s_info *sp; /* Slot Pointer */ /* Find slot info for 'previous location' */ list_for_each_entry(sp, slot_head, siblings) { if (sp->element_type == STORAGE_ELEMENT) if (sp->slot_location == s->last_location) if (!slotOccupied(sp)) /* previous location is empty */ return sp; } return NULL; } /* Return first empty storage slot. */ static struct s_info *find_empty_storage_slot(struct s_info *s, struct list_head *slot_head) { struct s_info *sp; /* Slot Pointer */ /* If previous location is no good - lets find first empty slot */ list_for_each_entry(sp, slot_head, siblings) { if (!slotOccupied(sp) && sp->element_type == STORAGE_ELEMENT) return sp; } return NULL; } /* Save config on shutdown - Not to be called at other times !! */ static void save_config(struct lu_phy_attr *lu) { FILE *ctrl; char lib_conf[CONF_FILE_SZ]; struct smc_priv *lu_priv; struct list_head *slot_head; struct list_head *drive_head; struct s_info *sp; /* Slot Pointer */ struct d_info *dp; /* Drive Pointer */ int last_element_type = 0; get_config(lib_conf, LIBCONTENTS_PERSIST, my_id); ctrl = fopen(lib_conf, "w"); if (!ctrl) { MHVTL_ERR("Can not open file %s to save state : %s", lib_conf, strerror(errno)); return; } lu_priv = lu->lu_private; drive_head = &lu_priv->drive_list; slot_head = &lu_priv->slot_list; /* Walk each drive and force-unload into previous location * - if possible */ list_for_each_entry(dp, drive_head, siblings) { if (slotOccupied(dp->slot)) { /* Force a move of media from drive into * empty storage slot on shutdown */ sp = dp->slot; MHVTL_DBG(1, "Found %s in drive %d from %d", sp->media->barcode, sp->slot_location - lu_priv->pm->start_drive + 1, sp->last_location); unload_drive_on_shutdown(sp, previous_storage_slot(sp, slot_head)); } } /* Walk each drive and force-unload into first empty storage slot */ list_for_each_entry(dp, drive_head, siblings) { if (slotOccupied(dp->slot)) { /* Force a move of media from drive into * empty storage slot on shutdown */ sp = dp->slot; MHVTL_DBG(1, "Found %s in drive %d from %d", sp->media->barcode, sp->slot_location - lu_priv->pm->start_drive + 1, sp->last_location); unload_drive_on_shutdown(sp, find_empty_storage_slot(sp, slot_head)); } } /* Walk the list of all slots and write data into .persist file */ list_for_each_entry(sp, slot_head, siblings) { /* Pretty up conf file - * Place a blank line between element types */ if (last_element_type != sp->element_type) { last_element_type = sp->element_type; fprintf(ctrl, "\n"); } switch (sp->element_type) { case DATA_TRANSFER: fprintf(ctrl, "Drive %d:\n", sp->slot_location - lu_priv->pm->start_drive + 1); break; case MEDIUM_TRANSPORT: fprintf(ctrl, "Picker %d: %s\n", sp->slot_location - lu_priv->pm->start_picker + 1, slotOccupied(sp) ? sp->media->barcode : ""); break; case MAP_ELEMENT: fprintf(ctrl, "MAP %d: %s\n", sp->slot_location - lu_priv->pm->start_map + 1, slotOccupied(sp) ? sp->media->barcode : ""); break; case STORAGE_ELEMENT: fprintf(ctrl, "Slot %d: %s\n", sp->slot_location - lu_priv->pm->start_storage + 1, slotOccupied(sp) ? sp->media->barcode : ""); break; } } fclose(ctrl); } static int init_lu(struct lu_phy_attr *lu, unsigned minor, struct mhvtl_ctl *ctl) { struct vpd **lu_vpd = lu->lu_vpd; char device_conf[CONF_FILE_SZ]; FILE *conf; char *b; /* Read from file into this buffer */ char *s; /* Somewhere for sscanf to store results */ int indx; struct mhvtl_ctl tmpctl; int found = 0; int linecount; backoff = DEFLT_BACKOFF_VALUE; lu->persist = FALSE; if (get_config(device_conf, DEVICE_CONF, my_id) < 0) exit(1); /* Set static 'home_directory' var - used for get_cart_type() function */ update_home_dir(my_id); /* Configure default inquiry data */ memset(&lu->inquiry, 0, MAX_INQUIRY_SZ); lu->inquiry[0] = TYPE_MEDIUM_CHANGER; /* SMC device */ lu->inquiry[1] = 0x80; /* Removable bit set */ lu->inquiry[2] = 0x05; /* SCSI Version (v3) */ lu->inquiry[3] = 0x02; /* Response Data Format */ lu->inquiry[4] = 59; /* Additional Length */ lu->inquiry[6] = 0x01; /* Addr16 */ lu->inquiry[7] = 0x20; /* Wbus16 */ put_unaligned_be16(0x0300, &lu->inquiry[58]); /* SPC-3 No ver claimed */ put_unaligned_be16(0x0960, &lu->inquiry[60]); /* iSCSI */ put_unaligned_be16(0x0200, &lu->inquiry[62]); /* SSC */ lu->ptype = TYPE_MEDIUM_CHANGER; /* SSC */ lu->sense_p = &sense[0]; /* Save pointer to sense buffer */ conf = fopen(device_conf, "r"); if (!conf) { MHVTL_ERR("Can not open config file %s : %s", device_conf, strerror(errno)); perror("Can not open config file"); exit(1); } s = zalloc(MALLOC_SZ); if (!s) { perror("Could not allocate memory"); exit(1); } b = zalloc(MALLOC_SZ); if (!b) { perror("Could not allocate memory"); exit(1); } lu->fifoname = NULL; lu->fifo_fd = NULL; lu->fifo_flag = 0; smc_slots.movecommand = NULL; smc_slots.commandtimeout = 20; /* While read in a line */ linecount = 0; /* Line count */ while (readline(b, MALLOC_SZ, conf) != NULL) { linecount++; if (b[0] == '#') /* Ignore comments */ continue; if (strlen(b) == 1) /* Reset drive number of blank line */ indx = 0xff; if (sscanf(b, "Library: %d CHANNEL: %d TARGET: %d LUN: %d", &indx, &tmpctl.channel, &tmpctl.id, &tmpctl.lun)) { MHVTL_DBG(2, "Found Library %d, looking for %u", indx, minor); if (indx == minor) { found = 1; memcpy(ctl, &tmpctl, sizeof(tmpctl)); } } if (indx == minor) { unsigned int c, d, e, f, g, h, j, k; int i; if (sscanf(b, " Unit serial number: %s", s)) { checkstrlen(s, SCSI_SN_LEN, linecount); snprintf(lu->lu_serial_no, SCSI_SN_LEN, "%-14s", s); } if (sscanf(b, " Product identification: %16c", s) > 0) { /* sscanf does not NULL terminate */ i = strlen(b) - 25; /* len of ' Product identification: ' */ s[i] = '\0'; snprintf(lu->product_id, PRODUCT_ID_LEN + 1, "%-16s", s); snprintf(&lu->inquiry[16], PRODUCT_ID_LEN + 1, "%-16s", s); } if (sscanf(b, " Product revision level: %s", s)) { checkstrlen(s, PRODUCT_REV_LEN, linecount); snprintf(&lu->inquiry[32], PRODUCT_REV_LEN + 1, "%-4s", s); } if (sscanf(b, " Vendor identification: %s", s)) { checkstrlen(s, VENDOR_ID_LEN, linecount); snprintf(lu->vendor_id, VENDOR_ID_LEN + 1, "%-8s", s); snprintf(&lu->inquiry[8], VENDOR_ID_LEN + 1, "%-8s", s); } if (sscanf(b, " fifo: %s", s)) process_fifoname(lu, s, 0); if (sscanf(b, " PERSIST: %s", s)) { if (!strncasecmp(s, "yes", 3) || (!strncasecmp(s, "true", 4))) lu->persist = TRUE; } if (sscanf(b, " movecommand: %s", s)) smc_slots.movecommand = strndup(s, MALLOC_SZ); if (sscanf(b, " commandtimeout: %d", &d)) smc_slots.commandtimeout = d; if (sscanf(b, " Backoff: %d", &i)) { if ((i > 1) && (i < 10000)) { MHVTL_DBG(1, "Backoff value: %d", i); backoff = i; } } i = sscanf(b, " NAA: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", &c, &d, &e, &f, &g, &h, &j, &k); if (i == 8) { free(lu->naa); lu->naa = zalloc(48); if (lu->naa) sprintf((char *)lu->naa, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", c, d, e, f, g, h, j, k); MHVTL_DBG(2, "Setting NAA: to %s", lu->naa); } else if (i > 0) { free(lu->naa); lu->naa = NULL; /* Cleanup string (replace 'nl' with null) * for logging */ rmnl(b, '\0', MALLOC_SZ); MHVTL_DBG(1, "NAA: Incorrect params: %s" ", Using defaults", b); } } } fclose(conf); free(b); free(s); if (found && !lu->inquiry[32]) { char *v; /* Default rev with mhvtl release info */ v = get_version(); MHVTL_DBG(1, "Adding default vers info: %s", v); sprintf(&lu->inquiry[32], "%-4s", v); free(v); } INIT_LIST_HEAD(&lu->den_list); INIT_LIST_HEAD(&lu->log_pg); INIT_LIST_HEAD(&lu->mode_pg); INIT_LIST_HEAD(&smc_slots.slot_list); INIT_LIST_HEAD(&smc_slots.drive_list); INIT_LIST_HEAD(&smc_slots.media_list); free(smc_slots.state_msg); smc_slots.num_drives = 0; smc_slots.num_picker = 0; smc_slots.num_map = 0; smc_slots.num_storage = 0; smc_slots.bufsize = SMC_BUF_SIZE; smc_slots.state_msg = NULL; /* Unit Serial Number */ lu_vpd[PCODE_OFFSET(0x80)] = alloc_vpd(strlen(lu->lu_serial_no)); update_vpd_80(lu, lu->lu_serial_no); lu->lu_private = &smc_slots; smc_slots.cap_closed = CAP_CLOSED; return found; } static void process_cmd(int cdev, uint8_t *buf, struct mhvtl_header *mhvtl_cmd, useconds_t pollInterval) { struct mhvtl_ds dbuf; uint8_t *cdb; /* Get the SCSI cdb from vtl driver * - Returns SCSI command S/No. */ cdb = (uint8_t *)&mhvtl_cmd->cdb; /* Interpret the SCSI command & process -> Returns no. of bytes to send back to kernel */ dbuf.sz = 0; dbuf.serialNo = mhvtl_cmd->serialNo; dbuf.data = buf; dbuf.sam_stat = sam_status; dbuf.sense_buf = &sense; processCommand(cdev, cdb, &dbuf, pollInterval); /* Complete SCSI cmd processing */ completeSCSICommand(cdev, &dbuf); /* dbuf.sam_stat was zeroed in completeSCSICommand */ sam_status = dbuf.sam_stat; } /* * Be nice and free all malloc() on exit */ static void cleanup_lu(struct lu_phy_attr *lu) { int i; struct smc_priv *lu_priv; struct list_head *slot_head; struct s_info *sp, *sn; /* Slot */ struct d_info *dp, *dn; /* Drive */ struct m_info *mp, *mn; /* Media */ lu_priv = lu->lu_private; /* Free all VPD pages */ for (i = 0x80; i < 0x100; i++) { if (lu->lu_vpd[PCODE_OFFSET(i)]) { dealloc_vpd(lu->lu_vpd[PCODE_OFFSET(i)]); lu->lu_vpd[PCODE_OFFSET(i)] = NULL; } } free(lu->naa); lu->naa = NULL; dealloc_all_mode_pages(lu); dealloc_all_log_pages(lu); slot_head = &lu_priv->slot_list; list_for_each_entry_safe(sp, sn, slot_head, siblings) { list_del(&sp->siblings); free(sp); } slot_head = &lu_priv->drive_list; list_for_each_entry_safe(dp, dn, slot_head, siblings) { list_del(&dp->siblings); free(dp); } slot_head = &lu_priv->media_list; list_for_each_entry_safe(mp, mn, slot_head, siblings) { list_del(&mp->siblings); free(mp); } free(lu_priv->state_msg); lu_priv->state_msg = NULL; } static void customise_ibm_lu(struct lu_phy_attr *lu) { if (!strncasecmp(lu->product_id, "3573-TL", 7)) init_ibmts3100(lu); else if (!strncasecmp(lu->product_id, "03584", 5)) init_ibm3584(lu); else init_default_smc(lu); } static void customise_stk_lu(struct lu_phy_attr *lu) { if (!strncasecmp(lu->product_id, "SL500", 5)) init_stkslxx(lu); /* STK SL series */ else if (!strncasecmp(lu->product_id, "L20", 3)) init_stkl20(lu); /* L20/40/80 */ else if (!strncasecmp(lu->product_id, "L40", 3)) init_stkl20(lu); /* L20/40/80 */ else if (!strncasecmp(lu->product_id, "L80", 3)) init_stkl20(lu); /* L20/40/80 */ else init_stklxx(lu); /* STK L series */ } static void customise_hp_lu(struct lu_phy_attr *lu) { if (!strncasecmp(lu->product_id, "MSL", 3)) init_hp_msl_smc(lu); else init_hp_eml_smc(lu); } static void customise_spectra_lu(struct lu_phy_attr *lu) { if (!strncasecmp(lu->product_id, "PYTHON", 6)) init_spectra_logic_smc(lu); else if (!strncasecmp(lu->product_id, "GATOR", 5)) init_spectra_gator_smc(lu); else if (!strncasecmp(lu->product_id, "215", 3)) init_spectra_215_smc(lu); else init_spectra_logic_smc(lu); } static void customise_lu(struct lu_phy_attr *lu) { if (!strncasecmp(lu->vendor_id, "stk", 3)) customise_stk_lu(lu); else if (!strncasecmp(lu->vendor_id, "IBM", 3)) customise_ibm_lu(lu); else if (!strncasecmp(lu->vendor_id, "HP", 2)) customise_hp_lu(lu); else if (!strncasecmp(lu->product_id, "OVERLAND", 8)) init_overland_smc(lu); else if (!strncasecmp(lu->product_id, "ADIC", 4)) init_scalar_smc(lu); else if (!strncasecmp(lu->product_id, "QUANTUM", 7)) init_scalar_smc(lu); else if (!strncasecmp(lu->vendor_id, "SPECTRA ", 7)) customise_spectra_lu(lu); else init_default_smc(lu); } void rereadconfig(int sig) { struct mhvtl_ctl ctl; int buffer_size; lunit.online = 0; /* Report library offline until finished */ MHVTL_DBG(1, "Caught signal (%d): Re-initialising library %d", sig, (int)my_id); cleanup_lu(&lunit); if (lunit.fifo_fd) { fclose(lunit.fifo_fd); free(lunit.fifoname); lunit.fifo_fd = NULL; } if (!init_lu(&lunit, my_id, &ctl)) { fprintf(stderr, "error: Cannot find entry for '%ld' in config file\n", my_id); exit(1); } if (lunit.fifoname) open_fifo(&lunit.fifo_fd, lunit.fifoname); customise_lu(&lunit); update_drive_details(&lunit); /* malloc a big enough buffer to fit worst case read element status */ buffer_size = (smc_slots.num_drives + smc_slots.num_picker + smc_slots.num_map + smc_slots.num_storage) * 80; reset_device(); /* Force a POWER-ON/RESET sense code */ if (buffer_size > smc_slots.bufsize) { MHVTL_LOG("Too many slots configured" " - possible buffer overflow"); MHVTL_LOG("Please shutdown this daemon and restart so" " correct buffer allocation can be performed"); /* Leave library offline */ } else { lunit.online = 1; /* Should be good to go */ } } void smc_personality_module_register(struct smc_personality_template *pm) { MHVTL_LOG("%s", pm->name); smc_slots.pm = pm; } static void caught_signal(int signo) { MHVTL_DBG(1, " %d", signo); printf("Please use 'vtlcmd exit' to shutdown nicely\n"); MHVTL_LOG("Please use 'vtlcmd exit' to shutdown nicely," " Received signal: %d", signo); } /* * main() * * e'nuf sed */ int main(int argc, char *argv[]) { int cdev; int ret; long pollInterval = 0L; uint8_t *buf; int buffer_size; int fifo_retval; int opt; int foreground = 0; int time_to_exit = 0; int last_state = MHVTL_STATE_UNKNOWN; struct list_head *slot_head = &smc_slots.slot_list; struct s_info *sp; struct d_info *dp; struct mhvtl_header mhvtl_cmd; struct mhvtl_ctl ctl; char s[100]; pid_t pid, sid, child_cleanup; struct sigaction new_action, old_action; char *progname = argv[0]; char *name = "mhvtl"; char *fifoname = NULL; memset(&mhvtl_cmd, 0, sizeof(struct mhvtl_header)); memset(&ctl, 0, sizeof(struct mhvtl_ctl)); /* Message Q */ int mlen, r_qid; struct q_entry r_entry; while ((opt = getopt(argc, argv, "dv::q:f::F")) != -1) { switch (opt) { case 'd': debug++; verbose = 9; /* If debug, make verbose... */ foreground = 1; break; case 'v': if (optarg) verbose = atoi(optarg); else verbose++; /* limit verbosity to single digit */ if (verbose > 9) verbose = 9; break; case 'q': my_id = atoi(optarg); if ((my_id < 0) || (my_id > MAXPRIOR)) { fprintf(stderr, "error: queue ID out of range [1..%u]\n", MAXPRIOR); usage(progname); exit(1); } break; case 'f': if (optarg) fifoname = strdup(optarg); break; case 'F': foreground = 1; break; default: usage(progname); exit(1); } } if (my_id < 0) { fprintf(stderr, "error: must supply queue ID\n"); usage(progname); exit(1); } openlog(progname, LOG_PID, LOG_DAEMON | LOG_WARNING); /* Clear Sense arr */ memset(sense, 0, sizeof(sense)); reset_device(); /* power-on reset */ if (!init_lu(&lunit, my_id, &ctl)) { fprintf(stderr, "error: Can not find entry for '%ld' in config file\n", my_id); exit(1); } /* Personality module init call */ customise_lu(&lunit); update_drive_details(&lunit); lunit.online = 1; /* Mark unit online */ if (chrdev_create(my_id)) { MHVTL_DBG(1, "Error creating device node mhvtl%d", (int)my_id); exit(1); } new_action.sa_handler = caught_signal; new_action.sa_flags = 0; sigemptyset(&new_action.sa_mask); sigaction(SIGALRM, &new_action, &old_action); sigaction(SIGINT, &new_action, &old_action); sigaction(SIGPIPE, &new_action, &old_action); sigaction(SIGTERM, &new_action, &old_action); sigaction(SIGUSR1, &new_action, &old_action); sigaction(SIGUSR2, &new_action, &old_action); new_action.sa_handler = rereadconfig; sigaction(SIGHUP, &new_action, &old_action); /* Initialise message queue as necessary */ r_qid = init_queue(); if (r_qid == -1) { fprintf(stderr, "error: Could not initialise message queue\n"); exit(1); } if (check_for_running_daemons(my_id)) { MHVTL_LOG("%s: version %s %s %s, found another running daemon... exiting", progname, MHVTL_VERSION, MHVTL_GITHASH, MHVTL_GITDATE); exit(2); } /* Clear out message Q by reading anything there. */ mlen = msgrcv(r_qid, &r_entry, MAXOBN, my_id, IPC_NOWAIT); while (mlen > 0) { MHVTL_DBG(2, "Found \"%s\" still in message Q", r_entry.msg.text); mlen = msgrcv(r_qid, &r_entry, MAXOBN, my_id, IPC_NOWAIT); } cdev = chrdev_open(name, my_id); if (cdev == -1) { MHVTL_ERR("Could not open /dev/%s%ld: %s", name, my_id, strerror(errno)); fflush(NULL); exit(1); } /* malloc a big enough buffer to fit worst case read element status */ buffer_size = (smc_slots.num_drives + smc_slots.num_picker + smc_slots.num_map + smc_slots.num_storage) * 80; buffer_size = max(SMC_BUF_SIZE, buffer_size); smc_slots.bufsize = buffer_size; MHVTL_DBG(1, "Setting buffer size to %d", buffer_size); buf = (uint8_t *)zalloc(buffer_size); if (NULL == buf) { perror("Problems allocating memory"); exit(1); } /* Send a message to each tape drive so they know the * controlling library's message Q id */ list_for_each_entry(sp, slot_head, siblings) { if (sp->element_type == DATA_TRANSFER) { dp = sp->drive; MHVTL_DBG(1, "Registering driveId: %ld", dp->drv_id); send_msg("Register", dp->drv_id); if (debug) { MHVTL_DBG(3, "\nDrive %d", sp->slot_location); strncpy(s, dp->inq_vendor_id, 8 + 2); rmnl(s, ' ', 8); s[8] = '\0'; MHVTL_DBG(3, "Vendor ID : \"%s\"", s); strncpy(s, dp->inq_product_id, 16 + 2); rmnl(s, ' ', 16); s[16] = '\0'; MHVTL_DBG(3, "Product ID : \"%s\"", s); strncpy(s, dp->inq_product_rev, 4 + 2); rmnl(s, ' ', 4); s[4] = '\0'; MHVTL_DBG(3, "Revision Level: \"%s\"", s); strncpy(s, dp->inq_product_sno, 10 + 2); rmnl(s, ' ', 10); s[10] = '\0'; MHVTL_DBG(3, "Product S/No : \"%s\"", s); MHVTL_DBG(3, "Drive location: %d", dp->slot->slot_location); MHVTL_DBG(3, "Drive occupied: %s", (dp->slot->status & STATUS_Full) ? "No" : "Yes"); } } } /* If debug or 'F' specified don't fork/run in background */ if (!foreground) { switch (pid = fork()) { case 0: /* Child */ break; case -1: fprintf(stderr, "error: Failed to fork daemon\n"); exit(-1); break; default: printf("%s process PID is %d\n", progname, (int)pid); exit(0); } umask(0); /* Change the file mode mask */ sid = setsid(); if (sid < 0) exit(-1); if ((chdir(MHVTL_HOME_PATH)) < 0) { perror("Unable to change directory to " MHVTL_HOME_PATH); exit(-1); } close(STDIN_FILENO); close(STDERR_FILENO); } MHVTL_LOG("[%ld] Started %s: version %s %s %s verbose log lvl: %d, lu [%d:%d:%d]", (long)getpid(), progname, MHVTL_VERSION, MHVTL_GITHASH, MHVTL_GITDATE, verbose, ctl.channel, ctl.id, ctl.lun); oom_adjust(); /* If fifoname passed as switch */ if (fifoname) process_fifoname(&lunit, fifoname, 1); /* fifoname can be defined in device.conf */ if (lunit.fifoname) open_fifo(&lunit.fifo_fd, lunit.fifoname); fifo_retval = inc_fifo_count(); if (fifo_retval == -ENOMEM) { MHVTL_ERR("shared memory setup failed - exiting..."); goto exit; } else if (fifo_retval < 0) { MHVTL_ERR("Failed to set fifo count()..."); } child_cleanup = add_lu(my_id, &ctl); if (!child_cleanup) { fprintf(stderr, "error: Could not create logical unit\n"); exit(1); } for (;;) { /* Check for any messages */ mlen = msgrcv(r_qid, &r_entry, MAXOBN, my_id, IPC_NOWAIT); if (mlen > 0) { if (processMessageQ(&r_entry.msg)) time_to_exit = 1; } else if (mlen < 0) { r_qid = init_queue(); if (r_qid == -1) MHVTL_ERR("Can not open message queue: %s", strerror(errno)); } ret = ioctl(cdev, VTL_POLL_AND_GET_HEADER, &mhvtl_cmd); if (ret < 0) { MHVTL_LOG("ret: %d : %s", ret, strerror(errno)); } else { if (child_cleanup) { if (waitpid(child_cleanup, NULL, WNOHANG)) { MHVTL_DBG(1, "[%ld] Cleaning up after add_lu " "child pid: %d", (long)getpid(), child_cleanup); child_cleanup = 0; } else { MHVTL_DBG(2, "[%ld] Child cleanup of %ld still outstanding", (long)getpid(), (long)child_cleanup); } } if (debug) fflush(NULL); /* So I can pipe debug o/p thru tee */ switch (ret) { case VTL_QUEUE_CMD: if (smc_slots.bufsize != buffer_size) { buffer_size = smc_slots.bufsize; buf = realloc(buf, buffer_size); if (!buf) { perror("Problems allocating memory"); exit(1); } } process_cmd(cdev, buf, &mhvtl_cmd, pollInterval); pollInterval = MIN_SLEEP_TIME; break; case VTL_IDLE: usleep(pollInterval); if (pollInterval < 1000000) pollInterval += backoff; break; default: MHVTL_LOG("ioctl(0x%x) returned %d", VTL_POLL_AND_GET_HEADER, ret); sleep(1); break; } if (current_state != last_state) { status_change(lunit.fifo_fd, current_state, my_id, &smc_slots.state_msg); last_state = current_state; } if (pollInterval > 0xf000) /* enough time to ensure no outstanding op in flight */ if (time_to_exit) goto exit; if (pollInterval > 0x18000) if (current_state != MHVTL_STATE_OFFLINE) current_state = MHVTL_STATE_IDLE; } } exit: ioctl(cdev, VTL_REMOVE_LU, &ctl); if (lunit.persist) save_config(&lunit); cleanup_lu(&lunit); close(cdev); free(buf); dec_fifo_count(); if (lunit.fifo_fd) { fclose(lunit.fifo_fd); unlink(lunit.fifoname); free(lunit.fifoname); } free_lock(my_id); exit(0); } ================================================ FILE: usr/cmd/vtltape.c ================================================ /* * This daemon is the SCSI SSC target (Sequential device - tape drive) * portion of the vtl package. * * The vtl package consists of: * a kernel module (vlt.ko) - Currently on 2.6.x Linux kernel support. * SCSI target daemons for both SMC and SSC devices. * * Copyright (C) 2005 - 2025 Mark Harvey markh794@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * v0.1 -> Proof (proof of concept) that this may actually work (just) * v0.2 -> Get queueCommand() callback working - * (Note to self: Sleeping in kernel is bad!) * v0.3 -> Message queues + make into daemon * changed lseek to lseek64 * v0.4 -> First copy given to anybody, * v0.10 -> First start of a Solaris x86 port.. Still underway. * v0.11 -> First start of a Linux 2.4 kernel port.. Still underway. * However I'm scrapping this kfifo stuff and passing a pointer * and using copy{to|from}_user routines instead. * v0.12 -> Forked into 'stable' (0.12) and 'devel' (0.13). * My current thinking : This is a dead end anyway. * An iSCSI target done in user-space is now my perferred solution. * This means I don't have to do any kernel level drivers * and leaverage the hosts native iSCSI initiator. * 0.14 13 Feb 2008 * Since ability to define device serial number, increased ver from * 0.12 to 0.14 * * 0.16 Jun 2009 * Moved SCSI Inquiry into user-space. * SCSI lu are created/destroyed as the daemon is started/shutdown */ #define _FILE_OFFSET_BITS 64 #define _XOPEN_SOURCE 500 #define __STDC_FORMAT_MACROS /* for PRId64 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mhvtl_list.h" #include "be_byteshift.h" #include "vtl_common.h" #include "mhvtl_scsi.h" #include "q.h" #include "logging.h" #include "vtllib.h" #include "vtlcart.h" #include "spc.h" #include "ssc.h" #include "mhvtl_log.h" #include "mode.h" char mhvtl_driver_name[] = "vtltape"; /* Variables for simple, logical only SCSI Encryption system */ struct encryption app_encryption_state; /* Stores the encryption info the application sent us */ #define UKAD_LENGTH app_encryption_state.ukad_length #define AKAD_LENGTH app_encryption_state.akad_length #define KEY_LENGTH app_encryption_state.key_length #define UKAD app_encryption_state.ukad #define AKAD app_encryption_state.akad #define KEY app_encryption_state.key #include #include "minilzo.h" static uint8_t last_cmd; /* Suppress Incorrect Length Indicator */ #define SILI 0x2 /* Fixed block format */ #define FIXED 0x1 #ifndef Solaris /* I'm sure there must be a header where lseek64() is defined */ loff_t lseek64(int, loff_t, int); #endif #define SEND_MSG_AND_LOG(s, id) \ { \ send_msg(s, id); \ MHVTL_DBG(1, "%ld: Replying to snd_id %" PRIu64 " with \"%s\"", my_id, id, s); \ } /* Backoff algrithm.. * Each empty poll of kernel module, add backoff to sleep time * and call usleep() before polling again. */ static long backoff; static useconds_t cumul_pollInterval; static int library_id = 0; #define MEDIA_WRITABLE 0 #define MEDIA_READONLY 1 static struct tape_drives_table { char *name; void (*init)(struct lu_phy_attr *); } tape_drives[] = { {"ULT3580-TD1 ", init_ult3580_td1}, {"ULT3580-TD2 ", init_ult3580_td2}, {"ULT3580-TD3 ", init_ult3580_td3}, {"ULT3580-TD4 ", init_ult3580_td4}, {"ULT3580-TD5 ", init_ult3580_td5}, {"ULT3580-TD6 ", init_ult3580_td6}, {"ULT3580-TD7 ", init_ult3580_td7}, {"ULT3580-HH7 ", init_ult3580_td7}, {"ULT3580-TD8 ", init_ult3580_td8}, {"ULT3580-HH8 ", init_ult3580_td8}, {"ULT3580-TD9 ", init_ult3580_td9}, {"ULT3580-HH9 ", init_ult3580_td9}, {"ULTRIUM-TD1 ", init_ult3580_td1}, {"ULTRIUM-TD2 ", init_ult3580_td2}, {"ULTRIUM-HH2 ", init_ult3580_td2}, {"ULTRIUM-TD3 ", init_ult3580_td3}, {"ULTRIUM-HH3 ", init_ult3580_td3}, {"ULTRIUM-TD4 ", init_ult3580_td4}, {"ULTRIUM-HH4 ", init_ult3580_td4}, {"ULTRIUM-TD5 ", init_ult3580_td5}, {"ULTRIUM-HH5 ", init_ult3580_td5}, {"ULTRIUM-TD6 ", init_ult3580_td6}, {"ULTRIUM-HH6 ", init_ult3580_td6}, {"ULTRIUM-TD7 ", init_ult3580_td7}, {"ULTRIUM-HH7 ", init_ult3580_td7}, {"ULTRIUM-TD8 ", init_ult3580_td8}, {"ULTRIUM-HH8 ", init_ult3580_td8}, {"ULTRIUM-TD9 ", init_ult3580_td9}, {"ULTRIUM-HH9 ", init_ult3580_td9}, {"Ultrium 1-SCSI ", init_hp_ult_1}, {"Ultrium 2-SCSI ", init_hp_ult_2}, {"Ultrium 3-SCSI ", init_hp_ult_3}, {"Ultrium 4-SCSI ", init_hp_ult_4}, {"Ultrium 5-SCSI ", init_hp_ult_5}, {"Ultrium 6-SCSI ", init_hp_ult_6}, {"Ultrium 7-SCSI ", init_hp_ult_7}, {"Ultrium 8-SCSI ", init_hp_ult_8}, {"SDX-300C ", init_ait1_ssc}, {"SDX-500C ", init_ait2_ssc}, {"SDX-500V ", init_ait2_ssc}, {"SDX-700C ", init_ait3_ssc}, {"SDX-700V ", init_ait3_ssc}, {"SDX-900V ", init_ait4_ssc}, {"03592J1A ", init_3592_j1a}, {"03592E05 ", init_3592_E05}, {"03592E06 ", init_3592_E06}, {"03592E07 ", init_3592_E07}, {"T10000C ", init_t10kC_ssc}, {"T10000B ", init_t10kB_ssc}, {"T10000A ", init_t10kA_ssc}, {"T9840D ", init_9840D_ssc}, {"T9840C ", init_9840C_ssc}, {"T9840B ", init_9840B_ssc}, {"T9840A ", init_9840A_ssc}, {"T9940B ", init_9940B_ssc}, {"T9940A ", init_9940A_ssc}, {"DLT7000 ", init_dlt7000_ssc}, {"DLT8000 ", init_dlt8000_ssc}, {"SDLT 320 ", init_sdlt320_ssc}, {"SDLT600 ", init_sdlt600_ssc}, {NULL, NULL}, }; static void (*drive_init)(struct lu_phy_attr *) = init_default_ssc; static void usage(char *progname) { printf("Usage: %s [OPTIONS] -q \n", progname); printf("Where:\n"); printf(" '-q ' is the queue priority number\n"); printf("and where OPTIONS are from:\n"); printf(" '-d' enable debug mode -> Don't run as daemon\n"); printf(" 'v[N]' enable verbose syslog messages level N [1]\n"); printf(" '-f FIFO' use FIFO to report real-time data\n"); printf(" '-F' run in the foreground\n"); } static int lookup_media_int(struct name_to_media_info *media_info, char *s) { unsigned int i; MHVTL_DBG(2, "looking for media type %s", s); for (i = 0; media_info[i].media_density != 0; i++) if (!strcmp(media_info[i].name, s)) return media_info[i].media_type; return Media_undefined; } #ifdef MHVTL_DEBUG static const char *lookup_density_name( struct name_to_media_info *media_info, int den) { unsigned int i; MHVTL_DBG(2, "looking for density type 0x%02x", den); for (i = 0; media_info[i].media_density != 0; i++) if (media_info[i].media_density == den) return media_info[i].name; return "(UNKNOWN density)"; } #endif static const char *lookup_media_type(struct name_to_media_info *media_info, int med) { unsigned int i; MHVTL_DBG(2, "looking for media type 0x%02x", med); for (i = 0; media_info[i].media_density != 0; i++) if (media_info[i].media_type == med) return media_info[i].name; return "(UNKNOWN media type)"; } int lookup_mode_media_type(struct name_to_media_info *media_info, int med) { unsigned int i; MHVTL_DBG(2, "looking for mode media type for 0x%02x", med); for (i = 0; media_info[i].media_density != 0; i++) { MHVTL_DBG(3, "%s : 0x%02x mode media type 0x%02x", media_info[i].name, media_info[i].media_type, media_info[i].mode_media_type); if (media_info[i].media_type == med) return media_info[i].mode_media_type; } return media_type_unknown; } static void finish_mount(int sig) { MHVTL_DBG(3, "+++ Trace - Received signal %d +++", sig); if (get_tape_load_status() == TAPE_LOADING) set_tape_load_status(TAPE_LOADED); } static void set_mount_timer(int t) { MHVTL_DBG(3, "+++ Trace +++ Setting alarm for %d", t); signal(SIGALRM, finish_mount); alarm(t); } void delay_opcode(int what, int value) { MHVTL_DBG(3, "+++ Trace --> what: %d, value: %d", what, value); switch (what) { case DELAY_LOAD: if (value) set_mount_timer(value); else finish_mount(1); break; default: sleep(value); break; } MHVTL_DBG(2, "Completed %d, sleep(%d)", what, value); } /***********************************************************************/ /* * Report supported densities */ #define REPORT_DENSITY_LEN 52 int resp_report_density(struct priv_lu_ssc *lu_priv, uint8_t media, struct mhvtl_ds *dbuf_p) { uint8_t *buf = (uint8_t *)dbuf_p->data; struct list_head *l_head; struct density_info *di; struct supported_density_list *den; int count; uint32_t a; uint8_t *ds; /* Density Support Data Block Descriptor */ l_head = &lu_priv->pm->lu->den_list; /* Zero out buf */ ds = &buf[4]; count = 0; buf[2] = 0; /* Reserved */ buf[3] = 0; /* Reserved */ /* Assigning Oranization (8 chars long) */ if (media) { /* Report supported density by this media */ count = 1; ds[0] = mam.MediumDensityCode; ds[1] = mam.MediumDensityCode; ds[2] = (OK_to_write) ? 0xa0 : 0x20; /* Set write OK flg */ a = get_unaligned_be32(&mam.media_info.bits_per_mm); put_unaligned_be24(a, &ds[5]); a = get_unaligned_be32(&mam.MediumWidth); put_unaligned_be16(a, &ds[8]); a = get_unaligned_be16(&mam.media_info.tracks); put_unaligned_be16(a, &ds[10]); a = get_unaligned_be32(&mam.max_capacity); put_unaligned_be32(a, &ds[12]); snprintf((char *)&ds[16], 9, "%-8s", mam.AssigningOrganization_1); snprintf((char *)&ds[24], 9, "%-8s", mam.media_info.density_name); snprintf((char *)&ds[32], 21, "%-20.20s", mam.media_info.description); /* Fudge.. Now 'fix' up the spaces. */ for (a = 16; a < REPORT_DENSITY_LEN; a++) if (!ds[a]) ds[a] = 0x20; /* replace 0 with ' ' */ } else { /* Report supported density by this drive */ list_for_each_entry(den, l_head, siblings) { di = den->density_info; count++; MHVTL_DBG(2, "%s -> %s", di->description, (den->rw) ? "RW" : "RO"); ds[0] = di->density; ds[1] = di->density; ds[2] = (den->rw) ? 0xa0 : 0x20; /* Set write OK flg */ put_unaligned_be16(REPORT_DENSITY_LEN, &ds[3]); put_unaligned_be24(di->bits_per_mm, &ds[5]); put_unaligned_be16(di->media_width, &ds[8]); put_unaligned_be16(di->tracks, &ds[10]); put_unaligned_be32(di->capacity, &ds[12]); snprintf((char *)&ds[16], 9, "%-8s", di->assigning_org); snprintf((char *)&ds[24], 9, "%-8s", di->density_name); snprintf((char *)&ds[32], 21, "%-20s", di->description); /* Fudge.. Now 'fix' up the spaces. */ for (a = 16; a < REPORT_DENSITY_LEN; a++) if (!ds[a]) ds[a] = 0x20; /* replace 0 with ' ' */ ds += REPORT_DENSITY_LEN; } } put_unaligned_be16((REPORT_DENSITY_LEN * count) + 2, &buf[0]); return REPORT_DENSITY_LEN * count + 4; } /* * Read Attribute * * Fill in 'buf' with data and return number of bytes */ int resp_read_attribute(struct scsi_cmd *cmd) { uint8_t *cdb = cmd->scb; uint8_t *buf = cmd->dbuf_p->data; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint16_t attrib; uint32_t alloc_len; uint32_t ret_val = 4; /* Available data length */ unsigned int byte_index = 4; int indx, found_attribute; struct s_sd sd; attrib = get_unaligned_be16(&cdb[8]); alloc_len = get_unaligned_be32(&cdb[10]); MHVTL_DBG(2, "Attribute : 0x%04x, allocation len: %d", attrib, alloc_len); memset_ssc_buf(cmd, alloc_len); /* Clear memory */ if (cdb[1] == 0) { /* Attribute Values */ for (indx = found_attribute = 0; mam.attributes[indx].length; indx++) { if (attrib == mam.attributes[indx].attribute_id) found_attribute = 1; if (found_attribute) { /* calculate available data length */ ret_val += mam.attributes[indx].length + 5; if (ret_val <= alloc_len) { /* add it to output */ MHVTL_DBG(2, "Attribute : %02x %02x %02x %02x %02x %02x\n", buf[byte_index], buf[byte_index + 1], buf[byte_index + 2], buf[byte_index + 3], buf[byte_index + 4], buf[byte_index + 5]); buf[byte_index++] = mam.attributes[indx].attribute_id >> 8; buf[byte_index++] = mam.attributes[indx].attribute_id; buf[byte_index++] = (mam.attributes[indx].read_only << 7) | mam.attributes[indx].format; buf[byte_index++] = mam.attributes[indx].length >> 8; buf[byte_index++] = mam.attributes[indx].length; memcpy(&buf[byte_index], mam.attributes[indx].value, mam.attributes[indx].length); byte_index += mam.attributes[indx].length; } } } if (!found_attribute) { MHVTL_DBG(2, "Attribute not found"); sd.byte0 = SKSV | CD; sd.field_pointer = 8; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } } else { /* Attribute List */ for (indx = found_attribute = 0; mam.attributes[indx].length; indx++) { /* calculate available data length */ ret_val += 2; if (ret_val <= alloc_len) { /* add it to output */ buf[byte_index++] = mam.attributes[indx].attribute_id >> 8; buf[byte_index++] = mam.attributes[indx].attribute_id; } } } put_unaligned_be32(ret_val, &buf[0]); return ret_val; } /* * Process WRITE ATTRIBUTE scsi command * Returns 0 if OK * or 1 if MAM needs to be written. * or -1 on failure. */ int resp_write_attribute(struct scsi_cmd *cmd) { uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint8_t *cdb = cmd->scb; uint8_t *buf = (uint8_t *)cmd->dbuf_p->data; struct priv_lu_ssc *lu_priv = cmd->lu->lu_private; struct MAM *mamp = lu_priv->mamp; struct MAM mam_backup; uint32_t alloc_len; uint16_t attrib; uint16_t attribute_length; unsigned int byte_index = 4; int indx, found_attribute; struct s_sd sd; alloc_len = get_unaligned_be32(&cdb[10]); attrib = get_unaligned_be16(&buf[byte_index]); MHVTL_DBG(2, "Write Attribute: 0x%x, allocation len: %d", attrib, alloc_len); memcpy(&mam_backup, mamp, sizeof(struct MAM)); /* In case of error, keep former state of mam */ for (byte_index = 4; byte_index < alloc_len;) { attrib = get_unaligned_be16(&buf[byte_index]); for (indx = found_attribute = 0; mam.attributes[indx].length; indx++) { if (attrib == mam.attributes[indx].attribute_id) { found_attribute = 1; attribute_length = get_unaligned_be16(&buf[byte_index + 3]); byte_index += 5; /* positioning to the actual value */ if ((attrib == 0x408) && /* Attribute == Medium Type */ (attribute_length == 1) && (buf[byte_index] == 0x80)) { /* set media to worm */ MHVTL_LOG("Converted media to WORM"); mamp->MediumType = MEDIA_TYPE_WORM; } else { memcpy(mam.attributes[indx].value, &buf[byte_index], mam.attributes[indx].length); } byte_index += attribute_length; /* Positioning to the next attribute if any */ break; } else { found_attribute = 0; sd.field_pointer = indx; } } if (!found_attribute) { memcpy(&mamp, &mam_backup, sizeof(mamp)); sd.byte0 = SKSV; sam_illegal_request(E_INVALID_FIELD_IN_PARMS, &sd, sam_stat); return 0; } } return found_attribute; } /* * Space over (to) x filemarks. Setmarks not supported as yet. */ void resp_space(int64_t count, int code, uint8_t *sam_stat) { struct s_sd sd; switch (code) { /* Space 'count' blocks */ case 0: if (count >= 0) position_blocks_forw(count, sam_stat); else position_blocks_back(-count, sam_stat); break; /* Space 'count' filemarks */ case 1: if (count >= 0) position_filemarks_forw(count, sam_stat); else position_filemarks_back(-count, sam_stat); break; /* Space to end-of-data - Ignore 'count' */ case 3: position_to_eod(sam_stat); break; default: sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); break; } delay_opcode(DELAY_POSITION, lu_ssc.delay_position); } #ifdef MHVTL_DEBUG static char *sps_pg0 = "Tape Data Encyrption in Support page"; static char *sps_pg1 = "Tape Data Encyrption Out Support Page"; static char *sps_pg16 = "Data Encryption Capabilities page"; static char *sps_pg17 = "Supported key formats page"; static char *sps_pg18 = "Data Encryption management capabilities page"; static char *sps_pg32 = "Data Encryption Status page"; static char *sps_pg33 = "Next Block Encryption Status Page"; static char *sps_pg48 = "Random Number Page"; static char *sps_pg49 = "Device Server Key Wrapping Public Key page"; static char *sps_reserved = "Security Protcol Specific : reserved value"; static char *lookup_sp_specific(uint16_t field) { MHVTL_DBG(3, "Lookup %d", field); switch (field) { case 0: return sps_pg0; case 1: return sps_pg1; case 16: return sps_pg16; case 17: return sps_pg17; case 18: return sps_pg18; case 32: return sps_pg32; case 33: return sps_pg33; case 48: return sps_pg48; case 49: return sps_pg49; default: return sps_reserved; break; } } #endif #define SUPPORTED_SECURITY_PROTOCOL_LIST 0 #define CERTIFICATE_DATA 1 #define SECURITY_PROTOCOL_INFORMATION 0 #define TAPE_DATA_ENCRYPTION 0x20 /* FIXME: * Took this certificate from my Ubuntu install * /usr/share/doc/libssl-dev/demos/tunala/CA.pem * I wonder if RIAA is in NZ ? * * Need to insert a valid certificate of my own here... */ #include "vtltape_pem.h" /* * Returns number of bytes in struct */ static int resp_spin_page_0(uint8_t *buf, uint16_t sps, uint32_t alloc_len, uint8_t *sam_stat) { int ret = SAM_STAT_GOOD; struct s_sd sd; MHVTL_DBG(2, "%s", lookup_sp_specific(sps)); switch (sps) { case SUPPORTED_SECURITY_PROTOCOL_LIST: buf[6] = 0; /* list length (MSB) */ buf[7] = 2; /* list length (LSB) */ buf[8] = SECURITY_PROTOCOL_INFORMATION; buf[9] = TAPE_DATA_ENCRYPTION; ret = 10; break; case CERTIFICATE_DATA: strncpy((char *)&buf[4], certificate, alloc_len - 4); if (strlen(certificate) >= alloc_len - 4) { put_unaligned_be16(alloc_len - 4, &buf[2]); ret = alloc_len; } else { put_unaligned_be16(strlen(certificate), &buf[2]); ret = strlen(certificate) + 4; } break; default: sd.byte0 = SKSV | CD; sd.field_pointer = 2; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); ret = SAM_STAT_CHECK_CONDITION; } return ret; } /* * Return number of valid bytes in data structure */ static int resp_spin_page_20(struct scsi_cmd *cmd) { int ret = 0; int i, correct_key; unsigned int count; uint8_t *buf = (uint8_t *)cmd->dbuf_p->data; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint16_t sps = get_unaligned_be16(&cmd->scb[2]); struct priv_lu_ssc *lu_priv; lu_priv = (struct priv_lu_ssc *)cmd->lu->lu_private; struct s_sd sd; MHVTL_DBG(2, "%s", lookup_sp_specific(sps)); switch (sps) { case ENCR_IN_SUPPORT_PAGES: put_unaligned_be16(ENCR_IN_SUPPORT_PAGES, &buf[0]); put_unaligned_be16(14, &buf[2]); /* List length */ put_unaligned_be16(ENCR_IN_SUPPORT_PAGES, &buf[4]); put_unaligned_be16(ENCR_OUT_SUPPORT_PAGES, &buf[6]); put_unaligned_be16(ENCR_CAPABILITIES, &buf[8]); put_unaligned_be16(ENCR_KEY_FORMATS, &buf[10]); put_unaligned_be16(ENCR_KEY_MGT_CAPABILITIES, &buf[12]); put_unaligned_be16(ENCR_DATA_ENCR_STATUS, &buf[14]); put_unaligned_be16(ENCR_NEXT_BLK_ENCR_STATUS, &buf[16]); ret = 18; break; case ENCR_OUT_SUPPORT_PAGES: put_unaligned_be16(ENCR_OUT_SUPPORT_PAGES, &buf[0]); put_unaligned_be16(2, &buf[2]); /* List length */ put_unaligned_be16(ENCR_SET_DATA_ENCRYPTION, &buf[4]); ret = 6; break; case ENCR_CAPABILITIES: ret = lu_priv->pm->encryption_capabilities(cmd); break; case ENCR_KEY_FORMATS: put_unaligned_be16(ENCR_KEY_FORMATS, &buf[0]); put_unaligned_be16(2, &buf[2]); /* List length */ put_unaligned_be16(0, &buf[4]); /* Plain text */ ret = 6; break; case ENCR_KEY_MGT_CAPABILITIES: put_unaligned_be16(ENCR_KEY_MGT_CAPABILITIES, &buf[0]); put_unaligned_be16(0x0c, &buf[2]); /* List length */ buf[4] = 1; /* LOCK_C */ buf[5] = 7; /* CKOD_C, DKOPR_C, CKORL_C */ buf[6] = 0; /* Reserved */ buf[7] = 7; /* AITN_C, LOCAL_C, PUBLIC_C */ /* buf 8 - 15 reserved */ ret = 16; break; case ENCR_DATA_ENCR_STATUS: put_unaligned_be16(ENCR_DATA_ENCR_STATUS, &buf[0]); put_unaligned_be16(0x20, &buf[2]); /* List length */ buf[4] = 0x21; /* I_T Nexus scope and Key Scope */ buf[5] = lu_priv->ENCRYPT_MODE; buf[6] = lu_priv->DECRYPT_MODE; buf[7] = 0x01; /* Algorithm Index */ put_unaligned_be32(lu_priv->KEY_INSTANCE_COUNTER, &buf[8]); ret = 24; i = 24; if (UKAD_LENGTH) { buf[3] += 4 + UKAD_LENGTH; buf[i++] = 0x00; buf[i++] = 0x00; buf[i++] = 0x00; buf[i++] = UKAD_LENGTH; for (count = 0; count < UKAD_LENGTH; ++count) buf[i++] = UKAD[count]; ret += 4 + UKAD_LENGTH; } if (AKAD_LENGTH) { buf[3] += 4 + AKAD_LENGTH; buf[i++] = 0x01; buf[i++] = 0x00; buf[i++] = 0x00; buf[i++] = AKAD_LENGTH; for (count = 0; count < AKAD_LENGTH; ++count) buf[i++] = AKAD[count]; ret += 4 + AKAD_LENGTH; } break; case ENCR_NEXT_BLK_ENCR_STATUS: if (get_tape_load_status() != TAPE_LOADED) { sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); break; } /* c_pos contains the NEXT block's header info already */ put_unaligned_be16(ENCR_NEXT_BLK_ENCR_STATUS, &buf[0]); buf[2] = 0; /* List length (MSB) */ buf[3] = 12; /* List length (MSB) */ if (sizeof(loff_t) > 32) put_unaligned_be64(c_pos->blk_number, &buf[4]); else put_unaligned_be32(c_pos->blk_number, &buf[8]); if (c_pos->blk_type != B_DATA) buf[12] = 0x2; /* not a logical block */ else buf[12] = 0x3; /* not encrypted */ buf[13] = 0x01; /* Algorithm Index */ ret = 16; if (c_pos->blk_flags & BLKHDR_FLG_ENCRYPTED) { correct_key = TRUE; i = 16; if (c_pos->blk_encryption_info.ukad_length) { buf[3] += 4 + c_pos->blk_encryption_info.ukad_length; buf[i++] = 0x00; buf[i++] = 0x01; buf[i++] = 0x00; buf[i++] = c_pos->blk_encryption_info.ukad_length; for (count = 0; count < c_pos->blk_encryption_info.ukad_length; ++count) buf[i++] = c_pos->blk_encryption_info.ukad[count]; ret += 4 + c_pos->blk_encryption_info.ukad_length; } if (c_pos->blk_encryption_info.akad_length) { buf[3] += 4 + c_pos->blk_encryption_info.akad_length; buf[i++] = 0x01; buf[i++] = 0x03; buf[i++] = 0x00; buf[i++] = c_pos->blk_encryption_info.akad_length; for (count = 0; count < c_pos->blk_encryption_info.akad_length; ++count) buf[i++] = c_pos->blk_encryption_info.akad[count]; ret += 4 + c_pos->blk_encryption_info.akad_length; } /* compare the keys */ if (correct_key) { if (c_pos->blk_encryption_info.key_length != KEY_LENGTH) correct_key = FALSE; for (count = 0; count < c_pos->blk_encryption_info.key_length; ++count) { if (c_pos->blk_encryption_info.key[count] != KEY[count]) { correct_key = FALSE; break; } } } if (correct_key) buf[12] = 0x5; /* encrypted, correct key */ else buf[12] = 0x6; /* encrypted, need key */ } break; default: sd.byte0 = SKSV | CD; sd.field_pointer = 2; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); } return ret; } /* * Retrieve Security Protocol Information */ uint8_t resp_spin(struct scsi_cmd *cmd) { uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint8_t *buf = (uint8_t *)cmd->dbuf_p->data; uint8_t *cdb = cmd->scb; uint16_t sps = get_unaligned_be16(&cmd->scb[2]); uint32_t alloc_len = get_unaligned_be32(&cdb[6]); uint8_t inc_512 = (cdb[4] & 0x80) ? 1 : 0; struct priv_lu_ssc *lu_priv; struct s_sd sd; lu_priv = (struct priv_lu_ssc *)cmd->lu->lu_private; cmd->dbuf_p->sz = 0; if (inc_512) alloc_len = alloc_len * 512; if (alloc_len > lu_priv->bufsize) { MHVTL_LOG("buffer too large - aborting"); sd.byte0 = SKSV | CD; sd.field_pointer = 6; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } memset_ssc_buf(cmd, alloc_len); switch (cdb[1]) { case SECURITY_PROTOCOL_INFORMATION: cmd->dbuf_p->sz = resp_spin_page_0(buf, sps, alloc_len, sam_stat); break; case TAPE_DATA_ENCRYPTION: cmd->dbuf_p->sz = resp_spin_page_20(cmd); break; default: MHVTL_DBG(1, "Security protocol 0x%04x unknown", cdb[1]); sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); } return *sam_stat; } uint8_t resp_spout(struct scsi_cmd *cmd) { uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint8_t *buf = (uint8_t *)cmd->dbuf_p->data; struct lu_phy_attr *lu; struct priv_lu_ssc *lu_priv; unsigned int count; struct s_sd sd; #ifdef MHVTL_DEBUG uint16_t sps = get_unaligned_be16(&cmd->scb[2]); uint8_t inc_512 = (cmd->scb[4] & 0x80) ? 1 : 0; #endif lu = cmd->lu; lu_priv = (struct priv_lu_ssc *)cmd->lu->lu_private; if (cmd->scb[1] != TAPE_DATA_ENCRYPTION) { MHVTL_DBG(1, "Security protocol 0x%02x unknown", cmd->scb[1]); sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } MHVTL_DBG(2, "Tape Data Encryption, %s, " " alloc len: 0x%02x, inc_512: %s", lookup_sp_specific(sps), cmd->dbuf_p->sz, (inc_512) ? "Set" : "Unset"); /* check for a legal "set data encryption page" */ if ((buf[0] != 0x00) || (buf[1] != 0x10) || (buf[2] != 0x00) || (buf[3] < 16) || (buf[8] != 0x01) || (buf[9] != 0x00)) { sd.byte0 = SKSV; /* Make sure the 'byte closest to [0]' is the one reported */ if (buf[9]) sd.field_pointer = 9; if (buf[8] != 1) sd.field_pointer = 8; if (buf[3] < 16) sd.field_pointer = 3; if (buf[2]) sd.field_pointer = 2; if (buf[1] != 0x10) sd.field_pointer = 1; if (buf[0]) sd.field_pointer = 0; sam_illegal_request(E_INVALID_FIELD_IN_PARMS, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } lu_ssc.KEY_INSTANCE_COUNTER++; lu_ssc.ENCRYPT_MODE = buf[6]; lu_ssc.DECRYPT_MODE = buf[7]; UKAD_LENGTH = 0; AKAD_LENGTH = 0; KEY_LENGTH = get_unaligned_be16(&buf[18]); for (count = 0; count < KEY_LENGTH; ++count) { KEY[count] = buf[20 + count]; } MHVTL_DBG(2, "Encrypt mode: %d Decrypt mode: %d, " "ukad len: %d akad len: %d", lu_ssc.ENCRYPT_MODE, lu_ssc.DECRYPT_MODE, UKAD_LENGTH, AKAD_LENGTH); if (cmd->dbuf_p->sz > (19 + KEY_LENGTH + 4)) { if (buf[20 + KEY_LENGTH] == 0x00) { MHVTL_DBG(2, "Unauthenticated Key Associated Data (UKAD) provided"); UKAD_LENGTH = get_unaligned_be16(&buf[22 + KEY_LENGTH]); for (count = 0; count < UKAD_LENGTH; ++count) { UKAD[count] = buf[24 + KEY_LENGTH + count]; } } else if (buf[20 + KEY_LENGTH] == 0x01) { MHVTL_DBG(2, "Authenticated Key Associated Data (AKAD) provided"); AKAD_LENGTH = get_unaligned_be16(&buf[22 + KEY_LENGTH]); for (count = 0; count < AKAD_LENGTH; ++count) { AKAD[count] = buf[24 + KEY_LENGTH + count]; } } } count = lu_priv->pm->kad_validation(lu_ssc.ENCRYPT_MODE, UKAD_LENGTH, AKAD_LENGTH); /* For some reason, this command needs to be failed */ if (count) { lu_ssc.KEY_INSTANCE_COUNTER--; lu_ssc.ENCRYPT_MODE = 0; lu_ssc.DECRYPT_MODE = buf[7]; UKAD_LENGTH = 0; AKAD_LENGTH = 0; KEY_LENGTH = 0; sam_illegal_request(E_INVALID_FIELD_IN_CDB, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (lu_priv->pm->update_encryption_mode) lu_priv->pm->update_encryption_mode(&lu->mode_pg, NULL, lu_ssc.ENCRYPT_MODE); return SAM_STAT_GOOD; } /* * Update MAM contents with current counters */ static void updateMAM(uint8_t *sam_stat, int load) { uint64_t bw; /* Bytes Written */ uint64_t br; /* Bytes Read */ uint64_t load_count; /* load count */ MHVTL_DBG(2, "updateMAM(%s)", (load) ? "load" : "unload"); /* Update on load */ if (load) { mam.record_dirty = 1; load_count = get_unaligned_be64(&mam.LoadCount); load_count++; put_unaligned_be64(load_count, &mam.LoadCount); memcpy(&mam.DevMakeSerialLastLoad3, &mam.DevMakeSerialLastLoad2, 40); memcpy(&mam.DevMakeSerialLastLoad2, &mam.DevMakeSerialLastLoad1, 40); memcpy(&mam.DevMakeSerialLastLoad1, &mam.DevMakeSerialLastLoad, 40); /* Initialise with ' ' space char */ memset(&mam.DevMakeSerialLastLoad, 0x20, 40); memcpy(&mam.DevMakeSerialLastLoad, &lunit.vendor_id, VENDOR_ID_LEN); memcpy(&mam.DevMakeSerialLastLoad[8], &lunit.lu_serial_no, SCSI_SN_LEN); } else { /* Update on unload */ mam.record_dirty = 0; /* Update bytes written this load. */ put_unaligned_be64(lu_ssc.bytesWritten_I, &mam.WrittenInLastLoad); put_unaligned_be64(lu_ssc.bytesRead_I, &mam.ReadInLastLoad); /* Update total bytes read/written */ bw = get_unaligned_be64(&mam.WrittenInMediumLife); bw += lu_ssc.bytesWritten_I; put_unaligned_be64(bw, &mam.WrittenInMediumLife); br = get_unaligned_be64(&mam.ReadInMediumLife); br += lu_ssc.bytesRead_I; put_unaligned_be64(br, &mam.ReadInMediumLife); } rewriteMAM(sam_stat); } /* * * Process the SCSI command * * Called with: * cdev -> Char dev file handle, * cdb -> SCSI Command buffer pointer, * dbuf -> struct mhvtl_ds * */ static void processCommand(int cdev, uint8_t *cdb, struct mhvtl_ds *dbuf_p, useconds_t pollInterval) { static int last_count; static uint64_t tot_delay; int err = 0; struct scsi_cmd _cmd; struct scsi_cmd *cmd; cmd = &_cmd; cmd->scb = cdb; cmd->scb_len = 16; /* fixme */ cmd->dbuf_p = dbuf_p; cmd->lu = &lunit; cmd->cdev = cdev; cmd->pollInterval = pollInterval; if ((cdb[0] == READ_6 || cdb[0] == WRITE_6) && cdb[0] == last_cmd) { MHVTL_DBG_PRT_CDB(2, cmd); tot_delay += cmd->pollInterval; if ((++last_count % 50) == 0) { MHVTL_DBG(1, "%dth contiguous %s request (%ld) " "(delay %" PRId64 ")", last_count, last_cmd == READ_6 ? "READ_6" : "WRITE_6", (long)dbuf_p->serialNo, tot_delay); tot_delay = 0; } } else { MHVTL_DBG_PRT_CDB(1, cmd); last_count = 0; tot_delay = 0; } /* Limited subset of commands don't need to check for power-on reset */ switch (cdb[0]) { case INQUIRY: /* Inquiry does not need power-on/reset, however the inquiry data may have changed */ if (check_inquiry_data_has_changed(&dbuf_p->sam_stat)) return; case REPORT_LUNS: case REQUEST_SENSE: case MODE_SELECT: dbuf_p->sam_stat = SAM_STAT_GOOD; break; default: if (check_reset(&dbuf_p->sam_stat)) return; } /* Skip main op code processing if pre-cmd returns non-zero */ if (cmd->lu->scsi_ops->ops[cdb[0]].pre_cmd_perform) err = cmd->lu->scsi_ops->ops[cdb[0]].pre_cmd_perform(cmd, NULL); if (!err) dbuf_p->sam_stat = cmd->lu->scsi_ops->ops[cdb[0]].cmd_perform(cmd); /* Post op code processing regardless */ if (cmd->lu->scsi_ops->ops[cdb[0]].post_cmd_perform) cmd->lu->scsi_ops->ops[cdb[0]].post_cmd_perform(cmd, NULL); last_cmd = cdb[0]; return; } static struct media_details *check_media_can_load(struct list_head *mdl, int mt) { struct media_details *m_detail; MHVTL_DBG(2, "Looking for media_type: 0x%02x", mt); list_for_each_entry(m_detail, mdl, siblings) { MHVTL_DBG(3, "testing against m_detail->media_type (0x%02x)", m_detail->media_type); if (m_detail->media_type == (unsigned int)mt) return m_detail; } return NULL; } /* * Attempt to load PCL - i.e. Open datafile and read in BOT header & MAM * * Returns: * == 0 -> Load OK * == 1 -> Tape already loaded. * == 2 -> format corrupt. * == 3 -> cartridge does not exist or cannot be opened. */ int loadTape(char *PCL, uint8_t *sam_stat) { int rc; uint64_t fg = TA_NONE; /* TapeAlert flags */ int overflow; struct media_details *m_detail; struct lu_phy_attr *lu; lu_ssc.bytesWritten_I = 0; /* Global - Bytes written this load */ lu_ssc.bytesWritten_M = 0; /* Global - Bytes written this load */ lu_ssc.bytesRead_I = 0; /* Global - Bytes read this load */ lu_ssc.bytesRead_M = 0; /* Global - Bytes read this load */ lu = lu_ssc.pm->lu; rc = load_tape(PCL, sam_stat); if (rc) { MHVTL_ERR("Media load failed.. Unsupported format"); lu_ssc.mediaSerialNo[0] = '\0'; if (rc == 2) { /* TapeAlert - Unsupported format */ fg = TA_MEDIA_NOT_SUPPORTED; update_TapeAlert(fg); } MHVTL_LOG("Tape Load (%s) failed with status: %d", PCL, rc); return rc; } set_tape_load_status(TAPE_LOADING); lu_ssc.pm->media_load(lu, TAPE_LOADED); overflow = snprintf((char *)lu_ssc.mediaSerialNo, sizeof(mam.MediumSerialNumber) - 1, "%s", (char *)mam.MediumSerialNumber); if (overflow >= sizeof(mam.MediumSerialNumber) - 1) { MHVTL_ERR("MAM medium serial number truncated to %s", mam.MediumSerialNumber); } MHVTL_DBG(1, "Media type '%s' loaded with S/No. : %s", lookup_media_type(lu_ssc.pm->media_handling, mam.MediaType), mam.MediumSerialNumber); rewind_tape(sam_stat); lu_ssc.max_capacity = get_unaligned_be64(&mam.max_capacity); switch (mam.MediumType) { case MEDIA_TYPE_DATA: set_current_state(MHVTL_STATE_LOADING); OK_to_write = 1; /* Reset flag to OK. */ if (lu_ssc.pm->clear_WORM) lu_ssc.pm->clear_WORM(&lu->mode_pg); sam_unit_attention(E_NOT_READY_TO_TRANSITION, sam_stat); break; case MEDIA_TYPE_CLEAN: set_current_state(MHVTL_STATE_LOADING_CLEAN); OK_to_write = 0; if (lu_ssc.pm->clear_WORM) lu_ssc.pm->clear_WORM(&lu->mode_pg); if (lu_ssc.pm->cleaning_media) lu_ssc.pm->cleaning_media(&lu_ssc); fg |= TA_CLEANING_MEDIA; MHVTL_DBG(1, "Cleaning media loaded"); sam_unit_attention(E_CLEANING_CART_INSTALLED, sam_stat); break; case MEDIA_TYPE_WORM: set_current_state(MHVTL_STATE_LOADING_WORM); /* Special condition... * If we * - rewind, * - write filemark * - EOD * We set this as writable media as the tape is blank. */ if (!lu_ssc.pm->set_WORM) { /* PM doesn't support WORM */ MHVTL_DBG(1, "load failed - WORM media," " but drive doesn't support WORM"); goto mismatchmedia; } if (c_pos->blk_type == B_EOD) { OK_to_write = 1; } else if (c_pos->blk_type != B_FILEMARK) { OK_to_write = 0; /* Check that this header is a filemark and * the next header is End of Data. * If it is, we are OK to write */ } else if (position_to_block(1, sam_stat)) { OK_to_write = 0; } else { if (c_pos->blk_type == B_EOD) OK_to_write = 1; else OK_to_write = 0; rewind_tape(sam_stat); } lu_ssc.pm->set_WORM(&lu->mode_pg); MHVTL_DBG(1, "Write Once Read Many (WORM) media loaded"); break; case MEDIA_TYPE_NULL: /* Special - don't save data, just metadata */ set_current_state(MHVTL_STATE_LOADING); OK_to_write = 1; /* Reset flag to OK. */ sam_unit_attention(E_NOT_READY_TO_TRANSITION, sam_stat); break; } /* Set TapeAlert flg 32h => */ /* Lost Statics */ if (mam.record_dirty != 0) { fg = TA_LOST_STATISTICS; MHVTL_DBG(1, "Previous unload was not clean"); } if (lu_ssc.max_capacity) { lu_ssc.early_warning_position = lu_ssc.max_capacity - lu_ssc.early_warning_sz; lu_ssc.prog_early_warning_position = lu_ssc.early_warning_position - lu_ssc.prog_early_warning_sz; } if (lu_ssc.pm->drive_supports_early_warning) { if (lu_ssc.pm->drive_supports_prog_early_warning) { MHVTL_DBG(2, "Tape capacity: %" PRId64 " + Early Warning %" PRId64 " + Prog Early Warning %" PRId64, lu_ssc.max_capacity, lu_ssc.early_warning_sz, lu_ssc.prog_early_warning_sz); } else { MHVTL_DBG(2, "Tape capacity: %" PRId64 " + Early Warning %" PRId64, lu_ssc.max_capacity, lu_ssc.early_warning_sz); } } else { MHVTL_DBG(2, "Tape capacity: %" PRId64, lu_ssc.max_capacity); } /* Increment load count */ updateMAM(sam_stat, 1); m_detail = check_media_can_load(&lu_ssc.supported_media_list, mam.MediaType); if (!m_detail) { /* Media not defined.. Reject */ MHVTL_DBG(3, "Undefined Media rejected"); goto mismatchmedia; } MHVTL_DBG(2, "Load Capability: 0x%02x", m_detail->load_capability); /* Now check for WORM support */ switch (mam.MediumType) { case MEDIA_TYPE_WORM: /* If media is WORM, check drive will allow mount */ if (m_detail->load_capability & (LOAD_WORM | LOAD_RW)) { /* Prev check will correctly set OK_to_write flag */ MHVTL_DBG(2, "Allow LOAD as R/W WORM"); } else if (m_detail->load_capability & (LOAD_WORM | LOAD_RO)) { MHVTL_DBG(2, "Allow LOAD as R/O WORM"); OK_to_write = 0; } else { MHVTL_ERR("Load failed: Unable to load as WORM"); goto mismatchmedia; } break; case MEDIA_TYPE_DATA: /* Allow media to be either RO or RW */ if (m_detail->load_capability & LOAD_RO) { MHVTL_DBG(2, "Mounting READ ONLY"); lu_ssc.MediaWriteProtect = MEDIA_READONLY; OK_to_write = 0; } else if (m_detail->load_capability & LOAD_RW) { if (mam.Flags & MAM_FLAGS_MEDIA_WRITE_PROTECT) { set_lp_11_wp(1); /* Update WriteProtect bit in LogPage 0x11 */ MHVTL_DBG(2, "Mounting READ ONLY - WP set"); lu_ssc.MediaWriteProtect = MEDIA_READONLY; OK_to_write = 0; } else { MHVTL_DBG(2, "Mounting READ/WRITE"); set_lp_11_wp(0); /* Update WriteProtect bit in LogPage 0x11 */ lu_ssc.MediaWriteProtect = MEDIA_WRITABLE; OK_to_write = 1; } } else if (m_detail->load_capability & LOAD_FAIL) { MHVTL_ERR("Load failed: Data format not suitable for " "read/write or read-only"); goto mismatchmedia; } break; case MEDIA_TYPE_NULL: break; default: /* Can't write to cleaning media */ OK_to_write = 0; break; } /* Update TapeAlert flags */ update_TapeAlert(fg); MHVTL_DBG(1, "Media is%s writable", (OK_to_write) ? "" : " not"); modeBlockDescriptor[0] = mam.MediumDensityCode; MHVTL_DBG(1, "Setting MediumDensityCode to %s (0x%02x)" " Media type: 0x%02x", lookup_density_name(lu_ssc.pm->media_handling, mam.MediumDensityCode), mam.MediumDensityCode, (uint8_t)lu->mode_media_type); delay_opcode(DELAY_LOAD, lu_ssc.delay_load); set_current_state(MHVTL_STATE_LOADED); return 0; /* Return successful load */ mismatchmedia: unload_tape(sam_stat); fg |= TA_MEDIA_NOT_SUPPORTED; /* Unsupported format */ update_TapeAlert(fg); MHVTL_ERR("Tape %s failed to load with type '%s' in drive type '%s'", PCL, lookup_media_type(lu_ssc.pm->media_handling, mam.MediaType), lu_ssc.pm->name); set_tape_load_status(TAPE_UNLOADED); lu_ssc.pm->media_load(lu, TAPE_UNLOADED); delay_opcode(DELAY_LOAD, lu_ssc.delay_load); set_current_state(MHVTL_STATE_LOAD_FAILED); return 1; } static void dump_linked_list(void) { struct media_details *m_detail; struct list_head *mdl; MHVTL_DBG(3, "Dumping media type support"); mdl = &lu_ssc.supported_media_list; list_for_each_entry(m_detail, mdl, siblings) { MHVTL_DBG(3, "Media type: 0x%02x, status: 0x%02x", m_detail->media_type, m_detail->load_capability); } } /* Strip (recover) the 'Physical Cartridge Label' * Well at least the data filename which relates to the same thing */ static char *strip_PCL(char *str, int start) { char *q; char *p; int a; p = str + start; /* p += 'start' (skip over 'load' string) * Then keep going until '*p' is a space or NULL */ for (a = 0, q = p; a < strlen(str) - start; a++, q++) { if (isspace(*q) || *q == '\0') { *q = '\0'; break; } } MHVTL_DBG(3, "Returning: \"%s\"", p); return p; } void unloadTape(int update_library, uint8_t *sam_stat) { struct lu_phy_attr *lu = lu_ssc.pm->lu; switch (get_tape_load_status()) { case TAPE_LOADING: case TAPE_LOADED: /* Don't update load count on unload -done at load time */ updateMAM(sam_stat, 0); unload_tape(sam_stat); if (lu_ssc.pm->clear_WORM) lu_ssc.pm->clear_WORM(&lu->mode_pg); if (lu_ssc.cleaning_media_state) lu_ssc.cleaning_media_state = NULL; lu_ssc.pm->media_load(lu, TAPE_UNLOADED); delay_opcode(DELAY_UNLOAD, lu_ssc.delay_unload); break; default: MHVTL_DBG(2, "Tape not mounted"); break; } if (update_library && lu_ssc.inLibrary && library_id > 0) { SEND_MSG_AND_LOG(msg_eject, (uint64_t)library_id); } set_tape_load_status(TAPE_UNLOADED); OK_to_write = 0; } static int processMessageQ(struct q_msg *msg, uint8_t *sam_stat) { char *pcl; char s[128]; char *z; int rc; struct lu_phy_attr *lu; int pcl_len; uint64_t fg = TA_NONE; /* TapeAlert flags */ lu = lu_ssc.pm->lu; MHVTL_DBG(1, "%ld: Received message \"%s\" from snd_id %ld", my_id, msg->text, msg->snd_id); /* Tape Load message from Library */ if (!strncmp(msg->text, "lload", 5)) { if (!lu_ssc.inLibrary) { MHVTL_DBG(2, "lload & drive not in library"); return 0; } if (lu_ssc.barcode) { MHVTL_ERR("%ld: snd_id %ld: Tape \"%s\" already in mouth of drive", my_id, msg->snd_id, lu_ssc.barcode); snprintf(s, sizeof(s), "Load failed - %s is already in mouth of drive", lu_ssc.barcode); } else { /* 'lload ' => offset of 6 */ pcl = strip_PCL(msg->text, 6); pcl_len = strlen(pcl) + 2; lu_ssc.barcode = malloc(pcl_len); if (!lu_ssc.barcode) { MHVTL_ERR("Ugghhh... out of memory allocating buffer for barcode: %s", pcl); snprintf(s, sizeof(s), "%s: %s", msg_load_failed, pcl); } else { rc = loadTape(pcl, sam_stat); strncpy(lu_ssc.barcode, pcl, pcl_len); snprintf(s, sizeof(s), "%s: %s", (get_tape_load_status() == TAPE_UNLOADED) ? msg_load_failed : msg_load_ok, pcl); /* If load fails - clean up 'barcode' */ if (rc) { MHVTL_LOG("Mount of %s failed, return code: %d", pcl, rc); free(lu_ssc.barcode); lu_ssc.barcode = NULL; if (rc == 2) { /* TapeAlert - Unsupported format */ fg = TA_MEDIA_NOT_SUPPORTED; update_TapeAlert(fg); } } } } SEND_MSG_AND_LOG(s, msg->snd_id); if (lu_ssc.barcode) set_lp11_medium_present(1); else set_lp11_medium_present(0); } /* Tape Load message from User space */ if (!strncmp(msg->text, "load", 4)) { pcl = strip_PCL(msg->text, 5); pcl_len = strlen(pcl) + 2; if (lu_ssc.inLibrary) MHVTL_ERR("Warn: Tape assigned to library - The library can't remove this tape !"); if (get_tape_load_status() == TAPE_LOADED) { MHVTL_DBG(2, "A tape is already mounted"); } else { loadTape(pcl, sam_stat); } /* Prevent a manual load, and the library moving another in it's place * Feature: There is no logic to remove the tape from 'mouth' of drive * A restart of the daemon will be required.. */ if (lu_ssc.barcode) { free(lu_ssc.barcode); } lu_ssc.barcode = malloc(pcl_len); if (lu_ssc.barcode) { strncpy(lu_ssc.barcode, pcl, pcl_len); set_lp11_medium_present(1); } else { MHVTL_ERR("Ugghhh... out of memory allocating buffer for barcode: %s", pcl); } } /* This needs to be called if an 'mt -f /dev/st* offline' rather than an 'unload' from library daemon */ if (!strncmp(msg->text, msg_set_empty, strlen(msg_set_empty))) { /* string define in q.h */ unloadTape(FALSE, sam_stat); /* Unload - in case something is loaded */ free(lu_ssc.barcode); lu_ssc.barcode = NULL; SEND_MSG_AND_LOG(msg_unload_ok, msg->snd_id); set_lp11_medium_present(0); } if (!strncmp(msg->text, msg_mount_state, strlen(msg_mount_state))) { /* string define in q.h */ sprintf(s, "%s", (lu_ssc.barcode) ? msg_occupied : msg_not_occupied); SEND_MSG_AND_LOG(s, msg->snd_id); } if (!strncmp(msg->text, "unload", 6)) { unloadTape(FALSE, sam_stat); SEND_MSG_AND_LOG(msg_unload_ok, msg->snd_id); free(lu_ssc.barcode); lu_ssc.barcode = NULL; set_lp11_medium_present(0); } if (!strncmp(msg->text, "exit", 4)) return 1; if (!strncmp(msg->text, "Register", 8)) { lu_ssc.inLibrary = 1; MHVTL_DBG(1, "Notice from Library controller : %s", msg->text); /* find_media_home_directory(NULL, library_id); */ } if (!strncmp(msg->text, "verbose", 7)) { if (verbose) verbose--; else verbose = 3; MHVTL_LOG("Verbose: %s at level %d", verbose ? "enabled" : "disabled", verbose); } if (!strncmp(msg->text, "InquiryDataChange", 17)) set_inquiry_data_changed(); if (!strncmp(msg->text, "TapeAlert", 9)) { uint64_t flg = TA_NONE; sscanf(msg->text, "TapeAlert %" PRIx64, &flg); update_TapeAlert(flg); } if (!strncmp(msg->text, "compression", 11)) { sscanf(msg->text, "compression %s", &s[0]); if (!strncasecmp(s, "lzo", 3)) lu_ssc.compressionType = LZO; if (!strncasecmp(s, "zlib", 4)) lu_ssc.compressionType = ZLIB; MHVTL_DBG(1, "Compression set to %s", (lu_ssc.compressionType == LZO) ? "LZO" : "ZLIB"); } if (!strncasecmp(msg->text, "append", 6)) { s[0] = '\0'; sscanf(msg->text, "Append Only %s", &s[0]); if (strlen(s) < 2) sscanf(msg->text, "APPEND ONLY %s", &s[0]); if (strlen(s) < 2) sscanf(msg->text, "append only %s", &s[0]); if (lu_ssc.pm->drive_supports_append_only_mode) { struct mode *m; m = lookup_mode_pg(&lu->mode_pg, 0x10, 1); if (!m) { MHVTL_LOG("Can't find Append Only mode page" ", Drive should support Append Only"); return 0; } if (!strncasecmp(s, "Yes", 3)) { m->pcodePointer[5] |= 0x10; lu_ssc.append_only_mode = 1; MHVTL_DBG(1, "Append Only set to \"Yes\""); } else if (!strncasecmp(s, "No", 2)) { m->pcodePointer[5] &= 0x0f; lu_ssc.append_only_mode = 0; MHVTL_DBG(1, "Append Only set to \"No\""); } else { MHVTL_LOG("Append Only value: %s unknown," " Leaving unchanged at: %s", s, (m->pcodePointer[5] & 0xf0) ? "Yes" : "No"); } } else MHVTL_LOG("This drive does not support Append Only mode"); } if (!strncasecmp(msg->text, "delay load", 10)) { z = strtok(msg->text, " "); z = strtok(NULL, " "); z = strtok(NULL, " "); if (atoi(z) > 0) lu_ssc.delay_load = min(atoi(z), MAX_DELAY_LOAD); else lu_ssc.delay_load = 0; MHVTL_DBG(2, "Setting delay_load to %d", lu_ssc.delay_load); } if (!strncasecmp(msg->text, "delay unload", 12)) { z = strtok(msg->text, " "); z = strtok(NULL, " "); z = strtok(NULL, " "); if (atoi(z) > 0) lu_ssc.delay_unload = min(atoi(z), MAX_DELAY_UNLOAD); else lu_ssc.delay_unload = 0; MHVTL_DBG(2, "Setting delay_unload to %d", lu_ssc.delay_unload); } if (!strncasecmp(msg->text, "delay rewind", 12)) { z = strtok(msg->text, " "); z = strtok(NULL, " "); z = strtok(NULL, " "); if (atoi(z) > 0) lu_ssc.delay_rewind = min(atoi(z), MAX_DELAY_REWIND); else lu_ssc.delay_rewind = 0; MHVTL_DBG(2, "Setting delay_rewind to %d", lu_ssc.delay_rewind); } if (!strncasecmp(msg->text, "delay thread", 12)) { z = strtok(msg->text, " "); z = strtok(NULL, " "); z = strtok(NULL, " "); if (atoi(z) > 0) lu_ssc.delay_thread = min(atoi(z), MAX_DELAY_THREAD); else lu_ssc.delay_thread = 0; MHVTL_DBG(2, "Setting delay_thread to %d", lu_ssc.delay_thread); } if (!strncasecmp(msg->text, "delay position", 14)) { z = strtok(msg->text, " "); z = strtok(NULL, " "); z = strtok(NULL, " "); if (atoi(z) > 0) lu_ssc.delay_position = min(atoi(z), MAX_DELAY_POSITION); else lu_ssc.delay_position = 0; MHVTL_DBG(2, "Setting delay_position to %d", lu_ssc.delay_position); } if (!strncmp(msg->text, "debug", 5)) { if (debug > 4) { debug = 1; } else if (debug > 1) { debug++; } else { debug++; verbose = 4; } printf("Debug: %d\n", debug); } if (!strncmp(msg->text, "dump", 4)) dump_linked_list(); return 0; } /* * A place to setup any customisations (WORM / Security handling) */ static void config_lu(struct lu_phy_attr *lu) { int i; for (i = 0; tape_drives[i].name; i++) { if (!strncmp(tape_drives[i].name, lu->product_id, max(strlen(tape_drives[i].name), strlen(lu->product_id)))) { drive_init = tape_drives[i].init; break; } } lu_ssc.early_warning_sz = EARLY_WARNING_SZ; lu_ssc.prog_early_warning_sz = 0; drive_init(lu); if (lu_ssc.configCompressionEnabled) { lu_ssc.pm->set_compression(&lu->mode_pg, lu_ssc.configCompressionFactor); set_lp11_compression(1); } else { lu_ssc.pm->clear_compression(&lu->mode_pg); set_lp11_compression(0); } MHVTL_DBG(1, "%s: supports WORM : %s", lu_ssc.pm->name, lu_ssc.pm->drive_supports_WORM ? "Yes" : "No"); MHVTL_DBG(1, "%s: supports append-only mode : %s", lu_ssc.pm->name, lu_ssc.pm->drive_supports_append_only_mode ? "Yes" : "No"); MHVTL_DBG(1, "%s: supports Security Protocol : %s", lu_ssc.pm->name, lu_ssc.pm->drive_supports_SP ? "Yes" : "No"); MHVTL_DBG(1, "%s: supports early warning mode : %s", lu_ssc.pm->name, lu_ssc.pm->drive_supports_early_warning ? "Yes" : "No"); MHVTL_DBG(1, "%s: supports prog early warning mode : %s", lu_ssc.pm->name, lu_ssc.pm->drive_supports_prog_early_warning ? "Yes" : "No"); MHVTL_DBG(1, "%s: supports SCSI Persistent Reservation : %s", lu_ssc.pm->name, lu_ssc.pm->drive_supports_SPR ? "Yes" : "No"); MHVTL_DBG(1, "%s: supports Data Integrity Validation (Logical Block Protection) : %s", lu_ssc.pm->name, lu_ssc.pm->drive_supports_LBP == 0 ? "No" : lu_ssc.pm->drive_supports_LBP == 1 ? "RS-CRC only" : lu_ssc.pm->drive_supports_LBP == 2 ? "RS-CRC and CRC32C" : "Invalid"); set_timestamp(0, 0); /* timestamp source - num mS from initialisation */ } static void cleanup_drive_media_list(struct lu_phy_attr *lu) { struct priv_lu_ssc *lu_tape; struct media_details *mdp, *ndp; struct list_head *den_list; lu_tape = (struct priv_lu_ssc *)lu->lu_private; den_list = &lu_tape->supported_media_list; list_for_each_entry_safe(mdp, ndp, den_list, siblings) { list_del(&mdp->siblings); free(mdp); } } int add_drive_media_list(struct lu_phy_attr *lu, int status, char *s) { struct priv_lu_ssc *lu_tape; struct media_details *m_detail; struct list_head *den_list; int media_type; lu_tape = (struct priv_lu_ssc *)lu->lu_private; den_list = &lu_tape->supported_media_list; MHVTL_DBG(2, "Adding %s, status: 0x%02x", s, status); media_type = lookup_media_int(lu_tape->pm->media_handling, s); m_detail = check_media_can_load(den_list, media_type); if (m_detail) { MHVTL_DBG(2, "Existing status for %s, status: 0x%02x", s, m_detail->load_capability); m_detail->load_capability |= status; MHVTL_DBG(2, "Already have an entry for %s, new status: 0x%02x", s, m_detail->load_capability); } else { MHVTL_DBG(2, "Adding new entry for %s", s); m_detail = zalloc(sizeof(struct media_details)); if (!m_detail) { MHVTL_ERR("Failed to allocate %d bytes", (int)sizeof(m_detail)); return -ENOMEM; } m_detail->media_type = media_type; m_detail->load_capability = status; list_add_tail(&m_detail->siblings, den_list); } set_TapeAlert(0); return 0; } static struct device_type_template ssc_ops = { .ops = { SCSI_OP_RANGE(0x00, 0xff, spc_illegal_op), /* 0x00 -> 0x0f */ SCSI_OP(0x00, ssc_tur), SCSI_OP(0x01, ssc_rewind), SCSI_OP(0x03, spc_request_sense), SCSI_OP(0x04, ssc_format_medium), SCSI_OP(0x05, ssc_read_block_limits), SCSI_OP(0x08, ssc_read_6), SCSI_OP(0x0a, ssc_write_6), SCSI_OP(0x0b, ssc_set_capacity), /* 0x10 -> 0x1f */ SCSI_OP(0x10, ssc_write_filemarks), SCSI_OP(0x11, ssc_space_6), SCSI_OP(0x12, spc_inquiry), SCSI_OP(0x13, ssc_verify_6), SCSI_OP(0x15, ssc_mode_select), SCSI_OP(0x16, ssc_reserve), SCSI_OP(0x17, ssc_release), SCSI_OP(0x19, ssc_erase), SCSI_OP(0x1a, spc_mode_sense), SCSI_OP(0x1b, ssc_load_unload), SCSI_OP(0x1c, ssc_recv_diagnostics), SCSI_OP(0x1d, ssc_send_diagnostics), SCSI_OP(0x1e, ssc_allow_prevent_removal), /* 0x20 -> 0x2f */ SCSI_OP(0x2b, ssc_locate), /* 0x30 -> 0x3f */ SCSI_OP(0x34, ssc_read_position), SCSI_OP(0x3c, spc_read_buffer), /* 0x40 -> 0x4f */ SCSI_OP(0x44, ssc_report_density_support), SCSI_OP(0x4c, ssc_log_select), SCSI_OP(0x4d, ssc_log_sense), /* 0x50 -> 0x5f */ SCSI_OP(0x55, ssc_mode_select), SCSI_OP(0x56, ssc_reserve), SCSI_OP(0x57, ssc_release), SCSI_OP(0x5a, spc_mode_sense), /* 0x60 -> 0x6f */ /* 0x70 -> 0x7f */ /* 0x80 -> 0x8f */ SCSI_OP(0x82, ssc_allow_overwrite), SCSI_OP(0x8c, ssc_read_attributes), SCSI_OP(0x8d, ssc_write_attributes), /* 0x90 -> 0x9f */ SCSI_OP(0x91, ssc_space_16), SCSI_OP(0x92, ssc_locate), /* 0xa0 -> 0xaf */ SCSI_OP(0xa0, spc_illegal_op), /* processed in the kernel module */ SCSI_OP(0xa3, ssc_a3_service_action), SCSI_OP(0xa4, ssc_a4_service_action), SCSI_OP(0xab, ssc_read_media_sn), /* 0xb0 -> 0xbf */ /* 0xc0 -> 0xcf */ /* 0xd0 -> 0xdf */ /* 0xe0 -> 0xef */ /* 0xf0 -> 0xff */ }}; /* * Update ops[xx] with new/updated/custom function 'f' */ void register_ops(struct lu_phy_attr *lu, int op, void *f, void *g, void *h) { lu->scsi_ops->ops[op].cmd_perform = f; lu->scsi_ops->ops[op].pre_cmd_perform = g; lu->scsi_ops->ops[op].post_cmd_perform = h; } #define MALLOC_SZ 512 static int init_lu(struct lu_phy_attr *lu, unsigned minor, struct mhvtl_ctl *ctl) { struct vpd **lu_vpd = lu->lu_vpd; char device_conf[CONF_FILE_SZ]; FILE *conf; char *b; /* Read from file into this buffer */ char *s; /* Somewhere for sscanf to store results */ int indx; struct mhvtl_ctl tmpctl; int found = 0; int linecount; if (get_config(device_conf, DEVICE_CONF, my_id) < 0) { exit(1); } INIT_LIST_HEAD(&lu->den_list); INIT_LIST_HEAD(&lu->log_pg); INIT_LIST_HEAD(&lu->mode_pg); lu->scsi_ops = &ssc_ops; strncpy(home_directory, MHVTL_HOME_PATH, HOME_DIR_PATH_SZ); lu->fifoname = NULL; lu->fifo_fd = NULL; lu->fifo_flag = 0; lu->ptype = TYPE_TAPE; backoff = DEFLT_BACKOFF_VALUE; lbp_rscrc_be = 1; /* Default RSCRC is big-endian */ lu->sense_p = &sense[0]; /* Default inquiry bits */ memset(&lu->inquiry, 0, MAX_INQUIRY_SZ); lu->inquiry[0] = TYPE_TAPE; /* SSC device */ lu->inquiry[1] = 0x80; /* Removable bit set */ lu->inquiry[2] = 0x05; /* SCSI Version (v3) */ lu->inquiry[3] = 0x02; /* Response Data Format */ lu->inquiry[4] = 59; /* Additional Length */ lu->inquiry[6] = 0x01; /* Addr16 */ lu->inquiry[7] = 0x20; /* Wbus16 */ put_unaligned_be16(0x0300, &lu->inquiry[58]); /* SPC-3 No ver claimed */ put_unaligned_be16(0x0960, &lu->inquiry[60]); /* iSCSI */ put_unaligned_be16(0x0200, &lu->inquiry[62]); /* SSC */ conf = fopen(device_conf, "r"); if (!conf) { MHVTL_ERR("Can not open config file %s : %s", device_conf, strerror(errno)); perror("Can not open config file"); exit(1); } s = zalloc(MALLOC_SZ); if (!s) { perror("Could not allocate memory"); exit(1); } b = zalloc(MALLOC_SZ); if (!b) { perror("Could not allocate memory"); exit(1); } /* While read in a line */ linecount = 0; while (readline(b, MALLOC_SZ, conf) != NULL) { linecount++; if (b[0] == '#') /* Ignore comments */ continue; if (strlen(b) == 1) /* Reset drive number of blank line */ indx = 0xff; if (sscanf(b, "Drive: %d CHANNEL: %d TARGET: %d LUN: %d", &indx, &tmpctl.channel, &tmpctl.id, &tmpctl.lun)) { MHVTL_DBG(2, "Looking for %d, Found drive %u", minor, indx); if (indx == minor) { found = 1; memcpy(ctl, &tmpctl, sizeof(tmpctl)); } } if (indx == minor) { unsigned int c, d, e, f, g, h, j, k; int i; memset(s, 0x20, MALLOC_SZ); if (sscanf(b, " Unit serial number: %s", s)) { checkstrlen(s, SCSI_SN_LEN, linecount); snprintf(lu->lu_serial_no, sizeof(lu->lu_serial_no), "%-10s", s); } if (sscanf(b, " Vendor identification: %s", s)) { checkstrlen(s, VENDOR_ID_LEN, linecount); snprintf(lu->vendor_id, VENDOR_ID_LEN + 1, "%-8s", s); snprintf(&lu->inquiry[8], VENDOR_ID_LEN + 1, "%-8s", s); } if (sscanf(b, " Product identification: %16c", s)) { /* sscanf does not NULL terminate */ /* 25 is len of ' Product identification: ' */ s[strlen(b) - 25] = '\0'; checkstrlen(s, PRODUCT_ID_LEN, linecount); snprintf(lu->product_id, PRODUCT_ID_LEN + 1, "%-16s", s); snprintf(&lu->inquiry[16], PRODUCT_ID_LEN + 1, "%-16s", s); } if (sscanf(b, " Product revision level: %s", s)) { checkstrlen(s, PRODUCT_REV_LEN, linecount); snprintf(&lu->inquiry[32], PRODUCT_REV_LEN + 1, "%-4s", s); } if (sscanf(b, " Library ID: %d", &library_id)) { MHVTL_DBG(2, "Library ID: %d", library_id); } if (sscanf(b, " LBP RSCRC BE: %d", &lbp_rscrc_be)) { MHVTL_DBG(2, "Logical Block Protection RSCRC: %d", lbp_rscrc_be); } if (sscanf(b, " Backoff: %d", &i)) { if ((i > 1) && (i < 10000)) { MHVTL_DBG(1, "Backoff value: %d", i); backoff = i; } } if (sscanf(b, " Compression type: %s", s)) { if (!strncasecmp(s, "lzo", 3)) lu_ssc.compressionType = LZO; if (!strncasecmp(s, "zlib", 4)) lu_ssc.compressionType = ZLIB; MHVTL_DBG(2, "Compression set to %s", (lu_ssc.compressionType == LZO) ? "LZO" : "ZLIB"); } if (sscanf(b, " Compression: factor %d enabled %d", &i, &j)) { lu_ssc.configCompressionFactor = i; lu_ssc.configCompressionEnabled = j; } else if (sscanf(b, " Compression: %d", &i)) { if ((i > Z_NO_COMPRESSION) && (i <= Z_BEST_COMPRESSION)) lu_ssc.configCompressionFactor = i; else lu_ssc.configCompressionFactor = 0; } if (sscanf(b, " fifo: %s", s)) process_fifoname(lu, s, 0); i = sscanf(b, " NAA: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", &c, &d, &e, &f, &g, &h, &j, &k); if (i == 8) { free(lu->naa); lu->naa = zalloc(48); if (lu->naa) sprintf((char *)lu->naa, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", c, d, e, f, g, h, j, k); MHVTL_DBG(2, "Setting NAA: to %s", lu->naa); } else if (i > 0) { int y; free(lu->naa); lu->naa = NULL; for (y = 0; y < MALLOC_SZ; y++) if (b[y] == '\n') b[y] = 0; MHVTL_DBG(1, "NAA: Incorrect params %s" " : using defaults", b); } } } fclose(conf); free(b); free(s); if (found && !lu->inquiry[32]) { char *v; /* Default rev with mhvtl release info */ v = get_version(); MHVTL_DBG(1, "Adding default vers info: %s", v); snprintf(&lu->inquiry[32], PRODUCT_REV_LEN + 1, "%-4s", v); free(v); } /* Unit Serial Number */ lu_vpd[PCODE_OFFSET(0x80)] = alloc_vpd(strlen(lu->lu_serial_no)); update_vpd_80(lu, lu->lu_serial_no); /* Device Identification */ lu_vpd[PCODE_OFFSET(0x83)] = alloc_vpd(VPD_83_SZ); update_vpd_83(lu, NULL); if (library_id) find_media_home_directory(NULL, library_id); if ((backoff < 10) || (backoff > 10000)) { backoff = DEFLT_BACKOFF_VALUE; MHVTL_LOG("Set default backoff value to %ld", backoff); } return found; } static void process_cmd(int cdev, uint8_t *buf, struct mhvtl_header *mhvtl_cmd, useconds_t pollInterval) { struct mhvtl_ds dbuf; uint8_t *cdb; /* Get the SCSI cdb from vtl driver * - Returns SCSI command S/No. */ cdb = (uint8_t *)&mhvtl_cmd->cdb; /* Interpret the SCSI command & process -> Returns no. of bytes to send back to kernel */ dbuf.sz = 0; dbuf.serialNo = mhvtl_cmd->serialNo; dbuf.data = buf; dbuf.sam_stat = lu_ssc.sam_status; dbuf.sense_buf = &sense; processCommand(cdev, cdb, &dbuf, pollInterval); /* Complete SCSI cmd processing */ completeSCSICommand(cdev, &dbuf); /* dbuf.sam_stat was zeroed in completeSCSICommand */ lu_ssc.sam_status = dbuf.sam_stat; } static void init_lu_ssc(struct priv_lu_ssc *lu_priv) { lu_priv->bufsize = 2 * 1024 * 1024; lu_priv->load_status = TAPE_UNLOADED; lu_priv->inLibrary = 0; lu_priv->sam_status = SAM_STAT_GOOD; lu_priv->MediaWriteProtect = MEDIA_WRITABLE; lu_priv->capacity_unit = 1; lu_priv->configCompressionFactor = Z_BEST_SPEED; lu_priv->bytesRead_I = 0; lu_priv->bytesRead_M = 0; lu_priv->bytesWritten_I = 0; lu_priv->bytesWritten_M = 0; lu_priv->c_pos = c_pos; lu_priv->KEY_INSTANCE_COUNTER = 0; lu_priv->DECRYPT_MODE = 0; lu_priv->ENCRYPT_MODE = 0; lu_priv->app_encr_info = &app_encryption_state; lu_priv->OK_2_write = &OK_to_write; lu_priv->barcode = NULL; lu_priv->mamp = &mam; INIT_LIST_HEAD(&lu_priv->supported_media_list); lu_priv->pm = NULL; lu_priv->state_msg = NULL; cumul_pollInterval = 0L; lu_priv->delay_load = 0; lu_priv->delay_unload = 0; lu_priv->delay_thread = 0; lu_priv->delay_position = 0; lu_priv->delay_rewind = 0; } /* * Be nice and free all malloc() on exit */ static void cleanup_lu(struct lu_phy_attr *lu) { int i; /* Free all VPD pages */ for (i = 0x80; i < 0x100; i++) { if (lu->lu_vpd[PCODE_OFFSET(i)]) { dealloc_vpd(lu->lu_vpd[PCODE_OFFSET(i)]); lu->lu_vpd[PCODE_OFFSET(i)] = NULL; } } dealloc_all_mode_pages(lu); dealloc_all_log_pages(lu); cleanup_drive_media_list(lu); cleanup_density_support(&lu->den_list); free(lu->naa); cart_deinit(); } void ssc_personality_module_register(struct ssc_personality_template *pm) { MHVTL_DBG(2, "%s", pm->name); lu_ssc.pm = pm; if (pm->drive_supports_SP) { register_ops(pm->lu, SECURITY_PROTOCOL_IN, ssc_spin, NULL, NULL); register_ops(pm->lu, SECURITY_PROTOCOL_OUT, ssc_spout, NULL, NULL); } if (pm->drive_supports_SPR) { register_ops(pm->lu, PERSISTENT_RESERVE_IN, ssc_pr_in, NULL, NULL); register_ops(pm->lu, PERSISTENT_RESERVE_OUT, ssc_pr_out, NULL, NULL); } } static void caught_signal(int signo) { printf("Please use 'vtlcmd exit' to shutdown nicely\n" " Received signal: %d\n\n", signo); MHVTL_LOG("Please use 'vtlcmd exit' to shutdown nicely," " Received signal: %d", signo); } int main(int argc, char *argv[]) { int cdev; int ret; int last_state = MHVTL_STATE_UNKNOWN; useconds_t sleep_time = 50000L; /* Used as backoff counter */ uint8_t *buf; pid_t child_cleanup, pid, ppid, sid; struct sigaction new_action, old_action; int fifo_retval; int opt; int foreground = 0; const pid_t not_started = -2; int time_to_exit = 0; char *progname = argv[0]; char *fifoname = NULL; const char *name = "mhvtl"; unsigned minor = 0; struct mhvtl_header mhvtl_cmd; struct mhvtl_header *cmd; struct mhvtl_ctl ctl; /* Message Q */ int mlen, r_qid; memset(&mhvtl_cmd, 0, sizeof(struct mhvtl_header)); memset(&ctl, 0, sizeof(struct mhvtl_ctl)); current_state = MHVTL_STATE_INIT; while ((opt = getopt(argc, argv, "dv::q:f::F")) != -1) { switch (opt) { case 'd': /* If debug, make verbose... */ debug = 4; verbose = 9; foreground = 1; break; case 'v': if (optarg) verbose = atoi(optarg); else verbose++; /* limit verbosity to single digit */ if (verbose > 9) verbose = 9; break; case 'q': my_id = atoi(optarg); if ((my_id < 0) || (my_id > MAXPRIOR)) { fprintf(stderr, "error: queue ID out of range [1..%u]\n", MAXPRIOR); usage(progname); exit(1); } break; case 'f': if (optarg) fifoname = strdup(optarg); break; case 'F': foreground = 1; break; default: usage(progname); exit(1); } } if (my_id < 0) { fprintf(stderr, "error: must supply queue ID\n"); usage(progname); exit(1); } minor = my_id; /* Minor == Message Queue priority */ openlog(progname, LOG_PID, LOG_DAEMON | LOG_WARNING); if (check_for_running_daemons(minor)) { printf("check_for_running_daemons(%d) returned true\n", minor); MHVTL_LOG("%s: version %s %s %s, found another running daemon... exiting", progname, MHVTL_VERSION, MHVTL_GITHASH, MHVTL_GITDATE); exit(2); } else { MHVTL_DBG(1, "No lock file found... Continuing"); } if (lzo_init() != LZO_E_OK) { MHVTL_ERR("Could not initialize LZO... Exiting"); exit(1); } /* Clear Sense arr */ memset(sense, 0, sizeof(sense)); /* Powered on / reset flag */ reset_device(); /* Initialise lu_ssc 'global vars' used by this daemon */ init_lu_ssc(&lu_ssc); lunit.lu_private = &lu_ssc; /* Parse config file and build up each device */ if (!init_lu(&lunit, minor, &ctl)) { fprintf(stderr, "error: Can not find entry for '%u' in config file\n", minor); exit(1); } /* * Determine drive type * register personality module * Indirectly, mode page tables are initialised */ config_lu(&lunit); if (chrdev_create(minor)) { MHVTL_DBG(1, "Unable to create device node mhvtl%u", minor); exit(1); } /* Initialise message queue as necessary */ if ((r_qid = init_queue()) == -1) { fprintf(stderr, "error: Could not initialise message queue\n"); exit(1); } cdev = chrdev_open(name, minor); if (cdev == -1) { MHVTL_ERR("Could not open /dev/%s%u: %s", name, minor, strerror(errno)); fflush(NULL); exit(1); } buf = (uint8_t *)zalloc(lu_ssc.bufsize); if (NULL == buf) { perror("Problems allocating memory"); exit(1); } if ((chdir(MHVTL_HOME_PATH)) < 0) { perror("Unable to change directory to " MHVTL_HOME_PATH); exit(-1); } /* If debug or 'F' specified don't fork/run in background */ if (!foreground) { ppid = getpid(); switch (pid = fork()) { case 0: /* Child */ break; case -1: perror("Failed to fork daemon"); exit(-1); break; default: MHVTL_DBG(1, "Parent PID: %ld successfully started daemon: PID %ld", (long)ppid, (long)pid); exit(0); break; } umask(0); /* Change the file mode mask */ sid = setsid(); if (sid < 0) exit(-1); close(STDIN_FILENO); close(STDERR_FILENO); } MHVTL_LOG("[%ld] Started %s: version %s %s %s verbose log lvl: %d, lu [%d:%d:%d]", (long)getpid(), progname, MHVTL_VERSION, MHVTL_GITHASH, MHVTL_GITDATE, verbose, ctl.channel, ctl.id, ctl.lun); MHVTL_DBG(1, "Size of buffer is %d", lu_ssc.bufsize); #ifdef __x86_64__ if (__builtin_cpu_supports("sse4.2")) { MHVTL_DBG(1, "crc32c using Intel sse4.2 hardware optimization"); } else { MHVTL_DBG(1, "crc32c not using Intel sse4.2 optimization"); } #endif oom_adjust(); new_action.sa_handler = caught_signal; new_action.sa_flags = 0; sigemptyset(&new_action.sa_mask); sigaction(SIGALRM, &new_action, &old_action); sigaction(SIGHUP, &new_action, &old_action); sigaction(SIGINT, &new_action, &old_action); sigaction(SIGPIPE, &new_action, &old_action); sigaction(SIGTERM, &new_action, &old_action); sigaction(SIGUSR1, &new_action, &old_action); sigaction(SIGUSR2, &new_action, &old_action); /* If fifoname passed as switch */ if (fifoname) process_fifoname(&lunit, fifoname, 1); /* fifoname can be defined in device.conf */ if (lunit.fifoname) open_fifo(&lunit.fifo_fd, lunit.fifoname); fifo_retval = inc_fifo_count(); if (fifo_retval == -ENOMEM) { MHVTL_ERR("shared memory setup failed - exiting..."); goto exit; } else if (fifo_retval < 0) { MHVTL_ERR("Failed to set fifo count()..."); } child_cleanup = not_started; for (;;) { /* Check for anything in the messages Q */ mlen = msgrcv(r_qid, &lu_ssc.r_entry, MAXOBN, my_id, IPC_NOWAIT); if (mlen > 0) { if (processMessageQ(&lu_ssc.r_entry.msg, &lu_ssc.sam_status)) { time_to_exit = 1; /* Flag that we need to exit */ MHVTL_DBG(1, "Exit called"); } } else if (mlen < 0) { if ((r_qid = init_queue()) == -1) { MHVTL_ERR("Can not open message queue: %s", strerror(errno)); } } ret = ioctl(cdev, VTL_POLL_AND_GET_HEADER, &mhvtl_cmd); if (ret < 0) { MHVTL_DBG(2, "ioctl(VTL_POLL_AND_GET_HEADER): %d : %s", ret, strerror(errno)); } else { if (debug) printf("ioctl(VX_TAPE_POLL_STATUS) " "returned: %d, interval: %ld\n", ret, (long)sleep_time); if (child_cleanup) { if (child_cleanup == not_started) { child_cleanup = add_lu(my_id, &ctl); if (!child_cleanup) { MHVTL_ERR("Failed to create logical unit - exiting..."); goto exit; } } if (waitpid(child_cleanup, NULL, WNOHANG)) { MHVTL_DBG(1, "[%ld] Cleaning up after add_lu " "child pid: %d", (long)getpid(), child_cleanup); child_cleanup = 0; } else { MHVTL_DBG(2, "[%ld] Child cleanup of %ld still outstanding", (long)getpid(), (long)child_cleanup); } } if (debug) fflush(NULL); switch (ret) { case VTL_QUEUE_CMD: /* A cdb to process */ cmd = malloc(sizeof(struct mhvtl_header)); if (!cmd) { MHVTL_ERR("Out of memory"); sleep_time = 1000000; } else { memcpy(cmd, &mhvtl_cmd, sizeof(mhvtl_cmd)); process_cmd(cdev, buf, cmd, sleep_time); /* Something to do, reduce poll time */ sleep_time = MIN_SLEEP_TIME; free(cmd); } break; case VTL_IDLE: usleep(sleep_time); /* While nothing to do, increase * time we sleep before polling again. */ if (sleep_time < 1000000) sleep_time += backoff; break; default: MHVTL_LOG("ioctl(0x%x) returned %d", VTL_POLL_AND_GET_HEADER, ret); sleep(1); break; } if (current_state != last_state) { status_change(lunit.fifo_fd, current_state, my_id, &lu_ssc.state_msg); last_state = current_state; } if (sleep_time > 0xf000) { if (sleep_time < (0xf000 + backoff)) { if (get_tape_load_status() == TAPE_LOADED) set_current_state(MHVTL_STATE_LOADED_IDLE); else set_current_state(MHVTL_STATE_IDLE); } if (time_to_exit) /* enough cycles to ensure no outstanding work remains */ goto exit; } } } exit: ioctl(cdev, VTL_REMOVE_LU, &ctl); cleanup_lu(&lunit); close(cdev); free(buf); dec_fifo_count(); if (lunit.fifo_fd) { fclose(lunit.fifo_fd); unlink(lunit.fifoname); free(lunit.fifoname); } free_lock(minor); exit(0); } ================================================ FILE: usr/mhvtl_io.c ================================================ /* * mhvtl_io.c * * deduplicate code - Move Read/Write routines * * dump_tape / vtltape both use same functions * which are currently duplicated */ #include #include #include #include #include #include #include "be_byteshift.h" #include "vtl_common.h" #include "mhvtl_scsi.h" #include "logging.h" #include "vtllib.h" #include "vtlcart.h" #include "ssc.h" #include "mhvtl_log.h" #include "ccan/crc32c/crc32c.h" #include #include "minilzo.h" uint32_t GenerateRSCRC(uint32_t crc, uint32_t size, const void *buf); uint32_t BlockVerifyRSCRC(const uint8_t *blkbuf, uint32_t blklen, int32_t bigendian); uint32_t BlockProtectRSCRC(uint8_t *blkbuf, uint32_t blklen, int32_t bigendian); static void mk_sense_short_block(uint32_t requested, uint32_t processed, uint8_t *sense_valid) { int difference = (int)requested - (int)processed; /* No sense, ILI bit set */ sam_no_sense(SD_ILI, NO_ADDITIONAL_SENSE, sense_valid); MHVTL_DBG(2, "Short block read: Requested: %d, Read: %d," " short by %d bytes", requested, processed, difference); /* Now fill in the datablock with number of bytes not read/written */ put_unaligned_be32(difference, &sense[3]); } static int uncompress_lzo_block(uint8_t *buf, uint32_t tgtsize, uint8_t *sam_stat) { uint8_t *cbuf, *c2buf; uint32_t disk_blk_size, blk_size; int rc, z; loff_t nread = 0; lzo_uint uncompress_sz; /* The tape block is compressed. Save field values we will need after the read which causes the tape block to advance. */ blk_size = c_pos->blk_size; disk_blk_size = c_pos->disk_blk_size; /* Malloc a buffer to hold the compressed data, and read the data into it. */ cbuf = (uint8_t *)malloc(disk_blk_size); if (!cbuf) { MHVTL_ERR("Out of memory: %d", __LINE__); sam_medium_error(E_DECOMPRESSION_CRC, sam_stat); return 0; } /* Can't reference c_pos after this point * read_tape_block increments c_pos to next block header */ nread = read_tape_block(cbuf, disk_blk_size, sam_stat); if (nread != disk_blk_size) { MHVTL_ERR("read failed, %s", strerror(errno)); sam_medium_error(E_UNRECOVERED_READ, sam_stat); free(cbuf); return 0; } rc = tgtsize; uncompress_sz = blk_size; /* If the scsi read buffer is at least as big as the size of the uncompressed data then we can uncompress directly into the read buffer. If not, then we need an extra buffer to uncompress into, then memcpy the subrange we need to the read buffer. */ if (tgtsize >= blk_size) { /* block sizes match, uncompress directly into buf */ z = lzo1x_decompress_safe(cbuf, disk_blk_size, buf, &uncompress_sz, NULL); } else { /* Initiator hasn't requested same size as data block */ c2buf = (uint8_t *)malloc(uncompress_sz); if (c2buf == NULL) { MHVTL_ERR("Out of memory: %d", __LINE__); sam_medium_error(E_DECOMPRESSION_CRC, sam_stat); free(cbuf); return 0; } z = lzo1x_decompress_safe(cbuf, disk_blk_size, c2buf, &uncompress_sz, NULL); /* Only copy out decompressed data on success; otherwise c2buf * contains uninitialized / partial data and would silently * corrupt the caller's read buffer. The switch (z) below will * set the appropriate sense for failure cases. */ if (z == LZO_E_OK) memcpy(buf, c2buf, tgtsize); free(c2buf); } switch (z) { case LZO_E_OK: MHVTL_DBG(2, "Read %u bytes of lzo compressed" " data, have %u bytes for result", (uint32_t)nread, blk_size); goto complete; break; case LZO_E_INPUT_NOT_CONSUMED: MHVTL_DBG(1, "The end of compressed block has been detected before all %d bytes", blk_size); break; case LZO_E_INPUT_OVERRUN: MHVTL_ERR("The decompressor requested more bytes from the compressed block"); break; case LZO_E_OUTPUT_OVERRUN: MHVTL_ERR("The decompressor requested to write more bytes than the uncompressed block can hold"); break; case LZO_E_LOOKBEHIND_OVERRUN: MHVTL_ERR("Look behind overrun - data is corrupted"); break; case LZO_E_EOF_NOT_FOUND: MHVTL_ERR("No EOF code was found in the compressed block"); break; case LZO_E_ERROR: MHVTL_ERR("Data is corrupt - generic lzo error received"); break; } sam_medium_error(E_DECOMPRESSION_CRC, sam_stat); rc = 0; complete: free(cbuf); return rc; } static int uncompress_zlib_block(uint8_t *buf, uint32_t tgtsize, uint8_t *sam_stat) { uint8_t *cbuf, *c2buf; loff_t nread = 0; uLongf uncompress_sz; uint32_t disk_blk_size, blk_size; int rc, z; /* The tape block is compressed. Save field values we will need after the read which causes the tape block to advance. */ blk_size = c_pos->blk_size; disk_blk_size = c_pos->disk_blk_size; /* Malloc a buffer to hold the compressed data, and read the data into it. */ cbuf = (uint8_t *)malloc(disk_blk_size); if (!cbuf) { MHVTL_ERR("Out of memory: %d", __LINE__); sam_medium_error(E_DECOMPRESSION_CRC, sam_stat); return 0; } nread = read_tape_block(cbuf, disk_blk_size, sam_stat); if (nread != disk_blk_size) { MHVTL_ERR("read failed, %s", strerror(errno)); sam_medium_error(E_UNRECOVERED_READ, sam_stat); free(cbuf); return 0; } rc = tgtsize; uncompress_sz = blk_size; /* If the scsi read buffer is at least as big as the size of the uncompressed data then we can uncompress directly into the read buffer. If not, then we need an extra buffer to uncompress into, then memcpy the subrange we need to the read buffer. */ if (tgtsize >= blk_size) { /* block sizes match, uncompress directly into buf */ z = uncompress(buf, &uncompress_sz, cbuf, disk_blk_size); } else { /* Initiator hasn't requested same size as data block */ c2buf = (uint8_t *)malloc(uncompress_sz); if (c2buf == NULL) { MHVTL_ERR("Out of memory: %d", __LINE__); sam_medium_error(E_DECOMPRESSION_CRC, sam_stat); free(cbuf); return 0; } z = uncompress(c2buf, &uncompress_sz, cbuf, disk_blk_size); /* Only copy out decompressed data on success; otherwise c2buf * contains uninitialized / partial data and would silently * corrupt the caller's read buffer. The switch (z) below will * set the appropriate sense for failure cases. */ if (z == Z_OK) memcpy(buf, c2buf, tgtsize); free(c2buf); } switch (z) { case Z_OK: MHVTL_DBG(2, "Read %u bytes of zlib compressed" " data, have %u bytes for result", (uint32_t)nread, blk_size); break; case Z_MEM_ERROR: MHVTL_ERR("Not enough memory to decompress"); sam_medium_error(E_DECOMPRESSION_CRC, sam_stat); rc = 0; break; case Z_DATA_ERROR: MHVTL_ERR("Block corrupt or incomplete"); sam_medium_error(E_DECOMPRESSION_CRC, sam_stat); rc = 0; break; case Z_BUF_ERROR: MHVTL_ERR("Not enough memory in destination buf"); sam_medium_error(E_DECOMPRESSION_CRC, sam_stat); rc = 0; break; } free(cbuf); return rc; } /* CRC32C */ static uint32_t mhvtl_crc32c(unsigned char const *buf, size_t size) { return crc32c(0, buf, size); } /* * Return number of bytes read. * 0 on error with sense[] filled in... */ int readBlock(uint8_t *buf, uint32_t request_sz, int sili, int lbp_method, uint8_t *sam_stat) { uint32_t disk_blk_size, blk_size, blk_number; uint32_t rc; uint32_t save_sense; uint32_t pre_crc; uint32_t blk_flags; uint32_t post_crc; uint32_t lbp_crc; uint8_t *bounce_buffer; int lbp_sz; MHVTL_DBG(3, "Request to read: %u bytes at partition/header %u/%u, SILI: %d, LBP_method: %s", request_sz, c_pos->partition_id, c_pos->blk_number, sili, (lbp_method == 0) ? "None" : (lbp_method == 1) ? "RS-CRC" : "CRC32c"); /* check for a zero length read * This is not an error, and shouldn't change the tape position */ if (request_sz == 0) return 0; /** Note: lbp_method will only be set if LBP_R is also set. ** Logical Block Protection - account for 4 byte CRC **/ lbp_sz = (lbp_method) ? request_sz - 4 : request_sz; switch (c_pos->blk_type) { case B_DATA: break; case B_FILEMARK: MHVTL_DBG(1, "Expected to find DATA header, found: FILEMARK"); position_blocks_forw(1, sam_stat); mk_sense_short_block(lbp_sz, 0, sam_stat); save_sense = get_unaligned_be32(&sense[3]); sam_no_sense(SD_FILEMARK, E_MARK, sam_stat); put_unaligned_be32(save_sense, &sense[3]); return 0; break; case B_EOD: MHVTL_DBG(1, "Expected to find DATA header, found: EOD"); mk_sense_short_block(lbp_sz, 0, sam_stat); save_sense = get_unaligned_be32(&sense[3]); sam_blank_check(E_END_OF_DATA, sam_stat); put_unaligned_be32(save_sense, &sense[3]); return 0; break; default: MHVTL_ERR("Unknown blk header at offset %u" " - Abort read cmd", c_pos->blk_number); sam_medium_error(E_UNRECOVERED_READ, sam_stat); return 0; break; } /* The tape block is compressed. Save field values we will need after the read causes the tape block to advance. */ blk_size = c_pos->blk_size; blk_number = c_pos->blk_number; disk_blk_size = c_pos->disk_blk_size; pre_crc = c_pos->uncomp_crc; blk_flags = c_pos->blk_flags; if (blk_size > request_sz) { /* Add a fudge of 4 bytes in case LBP is calculated */ bounce_buffer = malloc(blk_size + 4); if (!bounce_buffer) { MHVTL_ERR("Unable to allocate %d bytes for bounce buffer", blk_size); sam_medium_error(E_UNRECOVERED_READ, sam_stat); return 0; } } else { bounce_buffer = buf; } if (blk_flags & BLKHDR_FLG_LZO_COMPRESSED) rc = uncompress_lzo_block(bounce_buffer, blk_size, sam_stat); else if (blk_flags & BLKHDR_FLG_ZLIB_COMPRESSED) rc = uncompress_zlib_block(bounce_buffer, blk_size, sam_stat); else { /* If the tape block is uncompressed, we can read the number of bytes we need directly into the scsi read buffer and we are done. */ if (read_tape_block(bounce_buffer, blk_size, sam_stat) != blk_size) { MHVTL_ERR("read failed, %s", strerror(errno)); sam_medium_error(E_UNRECOVERED_READ, sam_stat); goto free_bounce_buf; } rc = blk_size; } /* At this point, rc should now contain the actual uncompressed size of the block just read */ if (blk_flags & BLKHDR_FLG_CRC) { post_crc = mhvtl_crc32c(bounce_buffer, rc); if (pre_crc != post_crc) { MHVTL_ERR("Recorded CRC: 0x%08x, Calculated CRC: 0x%08x", pre_crc, post_crc); sam_medium_error(E_DECOMPRESSION_CRC, sam_stat); goto free_bounce_buf; } MHVTL_DBG(3, "Recorded CRC: 0x%08x, calculated CRC: 0x%08x", pre_crc, post_crc); } /* Update Logical Block Protection CRC */ switch (lbp_method) { case 1: rc = BlockProtectRSCRC(bounce_buffer, rc, lbp_rscrc_be); if (rc == 0) { MHVTL_ERR("Failed to generate/append RSCRC: lbp_be: %d", lbp_rscrc_be); } MHVTL_DBG(2, "READ block %d LBP RSCRC : 0x%02x 0x%02x 0x%02x 0x%02x", blk_number, bounce_buffer[rc - 4], bounce_buffer[rc - 3], bounce_buffer[rc - 2], bounce_buffer[rc - 1]); break; case 2: MHVTL_DBG(2, "rc: %d, request_sz: %d bounce buffer before LBP: 0x%08x %08x", rc, request_sz, get_unaligned_be32(&bounce_buffer[rc - 4]), get_unaligned_be32(&bounce_buffer[rc])); /* If we don't have a LBP CRC32C format, re-calculate now */ lbp_crc = (blk_flags & BLKHDR_FLG_CRC) ? pre_crc : mhvtl_crc32c(bounce_buffer, rc); memcpy(&bounce_buffer[rc], &lbp_crc, 4); MHVTL_DBG(2, "Logical Block Protection - CRC32C, rc: %d, request_sz: %d, lbp_size: %d, CRC32C: 0x%8x", rc, request_sz, lbp_sz, lbp_crc); MHVTL_DBG(2, "rc: %d, request_sz: %d bounce buffer after LBP: 0x%08x %08x", rc, request_sz, get_unaligned_be32(&bounce_buffer[rc - 4]), get_unaligned_be32(&bounce_buffer[rc])); MHVTL_DBG(2, "READ block %d LBP RSCRC : 0x%02x 0x%02x 0x%02x 0x%02x", blk_number, bounce_buffer[rc], bounce_buffer[rc + 1], bounce_buffer[rc + 2], bounce_buffer[rc + 3]); rc += 4; /* Account for LBP checksum */ break; case 3: /* This should never occur - MODE 0a/f0 should not accept this value */ MHVTL_ERR("LBP method 3 not supported : Returning E_LOGICAL_BLOCK_GUARD_FAILED"); sam_hardware_error(E_LOGICAL_BLOCK_GUARD_FAILED, sam_stat); rc = 0; goto free_bounce_buf; break; default: break; } /* If bounce_buffer != buf then we're reading a large block and need to copy * data back into buf */ if (bounce_buffer != buf) { MHVTL_DBG(1, "Bounce buffer in use: request_sz: %d", request_sz); memcpy(buf, bounce_buffer, request_sz); free(bounce_buffer); rc = request_sz; } lu_ssc.bytesRead_I += blk_size; lu_ssc.bytesRead_M += disk_blk_size; /* * What SSC4-r01e says about incorrect length reads If the SILI bit is zero and an incorrect-length logical block is read, CHECK CONDITION status shall be returned. The ILI and VALID bits shall be set to one in the sense data and the additional sense code shall be set to NO ADDITIONAL SENSE INFORMATION. Upon termination, the logical position shall be after the incorrect-length logical block (i.e., end-of-partition side). If the FIXED bit is one, the INFORMATION field shall be set to the requested transfer length minus the actual number of logical blocks read, not including the incorrect-length logical block. If the FIXED bit is zero, the INFORMATION field shall be set to the requested transfer length minus the actual logical block length. Logical units that do not support negative values shall set the INFORMATION field to zero if the overlength condition exists. NOTE 35 - In the above case with the FIXED bit of one, only the position of the incorrect-length logical block may be determined from the sense data. The actual length of the incorrect logical block is not reported. Other means may be used to determine its actual length (e.g., read it again with the fixed bit set to zero). */ if (rc != request_sz) mk_sense_short_block(request_sz, rc, sam_stat); else if (!sili) { if (lbp_sz < blk_size) mk_sense_short_block(lbp_sz, blk_size, sam_stat); } return rc; free_bounce_buf: if (bounce_buffer != buf) free(bounce_buffer); return 0; } static lzo_uint mhvtl_compressBound(lzo_uint src_sz) { return src_sz + src_sz / 16 + 67; } /* Determine whether or not to store the crypto info in the tape * blk_header. * We may adjust this decision for the 3592. (See ibm_3592_xx.pm) */ static void setup_crypto(struct scsi_cmd *cmd, struct priv_lu_ssc *lu_priv) { lu_priv->app_encr_info = lu_priv->ENCRYPT_MODE == 2 ? &app_encryption_state : NULL; if (lu_priv->pm->valid_encryption_media) lu_priv->pm->valid_encryption_media(cmd); } /** Call with ** LBP method, bufer and size, and CRC32C CRC which was already calculated ** ** Return -1 on error or 0 on success (LBP CRC match) */ static int32_t verify_lbp_crc(int lbp_method, unsigned char const *buf, size_t src_sz, uint32_t crc32c) { uint32_t lbp_crc = 0; switch (lbp_method) { case 0: /* No method defined - skip check */ break; case 1: MHVTL_DBG(2, "WRITE block %d LBP RSCRC : 0x%02x 0x%02x 0x%02x 0x%02x", c_pos->blk_number - 1, buf[src_sz], buf[src_sz + 1], buf[src_sz + 2], buf[src_sz + 3]); if (!BlockVerifyRSCRC(buf, src_sz + 4, lbp_rscrc_be)) { if (lbp_rscrc_be) { MHVTL_ERR("RSCRC mismatch: lbp_be: %d - LBP provided: 0x%08x, calculated RSCRC: 0x%08x", lbp_rscrc_be, get_unaligned_be32(&buf[src_sz]), GenerateRSCRC(0, src_sz, buf)); } else { MHVTL_ERR("RSCRC mismatch: lbp_be: %d - LBP provided: 0x%08x, calculated RSCRC: 0x%08x", lbp_rscrc_be, get_unaligned_be32(&buf[src_sz]), __bswap_32(GenerateRSCRC(0, src_sz, buf))); } return -1; /* CRC mismatch */ } break; case 2: MHVTL_DBG(2, "WRITE block %d LBP CRC32C : 0x%02x 0x%02x 0x%02x 0x%02x", c_pos->blk_number - 1, buf[src_sz], buf[src_sz + 1], buf[src_sz + 2], buf[src_sz + 3]); lbp_crc = get_unaligned_be32(&crc32c); if (lbp_crc != get_unaligned_be32(&buf[src_sz])) { MHVTL_ERR("CRC32C mismatch - LBP: 0x%08x, calculated: 0x%08x", get_unaligned_be32(&buf[src_sz]), lbp_crc); return -1; /* CRC mismatch */ } break; default: MHVTL_ERR("Undefined LBP_method - failing check"); return -1; break; } return 0; } static void log_crc_options(int lbp_method, unsigned char const *buf, size_t size, uint32_t crc) { MHVTL_ERR("Legacy CRC32C: 0x%08x", crc); switch (lbp_method) { case 1: MHVTL_ERR("Reed-Solomon CRC with seed 0: 0x%08x", GenerateRSCRC(0, size, buf)); MHVTL_ERR("Inverted Reed-Solomon CRC with seed 0: 0x%08x", ~GenerateRSCRC(0, size, buf)); MHVTL_ERR("Reed-Solomon CRC with seed 0xffffffff: 0x%08x", GenerateRSCRC(0xffffffff, size, buf)); MHVTL_ERR("Inverted Reed-Solomon CRC with seed 0xffffffff: 0x%08x", ~GenerateRSCRC(0xffffffff, size, buf)); break; case 2: MHVTL_ERR("CRC32C with seed 0: 0x%08x", crc32c(0, buf, size)); MHVTL_ERR("Inverted CRC32C with seed 0: 0x%08x", ~crc32c(0, buf, size)); MHVTL_ERR("CRC32C with seed 0xffffffff: 0x%08x", crc32c(0xffffffff, buf, size)); MHVTL_ERR("Inverted CRC32C with seed 0xffffffff: 0x%08x", ~crc32c(0xffffffff, buf, size)); break; default: MHVTL_ERR("No matching LBP method"); break; } } static void log_lbp_method(int lbp_method) { switch (lbp_method) { case 1: MHVTL_DBG(2, "Drive supports RS-CRC Logical Block Protection, lbp_be: %d", lbp_rscrc_be); break; case 2: MHVTL_DBG(2, "Drive supports RS-CRC and CRC32C Logical Block Protection"); break; default: MHVTL_DBG(2, "Drive supports Logical Block Protection (LBP) but unsupported LBP method provided: %d", lbp_method); break; } } /* * Return number of bytes written to 'file' * * Zero on error with sense buffer already filled in */ static int writeBlock_nocomp(struct scsi_cmd *cmd, uint32_t src_sz, uint8_t null_wr, int lbp_method) { uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint8_t *src_buf = (uint8_t *)cmd->dbuf_p->data; struct priv_lu_ssc *lu_priv; uint32_t crc; int rc; lu_priv = (struct priv_lu_ssc *)cmd->lu->lu_private; crc = mhvtl_crc32c((unsigned char const *)src_buf, (size_t)src_sz); setup_crypto(cmd, lu_priv); rc = write_tape_block(src_buf, src_sz, 0, lu_priv->app_encr_info, 0, null_wr, crc, sam_stat); if (lu_priv->pm->drive_supports_LBP && lbp_method) { log_lbp_method(lbp_method); if (verify_lbp_crc(lbp_method, src_buf, src_sz, crc) < 0) { MHVTL_ERR("LBP mis-compare on write : Returning E_LOGICAL_BLOCK_GUARD_FAILED"); sam_hardware_error(E_LOGICAL_BLOCK_GUARD_FAILED, sam_stat); log_crc_options(lbp_method, src_buf, src_sz, crc); return 0; } } lu_priv->bytesWritten_M += src_sz; lu_priv->bytesWritten_I += src_sz; if (rc < 0) return 0; return src_sz; } /* * Return number of bytes written to 'file' * * Zero on error with sense buffer already filled in */ static int writeBlock_lzo(struct scsi_cmd *cmd, uint32_t src_sz, uint8_t null_wr, int lbp_method) { lzo_uint dest_len; lzo_uint src_len = src_sz; lzo_bytep dest_buf; lzo_bytep wrkmem = NULL; uint32_t crc; lzo_bytep src_buf = (lzo_bytep)cmd->dbuf_p->data; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; struct priv_lu_ssc *lu_priv; int rc; int z; lu_priv = (struct priv_lu_ssc *)cmd->lu->lu_private; crc = mhvtl_crc32c((unsigned char const *)src_buf, (size_t)src_sz); setup_crypto(cmd, lu_priv); dest_len = mhvtl_compressBound(src_sz); dest_buf = (lzo_bytep)malloc(dest_len); wrkmem = (lzo_bytep)malloc(LZO1X_1_MEM_COMPRESS); if (unlikely(!dest_buf)) { MHVTL_ERR("dest_buf malloc(%d) failed", (int)dest_len); sam_medium_error(E_WRITE_ERROR, sam_stat); free(wrkmem); return 0; } if (unlikely(!wrkmem)) { MHVTL_ERR("wrkmem malloc(%d) failed", (int)LZO1X_1_MEM_COMPRESS); sam_medium_error(E_WRITE_ERROR, sam_stat); free(dest_buf); return 0; } z = lzo1x_1_compress(src_buf, src_sz, dest_buf, &dest_len, wrkmem); if (unlikely(z != LZO_E_OK)) { MHVTL_ERR("LZO compression error"); sam_hardware_error(E_COMPRESSION_CHECK, sam_stat); return 0; } MHVTL_DBG(2, "Compression: Orig %d, after comp: %ld", src_sz, (unsigned long)dest_len); rc = write_tape_block(dest_buf, src_len, dest_len, lu_priv->app_encr_info, LZO, null_wr, crc, sam_stat); free(dest_buf); free(wrkmem); lu_priv->bytesWritten_M += dest_len; lu_priv->bytesWritten_I += src_len; if (lu_priv->pm->drive_supports_LBP && lbp_method) { log_lbp_method(lbp_method); if (verify_lbp_crc(lbp_method, src_buf, src_sz, crc) < 0) { MHVTL_ERR("LBP mis-compare on write : Returning E_LOGICAL_BLOCK_GUARD_FAILED - but wrote block anyway..."); sam_hardware_error(E_LOGICAL_BLOCK_GUARD_FAILED, sam_stat); log_crc_options(lbp_method, src_buf, src_sz, crc); return 0; } } if (rc < 0) return 0; return src_len; } /* * Return number of bytes written to 'file' * * Zero on error with sense buffer already filled in */ static int writeBlock_zlib(struct scsi_cmd *cmd, uint32_t src_sz, uint8_t null_wr, int lbp_method) { Bytef *dest_buf; uLong dest_len; uLong src_len = src_sz; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint8_t *src_buf = (uint8_t *)cmd->dbuf_p->data; struct priv_lu_ssc *lu_priv; uint32_t crc; int rc; int z; lu_priv = (struct priv_lu_ssc *)cmd->lu->lu_private; crc = mhvtl_crc32c((unsigned char const *)src_buf, (size_t)src_sz); setup_crypto(cmd, lu_priv); dest_len = compressBound(src_sz); dest_buf = (Bytef *)malloc(dest_len); if (!dest_buf) { MHVTL_ERR("malloc(%d) failed", (int)dest_len); sam_medium_error(E_WRITE_ERROR, sam_stat); return 0; } z = compress2(dest_buf, &dest_len, src_buf, src_sz, *lu_priv->compressionFactor); if (z != Z_OK) { switch (z) { case Z_MEM_ERROR: MHVTL_ERR("Not enough memory to compress data"); break; case Z_BUF_ERROR: MHVTL_ERR("Not enough memory in destination " "buf to compress data"); break; case Z_DATA_ERROR: MHVTL_ERR("Input data corrupt / incomplete"); break; } sam_hardware_error(E_COMPRESSION_CHECK, sam_stat); return 0; } MHVTL_DBG(2, "Compression: Orig %d, after comp: %ld" ", Compression factor: %d", src_sz, (unsigned long)dest_len, *lu_priv->compressionFactor); rc = write_tape_block(dest_buf, src_len, dest_len, lu_priv->app_encr_info, ZLIB, null_wr, crc, sam_stat); free(dest_buf); lu_priv->bytesWritten_M += dest_len; lu_priv->bytesWritten_I += src_len; if (lu_priv->pm->drive_supports_LBP && lbp_method) { log_lbp_method(lbp_method); if (verify_lbp_crc(lbp_method, src_buf, src_sz, crc) < 0) { MHVTL_ERR("LBP mis-compare on write : Returning E_LOGICAL_BLOCK_GUARD_FAILED"); sam_hardware_error(E_LOGICAL_BLOCK_GUARD_FAILED, sam_stat); log_crc_options(lbp_method, src_buf, src_sz, crc); return 0; } } if (rc < 0) return 0; return src_len; } int writeBlock(struct scsi_cmd *cmd, uint32_t src_sz) { struct priv_lu_ssc *lu_priv; int src_len; uint64_t current_position; int64_t remaining_capacity; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint32_t lbp_sz = src_sz; int lbp_method = 0; lu_priv = (struct priv_lu_ssc *)cmd->lu->lu_private; src_len = 0; if (lu_priv->pm->drive_supports_LBP) { if (lu_priv->LBP_W) { MHVTL_DBG(2, "Write using LBP (CRC: %s)", (lu_priv->LBP_method == 0) ? "Off" : (lu_priv->LBP_method == 1) ? "RS-CRC" : (lu_priv->LBP_method == 2) ? "CRC32C" : "Invalid"); switch (lu_priv->LBP_method) { case 1: lbp_method = 1; lbp_sz = src_sz - 4; break; case 2: lbp_method = 2; lbp_sz = src_sz - 4; break; default: break; } } } /* Check if we hit EOT and fail before attempting to write */ current_position = current_tape_offset(); if (current_position >= lu_priv->max_capacity) { mam.remaining_capacity = 0L; MHVTL_DBG(1, "End of Medium - VOLUME_OVERFLOW/EOM"); sam_no_sense(VOLUME_OVERFLOW | SD_EOM, E_EOM, sam_stat); return src_len; } if (lu_priv->mamp->MediumType == MEDIA_TYPE_NULL) { /* Don't compress if null tape media */ src_len = writeBlock_nocomp(cmd, lbp_sz, TRUE, 0); } else if (*lu_priv->compressionFactor == MHVTL_NO_COMPRESSION) { /* No compression - use the no-compression function */ src_len = writeBlock_nocomp(cmd, lbp_sz, FALSE, lbp_method); } else { switch (lu_priv->compressionType) { case LZO: src_len = writeBlock_lzo(cmd, lbp_sz, FALSE, lbp_method); break; case ZLIB: src_len = writeBlock_zlib(cmd, lbp_sz, FALSE, lbp_method); break; default: src_len = writeBlock_nocomp(cmd, lbp_sz, FALSE, lbp_method); break; } } if (!src_len) { /* Set 'Read/Write error' TapeAlert flag */ uint64_t fg = TA_HARD | TA_WRITE; set_TapeAlert(fg); return 0; } current_position = current_tape_offset(); if ((lu_priv->pm->drive_supports_early_warning) && (current_position >= (uint64_t)lu_priv->early_warning_position)) { MHVTL_DBG(1, "End of Medium - Early Warning"); sam_no_sense(SD_EOM, NO_ADDITIONAL_SENSE, sam_stat); } else if ((lu_priv->pm->drive_supports_prog_early_warning) && (current_position >= (uint64_t)lu_priv->prog_early_warning_position)) { /* FIXME: Need to implement REW bit in Device Configuration Mode Page * REW == Report Early Warning */ MHVTL_DBG(1, "End of Medium - Programmable Early Warning"); sam_no_sense(SD_EOM, E_PROGRAMMABLE_EARLY_WARNING, sam_stat); } remaining_capacity = lu_priv->max_capacity - current_position; if (remaining_capacity < 0) remaining_capacity = 0L; put_unaligned_be64(remaining_capacity, &mam.remaining_capacity); return src_len; } ================================================ FILE: usr/mhvtl_log.c ================================================ /* * This handles any SCSI OP 'mode sense / mode select' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include "mhvtl_list.h" #include "logging.h" #include "vtllib.h" #include "ssc.h" #include "be_byteshift.h" #include "mhvtl_log.h" #define LOG_PG_HEADER(pageCode) \ {(uint8_t)(pageCode), 0x00, 0x00} #define LOG_PARAM(paramCode, flags, name) \ { \ (uint8_t)((paramCode) >> 8), \ (uint8_t)((paramCode) & 0xFF), \ (flags), \ sizeof(pg->name), \ }, \ .name const char *log_page_desc[0x38] = { [0x00 ... 0x37] = "Unsupported Log page", [SUPPORTED_LOG_PAGES] = "Supported Log pages", [BUFFER_UNDER_OVER_RUN] = "Buffer Under/Over Run", [WRITE_ERROR_COUNTER] = "Write Error Counter", [READ_ERROR_COUNTER] = "Read Error Counter", [READ_REVERSE_ERROR_COUNTER] = "Read Reverse Error Counter", [VERIFY_ERROR_COUNTER] = "Verify Error Counter", [NON_MEDIUM_ERROR_COUNTER] = "Non-Medium Error Counter", [LAST_n_ERROR] = "Last N Error", [FORMAT_STATUS] = "Format Status", [LAST_n_DEFERRED_ERROR] = "Last N Deferred Error", [SEQUENTIAL_ACCESS_DEVICE] = "Sequential Access Device", [TEMPERATURE_PAGE] = "Temperature Page", [START_STOP_CYCLE_COUNTER] = "Start/Stop Cycle Counter", [APPLICATION_CLIENT] = "Application Client", [SELFTEST_RESULTS] = "Selftest Results", [VOLUME_STATISTICS] = "Volume Statistics", [DEVICE_STATUS] = "VHF Device Status", [TAPE_ALERT] = "Tape Alert", [INFORMATIONAL_EXCEPTIONS] = "Informational Exceptions", [TAPE_USAGE] = "Tape Usage", [TAPE_CAPACITY] = "Tape Capacity", [DATA_COMPRESSION] = "Data Compression", [PERFORMANCE_CHARACTERISTICS] = "Performance Characteristics", }; struct log_pg_list *lookup_log_pg(struct list_head *l, uint8_t page, uint8_t subpage) { struct log_pg_list *log_pg; MHVTL_DBG(3, "fetching log page (0x%02x - 0x%02x)", page, subpage); list_for_each_entry(log_pg, l, siblings) { if (log_pg->log_page_num == page && log_pg->log_subpage_num == subpage) { MHVTL_DBG(2, "log page (0x%02x - 0x%02x) found : %s", page, subpage, log_page_desc[page]); return log_pg; } } /* If no page found, ignore subpage */ list_for_each_entry(log_pg, l, siblings) { if (log_pg->log_page_num == page) { MHVTL_DBG(2, "log page (0x%02x - 0x%02x) found but wrong supbage: %s", page, subpage, log_page_desc[page]); return log_pg; } } MHVTL_DBG(3, "log page (0x%02x - 0x%02x) not found", page, subpage); return NULL; } int alloc_log_page(struct lu_phy_attr *lu, uint8_t page, uint8_t subpage, init_pg_fn init_log_pg, size_t pg_size) { struct log_pg_list *log_pg; int creation = 0; MHVTL_DBG(3, "%p : Allocate log page (0x%02x - 0x%02x), size %d", &lu->log_pg, page, subpage, (int)pg_size); /* Getting the entry, create it if does not exist */ log_pg = lookup_log_pg(&lu->log_pg, page, subpage); if (!log_pg) { creation = 1; log_pg = malloc(sizeof(struct log_pg_list)); } if (log_pg) { log_pg->log_page_num = page; log_pg->log_subpage_num = subpage; log_pg->size = pg_size; log_pg->p = malloc(pg_size); if (log_pg->p) { init_log_pg(log_pg->p); put_unaligned_be16(pg_size - sizeof(struct log_pg_header), /* Setting the real len of the log page */ &((struct log_pg_header *)log_pg->p)->len); if (creation) list_add_tail(&log_pg->siblings, &lu->log_pg); return 0; } else { MHVTL_ERR("Unable to malloc log page buffer (%zu)", pg_size); free(log_pg); return -ENOMEM; } } MHVTL_ERR("Unable to malloc log page entry (%zu)", pg_size); return -ENOMEM; } void dealloc_all_log_pages(struct lu_phy_attr *lu) { struct log_pg_list *lp, *ln; list_for_each_entry_safe(lp, ln, &lu->log_pg, siblings) { MHVTL_DBG(2, "Removing %s", lp->description); free(lp->p); list_del(&lp->siblings); free(lp); } } static void init_log_write_err_counter(void *log_ptr) { struct ErrorCounter_pg *pg = log_ptr; *pg = (struct ErrorCounter_pg){ LOG_PG_HEADER(WRITE_ERROR_COUNTER), LOG_PARAM(0x0000, 0x60, err_correctedWODelay) = 0x00, LOG_PARAM(0x0001, 0x60, err_correctedWDelay) = 0x00, LOG_PARAM(0x0002, 0x60, totalReTry) = 0x00, LOG_PARAM(0x0003, 0x60, totalErrorsCorrected) = 0x00, LOG_PARAM(0x0004, 0x60, correctAlgorithm) = 0x00, LOG_PARAM(0x0005, 0x60, bytesProcessed) = 0x00, LOG_PARAM(0x0006, 0x60, uncorrectedErrors) = 0x00, LOG_PARAM(0x8000, 0x60, readErrorsSinceLast) = 0x00, LOG_PARAM(0x8001, 0x60, totalRawReadError) = 0x00, LOG_PARAM(0x8002, 0x60, totalDropoutError) = 0x00, LOG_PARAM(0x8003, 0x60, totalServoTracking) = 0x00, }; } int add_log_write_err_counter(struct lu_phy_attr *lu) { return alloc_log_page(lu, WRITE_ERROR_COUNTER, NO_SUBPAGE, init_log_write_err_counter, sizeof(struct ErrorCounter_pg)); } static void init_log_read_err_counter(void *log_ptr) { struct ErrorCounter_pg *pg = log_ptr; *pg = (struct ErrorCounter_pg){ LOG_PG_HEADER(READ_ERROR_COUNTER), LOG_PARAM(0x0000, 0x60, err_correctedWODelay) = 0x00, LOG_PARAM(0x0001, 0x60, err_correctedWDelay) = 0x00, LOG_PARAM(0x0002, 0x60, totalReTry) = 0x00, LOG_PARAM(0x0003, 0x60, totalErrorsCorrected) = 0x00, LOG_PARAM(0x0004, 0x60, correctAlgorithm) = 0x00, LOG_PARAM(0x0005, 0x60, bytesProcessed) = 0x00, LOG_PARAM(0x0006, 0x60, uncorrectedErrors) = 0x00, LOG_PARAM(0x8000, 0x60, readErrorsSinceLast) = 0x00, LOG_PARAM(0x8001, 0x60, totalRawReadError) = 0x00, LOG_PARAM(0x8002, 0x60, totalDropoutError) = 0x00, LOG_PARAM(0x8003, 0x60, totalServoTracking) = 0x00, }; } int add_log_read_err_counter(struct lu_phy_attr *lu) { return alloc_log_page(lu, READ_ERROR_COUNTER, NO_SUBPAGE, init_log_read_err_counter, sizeof(struct ErrorCounter_pg)); } static void init_log_sequential_access(void *log_ptr) { struct SequentialAccessDevice_pg *pg = log_ptr; *pg = (struct SequentialAccessDevice_pg){ LOG_PG_HEADER(SEQUENTIAL_ACCESS_DEVICE), LOG_PARAM(0x0000, 0x40, writeDataB4Compression) = 0x00, LOG_PARAM(0x0001, 0x40, writeDataAfCompression) = 0x00, LOG_PARAM(0x0002, 0x40, readDataB4Compression) = 0x00, LOG_PARAM(0x0003, 0x40, readDataAfCompression) = 0x00, LOG_PARAM(0x0004, 0x40, capacity_bop_eod) = 0x00, LOG_PARAM(0x0005, 0x40, capacity_bop_ew) = 0x00, LOG_PARAM(0x0006, 0x40, capacity_ew_leop) = 0x00, LOG_PARAM(0x0007, 0x40, capacity_bop_curr) = 0x00, LOG_PARAM(0x0008, 0x40, capacity_buffer) = 0x00, LOG_PARAM(0x0100, 0x40, TapeAlert) = 0x00, LOG_PARAM(0x8000, 0x40, mbytes_processed) = 0x00, LOG_PARAM(0x8001, 0x40, load_cycle) = 0x00, LOG_PARAM(0x8002, 0x40, clean_cycle) = 0x00, }; } int add_log_sequential_access(struct lu_phy_attr *lu) { return alloc_log_page(lu, SEQUENTIAL_ACCESS_DEVICE, NO_SUBPAGE, init_log_sequential_access, sizeof(struct SequentialAccessDevice_pg)); } static void init_log_temperature_page(void *log_ptr) { struct Temperature_pg *pg = log_ptr; *pg = (struct Temperature_pg){ LOG_PG_HEADER(TEMPERATURE_PAGE), LOG_PARAM(0x0000, 0x60, temperature) = 0x00, }; } int add_log_temperature_page(struct lu_phy_attr *lu) { return alloc_log_page(lu, TEMPERATURE_PAGE, NO_SUBPAGE, init_log_temperature_page, sizeof(struct Temperature_pg)); } static void init_log_selftest_results(void *log_ptr) { struct SelfTestResults_pg *pg = log_ptr; *pg = (struct SelfTestResults_pg){ LOG_PG_HEADER(SELFTEST_RESULTS), LOG_PARAM(0x0001, 0x00, data01) = {0}, LOG_PARAM(0x0002, 0x00, data02) = {0}, LOG_PARAM(0x0003, 0x00, data03) = {0}, LOG_PARAM(0x0004, 0x00, data04) = {0}, LOG_PARAM(0x0005, 0x00, data05) = {0}, LOG_PARAM(0x0006, 0x00, data06) = {0}, LOG_PARAM(0x0007, 0x00, data07) = {0}, LOG_PARAM(0x0008, 0x00, data08) = {0}, LOG_PARAM(0x0009, 0x00, data09) = {0}, LOG_PARAM(0x000a, 0x00, data0a) = {0}, LOG_PARAM(0x000b, 0x00, data0b) = {0}, LOG_PARAM(0x000c, 0x00, data0c) = {0}, LOG_PARAM(0x000d, 0x00, data0d) = {0}, LOG_PARAM(0x000e, 0x00, data0e) = {0}, LOG_PARAM(0x000f, 0x00, data0f) = {0}, LOG_PARAM(0x0010, 0x00, data10) = {0}, LOG_PARAM(0x0011, 0x00, data11) = {0}, LOG_PARAM(0x0012, 0x00, data12) = {0}, LOG_PARAM(0x0013, 0x00, data13) = {0}, LOG_PARAM(0x0014, 0x00, data14) = {0}, }; } int add_log_selftest_results(struct lu_phy_attr *lu) { return alloc_log_page(lu, SELFTEST_RESULTS, NO_SUBPAGE, init_log_selftest_results, sizeof(struct SelfTestResults_pg)); } static void init_log_volume_statistics(void *log_ptr) { struct VolumeStatistics_pg *pg = log_ptr; *pg = (struct VolumeStatistics_pg){ LOG_PG_HEADER(VOLUME_STATISTICS), LOG_PARAM(0x0000, 0x03, PageValid) = 0x01, LOG_PARAM(0x0001, 0x03, VolumeMounts) = 0x00, LOG_PARAM(0x0002, 0x03, VolumeDatasetsWritten) = 0x00, LOG_PARAM(0x0003, 0x03, RecoveredWriteDataErrors) = 0x00, LOG_PARAM(0x0004, 0x03, UnrecoveredWriteDataErrors) = 0x00, LOG_PARAM(0x0005, 0x03, WriteServoErrors) = 0x00, LOG_PARAM(0x0006, 0x03, UnrecoveredWriteServoErrors) = 0x00, LOG_PARAM(0x0007, 0x03, VolumeDatasetsRead) = 0x00, LOG_PARAM(0x0008, 0x03, RecoveredReadErrors) = 0x00, LOG_PARAM(0x0009, 0x03, UnrecoveredReadErrors) = 0x00, LOG_PARAM(0x000C, 0x03, LastMountUnrecoveredWriteErrors) = 0x00, LOG_PARAM(0x000D, 0x03, LastMountUnrecoveredReadErrors) = 0x00, LOG_PARAM(0x000E, 0x03, LastMountMBWritten) = 0x00, LOG_PARAM(0x000F, 0x03, LastMountMBRead) = 0x00, LOG_PARAM(0x0010, 0x03, LifetimeMBWritten) = 0x00, LOG_PARAM(0x0011, 0x03, LifetimeMBRead) = 0x00, LOG_PARAM(0x0012, 0x03, LastLoadWriteCompressionRatio) = 0x00, LOG_PARAM(0x0013, 0x03, LastLoadReadCompressionRatio) = 0x00, LOG_PARAM(0x0014, 0x03, MediumMountTime) = {0}, LOG_PARAM(0x0015, 0x03, MediumReadyTime) = {0}, LOG_PARAM(0x0016, 0x03, TotalNativeCapacity) = 0xfffffffe, LOG_PARAM(0x0017, 0x03, TotalUsedNativeCapacity) = 0xfffffffe, LOG_PARAM(0x0018, 0x03, AppDesignCapacity) = 0x00, LOG_PARAM(0x0019, 0x03, VolumeLifetimeRemaining) = 0x00, LOG_PARAM(0x0040, 0x01, VolumeSerialNumber) = {0}, LOG_PARAM(0x0041, 0x01, TapeLotIdentifier) = {0}, LOG_PARAM(0x0042, 0x01, VolumeBarcode) = {0}, LOG_PARAM(0x0043, 0x01, VolumeManufacturer) = {0}, LOG_PARAM(0x0044, 0x01, VolumeLicenseCode) = {0}, LOG_PARAM(0x0045, 0x01, VolumePersonality) = {0}, LOG_PARAM(0x0080, 0x03, WriteProtect) = 0x00, LOG_PARAM(0x0081, 0x03, WORM) = 0x00, LOG_PARAM(0x0082, 0x03, TempExceeded) = 0x00, LOG_PARAM(0x0101, 0x03, BOMPasses) = 0x00, LOG_PARAM(0x0102, 0x03, MOTPasses) = 0x00, LOG_PARAM(0x0200, 0x03, FirstEncryptedLogicalObj) = {{{0}}}, LOG_PARAM(0x0201, 0x03, FirstUnencryptedLogicalObj) = {{{0}}}, LOG_PARAM(0x0202, 0x03, ApproxNativeCapacityPartition) = {{{0}}}, LOG_PARAM(0x0203, 0x03, ApproxUsedNativeCapacityPartition) = {{{0}}}, LOG_PARAM(0x0204, 0x03, RemainingCapacityToEWPartition) = {{{0}}}, }; for (int k = 0; k < MAX_PARTITIONS; k++) { pg->FirstEncryptedLogicalObj[k].header.len = sizeof(struct partition_record_size6) - 1; put_unaligned_be16(k, &pg->FirstEncryptedLogicalObj[k].header.partition_no); pg->FirstUnencryptedLogicalObj[k].header.len = sizeof(struct partition_record_size6) - 1; put_unaligned_be16(k, &pg->FirstUnencryptedLogicalObj[k].header.partition_no); pg->ApproxNativeCapacityPartition[k].header.len = sizeof(struct partition_record_size4) - 1; put_unaligned_be16(k, &pg->ApproxNativeCapacityPartition[k].header.partition_no); pg->ApproxUsedNativeCapacityPartition[k].header.len = sizeof(struct partition_record_size4) - 1; put_unaligned_be16(k, &pg->ApproxUsedNativeCapacityPartition[k].header.partition_no); pg->RemainingCapacityToEWPartition[k].header.len = sizeof(struct partition_record_size4) - 1; put_unaligned_be16(k, &pg->RemainingCapacityToEWPartition[k].header.partition_no); }; } int add_log_volume_statistics(struct lu_phy_attr *lu) { return alloc_log_page(lu, VOLUME_STATISTICS, NO_SUBPAGE, init_log_volume_statistics, sizeof(struct VolumeStatistics_pg)); } static void init_log_tape_alert(void *log_ptr) { struct TapeAlert_pg *pg = log_ptr; *pg = (struct TapeAlert_pg){ LOG_PG_HEADER(TAPE_ALERT), }; for (int i = 0; i < 64; i++) { pg->TapeAlert[i] = (struct TapeAlert_flag){{0x00, i + 1, 0xc0, 1}, 0x00}; } } int add_log_tape_alert(struct lu_phy_attr *lu) { return alloc_log_page(lu, TAPE_ALERT, NO_SUBPAGE, init_log_tape_alert, sizeof(struct TapeAlert_pg)); } static void init_log_tape_usage(void *log_ptr) { struct TapeUsage_pg *pg = log_ptr; *pg = (struct TapeUsage_pg){ LOG_PG_HEADER(TAPE_USAGE), LOG_PARAM(0x0001, 0xc0, volumeMounts) = 0x00, LOG_PARAM(0x0002, 0xc0, volumeDatasetsWritten) = 0x00, LOG_PARAM(0x0003, 0xc0, volWriteRetries) = 0x00, LOG_PARAM(0x0004, 0xc0, volWritePerms) = 0x00, LOG_PARAM(0x0005, 0xc0, volSuspendedWrites) = 0x00, LOG_PARAM(0x0006, 0xc0, volFatalSuspendedWrites) = 0x00, LOG_PARAM(0x0007, 0xc0, volDatasetsRead) = 0x00, LOG_PARAM(0x0008, 0xc0, volReadRetries) = 0x00, LOG_PARAM(0x0009, 0xc0, volReadPerms) = 0x00, LOG_PARAM(0x000a, 0xc0, volSuspendedReads) = 0x00, LOG_PARAM(0x000b, 0xc0, volFatalSuspendedReads) = 0x00, }; } int add_log_tape_usage(struct lu_phy_attr *lu) { return alloc_log_page(lu, TAPE_USAGE, NO_SUBPAGE, init_log_tape_usage, sizeof(struct TapeUsage_pg)); } static void init_log_device_status(void *log_ptr) { struct DeviceStatus_pg *pg = log_ptr; *pg = (struct DeviceStatus_pg){ LOG_PG_HEADER(DEVICE_STATUS), LOG_PARAM(0x0000, 0x03, vhf) = {{0x01, 0x00, 0x00, 0x01}}, }; } int add_log_device_status(struct lu_phy_attr *lu) { return alloc_log_page(lu, DEVICE_STATUS, NO_SUBPAGE, init_log_device_status, sizeof(struct DeviceStatus_pg)); } static struct DeviceStatus_pg *lookup_device_status_pg(void) { struct log_pg_list *log_pg; log_pg = lookup_log_pg(&lunit.log_pg, DEVICE_STATUS, NO_SUBPAGE); if (!log_pg) return NULL; return (struct DeviceStatus_pg *)log_pg->p; } static void init_log_tape_capacity(void *log_ptr) { struct TapeCapacity_pg *pg = log_ptr; *pg = (struct TapeCapacity_pg){ LOG_PG_HEADER(TAPE_CAPACITY), LOG_PARAM(0x0001, 0xc0, partition0remaining) = 0xfffffffe, LOG_PARAM(0x0002, 0xc0, partition1remaining) = 0xfffffffe, LOG_PARAM(0x0003, 0xc0, partition0maximum) = 0xfffffffe, LOG_PARAM(0x0004, 0xc0, partition1maximum) = 0xfffffffe, }; } int add_log_tape_capacity(struct lu_phy_attr *lu) { return alloc_log_page(lu, TAPE_CAPACITY, NO_SUBPAGE, init_log_tape_capacity, sizeof(struct TapeCapacity_pg)); } static void init_log_data_compression(void *log_ptr) { struct DataCompression_pg *pg = log_ptr; *pg = (struct DataCompression_pg){ LOG_PG_HEADER(DATA_COMPRESSION), LOG_PARAM(0x0000, 0x40, ReadCompressionRatio) = 0x00, LOG_PARAM(0x0001, 0x40, WriteCompressionRatio) = 0x00, LOG_PARAM(0x0002, 0x40, MBytesToServer) = 0x00, LOG_PARAM(0x0003, 0x40, BytesToServer) = 0x00, LOG_PARAM(0x0004, 0x40, MBytesReadFromTape) = 0x00, LOG_PARAM(0x0005, 0x40, BytesReadFromTape) = 0x00, LOG_PARAM(0x0006, 0x40, MBytesFromServer) = 0x00, LOG_PARAM(0x0007, 0x40, BytesFromServer) = 0x00, LOG_PARAM(0x0008, 0x40, MBytesWrittenToTape) = 0x00, LOG_PARAM(0x0009, 0x40, BytesWrittenToTape) = 0x00, }; } int add_log_data_compression(struct lu_phy_attr *lu) { return alloc_log_page(lu, DATA_COMPRESSION, NO_SUBPAGE, init_log_data_compression, sizeof(struct DataCompression_pg)); } static void init_log_performance_characteristics(void *log_ptr) { struct PerformanceCharacteristics_pg *pg = log_ptr; *pg = (struct PerformanceCharacteristics_pg){ LOG_PG_HEADER(PERFORMANCE_CHARACTERISTICS), LOG_PARAM(0x0000, 0x00, DriveEfficiency) = 0x00, }; } int add_log_performance_characteristics(struct lu_phy_attr *lu) { return alloc_log_page(lu, PERFORMANCE_CHARACTERISTICS, NO_SUBPAGE, init_log_performance_characteristics, sizeof(struct PerformanceCharacteristics_pg)); } /* Update MAM Accessible bit in LogPage 0x11 */ void set_lp_11_macc(int flag) { struct DeviceStatus_pg *lp = lookup_device_status_pg(); if (lp) lp->vhf.b4.MACC = flag; } void set_lp11_compression(int flag) { struct DeviceStatus_pg *lp = lookup_device_status_pg(); if (lp) lp->vhf.b4.CMPR = flag; } void set_lp_11_crqst(int flag) { struct DeviceStatus_pg *lp = lookup_device_status_pg(); if (lp) lp->vhf.b4.CRQST = flag; } void set_lp_11_crqrd(int flag) { struct DeviceStatus_pg *lp = lookup_device_status_pg(); if (lp) lp->vhf.b4.CRQRD = flag; } /* Update WriteProtect bit in LogPage 0x11 */ void set_lp_11_wp(int flag) { struct DeviceStatus_pg *lp = lookup_device_status_pg(); if (lp) lp->vhf.b4.WRTP = flag; } void set_lp11_medium_present(int flag) { struct DeviceStatus_pg *lp = lookup_device_status_pg(); if (!lp) return; lp->vhf.b5.MPRSNT = flag; if (!flag) { /* Clearing bit - also set state to unloaded */ set_lp_11_macc(0); /* MAM Accessible */ set_current_state(MHVTL_STATE_UNLOADED); } } #define SET_VOLSTAT_PARAM_H(paramCode, paramFlags, paramStruct) \ do { \ put_unaligned_be16((paramCode), header); \ ((struct pc_header *)header)->flags = (paramFlags); \ ((struct pc_header *)header)->len = mam.num_partitions * sizeof(paramStruct); \ for (i = 0; i < mam.num_partitions; ++i) { \ paramStruct *p_header = ((paramStruct *)(header + sizeof(struct pc_header))) + i; \ p_header->header.len = sizeof(paramStruct) - 1; \ put_unaligned_be16(i, &p_header->header.partition_no); \ } \ } while (0) #define SET_VOLSTAT_PARAM_H4(paramCode, paramFlags) \ SET_VOLSTAT_PARAM_H((paramCode), (paramFlags), struct partition_record_size4) #define SET_VOLSTAT_PARAM_H6(paramCode, paramFlags) \ SET_VOLSTAT_PARAM_H((paramCode), (paramFlags), struct partition_record_size6) /* Rewriting the page with the real number of partitions */ void update_VolumeStatistics(struct VolumeStatistics_pg *pg, struct priv_lu_ssc *lu_priv) { uint8_t *header; uint64_t cap __attribute__((unused)); /* fixme : should be used instead of telling max cap */ int i; /* First partition dependant parameter */ memset(&pg->h_FirstEncryptedLogicalObj, 0, sizeof(struct VolumeStatistics_pg) - offsetof(struct VolumeStatistics_pg, h_FirstEncryptedLogicalObj)); /* h_FirstEncryptedLogicalObj */ header = (uint8_t *)pg + offsetof(struct VolumeStatistics_pg, h_FirstEncryptedLogicalObj); SET_VOLSTAT_PARAM_H6(0x0200, 0x03); for (i = 0; i < mam.num_partitions; ++i) { struct partition_record_size6 *p_header = ((struct partition_record_size6 *)(header + sizeof(struct pc_header))) + i; put_unaligned_be48(0, &p_header->data); } /* h_FirstUnencryptedLogicalObj */ header += sizeof(struct pc_header) + ((struct pc_header *)header)->len; SET_VOLSTAT_PARAM_H6(0x0201, 0x03); for (i = 0; i < mam.num_partitions; ++i) { struct partition_record_size6 *p_header = ((struct partition_record_size6 *)(header + sizeof(struct pc_header))) + i; put_unaligned_be48(0, &p_header->data); } /* h_ApproxNativeCapacityPartition */ header += sizeof(struct pc_header) + ((struct pc_header *)header)->len; SET_VOLSTAT_PARAM_H4(0x0202, 0x03); if (get_tape_load_status() == TAPE_LOADED) { for (i = 0; i < mam.num_partitions; ++i) { struct partition_record_size4 *p_header = ((struct partition_record_size4 *)(header + sizeof(struct pc_header))) + i; cap = get_unaligned_be64(&mam.max_capacity) / lu_priv->capacity_unit; put_unaligned_be32(0xfffffffe, &p_header->data); MHVTL_DBG(1, "approx native capacity for partition %d : %u", i, get_unaligned_be32(&p_header->data)); } } /* h_ApproxUsedNativeCapacityPartition */ header += sizeof(struct pc_header) + ((struct pc_header *)header)->len; SET_VOLSTAT_PARAM_H4(0x0203, 0x03); for (i = 0; i < mam.num_partitions; ++i) { struct partition_record_size4 *p_header = ((struct partition_record_size4 *)(header + sizeof(struct pc_header))) + i; put_unaligned_be32(0xfffffffe, &p_header->data); MHVTL_DBG(1, "approx used native capacity for partition %d : %u", i, get_unaligned_be32(&p_header->data)); } /* h_RemainingCapacityToEWPartition */ header += sizeof(struct pc_header) + ((struct pc_header *)header)->len; SET_VOLSTAT_PARAM_H4(0x0204, 0x03); if (get_tape_load_status() == TAPE_LOADED) { for (i = 0; i < mam.num_partitions; ++i) { struct partition_record_size4 *p_header = ((struct partition_record_size4 *)(header + sizeof(struct pc_header))) + i; cap = get_unaligned_be64(&mam.remaining_capacity) / lu_priv->capacity_unit; put_unaligned_be32(0xfffffffe, &p_header->data); MHVTL_DBG(1, "remaining capacity to EW for partition %d : %u", i, get_unaligned_be32(&p_header->data)); } } } /* Only valid for SSC devices */ int update_TapeAlert(uint64_t flags) { struct SequentialAccessDevice_pg *sad; struct log_pg_list *l; uint64_t ta; l = lookup_log_pg(&lunit.log_pg, SEQUENTIAL_ACCESS_DEVICE, NO_SUBPAGE); if (l) { sad = (struct SequentialAccessDevice_pg *)l->p; ta = get_unaligned_be64(&sad->TapeAlert); MHVTL_DBG(2, "Adding flags: %.8x %.8x to %.8x %.8x", (uint32_t)(flags >> 32) & 0xffffffff, (uint32_t)flags & 0xffffffff, (uint32_t)(ta >> 32) & 0xffffffff, (uint32_t)ta & 0xffffffff); set_TapeAlert(ta | flags); if (flags & 1L << 19) /* Clean Now (required) */ set_lp_11_crqrd(1); else set_lp_11_crqrd(0); if (flags & 1L << 20) /* Clean Periodic (requested) */ set_lp_11_crqst(1); else set_lp_11_crqst(0); return 0; } return -1; } int set_TapeAlert(uint64_t flags) { struct SequentialAccessDevice_pg *sad; struct TapeAlert_pg *ta; struct DeviceStatus_pg *ds; struct log_pg_list *l; int i; /* Set LP 0x11 'TAFC' bit (TapeAlert Flag Changed) */ ds = lookup_device_status_pg(); if (ds) { if (flags) { ds->vhf.b7.TAFC = 1; MHVTL_DBG(2, "Setting TAFC bit true"); } else { ds->vhf.b7.TAFC = 0; MHVTL_DBG(3, "Not setting TAFC bit as flags is zero"); } } l = lookup_log_pg(&lunit.log_pg, TAPE_ALERT, NO_SUBPAGE); if (!l) return -1; ta = (struct TapeAlert_pg *)l->p; MHVTL_DBG(2, "Setting TapeAlert flags 0x%.8x %.8x", (uint32_t)(flags >> 32) & 0xffffffff, (uint32_t)flags & 0xffffffff); for (i = 0; i < 64; i++) ta->TapeAlert[i].value = (flags & (1ull << i)) ? 1 : 0; /* Don't treat not having a SEQUENTIAL ACCESS DEVICE log page * as fatal (e.g. SMC devices) */ l = lookup_log_pg(&lunit.log_pg, SEQUENTIAL_ACCESS_DEVICE, NO_SUBPAGE); if (l) { sad = (struct SequentialAccessDevice_pg *)l->p; put_unaligned_be64(flags, &sad->TapeAlert); } if (flags & 1L << 19) /* Clean Now (required) */ set_lp_11_crqrd(1); else set_lp_11_crqrd(0); if (flags & 1L << 20) /* Clean Periodic (requested) */ set_lp_11_crqst(1); else set_lp_11_crqst(0); return 0; } void update_TapeUsage(struct TapeUsage_pg *b) { uint64_t datasets = count_filemarks(-1); uint64_t load_count; /* if we have more than 1 filemark, * most apps write 2 filemarks to flag EOD * So, lets subtract one from the filemark count to * present a more accurate 'Data Set' count */ if (datasets > 1) datasets--; load_count = get_unaligned_be64(&lu_ssc.mamp->LoadCount); put_unaligned_be32(load_count, &b->volumeMounts); put_unaligned_be64(datasets, &b->volumeDatasetsWritten); } void update_TapeCapacity(struct TapeCapacity_pg *pg) { uint64_t cap __attribute__((unused)); /* fixme : should be used instead of telling max cap */ if (get_tape_load_status() == TAPE_LOADED) { cap = get_unaligned_be64(&mam.remaining_capacity) / lu_ssc.capacity_unit; put_unaligned_be32(0xfffffffe, &pg->partition0remaining); put_unaligned_be32(0xfffffffe, &pg->partition1remaining); cap = get_unaligned_be64(&mam.max_capacity) / lu_ssc.capacity_unit; put_unaligned_be32(0xfffffffe, &pg->partition0maximum); put_unaligned_be32(0xfffffffe, &pg->partition1maximum); } else { pg->partition0remaining = 0; pg->partition0maximum = 0; pg->partition1remaining = 0; pg->partition1maximum = 0; } } void update_SequentialAccessDevice(struct SequentialAccessDevice_pg *sa) { put_unaligned_be64(lu_ssc.bytesWritten_I, &sa->writeDataB4Compression); put_unaligned_be64(lu_ssc.bytesWritten_M, &sa->writeDataAfCompression); put_unaligned_be64(lu_ssc.bytesRead_M, &sa->readDataB4Compression); put_unaligned_be64(lu_ssc.bytesRead_I, &sa->readDataAfCompression); /* Values in MBytes */ if (get_tape_load_status() == TAPE_LOADED) { put_unaligned_be32(lu_ssc.max_capacity >> 20, &sa->capacity_bop_eod); put_unaligned_be32(lu_ssc.early_warning_position >> 20, &sa->capacity_bop_ew); put_unaligned_be32(lu_ssc.early_warning_sz >> 20, &sa->capacity_ew_leop); put_unaligned_be32(current_tape_offset() >> 20, &sa->capacity_bop_curr); } else { put_unaligned_be32(0xffffffff, &sa->capacity_bop_eod); put_unaligned_be32(0xffffffff, &sa->capacity_bop_ew); put_unaligned_be32(0xffffffff, &sa->capacity_ew_leop); put_unaligned_be32(0xffffffff, &sa->capacity_bop_curr); } } void set_current_state(int s) { struct DeviceStatus_pg *lp = lookup_device_status_pg(); current_state = s; /* Now translate the 'mhVTL' state into DT Device Activity values */ if (lp) { switch (s) { case MHVTL_STATE_UNLOADED: lp->vhf.b6 = 0; break; case MHVTL_STATE_LOADING: lp->vhf.b6 = 2; break; case MHVTL_STATE_LOADING_CLEAN: lp->vhf.b6 = 1; break; case MHVTL_STATE_LOADING_WORM: lp->vhf.b6 = 2; break; case MHVTL_STATE_LOADED: lp->vhf.b6 = 0; break; case MHVTL_STATE_LOADED_IDLE: lp->vhf.b6 = 0; break; case MHVTL_STATE_LOAD_FAILED: lp->vhf.b6 = 0; set_lp_11_macc(0); /* MAM Accessible - False */ break; case MHVTL_STATE_REWIND: lp->vhf.b6 = 0x8; break; case MHVTL_STATE_POSITIONING: lp->vhf.b6 = 0x7; break; case MHVTL_STATE_LOCATE: lp->vhf.b6 = 0x7; break; case MHVTL_STATE_READING: lp->vhf.b6 = 0x5; break; case MHVTL_STATE_WRITING: lp->vhf.b6 = 0x6; break; case MHVTL_STATE_UNLOADING: lp->vhf.b6 = 0x3; break; case MHVTL_STATE_ERASE: lp->vhf.b6 = 0x9; break; case MHVTL_STATE_VERIFY: lp->vhf.b6 = 0x4; break; } } } /* FIXME: Add VHF log page stuff here */ int get_tape_load_status(void) { return lu_ssc.load_status; } void set_tape_load_status(int s) { struct DeviceStatus_pg *lp = lookup_device_status_pg(); lu_ssc.load_status = s; if (lp) { switch (s) { case TAPE_UNLOADED: lp->vhf.b5.INXTN = 1; /* In transition */ lp->vhf.b5.MSTD = 0; /* Medium seated */ lp->vhf.b5.MTHRD = 0; /* Medium threaded */ lp->vhf.b5.MOUNTED = 0; /* Medium mounted */ lp->vhf.b5.MPRSNT = 0; /* Medium Present */ lp->vhf.b5.RAA = 1; /* Robotic access allowed */ lp->vhf.b5.INXTN = 0; /* Completed updates */ set_lp_11_macc(0); /* MAM Accessible */ break; case TAPE_LOADED: lp->vhf.b5.INXTN = 1; /* In transition */ lp->vhf.b5.MSTD = 1; /* Medium seated */ lp->vhf.b5.MTHRD = 1; /* Medium threaded */ lp->vhf.b5.MOUNTED = 1; /* Medium mounted */ lp->vhf.b5.MPRSNT = 1; /* Medium Present */ lp->vhf.b5.RAA = 1; /* Robotic access allowed */ lp->vhf.b5.INXTN = 0; /* Completed updates */ set_lp_11_macc(1); /* MAM Accessible */ break; case TAPE_LOADING: lp->vhf.b5.INXTN = 1; /* In transition */ lp->vhf.b5.MSTD = 1; /* Medium seated */ lp->vhf.b5.MTHRD = 0; /* Medium threaded */ lp->vhf.b5.MOUNTED = 0; /* Medium mounted */ lp->vhf.b5.MPRSNT = 1; /* Medium Present */ lp->vhf.b5.RAA = 1; /* Robotic access allowed */ lp->vhf.b5.INXTN = 0; /* Completed updates */ set_lp_11_macc(0); /* MAM Accessible */ break; } } } ================================================ FILE: usr/mode.c ================================================ /* * This handles any SCSI OP 'mode sense / mode select' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include "mhvtl_scsi.h" #include "mhvtl_list.h" #include "logging.h" #include "vtllib.h" #include "ssc.h" #include "smc.h" #include "be_byteshift.h" #include "mode.h" #include "mhvtl_log.h" static char *mode_rw_error_recover = "Read/Write Error Recovery"; static char *mode_disconnect_reconnect = "Disconnect/Reconnect"; static char *mode_control = "Control"; static char *mode_control_extension = "Control Extension"; static char *mode_control_data_protection = "Control Data Protection"; static char *mode_data_compression = "Data Compression"; static char *mode_device_configuration = "Device Configuration"; static char *mode_device_configuration_extension = "Device Configuration Extension"; static char *mode_medium_partition = "Medium Partition"; static char *mode_power_condition = "Power Condition"; static char *mode_information_exception = "Information Exception"; static char *mode_medium_configuration = "Medium Configuration"; static char *mode_vendor_24h = "Vendor (IBM) unique page 24h" " - Advise ENCRYPTION Capable device"; static char *mode_vendor_25h = "Vendor (IBM) unique page 25h" " - Early Warning"; static char *mode_encryption_mode = "Encryption Mode"; static char *mode_behaviour_configuration = "Behaviour Configuration"; static char *mode_ait_device_configuration = "AIT Device Configuration"; static char *mode_element_address = "Element Address"; static char *mode_transport_geometry = "Transport Geometry"; static char *mode_device_capabilities = "Device Capabilities"; static char *drive_configuration_page = "STK Vendor-Unique Drive Configuration"; struct mode *lookup_mode_pg(struct list_head *m, uint8_t page, uint8_t subpage) { struct mode *mp; MHVTL_DBG(3, "fetching mode page (0x%02x - 0x%02x)", page, subpage); list_for_each_entry(mp, m, siblings) { if (mp->pcode == page && mp->subpcode == subpage) { MHVTL_DBG(3, "mode page (0x%02x - 0x%02x) found : %s", page, subpage, mp->description); return mp; } } MHVTL_DBG(3, "mode page (0x%02x - 0x%02x) not found", page, subpage); return NULL; } /* * Used by mode sense/mode select struct. * * Allocate 'size' bytes & init to 0 * set first 2 bytes: * byte[0] = pcode * byte[1] = size - sizeof(byte[0]) * * Return pointer to mode structure being init. or NULL if alloc failed */ static struct mode *alloc_mode_page(struct list_head *m, uint8_t pcode, uint8_t subpcode, int size) { struct mode *mp; MHVTL_DBG(3, "Allocating %d bytes for (%02x/%02x)", size, pcode, subpcode); mp = lookup_mode_pg(m, pcode, subpcode); if (!mp) { /* Create a new entry */ mp = (struct mode *)zalloc(sizeof(struct mode)); } if (mp) { mp->pcodePointer = (uint8_t *)zalloc(size); if (mp->pcodePointer) { /* If ! null, set size of data */ mp->pcode = pcode; mp->subpcode = subpcode; mp->pcodeSize = size; /* Allocate a 'changeable bitmap' mode page info */ mp->pcodePointerBitMap = zalloc(size); if (!mp->pcodePointerBitMap) { free(mp); MHVTL_ERR("Mode Pointer bitmap: Unable to malloc(%d)", size); return NULL; } list_add_tail(&mp->siblings, m); return mp; } else { MHVTL_ERR("Unable to malloc(%d)", size); free(mp); } } return NULL; } void dealloc_all_mode_pages(struct lu_phy_attr *lu) { struct mode *mp, *mn; list_for_each_entry_safe(mp, mn, &lu->mode_pg, siblings) { MHVTL_DBG(2, "Removing %s", mp->description); free(mp->pcodePointer); free(mp->pcodePointerBitMap); list_del(&mp->siblings); free(mp); } } /* * READ/WRITE Error Recovery * SSC3-8.3.5 */ int add_mode_page_rw_err_recovery(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; pcode = MODE_RW_ERROR_RECOVER; subpcode = 0; size = 12; mode_pg = &lu->mode_pg; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_rw_error_recover, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->description = mode_rw_error_recover; return 0; } /* * Disconnect / Reconnect * SPC3-7.4.8 */ int add_mode_disconnect_reconnect(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_DISCONNECT_RECONNECT; subpcode = 0; size = 0x0e + 2; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_disconnect_reconnect, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[2] = 50; /* Buffer full ratio */ mp->pcodePointer[3] = 50; /* Buffer empty ratio */ mp->pcodePointer[10] = 4; mp->description = mode_disconnect_reconnect; return 0; } /* * Control * SPC3 */ int add_mode_control(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_CONTROL; subpcode = 0; size = 12; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_control, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->description = mode_control; return 0; } /* * Control Extension 0x0A/0x01 * SPC3 */ int add_mode_control_extension(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_CONTROL; subpcode = 1; size = 0x1e; /* 0x1c + 2 for page/subpage code */ MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_control_extension, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = subpcode; put_unaligned_be16(size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]), &mp->pcodePointer[2]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[1]; mp->pcodePointerBitMap[1] = mp->pcodePointer[2]; mp->description = mode_control_extension; return 0; } /* * Control Data Protection Mode page 0x0A/0xF0 * IBM Ultrium Logical Block Protection */ int add_mode_control_data_protection(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_CONTROL; subpcode = 0xf0; size = 0x1e; /* 0x1c + two bytes for page/subpage */ MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_control_extension, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; MHVTL_DBG(3, "Added mode page %s (%02x/%02x)", mode_control_extension, pcode, subpcode); mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = subpcode; put_unaligned_be16(size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]), &mp->pcodePointer[2]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[1]; mp->pcodePointerBitMap[1] = mp->pcodePointer[2]; mp->description = mode_control_data_protection; /* Default to off */ mp->pcodePointer[4] = 0x00; /* LBP Method: 0 off, 1 Reed-Solomon CRC, 2 CRC32C */ mp->pcodePointer[5] = 0x04; /* LBP length - 32bit CRC */ mp->pcodePointer[6] = 0; /* LBP on write and LBP on read */ /* And define changeable bits */ mp->pcodePointerBitMap[4] = 0x03; mp->pcodePointerBitMap[5] = 0x07; mp->pcodePointerBitMap[6] = 0xc0; return 0; } /* * Data Compression * SSC3-8.3.2 */ #define COMPRESSION_TYPE 0x10 int add_mode_data_compression(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_DATA_COMPRESSION; subpcode = 0; size = 16; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_data_compression, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[2] = 0xc0; /* Set data compression enable */ mp->pcodePointer[3] = 0x80; /* Set data decompression enable */ put_unaligned_be32(COMPRESSION_TYPE, &mp->pcodePointer[4]); put_unaligned_be32(COMPRESSION_TYPE, &mp->pcodePointer[8]); /* Changeable fields */ mp->pcodePointerBitMap[2] = 0xc0; /* DCE & DCC */ mp->pcodePointerBitMap[3] = 0x80; /* DDE bit */ put_unaligned_be32(0xffffffff, &mp->pcodePointer[4]); /* Comp alg */ put_unaligned_be32(0xffffffff, &mp->pcodePointer[8]); /* De-comp alg */ mp->description = mode_data_compression; return 0; } void set_mode_compression(struct scsi_cmd *cmd, uint8_t *p) { struct lu_phy_attr *lu = cmd->lu; struct priv_lu_ssc *lu_priv = lu->lu_private; struct ssc_personality_template *pm = lu_priv->pm; int dce = p[2] & 0x80; MHVTL_DBG(2, " Data Compression Enable : %s (0x%02x)", (p[2] & 0x80) ? "Yes" : "No", p[2]); MHVTL_DBG(2, " Data Compression Capable : %s", (p[2] & 0x40) ? "Yes" : "No"); MHVTL_DBG(2, " Data DeCompression Enable : %s (0x%02x)", (p[3] & 0x80) ? "Yes" : "No", p[3]); MHVTL_DBG(2, " Compression Algorithm : 0x%04x", get_unaligned_be32(&p[4])); MHVTL_DBG(2, " DeCompression Algorithm : 0x%04x", get_unaligned_be32(&p[8])); MHVTL_DBG(2, " Report Exception on Decompression: 0x%02x", (p[3] & 0x6) >> 5); if (dce) { /* Data Compression Enable bit set */ MHVTL_DBG(1, " Setting compression"); if (pm->set_compression) { pm->set_compression(&lu->mode_pg, lu_priv->configCompressionFactor); set_lp11_compression(1); /* Update LogPage 11 compression bit */ } } else { MHVTL_DBG(1, " Clearing compression"); if (pm->clear_compression) { pm->clear_compression(&lu->mode_pg); set_lp11_compression(0); /* Update LogPage 11 compression bit */ } } } /* * Device Configuration * SSC3-8.3.3 */ int add_mode_device_configuration(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; struct priv_lu_ssc *ssc; uint8_t pcode; uint8_t subpcode; uint8_t size; ssc = (struct priv_lu_ssc *)lu->lu_private; mode_pg = &lu->mode_pg; pcode = MODE_DEVICE_CONFIGURATION; subpcode = 0; size = 16; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_device_configuration, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); mp->pcodePointer[3] = 0; /* Active partition, default = 0 */ /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[7] = 0x64; /* Write delay (100mS intervals) */ mp->pcodePointer[8] = 0x40; /* Block Identifiers supported */ mp->pcodePointer[10] = 0x18; /* Enable EOD & Sync at early warning */ mp->pcodePointer[14] = ssc->configCompressionFactor; mp->pcodePointer[15] = 0x80; /* WTRE (WORM handling) */ mp->pcodePointerBitMap[14] = 0xff; /* Compression is changeable */ /* Set pointer for compressionFactor to correct location in * mode page struct */ ssc->compressionFactor = &mp->pcodePointer[14]; mp->description = mode_device_configuration; return 0; } void set_device_configuration(struct scsi_cmd *cmd, uint8_t *p) { struct lu_phy_attr *lu = cmd->lu; struct priv_lu_ssc *lu_priv = cmd->lu->lu_private; struct ssc_personality_template *pm = lu_priv->pm; MHVTL_DBG(2, " Report Early Warning : %s", (p[8] & 0x01) ? "Yes" : "No"); MHVTL_DBG(2, " Software Write Protect : %s", (p[10] & 0x04) ? "Yes" : "No"); MHVTL_DBG(2, " WORM Tamper Read Enable: %s", (p[15] & 0x80) ? "Yes" : "No"); MHVTL_DBG(2, " Setting device compression Algorithm"); if (p[14]) { /* Select Data Compression Alg */ MHVTL_DBG(2, " Mode Select->Setting compression: %d", p[14]); if (pm->set_compression) { pm->set_compression(&lu->mode_pg, lu_priv->configCompressionFactor); set_lp11_compression(1); /* Update LogPage 11 compression bit */ } } else { MHVTL_DBG(2, " Mode Select->Clearing compression"); if (pm->clear_compression) { pm->clear_compression(&lu->mode_pg); set_lp11_compression(0); /* Update LogPage 11 compression bit */ } } } int add_mode_device_configuration_extension(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct priv_lu_ssc *ssc; struct ssc_personality_template *pm; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; /* Only for TAPE (SSC) devices */ if (lu->ptype != TYPE_TAPE) return -ENOTTY; ssc = lu->lu_private; pm = ssc->pm; mode_pg = &lu->mode_pg; pcode = MODE_DEVICE_CONFIGURATION; subpcode = 0x01; size = 32; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_device_configuration_extension, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[5] = 0x02; /* Short erase mode - write EOD */ /* default size of early warning */ put_unaligned_be16(0, &mp->pcodePointer[6]); /* Update mode page bitmap to reflect changeable fields */ if (pm->drive_supports_append_only_mode) mp->pcodePointerBitMap[5] |= 0xf0; if (pm->drive_supports_prog_early_warning) { mp->pcodePointerBitMap[6] |= 0xff; mp->pcodePointerBitMap[7] |= 0xff; } mp->description = mode_device_configuration_extension; return 0; } uint8_t set_device_configuration_extension(struct scsi_cmd *cmd, uint8_t *p) { struct lu_phy_attr *lu = cmd->lu; struct priv_lu_ssc *lu_priv = cmd->lu->lu_private; struct ssc_personality_template *pm = lu_priv->pm; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; struct mode *mp; struct s_sd sd; int page_code_len; int write_mode; int pews; /* Programable Early Warning Size */ mp = lookup_mode_pg(&lu->mode_pg, MODE_DEVICE_CONFIGURATION, 1); /* Code error * Any device supporting this should have this mode page defined */ if (!mp) { sam_hardware_error(E_INTERNAL_TARGET_FAILURE, sam_stat); return SAM_STAT_CHECK_CONDITION; } page_code_len = get_unaligned_be16(&p[2]); if (page_code_len != 0x1c) { sd.byte0 = SKSV; sd.field_pointer = 2; MHVTL_LOG("Unexpected page code length.. Unexpected results"); sam_illegal_request(E_INVALID_FIELD_IN_PARMS, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } write_mode = (p[5] & 0xf0) >> 4; if (write_mode > 1) { MHVTL_LOG("Unsupported write mode: 0x%x", write_mode); sd.byte0 = SKSV | BPV | 7; /* bit 7 */ sd.field_pointer = 5; sam_illegal_request(E_INVALID_FIELD_IN_PARMS, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } MHVTL_DBG(2, "%s mode", write_mode ? "Append-only" : "Overwrite-allowed"); pews = get_unaligned_be16(&p[6]); if (pm->drive_supports_prog_early_warning) { MHVTL_DBG(2, "Set Programable Early Warning Size: %d", pews); lu_priv->prog_early_warning_sz = pews; update_prog_early_warning(lu); } else { MHVTL_DBG(2, "Programable Early Warning Size not supported" " by this device"); } MHVTL_DBG(2, "Volume containing encrypted logical blocks " "requires encryption: %d", p[8] & 0x01); if (pm->drive_supports_append_only_mode) { /* Can't reset append-only mode via mode page ssc4 8.3.8 */ if (lu_priv->append_only_mode && write_mode == 0) { MHVTL_LOG("Can't reset append only mode via mode page"); sam_illegal_request(E_INVALID_FIELD_IN_PARMS, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (write_mode) { lu_priv->append_only_mode = write_mode; lu_priv->allow_overwrite = FALSE; } } /* Now update our copy of this mode page */ mp->pcodePointer[5] &= 0x0f; mp->pcodePointer[5] |= write_mode << 4; return SAM_STAT_GOOD; } int add_mode_medium_partition(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_MEDIUM_PARTITION; subpcode = 0; size = 16; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_medium_partition, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[2] = MAX_PARTITIONS - 1; /* Maximum Additional Partitions */ mp->pcodePointer[3] = MAX_PARTITIONS - 1; /* Additional Partitions Defined should be dynamically set to mam.num_partitions - 1 later */ mp->pcodePointer[4] = 0x9c; /* FDP (Fixed Data Partitions) | * PSUM (partition size unit of measure) | * POFM (partition on Format Medium) */ mp->pcodePointer[5] = 0x03; /* Medium Format Recognition */ mp->pcodePointer[6] = 0x09; /* Partitioning Type | * Partition Units */ /* Partition size descriptors - sizes in GB*/ put_unaligned_be16(0x0002, &mp->pcodePointer[8]); put_unaligned_be16(0x0002, &mp->pcodePointer[10]); put_unaligned_be16(0x0002, &mp->pcodePointer[12]); put_unaligned_be16(0x0002, &mp->pcodePointer[14]); /* Changeable fields */ mp->pcodePointerBitMap[3] = 0xff; mp->pcodePointerBitMap[4] = 0xf8; mp->pcodePointerBitMap[6] = 0xff; put_unaligned_be16(0xffff, &mp->pcodePointerBitMap[8]); put_unaligned_be16(0xffff, &mp->pcodePointerBitMap[10]); put_unaligned_be16(0xffff, &mp->pcodePointerBitMap[12]); put_unaligned_be16(0xffff, &mp->pcodePointerBitMap[14]); mp->description = mode_medium_partition; return 0; } void set_medium_partition(struct scsi_cmd *cmd, uint8_t *p) { struct lu_phy_attr *lu = cmd->lu; struct mode *mp = lookup_mode_pg(&lu->mode_pg, MODE_MEDIUM_PARTITION, 0); /* ADDITIONAL PARTITIONS DEFINED */ mp->pcodePointer[3] = p[3]; mam.num_partitions = mp->pcodePointer[3] + 1; MHVTL_DBG(3, "New total number of partitions : %d", mam.num_partitions); mp->pcodePointer[4] = p[4]; /* flags */ mp->pcodePointer[5] = p[5]; /* MEDIUM FORMAT RECOGNITION */ mp->pcodePointer[6] = p[6]; /* PARTITIONING TYPE | PARTITION UNITS */ /* PARTITION SIZES */ for (int k = 0; k < mam.num_partitions; k++) { put_unaligned_be16(p[8 + 2 * k], &mp->pcodePointer[8 + 2 * k]); } } int add_mode_power_condition(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_POWER_CONDITION; subpcode = 0; size = 0x26; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_power_condition, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->description = mode_power_condition; return 0; } int add_mode_information_exception(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_INFORMATION_EXCEPTION; subpcode = 0; size = 12; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_information_exception, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[2] = 0x08; mp->pcodePointer[3] = 0x03; mp->description = mode_information_exception; return 0; } int add_mode_medium_configuration(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_MEDIUM_CONFIGURATION; subpcode = 0; size = 32; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_medium_configuration, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[4] = 0x01; /* WORM mode label restrictions */ mp->pcodePointer[5] = 0x01; /* WORM mode filemark restrictions */ mp->description = mode_medium_configuration; return 0; } /* * Initialise structure data for mode pages. * - Allocate memory for each mode page & init to 0 * - Set up size of mode page * - Set initial values of mode pages * * Return void - Nothing */ int add_mode_ult_encr_mode_pages(struct lu_phy_attr *lu) { struct list_head *mode_pg; /* Mode Page list */ struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_VENDOR_SPECIFIC_24H; subpcode = 0; size = 8; /* Vendor Unique (IBM Ultrium) * Page 151, table 118 * Advise ENCRYPTION Capable device */ MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_vendor_24h, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[7] = ENCR_C; mp->description = mode_vendor_24h; return 0; } /* * Initialise structure data for mode pages. * - Allocate memory for each mode page & init to 0 * - Set up size of mode page * - Set initial values of mode pages * * Return void - Nothing */ int add_mode_vendor_25h_mode_pages(struct lu_phy_attr *lu) { struct list_head *mode_pg; /* Mode Page list */ struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_VENDOR_SPECIFIC_25H; subpcode = 0; size = 32; /* Vendor Unique (IBM Ultrium) * Page 151, table 118 * Advise ENCRYPTION Capable device */ MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_vendor_25h, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[5] = 1; /* LEOP to maximize medium capacity */ mp->pcodePointer[6] = 1; /* Early Warning */ mp->description = mode_vendor_25h; return 0; } int add_mode_encryption_mode_attribute(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_ENCRYPTION_MODE; subpcode = 0x20; size = 9; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_encryption_mode, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; /* Application Managed Encryption */ mp->pcodePointer[5] = 0x03; /* Encryption Solution Method */ mp->pcodePointer[6] = 0x01; /* Key Path */ mp->pcodePointer[7] = 0x01; /* Default Encruption State */ mp->pcodePointer[8] = 0x00; /* Desnity Reporting */ mp->description = mode_encryption_mode; return 0; } int add_mode_ait_device_configuration(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_AIT_DEVICE_CONFIGURATION; subpcode = 0; size = 8; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_ait_device_configuration, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[2] = 0xf0; mp->pcodePointer[3] = 0x0a; mp->pcodePointer[4] = 0x40; mp->description = mode_ait_device_configuration; return 0; } int add_mode_element_address_assignment(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; static struct smc_priv *smc_p; uint8_t pcode; uint8_t subpcode; uint8_t size; uint8_t *p; mode_pg = &lu->mode_pg; smc_p = (struct smc_priv *)lu->lu_private; pcode = MODE_ELEMENT_ADDRESS; subpcode = 0; size = 20; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_element_address, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; p = mp->pcodePointer; p[0] = pcode; p[1] = size - sizeof(p[0]) - sizeof(p[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; put_unaligned_be16(smc_p->pm->start_picker, &p[2]); put_unaligned_be16(smc_p->num_picker, &p[4]); put_unaligned_be16(smc_p->pm->start_storage, &p[6]); put_unaligned_be16(smc_p->num_storage, &p[8]); put_unaligned_be16(smc_p->pm->start_map, &p[10]); put_unaligned_be16(smc_p->num_map, &p[12]); put_unaligned_be16(smc_p->pm->start_drive, &p[14]); put_unaligned_be16(smc_p->num_drives, &p[16]); mp->description = mode_element_address; return 0; } /* * Transport Geometry Parameters mode page * SMC-3 7.3.4 */ int add_mode_transport_geometry(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_TRANSPORT_GEOMETRY; subpcode = 0; size = 4; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_transport_geometry, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->description = mode_transport_geometry; return 0; } /* * Device Capabilities mode page: * SMC-3 7.3.2 */ int add_mode_device_capabilities(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_DEVICE_CAPABILITIES; subpcode = 0; size = 20; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_device_capabilities, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[2] = 0x0f; mp->pcodePointer[3] = 0x07; mp->pcodePointer[4] = 0x0f; mp->pcodePointer[5] = 0x0f; mp->pcodePointer[6] = 0x0f; mp->pcodePointer[7] = 0x0f; /* [8-11] -> reserved */ mp->pcodePointer[12] = 0x00; mp->pcodePointer[13] = 0x00; mp->pcodePointer[14] = 0x00; mp->pcodePointer[15] = 0x00; /* [16-19] -> reserved */ mp->description = mode_device_capabilities; return 0; } /* * Behavior Configuration Mode Page * IBM Ultrium SCSI Reference - 9th Edition */ int add_mode_behavior_configuration(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; pcode = MODE_BEHAVIOR_CONFIGURATION; subpcode = 0; size = 10; MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", mode_behaviour_configuration, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->pcodePointer[3] = 0; /* Clean Behavior */ mp->pcodePointer[4] = 0; /* WORM Behavior */ mp->description = mode_behaviour_configuration; return 0; } int update_prog_early_warning(struct lu_phy_attr *lu) { uint8_t *mp; struct mode *m; struct list_head *mode_pg; struct priv_lu_ssc *lu_priv; mode_pg = &lu->mode_pg; lu_priv = (struct priv_lu_ssc *)lu->lu_private; m = lookup_mode_pg(mode_pg, MODE_DEVICE_CONFIGURATION, 1); MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", mode_pg, m, m->pcodePointer); if (m) { mp = m->pcodePointer; if (!mp) return SAM_STAT_GOOD; put_unaligned_be16(lu_priv->prog_early_warning_sz, &mp[6]); } return SAM_STAT_GOOD; } int update_logical_block_protection(struct lu_phy_attr *lu, uint8_t *buf) { uint8_t *mp; struct mode *m; struct list_head *mode_pg; struct priv_lu_ssc *lu_priv; mode_pg = &lu->mode_pg; lu_priv = (struct priv_lu_ssc *)lu->lu_private; MHVTL_DBG(3, "+++ entry +++"); m = lookup_mode_pg(mode_pg, MODE_CONTROL, 0xf0); MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", mode_pg, m, m->pcodePointer); if (m) { mp = m->pcodePointer; if (!mp) { MHVTL_ERR("Could not find mode page"); return SAM_STAT_GOOD; } mp[4] = buf[4]; /* Logical Block Protection Method */ mp[5] = buf[5]; /* Logical Block Protection Information Length */ mp[6] = buf[6]; /* LBP_W & LBP_R */ lu_priv->LBP_method = buf[4] & 0x03; lu_priv->LBP_R = (buf[6] & 0x40) ? 1 : 0; lu_priv->LBP_W = (buf[6] & 0x80) ? 1 : 0; MHVTL_DBG(1, "Updating Logical Block Protection: Method: 0x%02x, LBP_R: %s, LPB_W: %s", lu_priv->LBP_method, lu_priv->LBP_R ? "True" : "False", lu_priv->LBP_W ? "True" : "False"); } return SAM_STAT_GOOD; } uint8_t set_lbp(struct scsi_cmd *cmd, uint8_t *buf, int len) { struct priv_lu_ssc *lu_priv = cmd->lu->lu_private; struct ssc_personality_template *pm = lu_priv->pm; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; struct s_sd sd; /* OK, the drive supports Logical Block Protection - good to go */ if (pm->drive_supports_LBP) { return update_logical_block_protection(cmd->lu, buf); } sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } int add_smc_mode_page_drive_configuration(struct lu_phy_attr *lu) { struct list_head *mode_pg; struct mode *mp; uint8_t pcode; uint8_t subpcode; uint8_t size; mode_pg = &lu->mode_pg; /* A Vendor-specific page for the StorageTek L20, L40 and L80 libraries */ pcode = 0x2d; subpcode = 0; size = 0x26; /* * FIXME: Need to fill in details from Table 4-21 L20 SCSI Reference Manual */ MHVTL_DBG(3, "Adding mode page %s (%02x/%02x)", drive_configuration_page, pcode, subpcode); mp = alloc_mode_page(mode_pg, pcode, subpcode, size); if (!mp) return -ENOMEM; mp->pcodePointer[0] = pcode; mp->pcodePointer[1] = size - sizeof(mp->pcodePointer[0]) - sizeof(mp->pcodePointer[1]); /* And copy pcode/size into bitmap structure */ mp->pcodePointerBitMap[0] = mp->pcodePointer[0]; mp->pcodePointerBitMap[1] = mp->pcodePointer[1]; mp->description = drive_configuration_page; return 0; } ================================================ FILE: usr/pm/ait_pm.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'STREAM' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include "be_byteshift.h" #include "mhvtl_scsi.h" #include "vtl_common.h" #include "vtllib.h" #include "logging.h" #include "ssc.h" #include "vtlcart.h" #include "mode.h" #include "mhvtl_log.h" static struct density_info density_ait1 = { 0x17d6, 0x50, 1, 100, medium_density_code_ait1, "SONY", "AIT-1", "Adv Intellgent Tape"}; static struct density_info density_ait2 = { 0x17d6, 0x50, 1, 200, medium_density_code_ait2, "SONY", "AIT-2", "Adv Intellgent Tape"}; static struct density_info density_ait3 = { 0x17d6, 0x50, 1, 300, medium_density_code_ait3, "SONY", "AIT-3", "Adv Intellgent Tape"}; static struct density_info density_ait4 = { 0x17d6, 0x50, 1, 400, medium_density_code_ait4, "SONY", "AIT-4", "Adv Intellgent Tape"}; static struct name_to_media_info media_info[] = { {"AIT1", Media_AIT1, media_type_unknown, medium_density_code_ait1}, {"AIT1 Clean", Media_AIT1_CLEAN, media_type_unknown, medium_density_code_ait1}, {"AIT2", Media_AIT2, media_type_unknown, medium_density_code_ait2}, {"AIT2 Clean", Media_AIT2_CLEAN, media_type_unknown, medium_density_code_ait2}, {"AIT3", Media_AIT3, media_type_unknown, medium_density_code_ait3}, {"AIT3 Clean", Media_AIT3_CLEAN, media_type_unknown, medium_density_code_ait3}, {"AIT4", Media_AIT4, media_type_unknown, medium_density_code_ait4}, {"AIT4 Clean", Media_AIT4_CLEAN, media_type_unknown, medium_density_code_ait4}, {"AIT4 WORM", Media_AIT4_WORM, media_type_unknown, medium_density_code_ait4}, {"", 0, 0, 0}, }; static uint8_t clear_ait_WORM(struct list_head *l) { uint8_t *smp_dp; struct mode *smp; MHVTL_DBG(3, "+++ Trace mode pages at %p +++", l); smp = lookup_mode_pg(l, MODE_AIT_DEVICE_CONFIGURATION, 0); if (smp) { smp_dp = smp->pcodePointer; smp_dp[4] = 0x0; } return SAM_STAT_GOOD; } static uint8_t set_ait_WORM(struct list_head *l) { uint8_t *smp_dp; struct mode *smp; MHVTL_DBG(3, "+++ Trace mode pages at %p +++", l); smp = lookup_mode_pg(l, MODE_AIT_DEVICE_CONFIGURATION, 0); if (smp) { smp_dp = smp->pcodePointer; smp_dp[4] = 0x40; } return SAM_STAT_GOOD; } static uint8_t clear_ait_compression(struct list_head *l) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", l); /* default clear_compression is in libvtlscsi */ return clear_compression_mode_pg(l); } static uint8_t set_ait_compression(struct list_head *l, int lvl) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", l); /* default set_compression is in libvtlscsi */ return set_compression_mode_pg(l, lvl); } static uint8_t update_ait_encryption_mode(struct list_head *m, void *p, int value) { MHVTL_DBG(3, "+++ Trace +++"); return SAM_STAT_GOOD; } /* FIXME: This is a copy of LTO4/5 encryption capabilities. Need to adjust to suit AIT4 */ static int encr_capabilities_ait(struct scsi_cmd *cmd) { uint8_t *buf = cmd->dbuf_p->data; struct priv_lu_ssc *lu_priv = cmd->lu->lu_private; put_unaligned_be16(ENCR_CAPABILITIES, &buf[0]); put_unaligned_be16(40, &buf[2]); /* List length */ buf[20] = 1; /* Algorithm index */ buf[21] = 0; /* Reserved */ put_unaligned_be16(0x14, &buf[22]); /* Descriptor length */ buf[24] = 0x3a; /* MAC C/DED_C DECRYPT_C = 2 ENCRYPT_C = 2 */ buf[25] = 0x10; /* NONCE_C = 1 */ /* Max unauthenticated key data */ put_unaligned_be16(0x20, &buf[26]); /* Max authenticated key data */ put_unaligned_be16(0x0c, &buf[28]); /* Key size */ put_unaligned_be16(0x20, &buf[30]); buf[32] = 0x01; /* EAREM */ /* buf 12 - 19 reserved */ buf[40] = 0; /* Encryption Algorithm Id */ buf[41] = 0x01; /* Encryption Algorithm Id */ buf[42] = 0; /* Encryption Algorithm Id */ buf[43] = 0x14; /* Encryption Algorithm Id */ /* adjustments for each emulated drive type */ buf[4] = 0x1; /* CFG_P == 01b */ if (lu_priv->load_status == TAPE_LOADED) { switch (mam.MediaType) { case Media_AIT4: MHVTL_DBG(1, "AIT4 Medium"); buf[24] |= 0x80; /* AVFMV */ break; } } buf[32] |= 0x08; /* RDMC_C == 4 */ return 44; } static void init_ait_inquiry(struct lu_phy_attr *lu) { int pg; uint8_t worm; uint8_t local_TapeAlert[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; worm = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_WORM; lu->inquiry[2] = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_ANSI_VERSION; /* Sequential Access device capabilities - Ref: 8.4.2 */ pg = PCODE_OFFSET(0xb0); lu->lu_vpd[pg] = alloc_vpd(VPD_B0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b0(lu, &worm); /* Manufacture-assigned serial number - Ref: 8.4.3 */ pg = PCODE_OFFSET(0xb1); lu->lu_vpd[pg] = alloc_vpd(VPD_B1_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b1(lu, lu->lu_serial_no); /* TapeAlert supported flags - Ref: 8.4.4 */ pg = PCODE_OFFSET(0xb2); lu->lu_vpd[pg] = alloc_vpd(VPD_B2_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b2(lu, &local_TapeAlert); /* VPD page 0xC0 */ pg = PCODE_OFFSET(0xc0); lu->lu_vpd[pg] = alloc_vpd(VPD_C0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_c0(lu, "10-03-2008 19:38:00"); /* VPD page 0xC1 */ pg = PCODE_OFFSET(0xc1); lu->lu_vpd[pg] = alloc_vpd(strlen("Security")); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_c1(lu, "Security"); } /* Dummy routine. Always return false */ static int ait_kad_validation(int mode, int ukad, int akad) { return FALSE; } /* Some comments before I forget how this is supose to work.. - cleaning_media_state is either 0 - Not mounted 1 - Cleaning media mounted -> return Cleaning cartridge installed 2 - Cleaning media mounted -> return Cause not reportable 3 - Cleaning media mounted -> return Initializing command required On cleaning media mount, ait_cleaning() is called which: Sets a pointer from priv_lu_ssc -> cleaning_media_state. Sets cleaning_media_state to 1. Sets a 30 second timer to call inc_cleaning_state() inc_cleaning_state() Increments cleaning_media_state. If cleaning media_state == 2, set another timer for 90 seconds to again call inc_cleaning_state. If the application issues a TUR, ssc_tur() will return one of the above status codes depending on the current value of cleaning_media_state. When the cleaning media is unmounted, the pointer in priv_lu_ssc to this var will be re-set to NULL so the ssc_tur() will return defautl value. */ static volatile sig_atomic_t cleaning_media_state; static void inc_cleaning_state(int sig); static void set_cleaning_timer(int t) { MHVTL_DBG(3, "+++ Trace +++ Setting alarm for %d", t); signal(SIGALRM, inc_cleaning_state); alarm(t); } static void inc_cleaning_state(int sig) { MHVTL_DBG(3, "+++ Trace +++"); signal(sig, inc_cleaning_state); cleaning_media_state++; if (cleaning_media_state == CLEAN_MOUNT_STAGE2) set_cleaning_timer(90); } static uint8_t ait_cleaning(void *ssc_priv) { struct priv_lu_ssc *ssc; MHVTL_DBG(3, "+++ Trace +++"); ssc = ssc_priv; ssc->cleaning_media_state = &cleaning_media_state; cleaning_media_state = CLEAN_MOUNT_STAGE1; set_cleaning_timer(30); return 0; } /* Table 6-29 Supported Mode pages * Sony SDX-900V v2.1 SCSI Reference Guide */ static void init_ait_mode_pages(struct lu_phy_attr *lu) { add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_medium_partition(lu); add_mode_information_exception(lu); add_mode_ait_device_configuration(lu); } /* load set to 1 for load, 0 for unload */ static uint8_t ait_media_load(struct lu_phy_attr *lu, int load) { uint8_t *smp_dp; struct mode *smp; MHVTL_DBG(3, "+++ Trace +++"); smp = lookup_mode_pg(&lu->mode_pg, MODE_AIT_DEVICE_CONFIGURATION, 0); if (smp) { smp_dp = smp->pcodePointer; if (load) smp_dp[3] = 0x0a; /* SPAN -> Set to 0Ah on load */ else smp_dp[3] = 0x0; /* SPAN -> Set to 0 on unload */ } return 0; } static char *name_ait_1 = "AIT"; static char *name_ait_2 = "AIT-2"; static char *name_ait_3 = "AIT-3"; static char *name_ait_4 = "AIT-4"; static struct ssc_personality_template ssc_pm = { .valid_encryption_blk = valid_encryption_blk, .update_encryption_mode = update_ait_encryption_mode, .encryption_capabilities = encr_capabilities_ait, .kad_validation = ait_kad_validation, .check_restrictions = check_restrictions, .clear_compression = clear_ait_compression, .set_compression = set_ait_compression, .media_load = ait_media_load, .cleaning_media = ait_cleaning, .media_handling = media_info, }; void init_ait1_ssc(struct lu_phy_attr *lu) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", &lu->mode_pg); ssc_pm.name = name_ait_1; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_ait1; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = FALSE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 2; ssc_personality_module_register(&ssc_pm); init_ait_inquiry(lu); init_ait_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); add_density_support(&lu->den_list, &density_ait1, 1); add_drive_media_list(lu, LOAD_RW, "AIT1"); add_drive_media_list(lu, LOAD_RO, "AIT1 Clean"); } void init_ait2_ssc(struct lu_phy_attr *lu) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", &lu->mode_pg); ssc_pm.name = name_ait_2; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_ait2; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ait_inquiry(lu); init_ait_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); add_density_support(&lu->den_list, &density_ait1, 1); add_density_support(&lu->den_list, &density_ait2, 1); add_drive_media_list(lu, LOAD_RW, "AIT1"); add_drive_media_list(lu, LOAD_RO, "AIT1 Clean"); add_drive_media_list(lu, LOAD_RW, "AIT2"); add_drive_media_list(lu, LOAD_RO, "AIT2 Clean"); } void init_ait3_ssc(struct lu_phy_attr *lu) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", &lu->mode_pg); ssc_pm.name = name_ait_3; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_ait3; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ait_inquiry(lu); init_ait_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); add_density_support(&lu->den_list, &density_ait1, 0); add_density_support(&lu->den_list, &density_ait2, 1); add_density_support(&lu->den_list, &density_ait3, 1); add_drive_media_list(lu, LOAD_RO, "AIT1"); add_drive_media_list(lu, LOAD_RO, "AIT1 Clean"); add_drive_media_list(lu, LOAD_RW, "AIT2"); add_drive_media_list(lu, LOAD_RO, "AIT2 Clean"); add_drive_media_list(lu, LOAD_RW, "AIT3"); add_drive_media_list(lu, LOAD_RO, "AIT3 Clean"); } void init_ait4_ssc(struct lu_phy_attr *lu) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", &lu->mode_pg); ssc_pm.name = name_ait_4; ssc_pm.lu = lu; ssc_pm.clear_WORM = clear_ait_WORM; ssc_pm.set_WORM = set_ait_WORM; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ait_inquiry(lu); init_ait_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); ssc_pm.native_drive_density = &density_ait4; ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 10; /* Capacity units in KBytes */ add_density_support(&lu->den_list, &density_ait2, 0); add_density_support(&lu->den_list, &density_ait3, 1); add_density_support(&lu->den_list, &density_ait4, 1); add_drive_media_list(lu, LOAD_RO, "AIT2"); add_drive_media_list(lu, LOAD_RO, "AIT2 Clean"); add_drive_media_list(lu, LOAD_RW, "AIT3"); add_drive_media_list(lu, LOAD_RO, "AIT3 Clean"); add_drive_media_list(lu, LOAD_RW, "AIT4"); add_drive_media_list(lu, LOAD_RO, "AIT4 Clean"); add_drive_media_list(lu, LOAD_RW, "AIT4 WORM"); } ================================================ FILE: usr/pm/default_smc_pm.c ================================================ /* * Personality module for default emulation */ #include #include #include "vtllib.h" #include "smc.h" #include "be_byteshift.h" static struct smc_personality_template smc_pm = { .library_has_map = TRUE, .library_has_barcode_reader = TRUE, .library_has_playground = TRUE, .dvcid_len = 32, }; static void update_default_inquiry(struct lu_phy_attr *lu) { struct smc_priv *smc_p; smc_p = lu->lu_private; lu->inquiry[2] = 5; /* SNSI Approved Version */ lu->inquiry[3] = 2; /* Response data format */ lu->inquiry[4] = 0x43; /* Additional length */ memcpy(&lu->inquiry[38], &lu->lu_serial_no, 12); lu->inquiry[55] |= smc_p->pm->library_has_barcode_reader ? 1 : 0; put_unaligned_be16(0x005c, &lu->inquiry[58]); /* SAM-2 */ put_unaligned_be16(0x0b56, &lu->inquiry[60]); /* SPI-4 */ put_unaligned_be16(0x02fe, &lu->inquiry[62]); /* SMC-2 */ put_unaligned_be16(0x030f, &lu->inquiry[64]); /* SPC-3 */ /* Device Identification */ lu->lu_vpd[PCODE_OFFSET(0x83)] = alloc_vpd(VPD_83_SZ); if (lu->lu_vpd[PCODE_OFFSET(0x83)]) update_vpd_83(lu, NULL); } void init_default_smc(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - Default emulation"; smc_pm.library_has_map = TRUE; smc_pm.library_has_barcode_reader = TRUE; smc_pm.library_has_playground = TRUE; smc_pm.dvcid_serial_only = FALSE; smc_pm.start_drive = 0x001; smc_pm.start_picker = 0x2f0; smc_pm.start_map = 0x300; smc_pm.start_storage = 0x400; smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); update_default_inquiry(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); } ================================================ FILE: usr/pm/default_ssc_pm.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'STREAM' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include #include "mhvtl_scsi.h" #include "vtl_common.h" #include "vtllib.h" #include "logging.h" #include "ssc.h" #include "spc.h" #include "mode.h" #include "mhvtl_log.h" static struct density_info density_default = { 1024, 127, 1, 500, medium_density_code_unknown, "mhVTL", "DEFAULT", "linuxVTL"}; static struct name_to_media_info media_info[] = { {"Undefined", Media_undefined, media_type_unknown, medium_density_code_unknown}, /* Ultrium media */ {"LTO1", Media_LTO1, media_type_lto1_data, medium_density_code_lto1}, {"LTO1 Clean", Media_LTO1_CLEAN, media_type_lto1_data, medium_density_code_lto1}, {"LTO2", Media_LTO2, media_type_lto2_data, medium_density_code_lto2}, {"LTO2 Clean", Media_LTO2_CLEAN, media_type_lto2_data, medium_density_code_lto2}, {"LTO3", Media_LTO3, media_type_lto3_data, medium_density_code_lto3}, {"LTO3 Clean", Media_LTO3_CLEAN, media_type_lto3_data, medium_density_code_lto3}, {"LTO3 WORM", Media_LTO3_WORM, media_type_lto3_worm, medium_density_code_lto3}, {"LTO4", Media_LTO4, media_type_lto4_data, medium_density_code_lto4}, {"LTO4 Clean", Media_LTO4_CLEAN, media_type_lto4_data, medium_density_code_lto4}, {"LTO4 WORM", Media_LTO4_WORM, media_type_lto4_worm, medium_density_code_lto4}, {"LTO5", Media_LTO5, media_type_lto5_data, medium_density_code_lto5}, {"LTO5 Clean", Media_LTO5_CLEAN, media_type_lto5_data, medium_density_code_lto5}, {"LTO5 WORM", Media_LTO5_WORM, media_type_lto5_worm, medium_density_code_lto5}, {"LTO6", Media_LTO6, media_type_lto6_data, medium_density_code_lto6}, {"LTO6 Clean", Media_LTO6_CLEAN, media_type_lto6_data, medium_density_code_lto6}, {"LTO6 WORM", Media_LTO6_WORM, media_type_lto6_worm, medium_density_code_lto6}, {"LTO7", Media_LTO7, media_type_lto7_data, medium_density_code_lto7}, {"LTO7 Clean", Media_LTO7_CLEAN, media_type_lto7_data, medium_density_code_lto7}, {"LTO7 WORM", Media_LTO7_WORM, media_type_lto7_worm, medium_density_code_lto7}, {"LTO8", Media_LTO8, media_type_lto8_data, medium_density_code_lto8}, {"LTO8 Clean", Media_LTO8_CLEAN, media_type_lto8_data, medium_density_code_lto8}, {"LTO8 WORM", Media_LTO8_WORM, media_type_lto8_worm, medium_density_code_lto8}, {"LTO9", Media_LTO9, media_type_lto9_data, medium_density_code_lto9}, {"LTO9 Clean", Media_LTO9_CLEAN, media_type_lto9_data, medium_density_code_lto9}, {"LTO9 WORM", Media_LTO9_WORM, media_type_lto9_worm, medium_density_code_lto9}, /* IBM 03592 media */ {"03592 JA", Media_3592_JA, media_type_unknown, medium_density_code_j1a}, {"03592 JA Clean", Media_3592_JA_CLEAN, media_type_unknown, medium_density_code_j1a}, {"03592 JA WORM", Media_3592_JW, media_type_unknown, medium_density_code_j1a}, {"03592 JB", Media_3592_JB, media_type_unknown, medium_density_code_e05}, {"03592 JB Clean", Media_3592_JB_CLEAN, media_type_unknown, medium_density_code_e05}, {"03592 JB ENCR", Media_3592_JB, media_type_unknown, medium_density_code_e05_ENCR}, {"03592 JC", Media_3592_JX, media_type_unknown, medium_density_code_e06}, {"03592 JC Clean", Media_3592_JX_CLEAN, media_type_unknown, medium_density_code_e06}, {"03592 JC ENCR", Media_3592_JX, media_type_unknown, medium_density_code_e06_ENCR}, /* AIT media */ {"AIT1", Media_AIT1, media_type_unknown, medium_density_code_ait1}, {"AIT1 Clean", Media_AIT1_CLEAN, media_type_unknown, medium_density_code_ait1}, {"AIT2", Media_AIT2, media_type_unknown, medium_density_code_ait2}, {"AIT2 Clean", Media_AIT2_CLEAN, media_type_unknown, medium_density_code_ait2}, {"AIT3", Media_AIT3, media_type_unknown, medium_density_code_ait3}, {"AIT3 Clean", Media_AIT3_CLEAN, media_type_unknown, medium_density_code_ait3}, {"AIT4", Media_AIT4, media_type_unknown, medium_density_code_ait4}, {"AIT4 Clean", Media_AIT4_CLEAN, media_type_unknown, medium_density_code_ait4}, {"AIT4 WORM", Media_AIT4_WORM, media_type_unknown, medium_density_code_ait4}, /* STK 9x40 media */ {"9840A", Media_9840A, media_type_unknown, medium_density_code_9840A}, {"9840A Clean", Media_9840A_CLEAN, media_type_unknown, medium_density_code_9840A}, {"9840B", Media_9840B, media_type_unknown, medium_density_code_9840B}, {"9840B Clean", Media_9840B_CLEAN, media_type_unknown, medium_density_code_9840B}, {"9840C", Media_9840C, media_type_unknown, medium_density_code_9840C}, {"9840C Clean", Media_9840C_CLEAN, media_type_unknown, medium_density_code_9840C}, {"9840D", Media_9840D, media_type_unknown, medium_density_code_9840D}, {"9840D Clean", Media_9840D_CLEAN, media_type_unknown, medium_density_code_9840D}, {"9940A", Media_9940A, media_type_unknown, medium_density_code_9940A}, {"9940A Clean", Media_9940A_CLEAN, media_type_unknown, medium_density_code_9940A}, {"9940B", Media_9940B, media_type_unknown, medium_density_code_9940B}, {"9940B Clean", Media_9940B_CLEAN, media_type_unknown, medium_density_code_9940B}, /* STK T10000 media */ {"T10KA", Media_T10KA, media_type_unknown, medium_density_code_10kA}, {"T10KA WORM", Media_T10KA_WORM, media_type_unknown, medium_density_code_10kA}, {"T10KA Clean", Media_T10KA_CLEAN, media_type_unknown, medium_density_code_10kA}, {"T10KB", Media_T10KB, media_type_unknown, medium_density_code_10kB}, {"T10KB WORM", Media_T10KB_WORM, media_type_unknown, medium_density_code_10kB}, {"T10KB Clean", Media_T10KB_CLEAN, media_type_unknown, medium_density_code_10kB}, {"T10KC", Media_T10KC, media_type_unknown, medium_density_code_10kC}, {"T10KC WORM", Media_T10KC_WORM, media_type_unknown, medium_density_code_10kC}, {"T10KC Clean", Media_T10KC_CLEAN, media_type_unknown, medium_density_code_10kC}, /* Quantum DLT / SDLT media */ {"DLT2", Media_DLT2, media_type_unknown, medium_density_code_dlt2}, {"DLT3", Media_DLT3, media_type_unknown, medium_density_code_dlt3}, {"DLT4", Media_DLT4, media_type_unknown, medium_density_code_dlt4}, {"SDLT", Media_SDLT, media_type_unknown, medium_density_code_sdlt}, {"SDLT 220", Media_SDLT220, media_type_unknown, medium_density_code_220}, {"SDLT 320", Media_SDLT320, media_type_unknown, medium_density_code_320}, {"SDLT 320 Clean", Media_SDLT320_CLEAN, media_type_unknown, medium_density_code_320}, {"SDLT 600", Media_SDLT600, media_type_unknown, medium_density_code_600}, {"SDLT 600 Clean", Media_SDLT600_CLEAN, media_type_unknown, medium_density_code_600}, {"SDLT 600 WORM", Media_SDLT600_WORM, media_type_unknown, medium_density_code_600}, /* 4MM DAT media */ {"DDS1", Media_DDS1, media_type_unknown, medium_density_code_DDS1}, {"DDS2", Media_DDS2, media_type_unknown, medium_density_code_DDS2}, {"DDS3", Media_DDS3, media_type_unknown, medium_density_code_DDS3}, {"DDS4", Media_DDS4, media_type_unknown, medium_density_code_DDS4}, {"DDS5", Media_DDS5, media_type_unknown, medium_density_code_DDS5}, {"", 0, 0, 0}, }; static uint8_t clear_default_comp(struct list_head *l) { MHVTL_DBG(3, "+++ Trace +++"); /* default clear_compression is in libvtlscsi */ return clear_compression_mode_pg(l); } static uint8_t set_default_comp(struct list_head *l, int lvl) { MHVTL_DBG(3, "+++ Trace +++"); /* default set_compression is in libvtlscsi */ return set_compression_mode_pg(l, lvl); } static uint8_t update_default_encryption_mode(struct list_head *m, void *p, int value) { MHVTL_DBG(3, "+++ Trace +++"); return SAM_STAT_GOOD; } static uint8_t set_default_WORM(struct list_head *l) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", l); return set_WORM(l); } static uint8_t clear_default_WORM(struct list_head *l) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", l); return clear_WORM(l); } static void init_default_inquiry(struct lu_phy_attr *lu) { int pg; uint8_t worm; uint8_t local_TapeAlert[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; worm = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_WORM; lu->inquiry[2] = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_ANSI_VERSION; /* Sequential Access device capabilities - Ref: 8.4.2 */ pg = PCODE_OFFSET(0xb0); lu->lu_vpd[pg] = alloc_vpd(VPD_B0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b0(lu, &worm); /* Manufacture-assigned serial number - Ref: 8.4.3 */ pg = PCODE_OFFSET(0xb1); lu->lu_vpd[pg] = alloc_vpd(VPD_B1_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b1(lu, lu->lu_serial_no); /* TapeAlert supported flags - Ref: 8.4.4 */ pg = PCODE_OFFSET(0xb2); lu->lu_vpd[pg] = alloc_vpd(VPD_B2_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b2(lu, &local_TapeAlert); /* VPD page 0xC0 */ pg = PCODE_OFFSET(0xc0); lu->lu_vpd[pg] = alloc_vpd(VPD_C0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_c0(lu, "10-03-2008 19:38:00"); /* VPD page 0xC1 */ pg = PCODE_OFFSET(0xc1); lu->lu_vpd[pg] = alloc_vpd(strlen("Security")); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_c1(lu, "Security"); } /* Dummy routine. Always return false */ static int default_kad_validation(int mode, int ukad, int akad) { return FALSE; } /* Some comments before I forget how this is supose to work.. - cleaning_media_state is either 0 - Not mounted 1 - Cleaning media mounted -> return Cleaning cartridge installed 2 - Cleaning media mounted -> return Cause not reportable 3 - Cleaning media mounted -> return Initializing command required On cleaning media mount, default_cleaning() is called which: Sets a pointer from priv_lu_ssc -> cleaning_media_state. Sets cleaning_media_state to 1. Sets a 30 second timer to call inc_cleaning_state() inc_cleaning_state() Increments cleaning_media_state. If cleaning media_state == 2, set another timer for 90 seconds to again call inc_cleaning_state. If the application issues a TUR, ssc_tur() will return one of the above status codes depending on the current value of cleaning_media_state. When the cleaning media is unmounted, the pointer in priv_lu_ssc to this var will be re-set to NULL so the ssc_tur() will return defautl value. */ static volatile sig_atomic_t cleaning_media_state; static void inc_cleaning_state(int sig); static void set_cleaning_timer(int t) { MHVTL_DBG(3, "+++ Trace +++ Setting alarm for %d", t); signal(SIGALRM, inc_cleaning_state); alarm(t); } static void inc_cleaning_state(int sig) { MHVTL_DBG(3, "+++ Trace +++"); signal(sig, inc_cleaning_state); cleaning_media_state++; if (cleaning_media_state == CLEAN_MOUNT_STAGE2) set_cleaning_timer(90); } static uint8_t default_media_load(struct lu_phy_attr *lu, int load) { MHVTL_DBG(3, "+++ Trace +++ %s", (load) ? "load" : "unload"); return 0; } static uint8_t default_cleaning(void *ssc_priv) { struct priv_lu_ssc *ssc; MHVTL_DBG(3, "+++ Trace +++"); ssc = ssc_priv; ssc->cleaning_media_state = &cleaning_media_state; cleaning_media_state = CLEAN_MOUNT_STAGE1; set_cleaning_timer(30); return 0; } static void init_default_mode_pages(struct lu_phy_attr *lu) { add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); } static char *pm_name = "default emulation"; static struct ssc_personality_template ssc_pm = { .valid_encryption_blk = valid_encryption_blk, .update_encryption_mode = update_default_encryption_mode, .kad_validation = default_kad_validation, .check_restrictions = check_restrictions, .clear_compression = clear_default_comp, .set_compression = set_default_comp, .clear_WORM = clear_default_WORM, .set_WORM = set_default_WORM, .media_load = default_media_load, .cleaning_media = default_cleaning, .media_handling = media_info, }; void init_default_ssc(struct lu_phy_attr *lu) { MHVTL_DBG(3, "+++ Trace +++"); ssc_pm.name = pm_name; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_default; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = FALSE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = FALSE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 2; ssc_personality_module_register(&ssc_pm); init_default_inquiry(lu); init_default_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); add_density_support(&lu->den_list, &density_default, 1); /* LTO media */ add_drive_media_list(lu, LOAD_RW, "LTO1"); add_drive_media_list(lu, LOAD_RW, "LTO2"); add_drive_media_list(lu, LOAD_RW, "LTO3"); add_drive_media_list(lu, LOAD_RW, "LTO4"); add_drive_media_list(lu, LOAD_RW, "LTO5"); add_drive_media_list(lu, LOAD_RW, "LTO6"); add_drive_media_list(lu, LOAD_RW, "LTO7"); add_drive_media_list(lu, LOAD_RW, "LTO8"); add_drive_media_list(lu, LOAD_RW, "LTO9"); /* DDS media */ add_drive_media_list(lu, LOAD_RW, "DDS1"); add_drive_media_list(lu, LOAD_RW, "DDS2"); add_drive_media_list(lu, LOAD_RW, "DDS3"); add_drive_media_list(lu, LOAD_RW, "DDS4"); add_drive_media_list(lu, LOAD_RW, "DDS5"); /* DLT media */ add_drive_media_list(lu, LOAD_RW, "DLT2"); add_drive_media_list(lu, LOAD_RW, "DLT3"); add_drive_media_list(lu, LOAD_RW, "DLT4"); add_drive_media_list(lu, LOAD_RW, "SDLT"); add_drive_media_list(lu, LOAD_RW, "SDLT 220"); add_drive_media_list(lu, LOAD_RW, "SDLT 320"); add_drive_media_list(lu, LOAD_RW, "SDLT 600"); /* STK T10000 */ add_drive_media_list(lu, LOAD_RW, "T10KA"); add_drive_media_list(lu, LOAD_RW, "T10KB"); add_drive_media_list(lu, LOAD_RW, "T10KC"); add_drive_media_list(lu, LOAD_RW, "T10KD"); /* STK 9x40 */ add_drive_media_list(lu, LOAD_RW, "9840A"); add_drive_media_list(lu, LOAD_RW, "9840B"); add_drive_media_list(lu, LOAD_RW, "9840C"); add_drive_media_list(lu, LOAD_RW, "9840D"); add_drive_media_list(lu, LOAD_RW, "9940A"); add_drive_media_list(lu, LOAD_RW, "9940B"); /* AIT media */ add_drive_media_list(lu, LOAD_RW, "AIT1"); add_drive_media_list(lu, LOAD_RW, "AIT2"); add_drive_media_list(lu, LOAD_RW, "AIT3"); add_drive_media_list(lu, LOAD_RW, "AIT4"); /* IBM 03592 series */ add_drive_media_list(lu, LOAD_RW, "03592 JA"); add_drive_media_list(lu, LOAD_RW, "03592 JB"); add_drive_media_list(lu, LOAD_RW, "03592 JC"); /* Don't support PERSISTENT RESERVATION */ register_ops(lu, PERSISTENT_RESERVE_IN, spc_illegal_op, NULL, NULL); register_ops(lu, PERSISTENT_RESERVE_OUT, spc_illegal_op, NULL, NULL); } ================================================ FILE: usr/pm/hp_smc_pm.c ================================================ /* * Personality module for HP E-Series */ #include #include #include "vtllib.h" #include "smc.h" #include "logging.h" static void update_eml_vpd_80(struct lu_phy_attr *lu) { struct vpd **lu_vpd = lu->lu_vpd; uint8_t *d; int pg; /* Unit Serial Number */ pg = PCODE_OFFSET(0x80); if (lu_vpd[pg]) /* Free any earlier allocation */ dealloc_vpd(lu_vpd[pg]); lu_vpd[pg] = alloc_vpd(0x12); if (lu_vpd[pg]) { d = lu_vpd[pg]->data; snprintf((char *)&d[0], 11, "%-10.10s", lu->lu_serial_no); /* Unique Logical Library Identifier */ } else { MHVTL_ERR("Could not malloc(0x12) bytes, line %d", __LINE__); } } static void update_eml_vpd_83(struct lu_phy_attr *lu) { struct vpd **lu_vpd = lu->lu_vpd; uint8_t *d; int num; char *ptr; int pg; int len, j; num = VENDOR_ID_LEN + PRODUCT_ID_LEN + 10; pg = PCODE_OFFSET(0x83); if (lu_vpd[pg]) /* Free any earlier allocation */ dealloc_vpd(lu_vpd[pg]); lu_vpd[pg] = alloc_vpd(num + 12); if (!lu_vpd[pg]) { MHVTL_ERR("Can't malloc() to setup for vpd_83"); return; } d = lu_vpd[pg]->data; d[0] = 2; d[1] = 1; d[2] = 0; d[3] = num; memcpy(&d[4], &lu->vendor_id, VENDOR_ID_LEN); memcpy(&d[12], &lu->product_id, PRODUCT_ID_LEN); memcpy(&d[28], &lu->lu_serial_no, 10); len = (int)strlen(lu->lu_serial_no); ptr = &lu->lu_serial_no[len]; num += 4; /* NAA IEEE registered identifier (faked) */ d[num] = 0x1; /* Binary */ d[num + 1] = 0x3; d[num + 2] = 0x0; d[num + 3] = 0x8; d[num + 4] = 0x51; d[num + 5] = 0x23; d[num + 6] = 0x45; d[num + 7] = 0x60; d[num + 8] = 0x3; d[num + 9] = 0x3; d[num + 10] = 0x3; d[num + 11] = 0x3; if (lu->naa) { /* If defined in config file */ sscanf((const char *)lu->naa, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &d[num + 4], &d[num + 5], &d[num + 6], &d[num + 7], &d[num + 8], &d[num + 9], &d[num + 10], &d[num + 11]); } else { /* Else munge the serial number */ ptr--; for (j = 11; j > 3; ptr--, j--) d[num + j] = *ptr; } d[num + 4] &= 0x0f; d[num + 4] |= 0x50; } static struct smc_personality_template smc_pm = { .library_has_map = TRUE, .library_has_barcode_reader = TRUE, .library_has_playground = FALSE, .start_picker = 0x0001, .start_map = 0x000a, .start_drive = 0x01f4, .start_storage = 0x03e8, .dvcid_len = 34, }; void init_hp_eml_smc(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - HP EML E-Series emulation"; smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); update_eml_vpd_80(lu); update_eml_vpd_83(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); } void init_hp_msl_smc(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - HP MSL Series emulation"; smc_pm.lu = lu; smc_pm.start_picker = 0x0001; smc_pm.start_storage = 0x0020; smc_pm.start_drive = 0x01e0; smc_pm.start_map = 0x01c0; smc_pm.dvcid_len = 20, smc_pm.dvcid_serial_only = TRUE, smc_pm.no_dvcid_flag = TRUE, lu->inquiry[2] = 2; /* Set SCSI-2 Approved Version */ smc_personality_module_register(&smc_pm); init_slot_info(lu); update_eml_vpd_80(lu); update_eml_vpd_83(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); } ================================================ FILE: usr/pm/hp_ultrium_pm.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'STREAM' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include "be_byteshift.h" #include "mhvtl_scsi.h" #include "vtl_common.h" #include "vtllib.h" #include "logging.h" #include "ssc.h" #include "vtlcart.h" #include "mode.h" #include "mhvtl_log.h" static struct density_info density_lto1 = { 4880, 127, 384, 100000, medium_density_code_lto1, "LTO-CVE", "U-18", "Ultrium 1/8T"}; static struct density_info density_lto2 = { 4880, 127, 512, 200000, medium_density_code_lto2, "LTO-CVE", "U-28", "Ultrium 2/8T"}; static struct density_info density_lto3 = { 9638, 127, 704, 381469, medium_density_code_lto3, "LTO-CVE", "U-316", "Ultrium 3/16T"}; static struct density_info density_lto4 = { 12725, 127, 896, 800000, medium_density_code_lto4, "LTO-CVE", "U-416", "Ultrium 4/16T"}; static struct density_info density_lto5 = { 15142, 127, 1280, 1500000, medium_density_code_lto5, "LTO-CVE", "U-516", "Ultrium 5/16T"}; static struct density_info density_lto6 = { 18441, 127, 2176, 3200000, medium_density_code_lto6, "LTO-CVE", "U-616", "Ultrium 6/16T"}; static struct density_info density_lto7 = { 19107, 127, 3584, 6000000, medium_density_code_lto7, "LTO-CVE", "U-732", "Ultrium 7/32T"}; static struct density_info density_lto8 = { 20669, 127, 6656, 12000000, medium_density_code_lto8, "LTO-CVE", "U-832", "Ultrium 8/32T"}; static struct name_to_media_info media_info[] = { {"LTO1", Media_LTO1, media_type_hp_lto_data, medium_density_code_lto1}, {"LTO1 Clean", Media_LTO1_CLEAN, media_type_hp_lto_data, medium_density_code_lto1}, {"LTO2", Media_LTO2, media_type_hp_lto_data, medium_density_code_lto2}, {"LTO2 Clean", Media_LTO2_CLEAN, media_type_hp_lto_data, medium_density_code_lto2}, {"LTO3", Media_LTO3, media_type_hp_lto_data, medium_density_code_lto3}, {"LTO3 Clean", Media_LTO3_CLEAN, media_type_hp_lto_data, medium_density_code_lto3}, {"LTO3 WORM", Media_LTO3_WORM, media_type_hp_lto_worm, medium_density_code_lto3}, {"LTO4", Media_LTO4, media_type_hp_lto_data, medium_density_code_lto4}, {"LTO4 Clean", Media_LTO4_CLEAN, media_type_hp_lto_data, medium_density_code_lto4}, {"LTO4 WORM", Media_LTO4_WORM, media_type_hp_lto_worm, medium_density_code_lto4}, {"LTO5", Media_LTO5, media_type_hp_lto_data, medium_density_code_lto5}, {"LTO5 Clean", Media_LTO5_CLEAN, media_type_hp_lto_data, medium_density_code_lto5}, {"LTO5 WORM", Media_LTO5_WORM, media_type_hp_lto_worm, medium_density_code_lto5}, {"LTO6", Media_LTO6, media_type_hp_lto_data, medium_density_code_lto6}, {"LTO6 Clean", Media_LTO6_CLEAN, media_type_hp_lto_data, medium_density_code_lto6}, {"LTO6 WORM", Media_LTO6_WORM, media_type_hp_lto_worm, medium_density_code_lto6}, {"LTO7", Media_LTO7, media_type_hp_lto_data, medium_density_code_lto7}, {"LTO7 Clean", Media_LTO7_CLEAN, media_type_hp_lto_data, medium_density_code_lto7}, {"LTO7 WORM", Media_LTO7_WORM, media_type_hp_lto_worm, medium_density_code_lto7}, {"LTO8", Media_LTO8, media_type_hp_lto_data, medium_density_code_lto8}, {"LTO8 Clean", Media_LTO8_CLEAN, media_type_hp_lto_data, medium_density_code_lto8}, {"LTO8 WORM", Media_LTO8_WORM, media_type_hp_lto_worm, medium_density_code_lto8}, {"", 0, 0, 0}, }; static uint8_t clear_ult_compression(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); /* default clear_compression is in libvtlscsi */ return clear_compression_mode_pg(m); } static uint8_t set_ult_compression(struct list_head *m, int lvl) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); /* default set_compression is in libvtlscsi */ return set_compression_mode_pg(m, lvl); } static uint8_t set_ult_WORM(struct list_head *lst) { uint8_t *mp; struct mode *m; set_WORM(lst); /* Default WORM setup */ /* Now for the Ultrium unique stuff */ m = lookup_mode_pg(lst, MODE_BEHAVIOR_CONFIGURATION, 0); if (m) { MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", lst, m, m->pcodePointer); mp = m->pcodePointer; if (!mp) return SAM_STAT_GOOD; mp[4] = 0x01; /* WORM Behavior */ } else { MHVTL_DBG(2, "MODE BEHAVIOUR CONFIGURATION page not found"); } return SAM_STAT_GOOD; } static uint8_t clear_ult_WORM(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); return clear_WORM(m); } static uint8_t update_ult_encryption_mode(struct list_head *m, void *p, int value) { struct mode *smp; MHVTL_DBG(3, "+++ Trace +++"); smp = lookup_mode_pg(m, MODE_VENDOR_SPECIFIC_24H, 0); if (smp) { if (value) smp->pcodePointer[5] |= ENCR_E; else smp->pcodePointer[5] &= ~ENCR_E; } return SAM_STAT_GOOD; } static int encr_capabilities_ult(struct scsi_cmd *cmd) { uint8_t *buf = cmd->dbuf_p->data; struct priv_lu_ssc *lu_priv = cmd->lu->lu_private; put_unaligned_be16(ENCR_CAPABILITIES, &buf[0]); put_unaligned_be16(40, &buf[2]); /* List length */ buf[20] = 1; /* Algorithm index */ buf[21] = 0; /* Reserved */ put_unaligned_be16(0x14, &buf[22]); /* Descriptor length */ buf[24] = 0x3a; /* MAC C/DED_C DECRYPT_C = 2 ENCRYPT_C = 2 */ buf[25] = 0x10; /* NONCE_C = 1 */ /* Max unauthenticated key data */ put_unaligned_be16(0x20, &buf[26]); /* Max authenticated key data */ put_unaligned_be16(0x0c, &buf[28]); /* Key size */ put_unaligned_be16(0x20, &buf[30]); buf[32] = 0x01; /* EAREM */ /* buf 12 - 19 reserved */ buf[40] = 0; /* Encryption Algorithm Id */ buf[41] = 0x01; /* Encryption Algorithm Id */ buf[42] = 0; /* Encryption Algorithm Id */ buf[43] = 0x14; /* Encryption Algorithm Id */ /* adjustments for each emulated drive type */ buf[4] = 0x1; /* CFG_P == 01b */ if (lu_priv->load_status == TAPE_LOADED) { switch (mam.MediaType) { case Media_LTO4: MHVTL_DBG(1, "LTO4 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; case Media_LTO5: MHVTL_DBG(1, "LTO5 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; case Media_LTO6: MHVTL_DBG(1, "LTO6 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; case Media_LTO7: MHVTL_DBG(1, "LTO7 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; case Media_LTO8: MHVTL_DBG(1, "LTO8 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; default: MHVTL_DBG(1, "Unable to set Encryption (AVFMV) bit on mounted media"); break; } } buf[32] |= 0x08; /* RDMC_C == 4 */ return 44; } /* Reference HP LTO5 v3 SCSI Reference [04 - 29] - Component "cccc" [30 - 48] - Version "rrr.vvv" [49 - 72] - Date "yyyy/mm/dd hh:mm" [73 - 95] - Variant "xxxx" */ static void update_hp_vpd_cx(struct lu_phy_attr *lu, uint8_t pg, char *comp, char *vers, char *date, char *variant) { struct vpd *vpd_p; char *data; vpd_p = lu->lu_vpd[PCODE_OFFSET(pg)]; if (!vpd_p) { MHVTL_LOG("Arrhhh... vpd pg %d not defined...", pg); return; } data = (char *)vpd_p->data; data[3] = 0x5c; snprintf(&data[4], 25, "%-24.24s", comp); snprintf(&data[30], 19, "%-18.18s", vers); snprintf(&data[49], 25, "%-24.24s", date); snprintf(&data[73], 23, "%-22.22s", variant); } static void init_ult_inquiry(struct lu_phy_attr *lu) { int pg; uint8_t worm; uint8_t local_TapeAlert[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; worm = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_WORM; lu->inquiry[2] = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_ANSI_VERSION; lu->inquiry[40] = worm; /* Reference 3.7.1 HP Ultrium ISV Cookbook */ pg = PCODE_OFFSET(0x86); lu->lu_vpd[pg] = alloc_vpd(VPD_86_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_86(lu, ((struct priv_lu_ssc *)lu->lu_private)->pm); /* Sequential Access device capabilities - Ref: 8.4.2 */ pg = PCODE_OFFSET(0xb0); lu->lu_vpd[pg] = alloc_vpd(VPD_B0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b0(lu, &worm); /* Manufacture-assigned serial number - Ref: 8.4.3 */ pg = PCODE_OFFSET(0xb1); lu->lu_vpd[pg] = alloc_vpd(VPD_B1_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b1(lu, lu->lu_serial_no); /* TapeAlert supported flags - Ref: 8.4.4 */ pg = PCODE_OFFSET(0xb2); lu->lu_vpd[pg] = alloc_vpd(VPD_B2_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b2(lu, &local_TapeAlert); /* VPD page 0xC0 - Firmware revision page */ pg = PCODE_OFFSET(0xc0); lu->lu_vpd[pg] = alloc_vpd(0x60); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_hp_vpd_cx(lu, pg, "Firmware", MHVTL_VERSION, "2012/04/18 19:38", "6"); /* VPD page 0xC1 - Hardware */ pg = PCODE_OFFSET(0xc1); lu->lu_vpd[pg] = alloc_vpd(0x60); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_hp_vpd_cx(lu, pg, "Hardware", MHVTL_VERSION, "2012/04/18 06:53", "5"); /* VPD page 0xC2 - PCA */ pg = PCODE_OFFSET(0xc2); lu->lu_vpd[pg] = alloc_vpd(0x60); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_hp_vpd_cx(lu, pg, "PCA", MHVTL_VERSION, "1996/11/29 10:00", "4"); /* VPD page 0xC3 - Mechanism */ pg = PCODE_OFFSET(0xc3); lu->lu_vpd[pg] = alloc_vpd(0x60); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_hp_vpd_cx(lu, pg, "Mechanism", MHVTL_VERSION, "1992/08/11 10:00", "3"); /* VPD page 0xC4 - Head Assembly */ pg = PCODE_OFFSET(0xc4); lu->lu_vpd[pg] = alloc_vpd(0x60); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_hp_vpd_cx(lu, pg, "Head Assy", MHVTL_VERSION, "1966/07/28 10:00", "2"); /* VPD page 0xC5 - ACI */ pg = PCODE_OFFSET(0xc5); lu->lu_vpd[pg] = alloc_vpd(0x60); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_hp_vpd_cx(lu, pg, "ACI", MHVTL_VERSION, "1960/03/10 10:00", "1"); } static int hp_lto_kad_validation(int encrypt_mode, int ukad, int akad) { int count = FALSE; if (ukad > 32 || akad > 12) count = TRUE; if (!encrypt_mode && (ukad || akad)) count = TRUE; return count; } /* Some comments before I forget how this is supose to work.. - cleaning_media_state is either 0 - Not mounted 1 - Cleaning media mounted -> return Cleaning cartridge installed 2 - Cleaning media mounted -> return Cause not reportable 3 - Cleaning media mounted -> return Initializing command required On cleaning media mount, hp_cleaning() is called which: Sets a pointer from priv_lu_ssc -> cleaning_media_state. Sets cleaning_media_state to 1. Sets a 30 second timer to call inc_cleaning_state() inc_cleaning_state() Increments cleaning_media_state. If cleaning media_state == 2, set another timer for 90 seconds to again call inc_cleaning_state. If the application issues a TUR, ssc_tur() will return one of the above status codes depending on the current value of cleaning_media_state. When the cleaning media is unmounted, the pointer in priv_lu_ssc to this var will be re-set to NULL so the ssc_tur() will return defautl value. */ static volatile sig_atomic_t cleaning_media_state; static void inc_cleaning_state(int sig); static void set_cleaning_timer(int t) { MHVTL_DBG(3, "+++ Trace +++ Setting alarm for %d", t); signal(SIGALRM, inc_cleaning_state); alarm(t); } static void inc_cleaning_state(int sig) { MHVTL_DBG(3, "+++ Trace +++"); signal(sig, inc_cleaning_state); cleaning_media_state++; if (cleaning_media_state == CLEAN_MOUNT_STAGE2) set_cleaning_timer(90); } static uint8_t hp_media_load(struct lu_phy_attr *lu, int load) { struct priv_lu_ssc *lu_priv = lu->lu_private; MHVTL_DBG(3, "+++ Trace +++ %s", (load) ? "load" : "unload"); if (load) { lu->mode_media_type = 0; /* Data */ if (lu_priv->mamp->MediumType == MEDIA_TYPE_WORM) lu->mode_media_type = 0x01; /* WORM media */ } else { lu->mode_media_type = 0; } return 0; } static uint8_t hp_cleaning(void *ssc_priv) { struct priv_lu_ssc *ssc; MHVTL_DBG(3, "+++ Trace +++"); ssc = ssc_priv; ssc->cleaning_media_state = &cleaning_media_state; cleaning_media_state = CLEAN_MOUNT_STAGE1; set_cleaning_timer(30); return 0; } static char *pm_name_lto1 = "HP LTO-1"; static char *pm_name_lto2 = "HP LTO-2"; static char *pm_name_lto3 = "HP LTO-3"; static char *pm_name_lto4 = "HP LTO-4"; static char *pm_name_lto5 = "HP LTO-5"; static char *pm_name_lto6 = "HP LTO-6"; static char *pm_name_lto7 = "HP LTO-7"; static char *pm_name_lto8 = "HP LTO-8"; static struct ssc_personality_template ssc_pm = { .valid_encryption_blk = valid_encryption_blk, /* default in ssc.c */ .check_restrictions = check_restrictions, /* default in ssc.c */ .clear_compression = clear_ult_compression, .set_compression = set_ult_compression, .media_load = hp_media_load, .cleaning_media = hp_cleaning, .media_handling = media_info, }; void init_hp_ult_1(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto1; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto1; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); /* IBM Ultrium SCSI Reference (5edition - Oct 2001) * lists these mode pages */ add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_information_exception(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_lto1, 1); add_drive_media_list(lu, LOAD_RW, "LTO1"); add_drive_media_list(lu, LOAD_RO, "LTO1 Clean"); } void init_hp_ult_2(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto2; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto2; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); /* Based on 9th edition of IBM SCSI Reference */ add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_behavior_configuration(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_lto1, 1); add_density_support(&lu->den_list, &density_lto2, 1); add_drive_media_list(lu, LOAD_RW, "LTO1"); add_drive_media_list(lu, LOAD_RO, "LTO1 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO2"); add_drive_media_list(lu, LOAD_RO, "LTO2 Clean"); } void init_hp_ult_3(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto3; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto3; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); /* Based on 9th edition of IBM SCSI Reference */ add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_behavior_configuration(lu); add_mode_vendor_25h_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_lto1, 0); add_density_support(&lu->den_list, &density_lto2, 1); add_density_support(&lu->den_list, &density_lto3, 1); add_drive_media_list(lu, LOAD_RO, "LTO1"); add_drive_media_list(lu, LOAD_RO, "LTO1 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO2"); add_drive_media_list(lu, LOAD_RO, "LTO2 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO3"); add_drive_media_list(lu, LOAD_RO, "LTO3 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO3 WORM"); } void init_hp_ult_4(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto4; ssc_pm.lu = lu; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_pm.native_drive_density = &density_lto4; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = hp_lto_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-4 */ add_mode_vendor_25h_mode_pages(lu); add_mode_encryption_mode_attribute(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; register_ops(lu, SECURITY_PROTOCOL_IN, ssc_spin, NULL, NULL); register_ops(lu, SECURITY_PROTOCOL_OUT, ssc_spout, NULL, NULL); add_density_support(&lu->den_list, &density_lto2, 0); add_density_support(&lu->den_list, &density_lto3, 1); add_density_support(&lu->den_list, &density_lto4, 1); add_drive_media_list(lu, LOAD_RO, "LTO2"); add_drive_media_list(lu, LOAD_RO, "LTO2 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO3"); add_drive_media_list(lu, LOAD_RO, "LTO3 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO3 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO4"); add_drive_media_list(lu, LOAD_RO, "LTO4 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO4 WORM"); } void init_hp_ult_5(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto5; ssc_pm.lu = lu; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_pm.native_drive_density = &density_lto5; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = hp_lto_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-5 */ add_mode_vendor_25h_mode_pages(lu); add_mode_encryption_mode_attribute(lu); /* Supports non-zero programable early warning */ update_prog_early_warning(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; register_ops(lu, SECURITY_PROTOCOL_IN, ssc_spin, NULL, NULL); register_ops(lu, SECURITY_PROTOCOL_OUT, ssc_spout, NULL, NULL); add_density_support(&lu->den_list, &density_lto3, 0); add_density_support(&lu->den_list, &density_lto4, 1); add_density_support(&lu->den_list, &density_lto5, 1); add_drive_media_list(lu, LOAD_RO, "LTO3"); add_drive_media_list(lu, LOAD_RO, "LTO3 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO4"); add_drive_media_list(lu, LOAD_RO, "LTO4 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO4 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO4 ENCR"); add_drive_media_list(lu, LOAD_RW, "LTO5"); add_drive_media_list(lu, LOAD_RO, "LTO5 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO5 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO5 ENCR"); } void init_hp_ult_6(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto6; ssc_pm.lu = lu; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_pm.native_drive_density = &density_lto6; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = hp_lto_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-5 */ add_mode_vendor_25h_mode_pages(lu); add_mode_encryption_mode_attribute(lu); /* Supports non-zero programable early warning */ update_prog_early_warning(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; register_ops(lu, SECURITY_PROTOCOL_IN, ssc_spin, NULL, NULL); register_ops(lu, SECURITY_PROTOCOL_OUT, ssc_spout, NULL, NULL); add_density_support(&lu->den_list, &density_lto4, 0); add_density_support(&lu->den_list, &density_lto5, 1); add_density_support(&lu->den_list, &density_lto6, 1); add_drive_media_list(lu, LOAD_RO, "LTO4"); add_drive_media_list(lu, LOAD_RO, "LTO4 Clean"); add_drive_media_list(lu, LOAD_RO, "LTO4 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO5"); add_drive_media_list(lu, LOAD_RO, "LTO5 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO5 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO5 ENCR"); add_drive_media_list(lu, LOAD_RW, "LTO6"); add_drive_media_list(lu, LOAD_RO, "LTO6 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO6 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO6 ENCR"); } void init_hp_ult_7(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto7; ssc_pm.lu = lu; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_pm.native_drive_density = &density_lto7; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = hp_lto_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-5 */ add_mode_vendor_25h_mode_pages(lu); add_mode_encryption_mode_attribute(lu); /* Supports non-zero programable early warning */ update_prog_early_warning(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; register_ops(lu, SECURITY_PROTOCOL_IN, ssc_spin, NULL, NULL); register_ops(lu, SECURITY_PROTOCOL_OUT, ssc_spout, NULL, NULL); add_density_support(&lu->den_list, &density_lto5, 0); add_density_support(&lu->den_list, &density_lto6, 1); add_density_support(&lu->den_list, &density_lto7, 1); add_drive_media_list(lu, LOAD_RO, "LTO5"); add_drive_media_list(lu, LOAD_RO, "LTO5 Clean"); add_drive_media_list(lu, LOAD_RO, "LTO5 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO6"); add_drive_media_list(lu, LOAD_RO, "LTO6 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO6 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO6 ENCR"); add_drive_media_list(lu, LOAD_RW, "LTO7"); add_drive_media_list(lu, LOAD_RO, "LTO7 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO7 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO7 ENCR"); } void init_hp_ult_8(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto8; ssc_pm.lu = lu; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_pm.native_drive_density = &density_lto8; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = hp_lto_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-5 */ add_mode_vendor_25h_mode_pages(lu); add_mode_encryption_mode_attribute(lu); /* Supports non-zero programable early warning */ update_prog_early_warning(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; register_ops(lu, SECURITY_PROTOCOL_IN, ssc_spin, NULL, NULL); register_ops(lu, SECURITY_PROTOCOL_OUT, ssc_spout, NULL, NULL); /* LTO 8 drives cannot read LTO6 cartridges. https://www.lto.org/lto-generation-compatibility/ "LTO drive generations 1-7 are able to read tapes from two generations prior and are able to write to tapes from the prior generation. LTO-8 drives can read and write to LTO-7 and LTO-8 media*/ add_density_support(&lu->den_list, &density_lto7, 1); add_density_support(&lu->den_list, &density_lto8, 1); add_drive_media_list(lu, LOAD_RW, "LTO7"); add_drive_media_list(lu, LOAD_RO, "LTO7 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO7 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO7 ENCR"); add_drive_media_list(lu, LOAD_RW, "LTO8"); add_drive_media_list(lu, LOAD_RO, "LTO8 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO8 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO8 ENCR"); } ================================================ FILE: usr/pm/ibm_03592_pm.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'STREAM' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include #include "be_byteshift.h" #include "mhvtl_scsi.h" #include "vtl_common.h" #include "vtllib.h" #include "logging.h" #include "ssc.h" #include "vtlcart.h" #include "mode.h" #include "mhvtl_log.h" /* Note need to change 'medium density' if encryption is enabled / disabled */ static struct density_info density_j1a = { 0x2e18, 0x0d, 0x200, 0x493e0, medium_density_code_j1a, "IBM", "3592A1", ""}; static struct density_info density_e05 = { 0x2e18, 0x0d, 0x380, 0x7a120, medium_density_code_e05, "IBM", "3592A2", ""}; static struct density_info density_e06 = { 0x348c, 0x0d, 0x480, 0x7a120, medium_density_code_e06, "IBM", "3592A3", ""}; static struct density_info density_e07 = { 0x348c, 0x0d, 0x480, 0x7a120, medium_density_code_e07, "IBM", "3592A4", ""}; static struct name_to_media_info media_info[] = { {"03592 JA", Media_3592_JA, media_type_unknown, medium_density_code_j1a}, {"03592 JA Clean", Media_3592_JA_CLEAN, media_type_unknown, medium_density_code_j1a}, {"03592 JA WORM", Media_3592_JW, media_type_unknown, medium_density_code_j1a}, {"03592 JB", Media_3592_JB, media_type_unknown, medium_density_code_e05}, {"03592 JB Clean", Media_3592_JB_CLEAN, media_type_unknown, medium_density_code_e05}, {"03592 JB ENCR", Media_3592_JB, media_type_unknown, medium_density_code_e05_ENCR}, {"03592 JC", Media_3592_JX, media_type_unknown, medium_density_code_e06}, {"03592 JC Clean", Media_3592_JX_CLEAN, media_type_unknown, medium_density_code_e06}, {"03592 JC ENCR", Media_3592_JX, media_type_unknown, medium_density_code_e06_ENCR}, {"03592 JK", Media_3592_JK, media_type_unknown, medium_density_code_e07}, {"03592 JK Clean", Media_3592_JK_CLEAN, media_type_unknown, medium_density_code_e07}, {"03592 JK ENCR", Media_3592_JK, media_type_unknown, medium_density_code_e07_ENCR}, {"", 0, 0, 0}, }; static uint8_t valid_encryption_media_E06(struct scsi_cmd *cmd) { uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; struct lu_phy_attr *lu; struct priv_lu_ssc *lu_priv; MHVTL_DBG(3, "+++ Trace +++"); lu = cmd->lu; lu_priv = lu->lu_private; if (c_pos->blk_number == 0) { /* 3590 media must be formatted to allow encryption. * This is done by writting an ANSI like label * (NBU label is close enough) to the tape while * an encryption key is in place. The drive doesn't * actually use the key, but sets the tape format */ if (lu_priv->pm->drive_type == drive_3592_E06) { if (lu_priv->ENCRYPT_MODE == 2) { lu_priv->app_encr_info = NULL; mam.Flags |= MAM_FLAGS_ENCRYPTION_FORMAT; } else mam.Flags &= ~MAM_FLAGS_ENCRYPTION_FORMAT; } modeBlockDescriptor[0] = lu_priv->pm->native_drive_density->density; mam.MediumDensityCode = modeBlockDescriptor[0]; mam.FormattedDensityCode = modeBlockDescriptor[0]; rewriteMAM(sam_stat); } else { /* Extra check for 3592 to be sure the cartridge is * formatted for encryption */ if ((lu_priv->pm->drive_type == drive_3592_E06) && lu_priv->ENCRYPT_MODE && !(mam.Flags & MAM_FLAGS_ENCRYPTION_FORMAT)) { sam_data_protect(E_WRITE_PROTECT, sam_stat); return 0; } if (mam.MediumDensityCode != lu_priv->pm->native_drive_density->density) { switch (lu_priv->pm->drive_type) { case drive_3592_E05: if (mam.MediumDensityCode == medium_density_code_j1a) break; sam_data_protect(E_WRITE_PROTECT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; case drive_3592_E06: if (mam.MediumDensityCode == medium_density_code_e05) break; sam_data_protect(E_WRITE_PROTECT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; case drive_3592_E07: if (mam.MediumDensityCode == medium_density_code_e06) break; sam_data_protect(E_WRITE_PROTECT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; default: sam_data_protect(E_WRITE_PROTECT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } } } return SAM_STAT_GOOD; } static uint8_t clear_3592_comp(struct list_head *m) { MHVTL_DBG(3, "+++ Trace +++"); /* default clear_compression is in libvtlscsi */ return clear_compression_mode_pg(m); } static uint8_t set_3592_comp(struct list_head *m, int lvl) { MHVTL_DBG(3, "+++ Trace +++"); /* default set_compression is in libvtlscsi */ return set_compression_mode_pg(m, lvl); } static uint8_t update_3592_encryption_mode(struct list_head *m, void *p, int value) { MHVTL_DBG(3, "+++ Trace +++"); return SAM_STAT_GOOD; } static uint8_t set_3592_WORM(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); return set_WORM(m); } static uint8_t clear_3592_WORM(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); return clear_WORM(m); } static int encr_capabilities_3592(struct scsi_cmd *cmd) { uint8_t *buf = cmd->dbuf_p->data; struct priv_lu_ssc *lu_priv = cmd->lu->lu_private; put_unaligned_be16(ENCR_CAPABILITIES, &buf[0]); put_unaligned_be16(40, &buf[2]); /* List length */ buf[20] = 1; /* Algorithm index */ buf[21] = 0; /* Reserved */ put_unaligned_be16(0x14, &buf[22]); /* Descriptor length */ buf[24] = 0x3a; /* MAC C/DED_C DECRYPT_C = 2 ENCRYPT_C = 2 */ buf[25] = 0x10; /* NONCE_C = 1 */ /* Max unauthenticated key data */ put_unaligned_be16(0x20, &buf[26]); /* Max authenticated key data */ put_unaligned_be16(0x0c, &buf[28]); /* Key size */ put_unaligned_be16(0x20, &buf[30]); buf[32] = 0x01; /* EAREM */ /* buf 12 - 19 reserved */ buf[40] = 0; /* Encryption Algorithm Id */ buf[41] = 0x01; /* Encryption Algorithm Id */ buf[42] = 0; /* Encryption Algorithm Id */ buf[43] = 0x14; /* Encryption Algorithm Id */ /* MHVTL_DBG(1, "Drive type: %s, Media type: %s", drive_name(lunit.drive_type), lookup_media_type(mam.MediaType)); */ if (lu_priv->load_status == TAPE_LOADED) { buf[24] |= 0x80; /* AVFMV */ buf[27] = 0x00; /* Max unauthenticated key data */ buf[32] |= 0x0e; /* RDMC_C == 7 */ } return 44; } static void init_3592_inquiry(struct lu_phy_attr *lu) { int pg; uint8_t worm; uint8_t local_TapeAlert[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; worm = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_WORM; lu->inquiry[2] = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_ANSI_VERSION; /* Sequential Access device capabilities - Ref: 8.4.2 */ pg = PCODE_OFFSET(0xb0); lu->lu_vpd[pg] = alloc_vpd(VPD_B0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b0(lu, &worm); /* Manufacture-assigned serial number - Ref: 8.4.3 */ pg = PCODE_OFFSET(0xb1); lu->lu_vpd[pg] = alloc_vpd(VPD_B1_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b1(lu, lu->lu_serial_no); /* TapeAlert supported flags - Ref: 8.4.4 */ pg = PCODE_OFFSET(0xb2); lu->lu_vpd[pg] = alloc_vpd(VPD_B2_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b2(lu, &local_TapeAlert); /* VPD page 0xC0 */ pg = PCODE_OFFSET(0xc0); lu->lu_vpd[pg] = alloc_vpd(VPD_C0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_c0(lu, "10-03-2008 19:38:00"); /* VPD page 0xC1 */ pg = PCODE_OFFSET(0xc1); lu->lu_vpd[pg] = alloc_vpd(strlen("Security")); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_c1(lu, "Security"); } static int e06_kad_validation(int encrypt_mode, int ukad, int akad) { int count = FALSE; if (ukad > 0 || akad > 12) count = TRUE; if (!encrypt_mode && (ukad || akad)) count = TRUE; return count; } /* Some comments before I forget how this is supose to work.. - cleaning_media_state is either 0 - Not mounted 1 - Cleaning media mounted -> return Cleaning cartridge installed 2 - Cleaning media mounted -> return Cause not reportable 3 - Cleaning media mounted -> return Initializing command required On cleaning media mount, ibm_cleaning() is called which: Sets a pointer from priv_lu_ssc -> cleaning_media_state. Sets cleaning_media_state to 1. Sets a 30 second timer to call inc_cleaning_state() inc_cleaning_state() Increments cleaning_media_state. If cleaning media_state == 2, set another timer for 90 seconds to again call inc_cleaning_state. If the application issues a TUR, ssc_tur() will return one of the above status codes depending on the current value of cleaning_media_state. When the cleaning media is unmounted, the pointer in priv_lu_ssc to this var will be re-set to NULL so the ssc_tur() will return defautl value. */ static volatile sig_atomic_t cleaning_media_state; static void inc_cleaning_state(int sig); static void set_cleaning_timer(int t) { MHVTL_DBG(3, "+++ Trace +++ Setting alarm for %d", t); signal(SIGALRM, inc_cleaning_state); alarm(t); } static void inc_cleaning_state(int sig) { MHVTL_DBG(3, "+++ Trace +++"); signal(sig, inc_cleaning_state); cleaning_media_state++; if (cleaning_media_state == CLEAN_MOUNT_STAGE2) set_cleaning_timer(90); } static uint8_t ibm_media_load(struct lu_phy_attr *lu, int load) { MHVTL_DBG(3, "+++ Trace +++ %s", (load) ? "load" : "unload"); return 0; } static uint8_t ibm_cleaning(void *ssc_priv) { struct priv_lu_ssc *ssc; MHVTL_DBG(3, "+++ Trace +++"); ssc = ssc_priv; ssc->cleaning_media_state = &cleaning_media_state; cleaning_media_state = CLEAN_MOUNT_STAGE1; set_cleaning_timer(30); return 0; } static void init_03592_mode_pages(struct lu_phy_attr *lu) { add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); } static char *pm_name_j1a = "03592J1A"; static char *pm_name_e05 = "03592E05"; static char *pm_name_e06 = "03592E06"; static char *pm_name_e07 = "03592E07"; static struct ssc_personality_template ssc_pm = { .valid_encryption_blk = valid_encryption_blk, .valid_encryption_media = valid_encryption_media_E06, .update_encryption_mode = update_3592_encryption_mode, .kad_validation = e06_kad_validation, .check_restrictions = check_restrictions, .clear_compression = clear_3592_comp, .set_compression = set_3592_comp, .clear_WORM = clear_3592_WORM, .set_WORM = set_3592_WORM, .media_load = ibm_media_load, .cleaning_media = ibm_cleaning, .media_handling = media_info, }; void init_3592_j1a(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_j1a; ssc_pm.lu = lu; ssc_pm.drive_type = drive_3592_J1A; ssc_pm.native_drive_density = &density_j1a; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_3592_inquiry(lu); init_03592_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); add_density_support(&lu->den_list, &density_j1a, 1); add_drive_media_list(lu, LOAD_RW, "03592 JA"); add_drive_media_list(lu, LOAD_RO, "03592 JA Clean"); add_drive_media_list(lu, LOAD_RW, "03592 JA WORM"); } void init_3592_E05(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_e05; ssc_pm.lu = lu; ssc_pm.drive_type = drive_3592_E05; ssc_pm.native_drive_density = &density_e05; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_3592_inquiry(lu); init_03592_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); add_density_support(&lu->den_list, &density_j1a, 1); add_density_support(&lu->den_list, &density_e05, 1); add_drive_media_list(lu, LOAD_RW, "03592 JA"); add_drive_media_list(lu, LOAD_RO, "03592 JA Clean"); add_drive_media_list(lu, LOAD_RW, "03592 JA WORM"); add_drive_media_list(lu, LOAD_RW, "03592 JB"); add_drive_media_list(lu, LOAD_RO, "03592 JB Clean"); add_drive_media_list(lu, LOAD_RW, "03592 JB WORM"); } void init_3592_E06(struct lu_phy_attr *lu) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", &lu->mode_pg); ssc_pm.name = pm_name_e06; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_e06; ssc_pm.encryption_capabilities = encr_capabilities_3592; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_3592_inquiry(lu); init_03592_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); ssc_pm.drive_type = drive_3592_E06; register_ops(lu, SECURITY_PROTOCOL_IN, ssc_spin, NULL, NULL); register_ops(lu, SECURITY_PROTOCOL_OUT, ssc_spout, NULL, NULL); add_density_support(&lu->den_list, &density_j1a, 0); add_density_support(&lu->den_list, &density_e05, 1); add_density_support(&lu->den_list, &density_e06, 1); add_drive_media_list(lu, LOAD_RW, "03592 JA"); add_drive_media_list(lu, LOAD_RO, "03592 JA Clean"); add_drive_media_list(lu, LOAD_RW, "03592 JA WORM"); add_drive_media_list(lu, LOAD_RW, "03592 JB"); add_drive_media_list(lu, LOAD_RO, "03592 JB Clean"); add_drive_media_list(lu, LOAD_RW, "03592 JB WORM"); add_drive_media_list(lu, LOAD_RW, "03592 JC"); add_drive_media_list(lu, LOAD_RO, "03592 JC Clean"); add_drive_media_list(lu, LOAD_RW, "03592 JC WORM"); } void init_3592_E07(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_e07; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_e07; ssc_pm.encryption_capabilities = encr_capabilities_3592; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_3592_inquiry(lu); init_03592_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); ssc_pm.drive_type = drive_3592_E07; register_ops(lu, SECURITY_PROTOCOL_IN, ssc_spin, NULL, NULL); register_ops(lu, SECURITY_PROTOCOL_OUT, ssc_spout, NULL, NULL); add_density_support(&lu->den_list, &density_e05, 0); add_density_support(&lu->den_list, &density_e06, 1); add_density_support(&lu->den_list, &density_e07, 1); add_drive_media_list(lu, LOAD_RW, "03592 JB"); add_drive_media_list(lu, LOAD_RO, "03592 JB Clean"); add_drive_media_list(lu, LOAD_RW, "03592 JB WORM"); add_drive_media_list(lu, LOAD_RW, "03592 JC"); add_drive_media_list(lu, LOAD_RO, "03592 JC Clean"); add_drive_media_list(lu, LOAD_RW, "03592 JC WORM"); add_drive_media_list(lu, LOAD_RW, "03592 JK"); add_drive_media_list(lu, LOAD_RO, "03592 JK Clean"); add_drive_media_list(lu, LOAD_RW, "03592 JK WORM"); } ================================================ FILE: usr/pm/ibm_smc_pm.c ================================================ /* * Personality module for IBM TotalStorage(c) 3584 series of robots * e.g. TS3500 */ #include #include #include "vtllib.h" #include "mhvtl_scsi.h" #include "smc.h" #include "logging.h" #include "be_byteshift.h" static struct smc_personality_template smc_pm = { .library_has_map = TRUE, .library_has_barcode_reader = TRUE, .library_has_playground = TRUE, .dvcid_len = 34, }; /* * Undocumented page - raw dump from a real library.. */ static void update_ibm_3100_vpd_d0(struct lu_phy_attr *lu) { struct vpd **lu_vpd; uint8_t *d; int pg; uint8_t pg_d0[] = { 0x08, 0xd0, 0x00, 0xc0, 0x04, 0x53, 0x43, 0x44, 0x44, 0x00, 0x04, 0x04, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x06, 0x04, 0x22, 0x80, 0x00, 0x00, 0x00, 0x08, 0x07, 0x80, 0x00, 0x00, 0x05, 0x00, 0x00, 0x0a, 0x00, 0x0b, 0x08, 0x80, 0x00, 0x00, 0x00, 0x35, 0x73, 0x00, 0x40, 0x00, 0x0e, 0x02, 0x00, 0xff, 0x00, 0x0f, 0x01, 0xff, 0x00, 0x10, 0x02, 0x00, 0xff, 0x00, 0x11, 0x10, 0x04, 0x8e, 0x82, 0x72, 0x04, 0x83, 0x82, 0x75, 0x3b, 0x12, 0x82, 0x75, 0x04, 0x12, 0x82, 0x76, 0x00, 0x14, 0x3a, 0x00, 0x01, 0x03, 0x01, 0x07, 0x0b, 0x12, 0x01, 0x15, 0x01, 0x16, 0x01, 0x17, 0x01, 0x1a, 0x01, 0x1b, 0x0a, 0x1d, 0x01, 0x1e, 0x01, 0x2b, 0x0a, 0x37, 0x0b, 0x3b, 0x03, 0x3c, 0x01, 0x4c, 0x01, 0x4d, 0x01, 0x55, 0x01, 0x56, 0x01, 0x57, 0x01, 0x5a, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0xa3, 0x01, 0xa4, 0x01, 0xa5, 0x19, 0xb5, 0x01, 0xb6, 0x01, 0xb8, 0x01, 0x00, 0x16, 0x03, 0x80, 0x24, 0x02, 0x00, 0x17, 0x11, 0x00, 0x00, 0x10, 0x20, 0x06, 0x01, 0x00, 0x10, 0x20, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x01, 0x60, 0x00, 0x1c, 0x05, 0x00, 0x00, 0x06, 0x06, 0x02, 0x00, 0x24, 0x0c, 0x83, 0x00, 0x83, 0x03, 0x83, 0x30, 0x83, 0x11, 0x3b, 0x12, 0x83, 0x02, 0x00, 0x26, 0x02, 0x00, 0x05}; lu_vpd = lu->lu_vpd; pg = PCODE_OFFSET(0xd0); if (lu_vpd[pg]) /* Free any earlier allocation */ dealloc_vpd(lu_vpd[pg]); lu_vpd[pg] = alloc_vpd(0xc8); if (!lu_vpd[pg]) { MHVTL_ERR("Could not malloc(0xc8) bytes, line %d", __LINE__); return; } d = lu_vpd[pg]->data; memcpy(d, &pg_d0[0], sizeof(pg_d0)); } /* * Undocumented page - raw dump from a real library.. */ static void update_ibm_3100_vpd_ff(struct lu_phy_attr *lu) { struct vpd **lu_vpd; uint8_t *d; int pg; uint8_t pg_ff[] = { 0x08, 0xff, 0x00, 0x20, 0xb5, 0x8e, 0xb0, 0x0e, 0x1c, 0x0e, 0x98, 0x0e, 0x30, 0x0b, 0x90, 0x35, 0x1c, 0x0b, 0x35, 0x0b, 0x98, 0x40, 0x78, 0xc0, 0x07, 0x71, 0xd4, 0x0b, 0x98, 0x80, 0x78, 0x00, 0x28, 0x0f, 0xd0, 0x34}; lu_vpd = lu->lu_vpd; pg = PCODE_OFFSET(0xff); if (lu_vpd[pg]) /* Free any earlier allocation */ dealloc_vpd(lu_vpd[pg]); lu_vpd[pg] = alloc_vpd(0x26); if (!lu_vpd[pg]) { MHVTL_ERR("Could not malloc(0x26) bytes, line %d", __LINE__); return; } d = lu_vpd[pg]->data; memcpy(d, &pg_ff[0], sizeof(pg_ff)); } static void update_3573_device_capabilities(struct lu_phy_attr *lu) { struct mode *mp; mp = lookup_mode_pg(&lu->mode_pg, MODE_DEVICE_CAPABILITIES, 0); if (!mp) { /* Can't find page ??? */ MHVTL_ERR("Can't find MODE_DEVICE_CAPABILITIES page"); return; } mp->pcodePointer[2] = 0x0e; mp->pcodePointer[3] = 0x00; mp->pcodePointer[4] = 0x0e; /* Medium Transport Capabilities */ mp->pcodePointer[5] = 0x0e; /* Storage Element Capabilities */ mp->pcodePointer[6] = 0x0e; /* MAP Element Capabilities */ mp->pcodePointer[7] = 0x0e; /* Data Trans. Element Capabilities */ mp->pcodePointer[12] = 0x0e; /* Medium Transport Capabilities */ mp->pcodePointer[13] = 0x0e; /* Storage Element Capabilities */ mp->pcodePointer[14] = 0x0e; /* MAP Element Capabilities */ mp->pcodePointer[15] = 0x0e; /* Data Trans. Element Capabilities */ } static void update_3584_device_capabilities(struct lu_phy_attr *lu) { struct mode *mp; mp = lookup_mode_pg(&lu->mode_pg, MODE_DEVICE_CAPABILITIES, 0); if (!mp) { /* Can't find page ??? */ MHVTL_ERR("Can't find MODE_DEVICE_CAPABILITIES page"); return; } mp->pcodePointer[2] = 0x0e; mp->pcodePointer[3] = 0x00; mp->pcodePointer[4] = 0x0e; /* Medium Transport Capabilities */ mp->pcodePointer[5] = 0x0e; /* Storage Element Capabilities */ mp->pcodePointer[6] = 0x0e; /* MAP Element Capabilities */ mp->pcodePointer[7] = 0x0e; /* Data Trans. Element Capabilities */ mp->pcodePointer[12] = 0x0e; /* Medium Transport Capabilities */ mp->pcodePointer[13] = 0x0e; /* Storage Element Capabilities */ mp->pcodePointer[14] = 0x0e; /* MAP Element Capabilities */ mp->pcodePointer[15] = 0x0e; /* Data Trans. Element Capabilities */ } static void update_ibm_3584_vpd_80(struct lu_phy_attr *lu) { struct vpd **lu_vpd; struct smc_priv *smc_p; uint8_t *d; int pg; lu_vpd = lu->lu_vpd; smc_p = lu->lu_private; /* Unit Serial Number */ pg = PCODE_OFFSET(0x80); if (lu_vpd[pg]) /* Free any earlier allocation */ dealloc_vpd(lu_vpd[pg]); lu_vpd[pg] = alloc_vpd(0x16); if (!lu_vpd[pg]) { MHVTL_ERR("Could not malloc(0x16) bytes, line %d", __LINE__); return; } d = lu_vpd[pg]->data; /* d[4 - 15] Serial number of device */ snprintf((char *)&d[0], 11, "%-10.10s", lu->lu_serial_no); /* First Storage Element Address */ snprintf((char *)&d[12], 5, "%04x", smc_p->pm->start_storage); } static void update_ibm_3584_vpd_83(struct lu_phy_attr *lu) { struct vpd **lu_vpd; struct smc_priv *smc_p; int pg; uint8_t *d; lu_vpd = lu->lu_vpd; smc_p = lu->lu_private; /* Device Identification */ pg = PCODE_OFFSET(0x83); if (lu_vpd[pg]) /* Free any earlier allocation */ dealloc_vpd(lu_vpd[pg]); lu_vpd[pg] = alloc_vpd(0x2c); if (!lu_vpd[pg]) { MHVTL_ERR("Could not malloc(0x2c) bytes, line %d", __LINE__); return; } d = lu_vpd[pg]->data; d[0] = 2; /* Code set */ d[1] = 1; /* Identifier Type */ d[3] = 0x28; /* Identifier length */ /* Vendor ID */ memcpy(&d[4], &lu->inquiry[8], 8); /* Device type and Model Number */ memcpy(&d[12], &lu->inquiry[16], 16); /* Serial Number of device */ memcpy(&d[28], &lu->inquiry[38], 12); /* First Storage Element Address */ snprintf((char *)&d[40], 5, "%04x", smc_p->pm->start_storage); } static void update_ibm_3584_inquiry(struct lu_phy_attr *lu) { lu->inquiry[2] = 3; /* SNSI Approved Version */ lu->inquiry[3] = 2; /* Response data format */ lu->inquiry[4] = 0x35; /* Additional length */ memcpy(&lu->inquiry[38], &lu->lu_serial_no, 12); lu->inquiry[50] = 0x30; lu->inquiry[51] = 0x30; } static void update_ibm_3100_inquiry(struct lu_phy_attr *lu) { struct smc_priv *smc_p; smc_p = lu->lu_private; lu->inquiry[2] = 5; /* SNSI Approved Version */ lu->inquiry[3] = 2; /* Response data format */ lu->inquiry[4] = 0x43; /* Additional length */ memcpy(&lu->inquiry[38], &lu->lu_serial_no, 12); lu->inquiry[55] |= smc_p->pm->library_has_barcode_reader ? 1 : 0; put_unaligned_be16(0x005c, &lu->inquiry[58]); /* SAM-2 */ put_unaligned_be16(0x0b56, &lu->inquiry[60]); /* SPI-4 */ put_unaligned_be16(0x02fe, &lu->inquiry[62]); /* SMC-2 */ put_unaligned_be16(0x030f, &lu->inquiry[64]); /* SPC-3 */ } static void update_ibm_3100_vpd_80(struct lu_phy_attr *lu) { struct vpd **lu_vpd = lu->lu_vpd; uint8_t *d; int pg; /* Unit Serial Number */ pg = PCODE_OFFSET(0x80); if (lu_vpd[pg]) /* Free any earlier allocation */ dealloc_vpd(lu_vpd[pg]); lu_vpd[pg] = alloc_vpd(0x10); if (!lu_vpd[pg]) { MHVTL_ERR("Could not malloc(0x10) bytes, line %d", __LINE__); return; } d = lu_vpd[pg]->data; /* d[4 - 15] Serial number of device */ snprintf((char *)&d[0], 13, "%-12.12s", lu->lu_serial_no); /* Unique Logical Library Identifier */ memcpy(&d[12], "_LL0", 4); } static void update_ibm_3100_vpd_83(struct lu_phy_attr *lu) { struct vpd **lu_vpd = lu->lu_vpd; int pg; uint8_t *d; /* Device Identification */ pg = PCODE_OFFSET(0x83); if (lu_vpd[pg]) /* Free any earlier allocation */ dealloc_vpd(lu_vpd[pg]); lu_vpd[pg] = alloc_vpd(0x2c); if (!lu_vpd[pg]) { MHVTL_ERR("Could not malloc(0x2c) bytes, line %d", __LINE__); return; } d = lu_vpd[pg]->data; d[0] = 2; /* Code set */ d[1] = 1; /* Identifier Type */ d[3] = 0x28; /* Identifier length */ /* Vendor ID */ memcpy(&d[4], &lu->inquiry[8], 8); /* Device type and Model Number */ memcpy(&d[12], &lu->inquiry[16], 16); /* Serial Number of device */ memcpy(&d[28], &lu->inquiry[38], 12); /* Unique Logical Library Identifier */ memcpy(&d[40], "_LL0", 4); } static void update_ibm_3100_vpd_c0(struct lu_phy_attr *lu) { struct vpd **lu_vpd = lu->lu_vpd; int pg; uint8_t *d; int y, m, dd, hh, mm, ss; ymd(&y, &m, &dd, &hh, &mm, &ss); /* Device Identification */ pg = PCODE_OFFSET(0xc0); if (lu_vpd[pg]) /* Free any earlier allocation */ dealloc_vpd(lu_vpd[pg]); lu_vpd[pg] = alloc_vpd(0x40); if (!lu_vpd[pg]) { MHVTL_ERR("Could not malloc(0x40) bytes, line %d", __LINE__); return; } d = lu_vpd[pg]->data; sprintf((char *)&d[4], "%s", "DEAD"); /* Media f/w checksum */ /* Media changer firmware build date (mm-dd-yyyy) */ snprintf((char *)&d[8], 22, "%02d-%02d-%04d", m, dd, y); } /* 3573TL */ void init_ibmts3100(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - IBM TS3100 series emulation"; smc_pm.library_has_map = TRUE; smc_pm.library_has_barcode_reader = TRUE; smc_pm.library_has_playground = TRUE; /* Follow IBM TS3100 & TS3200 SCSI Reference Manual */ smc_pm.start_picker = 0x0001; smc_pm.start_map = 0x0010; smc_pm.start_drive = 0x0100; smc_pm.start_storage = 0x1000; smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); /* Update vendor specific info in main INQUIRY page */ update_ibm_3100_inquiry(lu); /* Need slot info before we can fill out VPD data */ update_ibm_3100_vpd_80(lu); update_ibm_3100_vpd_83(lu); /* IBM Doco hints at VPD page 0xd0 & 0xff - but does not document it */ /* * lsscsi -g * [2:0:1:0] tape IBM ULT3580-TD4 8192 /dev/st0 /dev/sg2 * [2:0:1:1] mediumx IBM 3573-TL 6.50 - /dev/sg3 * * # sg_inq -p 0 /dev/sg3 * Only hex output supported * VPD INQUIRY, page code=0x00: * [PQual=0 Peripheral device type: medium changer] * Supported VPD pages: * 0x0 Supported VPD pages * 0x80 Unit serial number * 0x83 Device identification * 0xc0 vendor: Firmware numbers (seagate); Unit path report (EMC) * 0xd0 * 0xff * # sg_inq -H -p 0xc0 /dev/sg3 * VPD INQUIRY, page code=0xc0: * 00 08 c0 00 3c 00 00 00 00 34 38 34 37 30 34 2d 30 ...<....484704-0 * 10 33 2d 32 30 30 38 20 20 20 20 20 20 20 20 20 20 3-2008 * 20 20 20 20 20 00 00 00 00 00 00 00 00 00 00 00 00 ............ * 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ */ update_ibm_3100_vpd_c0(lu); /* * # sg_inq -p 0xd0 /dev/sg3 * VPD INQUIRY, page code=0xd0: * 00 08 d0 00 c0 04 53 43 44 44 00 04 04 c0 00 00 00 .....SCDD....... * 10 00 06 04 22 80 00 00 00 08 07 80 00 00 05 00 00 ..."............ * 20 0a 00 0b 08 80 00 00 00 35 73 00 40 00 0e 02 00 ........5s.@.... * 30 ff 00 0f 01 ff 00 10 02 00 ff 00 11 10 04 8e 82 ................ * 40 72 04 83 82 75 3b 12 82 75 04 12 82 76 00 14 3a r...u;..u...v..: * 50 00 01 03 01 07 0b 12 01 15 01 16 01 17 01 1a 01 ................ * 60 1b 0a 1d 01 1e 01 2b 0a 37 0b 3b 03 3c 01 4c 01 ......+.7.;.<.L. * 70 4d 01 55 01 56 01 57 01 5a 01 5e 01 5f 01 a3 01 M.U.V.W.Z.^._... * 80 a4 01 a5 19 b5 01 b6 01 b8 01 00 16 03 80 24 02 ..............$. * 90 00 17 11 00 00 10 20 06 01 00 10 20 06 01 01 00 ...... .... .... * a0 00 00 00 00 00 1b 01 60 00 1c 05 00 00 06 06 02 .......`........ * b0 00 24 0c 83 00 83 03 83 30 83 11 3b 12 83 02 00 .$......0..;.... * c0 26 02 00 05 &... */ update_ibm_3100_vpd_d0(lu); /* * # sg_inq -p 0xff /dev/sg3 * VPD INQUIRY, page code=0xff: * 00 08 ff 00 20 b5 8e b0 0e 1c 0e 98 0e 30 0b 90 35 ... ........0..5 * 10 1c 0b 35 0b 98 40 78 c0 07 71 d4 0b 98 80 78 00 ..5..@x..q....x. * 20 28 0f d0 34 (..4 * */ update_ibm_3100_vpd_ff(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); update_3573_device_capabilities(lu); } /* * This should be fun to keep in sync.. * * 03584L22 => 03592 drives * 03584L32 => LTO drives * 03584L42 => DLT drives */ void init_ibm3584(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - IBM 03584 series emulation"; smc_pm.library_has_map = TRUE; smc_pm.library_has_barcode_reader = TRUE; smc_pm.library_has_playground = TRUE; /* Follow IBM 3584 SCSI Reference Manual */ smc_pm.start_picker = 0x0001; smc_pm.start_drive = 0x0101; smc_pm.start_map = 0x0300; smc_pm.start_storage = 0x0400; smc_pm.lu = lu; smc_personality_module_register(&smc_pm); /* Initialise order 'Picker, Drives, MAP, Storage */ init_slot_info(lu); /* Update vendor specific info in main INQUIRY page */ update_ibm_3584_inquiry(lu); /* Need slot info before we can fill out VPD data */ update_ibm_3584_vpd_80(lu); update_ibm_3584_vpd_83(lu); /* IBM Doco hints at VPD page 0xd0 - but does not document it */ /* At least log what we should contain * Need to identify a sanity check on incorrect * drive config later * * Forth edition of the IBM System Storage TS3500 Tape Library SCSI * Reference (August 2011) * */ if (!strncasecmp(lu->product_id, "03584L22", 8) || !strncasecmp(lu->product_id, "03584L23", 8) || !strncasecmp(lu->product_id, "03584D22", 8) || !strncasecmp(lu->product_id, "03584D23", 8)) { MHVTL_LOG("%s library should contain 03592 drives", lu->product_id); } else if (!strncasecmp(lu->product_id, "03584L32", 8) || !strncasecmp(lu->product_id, "03584D32", 8) || !strncasecmp(lu->product_id, "03584L52", 8) || !strncasecmp(lu->product_id, "03584D52", 8) || !strncasecmp(lu->product_id, "03584L53", 8) || !strncasecmp(lu->product_id, "03584D53", 8)) { MHVTL_LOG("%s library should contain LTO drives", lu->product_id); } else if (!strncasecmp(lu->product_id, "03584L42", 8) || !strncasecmp(lu->product_id, "03584L42", 8)) { MHVTL_LOG("%s library should contain DLT drives", lu->product_id); } else { MHVTL_ERR("%s library model not known", lu->product_id); } init_smc_log_pages(lu); init_smc_mode_pages(lu); update_3584_device_capabilities(lu); } ================================================ FILE: usr/pm/overland_pm.c ================================================ /* * Personality module for OVERLAND */ #include #include #include "vtllib.h" #include "smc.h" #include "logging.h" static void update_eml_vpd_80(struct lu_phy_attr *lu) { struct vpd **lu_vpd = lu->lu_vpd; uint8_t *d; int pg; /* Unit Serial Number */ pg = PCODE_OFFSET(0x80); if (lu_vpd[pg]) /* Free any earlier allocation */ dealloc_vpd(lu_vpd[pg]); lu_vpd[pg] = alloc_vpd(0x12); if (lu_vpd[pg]) { d = lu_vpd[pg]->data; /* d[4 - 15] Serial number of device */ snprintf((char *)&d[0], 11, "%-10.10s", lu->lu_serial_no); /* Unique Logical Library Identifier */ } else { MHVTL_ERR("Could not malloc(0x12) bytes, line %d", __LINE__); } } static void update_eml_vpd_83(struct lu_phy_attr *lu) { struct vpd *vpd_pg = lu->lu_vpd[PCODE_OFFSET(0x83)]; uint8_t *d; int num; char *ptr; int len, j; d = vpd_pg->data; d[0] = 2; d[1] = 1; d[2] = 0; num = VENDOR_ID_LEN + PRODUCT_ID_LEN + 10; d[3] = num; memcpy(&d[4], &lu->vendor_id, VENDOR_ID_LEN); memcpy(&d[12], &lu->product_id, PRODUCT_ID_LEN); memcpy(&d[28], &lu->lu_serial_no, 10); len = (int)strlen(lu->lu_serial_no); ptr = &lu->lu_serial_no[len]; num += 4; /* NAA IEEE registered identifier (faked) */ d[num] = 0x1; /* Binary */ d[num + 1] = 0x3; d[num + 2] = 0x0; d[num + 3] = 0x8; d[num + 4] = 0x51; d[num + 5] = 0x23; d[num + 6] = 0x45; d[num + 7] = 0x60; d[num + 8] = 0x3; d[num + 9] = 0x3; d[num + 10] = 0x3; d[num + 11] = 0x3; if (lu->naa) { /* If defined in config file */ sscanf((const char *)lu->naa, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &d[num + 4], &d[num + 5], &d[num + 6], &d[num + 7], &d[num + 8], &d[num + 9], &d[num + 10], &d[num + 11]); } else { /* Else munge the serial number */ ptr--; for (j = 11; j > 3; ptr--, j--) d[num + j] = *ptr; } d[num + 4] &= 0x0f; d[num + 4] |= 0x50; } static struct smc_personality_template smc_pm = { .library_has_map = TRUE, .library_has_barcode_reader = TRUE, .library_has_playground = FALSE, .start_map = 0x0000, .start_picker = 0x0001, .start_storage = 0x0002, .start_drive = 0x00ff, .dvcid_len = 34, }; void init_overland_smc(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - Overland Series emulation"; smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); update_eml_vpd_80(lu); update_eml_vpd_83(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); } ================================================ FILE: usr/pm/quantum_dlt_pm.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'STREAM' * * Copyright (C) 2005 - 2025 Mark Harvey markh794@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include #include "mhvtl_scsi.h" #include "vtl_common.h" #include "vtllib.h" #include "vtlcart.h" #include "logging.h" #include "ssc.h" #include "spc.h" #include "mode.h" #include "mhvtl_log.h" /* FIXME: This data needs to be updated to suit SDLT range of media */ static struct density_info density_dlt2 = { 1000, 640, 64, 1000, medium_density_code_dlt2, "DLT-CVE", "DLT II", "DLTtape II"}; static struct density_info density_dlt3 = { 62500, 640, 64, 20000, medium_density_code_dlt3, "DLT-CVE", "DLT III", "DLTtape III"}; static struct density_info density_dlt4 = { 85937, 640, 52, 35000, medium_density_code_dlt4, "DLT-CVE", "DLT IV", "DLTtape IV"}; static struct density_info density_sdlt = { 15142, 640, 1502, 80000, medium_density_code_sdlt, "DLT-CVE", "U-216", "SDLT"}; static struct density_info density_sdlt220 = { 15142, 640, 1502, 80000, medium_density_code_220, "DLT-CVE", "U-316", "SDLT 220"}; static struct density_info density_sdlt320 = { 15142, 640, 1502, 80000, medium_density_code_320, "DLT-CVE", "U-416", "SDLT 320"}; static struct density_info density_sdlt600 = { 15142, 640, 1502, 80000, medium_density_code_600, "DLT-CVE", "U-516", "SDLT 600"}; static struct name_to_media_info media_info[] = { {"DLT2", Media_DLT2, media_type_unknown, medium_density_code_dlt2}, {"DLT3", Media_DLT3, media_type_unknown, medium_density_code_dlt3}, {"DLT4", Media_DLT4, media_type_unknown, medium_density_code_dlt4}, {"SDLT", Media_SDLT, media_type_unknown, medium_density_code_sdlt}, {"SDLT 220", Media_SDLT220, media_type_unknown, medium_density_code_220}, {"SDLT 320", Media_SDLT320, media_type_unknown, medium_density_code_320}, {"SDLT 320 Clean", Media_SDLT320_CLEAN, media_type_unknown, medium_density_code_320}, {"SDLT 600", Media_SDLT600, media_type_unknown, medium_density_code_600}, {"SDLT 600 Clean", Media_SDLT600_CLEAN, media_type_unknown, medium_density_code_600}, {"SDLT 600 WORM", Media_SDLT600_WORM, media_type_unknown, medium_density_code_600}, {"", 0, 0, 0}, }; static uint8_t clear_dlt_compression(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); /* default clear_compression is in libvtlscsi */ return clear_compression_mode_pg(m); } static uint8_t set_dlt_compression(struct list_head *m, int lvl) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); /* default set_compression is in libvtlscsi */ return set_compression_mode_pg(m, lvl); } /* As per IBM LTO5 SCSI Programmers Guide.. * Filling in compile time/date & dummy 'platform' string */ static void update_vpd_dlt_c0(struct lu_phy_attr *lu) { uint8_t *data; struct vpd *vpd_p; int h, m, s; int day, month, year; vpd_p = lu->lu_vpd[PCODE_OFFSET(0xc0)]; data = vpd_p->data; month = 0; ymd(&year, &month, &day, &h, &m, &s); data[1] = 0xc0; data[3] = 0x28; /* Controller firmware build date */ sprintf((char *)&data[20], "%02d-%02d-%04d %02d:%02d:%02d", day, month, year, h, m, s); } static int get_product_family(struct lu_phy_attr *lu) { int ret; if (!strncmp(lu->product_id, "SDLT600", 7)) ret = 0xc0; /* Product Family - (300/600 GB) */ else if (!strncmp(lu->product_id, "SDLT 320", 8)) ret = 0xb0; /* Product Family - (160/320 GB) */ else ret = 0xa0; /* Product Family - (110/220 GB) */ return ret; } static void update_vpd_dlt_c1(struct lu_phy_attr *lu, char *sn) { uint8_t *data; struct vpd *vpd_p; vpd_p = lu->lu_vpd[PCODE_OFFSET(0xc1)]; data = vpd_p->data; data[1] = 0xc1; data[3] = 0x39; data[4] = get_product_family(lu); snprintf((char *)&data[4], 13, "%-12s", sn); snprintf((char *)&data[24], 13, "%-12s", sn); } static uint8_t set_dlt_WORM(struct list_head *lst) { uint8_t *mp; struct mode *m; set_WORM(lst); /* Default WORM setup */ /* Now for the Ultrium unique stuff */ m = lookup_mode_pg(lst, MODE_BEHAVIOR_CONFIGURATION, 0); if (m) { MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", lst, m, m->pcodePointer); mp = m->pcodePointer; if (!mp) return SAM_STAT_GOOD; mp[4] = 0x01; /* WORM Behavior */ } else { MHVTL_DBG(2, "MODE BEHAVIOUR CONFIGURATION page not found"); } return SAM_STAT_GOOD; } static uint8_t clear_dlt_WORM(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); return clear_WORM(m); } /* DLT7000 & DLT8000 */ static void init_dlt_inquiry(struct lu_phy_attr *lu) { int pg; char b[32]; int x, y, z; lu->inquiry[2] = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_ANSI_VERSION; lu->inquiry[36] = get_product_family(lu); sprintf(b, "%s", MHVTL_VERSION); sscanf(b, "%d.%d.%d", &x, &y, &z); if (x) { lu->inquiry[37] = x; lu->inquiry[38] = y; } else { lu->inquiry[37] = y; lu->inquiry[38] = z; } /* VPD page 0xC0 */ pg = PCODE_OFFSET(0xc0); lu->lu_vpd[pg] = alloc_vpd(44); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_dlt_c0(lu); /* VPD page 0xC1 */ pg = PCODE_OFFSET(0xc1); lu->lu_vpd[pg] = alloc_vpd(44); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_dlt_c1(lu, lu->lu_serial_no); } /* SuperDLT range */ static void init_sdlt_inquiry(struct lu_phy_attr *lu) { int pg; uint8_t worm; uint8_t ta[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; char b[32]; int x, y, z; worm = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_WORM; lu->inquiry[2] = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_ANSI_VERSION; lu->inquiry[36] = get_product_family(lu); sprintf(b, "%s", MHVTL_VERSION); sscanf(b, "%d.%d.%d", &x, &y, &z); if (x) { lu->inquiry[37] = x; lu->inquiry[38] = y; } else { lu->inquiry[37] = y; lu->inquiry[38] = z; } /* Sequential Access device capabilities - Ref: 8.4.2 */ pg = PCODE_OFFSET(0xb0); lu->lu_vpd[pg] = alloc_vpd(VPD_B0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b0(lu, &worm); /* Manufacture-assigned serial number - Ref: 8.4.3 */ pg = PCODE_OFFSET(0xb1); lu->lu_vpd[pg] = alloc_vpd(VPD_B1_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b1(lu, lu->lu_serial_no); /* TapeAlert supported flags - Ref: 8.4.4 */ pg = PCODE_OFFSET(0xb2); lu->lu_vpd[pg] = alloc_vpd(VPD_B2_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b2(lu, &ta); /* VPD page 0xC0 */ pg = PCODE_OFFSET(0xc0); lu->lu_vpd[pg] = alloc_vpd(44); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_dlt_c0(lu); /* VPD page 0xC1 */ pg = PCODE_OFFSET(0xc1); lu->lu_vpd[pg] = alloc_vpd(44); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_dlt_c1(lu, lu->lu_serial_no); } /* Some comments before I forget how this is supose to work.. - cleaning_media_state is either 0 - Not mounted 1 - Cleaning media mounted -> return Cleaning cartridge installed 2 - Cleaning media mounted -> return Cause not reportable 3 - Cleaning media mounted -> return Initializing command required On cleaning media mount, dlt_cleaning() is called which: Sets a pointer from priv_lu_ssc -> cleaning_media_state. Sets cleaning_media_state to 1. Sets a 30 second timer to call inc_cleaning_state() inc_cleaning_state() Increments cleaning_media_state. If cleaning media_state == 2, set another timer for 90 seconds to again call inc_cleaning_state. If the application issues a TUR, ssc_tur() will return one of the above status codes depending on the current value of cleaning_media_state. When the cleaning media is unmounted, the pointer in priv_lu_ssc to this var will be re-set to NULL so the ssc_tur() will return defautl value. */ static volatile sig_atomic_t cleaning_media_state; static void inc_cleaning_state(int sig); static void set_cleaning_timer(int t) { MHVTL_DBG(3, "+++ Trace +++ Setting alarm for %d", t); signal(SIGALRM, inc_cleaning_state); alarm(t); } static void inc_cleaning_state(int sig) { MHVTL_DBG(3, "+++ Trace +++"); signal(sig, inc_cleaning_state); cleaning_media_state++; if (cleaning_media_state == CLEAN_MOUNT_STAGE2) set_cleaning_timer(90); } static uint8_t dlt_media_load(struct lu_phy_attr *lu, int load) { MHVTL_DBG(3, "+++ Trace +++ %s", (load) ? "load" : "unload"); return 0; } static uint8_t dlt_cleaning(void *ssc_priv) { struct priv_lu_ssc *ssc; MHVTL_DBG(3, "+++ Trace +++"); ssc = ssc_priv; ssc->cleaning_media_state = &cleaning_media_state; cleaning_media_state = CLEAN_MOUNT_STAGE1; set_cleaning_timer(30); return 0; } static char *pm_name_dlt7000 = "DLT7000"; static char *pm_name_dlt8000 = "DLT8000"; static char *pm_name_sdlt320 = "SDLT320"; static char *pm_name_sdlt600 = "SDLT600"; static struct ssc_personality_template ssc_pm = { .valid_encryption_blk = valid_encryption_blk, .check_restrictions = check_restrictions, /* default in ssc.c */ .clear_compression = clear_dlt_compression, .set_compression = set_dlt_compression, .media_load = dlt_media_load, .cleaning_media = dlt_cleaning, .media_handling = media_info, }; void init_dlt7000_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_dlt7000; ssc_pm.lu = lu; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = FALSE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = FALSE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 2; ssc_personality_module_register(&ssc_pm); init_dlt_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_medium_partition(lu); add_mode_information_exception(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); ssc_pm.native_drive_density = &density_dlt4; /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_dlt2, 0); add_density_support(&lu->den_list, &density_dlt3, 1); add_density_support(&lu->den_list, &density_dlt4, 1); add_drive_media_list(lu, LOAD_RO, "DLT3"); add_drive_media_list(lu, LOAD_RW, "DLT4"); add_drive_media_list(lu, LOAD_RO, "DLT4 Clean"); } void init_dlt8000_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_dlt8000; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_dlt4; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = FALSE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = FALSE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 2; ssc_personality_module_register(&ssc_pm); init_dlt_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_medium_partition(lu); add_mode_information_exception(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; /* Don't support PERSISTENT RESERVATION */ register_ops(lu, PERSISTENT_RESERVE_IN, spc_illegal_op, NULL, NULL); register_ops(lu, PERSISTENT_RESERVE_OUT, spc_illegal_op, NULL, NULL); add_density_support(&lu->den_list, &density_dlt2, 0); add_density_support(&lu->den_list, &density_dlt3, 1); add_density_support(&lu->den_list, &density_dlt4, 1); add_drive_media_list(lu, LOAD_RO, "DLT3"); add_drive_media_list(lu, LOAD_RW, "DLT4"); add_drive_media_list(lu, LOAD_RO, "DLT4 Clean"); } void init_sdlt320_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_sdlt320; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_sdlt320; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_sdlt_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_sdlt, 0); add_density_support(&lu->den_list, &density_sdlt220, 1); add_density_support(&lu->den_list, &density_sdlt320, 1); add_drive_media_list(lu, LOAD_RO, "SDLT"); add_drive_media_list(lu, LOAD_RW, "SDLT 220"); add_drive_media_list(lu, LOAD_RW, "SDLT 320"); add_drive_media_list(lu, LOAD_RO, "SDLT 320 Clean"); } void init_sdlt600_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_sdlt600; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_sdlt600; ssc_pm.clear_WORM = clear_dlt_WORM; ssc_pm.set_WORM = set_dlt_WORM; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_sdlt_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_sdlt220, 0); add_density_support(&lu->den_list, &density_sdlt320, 1); add_density_support(&lu->den_list, &density_sdlt600, 1); add_drive_media_list(lu, LOAD_RO, "SDLT 220"); add_drive_media_list(lu, LOAD_RW, "SDLT 320"); add_drive_media_list(lu, LOAD_RO, "SDLT 320 Clean"); add_drive_media_list(lu, LOAD_RW, "SDLT 600"); add_drive_media_list(lu, LOAD_RO, "SDLT 600 Clean"); add_drive_media_list(lu, LOAD_RW, "SDLT 600 WORM"); } ================================================ FILE: usr/pm/scalar_pm.c ================================================ /* * Personality module for Scalar series of robots */ #include #include "vtllib.h" #include "smc.h" #include "logging.h" #include "be_byteshift.h" static struct smc_personality_template smc_pm = { .library_has_map = TRUE, .library_has_barcode_reader = TRUE, .library_has_playground = FALSE, .dvcid_len = 34, }; static void init_scalar_inquiry(struct lu_phy_attr *lu) { struct smc_priv *smc_p; smc_p = lu->lu_private; lu->inquiry[2] = 3; /* SNSI Approved Version */ lu->inquiry[3] = 2; /* Response data format */ lu->inquiry[4] = 0x1f; /* Additional length */ lu->inquiry[6] |= smc_p->pm->library_has_barcode_reader ? 0x20 : 0; lu->inquiry[55] |= smc_p->pm->library_has_barcode_reader ? 1 : 0; put_unaligned_be16(0x005c, &lu->inquiry[58]); /* SAM-2 */ put_unaligned_be16(0x008d, &lu->inquiry[60]); /* SAM-4 */ put_unaligned_be16(0x0120, &lu->inquiry[62]); /* SPC-3 */ put_unaligned_be16(0x02fe, &lu->inquiry[64]); /* SMC-2 */ } static void update_scalar_vpd_80(struct lu_phy_attr *lu) { struct vpd *lu_vpd; uint8_t *d; lu_vpd = lu->lu_vpd[PCODE_OFFSET(0x80)]; /* Unit Serial Number */ if (lu_vpd) /* Free any earlier allocation */ dealloc_vpd(lu_vpd); lu_vpd = alloc_vpd(24); if (lu_vpd) { d = lu_vpd->data; /* d[4 - 27] Serial number prefixed by Vendor ID */ snprintf((char *)&d[0], 26, "%-s%-17s", lu->vendor_id, lu->lu_serial_no); } else { MHVTL_ERR("Could not malloc(24) bytes, line %d", __LINE__); } } static void update_scalar_vpd_83(struct lu_phy_attr *lu) { struct vpd *lu_vpd; uint8_t *d; lu_vpd = lu->lu_vpd[PCODE_OFFSET(0x83)]; /* Unit Serial Number */ if (lu_vpd) /* Free any earlier allocation */ dealloc_vpd(lu_vpd); lu_vpd = alloc_vpd(36); if (lu_vpd) { d = lu_vpd->data; d[0] = 0xf2; d[1] = 0x01; d[3] = 0x20; snprintf((char *)&d[4], 9, "%-8s", lu->vendor_id); snprintf((char *)&d[12], 25, "%-24s", lu->lu_serial_no); } else { MHVTL_ERR("Could not malloc(36) bytes, line %d", __LINE__); } } void init_scalar_smc(struct lu_phy_attr *lu) { int h, m, sec; int day, month, year; smc_pm.name = "mhVTL - Scalar emulation"; smc_pm.library_has_map = TRUE; smc_pm.library_has_barcode_reader = TRUE; smc_pm.library_has_playground = FALSE; smc_pm.dvcid_serial_only = FALSE; smc_pm.start_picker = 0x0001; smc_pm.start_map = 0x0010; smc_pm.start_drive = 0x0100; smc_pm.start_storage = 0x1000; smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); /* Reference Quantum 6-00423013 SCSI Reference - Rev A */ ymd(&year, &month, &day, &h, &m, &sec); /* Controller firmware build date */ sprintf((char *)&lu->inquiry[36], "%04d-%02d-%02d %02d:%02d:%02d", year, month, day, h, m, sec); init_scalar_inquiry(lu); update_scalar_vpd_80(lu); update_scalar_vpd_83(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); } ================================================ FILE: usr/pm/spectra_pm.c ================================================ /* * Personality module for Spectra Logic */ #include "vtllib.h" #include "smc.h" #include "logging.h" #include "mhvtl_scsi.h" static void update_spectra_215_device_capabilities(struct lu_phy_attr *lu) { struct mode *mp; mp = lookup_mode_pg(&lu->mode_pg, MODE_DEVICE_CAPABILITIES, 0); if (!mp) { /* Can't find page ??? */ MHVTL_ERR("Can't find MODE_DEVICE_CAPABILITIES page"); return; } mp->pcodePointer[2] = 0x0b; mp->pcodePointer[3] = 0x00; mp->pcodePointer[4] = 0x0a; mp->pcodePointer[5] = 0x0b; mp->pcodePointer[6] = 0x00; mp->pcodePointer[7] = 0x0b; } static void update_spectra_gator_device_capabilities(struct lu_phy_attr *lu) { struct mode *mp; mp = lookup_mode_pg(&lu->mode_pg, MODE_DEVICE_CAPABILITIES, 0); if (!mp) { /* Can't find page ??? */ MHVTL_ERR("Can't find MODE_DEVICE_CAPABILITIES page"); return; } mp->pcodePointer[2] = 0x0e; mp->pcodePointer[3] = 0x00; mp->pcodePointer[4] = 0x0e; mp->pcodePointer[5] = 0x0e; mp->pcodePointer[6] = 0x0e; mp->pcodePointer[7] = 0x0e; } static void update_spectra_t_series_device_capabilities(struct lu_phy_attr *lu) { struct mode *mp; mp = lookup_mode_pg(&lu->mode_pg, MODE_DEVICE_CAPABILITIES, 0); if (!mp) { /* Can't find page ??? */ MHVTL_ERR("Can't find MODE_DEVICE_CAPABILITIES page"); return; } mp->pcodePointer[2] = 0x0e; mp->pcodePointer[3] = 0x00; mp->pcodePointer[4] = 0x0e; mp->pcodePointer[5] = 0x0e; mp->pcodePointer[6] = 0x0e; mp->pcodePointer[7] = 0x0e; } static struct smc_personality_template smc_pm = { .library_has_map = TRUE, .library_has_barcode_reader = TRUE, .library_has_playground = FALSE, /* Rev G of SpectraLogic Tseries states this is now 1Eh * Yet... * DVCID=1 'Identifier Length (1Eh) but goes on to describe * fields 52 through 83 are for the Device Identifier -> and that * adds up to 32 by my calculations. And in table 10-5 specifies * Identifier Length as 1Gh... (hmmm forgot to carry the one in * base 16 addition when they got to 1Fh) - which is of course * 20h (32 decimal). */ .dvcid_len = 0x20, }; void init_spectra_215_smc(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - Spectra Treefrog emulation"; smc_pm.library_has_map = FALSE; smc_pm.library_has_barcode_reader = TRUE; smc_pm.dvcid_serial_only = TRUE; smc_pm.no_dvcid_flag = TRUE; smc_pm.dvcid_len = 0; /* Extracted from Spectra Treefrog-Series SCSI Developers guide */ smc_pm.start_picker = 86; smc_pm.start_map = 99; /* fake */ smc_pm.start_drive = 31; smc_pm.start_storage = 1; smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); /* Now that 'init_smc_mode_pages()' has allocated device capabilities * page, update to valid default values for Spectra-Logic treefrog */ update_spectra_215_device_capabilities(lu); } void init_spectra_gator_smc(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - Spectra Gator emulation"; smc_pm.library_has_map = TRUE; smc_pm.library_has_barcode_reader = TRUE; smc_pm.dvcid_serial_only = TRUE; smc_pm.dvcid_len = 10; /* Extracted from Spectra Gator SCSI Developers guide */ smc_pm.start_picker = 0x02c3; smc_pm.start_map = 0x0001; smc_pm.start_drive = 0x02a3; smc_pm.start_storage = 0x001e; smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); /* Now that 'init_smc_mode_pages()' has allocated device capabilities * page, update to valid default values for Spectra-Logic Gator Series */ update_spectra_gator_device_capabilities(lu); } void init_spectra_logic_smc(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - Spectra T-Series emulation"; smc_pm.library_has_map = TRUE; smc_pm.library_has_barcode_reader = TRUE; smc_pm.dvcid_serial_only = TRUE; /* Extracted from Spectra T-Series SCSI Developers guide */ smc_pm.start_picker = 0x0001; smc_pm.start_map = 0x0010; smc_pm.start_drive = 0x0100; smc_pm.start_storage = 0x1000; smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); /* Now that 'init_smc_mode_pages()' has allocated device capabilities * page, update to valid default values for Spectra-Logic T Series */ update_spectra_t_series_device_capabilities(lu); } ================================================ FILE: usr/pm/stk9x40_pm.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'STREAM' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include #include "be_byteshift.h" #include "mhvtl_scsi.h" #include "vtl_common.h" #include "vtllib.h" #include "logging.h" #include "ssc.h" #include "vtlcart.h" #include "mode.h" #include "mhvtl_log.h" static struct density_info density_9840A = { 0, 127, 288, 0x4e20, medium_density_code_9840A, "STK", "R-20", "Raven 20 GB"}; static struct density_info density_9840B = { 0, 127, 288, 0x4e20, medium_density_code_9840B, "STK", "R-20", "Raven 20 GB"}; static struct density_info density_9840C = { 0, 127, 288, 0x9c40, medium_density_code_9840C, "STK", "R-40", "Raven 40 GB"}; static struct density_info density_9840D = { 0, 127, 576, 0x124f8, medium_density_code_9840D, "STK", "R-75", "Raven 75 GB"}; static struct density_info density_9940A = { 0, 127, 288, 0xea60, medium_density_code_9940A, "STK", "P-60", "PeakCapacity 60 GB"}; static struct density_info density_9940B = { 0, 127, 576, 0x30d40, medium_density_code_9940B, "STK", "P-200", "PeakCapacity 200 GB"}; static struct name_to_media_info media_info[] = { {"9840A", Media_9840A, media_type_unknown, medium_density_code_9840A}, {"9840A Clean", Media_9840A_CLEAN, media_type_unknown, medium_density_code_9840A}, {"9840B", Media_9840B, media_type_unknown, medium_density_code_9840B}, {"9840B Clean", Media_9840B_CLEAN, media_type_unknown, medium_density_code_9840B}, {"9840C", Media_9840C, media_type_unknown, medium_density_code_9840C}, {"9840C Clean", Media_9840C_CLEAN, media_type_unknown, medium_density_code_9840C}, {"9840D", Media_9840D, media_type_unknown, medium_density_code_9840D}, {"9840D Clean", Media_9840D_CLEAN, media_type_unknown, medium_density_code_9840D}, {"9940A", Media_9940A, media_type_unknown, medium_density_code_9940A}, {"9940A Clean", Media_9940A_CLEAN, media_type_unknown, medium_density_code_9940A}, {"9940B", Media_9940B, media_type_unknown, medium_density_code_9940B}, {"9940B Clean", Media_9940B_CLEAN, media_type_unknown, medium_density_code_9940B}, {"", 0, 0, 0}, }; /* * Returns true if blk header has correct encryption key data */ #define UKAD_LENGTH (encr->ukad_length) #define AKAD_LENGTH (encr->akad_length) #define KEY_LENGTH (encr->key_length) #define UKAD (encr->ukad) #define AKAD (encr->akad) #define KEY (encr->key) uint8_t valid_encryption_blk_9840(struct scsi_cmd *cmd) { uint8_t correct_key; int i; struct lu_phy_attr *lu = cmd->lu; struct priv_lu_ssc *lu_priv; struct encryption *encr; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; MHVTL_DBG(3, "+++ Trace +++"); lu_priv = lu->lu_private; encr = lu_priv->app_encr_info; /* decryption logic */ correct_key = TRUE; if (c_pos->blk_flags & BLKHDR_FLG_ENCRYPTED) { /* compare the keys - STK requires UKAD back to decrypt */ if (lu_priv->DECRYPT_MODE > 1) { if (c_pos->blk_encryption_info.key_length != KEY_LENGTH) { sam_data_protect(E_INCORRECT_KEY, sam_stat); correct_key = FALSE; return correct_key; } for (i = 0; i < c_pos->blk_encryption_info.key_length; ++i) { if (c_pos->blk_encryption_info.key[i] != KEY[i]) { sam_data_protect(E_INCORRECT_KEY, sam_stat); correct_key = FALSE; break; } } if (c_pos->blk_encryption_info.ukad_length != UKAD_LENGTH) { sam_data_protect(E_INCORRECT_KEY, sam_stat); correct_key = FALSE; return correct_key; } for (i = 0; i < c_pos->blk_encryption_info.ukad_length; ++i) { if (c_pos->blk_encryption_info.ukad[i] != UKAD[i]) { sam_data_protect(E_INCORRECT_KEY, sam_stat); correct_key = FALSE; break; } } } else { sam_data_protect(E_UNABLE_TO_DECRYPT, sam_stat); correct_key = FALSE; } } else if (lu_priv->DECRYPT_MODE == 2) { sam_data_protect(E_UNENCRYPTED_DATA, sam_stat); correct_key = FALSE; } return correct_key; } static uint8_t clear_9840_comp(struct list_head *m) { MHVTL_DBG(3, "+++ Trace +++"); /* default clear_compression is in libvtlscsi */ return clear_compression_mode_pg(m); } static uint8_t set_9840_comp(struct list_head *m, int lvl) { MHVTL_DBG(3, "+++ Trace +++"); /* default set_compression is in libvtlscsi */ return set_compression_mode_pg(m, lvl); } static uint8_t update_9840_encryption_mode(struct list_head *m, void *p, int value) { MHVTL_DBG(3, "+++ Trace +++"); return SAM_STAT_GOOD; } static uint8_t set_9840_WORM(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); return set_WORM(m); } static uint8_t clear_9840_WORM(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); return clear_WORM(m); } static int encr_capabilities_9840(struct scsi_cmd *cmd) { uint8_t *buf = cmd->dbuf_p->data; MHVTL_DBG(3, "+++ Trace +++"); put_unaligned_be16(ENCR_CAPABILITIES, &buf[0]); put_unaligned_be16(40, &buf[2]); /* List length */ buf[20] = 1; /* Algorithm index */ buf[21] = 0; /* Reserved */ put_unaligned_be16(0x14, &buf[22]); /* Descriptor length */ buf[24] = 0x3a; /* MAC C/DED_C DECRYPT_C = 2 ENCRYPT_C = 2 */ buf[25] = 0x10; /* NONCE_C = 1 */ /* Max unauthenticated key data */ put_unaligned_be16(0x20, &buf[26]); /* Max authenticated key data */ put_unaligned_be16(0x0c, &buf[28]); /* Key size */ put_unaligned_be16(0x20, &buf[30]); buf[32] = 0x01; /* EAREM */ /* buf 12 - 19 reserved */ buf[40] = 0; /* Encryption Algorithm Id */ buf[41] = 0x01; /* Encryption Algorithm Id */ buf[42] = 0; /* Encryption Algorithm Id */ buf[43] = 0x14; /* Encryption Algorithm Id */ buf[4] = 0x1; /* CFG_P == 01b */ if (get_tape_load_status() == TAPE_LOADED) { buf[24] |= 0x80; /* AVFMV */ buf[27] = 0x1e; /* Max unauthenticated key data */ buf[29] = 0x00; /* Max authenticated key data */ buf[32] |= 0x42; /* DKAD_C == 1, RDMC_C == 1 */ buf[40] = 0x80; /* Encryption Algorithm Id */ buf[43] = 0x10; /* Encryption Algorithm Id */ } return 44; } static int T9840_kad_validation(int encrypt_mode, int ukad, int akad) { if (ukad > 30 || akad > 0) return TRUE; return FALSE; } /* Some comments before I forget how this is supose to work.. - cleaning_media_state is either 0 - Not mounted 1 - Cleaning media mounted -> return Cleaning cartridge installed 2 - Cleaning media mounted -> return Cause not reportable 3 - Cleaning media mounted -> return Initializing command required On cleaning media mount, 9840_cleaning() is called which: Sets a pointer from priv_lu_ssc -> cleaning_media_state. Sets cleaning_media_state to 1. Sets a 30 second timer to call inc_cleaning_state() inc_cleaning_state() Increments cleaning_media_state. If cleaning media_state == 2, set another timer for 90 seconds to again call inc_cleaning_state. If the application issues a TUR, ssc_tur() will return one of the above status codes depending on the current value of cleaning_media_state. When the cleaning media is unmounted, the pointer in priv_lu_ssc to this var will be re-set to NULL so the ssc_tur() will return defautl value. */ static volatile sig_atomic_t cleaning_media_state; static void inc_cleaning_state(int sig); static void set_cleaning_timer(int t) { MHVTL_DBG(3, "+++ Trace +++ Setting alarm for %d", t); signal(SIGALRM, inc_cleaning_state); alarm(t); } static void inc_cleaning_state(int sig) { MHVTL_DBG(3, "+++ Trace +++"); signal(sig, inc_cleaning_state); cleaning_media_state++; if (cleaning_media_state == CLEAN_MOUNT_STAGE2) set_cleaning_timer(90); } static uint8_t T9840_media_load(struct lu_phy_attr *lu, int load) { MHVTL_DBG(3, "+++ Trace +++ %s", (load) ? "load" : "unload"); return 0; } static uint8_t T9840_cleaning(void *ssc_priv) { struct priv_lu_ssc *ssc; MHVTL_DBG(3, "+++ Trace +++"); ssc = ssc_priv; ssc->cleaning_media_state = &cleaning_media_state; cleaning_media_state = CLEAN_MOUNT_STAGE1; set_cleaning_timer(30); return 0; } static void init_9840_mode_pages(struct lu_phy_attr *lu) { add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_information_exception(lu); } static char *pm_name_9840A = "T9840A"; static char *pm_name_9840B = "T9840B"; static char *pm_name_9840C = "T9840C"; static char *pm_name_9840D = "T9840D"; static char *pm_name_9940A = "T9940A"; static char *pm_name_9940B = "T9940B"; static struct ssc_personality_template ssc_pm = { .valid_encryption_blk = valid_encryption_blk_9840, .update_encryption_mode = update_9840_encryption_mode, .encryption_capabilities = encr_capabilities_9840, .kad_validation = T9840_kad_validation, .check_restrictions = check_restrictions, .clear_compression = clear_9840_comp, .set_compression = set_9840_comp, .clear_WORM = clear_9840_WORM, .set_WORM = set_9840_WORM, .media_load = T9840_media_load, .cleaning_media = T9840_cleaning, .media_handling = media_info, }; #define INQUIRY_LEN 74 static void init_9840_inquiry(struct lu_phy_attr *lu) { int pg; uint8_t worm; worm = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_WORM; lu->inquiry[2] = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_ANSI_VERSION; lu->inquiry[3] = 0x42; lu->inquiry[4] = INQUIRY_LEN - 5; /* Additional Length */ lu->inquiry[54] = 0x04; /* Key Management */ lu->inquiry[55] = 0x12; /* Support Encryption & Compression */ /* Sequential Access device capabilities - Ref: 8.4.2 */ pg = PCODE_OFFSET(0xb0); lu->lu_vpd[pg] = alloc_vpd(VPD_B0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b0(lu, &worm); } void init_9840A_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_9840A; ssc_pm.lu = lu; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_pm.native_drive_density = &density_9840A; ssc_personality_module_register(&ssc_pm); init_9840_inquiry(lu); init_9840_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); register_ops(lu, LOAD_DISPLAY, ssc_load_display, NULL, NULL); add_density_support(&lu->den_list, &density_9840A, 1); add_drive_media_list(lu, LOAD_RW, "9840A"); add_drive_media_list(lu, LOAD_RO, "9840A Clean"); } void init_9840B_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_9840B; ssc_pm.lu = lu; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_pm.native_drive_density = &density_9840B; ssc_personality_module_register(&ssc_pm); init_9840_inquiry(lu); init_9840_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); register_ops(lu, LOAD_DISPLAY, ssc_load_display, NULL, NULL); add_density_support(&lu->den_list, &density_9840A, 1); add_density_support(&lu->den_list, &density_9840B, 1); add_drive_media_list(lu, LOAD_RW, "9840A"); add_drive_media_list(lu, LOAD_RO, "9840A Clean"); add_drive_media_list(lu, LOAD_RW, "9840B"); add_drive_media_list(lu, LOAD_RO, "9840B Clean"); } void init_9840C_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_9840C; ssc_pm.lu = lu; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_9840_inquiry(lu); init_9840_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); ssc_pm.native_drive_density = &density_9840C; register_ops(lu, LOAD_DISPLAY, ssc_load_display, NULL, NULL); add_density_support(&lu->den_list, &density_9840A, 0); add_density_support(&lu->den_list, &density_9840B, 1); add_density_support(&lu->den_list, &density_9840C, 1); add_drive_media_list(lu, LOAD_RO, "9840A"); add_drive_media_list(lu, LOAD_RO, "9840A Clean"); add_drive_media_list(lu, LOAD_RW, "9840B"); add_drive_media_list(lu, LOAD_RO, "9840B Clean"); add_drive_media_list(lu, LOAD_RW, "9840C"); add_drive_media_list(lu, LOAD_RO, "9840C Clean"); } void init_9840D_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_9840D; ssc_pm.lu = lu; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.native_drive_density = &density_9840D; ssc_personality_module_register(&ssc_pm); init_9840_inquiry(lu); init_9840_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); register_ops(lu, LOAD_DISPLAY, ssc_load_display, NULL, NULL); add_density_support(&lu->den_list, &density_9840A, 0); add_density_support(&lu->den_list, &density_9840B, 0); add_density_support(&lu->den_list, &density_9840C, 1); add_density_support(&lu->den_list, &density_9840D, 1); add_drive_media_list(lu, LOAD_RO, "9840B"); add_drive_media_list(lu, LOAD_RO, "9840B Clean"); add_drive_media_list(lu, LOAD_RW, "9840C"); add_drive_media_list(lu, LOAD_RO, "9840C Clean"); add_drive_media_list(lu, LOAD_RW, "9840D"); add_drive_media_list(lu, LOAD_RO, "9840D Clean"); } void init_9940A_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_9940A; ssc_pm.lu = lu; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_pm.native_drive_density = &density_9940A; ssc_personality_module_register(&ssc_pm); init_9840_inquiry(lu); init_9840_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); register_ops(lu, LOAD_DISPLAY, ssc_load_display, NULL, NULL); add_density_support(&lu->den_list, &density_9940A, 1); add_drive_media_list(lu, LOAD_RW, "9840A"); add_drive_media_list(lu, LOAD_RO, "9840A Clean"); add_drive_media_list(lu, LOAD_RW, "9840A"); add_drive_media_list(lu, LOAD_RO, "9840A Clean"); add_drive_media_list(lu, LOAD_RW, "9840A"); add_drive_media_list(lu, LOAD_RO, "9840A Clean"); } void init_9940B_ssc(struct lu_phy_attr *lu) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", &lu->mode_pg); ssc_pm.name = pm_name_9940B; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_9940B; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_9840_inquiry(lu); init_9840_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); register_ops(lu, LOAD_DISPLAY, ssc_load_display, NULL, NULL); add_density_support(&lu->den_list, &density_9940A, 1); add_density_support(&lu->den_list, &density_9940B, 1); add_drive_media_list(lu, LOAD_RW, "9940A"); add_drive_media_list(lu, LOAD_RO, "9940A Clean"); add_drive_media_list(lu, LOAD_RW, "9940B"); add_drive_media_list(lu, LOAD_RO, "9940B Clean"); } ================================================ FILE: usr/pm/stklxx_pm.c ================================================ /* * Personality module for STK L series of robots * e.g. L180, L700, L20/40/80 */ #include #include "vtllib.h" #include "smc.h" #include "logging.h" #include "mode.h" static struct smc_personality_template smc_pm = { .library_has_map = TRUE, .library_has_barcode_reader = TRUE, .library_has_playground = TRUE, .dvcid_len = 34, }; static void update_stk_l_vpd_80(struct lu_phy_attr *lu) { struct vpd **lu_vpd; uint8_t *d; lu_vpd = &lu->lu_vpd[PCODE_OFFSET(0x80)]; /* Unit Serial Number */ if (*lu_vpd) { /* Free any earlier allocation */ dealloc_vpd(*lu_vpd); *lu_vpd = NULL; } *lu_vpd = alloc_vpd(0x12); if (*lu_vpd) { d = (*lu_vpd)->data; /* d[4 - 15] Serial number of device */ snprintf((char *)&d[0], 13, "%-12.12s", lu->lu_serial_no); /* Unique Logical Library Identifier */ } else { MHVTL_ERR("Could not malloc(0x12) bytes, line %d", __LINE__); } } static void update_stk_l_vpd_83(struct lu_phy_attr *lu) { struct vpd *lu_vpd; lu_vpd = lu->lu_vpd[PCODE_OFFSET(0x83)]; /* STK L series do not have this VPD page - remove */ if (lu_vpd) { /* Free any earlier allocation */ dealloc_vpd(lu_vpd); lu->lu_vpd[PCODE_OFFSET(0x83)] = NULL; } } void init_stkl20(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - STK L20/40/80 series emulation"; smc_pm.library_has_map = TRUE; smc_pm.library_has_barcode_reader = TRUE; smc_pm.library_has_playground = TRUE; /* Follow L20 SCSI Reference Manual */ smc_pm.start_picker = 0x0001; smc_pm.start_map = 0x000a; /* 10d - 55d */ smc_pm.start_drive = 0x01f4; /* 500d - 519d */ smc_pm.start_storage = 0x03e8; /* 1000d - 1677d */ smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); update_stk_l_vpd_80(lu); update_stk_l_vpd_83(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); /* FIXME: Need to add page 0x2d - Drive Configuration Page */ add_smc_mode_page_drive_configuration(lu); } void init_stklxx(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - STK L series emulation"; smc_pm.library_has_map = TRUE; smc_pm.library_has_barcode_reader = TRUE; smc_pm.library_has_playground = TRUE; /* Follow L700e/L180 SCSI Reference Manual - 8th Edition */ smc_pm.start_picker = 0x0001; smc_pm.start_map = 0x000a; /* 10d - 55d */ smc_pm.start_drive = 0x01f4; /* 500d - 519d */ smc_pm.start_storage = 0x03e8; /* 1000d - 1677d */ smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); update_stk_l_vpd_80(lu); update_stk_l_vpd_83(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); } void init_stkslxx(struct lu_phy_attr *lu) { smc_pm.name = "mhVTL - STK SL series emulation"; smc_pm.library_has_map = TRUE; smc_pm.library_has_barcode_reader = TRUE; smc_pm.library_has_playground = TRUE; /* Follow Streamline SL500 Interface Reference Manual - 2th Edition */ smc_pm.start_picker = 0x0001; smc_pm.start_map = 0x000a; /* 10d - 55d */ smc_pm.start_drive = 0x01f4; /* 500d - 518d */ smc_pm.start_storage = 0x03e8; /* 1000d - 1628d */ smc_pm.lu = lu; smc_personality_module_register(&smc_pm); init_slot_info(lu); update_stk_l_vpd_80(lu); update_stk_l_vpd_83(lu); init_smc_log_pages(lu); init_smc_mode_pages(lu); } ================================================ FILE: usr/pm/t10000_pm.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'STREAM' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include #include "be_byteshift.h" #include "mhvtl_scsi.h" #include "vtl_common.h" #include "vtllib.h" #include "logging.h" #include "ssc.h" #include "spc.h" #include "vtlcart.h" #include "mode.h" #include "mhvtl_log.h" static struct density_info density_t10kA = { 0, 127, 0x300, 0x7a120, medium_density_code_10kA, "STK", "T1 - 500", "T1 - 500 GB"}; static struct density_info density_t10kB = { 0, 127, 0x480, 0x1d4c0, medium_density_code_10kB, "STK", "T1 - 1000", "T1 - 1000 GB"}; static struct density_info density_t10kC = { 0, 127, 0x600, 0x30000, medium_density_code_10kC, "STK", "T2 - 5000", "T1 - 5000 GB"}; static struct name_to_media_info media_info[] = { {"T10KA", Media_T10KA, media_type_unknown, medium_density_code_10kA}, {"T10KA WORM", Media_T10KA_WORM, media_type_unknown, medium_density_code_10kA}, {"T10KA Clean", Media_T10KA_CLEAN, media_type_unknown, medium_density_code_10kA}, {"T10KB", Media_T10KB, media_type_unknown, medium_density_code_10kB}, {"T10KB WORM", Media_T10KB_WORM, media_type_unknown, medium_density_code_10kB}, {"T10KB Clean", Media_T10KB_CLEAN, media_type_unknown, medium_density_code_10kB}, {"T10KC", Media_T10KC, media_type_unknown, medium_density_code_10kC}, {"T10KC WORM", Media_T10KC_WORM, media_type_unknown, medium_density_code_10kC}, {"T10KC Clean", Media_T10KC_CLEAN, media_type_unknown, medium_density_code_10kC}, {"", 0, 0, 0}, }; /* * Returns true if blk header has correct encryption key data */ #define UKAD_LENGTH (encr->ukad_length) #define AKAD_LENGTH (encr->akad_length) #define KEY_LENGTH (encr->key_length) #define UKAD (encr->ukad) #define AKAD (encr->akad) #define KEY (encr->key) uint8_t valid_encryption_blk_t10k(struct scsi_cmd *cmd) { uint8_t correct_key; int i; struct lu_phy_attr *lu = cmd->lu; struct priv_lu_ssc *lu_priv; struct encryption *encr; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; MHVTL_DBG(3, "+++ Trace +++"); lu_priv = lu->lu_private; encr = lu_priv->app_encr_info; /* decryption logic */ correct_key = TRUE; if (c_pos->blk_flags & BLKHDR_FLG_ENCRYPTED) { /* compare the keys - STK requires UKAD back to decrypt */ if (lu_priv->DECRYPT_MODE > 1) { if (c_pos->blk_encryption_info.key_length != KEY_LENGTH) { sam_data_protect(E_INCORRECT_KEY, sam_stat); correct_key = FALSE; return correct_key; } for (i = 0; i < c_pos->blk_encryption_info.key_length; ++i) { if (c_pos->blk_encryption_info.key[i] != KEY[i]) { sam_data_protect(E_INCORRECT_KEY, sam_stat); correct_key = FALSE; break; } } if (c_pos->blk_encryption_info.ukad_length != UKAD_LENGTH) { sam_data_protect(E_INCORRECT_KEY, sam_stat); correct_key = FALSE; return correct_key; } for (i = 0; i < c_pos->blk_encryption_info.ukad_length; ++i) { if (c_pos->blk_encryption_info.ukad[i] != UKAD[i]) { sam_data_protect(E_INCORRECT_KEY, sam_stat); correct_key = FALSE; break; } } } else { sam_data_protect(E_UNABLE_TO_DECRYPT, sam_stat); correct_key = FALSE; } } else if (lu_priv->DECRYPT_MODE == 2) { sam_data_protect(E_UNENCRYPTED_DATA, sam_stat); correct_key = FALSE; } return correct_key; } static uint8_t clear_t10k_comp(struct list_head *m) { MHVTL_DBG(3, "+++ Trace +++"); /* default clear_compression is in libvtlscsi */ return clear_compression_mode_pg(m); } static uint8_t set_t10k_comp(struct list_head *m, int lvl) { MHVTL_DBG(3, "+++ Trace +++"); /* default set_compression is in libvtlscsi */ return set_compression_mode_pg(m, lvl); } static uint8_t update_t10k_encryption_mode(struct list_head *m, void *p, int value) { MHVTL_DBG(3, "+++ Trace +++"); return SAM_STAT_GOOD; } static uint8_t set_t10k_WORM(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); return set_WORM(m); } static uint8_t clear_t10k_WORM(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); return clear_WORM(m); } static int encr_capabilities_t10k(struct scsi_cmd *cmd) { uint8_t *buf = cmd->dbuf_p->data; MHVTL_DBG(3, "+++ Trace +++"); put_unaligned_be16(ENCR_CAPABILITIES, &buf[0]); put_unaligned_be16(40, &buf[2]); /* List length */ buf[20] = 1; /* Algorithm index */ buf[21] = 0; /* Reserved */ put_unaligned_be16(0x14, &buf[22]); /* Descriptor length */ buf[24] = 0x3a; /* MAC C/DED_C DECRYPT_C = 2 ENCRYPT_C = 2 */ buf[25] = 0x10; /* NONCE_C = 1 */ /* Max unauthenticated key data */ put_unaligned_be16(0x20, &buf[26]); /* Max authenticated key data */ put_unaligned_be16(0x0c, &buf[28]); /* Key size */ put_unaligned_be16(0x20, &buf[30]); buf[32] = 0x01; /* EAREM */ /* buf 12 - 19 reserved */ buf[40] = 0; /* Encryption Algorithm Id */ buf[41] = 0x01; /* Encryption Algorithm Id */ buf[42] = 0; /* Encryption Algorithm Id */ buf[43] = 0x14; /* Encryption Algorithm Id */ buf[4] = 0x1; /* CFG_P == 01b */ if (get_tape_load_status() == TAPE_LOADED) { buf[24] |= 0x80; /* AVFMV */ buf[27] = 0x1e; /* Max unauthenticated key data */ buf[29] = 0x00; /* Max authenticated key data */ buf[32] |= 0x42; /* DKAD_C == 1, RDMC_C == 1 */ buf[40] = 0x80; /* Encryption Algorithm Id */ buf[43] = 0x10; /* Encryption Algorithm Id */ } return 44; } static int t10k_kad_validation(int encrypt_mode, int ukad, int akad) { if (ukad > 30 || akad > 0) return TRUE; return FALSE; } /* Some comments before I forget how this is supose to work.. - cleaning_media_state is either 0 - Not mounted 1 - Cleaning media mounted -> return Cleaning cartridge installed 2 - Cleaning media mounted -> return Cause not reportable 3 - Cleaning media mounted -> return Initializing command required On cleaning media mount, t10k_cleaning() is called which: Sets a pointer from priv_lu_ssc -> cleaning_media_state. Sets cleaning_media_state to 1. Sets a 30 second timer to call inc_cleaning_state() inc_cleaning_state() Increments cleaning_media_state. If cleaning media_state == 2, set another timer for 90 seconds to again call inc_cleaning_state. If the application issues a TUR, ssc_tur() will return one of the above status codes depending on the current value of cleaning_media_state. When the cleaning media is unmounted, the pointer in priv_lu_ssc to this var will be re-set to NULL so the ssc_tur() will return defautl value. */ static volatile sig_atomic_t cleaning_media_state; static void inc_cleaning_state(int sig); static void set_cleaning_timer(int t) { MHVTL_DBG(3, "+++ Trace +++ Setting alarm for %d", t); signal(SIGALRM, inc_cleaning_state); alarm(t); } static void inc_cleaning_state(int sig) { MHVTL_DBG(3, "+++ Trace +++"); signal(sig, inc_cleaning_state); cleaning_media_state++; if (cleaning_media_state == CLEAN_MOUNT_STAGE2) set_cleaning_timer(90); } static uint8_t t10k_media_load(struct lu_phy_attr *lu, int load) { uint8_t *sense_p = lu->sense_p; struct priv_lu_ssc *ssc; ssc = lu->lu_private; MHVTL_DBG(3, "+++ Trace +++ %s", (load) ? "load" : "unload"); if (load) { switch (ssc->mamp->MediumType) { case MEDIA_TYPE_WORM: sense_p[24] |= 0x02; /* Data + Append-only */ /* Now fall thru to 'Data' */ case MEDIA_TYPE_DATA: sense_p[24] |= 0x10; break; case MEDIA_TYPE_CLEAN: sense_p[24] |= 0x80; /* Cleaning cart */ break; case MEDIA_TYPE_FIRMWARE: sense_p[24] |= 0x20; break; default: sense_p[24] &= 0x0d; /* Unknown type */ break; } } else { sense_p[24] &= 0x0d; /* Unknown type & mask out Volsafe */ } if (ssc->append_only_mode) sense_p[24] |= 0x02; return 0; } static uint8_t t10k_cleaning(void *ssc_priv) { struct priv_lu_ssc *ssc; MHVTL_DBG(3, "+++ Trace +++"); ssc = ssc_priv; ssc->cleaning_media_state = &cleaning_media_state; cleaning_media_state = CLEAN_MOUNT_STAGE1; set_cleaning_timer(30); return 0; } static void init_t10k_mode_pages(struct lu_phy_attr *lu) { add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); } /* T10K return tape type in request sense information * * Reference: T10000 Interface Reference Manual * August 2009 * Revision M * * sense[24] 7 6 5 4 3 2 1 0 * | | | +- TapeEOL - Tape loaded is End-Of-Life * | | +--- Volsafe - Current tape is append-only * | +----- MIRBad - Metadata on tape is defective * | | | | +------- DAvail - Diagnostic info available * Tape Type +-+-+-+ * 1000b = Cleaning tape * 0100b = Dump tape * 0010b = Code load tape * 0001b = Data Tape * 0000b = Unknown type */ static void t10k_init_sense(struct scsi_cmd *cmd) { uint8_t *sense_buf = (uint8_t *)cmd->dbuf_p->sense_buf; struct priv_lu_ssc *lu_priv = cmd->lu->lu_private; if (get_tape_load_status()) { if (lu_priv->append_only_mode) sense_buf[24] |= 0x02; switch (lu_priv->mamp->MediumType) { case MEDIA_TYPE_WORM: sense_buf[24] |= 0x02; /* Append-only */ /* Fall thru to MEDIA_TYPE_DATA */ case MEDIA_TYPE_DATA: sense_buf[24] |= 0x10; break; case MEDIA_TYPE_CLEAN: sense_buf[24] |= 0x80; /* Cleaning cart */ break; case MEDIA_TYPE_FIRMWARE: sense_buf[24] |= 0x20; break; default: sense_buf[24] &= 0x0d; /* Unknown type */ break; } } if (lu_priv->inLibrary) sense_buf[25] = 0x02; /* LibAtt */ } uint8_t t10k_sense(struct scsi_cmd *cmd) { t10k_init_sense(cmd); return spc_request_sense(cmd); } static char *pm_name_t10kA = "T10000A"; static char *pm_name_t10kB = "T10000B"; static char *pm_name_t10kC = "T10000C"; static struct ssc_personality_template ssc_pm = { .valid_encryption_blk = valid_encryption_blk_t10k, .update_encryption_mode = update_t10k_encryption_mode, .encryption_capabilities = encr_capabilities_t10k, .kad_validation = t10k_kad_validation, .check_restrictions = check_restrictions, .clear_compression = clear_t10k_comp, .set_compression = set_t10k_comp, .clear_WORM = clear_t10k_WORM, .set_WORM = set_t10k_WORM, .media_load = t10k_media_load, .cleaning_media = t10k_cleaning, .media_handling = media_info, }; #define INQUIRY_LEN 74 static void init_t10k_inquiry(struct lu_phy_attr *lu) { int pg; uint8_t worm; worm = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_WORM; lu->inquiry[2] = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_ANSI_VERSION; lu->inquiry[3] = 0x42; lu->inquiry[4] = INQUIRY_LEN - 5; /* Additional Length */ if (ssc_pm.drive_supports_SP) { /* Security Protocols */ lu->inquiry[54] |= 0x04; /* Key management - DPKM SPIN/SPOUT */ lu->inquiry[55] |= 0x10; /* Encrypt */ } /* FIXME: Need to add 'LibAtt' too */ /* WORM... */ if (ssc_pm.drive_supports_WORM) lu->inquiry[55] |= 0x04; /* VolSafe set */ /* Set Data Compression enabled */ lu->inquiry[55] |= 0x02; /* DCMP bit enabled */ /* Version Descriptor */ put_unaligned_be16(0x0077, &lu->inquiry[58]); put_unaligned_be16(0x0314, &lu->inquiry[60]); put_unaligned_be16(0x0403, &lu->inquiry[62]); put_unaligned_be16(0x0a11, &lu->inquiry[64]); /* Sequential Access device capabilities - Ref: 8.4.2 */ pg = PCODE_OFFSET(0xb0); lu->lu_vpd[pg] = alloc_vpd(VPD_B0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b0(lu, &worm); } void init_t10kA_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_t10kA; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_t10kA; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_t10k_inquiry(lu); init_t10k_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); register_ops(lu, LOAD_DISPLAY, ssc_load_display, NULL, NULL); register_ops(lu, REQUEST_SENSE, t10k_sense, NULL, NULL); add_density_support(&lu->den_list, &density_t10kA, 1); add_drive_media_list(lu, LOAD_RW, "T10KA"); add_drive_media_list(lu, LOAD_RO, "T10KA Clean"); } void init_t10kB_ssc(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_t10kB; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_t10kB; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_t10k_inquiry(lu); init_t10k_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); register_ops(lu, LOAD_DISPLAY, ssc_load_display, NULL, NULL); register_ops(lu, REQUEST_SENSE, t10k_sense, NULL, NULL); add_density_support(&lu->den_list, &density_t10kA, 1); add_density_support(&lu->den_list, &density_t10kB, 1); add_drive_media_list(lu, LOAD_RW, "T10KA"); add_drive_media_list(lu, LOAD_RO, "T10KA Clean"); add_drive_media_list(lu, LOAD_RW, "T10KB"); add_drive_media_list(lu, LOAD_RO, "T10KB Clean"); } void init_t10kC_ssc(struct lu_phy_attr *lu) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", &lu->mode_pg); ssc_pm.name = pm_name_t10kC; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_t10kC; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_t10k_inquiry(lu); init_t10k_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); register_ops(lu, LOAD_DISPLAY, ssc_load_display, NULL, NULL); register_ops(lu, REQUEST_SENSE, t10k_sense, NULL, NULL); add_density_support(&lu->den_list, &density_t10kA, 0); add_density_support(&lu->den_list, &density_t10kB, 1); add_density_support(&lu->den_list, &density_t10kC, 1); add_drive_media_list(lu, LOAD_RW, "T10KA"); add_drive_media_list(lu, LOAD_RO, "T10KA Clean"); add_drive_media_list(lu, LOAD_RW, "T10KB"); add_drive_media_list(lu, LOAD_RO, "T10KB Clean"); add_drive_media_list(lu, LOAD_RW, "T10KC"); add_drive_media_list(lu, LOAD_RO, "T10KC Clean"); } ================================================ FILE: usr/pm/ult3580_pm.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'STREAM' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include "be_byteshift.h" #include "mhvtl_scsi.h" #include "vtl_common.h" #include "vtllib.h" #include "logging.h" #include "ssc.h" #include "vtlcart.h" #include "mode.h" #include "mhvtl_log.h" static struct density_info density_lto1 = { 4880, 127, 384, 100000, medium_density_code_lto1, "LTO-CVE", "U-18", "Ultrium 1/8T"}; static struct density_info density_lto2 = { 4880, 127, 512, 200000, medium_density_code_lto2, "LTO-CVE", "U-28", "Ultrium 2/8T"}; static struct density_info density_lto3 = { 9638, 127, 704, 381469, medium_density_code_lto3, "LTO-CVE", "U-316", "Ultrium 3/16T"}; static struct density_info density_lto4 = { 12725, 127, 896, 800000, medium_density_code_lto4, "LTO-CVE", "U-416", "Ultrium 4/16T"}; static struct density_info density_lto5 = { 15142, 127, 1280, 1500000, medium_density_code_lto5, "LTO-CVE", "U-516", "Ultrium 5/16T"}; static struct density_info density_lto6 = { 15142, 127, 2176, 2500000, medium_density_code_lto6, "LTO-CVE", "U-616", "Ultrium 6/16T"}; static struct density_info density_lto7 = { 19107, 127, 3584, 6000000, medium_density_code_lto7, "LTO-CVE", "U-732", "Ultrium 7/32T"}; static struct density_info density_lto8 = { 20669, 127, 6656, 12000000, medium_density_code_lto8, "LTO-CVE", "U-832", "Ultrium 8/32T"}; static struct density_info density_lto9 = { 21456, 127, 8960, 18000000, medium_density_code_lto9, "LTO-CVE", "U-932", "Ultrium 9/48T"}; static struct name_to_media_info media_info[] = { {"LTO1", Media_LTO1, media_type_lto1_data, medium_density_code_lto1}, {"LTO1 Clean", Media_LTO1_CLEAN, media_type_lto1_data, medium_density_code_lto1}, {"LTO2", Media_LTO2, media_type_lto2_data, medium_density_code_lto2}, {"LTO2 Clean", Media_LTO2_CLEAN, media_type_lto2_data, medium_density_code_lto2}, {"LTO3", Media_LTO3, media_type_lto3_data, medium_density_code_lto3}, {"LTO3 Clean", Media_LTO3_CLEAN, media_type_lto3_data, medium_density_code_lto3}, {"LTO3 WORM", Media_LTO3_WORM, media_type_lto3_worm, medium_density_code_lto3}, {"LTO4", Media_LTO4, media_type_lto4_data, medium_density_code_lto4}, {"LTO4 Clean", Media_LTO4_CLEAN, media_type_lto4_data, medium_density_code_lto4}, {"LTO4 WORM", Media_LTO4_WORM, media_type_lto4_worm, medium_density_code_lto4}, {"LTO5", Media_LTO5, media_type_lto5_data, medium_density_code_lto5}, {"LTO5 Clean", Media_LTO5_CLEAN, media_type_lto5_data, medium_density_code_lto5}, {"LTO5 WORM", Media_LTO5_WORM, media_type_lto5_worm, medium_density_code_lto5}, {"LTO6", Media_LTO6, media_type_lto6_data, medium_density_code_lto6}, {"LTO6 Clean", Media_LTO6_CLEAN, media_type_lto6_data, medium_density_code_lto6}, {"LTO6 WORM", Media_LTO6_WORM, media_type_lto6_worm, medium_density_code_lto6}, {"LTO7", Media_LTO7, media_type_lto7_data, medium_density_code_lto7}, {"LTO7 Clean", Media_LTO7_CLEAN, media_type_lto7_data, medium_density_code_lto7}, {"LTO7 WORM", Media_LTO7_WORM, media_type_lto7_worm, medium_density_code_lto7}, {"LTO8", Media_LTO8, media_type_lto8_data, medium_density_code_lto8}, {"LTO8 Clean", Media_LTO8_CLEAN, media_type_lto8_data, medium_density_code_lto8}, {"LTO8 WORM", Media_LTO8_WORM, media_type_lto8_worm, medium_density_code_lto8}, {"LTO9", Media_LTO9, media_type_lto9_data, medium_density_code_lto9}, {"LTO9 Clean", Media_LTO9_CLEAN, media_type_lto9_data, medium_density_code_lto9}, {"LTO9 WORM", Media_LTO9_WORM, media_type_lto9_worm, medium_density_code_lto9}, {"", 0, 0, 0}, }; static uint8_t clear_ult_compression(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); /* default clear_compression is in libvtlscsi */ return clear_compression_mode_pg(m); } static uint8_t set_ult_compression(struct list_head *m, int lvl) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); /* default set_compression is in libvtlscsi */ return set_compression_mode_pg(m, lvl); } /* As per IBM LTO5 SCSI Programmers Guide.. * Filling in compile time/date & dummy 'platform' string */ static void update_vpd_ult_c0(struct lu_phy_attr *lu) { uint8_t *data; struct vpd *vpd_p; int h, m, s; int day, month, year; vpd_p = lu->lu_vpd[PCODE_OFFSET(0xc0)]; data = vpd_p->data; ymd(&year, &month, &day, &h, &m, &s); data[1] = 0xc0; data[3] = 0x27; sprintf((char *)&data[16], "%02d%02d%02d", h, m, s); sprintf((char *)&data[23], "%04d%02d%02d", year, month, day); sprintf((char *)&data[31], "mhvtl_fl_f"); } static void update_vpd_ult_c1(struct lu_phy_attr *lu, char *sn) { uint8_t *data; struct vpd *vpd_p; vpd_p = lu->lu_vpd[PCODE_OFFSET(0xc1)]; data = vpd_p->data; data[1] = 0xc1; data[3] = 0x18; snprintf((char *)&data[4], 13, "%-12.12s", sn); snprintf((char *)&data[16], 13, "%-12.12s", sn); } static uint8_t set_ult_WORM(struct list_head *lst) { uint8_t *mp; struct mode *m; set_WORM(lst); /* Default WORM setup */ /* Now for the Ultrium unique stuff */ m = lookup_mode_pg(lst, MODE_BEHAVIOR_CONFIGURATION, 0); if (m) { MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", lst, m, m->pcodePointer); mp = m->pcodePointer; if (!mp) return SAM_STAT_GOOD; mp[4] = 0x01; /* WORM Behavior */ } else { MHVTL_DBG(2, "MODE BEHAVIOUR CONFIGURATION page not found"); } return SAM_STAT_GOOD; } static uint8_t clear_ult_WORM(struct list_head *m) { MHVTL_DBG(3, "+++ Trace mode pages at %p +++", m); return clear_WORM(m); } static uint8_t update_ult_encryption_mode(struct list_head *m, void *p, int value) { struct mode *mp; MHVTL_DBG(3, "+++ Trace +++"); mp = lookup_mode_pg(m, MODE_VENDOR_SPECIFIC_24H, 0); if (mp) { if (value) mp->pcodePointer[5] |= ENCR_E; else mp->pcodePointer[5] &= ~ENCR_E; } return SAM_STAT_GOOD; } static int encr_capabilities_ult(struct scsi_cmd *cmd) { uint8_t *buf = cmd->dbuf_p->data; put_unaligned_be16(ENCR_CAPABILITIES, &buf[0]); put_unaligned_be16(40, &buf[2]); /* List length */ buf[20] = 1; /* Algorithm index */ buf[21] = 0; /* Reserved */ put_unaligned_be16(0x14, &buf[22]); /* Descriptor length */ buf[24] = 0x3a; /* MAC C/DED_C DECRYPT_C = 2 ENCRYPT_C = 2 */ buf[25] = 0x10; /* NONCE_C = 1 */ /* Max unauthenticated key data */ put_unaligned_be16(0x20, &buf[26]); /* Max authenticated key data */ put_unaligned_be16(0x0c, &buf[28]); /* Key size */ put_unaligned_be16(0x20, &buf[30]); buf[32] = 0x01; /* EAREM */ /* buf 12 - 19 reserved */ buf[40] = 0; /* Encryption Algorithm Id */ buf[41] = 0x01; /* Encryption Algorithm Id */ buf[42] = 0; /* Encryption Algorithm Id */ buf[43] = 0x14; /* Encryption Algorithm Id */ /* adjustments for each emulated drive type */ buf[4] = 0x1; /* CFG_P == 01b */ if (get_tape_load_status() == TAPE_LOADED) { switch (mam.MediaType) { case Media_LTO4: MHVTL_DBG(1, "LTO4 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; case Media_LTO5: MHVTL_DBG(1, "LTO5 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; case Media_LTO6: MHVTL_DBG(1, "LTO6 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; case Media_LTO7: MHVTL_DBG(1, "LTO7 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; case Media_LTO8: MHVTL_DBG(1, "LTO8 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; case Media_LTO9: MHVTL_DBG(1, "LTO9 Medium - Setting AVFMV"); buf[24] |= 0x80; /* AVFMV */ break; default: MHVTL_DBG(1, "Unable to set Encryption (AVFMV) bit on mounted media"); break; } } buf[32] |= 0x08; /* RDMC_C == 4 */ return 44; } static void update_vpd_lbp(struct lu_phy_attr *lu) { struct vpd *v; uint8_t *d; v = lu->lu_vpd[PCODE_OFFSET(0xb5)]; d = v->data; d[4] = 7; /* LBP method for LBP disabled */ d[5] = 0; /* LBP method */ d[6] = 0; /* LBP len */ d[7] = 0; /* LBP_W & LBP_R set to 0 */ /* LBP method for RS-CRC */ d[12] = 7; d[13] = 1; /* LBP method */ d[14] = 4; /* LBP len */ d[15] = 0xc0; /* LBP_W & LBP_R set to true */ if (((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_LBP == LBP_CRC32C) { d[20] = 7; d[21] = 2; /* LBP method */ d[22] = 4; /* LBP len */ d[23] = 0xc0; /* LBP_W & LBP_R set to true */ } } static void init_ult_inquiry(struct lu_phy_attr *lu) { int pg; uint8_t worm; uint8_t local_TapeAlert[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; worm = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_WORM; lu->inquiry[2] = ((struct priv_lu_ssc *)lu->lu_private)->pm->drive_ANSI_VERSION; /* Set Protect bit if 'drive supports Logical Block Protection */ lu->inquiry[5] |= (((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_LBP) ? 1 : 0; /* Extended INQUIRY Data VPD page */ pg = PCODE_OFFSET(0x86); lu->lu_vpd[pg] = alloc_vpd(VPD_86_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_86(lu, ((struct priv_lu_ssc *)lu->lu_private)->pm); /* Sequential Access device capabilities - Ref: 8.4.2 */ pg = PCODE_OFFSET(0xb0); lu->lu_vpd[pg] = alloc_vpd(VPD_B0_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b0(lu, &worm); /* Manufacture-assigned serial number - Ref: 8.4.3 */ pg = PCODE_OFFSET(0xb1); lu->lu_vpd[pg] = alloc_vpd(VPD_B1_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b1(lu, lu->lu_serial_no); /* TapeAlert supported flags - Ref: 8.4.4 */ pg = PCODE_OFFSET(0xb2); lu->lu_vpd[pg] = alloc_vpd(VPD_B2_SZ); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_b2(lu, &local_TapeAlert); /* Logical Block Protection - Ref: 6.3.13.13 (LTO-9 reference guide) */ if (((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_LBP) { /* two pages ? - one for CRC32C and one for RS-CRC */ int pg_size = (((struct priv_lu_ssc *)lu->lu_private)->pm->drive_supports_LBP == LBP_RSCRC) ? VPD_B5_SZ : VPD_B5_SZ + VPD_B5_SZ; pg = PCODE_OFFSET(0xb5); lu->lu_vpd[pg] = alloc_vpd(pg_size + 12); /* header page, one page for 'disabled' LBP + one or two for RS-CRC / CRC32C */ if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_lbp(lu); } /* VPD page 0xC0 */ pg = PCODE_OFFSET(0xc0); lu->lu_vpd[pg] = alloc_vpd(43); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_ult_c0(lu); /* VPD page 0xC1 */ pg = PCODE_OFFSET(0xc1); lu->lu_vpd[pg] = alloc_vpd(28); if (!lu->lu_vpd[pg]) { MHVTL_ERR("Failed to malloc(): Line %d", __LINE__); exit(-ENOMEM); } update_vpd_ult_c1(lu, lu->lu_serial_no); } static int td4_kad_validation(int encrypt_mode, int ukad, int akad) { int count = FALSE; if (ukad > 32 || akad > 12) count = TRUE; if (!encrypt_mode && (ukad || akad)) count = TRUE; return count; } /* Some comments before I forget how this is supose to work.. - cleaning_media_state is either 0 - Not mounted 1 - Cleaning media mounted -> return Cleaning cartridge installed 2 - Cleaning media mounted -> return Cause not reportable 3 - Cleaning media mounted -> return Initializing command required On cleaning media mount, ult_cleaning() is called which: Sets a pointer from priv_lu_ssc -> cleaning_media_state. Sets cleaning_media_state to 1. Sets a 30 second timer to call inc_cleaning_state() inc_cleaning_state() Increments cleaning_media_state. If cleaning media_state == 2, set another timer for 90 seconds to again call inc_cleaning_state. If the application issues a TUR, ssc_tur() will return one of the above status codes depending on the current value of cleaning_media_state. When the cleaning media is unmounted, the pointer in priv_lu_ssc to this var will be re-set to NULL so the ssc_tur() will return defautl value. */ static volatile sig_atomic_t cleaning_media_state; static void inc_cleaning_state(int sig); static void set_cleaning_timer(int t) { MHVTL_DBG(3, "+++ Trace +++ Setting alarm for %d", t); signal(SIGALRM, inc_cleaning_state); alarm(t); } static void inc_cleaning_state(int sig) { MHVTL_DBG(3, "+++ Trace +++"); signal(sig, inc_cleaning_state); cleaning_media_state++; if (cleaning_media_state == CLEAN_MOUNT_STAGE2) set_cleaning_timer(90); } static uint8_t ult_media_load(struct lu_phy_attr *lu, int load) { struct priv_lu_ssc *lu_priv = lu->lu_private; MHVTL_DBG(3, "+++ Trace +++ %s", (load) ? "load" : "unload"); if (load) { switch (lu_priv->mamp->MediaType) { case Media_LTO1: lu->mode_media_type = media_type_lto1_data; break; case Media_LTO2: lu->mode_media_type = media_type_lto2_data; break; case Media_LTO3: lu->mode_media_type = media_type_lto3_data; break; case Media_LTO4: lu->mode_media_type = media_type_lto4_data; break; case Media_LTO5: lu->mode_media_type = media_type_lto5_data; break; case Media_LTO6: lu->mode_media_type = media_type_lto6_data; break; case Media_LTO7: lu->mode_media_type = media_type_lto7_data; break; case Media_LTO8: lu->mode_media_type = media_type_lto8_data; break; case Media_LTO9: lu->mode_media_type = media_type_lto9_data; break; default: lu->mode_media_type = 0; } if (lu_priv->mamp->MediumType == MEDIA_TYPE_WORM) lu->mode_media_type |= 0x04; } else { lu->mode_media_type = 0; } return 0; } static uint8_t ult_cleaning(void *ssc_priv) { struct priv_lu_ssc *ssc; MHVTL_DBG(3, "+++ Trace +++"); ssc = ssc_priv; ssc->cleaning_media_state = &cleaning_media_state; cleaning_media_state = CLEAN_MOUNT_STAGE1; set_cleaning_timer(30); return 0; } static char *pm_name_lto1 = "LTO-1"; static char *pm_name_lto2 = "LTO-2"; static char *pm_name_lto3 = "LTO-3"; static char *pm_name_lto4 = "LTO-4"; static char *pm_name_lto5 = "LTO-5"; static char *pm_name_lto6 = "LTO-6"; static char *pm_name_lto7 = "LTO-7"; static char *pm_name_lto8 = "LTO-8"; static char *pm_name_lto9 = "LTO-9"; static struct ssc_personality_template ssc_pm = { .valid_encryption_blk = valid_encryption_blk, /* default in ssc.c */ .check_restrictions = check_restrictions, /* default in ssc.c */ .clear_compression = clear_ult_compression, .set_compression = set_ult_compression, .media_load = ult_media_load, .cleaning_media = ult_cleaning, .media_handling = media_info, }; void init_ult3580_td1(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto1; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto1; /* Drive capabilities need to be defined before mode pages */ ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); /* IBM Ultrium SCSI Reference (5edition - Oct 2001) * lists these mode pages */ add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_information_exception(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_lto1, 1); add_drive_media_list(lu, LOAD_RW, "LTO1"); add_drive_media_list(lu, LOAD_RO, "LTO1 Clean"); } void init_ult3580_td2(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto2; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto2; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = FALSE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); /* Based on 9th edition of IBM SCSI Reference */ add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_behavior_configuration(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_lto1, 1); add_density_support(&lu->den_list, &density_lto2, 1); add_drive_media_list(lu, LOAD_RW, "LTO1"); add_drive_media_list(lu, LOAD_RO, "LTO1 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO2"); add_drive_media_list(lu, LOAD_RO, "LTO2 Clean"); } void init_ult3580_td3(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto3; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto3; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = FALSE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); /* Based on 9th edition of IBM SCSI Reference */ add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_behavior_configuration(lu); add_mode_vendor_25h_mode_pages(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_device_status(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_lto1, 0); add_density_support(&lu->den_list, &density_lto2, 1); add_density_support(&lu->den_list, &density_lto3, 1); add_drive_media_list(lu, LOAD_RO, "LTO1"); add_drive_media_list(lu, LOAD_RO, "LTO1 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO2"); add_drive_media_list(lu, LOAD_RO, "LTO2 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO3"); add_drive_media_list(lu, LOAD_RO, "LTO3 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO3 WORM"); } void init_ult3580_td4(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto4; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto4; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = td4_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_pm.drive_supports_append_only_mode = FALSE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = FALSE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-4 */ add_mode_vendor_25h_mode_pages(lu); add_mode_behavior_configuration(lu); add_mode_encryption_mode_attribute(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_device_status(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_lto2, 0); add_density_support(&lu->den_list, &density_lto3, 1); add_density_support(&lu->den_list, &density_lto4, 1); add_drive_media_list(lu, LOAD_RO, "LTO2"); add_drive_media_list(lu, LOAD_RO, "LTO2 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO3"); add_drive_media_list(lu, LOAD_RO, "LTO3 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO3 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO4"); add_drive_media_list(lu, LOAD_RO, "LTO4 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO4 WORM"); } void init_ult3580_td5(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto5; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto5; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = td4_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_pm.drive_supports_append_only_mode = TRUE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = TRUE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_supports_LBP = LBP_RSCRC; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); /* LBP appears in LTO5 & 6 IBM SCSI reference Feb 2013 */ add_mode_control_data_protection(lu); /* LBP 0x0a/0xf0 */ add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-5 */ add_mode_vendor_25h_mode_pages(lu); add_mode_behavior_configuration(lu); add_mode_encryption_mode_attribute(lu); /* Supports non-zero programable early warning */ update_prog_early_warning(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_device_status(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_lto3, 0); add_density_support(&lu->den_list, &density_lto4, 1); add_density_support(&lu->den_list, &density_lto5, 1); add_drive_media_list(lu, LOAD_RO, "LTO3"); add_drive_media_list(lu, LOAD_RO, "LTO3 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO4"); add_drive_media_list(lu, LOAD_RO, "LTO4 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO4 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO4 ENCR"); add_drive_media_list(lu, LOAD_RW, "LTO5"); add_drive_media_list(lu, LOAD_RO, "LTO5 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO5 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO5 ENCR"); } void init_ult3580_td6(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto6; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto6; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = td4_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_pm.drive_supports_append_only_mode = TRUE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = TRUE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_supports_LBP = LBP_RSCRC; ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); /* LBP appears in LTO5 & 6 IBM SCSI reference Feb 2013 */ add_mode_control_data_protection(lu); /* LBP 0x0a/0xf0 */ add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-6 */ add_mode_vendor_25h_mode_pages(lu); add_mode_behavior_configuration(lu); add_mode_encryption_mode_attribute(lu); /* Supports non-zero programable early warning */ update_prog_early_warning(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_device_status(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_lto4, 0); add_density_support(&lu->den_list, &density_lto5, 1); add_density_support(&lu->den_list, &density_lto6, 1); add_drive_media_list(lu, LOAD_RO, "LTO4"); add_drive_media_list(lu, LOAD_RO, "LTO4 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO4 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO5"); add_drive_media_list(lu, LOAD_RO, "LTO5 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO5 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO5 ENCR"); add_drive_media_list(lu, LOAD_RW, "LTO6"); add_drive_media_list(lu, LOAD_RO, "LTO6 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO6 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO6 ENCR"); } void init_ult3580_td7(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto7; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto7; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = td4_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_pm.drive_supports_append_only_mode = TRUE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = TRUE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_supports_LBP = LBP_CRC32C; /* both RS-CRC and CRC32C */ ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_control_data_protection(lu); /* LBP 0x0a/0xf0 */ add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-7 */ add_mode_vendor_25h_mode_pages(lu); add_mode_behavior_configuration(lu); add_mode_encryption_mode_attribute(lu); /* Supports non-zero programable early warning */ update_prog_early_warning(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_device_status(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; add_density_support(&lu->den_list, &density_lto5, 0); add_density_support(&lu->den_list, &density_lto6, 1); add_density_support(&lu->den_list, &density_lto7, 1); add_drive_media_list(lu, LOAD_RO, "LTO5"); add_drive_media_list(lu, LOAD_RO, "LTO5 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO5 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO5 ENCR"); add_drive_media_list(lu, LOAD_RW, "LTO6"); add_drive_media_list(lu, LOAD_RO, "LTO6 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO6 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO6 ENCR"); add_drive_media_list(lu, LOAD_RW, "LTO7"); add_drive_media_list(lu, LOAD_RO, "LTO7 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO7 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO7 ENCR"); } void init_ult3580_td8(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto8; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto8; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = td4_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_pm.drive_supports_append_only_mode = TRUE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = TRUE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_supports_LBP = LBP_CRC32C; /* both RS-CRC and CRC32C */ ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_control_data_protection(lu); /* LBP 0x0a/0xf0 */ add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-7+ */ add_mode_vendor_25h_mode_pages(lu); add_mode_behavior_configuration(lu); add_mode_encryption_mode_attribute(lu); /* Supports non-zero programable early warning */ update_prog_early_warning(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_device_status(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; /* LTO 8 drives cannot read LTO6 cartridges. https://www.lto.org/lto-generation-compatibility/ "LTO drive generations 1-7 are able to read tapes from two generations prior and are able to write to tapes from the prior generation. LTO-8 drives can read and write to LTO-7 and LTO-8 media*/ add_density_support(&lu->den_list, &density_lto7, 1); add_density_support(&lu->den_list, &density_lto8, 1); add_drive_media_list(lu, LOAD_RW, "LTO7"); add_drive_media_list(lu, LOAD_RO, "LTO7 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO7 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO7 ENCR"); add_drive_media_list(lu, LOAD_RW, "LTO8"); add_drive_media_list(lu, LOAD_RO, "LTO8 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO8 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO8 ENCR"); } void init_ult3580_td9(struct lu_phy_attr *lu) { ssc_pm.name = pm_name_lto9; ssc_pm.lu = lu; ssc_pm.native_drive_density = &density_lto9; ssc_pm.update_encryption_mode = update_ult_encryption_mode; ssc_pm.encryption_capabilities = encr_capabilities_ult; ssc_pm.kad_validation = td4_kad_validation; ssc_pm.clear_WORM = clear_ult_WORM; ssc_pm.set_WORM = set_ult_WORM; ssc_pm.drive_supports_append_only_mode = TRUE; ssc_pm.drive_supports_early_warning = TRUE; ssc_pm.drive_supports_prog_early_warning = TRUE; ssc_pm.drive_supports_WORM = TRUE; ssc_pm.drive_supports_SPR = TRUE; ssc_pm.drive_supports_SP = TRUE; ssc_pm.drive_supports_LBP = LBP_CRC32C; /* both RS-CRC and CRC32C */ ssc_pm.drive_ANSI_VERSION = 5; ssc_personality_module_register(&ssc_pm); init_ult_inquiry(lu); add_mode_page_rw_err_recovery(lu); add_mode_disconnect_reconnect(lu); add_mode_control(lu); add_mode_control_extension(lu); add_mode_control_data_protection(lu); /* LBP 0x0a/0xf0 */ add_mode_data_compression(lu); add_mode_device_configuration(lu); add_mode_device_configuration_extension(lu); add_mode_medium_partition(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_medium_configuration(lu); add_mode_ult_encr_mode_pages(lu); /* Extra for LTO-7+ */ add_mode_vendor_25h_mode_pages(lu); add_mode_behavior_configuration(lu); add_mode_encryption_mode_attribute(lu); /* Supports non-zero programable early warning */ update_prog_early_warning(lu); add_log_write_err_counter(lu); add_log_read_err_counter(lu); add_log_sequential_access(lu); add_log_temperature_page(lu); add_log_selftest_results(lu); add_log_device_status(lu); add_log_volume_statistics(lu); add_log_tape_alert(lu); add_log_tape_usage(lu); add_log_tape_capacity(lu); add_log_data_compression(lu); add_log_performance_characteristics(lu); /* Capacity units in MBytes */ ((struct priv_lu_ssc *)lu->lu_private)->capacity_unit = 1L << 20; /* LTO 9 drives cannot read LTO7 cartridges. https://www.lto.org/lto-generation-compatibility/ "LTO drive generations 1-7 are able to read tapes from two generations prior and are able to write to tapes from the prior generation. LTO-9 drives can read and write to LTO-8 and LTO-9 media only*/ add_density_support(&lu->den_list, &density_lto8, 1); add_density_support(&lu->den_list, &density_lto9, 1); add_drive_media_list(lu, LOAD_RW, "LTO8"); add_drive_media_list(lu, LOAD_RO, "LTO8 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO8 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO8 ENCR"); add_drive_media_list(lu, LOAD_RW, "LTO9"); add_drive_media_list(lu, LOAD_RO, "LTO9 Clean"); add_drive_media_list(lu, LOAD_RW, "LTO9 WORM"); add_drive_media_list(lu, LOAD_RW, "LTO9 ENCR"); } ================================================ FILE: usr/smc.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'MEDIUM CHANGER' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include #include "be_byteshift.h" #include "mhvtl_scsi.h" #include "mhvtl_list.h" #include "vtl_common.h" #include "vtllib.h" #include "logging.h" #include "smc.h" #include "q.h" #include "mhvtl_log.h" #include "subprocess.h" uint8_t smc_allow_removal(struct scsi_cmd *cmd) { MHVTL_DBG(1, "%s MEDIUM REMOVAL (%ld) **", (cmd->scb[4]) ? "PREVENT" : "ALLOW", (long)cmd->dbuf_p->serialNo); return SAM_STAT_GOOD; } uint8_t smc_initialize_element_status(struct scsi_cmd *cmd) { uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; current_state = MHVTL_STATE_INITIALISE_ELEMENTS; MHVTL_DBG(1, "%s (%ld) **", "INITIALIZE ELEMENT", (long)cmd->dbuf_p->serialNo); if (!cmd->lu->online) { sam_not_ready(NO_ADDITIONAL_SENSE, sam_stat); return SAM_STAT_CHECK_CONDITION; } sleep(1); return SAM_STAT_GOOD; } uint8_t smc_initialize_element_status_with_range(struct scsi_cmd *cmd) { uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; current_state = MHVTL_STATE_INITIALISE_ELEMENTS; MHVTL_DBG(1, "%s (%ld) **", "INITIALIZE ELEMENT RANGE", (long)cmd->dbuf_p->serialNo); if (!cmd->lu->online) { sam_not_ready(NO_ADDITIONAL_SENSE, sam_stat); return SAM_STAT_CHECK_CONDITION; } sleep(1); return SAM_STAT_GOOD; } /* Return the element type of a particular element address */ static int slot_type(struct smc_priv *smc_p, int addr) { if ((addr >= smc_p->pm->start_drive) && (addr < smc_p->pm->start_drive + smc_p->num_drives)) return DATA_TRANSFER; if ((addr >= smc_p->pm->start_picker) && (addr < smc_p->pm->start_picker + smc_p->num_picker)) return MEDIUM_TRANSPORT; if ((addr >= smc_p->pm->start_map) && (addr < smc_p->pm->start_map + smc_p->num_map)) return MAP_ELEMENT; if ((addr >= smc_p->pm->start_storage) && (addr < smc_p->pm->start_storage + smc_p->num_storage)) return STORAGE_ELEMENT; return 0; } /* * Returns a 'human frendly' slot number * i.e. One with the internal offset removed (start counting at 1). */ static int slot_number(struct smc_personality_template *pm, struct s_info *sp) { switch (sp->element_type) { case MEDIUM_TRANSPORT: return sp->slot_location - pm->start_picker + 1; case STORAGE_ELEMENT: return sp->slot_location - pm->start_storage + 1; case MAP_ELEMENT: return sp->slot_location - pm->start_map + 1; case DATA_TRANSFER: return sp->slot_location - pm->start_drive + 1; } return 0; } /* * Takes a slot number and returns a struct pointer to the slot */ static struct s_info *slot2struct(struct smc_priv *smc_p, int addr) { struct list_head *slot_head; struct s_info *sp; slot_head = &smc_p->slot_list; list_for_each_entry(sp, slot_head, siblings) { if (sp->slot_location == (unsigned int)addr) return sp; } MHVTL_DBG(1, "Arrr... Could not find slot %d", addr); return NULL; } /* * Takes a Drive number and returns a struct pointer to the drive */ static struct d_info *drive2struct(struct smc_priv *smc_p, int addr) { struct s_info *s; s = slot2struct(smc_p, addr); if (s) return s->drive; return NULL; } /* Query the drive to check if it thinks it is empty */ static int is_drive_empty(struct d_info *drv) { int mlen, r_qid; struct q_entry q; /* Initialise message queue as necessary */ r_qid = init_queue(); if (r_qid == -1) { printf("Could not initialise message queue\n"); exit(1); } MHVTL_DBG(1, "%ld: Sending \"%s\" to snd_id %ld", my_id, msg_mount_state, drv->drv_id); send_msg(msg_mount_state, drv->drv_id); mlen = msgrcv(r_qid, &q, MAXOBN, my_id, MSG_NOERROR); if (mlen > 0) MHVTL_DBG(1, "%ld: Received \"%s\" from snd_id %ld", my_id, q.msg.text, q.msg.snd_id); /* string defined in q.h */ return strncmp(msg_not_occupied, q.msg.text, 12); } /* returns true if medium transport access to slot is OK */ int slotAccess(struct s_info *s) { return s->status & STATUS_Access; } /* Returns true if slot has media in it */ int slotOccupied(struct s_info *s) { return s->status & STATUS_Full; } /* Returns true if drive has media in it */ static int driveOccupied(struct d_info *d) { int ret; ret = slotOccupied(d->slot); ret |= is_drive_empty(d); return ret; } static int check_tape_unload(void) { int mlen, r_qid; struct q_entry q; /* Initialise message queue as necessary */ r_qid = init_queue(); if (r_qid == -1) { printf("Could not initialise message queue\n"); exit(1); } mlen = msgrcv(r_qid, &q, MAXOBN, my_id, MSG_NOERROR); if (mlen > 0) MHVTL_DBG(1, "%ld: Received \"%s\" from snd_id %ld", my_id, q.msg.text, q.msg.snd_id); /* msg defined in q.h */ return strncmp(msg_unload_ok, q.msg.text, 11); } /* * A value of 0 indicates that media movement from the I/O port * to the handler is denied; a value of 1 indicates that the movement * is permitted. */ /* static void setInEnableStatus(struct s_info *s, int flg) { if (flg) s->status |= STATUS_InEnab; else s->status &= ~STATUS_InEnab; } */ /* * A value of 0 in the Export Enable field indicates that media movement * from the handler to the I/O port is denied. A value of 1 indicates that * movement is permitted. */ /* static void setExEnableStatus(struct s_info *s, int flg) { if (flg) s->status |= STATUS_ExEnab; else s->status &= ~STATUS_ExEnab; } */ /* * 1 / 0 Set/Clear the Access bit. * Access bit when set, indicates the medium transport can access media */ void setAccessStatus(struct s_info *s, int flg) { if (flg) s->status |= STATUS_Access; else s->status &= ~STATUS_Access; } /* * Reset to 0 indicates it is in normal state, set to 1 indicates an Exception * condition exists. An exception indicates the libary is uncertain of an * elements status. */ /* static void setExceptStatus(struct s_info *s, int flg) { if (flg) s->status |= STATUS_Except; else s->status &= ~STATUS_Except; } */ /* * If set(1) then cartridge placed by operator * If clear(0), placed there by handler. */ void setImpExpStatus(struct s_info *s, int flg) { if (flg) s->status |= STATUS_ImpExp; else s->status &= ~STATUS_ImpExp; } /* * Sets the 'Full' bit true/false in the status field */ void setFullStatus(struct s_info *s, int flg) { if (flg) s->status |= STATUS_Full; else s->status &= ~STATUS_Full; } void setSlotEmpty(struct s_info *s) { setFullStatus(s, 0); } static void setDriveEmpty(struct d_info *d) { setFullStatus(d->slot, 0); send_msg(msg_set_empty, d->drv_id); /* Wait for ack from drive */ check_tape_unload(); } void setSlotFull(struct s_info *s) { setFullStatus(s, 1); } void setDriveFull(struct d_info *d) { setFullStatus(d->slot, 1); } /* Returns 1 (true) if slot is MAP slot */ static int is_map_slot(struct s_info *s) { MHVTL_DBG(2, "slot type %d: %s", s->element_type, (s->element_type == MAP_ELEMENT) ? "MAP" : "NOT A MAP"); return s->element_type == MAP_ELEMENT; } static int map_access_ok(struct smc_priv *smc_p, struct s_info *s) { if (is_map_slot(s)) { MHVTL_DBG(3, "Returning status of %d", smc_p->cap_closed); return smc_p->cap_closed; } MHVTL_DBG(3, "Returning 0"); return 0; } static int dump_element_desc(uint8_t *p, int voltag, int num_elem, int len, char dvcid_serial_only) { int i, j, idlen; i = 0; for (j = 0; j < num_elem; j++) { MHVTL_DBG(3, " Debug.... i = %d, len = %d", i, len); MHVTL_DBG(3, " Element Address : %d", get_unaligned_be16(&p[i])); MHVTL_DBG(3, " Status : 0x%02x", p[i + 2]); MHVTL_DBG(3, " Medium type : %d", p[i + 9] & 0x7); if (p[i + 9] & 0x80) MHVTL_DBG(3, " Source Address : %d", get_unaligned_be16(&p[i + 10])); i += 12; if (voltag) { i += VOLTAG_LEN; MHVTL_DBG(3, " Voltag info..."); } MHVTL_DBG(3, " Identification Descriptor"); MHVTL_DBG(3, " Code Set : 0x%02x", p[i] & 0xf); MHVTL_DBG(3, " Identifier type : 0x%02x", p[i + 1] & 0xf); idlen = p[i + 3]; MHVTL_DBG(3, " Identifier length : %d", idlen); if (idlen) { if (dvcid_serial_only) { MHVTL_DBG(3, " ASCII data : %.*s", idlen, &p[i + 4]); } else { MHVTL_DBG(3, " ASCII data : %.*s", 8, &p[i + 4]); MHVTL_DBG(3, " ASCII data : %.*s", 16, &p[i + 12]); MHVTL_DBG(3, " ASCII data : %.*s", 10, &p[i + 28]); } } i = (j + 1) * len; } return i; } static void decode_element_status(struct smc_priv *smc_p, uint8_t *p) { int voltag; int elem_len; int page_elements, page_bytes; int total_count; int i; total_count = get_unaligned_be24(&p[5]); MHVTL_DBG(3, "Element Status Data"); MHVTL_DBG(3, " First element reported : %d", get_unaligned_be16(&p[0])); MHVTL_DBG(3, " Number of elements available : %d", get_unaligned_be16(&p[2])); MHVTL_DBG(3, " Byte count of report : %d", get_unaligned_be24(&p[5])); p += 8; total_count -= 8; while (total_count > 0) { MHVTL_DBG(3, "Element Status Page"); MHVTL_DBG(3, " Element Type code : %d (%s)", p[0], slot_type_str(p[0])); voltag = (p[1] & 0x80) ? 1 : 0; MHVTL_DBG(3, " Primary Vol Tag : %s", voltag ? "Yes" : "No"); MHVTL_DBG(3, " Alt Vol Tag : %s", (p[1] & 0x40) ? "Yes" : "No"); elem_len = get_unaligned_be16(&p[2]); MHVTL_DBG(3, " Element descriptor length : %d", elem_len); page_bytes = get_unaligned_be24(&p[5]); MHVTL_DBG(3, " Byte count of descriptor data: %d", page_bytes); page_elements = page_bytes / elem_len; p += 8; total_count -= 8; MHVTL_DBG(3, "Element Descriptor(s) : Num of Elements %d", page_elements); i = dump_element_desc(p, voltag, page_elements, elem_len, smc_p->pm->dvcid_serial_only); p += i; total_count -= i; } if (debug) fflush(NULL); } /* * Calculate length of one element */ static int sizeof_element(struct scsi_cmd *cmd, int type) { struct smc_priv *smc_p = (struct smc_priv *)cmd->lu->lu_private; int dvcid; int voltag; voltag = (cmd->scb[1] & 0x10) >> 4; if (smc_p->pm->no_dvcid_flag) dvcid = 1; else dvcid = cmd->scb[6] & 0x01; /* Device ID */ return 16 + (voltag ? VOLTAG_LEN : 0) + (dvcid && (type == DATA_TRANSFER) ? smc_p->pm->dvcid_len : 0); } /* * Fill in a single element descriptor * * Returns number of bytes in element data. */ static int fill_ed(struct scsi_cmd *cmd, uint8_t *p, struct s_info *s) { struct smc_priv *smc_p = (struct smc_priv *)cmd->lu->lu_private; struct d_info *d = NULL; int j = 0; uint8_t voltag; uint8_t dvcid; voltag = (cmd->scb[1] & 0x10) >> 4; if (smc_p->pm->no_dvcid_flag) dvcid = 1; else dvcid = cmd->scb[6] & 0x01; /* Device ID */ /* Should never occur, but better to trap then core */ if (!s) { MHVTL_DBG(1, "Slot out of range"); return 0; } if (s->element_type == DATA_TRANSFER) d = s->drive; put_unaligned_be16(s->slot_location, &p[j]); j += 2; p[j] = s->status; if (s->element_type == MAP_ELEMENT) { if (smc_p->cap_closed) p[j] |= STATUS_Access; else p[j] &= ~STATUS_Access; } j++; p[j++] = 0; /* Reserved */ /* Possible values for ASC/ASCQ for data transfer elements * 0x30/0x03 Cleaner cartridge present * 0x83/0x00 Barcode not scanned * 0x83/0x02 No magazine installed * 0x83/0x04 Tape drive not installed * 0x83/0x09 Unable to read bar code * 0x80/0x5d Drive operating in overheated state * 0x80/0x5e Drive being shutdown due to overheat condition * 0x80/0x63 Drive operating with low module fan speed * 0x80/0x5f Drive being shutdown due to low module fan speed */ p[j++] = (s->asc_ascq >> 8) & 0xff; /* Additional Sense Code */ p[j++] = s->asc_ascq & 0xff; /* Additional Sense Code Qualifer */ p[j++] = 0; /* Reserved */ if (s->element_type == DATA_TRANSFER) p[j++] = d->SCSI_ID; else p[j++] = 0; /* Reserved */ p[j++] = 0; /* Reserved */ /* bit 8 set if Source Storage Element is valid | s->occupied */ if (s->media) p[j] = (s->media->last_location > 0) ? 0x80 : 0; else p[j] = 0; /* Ref: smc3r12 - Table 28 * 0 - empty, * 1 - data, * 2 - cleaning tape, * 3 - Cleaning, * 4 - WORM, * 5 - Microcode image medium */ if (s->media) p[j] |= s->media->cart_type & 0x0f; j++; /* Source Storage Element Address */ put_unaligned_be16(s->last_location, &p[j]); j += 2; MHVTL_DBG(2, "Slot location: %d, DVCID: %d, VOLTAG: %d, status: 0x%02x", s->slot_location, dvcid, voltag, s->status); if (voltag) { /* Barcode with trailing space(s) */ if (s->status & STATUS_Full) { if (!(s->media->internal_status & INSTATUS_NO_BARCODE)) blank_fill(&p[j], s->media->barcode, VOLTAG_LEN); else memset(&p[j], 0, VOLTAG_LEN); } else memset(&p[j], 0, VOLTAG_LEN); j += VOLTAG_LEN; /* Account for barcode */ } if (dvcid && s->element_type == DATA_TRANSFER) { p[j++] = 2; /* Code set 2 = ASCII */ /* Identifier type - If serial number only - 0, otherwise 1 */ p[j++] = smc_p->pm->dvcid_serial_only ? 0 : 1; p[j++] = 0; /* Reserved */ p[j++] = smc_p->pm->dvcid_len; /* Identifier Length */ if (smc_p->pm->dvcid_serial_only) { blank_fill(&p[j], d->inq_product_sno, smc_p->pm->dvcid_len); j += smc_p->pm->dvcid_len; } else { blank_fill(&p[j], d->inq_vendor_id, 8); j += 8; blank_fill(&p[j], d->inq_product_id, 16); j += 16; blank_fill(&p[j], d->inq_product_sno, 10); j += 10; } } else { p[j++] = 0; /* Reserved */ p[j++] = 0; /* Reserved */ p[j++] = 0; /* Reserved */ p[j++] = 0; /* Reserved */ } MHVTL_DBG(3, "Element Descriptor - Returning %d (0x%02x) bytes", j, j); return j; } /* * Fill in element status page Header (8 bytes) */ static void fill_element_status_page_hdr(struct scsi_cmd *cmd, uint8_t *p, uint16_t element_count, uint8_t type) { struct smc_priv *smc_p; int element_sz; uint32_t element_len; uint8_t voltag; smc_p = (struct smc_priv *)cmd->lu->lu_private; voltag = (cmd->scb[1] & 0x10) >> 4; element_sz = sizeof_element(cmd, type); p[0] = type; /* Element type Code */ /* Primary Volume Tag set - Returning Barcode info */ p[1] = (voltag == 0) ? 0 : 0x80; if (smc_p->pm->dvcid_serial_only && type == DATA_TRANSFER) p[1] |= 0x40; /* Set AVolTag */ /* Number of bytes per element */ put_unaligned_be16(element_sz, &p[2]); element_len = element_sz * element_count; /* Total number of bytes in all element descriptors */ put_unaligned_be24(element_len, &p[5]); /* Reserved */ p[4] = 0; /* Above mask should have already set this to 0... */ MHVTL_DBG(2, "Element Status Page Header: " "%02x %02x %02x %02x %02x %02x %02x %02x", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); } /* * Build the initial ELEMENT STATUS HEADER * */ static int fill_element_status_data_hdr(uint8_t *p, int start, int count, uint32_t byte_count) { MHVTL_DBG(2, "Building READ ELEMENT STATUS Header struct"); MHVTL_DBG(2, " Starting slot: %d, number of configured slots: %d", start, count); /* Start of ELEMENT STATUS DATA */ put_unaligned_be16(start, &p[0]); put_unaligned_be16(count, &p[2]); /* The byte_count should be the length required to return all of * valid data. * The 'allocated length' indicates how much data can be returned. */ put_unaligned_be24(byte_count, &p[5]); MHVTL_DBG(2, " Element Status Data HEADER: " "%02x %02x %02x %02x %02x %02x %02x %02x", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); MHVTL_DBG(3, " Decoded:"); MHVTL_DBG(3, " First element Address : %d (0x%02x)", get_unaligned_be16(&p[0]), get_unaligned_be16(&p[0])); MHVTL_DBG(3, " Number elements reported : %d (0x%02x)", get_unaligned_be16(&p[2]), get_unaligned_be16(&p[2])); MHVTL_DBG(3, " Total byte count : %d (0x%04x)", get_unaligned_be32(&p[4]), get_unaligned_be32(&p[4])); return 8; /* Header is 8 bytes in size.. */ } /* Returns address of first available elements from starting number */ static uint32_t find_first_matching_element(struct smc_priv *priv, uint32_t start, uint8_t type) { struct list_head *slot_head; struct s_info *sp; slot_head = &priv->slot_list; list_for_each_entry(sp, slot_head, siblings) { if (!type) { /* Element type not defined */ if (sp->slot_location >= start) return sp->slot_location; } else if (sp->element_type == type) { if (sp->slot_location >= start) return sp->slot_location; } } return 0; } /* Returns number of available elements left from starting number */ static uint32_t num_available_elements(struct smc_priv *priv, uint8_t type, uint32_t start, uint32_t max) { struct list_head *slot_head; struct s_info *sp; unsigned int counted = 0; slot_head = &priv->slot_list; list_for_each_entry(sp, slot_head, siblings) { if (!type) { /* Element type not defined */ if (sp->slot_location >= start) if (counted < max) counted++; } else if (sp->element_type == type) { if (sp->slot_location >= start) if (counted < max) counted++; } } MHVTL_DBG(2, "Determining %d element%s of type %s starting at %d" ", returning %d", max, max == 1 ? "" : "s", slot_type_str(type), start, counted); return counted; } /* * Fill in Element status page header + each Element descriptor * * uint8_t *p -> Pointer to data buffer address * uint16_t start -> Starting slot number * uint8_t type -> Slot type * uint16_t residual -> Sum of slots already reported on * * Returns number of bytes in element page(s) */ static uint32_t fill_element_page(struct scsi_cmd *cmd, uint8_t *p, uint16_t start, uint8_t type, uint16_t residual) { struct smc_priv *smc_p; int j; uint8_t *cdb = cmd->scb; struct s_info *sp; uint16_t max_count; /* Max element count */ uint32_t avail_count; uint32_t element_sz; uint16_t begin_element; int slot_count; max_count = get_unaligned_be16(&cdb[4]); if (max_count == 0) max_count = ~0; slot_count = max_count - residual; if (slot_count <= 0) return 0; /* No more slots to report on */ /* Update max_count to reflect 'sum' value */ max_count = (uint16_t)slot_count; smc_p = (struct smc_priv *)cmd->lu->lu_private; MHVTL_DBG(2, "Query %d element%s starting from addr: %d" " of type: (%d) %s", max_count, (max_count == 1) ? "" : "s", start, type, slot_type_str(type)); /* Find first valid slot. */ begin_element = find_first_matching_element(smc_p, start, type); if (begin_element == 0) { MHVTL_DBG(1, "Start element is still 0, line %d", __LINE__); return 0; } avail_count = num_available_elements(smc_p, type, start, max_count); MHVTL_DBG(3, "Available count: %d, type: (%d) %s", avail_count, type, slot_type_str(type)); /* Create Element Status Page Header. */ fill_element_status_page_hdr(cmd, p, avail_count, type); /* Account for the 8 bytes in element status page header */ p += 8; avail_count = 8; /* Reuse avail_count as available byte count */ /* Now loop over each slot and fill in details. */ j = 1; list_for_each_entry(sp, &smc_p->slot_list, siblings) { if (sp->slot_location < start) continue; if (type) { if (sp->element_type != type) continue; /* Don't report on this one */ } else { /* Any type.. Need to fill in one type at a time */ if (sp->slot_location == start) type = sp->element_type; } element_sz = fill_ed(cmd, p, sp); avail_count += element_sz; /* inc byte count */ MHVTL_DBG(3, "Count: %d, max_count: %d, slot: %d, " "byte_count: 0x%04x (%d)", j, max_count, sp->slot_location, avail_count, avail_count); if (debug) hex_dump(p, element_sz); p += element_sz; /* inc pointer into dest buf */ j++; if (j > max_count) break; } return avail_count; } /* * Build READ ELEMENT STATUS data. * * Returns number of bytes to xfer back to host. */ #define NUM_SLOT 4 /* 4 Element types */ uint8_t smc_read_element_status(struct scsi_cmd *cmd) { struct smc_priv *smc_p = (struct smc_priv *)cmd->lu->lu_private; struct lu_phy_attr *lu = cmd->lu; uint8_t *cdb = cmd->scb; uint8_t *p; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint8_t type = cdb[1] & 0x0f; uint16_t sum; uint16_t req_start_elem; uint16_t req_number; /* Num of elements initiator requested */ uint32_t alloc_len; /* Amount of space initiator has pre alloc */ uint16_t start; /* First valid slot location */ uint16_t start_any; /* First valid slot location - temp count */ uint32_t elem_byte_count; uint32_t byte_count; uint32_t cur_count; struct s_sd sd; struct smc_type_slot type_arr[NUM_SLOT]; /* Hold sorted slot type */ char start_slot_type; int i; #ifdef MHVTL_DEBUG uint8_t voltag = (cdb[1] & 0x10) >> 4; uint8_t dvcid; /* Device ID */ if (smc_p->pm->no_dvcid_flag) dvcid = 1; else dvcid = cmd->scb[6] & 0x01; /* Device ID */ #endif MHVTL_DBG(1, "READ ELEMENT STATUS (%ld) **", (long)cmd->dbuf_p->serialNo); req_start_elem = get_unaligned_be16(&cdb[2]); req_number = get_unaligned_be16(&cdb[4]); alloc_len = get_unaligned_be24(&cdb[7]); MHVTL_DBG(3, " Element type(%d) => %s", type, slot_type_str(type)); MHVTL_DBG(3, " Starting Element Address: %d", req_start_elem); MHVTL_DBG(3, " Number of Elements : %d", req_number); MHVTL_DBG(3, " Allocation length : %d (0x%04x)", alloc_len, alloc_len); MHVTL_DBG(3, " Device ID: %s, voltag: %s", (dvcid == 0) ? "No" : "Yes", (voltag == 0) ? "No" : "Yes"); p = (uint8_t *)cmd->dbuf_p->data; /* Set alloc_len to smallest value */ alloc_len = min(alloc_len, smc_p->bufsize); cmd->dbuf_p->sz = 0; /* Init buffer */ memset(p, 0, alloc_len); if (cdb[11] != 0x0) { /* Reserved byte.. */ MHVTL_DBG(2, "cdb[11] : Illegal value of %02x", cdb[11]); sd.byte0 = SKSV | CD; sd.field_pointer = 11; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } /* Find first matching slot number which matches the type. */ start = find_first_matching_element(smc_p, req_start_elem, type); if (start == 0) { /* Nothing found.. */ MHVTL_DBG(1, "Start element is still 0, line %d", __LINE__); sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } /* Leave room for 'Element Status data' header which is filled in * after we figure out how many elements to report */ p += 8; /* Byte count of report available all pages, n-7 * Reference: table 44 6.12.2 smc3r15 * i.e. Don't include 8 byte 'Element Status data' header in the count */ elem_byte_count = 0; sum = 0; /* keep track of number of elements already reported */ switch (type) { case MEDIUM_TRANSPORT: case STORAGE_ELEMENT: case MAP_ELEMENT: case DATA_TRANSFER: elem_byte_count += fill_element_page(cmd, p, start, type, sum); break; case ANY: /* Don't modify 'start' value as it is needed later */ start_any = start; start_slot_type = slot_type(smc_p, start_any); sort_library_slot_type(lu, &type_arr[0]); /* Find out where we are up to */ for (i = 0; i < NUM_SLOT; i++) { MHVTL_DBG(2, "Testing type %d with %d", type_arr[i].type, start_slot_type); if (type_arr[i].type == start_slot_type) break; } if (i >= NUM_SLOT) { /* Not found in above for loop */ MHVTL_ERR("Couldn't find starting slot type !!"); sd.byte0 = SKSV | CD; sd.field_pointer = 3; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } MHVTL_DBG(3, "All element type: Starting at %d", start_any); for (; i < NUM_SLOT; i++) { MHVTL_DBG(3, "i: %d, type_arr[i].type: (%d) %s, " "type_arr[i].start: %d", i, type_arr[i].type, slot_type_str(type_arr[i].type), type_arr[i].start); byte_count = fill_element_page(cmd, p, start_any, type_arr[i].type, sum); elem_byte_count += byte_count; p += byte_count; sum += byte_count / sizeof_element(cmd, type_arr[i].type); if (i < NUM_SLOT - 1) /* Next slot type number */ start_any = type_arr[i + 1].start; } break; default: /* Illegal descriptor type. */ sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } cur_count = num_available_elements(smc_p, type, start, req_number); /* Now populate the 'main' header structure with byte count.. */ fill_element_status_data_hdr(cmd->dbuf_p->data, start, cur_count, elem_byte_count); #ifdef MHVTL_DEBUG if (debug) hex_dump(cmd->dbuf_p->data, elem_byte_count); #endif if (verbose > 2) decode_element_status(smc_p, cmd->dbuf_p->data); cmd->dbuf_p->sz = min(elem_byte_count + 8, alloc_len); MHVTL_DBG(2, "Element count: %d, Elem byte count: %d (0x%04x)," " alloc_len: %d, returning %d", cur_count, elem_byte_count, elem_byte_count, alloc_len, cmd->dbuf_p->sz); return SAM_STAT_GOOD; } /* Expect a response from tape drive on load success/failure * Returns 0 on success * non-zero on load failure * FIXME: I really need a timeout here.. */ static int check_tape_load(void) { int mlen, r_qid; struct q_entry q; /* Initialise message queue as necessary */ r_qid = init_queue(); if (r_qid == -1) { printf("Could not initialise message queue\n"); exit(1); } mlen = msgrcv(r_qid, &q, MAXOBN, my_id, MSG_NOERROR); if (mlen > 0) MHVTL_DBG(1, "%ld: Received \"%s\" from snd_id %ld", my_id, q.msg.text, q.msg.snd_id); /* msg defined in q.h */ return strncmp(msg_load_ok, q.msg.text, strlen(msg_load_ok)); } /* * Logically move information from 'src' address to 'dest' address */ static void move_cart(struct s_info *src, struct s_info *dest) { dest->media = src->media; dest->last_location = src->slot_location; dest->media->last_location = src->slot_location; setSlotFull(dest); if (is_map_slot(dest)) setImpExpStatus(dest, ROBOT_ARM); /* Placed by robot arm */ src->media = NULL; src->last_location = 0; /* Forget where the old media was */ setSlotEmpty(src); /* Clear Full bit */ MHVTL_DBG(1, "Setting src slot access true"); setAccessStatus(src, 1); /* Set the access bit now it's empty */ } static int run_move_command(struct smc_priv *smc_p, struct s_info *src, struct s_info *dest, uint8_t *sam_stat) { char *movecommand; char barcode[MAX_BARCODE_LEN + 1]; int res = 0; int cmdlen; if (!smc_p->movecommand) { /* no command: do nothing */ return SAM_STAT_GOOD; } cmdlen = strlen(smc_p->movecommand) + MAX_BARCODE_LEN + 4 * 10; movecommand = zalloc(cmdlen + 1); if (!movecommand) { MHVTL_ERR("malloc failed"); sam_hardware_error(E_MANUAL_INTERVENTION_REQ, sam_stat); return SAM_STAT_CHECK_CONDITION; } sprintf(barcode, "%s", src->media->barcode); truncate_spaces(&barcode[0], MAX_BARCODE_LEN + 1); snprintf(movecommand, cmdlen, "%s %s %d %s %d %s", smc_p->movecommand, slot_type_str(src->element_type), slot_number(smc_p->pm, src), slot_type_str(dest->element_type), slot_number(smc_p->pm, dest), barcode); MHVTL_DBG(3, "Calling external script: %s", movecommand); res = run_command(movecommand, smc_p->commandtimeout); if (res) { MHVTL_ERR("move command returned %d", res); sam_hardware_error(E_MANUAL_INTERVENTION_REQ, sam_stat); return SAM_STAT_CHECK_CONDITION; } return SAM_STAT_GOOD; } static int move_slot2drive(struct smc_priv *smc_p, int src_addr, int dest_addr, uint8_t *sam_stat) { struct s_info *src; struct d_info *dest; char cmd[MAX_BARCODE_LEN + 12]; int retval; current_state = MHVTL_STATE_MOVING_SLOT_2_DRIVE; src = slot2struct(smc_p, src_addr); dest = drive2struct(smc_p, dest_addr); if (!slotOccupied(src)) { sam_illegal_request(E_MEDIUM_SRC_EMPTY, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (driveOccupied(dest)) { sam_illegal_request(E_MEDIUM_DEST_FULL, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (src->element_type == MAP_ELEMENT) { if (!map_access_ok(smc_p, src)) { MHVTL_DBG(2, "SOURCE MAP port not accessable"); sam_not_ready(E_MAP_OPEN, sam_stat); return SAM_STAT_CHECK_CONDITION; } } /* Call any external cmd first before changing state */ retval = run_move_command(smc_p, src, dest->slot, sam_stat); if (retval) return retval; sprintf(cmd, "lload %s", src->media->barcode); /* Remove traling spaces */ truncate_spaces(&cmd[6], MAX_BARCODE_LEN + 1); /* FIXME: About here would be a good spot to create any 'missing' * media. That way, the user would not have to pre-create * media. */ MHVTL_DBG(1, "About to send cmd: \'%s\' to drive %d", cmd, slot_number(smc_p->pm, dest->slot)); send_msg(cmd, dest->drv_id); if (!smc_p->state_msg) smc_p->state_msg = (char *)zalloc(DEF_SMC_PRIV_STATE_MSG_LENGTH); if (smc_p->state_msg) { /* Re-use 'cmd[]' var */ snprintf(cmd, sizeof(cmd), "%s", src->media->barcode); truncate_spaces(&cmd[0], MAX_BARCODE_LEN + 1); snprintf(smc_p->state_msg, DEF_SMC_PRIV_STATE_MSG_LENGTH, "Moving %s from %s slot %d to drive %d", cmd, slot_type_str(src->element_type), slot_number(smc_p->pm, src), slot_number(smc_p->pm, dest->slot)); } if (check_tape_load()) { MHVTL_ERR("Load of %s into drive %d failed", cmd, slot_number(smc_p->pm, dest->slot)); sam_hardware_error(E_MANUAL_INTERVENTION_REQ, sam_stat); return SAM_STAT_CHECK_CONDITION; } move_cart(src, dest->slot); setDriveFull(dest); /* Set the 'Access bit' to zero - i.e. the picker arm can't access it */ setAccessStatus(dest->slot, 0); return retval; } static int move_slot2slot(struct smc_priv *smc_p, int src_addr, int dest_addr, uint8_t *sam_stat) { struct s_info *src; struct s_info *dest; char cmd[MAX_BARCODE_LEN + 1]; int retval; current_state = MHVTL_STATE_MOVING_SLOT_2_SLOT; src = slot2struct(smc_p, src_addr); dest = slot2struct(smc_p, dest_addr); MHVTL_DBG(1, "Moving from %s slot %d to %s slot %d", slot_type_str(src->element_type), src->slot_location, slot_type_str(dest->element_type), dest->slot_location); if (!slotOccupied(src)) { sam_illegal_request(E_MEDIUM_SRC_EMPTY, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (slotOccupied(dest)) { sam_illegal_request(E_MEDIUM_DEST_FULL, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (src->element_type == MAP_ELEMENT) { if (!map_access_ok(smc_p, src)) { MHVTL_DBG(2, "SOURCE MAP port not accessable"); sam_not_ready(E_MAP_OPEN, sam_stat); return SAM_STAT_CHECK_CONDITION; } } if (dest->element_type == MAP_ELEMENT) { if (!map_access_ok(smc_p, dest)) { MHVTL_DBG(2, "DESTINATION MAP port not accessable"); sam_not_ready(E_MAP_OPEN, sam_stat); return SAM_STAT_CHECK_CONDITION; } } if (!smc_p->state_msg) smc_p->state_msg = zalloc(DEF_SMC_PRIV_STATE_MSG_LENGTH); if (smc_p->state_msg) { snprintf(cmd, sizeof(cmd), "%s", src->media->barcode); truncate_spaces(&cmd[0], MAX_BARCODE_LEN + 1); snprintf(smc_p->state_msg, DEF_SMC_PRIV_STATE_MSG_LENGTH, "Moving %s from %s slot %d to %s slot %d", cmd, slot_type_str(src->element_type), slot_number(smc_p->pm, src), slot_type_str(dest->element_type), slot_number(smc_p->pm, dest)); } retval = run_move_command(smc_p, src, dest, sam_stat); if (retval) return retval; move_cart(src, dest); return retval; } /* Return OK if 'addr' is within either a MAP, Drive or Storage slot */ static int valid_slot(struct smc_priv *smc_p, int addr) { struct s_info *slt; struct d_info *drv; MHVTL_DBG(3, "%s slot %d", slot_type_str(slot_type(smc_p, addr)), addr); switch (slot_type(smc_p, addr)) { case STORAGE_ELEMENT: case MAP_ELEMENT: slt = slot2struct(smc_p, addr); if (slt) return TRUE; /* slot, return true */ break; case DATA_TRANSFER: drv = drive2struct(smc_p, addr); if (!drv) { MHVTL_DBG(1, "No target drive %d in device.conf", addr); return FALSE; /* No drive, return false */ } if (drv->drv_id) { MHVTL_DBG(3, "Found drive id: %d", (int)drv->drv_id); return TRUE; /* Found a drive ID */ } else { MHVTL_ERR("No drive in slot: %d", addr); } break; } return FALSE; } static int move_drive2slot(struct smc_priv *smc_p, int src_addr, int dest_addr, uint8_t *sam_stat) { char cmd[MAX_BARCODE_LEN + 1 + 12]; /* 12 being the longest msg string */ struct d_info *src; struct s_info *dest; int retval; current_state = MHVTL_STATE_MOVING_DRIVE_2_SLOT; src = drive2struct(smc_p, src_addr); dest = slot2struct(smc_p, dest_addr); if (!driveOccupied(src)) { sam_illegal_request(E_MEDIUM_SRC_EMPTY, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (slotOccupied(dest)) { sam_illegal_request(E_MEDIUM_DEST_FULL, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (dest->element_type == MAP_ELEMENT) { if (!map_access_ok(smc_p, dest)) { sam_not_ready(E_MAP_OPEN, sam_stat); return SAM_STAT_CHECK_CONDITION; } } /* Send any external command before any changes here */ retval = run_move_command(smc_p, src->slot, dest, sam_stat); if (retval) return retval; /* Send 'unload' message to drive b4 the move.. If not already unloaded */ if (!slotAccess(src->slot)) { sprintf(cmd, "unload %s", src->slot->media->barcode); send_msg(cmd, src->drv_id); /* Now we wait for the tape device to respond with status of unload */ if (check_tape_unload()) { MHVTL_ERR("Unload of %s from drive %d failed", cmd, slot_number(smc_p->pm, src->slot)); sam_hardware_error(E_MANUAL_INTERVENTION_REQ, sam_stat); return SAM_STAT_CHECK_CONDITION; } } if (!smc_p->state_msg) smc_p->state_msg = zalloc(DEF_SMC_PRIV_STATE_MSG_LENGTH); if (smc_p->state_msg) { snprintf(cmd, sizeof(cmd), "%s", src->slot->media->barcode); truncate_spaces(&cmd[0], MAX_BARCODE_LEN + 1); snprintf(smc_p->state_msg, DEF_SMC_PRIV_STATE_MSG_LENGTH, "Moving %s from drive %d to %s slot %d", cmd, slot_number(smc_p->pm, src->slot), slot_type_str(dest->element_type), slot_number(smc_p->pm, dest)); } move_cart(src->slot, dest); setDriveEmpty(src); return retval; } /* Move media in drive 'src_addr' to drive 'dest_addr' */ static int move_drive2drive(struct smc_priv *smc_p, int src_addr, int dest_addr, uint8_t *sam_stat) { struct d_info *src; struct d_info *dest; char cmd[MAX_BARCODE_LEN + 12]; int retval; current_state = MHVTL_STATE_MOVING_DRIVE_2_DRIVE; src = drive2struct(smc_p, src_addr); dest = drive2struct(smc_p, dest_addr); if (src_addr == dest_addr) { /* Did not find documentation for this behavior but feels like it should not fail */ MHVTL_DBG(1, "Same source and destination address : %d : do nothing", src_addr); return SAM_STAT_GOOD; } if (!driveOccupied(src)) { sam_illegal_request(E_MEDIUM_SRC_EMPTY, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (driveOccupied(dest)) { sam_illegal_request(E_MEDIUM_DEST_FULL, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } /* Execute any external commands before changing state */ retval = run_move_command(smc_p, src->slot, dest->slot, sam_stat); if (retval) return retval; /* Send 'unload' message to drive b4 the move.. */ MHVTL_DBG(2, "Unloading %s from drive %d", src->slot->media->barcode, slot_number(smc_p->pm, src->slot)); sprintf(cmd, "unload %s", src->slot->media->barcode); send_msg(cmd, src->drv_id); /* Now we wait for the tape device to respond with status of unload */ if (check_tape_unload()) { MHVTL_ERR("Failed to unload tape from drive %d", slot_number(smc_p->pm, src->slot)); sam_hardware_error(E_MANUAL_INTERVENTION_REQ, sam_stat); return SAM_STAT_CHECK_CONDITION; } move_cart(src->slot, dest->slot); setDriveEmpty(src); sprintf(cmd, "lload %s", dest->slot->media->barcode); truncate_spaces(&cmd[6], MAX_BARCODE_LEN + 1); MHVTL_DBG(2, "Sending cmd: \'%s\' to drive %d", cmd, slot_number(smc_p->pm, dest->slot)); send_msg(cmd, dest->drv_id); if (check_tape_load()) { /* Failed, so put the tape back where it came from */ MHVTL_ERR("Failed to move to drive %d, " "placing back into drive %d", slot_number(smc_p->pm, dest->slot), slot_number(smc_p->pm, src->slot)); move_cart(dest->slot, src->slot); sprintf(cmd, "lload %s", src->slot->media->barcode); truncate_spaces(&cmd[6], MAX_BARCODE_LEN + 1); send_msg(cmd, src->drv_id); check_tape_load(); sam_hardware_error(E_MANUAL_INTERVENTION_REQ, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (!smc_p->state_msg) smc_p->state_msg = zalloc(DEF_SMC_PRIV_STATE_MSG_LENGTH); if (smc_p->state_msg) { /* Re-use 'cmd[]' var */ snprintf(cmd, sizeof(cmd), "%s", dest->slot->media->barcode); truncate_spaces(&cmd[0], MAX_BARCODE_LEN + 1); snprintf(smc_p->state_msg, DEF_SMC_PRIV_STATE_MSG_LENGTH, "Moving %s from drive %d to drive %d", cmd, slot_number(smc_p->pm, src->slot), slot_number(smc_p->pm, dest->slot)); } /* Set the 'Access bit' to zero - i.e. the picker arm can't access it */ setAccessStatus(dest->slot, 0); return retval; } /* Move a piece of medium from one slot to another */ uint8_t smc_move_medium(struct scsi_cmd *cmd) { uint8_t *cdb = cmd->scb; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; int transport_addr; int src_addr, src_type; int dest_addr, dest_type; int retval = SAM_STAT_GOOD; struct smc_priv *smc_p = cmd->lu->lu_private; struct s_sd sd; MHVTL_DBG(1, "MOVE MEDIUM (%ld) **", (long)cmd->dbuf_p->serialNo); transport_addr = get_unaligned_be16(&cdb[2]); src_addr = get_unaligned_be16(&cdb[4]); dest_addr = get_unaligned_be16(&cdb[6]); src_type = slot_type(smc_p, src_addr); dest_type = slot_type(smc_p, dest_addr); if (cdb[11] & 0xc0) { MHVTL_DBG(1, "%s", (cdb[11] & 0x80) ? " Retract I/O port" : " Extend I/O port"); } else { MHVTL_DBG(1, "Moving from slot %d to slot %d using transport %d, Invert media: %s", src_addr, dest_addr, transport_addr, (cdb[10]) ? "yes" : "no"); } if (cdb[10] != 0) { /* Can not Invert media */ MHVTL_ERR("Can not invert media"); sd.byte0 = SKSV | CD; sd.field_pointer = 10; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (cdb[11] == 0xc0) { /* Invalid combo of Extend/retract I/O port */ MHVTL_ERR("Extend/retract I/O port invalid"); sd.byte0 = SKSV | CD; sd.field_pointer = 11; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (cdb[11]) /* Must be an Extend/Retract I/O port cdb.. NO-OP */ return SAM_STAT_GOOD; if (transport_addr == 0) transport_addr = smc_p->pm->start_picker; if (slot_type(smc_p, transport_addr) != MEDIUM_TRANSPORT) { MHVTL_ERR("Can't move media using slot type %d", slot_type(smc_p, transport_addr)); sd.byte0 = SKSV | CD; sd.field_pointer = 2; sam_illegal_request(E_INVALID_ELEMENT_ADDR, &sd, sam_stat); retval = SAM_STAT_CHECK_CONDITION; } if (!valid_slot(smc_p, src_addr)) { MHVTL_ERR("Invalid source slot: %d", src_addr); sd.byte0 = SKSV | CD; sd.field_pointer = 4; sam_illegal_request(E_INVALID_ELEMENT_ADDR, &sd, sam_stat); retval = SAM_STAT_CHECK_CONDITION; } if (!valid_slot(smc_p, dest_addr)) { MHVTL_ERR("Invalid dest slot: %d", dest_addr); sd.byte0 = SKSV | CD; sd.field_pointer = 6; sam_illegal_request(E_INVALID_ELEMENT_ADDR, &sd, sam_stat); retval = SAM_STAT_CHECK_CONDITION; } if (retval == SAM_STAT_GOOD) { if (src_type == DATA_TRANSFER && dest_type == DATA_TRANSFER) { /* Move between drives */ retval = move_drive2drive(smc_p, src_addr, dest_addr, sam_stat); } else if (src_type == DATA_TRANSFER) { retval = move_drive2slot(smc_p, src_addr, dest_addr, sam_stat); } else if (dest_type == DATA_TRANSFER) { retval = move_slot2drive(smc_p, src_addr, dest_addr, sam_stat); } else { /* Move between (non-drive) slots */ retval = move_slot2slot(smc_p, src_addr, dest_addr, sam_stat); } } return retval; } uint8_t smc_rezero(struct scsi_cmd *cmd) { MHVTL_DBG(1, "REZERO (%ld) **", (long)cmd->dbuf_p->serialNo); if (!cmd->lu->online) { sam_not_ready(NO_ADDITIONAL_SENSE, &cmd->dbuf_p->sam_stat); return SAM_STAT_CHECK_CONDITION; } sleep(1); return SAM_STAT_GOOD; } uint8_t smc_open_close_import_export_element(struct scsi_cmd *cmd) { uint8_t *cdb = cmd->scb; struct smc_priv *smc_p = cmd->lu->lu_private; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; int addr; int action_code; struct s_sd sd; MHVTL_DBG(1, "OPEN/CLOSE IMPORT/EXPORT ELEMENT (%ld) **", (long)cmd->dbuf_p->serialNo); addr = get_unaligned_be16(&cdb[2]); action_code = cdb[4] & 0x1f; MHVTL_DBG(2, "addr: %d action_code: %d", addr, action_code); if (slot_type(smc_p, addr) != MAP_ELEMENT) { sam_illegal_request(E_INVALID_ELEMENT_ADDR, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } switch (action_code) { case 0: /* open */ if (smc_p->cap_closed == CAP_CLOSED) { MHVTL_DBG(2, "opening CAP"); smc_p->cap_closed = CAP_OPEN; } break; case 1: /* close */ if (smc_p->cap_closed == CAP_OPEN) { MHVTL_DBG(2, "closing CAP"); smc_p->cap_closed = CAP_CLOSED; } break; default: MHVTL_DBG(1, "unknown action code: %d", action_code); sd.byte0 = SKSV | CD; sd.field_pointer = 4; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } return SAM_STAT_GOOD; } uint8_t smc_log_sense(struct scsi_cmd *cmd) { struct lu_phy_attr *lu; uint8_t *b = cmd->dbuf_p->data; uint8_t *cdb = cmd->scb; uint8_t *sam_stat; int retval; int i; uint16_t alloc_len; struct list_head *l_head; struct log_pg_list *l; struct s_sd sd; MHVTL_DBG(1, "LOG SENSE (%ld) **", (long)cmd->dbuf_p->serialNo); alloc_len = get_unaligned_be16(&cdb[7]); cmd->dbuf_p->sz = alloc_len; lu = cmd->lu; sam_stat = &cmd->dbuf_p->sam_stat; l_head = &lu->log_pg; retval = 0; switch (cdb[2] & 0x3f) { case 0: /* Send supported pages */ MHVTL_DBG(1, "LOG SENSE: Sending supported pages"); memset(b, 0, 4); /* Clear first few (4) bytes */ i = 4; b[i++] = 0; /* b[0] is log page '0' (this one) */ list_for_each_entry(l, l_head, siblings) { MHVTL_DBG(3, "found page 0x%02x", l->log_page_num); b[i] = l->log_page_num; i++; } put_unaligned_be16(i - 4, &b[2]); retval = i; break; case TEMPERATURE_PAGE: /* Temperature page */ MHVTL_DBG(1, "LOG SENSE: Temperature page"); l = lookup_log_pg(&lu->log_pg, TEMPERATURE_PAGE, NO_SUBPAGE); if (!l) goto log_page_not_found; b = memcpy(b, l->p, l->size); retval = l->size; break; case TAPE_ALERT: /* TapeAlert page */ MHVTL_DBG(1, "LOG SENSE: TapeAlert page"); /* MHVTL_DBG(2, " Returning TapeAlert flags: 0x%" PRIx64, get_unaligned_be64(&SequentialAccessDevice_pg.TapeAlert)); */ l = lookup_log_pg(&lu->log_pg, TAPE_ALERT, NO_SUBPAGE); if (!l) goto log_page_not_found; b = memcpy(b, l->p, l->size); retval = l->size; /* Clear flags after value read. */ if (alloc_len > 4) update_TapeAlert(TA_NONE); else MHVTL_DBG(1, "TapeAlert : Alloc len short -" " Not clearing TapeAlert flags."); break; default: MHVTL_DBG(1, "LOG SENSE: Unknown code: 0x%x", cdb[2] & 0x3f); goto log_page_not_found; break; } cmd->dbuf_p->sz = retval; return SAM_STAT_GOOD; log_page_not_found: cmd->dbuf_p->sz = 0; sd.byte0 = SKSV | CD; sd.field_pointer = 2; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } void unload_drive_on_shutdown(struct s_info *src, struct s_info *dest) { if (!dest) return; MHVTL_DBG(1, "Force unload of media %s to slot %d", src->media->barcode, dest->slot_location); move_cart(src, dest); } ================================================ FILE: usr/spc.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'PRIMARY' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include "be_byteshift.h" #include "mhvtl_scsi.h" #include "vtl_common.h" #include "vtllib.h" #include "logging.h" #include "ssc.h" extern unsigned char sense[]; uint32_t SPR_Reservation_Generation; uint8_t SPR_Reservation_Type; uint64_t SPR_Reservation_Key; struct vpd *alloc_vpd(uint16_t sz) { struct vpd *vpd_pg; vpd_pg = zalloc(sizeof(struct vpd)); if (!vpd_pg) { MHVTL_ERR("Could not malloc %d bytes of mem", (int)sizeof(struct vpd)); return NULL; } vpd_pg->data = zalloc(sz); if (!vpd_pg->data) { MHVTL_ERR("Could not malloc %d bytes of mem", sz); free(vpd_pg); return NULL; } vpd_pg->sz = sz; return vpd_pg; } void dealloc_vpd(struct vpd *pg) { free(pg->data); free(pg); } uint8_t spc_inquiry(struct scsi_cmd *cmd) { int len = 0; struct vpd *vpd_pg; uint8_t *data = (uint8_t *)cmd->dbuf_p->data; uint8_t *cdb = cmd->scb; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; struct lu_phy_attr *lu = cmd->lu; struct s_sd sd; MHVTL_DBG(1, "INQUIRY ** (%ld)", (long)cmd->dbuf_p->serialNo); if (((cdb[1] & 0x3) == 0x3) || (!(cdb[1] & 0x3) && cdb[2])) { sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (cdb[1] & 0x3) { /* VPD bit set - clear memory */ memset(data, 0, MAX_INQUIRY_SZ); } else { /* Standard inquiry - copy in-mem data struct */ memcpy(cmd->dbuf_p->data, lu->inquiry, MAX_INQUIRY_SZ); len = lu->inquiry[4] + 5; } if (cdb[1] & 0x2) { /* CmdDt bit is set */ /* We do not support it now. */ data[1] = 0x1; data[5] = 0; len = 6; } else if (cdb[1] & 0x1) { /* VPD */ uint8_t pcode = cdb[2]; MHVTL_DBG(2, "VPD Page code 0x%02x", pcode); if (pcode == 0x00) { uint8_t *p; unsigned int i, cnt; data[0] = lu->ptype; data[1] = 0; data[2] = 0; cnt = 1; p = data + 5; for (i = 0; i < ARRAY_SIZE(lu->lu_vpd); i++) { if (lu->lu_vpd[i]) { *p++ = i | 0x80; cnt++; } } data[3] = cnt; data[4] = 0x0; len = cnt + 4; hex_dump(data, len); } else if (lu->lu_vpd[PCODE_OFFSET(pcode)]) { vpd_pg = lu->lu_vpd[PCODE_OFFSET(pcode)]; MHVTL_DBG(2, "VPD Found page 0x%x", pcode); data[0] = lu->ptype; data[1] = pcode; data[2] = (vpd_pg->sz >> 8); data[3] = vpd_pg->sz & 0xff; memcpy(&data[4], vpd_pg->data, vpd_pg->sz); len = vpd_pg->sz + 4; hex_dump(data, len); } } cmd->dbuf_p->sz = len; return SAM_STAT_GOOD; } #ifdef MHVTL_DEBUG /* * Process PERSITENT RESERVE OUT scsi command * Returns 0 if OK * or -1 on failure. */ static char *type_str[] = { "Obsolete", "Write exclusive", "Obsolete", "Exclusive access", "Obsolete", }; static char *type_unknown = "Undefined"; static char *lookup_type(uint8_t type) { if (type > 4) return type_unknown; else return type_str[type]; } static char *serv_action_str[] = { "Register", "Reserve", "Release", "Clear", "Preempt", "Preempt & abort", "Register & ignore existing key", "Register & move", }; static char *sa_unknown = "Undefined"; static char *lookup_sa(uint8_t sa) { if (sa > 7) return sa_unknown; else return serv_action_str[sa]; } #endif #define SPR_EXCLUSIVE_ACCESS 3 uint8_t resp_spc_pro(uint8_t *cdb, struct mhvtl_ds *dbuf_p) { uint64_t RK; uint64_t SARK; uint16_t SA; uint8_t TYPE; uint8_t *sam_stat = &dbuf_p->sam_stat; uint8_t *buf = (uint8_t *)dbuf_p->data; struct s_sd sd; if (dbuf_p->sz != 24) { sd.byte0 = SKSV | CD; sd.field_pointer = 5; sam_illegal_request(E_PARAMETER_LIST_LENGTH_ERR, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } *sam_stat = SAM_STAT_GOOD; SA = cdb[1] & 0x1f; TYPE = cdb[2] & 0x0f; RK = get_unaligned_be64(&buf[0]); SARK = get_unaligned_be64(&buf[8]); MHVTL_DBG(2, "Key 0x%.8x %.8x SA Key 0x%.8x %.8x " "Service Action: %s, Type: %s", (uint32_t)(RK >> 32) & 0xffffffff, (uint32_t)RK & 0xffffffff, (uint32_t)(SARK >> 32) & 0xffffffff, (uint32_t)SARK & 0xffffffff, lookup_sa(SA), lookup_type(TYPE)); MHVTL_DBG(2, "Reservation key was: 0x%.8x 0x%.8x", (uint32_t)(SPR_Reservation_Key >> 32) & 0xffffffff, (uint32_t)(SPR_Reservation_Key & 0xffffffff)); switch (SA) { case 0: /* REGISTER */ if (SPR_Reservation_Key) { if (RK == SPR_Reservation_Key) { if (SARK) { SPR_Reservation_Key = SARK; } else { SPR_Reservation_Key = 0UL; SPR_Reservation_Type = 0; } SPR_Reservation_Generation++; } else { *sam_stat = SAM_STAT_RESERVATION_CONFLICT; } } else { if (RK) { *sam_stat = SAM_STAT_RESERVATION_CONFLICT; } else { SPR_Reservation_Key = SARK; SPR_Reservation_Generation++; } } break; case 1: /* RESERVE */ if (SPR_Reservation_Key) if (RK == SPR_Reservation_Key) if (TYPE == SPR_EXCLUSIVE_ACCESS) { SPR_Reservation_Type = TYPE; break; } *sam_stat = SAM_STAT_RESERVATION_CONFLICT; break; case 2: /* RELEASE */ if (SPR_Reservation_Key) if (RK == SPR_Reservation_Key) if (TYPE == SPR_EXCLUSIVE_ACCESS) { SPR_Reservation_Type = 0; break; } *sam_stat = SAM_STAT_RESERVATION_CONFLICT; break; case 3: /* CLEAR */ if (!SPR_Reservation_Key && !SPR_Reservation_Key) { *sam_stat = SAM_STAT_RESERVATION_CONFLICT; } else { if (RK == SPR_Reservation_Key) { SPR_Reservation_Key = 0UL; SPR_Reservation_Type = 0; SPR_Reservation_Generation++; } else { *sam_stat = SAM_STAT_RESERVATION_CONFLICT; } } break; case 4: /* PREEMT */ case 5: /* PREEMPT AND ABORT */ /* this is pretty weird, * in that we can only have a single key registered, * so preempt is pretty simplified */ if ((!SPR_Reservation_Key) && (!RK) && (!SARK)) { *sam_stat = SAM_STAT_RESERVATION_CONFLICT; } else { if (SPR_Reservation_Type) { if (SARK == SPR_Reservation_Key) { SPR_Reservation_Key = RK; SPR_Reservation_Type = TYPE; SPR_Reservation_Generation++; } } else { if (SARK == SPR_Reservation_Key) { SPR_Reservation_Key = 0UL; SPR_Reservation_Generation++; } } } break; case 6: /* REGISTER AND IGNORE EXISTING KEY */ if (SPR_Reservation_Key) { if (SARK) { SPR_Reservation_Key = SARK; } else { SPR_Reservation_Key = 0UL; SPR_Reservation_Type = 0; } } else { SPR_Reservation_Key = SARK; } SPR_Reservation_Generation++; break; case 7: /* REGISTER AND MOVE */ sam_illegal_request(E_INVALID_FIELD_IN_CDB, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; break; default: sam_illegal_request(E_INVALID_FIELD_IN_CDB, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } MHVTL_DBG(2, "Reservation key now: 0x%.8x 0x%.8x", (uint32_t)(SPR_Reservation_Key >> 32) & 0xffffffff, (uint32_t)(SPR_Reservation_Key & 0xffffffff)); return *sam_stat; } /* * Process PERSITENT RESERVE IN scsi command */ uint8_t resp_spc_pri(uint8_t *cdb, struct mhvtl_ds *dbuf_p) { uint16_t alloc_len; uint16_t SA; uint8_t *buf = (uint8_t *)dbuf_p->data; uint8_t *sam_stat = &dbuf_p->sam_stat; uint8_t sam_status; struct s_sd sd; SA = cdb[1] & 0x1f; alloc_len = get_unaligned_be16(&cdb[7]); memset(buf, 0, alloc_len); /* Clear memory */ MHVTL_DBG(1, "service action: %d", SA); sam_status = SAM_STAT_GOOD; switch (SA) { case 0: /* READ KEYS */ put_unaligned_be32(SPR_Reservation_Generation, &buf[0]); if (!SPR_Reservation_Key) { dbuf_p->sz = 8; break; } buf[7] = 8; put_unaligned_be64(SPR_Reservation_Key, &buf[8]); dbuf_p->sz = 16; break; case 1: /* READ RESERVATON */ put_unaligned_be32(SPR_Reservation_Generation, &buf[0]); if (!SPR_Reservation_Type) { dbuf_p->sz = 8; break; } buf[7] = 16; put_unaligned_be64(SPR_Reservation_Key, &buf[8]); buf[21] = SPR_Reservation_Type; dbuf_p->sz = 24; break; case 2: /* REPORT CAPABILITIES */ buf[1] = 8; buf[2] = 0x10; buf[3] = 0x80; buf[4] = 0x08; dbuf_p->sz = 8; break; case 3: /* READ FULL STATUS */ default: sd.byte0 = SKSV | CD; sd.field_pointer = 1; dbuf_p->sz = 0; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); sam_status = SAM_STAT_CHECK_CONDITION; break; } return sam_status; } uint8_t spc_tur(struct scsi_cmd *cmd) { MHVTL_DBG(1, "** %s (%ld) %s **", "TEST UNIT READY : Returning => ", (long)cmd->dbuf_p->serialNo, (cmd->lu->online) ? "Online" : "Offline"); if (cmd->lu->online) return SAM_STAT_GOOD; sam_not_ready(NO_ADDITIONAL_SENSE, &cmd->dbuf_p->sam_stat); return SAM_STAT_CHECK_CONDITION; } uint8_t spc_illegal_op(struct scsi_cmd *cmd) { uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; struct s_sd sd; MHVTL_DBG(1, "UNSUPPORTED OP CODE **"); sd.byte0 = SKSV | CD; sd.field_pointer = 0; /* byte 0 in cdb is invalid (the op code) */ sam_illegal_request(E_INVALID_OP_CODE, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } uint8_t spc_request_sense(struct scsi_cmd *cmd) { uint8_t *sense_buf = (uint8_t *)cmd->dbuf_p->sense_buf; uint8_t *cdb = cmd->scb; MHVTL_DBG(1, "REQUEST SENSE (%ld) : KEY/ASC/ASCQ " "[0x%02x 0x%02x 0x%02x]" " Filemark: %s, EOM: %s, ILI: %s", (long)cmd->dbuf_p->serialNo, sense_buf[2] & 0x0f, sense_buf[12], sense_buf[13], (sense_buf[2] & SD_FILEMARK) ? "yes" : "no", (sense_buf[2] & SD_EOM) ? "yes" : "no", (sense_buf[2] & SD_ILI) ? "yes" : "no"); assert(cmd->dbuf_p->data); /* Clear out the request sense flag */ cmd->dbuf_p->sam_stat = 0; /* set buf size */ cmd->dbuf_p->sz = min(cdb[4], (uint8_t)SENSE_BUF_SIZE); memcpy(cmd->dbuf_p->data, sense_buf, cmd->dbuf_p->sz); memset(sense_buf, 0, 18); /* First 18bytes contain usual suspect */ sense_buf[0] = SD_CURRENT_INFORMATION_FIXED; return SAM_STAT_GOOD; } /* * Log Select * * Set the logs to a known state. * * Currently a no-op */ static char LOG_SELECT_00[] = "Current threshold values"; static char LOG_SELECT_01[] = "Current cumulative values"; static char LOG_SELECT_10[] = "Default threshold values"; static char LOG_SELECT_11[] = "Default cumulative values"; uint8_t spc_log_select(struct scsi_cmd *cmd) { uint8_t *cdb = cmd->scb; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; char sp = cdb[1] & 0x1; /* Save Parameters */ char pcr = cdb[1] & 0x2; /* Parameter Code Reset */ uint16_t parmList; char *parmString = "Undefined"; struct s_sd sd; parmList = get_unaligned_be16(&cdb[7]); /* bytes 7 & 8 are parm list. */ MHVTL_DBG(1, "LOG SELECT (%ld)%s **", (long)cmd->dbuf_p->serialNo, (pcr) ? " : Parameter Code Reset " : ""); if (sp) { MHVTL_DBG(1, " Log Select - Save Parameters not supported"); sd.byte0 = SKSV | CD | BPV | 1; /* bit 1 */ sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (pcr) { /* Check for Parameter code reset */ if (parmList) { /* If non-zero, error */ sd.byte0 = SKSV | CD; sd.field_pointer = 7; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } switch ((cdb[2] & 0xc0) >> 6) { case 0: parmString = LOG_SELECT_00; break; case 1: parmString = LOG_SELECT_01; break; case 2: parmString = LOG_SELECT_10; break; case 3: parmString = LOG_SELECT_11; break; } MHVTL_DBG(1, " %s", parmString); } return SAM_STAT_GOOD; } /* * Process the MODE_SELECT command */ uint8_t spc_mode_select(struct scsi_cmd *cmd) { MHVTL_DBG(1, "MODE SELECT (%ld) **", (long)cmd->dbuf_p->serialNo); cmd->dbuf_p->sz = 0; return SAM_STAT_GOOD; } /* * Add data for pcode to buffer pointed to by p * Return: Number of chars moved. */ static int add_pcode(struct mode *m, uint8_t pc, uint8_t *p) { if (pc == 1) /* Report Changeable bitmap */ memcpy(p, m->pcodePointerBitMap, m->pcodeSize); else memcpy(p, m->pcodePointer, m->pcodeSize); return m->pcodeSize; } /* * Build mode sense data into *buf * Return SAM STATUS */ uint8_t spc_mode_sense(struct scsi_cmd *cmd) { int pc, pcode, subpcode; int alloc_len, msense_6; int len = 0; int offset = 0; uint8_t *ap; struct mode *smp; /* Struct mode pointer... */ struct priv_lu_ssc *ssc; int i, j; int WriteProtect = 0; struct s_sd sd; uint8_t *buf = (uint8_t *)cmd->dbuf_p->data; uint8_t *scb = cmd->scb; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; struct list_head *m = &cmd->lu->mode_pg; if (cmd->lu->ptype == TYPE_TAPE) { ssc = cmd->lu->lu_private; WriteProtect = ssc->MediaWriteProtect; } #ifdef MHVTL_DEBUG char *pcString[] = { "Current values", "Changeable values", "Default values", "Saved values", }; #endif /* Disable Block Descriptors */ uint8_t blockDescriptorLen = (scb[1] & 0x8) ? 0 : 8; /* pc => page control 00 -> 0: Report Current values 01 -> 1: Report Changeable values 10 -> 2: Report Default values 11 -> 3: Report Saved values */ pc = (scb[2] & 0xc0) >> 6; /* pcode -> Page Code */ pcode = scb[2] & 0x3f; subpcode = scb[3]; msense_6 = (MODE_SENSE == scb[0]); alloc_len = msense_6 ? scb[4] : ((scb[7] << 8) | scb[8]); offset = msense_6 ? 4 : 8; MHVTL_DBG(1, "MODE SENSE %d (%ld) **", (msense_6) ? 6 : 10, (long)cmd->dbuf_p->serialNo); MHVTL_DBG(2, " Page Control : %s(0x%02x)", pcString[pc], pc); MHVTL_DBG(2, " Page/Subpage Code: 0x%02x/0x%02x", pcode, subpcode); MHVTL_DBG(2, " %s Block Descriptor", (blockDescriptorLen) ? "Report" : "Disable"); MHVTL_DBG(2, " Allocation len : %d", alloc_len); if (0x3 == pc) { /* Saving values not supported */ MHVTL_DBG(2, "Reporting on Saved Values not supported"); sam_illegal_request(E_SAVING_PARMS_UNSUP, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (pcode == 0x3f && (subpcode != 0x0 && subpcode != 0xff)) { sd.byte0 = SKSV | CD; sd.field_pointer = 3; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } memset(buf, 0, alloc_len); /* Set return data to null */ offset += blockDescriptorLen; ap = buf + offset; switch (pcode) { case 0: len = 0; break; case 0x3f: /* Walk thru all possibilities */ if (subpcode == 0) { for (i = 1; i < 0x3f; i++) { smp = lookup_mode_pg(m, i, subpcode); if (smp) len += add_pcode(smp, pc, (uint8_t *)ap + len); } } else { /* 0x01 - 0xfe are reserved. Should only be 0xff */ for (i = 1; i < 0x3f; i++) { for (j = 0; j < 0xff; j++) { smp = lookup_mode_pg(m, i, j); if (smp) len += add_pcode(smp, pc, (uint8_t *)ap + len); } } } break; default: if (subpcode == 0xff) { /* All sub-pcodes for this pcode */ for (i = 0; i < 0xff; i++) { smp = lookup_mode_pg(m, pcode, i); if (smp) len += add_pcode(smp, pc, (uint8_t *)ap + len); } } else { smp = lookup_mode_pg(m, pcode, subpcode); if (smp) len = add_pcode(smp, pc, (uint8_t *)ap); } break; } offset += len; if (pcode != 0) /* 0 = No page code requested */ if (0 == len) { /* Page not found.. */ MHVTL_DBG(2, "Unknown mode page: 0x%02x sub-page code: 0x%02x", pcode, subpcode); sd.byte0 = SKSV | CD; sd.field_pointer = 2; /* Byte 2 page code */ sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } /* Fill in header.. */ if (msense_6) { buf[0] = offset - 1; /* size - sizeof(buf[0]) field */ buf[1] = cmd->lu->mode_media_type; buf[2] = (WriteProtect ? 0x80 : 0x00) | 0x10; buf[3] = blockDescriptorLen; /* If the length > 0, copy Block Desc. */ if (blockDescriptorLen) { switch (pc) { case 0: case 2: memcpy(&buf[4], modeBlockDescriptor, blockDescriptorLen); break; case 1: buf[9] = 0xff; buf[10] = 0xff; buf[11] = 0xff; break; } } } else { put_unaligned_be16(offset - 2, &buf[0]); buf[2] = cmd->lu->mode_media_type; buf[3] = (WriteProtect ? 0x80 : 0x00) | 0x10; put_unaligned_be16(blockDescriptorLen, &buf[6]); /* If the length > 0, copy Block Desc. */ if (blockDescriptorLen) { switch (pc) { case 0: case 2: memcpy(&buf[8], modeBlockDescriptor, blockDescriptorLen); break; case 1: buf[13] = 0xff; buf[14] = 0xff; buf[15] = 0xff; break; } } } #ifdef MHVTL_DEBUG if (debug) { printf("mode sense: Returning %d bytes\n", offset); hex_dump(buf, offset); } #endif cmd->dbuf_p->sz = offset; return SAM_STAT_GOOD; } uint8_t spc_release(struct scsi_cmd *cmd) { MHVTL_DBG(1, "RELEASE UNIT (%ld) **", (long)cmd->dbuf_p->serialNo); return SAM_STAT_GOOD; } uint8_t spc_reserve(struct scsi_cmd *cmd) { MHVTL_DBG(1, "RESERVE UNIT (%ld) **", (long)cmd->dbuf_p->serialNo); return SAM_STAT_GOOD; } uint8_t spc_send_diagnostics(struct scsi_cmd *cmd) { MHVTL_DBG(1, "SEND DIAGNOSTICS (%ld) **", (long)cmd->dbuf_p->serialNo); return SAM_STAT_GOOD; } uint8_t spc_recv_diagnostics(struct scsi_cmd *cmd) { uint8_t *data; uint8_t pc; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; struct s_sd sd; MHVTL_DBG(1, "RECEIVE DIAGNOSTIC (%ld) **", (long)cmd->dbuf_p->serialNo); if (cmd->scb[1] & 0x01) { /* Page Code Valid bit */ pc = cmd->scb[2]; MHVTL_DBG(3, "Page code: %d", pc); if (pc == 0) { data = cmd->dbuf_p->data; memset(data, 0, 10); /* Clear any junk */ put_unaligned_be16(1, &data[2]); cmd->dbuf_p->sz = 5; return SAM_STAT_GOOD; } } sd.byte0 = SKSV | CD; sd.field_pointer = 1; cmd->dbuf_p->sz = 0; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } uint8_t spc_read_buffer(struct scsi_cmd *cmd) { /* uint8_t *cdb = cmd->scb; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint8_t mode = cdb[1] & 0x11111; uint8_t buf_id = cdb[2]; uint16_t buf_offset = get_unaligned_be16(&cdb[3]); uint16_t alloc_length = get_unaligned_be16(&cdb[6]); */ return SAM_STAT_GOOD; } ================================================ FILE: usr/ssc.c ================================================ /* * This handles any SCSI OP codes defined in the standards as 'STREAM' * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * See comments in vtltape.c for a more complete version release... * */ #include #include #include #include #include #include "be_byteshift.h" #include "mhvtl_scsi.h" #include "mhvtl_list.h" #include "vtl_common.h" #include "logging.h" #include "vtllib.h" #include "spc.h" #include "ssc.h" #include "vtlcart.h" #include "mhvtl_log.h" #include "mode.h" #ifdef MHVTL_DEBUG static struct allow_overwrite_state { char *desc; } allow_overwrite_desc[] = { { "Disabled", }, { "Current Position", }, { "Format", }, { "Opps, Invalid field in CDB", }, }; #endif #define declare_ssc_vars \ struct lu_phy_attr *lu __attribute__((unused)) = cmd->lu; \ struct priv_lu_ssc *lu_priv __attribute__((unused)) = lu->lu_private; \ struct encryption *encr __attribute__((unused)) = lu_priv->app_encr_info; \ uint8_t *cdb __attribute__((unused)) = cmd->scb; \ struct mhvtl_ds *dbuf_p __attribute__((unused)) = cmd->dbuf_p; \ uint8_t *buf __attribute__((unused)) = (uint8_t *)dbuf_p->data; \ uint8_t *sam_stat __attribute__((unused)) = &dbuf_p->sam_stat; \ *sam_stat = SAM_STAT_GOOD; void memset_ssc_buf(struct scsi_cmd *cmd, uint64_t alloc_len) { declare_ssc_vars; memset(buf, 0, min((int)alloc_len, lu_priv->bufsize)); } uint8_t ssc_allow_overwrite(struct scsi_cmd *cmd) { declare_ssc_vars; struct s_sd sd; uint64_t allow_overwrite_block; uint8_t allow_overwrite = cdb[2] & 0x0f; uint8_t partition = cdb[3]; if (allow_overwrite > 2) /* Truncate bad values 3 to 15 -> '3' */ allow_overwrite = 3; MHVTL_DBG(1, "ALLOW OVERWRITE (%ld) : %s **", (long)dbuf_p->serialNo, allow_overwrite_desc[allow_overwrite].desc); lu_priv->allow_overwrite = FALSE; switch (allow_overwrite) { case 0: break; case 1: /* current position */ if (partition) { /* Partitions not supported at this stage */ MHVTL_LOG("Partitions not implemented at this time"); sd.byte0 = SKSV | CD; sd.field_pointer = 3; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } allow_overwrite_block = get_unaligned_be64(&cdb[4]); MHVTL_DBG(1, "Allow overwrite block: %lld", (long long)allow_overwrite_block); if (allow_overwrite_block == current_tape_block()) { lu_priv->allow_overwrite_block = allow_overwrite_block; lu_priv->allow_overwrite = TRUE; } else { /* Set allow_overwrite position to an invalid number */ lu_priv->allow_overwrite_block = 0; lu_priv->allow_overwrite_block--; sam_illegal_request(E_SEQUENTIAL_POSITIONING_ERROR, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } break; case 2: lu_priv->allow_overwrite = 2; break; default: sd.byte0 = SKSV | CD; sd.field_pointer = 2; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } return SAM_STAT_GOOD; } uint8_t ssc_log_select(struct scsi_cmd *cmd) { declare_ssc_vars; uint8_t pcr = cdb[1] & 0x2; /* Parameter code reset */ *sam_stat = spc_log_select(cmd); if (*sam_stat) /* spc_log_select() failed - return */ return *sam_stat; if (pcr) { switch ((cdb[2] & 0xc0) >> 6) { case 3: lu_priv->bytesRead_I = 0; lu_priv->bytesRead_M = 0; lu_priv->bytesWritten_I = 0; lu_priv->bytesWritten_M = 0; break; } } return SAM_STAT_GOOD; } uint8_t complete_read_6(struct scsi_cmd *cmd, int sz, int count) { declare_ssc_vars; int lbp_method; int k; int retval = 0; int fixed = cdb[1] & FIXED_BLOCK; switch (get_tape_load_status()) { case TAPE_LOADING: sam_not_ready(E_BECOMING_READY, sam_stat); return SAM_STAT_CHECK_CONDITION; break; case TAPE_LOADED: if (mam.MediumType == MEDIA_TYPE_CLEAN) { MHVTL_DBG(3, "Cleaning cart loaded"); sam_not_ready(E_CLEANING_CART_INSTALLED, sam_stat); return SAM_STAT_CHECK_CONDITION; } break; case TAPE_UNLOADED: MHVTL_DBG(3, "No media loaded"); sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; default: MHVTL_DBG(1, "Media format corrupt"); sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } for (k = 0; k < count; k++) { if (!lu_priv->pm->valid_encryption_blk(cmd)) return SAM_STAT_CHECK_CONDITION; /* If LBP Read bit is set, pass through the LBP_method 0: off, 1 RS-CRC, 2 CRC32C */ lbp_method = (lu_priv->LBP_R) ? lu_priv->LBP_method : 0; retval = readBlock(buf, sz, cdb[1] & SILI, lbp_method, sam_stat); if (!retval && fixed) { /* Fixed block read hack: * * Overwrite INFORMATION field with: * * SSC4r01e states: * The INFORMATION field shall be set to the requested * transfer length minus the actual number of logical * blocks read (not including the incorrect-length * logical block). * NOTE 35 - In the above case with the FIXED bit of * one, only the position of the incorrect-length * logical block may be determined from the sense * data. The actual length of the incorrect logical * block is not reported. Other means may be used to * determine its actual length (e.g., read it again * with the fixed bit set to zero). */ MHVTL_DBG(2, "Fixed block read short by %d blocks", count - k); put_unaligned_be32(count - k, &sense[3]); break; } buf += retval; dbuf_p->sz += retval; } if (retval > (sz * count)) retval = sz * count; return *sam_stat; } /* VERIFY_6: * * Here is what the IBM_LTO SCSI Reference (18Aug2021) says about the op code. * GA32-0928-04 18 August 2021 * * The following parameters apply: * * • VTE (verify to end-of-data): If the VTE bit is set to zero, then a verify to EOD is not requested. * If the VTE bit is set to one, then the expected verification sequence termination condition is met * when EOD is encountered. If a filemark is encountered during the sequence, processing continues. * If the verify command fails, then the VALID bit and the INFORMATION field of sense data are set * to zero. The VBF bit shall be set to zero. The VERIFICATION LENGTH field is ignored. * * • VLBPM (verify logical block protection method): This bit has no effect. The result is the same for either setting. * * • VBF (verify by filemarks): If the VBF bit is set to zero, then a verify of n filemarks is not requested. * If the VBF bit is set to one, then the expected verification sequence termination condition is met * if the number of filemarks specified by the VERIFICATION LENGTH field have been traversed. If a * filemark is encountered during the sequence, processing continues. If EOD is encountered, the * sense key is set to BLANK CHECK, the EOM bit is set to one if the logical position is at or * after early warning, and the additional sense code is set to END-OF-DATA DETECTED. If a verify * operation fails, then the verification sequence terminates and the VALID bit is set to one and * the INFORMATION FIELD is set to the requested verification length minus the actual number of * filemarks successfully traversed. The VTE bit shall be set to zero. * NOTE 30 - Following the completion of a verify with the VBF bit set to one, the application * client should issue a READ POSITION command to determine the logical object identifier associated * with the current logical position. * * • IMMED (immediate) : An IMMED bit set to zero specifies the command shall not return status until * the verify sequence has completed. * An IMMED bit set to one specifies status shall be returned as soon as the command descriptor block * has been validated. Verification sequences that complete unsuccessfully generate deferred sense data * indicating the reason for termination (e.g., .an incorrect length logical block is encountered and * the sense data is set to indicate an incorrect length block was encountered). * NOTE 31 - In order to ensure that no errors are lost, the application client should set the IMMED * bit to zero on the last VERIFY (6) command of a series of VERIFY (6) commands. * * • BYTCMP (byte compare): Byte compare is not supported by this device. The BYTCMP bit shall be set to * zero to specify the verification shall be a verification of logical blocks on the medium (e.g., CRC, ECC). * No data shall be transferred from the application client to the device server. * * • FIXED: If the VTE bit and the VBF bit are set to zero and the FIXED bit is set to one, then the * expected verification sequence termination condition is met when the number of logical blocks * specified in the VERIFICATION LENGTH field have been traversed. If the VERIFICATION LENGTH field * is set to zero, then no logical objects are verified and the current logical position is not changed. * This condition is not an error. If a file-mark is encountered during the sequence, processing * terminates with filemark encountered as specified in the READ(6) command (see 5.2.15). * If EOD is encountered, the sense key is set to BLANK CHECK, the EOM bit is set to one if the logical * position is at or after early warning, and the additional sense code is set to END-OF-DATA DETECTED. * If a verify operation fails, then the verification sequence terminates and the VALID bit is set to * one and the INFORMATION FIELD is set to the requested verification length minus the actual number * of logical blocks successfully traversed. * If the VTE bit and the VBF bit are set to zero and the FIXED bit is set to zero, then the expected * verification sequence termination condition is met when one logical block has been traversed. * The length of the verified logical block is equal to the value specified in the VERIFICATION LENGTH field. * If the VERIFICATION LENGTH field is set to zero, then no logical objects are verified and the * current logical position is not changed. This condition is not considered an error. If a filemark * is encountered during the sequence, pro- cessing terminates with filemark encountered as specified in the * READ(6) command (see 5.2.15). If EOD is encountered, the sense key is set to BLANK CHECK, the EOM bit is * set to one if the logical position is at or after early warning, and the additional sense code is set to * END-OF-DATA DETECTED. If a verify oper- ation fails, then the verification sequence terminates and the * VALID bit is set to one and the INFORMATION FIELD is set to the requested verification length minus * the actual number of bytes successfully traversed. * A FIXED bit set to zero and either the VTE bit set to one or the VBF bit set to one specifies that the * block length shall not be checked. * A FIXED bit set to one specifies that the length of verified logical blocks shall be equal to the the * current block length reported in the mode parameters block descriptor. Refer to the READ(6) command * (see 5.2.15) for a description of the FIXED bit and any error conditions that may result from incorrect usage. * * • VERIFICATION LENGTH: The VERIFICATION LENGTH field specifies the number of bytes, logical blocks, * or file-marks to traverse during verification, as specified by the VBF bit and the FIXED bit. * If the VTE bit is set to one, then the VERIFICATION LENGTH field is ignored. * If the VERIFICATION LENGTH field is set to zero and the VTE bit is set to zero, then no logical objects * are verified and the current logical position is not changed. This condition is not considered an error. */ uint8_t ssc_verify_6(struct scsi_cmd *cmd) { declare_ssc_vars; int blocks; int sz; struct s_sd sd; struct verify_6_bits *cdb1 = (struct verify_6_bits *)&cdb[1]; set_current_state(MHVTL_STATE_VERIFY); dbuf_p->sz = 0; /* zero data xfer between application and target */ opcode_6_params(cmd, &blocks, &sz); MHVTL_DBG(1, "VERIFY 6 : %s: %d, fixed: %d, bytcmp: %d, immed: %d, vbf: %d, vlbpm: %d, vte: %d, cdb[1]: 0x%02x (%ld) **", (cdb1->FIXED) ? "Num blks" : "byte count", (cdb1->FIXED) ? blocks : sz, cdb1->FIXED, cdb1->BYTCMP, cdb1->IMMED, cdb1->VBF, cdb1->VLBPM, cdb1->VTE, cdb[1], (long)dbuf_p->serialNo); if (cdb1->VTE) { /* To be implemented */ MHVTL_DBG(1, "Verify to end of data is currently not implemented"); sd.byte0 = SKSV | CD | BPV | 0x6; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (cdb1->VBF) { int count; count = (cdb1->FIXED) ? blocks : sz; MHVTL_DBG(1, "Verify %d filemarks", count); resp_space(count, 1, sam_stat); /* Lets try and move 'count' number of filemarks */ return *sam_stat; } return complete_read_6(cmd, sz, blocks); } uint8_t ssc_read_6(struct scsi_cmd *cmd) { declare_ssc_vars; int count; int sz; struct s_sd sd; set_current_state(MHVTL_STATE_READING); opcode_6_params(cmd, &count, &sz); MHVTL_DBG(3, "READ 6 (%ld) ** %d block%s of %d bytes ", (long)dbuf_p->serialNo, count, count == 1 ? "" : "s", sz); /* If both FIXED & SILI bits set, invalid combo.. */ if ((cdb[1] & (SILI | FIXED_BLOCK)) == (SILI | FIXED_BLOCK)) { MHVTL_DBG(1, "Suppress ILI and Fixed block " "read not allowed by SSC3"); sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } return complete_read_6(cmd, sz, count); } uint8_t ssc_write_6(struct scsi_cmd *cmd) { declare_ssc_vars; int retval = 0; int count; int sz; int k; set_current_state(MHVTL_STATE_WRITING); opcode_6_params(cmd, &count, &sz); MHVTL_DBG(3, "WRITE 6 : %d block%s of %d bytes (%ld) **", count, count == 1 ? "" : "s", sz, (long)dbuf_p->serialNo); /* FIXME: Should handle this instead of 'check & warn' */ if ((sz * count) > lu_priv->bufsize) MHVTL_DBG(1, "Fatal: bufsize %d, requested write of %d bytes", lu_priv->bufsize, sz); dbuf_p->sz = sz * count; /* Retrieve data from kernel - unless media type is 'null' */ if (likely(mam.MediumType != MEDIA_TYPE_NULL)) retrieve_CDB_data(cmd->cdev, dbuf_p); if (!lu_priv->pm->check_restrictions(cmd)) return SAM_STAT_CHECK_CONDITION; if (OK_to_write) { for (k = 0; k < count; k++) { retval = writeBlock(cmd, sz); buf += retval; if (*sam_stat) return *sam_stat; } } return SAM_STAT_GOOD; } /* * Check for any write restrictions - e.g. WORM, or Clean Cartridge mounted. * Return 1 = OK to write, zero -> Can't write. */ uint8_t check_restrictions(struct scsi_cmd *cmd) { declare_ssc_vars; /* Check that there is a piece of media loaded.. */ switch (get_tape_load_status()) { case TAPE_LOADING: sam_not_ready(E_BECOMING_READY, sam_stat); *lu_priv->OK_2_write = 0; return *lu_priv->OK_2_write; break; case TAPE_LOADED: /* Do nothing */ break; case TAPE_UNLOADED: sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); *lu_priv->OK_2_write = 0; return *lu_priv->OK_2_write; break; default: sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); *lu_priv->OK_2_write = 0; return *lu_priv->OK_2_write; break; } switch (mam.MediumType) { case MEDIA_TYPE_CLEAN: sam_not_ready(E_CLEANING_CART_INSTALLED, sam_stat); MHVTL_DBG(2, "Can not write - Cleaning cart"); *lu_priv->OK_2_write = 0; break; case MEDIA_TYPE_WORM: /* If we are not at end of data for a write * and media is defined as WORM, fail... */ /* OK to append to end of 'tape' */ if (c_pos->blk_type == B_EOD) *lu_priv->OK_2_write = 1; if (!*lu_priv->OK_2_write) { MHVTL_DBG(1, "Failed attempt to overwrite WORM data"); sam_data_protect(E_MEDIUM_OVERWRITE_ATTEMPT, sam_stat); } break; case MEDIA_TYPE_DATA: *lu_priv->OK_2_write = 1; break; case MEDIA_TYPE_NULL: *lu_priv->OK_2_write = 1; /* Special case - no need for more checks */ return *lu_priv->OK_2_write; break; default: *lu_priv->OK_2_write = 0; sam_illegal_request(E_MEDIUM_INCOMPATIBLE, NULL, sam_stat); } /* over-ride the above IF the virtual write protect switch is on */ if (*lu_priv->OK_2_write && lu_priv->MediaWriteProtect) { *lu_priv->OK_2_write = 0; sam_data_protect(E_WRITE_PROTECT, sam_stat); } /* over-ride the above IF running in append_only mode and this write * isn't authorized. * Some writes would be OK, like the first write to an empty tape or * WORM media overwriting a filemark that is next to EOD */ if (*lu_priv->OK_2_write && lu_priv->append_only_mode) { if ((c_pos->blk_number != lu_priv->allow_overwrite_block) && (c_pos->blk_type != B_EOD)) { uint64_t TAflag; lu_priv->OK_2_write = 0; lu_priv->allow_overwrite = FALSE; sam_data_protect(E_MEDIUM_OVERWRITE_ATTEMPT, sam_stat); /* And set TapeAlert flg 09 -> WRITE PROTECT */ TAflag = TA_WRITE_PROTECT; update_TapeAlert(TAflag); } } MHVTL_DBG(2, "returning:%s writable", (*lu_priv->OK_2_write) ? "" : " not"); return *lu_priv->OK_2_write; } /* * Returns true if blk header has correct encryption key data */ #define UKAD_LENGTH (encr->ukad_length) #define AKAD_LENGTH (encr->akad_length) #define KEY_LENGTH (encr->key_length) #define UKAD (encr->ukad) #define AKAD (encr->akad) #define KEY (encr->key) uint8_t valid_encryption_blk(struct scsi_cmd *cmd) { declare_ssc_vars; uint8_t correct_key; int i; /* decryption logic */ correct_key = TRUE; if (c_pos->blk_flags & BLKHDR_FLG_ENCRYPTED) { /* compare the keys */ if (lu_priv->DECRYPT_MODE > 1) { if (c_pos->blk_encryption_info.key_length != KEY_LENGTH) { sam_data_protect(E_INCORRECT_KEY, sam_stat); correct_key = FALSE; } for (i = 0; i < c_pos->blk_encryption_info.key_length; ++i) { if (c_pos->blk_encryption_info.key[i] != KEY[i]) { sam_data_protect(E_INCORRECT_KEY, sam_stat); correct_key = FALSE; return correct_key; } } } else { sam_data_protect(E_UNABLE_TO_DECRYPT, sam_stat); correct_key = FALSE; } } else if (lu_priv->DECRYPT_MODE == 2) { sam_data_protect(E_UNENCRYPTED_DATA, sam_stat); correct_key = FALSE; } return correct_key; } uint8_t valid_encryption_media(struct scsi_cmd *cmd) { declare_ssc_vars; if (c_pos->blk_number == 0) { modeBlockDescriptor[0] = lu_priv->pm->native_drive_density->density; mam.MediumDensityCode = modeBlockDescriptor[0]; mam.FormattedDensityCode = modeBlockDescriptor[0]; rewriteMAM(sam_stat); } else { if (mam.MediumDensityCode != lu_priv->pm->native_drive_density->density) { sam_data_protect(E_WRITE_PROTECT, sam_stat); return SAM_STAT_CHECK_CONDITION; } } return SAM_STAT_GOOD; } uint8_t ssc_allow_prevent_removal(struct scsi_cmd *cmd) { /* FIXME: Currently does nothing... */ MHVTL_DBG(1, "%s MEDIA REMOVAL (%ld) **", (cmd->scb[4]) ? "PREVENT" : "ALLOW", (long)cmd->dbuf_p->serialNo); return SAM_STAT_GOOD; } uint8_t ssc_format_medium(struct scsi_cmd *cmd) { declare_ssc_vars; MHVTL_DBG(1, "FORMAT MEDIUM (%ld) **", (long)dbuf_p->serialNo); if (!lu_priv->pm->check_restrictions(cmd)) return SAM_STAT_CHECK_CONDITION; if (c_pos->blk_number != 0) { MHVTL_DBG(2, "Failed - Not at beginning"); sam_illegal_request(E_POSITION_PAST_BOM, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } /* 0h = format the volume to a single partition */ /* 1h = use medium partition mode page to format the partitions */ /* 2h = do 0h then 1h */ switch (cmd->scb[2] & 0x0F) /* Format */ { case 0x00: mam.num_partitions = 1; format_tape(sam_stat); break; case 0x01: /* TODO : take MODE_MEDIUM_PARTITION into account */ format_tape(sam_stat); break; case 0x02: /* TODO : do 0x00 then 0x01 */ format_tape(sam_stat); break; default: sam_illegal_request(E_INVALID_FIELD_IN_CDB, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } return SAM_STAT_GOOD; } uint8_t ssc_locate(struct scsi_cmd *cmd) { declare_ssc_vars; uint64_t blk_no; uint8_t partition_no = c_pos->partition_id; set_current_state(MHVTL_STATE_LOCATE); MHVTL_DBG(1, "LOCATE %d (%ld) **", (cdb[0] == LOCATE_16) ? 16 : 10, (long)dbuf_p->serialNo); switch (cdb[0]) { case LOCATE_10: if (cdb[1] & 0b00000010) /* CP - Change Partition */ partition_no = cdb[8]; blk_no = (uint64_t)get_unaligned_be32(&cdb[3]); break; case LOCATE_16: if (cdb[1] & 0b00000010) partition_no = cdb[3]; switch (cdb[1] & 0b00011000) { /* Destination Type */ case 0b00: /* with logical object identifier */ blk_no = get_unaligned_be64(&cdb[4]); /* mhvtl only supports u32 blk_numbers so blk_no will be truncated */ break; case 0b01: /* with logical file identifier */ blk_no = block_from_filemark(partition_no, get_unaligned_be64(&cdb[4])); break; case 0b11: /* finish with EOD */ blk_no = last_block(partition_no); default: sam_illegal_request(E_INVALID_FIELD_IN_CDB, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } break; default: return SAM_STAT_CHECK_CONDITION; } if ((cdb[1] & 0b00000010) && partition_no > mam.num_partitions) { sam_illegal_request(E_INVALID_FIELD_IN_CDB, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } /* If we want to seek closer to beginning of file than * we currently are, rewind and seek from there */ MHVTL_DBG(2, "Current partition/blk: %u/%u, seek: %u/%u", c_pos->partition_id, c_pos->blk_number, partition_no, (uint32_t)blk_no); change_partition(partition_no); position_to_block((uint32_t)blk_no, sam_stat); return *sam_stat; } uint8_t ssc_load_display(struct scsi_cmd *cmd) { declare_ssc_vars; char str1[9]; char str2[9]; MHVTL_DBG(1, "LOAD DISPLAY (%ld) - T10000 specific **", (long)dbuf_p->serialNo); dbuf_p->sz = cdb[4]; retrieve_CDB_data(cmd->cdev, dbuf_p); memcpy(str1, &buf[1], 8); str1[8] = 0; memcpy(str2, &buf[9], 8); str2[8] = 0; MHVTL_DBG(3, "Raw data: %02x " "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15], buf[16]); switch (buf[0] >> 5) { /* Bits 5, 6 & 7 are overlay */ case 0: MHVTL_DBG(1, "Display \'%s\' until next" " command that initiates tape motion", /* Low/High bit */ (buf[0] & 2) ? str2 : str1); break; case 1: MHVTL_DBG(1, "Maintain \'%s\' until the" " cartridge is unloaded", /* Low/High bit */ (buf[0] & 2) ? str2 : str1); break; case 2: MHVTL_DBG(1, "Maintain \'%s\' until the drive" " is next loaded", str1); break; case 3: MHVTL_DBG(1, "Physically access tape drive with" "out changing the msg"); break; case 7: MHVTL_DBG(1, "Display \'%s\' until the tape" " drive is unloaded then \'%s\'", str1, str2); break; } MHVTL_DBG(2, "Load display: msg1: %s msg2: %s", str1, str2); dbuf_p->sz = 0; return SAM_STAT_GOOD; } #define REPORT_TIMESTAMP_DATA_LEN 0x0a /* If timestamp_source == 0 : timestamp is num of uS at logical unit initialization - get_timestamp will return delta of mS between init and now * If timestamp_source == 2 : timestamp is set to that provided by initiator - get_timestamp will return delta + timestamp */ static uint64_t timestamp; /* Used for device clock - number uS since initialization */ static int64_t timestamp_offset; /* Used for device clock - offset of local clock and initiator 'set timestamp' value */ static uint8_t timestamp_source; void set_timestamp(uint8_t source, uint64_t ts) { struct timeval tv; uint64_t now; timestamp_source = source; gettimeofday(&tv, NULL); now = 1000000 * tv.tv_sec + tv.tv_usec; if (source) { timestamp = ts * 1000; /* save as uSec */ timestamp_offset = now - timestamp; } else { timestamp = now; timestamp_offset = 0; } MHVTL_DBG(1, "SET timestamp: source %u, timestamp is %lu, offset is %ld", source, timestamp, timestamp_offset); } static uint64_t get_timestamp() { struct timeval tv; uint64_t now; gettimeofday(&tv, NULL); now = 1000000 * tv.tv_sec + tv.tv_usec; if (timestamp_source) { MHVTL_DBG(1, "now: %lx, offset: %ld, ret val: 0x%lx", now, timestamp_offset, (now - timestamp_offset) / 1000); return (now - timestamp_offset) / 1000; /* Account for any offset between local time and initiator set time */ } else { MHVTL_DBG(1, "now: %lx, timestamp: %lx, ret val: 0x%lx", now, timestamp, (now - timestamp) / 1000); return (now - timestamp) / 1000; /* Num of mS since init */ } } static uint8_t report_timestamp(struct scsi_cmd *cmd) { declare_ssc_vars; memset(buf, 0, REPORT_TIMESTAMP_DATA_LEN + 2); put_unaligned_be16(REPORT_TIMESTAMP_DATA_LEN, &buf[0]); buf[2] = timestamp_source; /* Timestamp origin - Timestamp initialised to zero at power-on */ put_unaligned_be48(get_timestamp() & 0xffffffffffff, &buf[4]); MHVTL_DBG(1, "Returning timestamp 0x%08lx (%lu)", get_unaligned_be48(&buf[4]), get_unaligned_be48(&buf[4])); dbuf_p->sz = REPORT_TIMESTAMP_DATA_LEN + 2; *sam_stat = SAM_STAT_GOOD; return SAM_STAT_GOOD; } static uint8_t configure_timestamp(struct scsi_cmd *cmd) { declare_ssc_vars; struct s_sd sd; dbuf_p->sz = get_unaligned_be32(&cdb[6]); if (dbuf_p->sz == 0) return SAM_STAT_GOOD; if (dbuf_p->sz != 0x0c) { dbuf_p->sz = 0; sd.byte0 = SKSV; sd.field_pointer = 6; MHVTL_LOG("Unexpected timestamp parameter length.."); sam_illegal_request(E_INVALID_FIELD_IN_PARMS, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } retrieve_CDB_data(cmd->cdev, cmd->dbuf_p); if (buf[4] > 0xf0) { /* overflow - Illegal request */ dbuf_p->sz = 0; sd.byte0 = SKSV; sd.field_pointer = 4; MHVTL_LOG("Unexpected set timestamp value.. Value too large"); sam_illegal_request(E_INVALID_FIELD_IN_PARMS, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } set_timestamp(2, get_unaligned_be48(&buf[4])); dbuf_p->sz = 0; *sam_stat = SAM_STAT_GOOD; return SAM_STAT_GOOD; } uint8_t ssc_a3_service_action(struct scsi_cmd *cmd) { declare_ssc_vars; switch (cdb[1]) { case MANAGEMENT_PROTOCOL_IN: log_opcode("MANAGEMENT PROTOCOL IN **", cmd); break; case REPORT_ALIASES: log_opcode("REPORT ALIASES **", cmd); break; case REPORT_SUPPORTED_OPCODES: log_opcode("REPORT SUPPORTED OPCODES **", cmd); break; case REPORT_TIMESTAMP: MHVTL_DBG(1, "REPORT TIMESTAMP (%ld) **", (long)dbuf_p->serialNo); return report_timestamp(cmd); break; default: log_opcode("UNKNOWN SERVICE ACTION A3 **", cmd); break; } return *sam_stat; } uint8_t ssc_a4_service_action(struct scsi_cmd *cmd) { declare_ssc_vars; switch (cdb[1]) { case MANAGEMENT_PROTOCOL_OUT: log_opcode("MANAGEMENT PROTOCOL OUT **", cmd); break; case CHANGE_ALIASES: log_opcode("CHANGE ALIASES **", cmd); break; case FORCED_EJECT: log_opcode("FORCED EJECT **", cmd); break; case SET_TIMESTAMP: MHVTL_DBG(1, "SET TIMESTAMP (%ld) **", (long)dbuf_p->serialNo); return configure_timestamp(cmd); break; default: log_opcode("UNKNOWN SERVICE ACTION A4 **", cmd); break; } return *sam_stat; } uint8_t ssc_spout(struct scsi_cmd *cmd) { declare_ssc_vars; MHVTL_DBG(1, "SECURITY PROTOCOL OUT (%ld) **", (long)dbuf_p->serialNo); dbuf_p->sz = get_unaligned_be32(&cdb[6]); /* Check for '512 increment' bit & multiply sz by 512 if set */ dbuf_p->sz *= (cdb[4] & 0x80) ? 512 : 1; retrieve_CDB_data(cmd->cdev, dbuf_p); return resp_spout(cmd); } uint8_t ssc_spin(struct scsi_cmd *cmd) { MHVTL_DBG(1, "SECURITY PROTOCOL IN (%ld) **", (long)cmd->dbuf_p->serialNo); return resp_spin(cmd); } uint8_t ssc_pr_out(struct scsi_cmd *cmd) { declare_ssc_vars; MHVTL_DBG(1, "PERSISTENT RESERVE OUT (%ld) **", (long)dbuf_p->serialNo); if (lu_priv->I_am_SPC_2_Reserved) { MHVTL_DBG(1, "SPC 2 reserved"); *sam_stat = SAM_STAT_RESERVATION_CONFLICT; return SAM_STAT_RESERVATION_CONFLICT; } dbuf_p->sz = get_unaligned_be32(&cdb[5]); retrieve_CDB_data(cmd->cdev, dbuf_p); return resp_spc_pro(cdb, dbuf_p); } /* * Process the MODE_SELECT command */ uint8_t ssc_mode_select(struct scsi_cmd *cmd) { declare_ssc_vars; uint8_t *bdb = NULL; int page_len = 0; int mode_param_h_sz = 0; int i, j; int count; int save_pages; int page_format; int mselect_6 = 0; int page; int offset; int mode_medium_type; int mode_dev_spec_param; int mode_block_descriptor_len; struct s_sd sd; save_pages = cdb[1] & 0x01; page_format = (cdb[1] & (1 << 4)) ? 1 : 0; switch (cdb[0]) { case MODE_SELECT: mselect_6 = 1; dbuf_p->sz = cdb[4]; mode_param_h_sz = 4; break; case MODE_SELECT_10: dbuf_p->sz = get_unaligned_be16(&cdb[7]); mode_param_h_sz = 8; break; default: dbuf_p->sz = 0; } MHVTL_DBG(1, "MODE SELECT %d (%ld) **", (mselect_6) ? 6 : 10, (long)dbuf_p->serialNo); MHVTL_DBG(1, " Save Pages: %d, Page Format conforms to %s standard", save_pages, (page_format) ? "T10" : "Vendor uniq"); count = retrieve_CDB_data(cmd->cdev, dbuf_p); switch (cmd->scb[0]) { case MODE_SELECT: page_len = buf[5]; break; case MODE_SELECT_10: page_len = get_unaligned_be16(&buf[9]); break; } /* * As per t10.org SPC4r31 (6.9) * * A page format (PF) bit set to zero specifies that all parameters after * the block descriptors are vendor specific. A PF bit set to one specifies * that the MODE SELECT parameters following the header and block * descriptor(s) are structured as pages of related parameters and are * as defined in this standard. */ if (!page_format && page_len) { MHVTL_DBG(1, "PF bit cleared, yet page data supplied. Len: %d", page_len); sd.byte0 = SKSV | CD | BPV | 4; /* bit 4 is invalid */ sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } #ifdef MHVTL_DEBUG if (debug) hex_dump(buf, cmd->dbuf_p->sz); #endif /* T10 spec => MODE DATA LEN is reserved for MODE SELECT */ MHVTL_DBG(3, "count: %d, param header len: %d", count, mode_param_h_sz); switch (mode_param_h_sz) { case 4: /* MODE SELECT 6 */ mode_medium_type = buf[1]; mode_dev_spec_param = buf[2]; mode_block_descriptor_len = buf[3]; bdb = &buf[4]; break; case 8: /* MODE SELECT 10 */ mode_medium_type = buf[2]; mode_dev_spec_param = buf[3]; mode_block_descriptor_len = get_unaligned_be16(&buf[6]); bdb = &buf[8]; break; default: /* Shouldn't be possible */ MHVTL_LOG("Should never see this: line %d", __LINE__); mode_medium_type = 0; mode_dev_spec_param = 0; mode_block_descriptor_len = 0; } i = j = 0; MHVTL_DBG(3, " %02d: %02x %02x %02x %02x" " %02x %02x %02x %02x" " %02x %02x %02x %02x" " %02x %02x %02x %02x", j, buf[i + 0], buf[i + 1], buf[i + 2], buf[i + 3], buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7], buf[i + 8], buf[i + 9], buf[i + 10], buf[i + 11], buf[i + 12], buf[i + 13], buf[i + 14], buf[i + 15]); MHVTL_DBG(3, "Mode Param header: Medium type 0x%02x, " "Device spec param 0x%02x, " "Blk Descr Len 0x%02x, " "Buff mode %d, Speed %d", mode_medium_type, mode_dev_spec_param, mode_block_descriptor_len, (mode_dev_spec_param & 0x70) >> 4, (mode_dev_spec_param & 0x0f)); i = mode_param_h_sz; if (mode_block_descriptor_len) { memcpy(modeBlockDescriptor, bdb, mode_block_descriptor_len); MHVTL_DBG(3, "Descriptor block: density code 0x%02x, " "No. of blocks 0x%02x, " "Block length 0x%02x", buf[i], get_unaligned_be24(&buf[i + 1]), get_unaligned_be24(&buf[i + 5])); } /* * As per t10.org SPC4r31 (6.9) * * A save pages (SP) bit set to zero specifies that the device server * shall perform the specified MODE SELECT operation, and shall not * save any mode pages. If the logical unit implements no distinction * between current and saved mode pages and the SP bit is set to zero, * the command shall be terminated with CHECK CONDITION status, * with the sense key set to ILLEGAL REQUEST, and the additional * sense code set to INVALID FIELD IN CDB. * An SP bit set to one specifies that the device server shall perform * the specified MODE SELECT operation, and shall save to a nonvolatile * vendor specific location all the saveable mode pages including any * sent in the Data-Out Buffer. * Mode pages that are saved are specified by the parameter saveable * (PS) bit that is returned in the first byte of each mode page by * the MODE SENSE command (see 7.5). If the PS bit is set to one in * the MODE SENSE data, then the mode page shall be saveable by * issuing a MODE SELECT command with the SP bit set to one. If the * logical unit does not implement saved mode pages and the SP bit is * set to one, then the command shall be terminated with CHECK CONDITION * status, with the sense key set to ILLEGAL REQUEST, and the additional * sense code set to INVALID FIELD IN CDB. */ if (save_pages) { MHVTL_DBG(1, " Save pages bit set. Not supported"); sd.byte0 = SKSV | CD | BPV | 1; /* bit 1 is invalid */ sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } i += mode_block_descriptor_len; j = 0; while (i < count) { offset = 2; page = buf[i]; page_len = buf[i + 1]; MHVTL_DBG(2, " Page: 0x%02x, Page Len: 0x%02x", page, page_len); if (page_len) { MHVTL_DBG(3, " %02d: %02x %02x %02x %02x" " %02x %02x %02x %02x", j, buf[i + 0], buf[i + 1], buf[i + 2], buf[i + 3], buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7]); } if (page_len > 8) { if (page_len == 0x0e) { /* Common page len */ MHVTL_DBG(3, " %02d: %02x %02x %02x %02x" " %02x %02x", j + 8, buf[i + 8], buf[i + 9], buf[i + 10], buf[i + 11], buf[i + 12], buf[i + 13]); } else { MHVTL_DBG(3, " %02d: %02x %02x %02x %02x" " %02x %02x %02x %02x", j + 8, buf[i + 8], buf[i + 9], buf[i + 10], buf[i + 11], buf[i + 12], buf[i + 13], buf[i + 14], buf[i + 15]); } } if (page_len > 16) { MHVTL_DBG(3, " %02d: %02x %02x %02x %02x" " %02x %02x %02x %02x", j + 16, buf[i + 16], buf[i + 17], buf[i + 18], buf[i + 19], buf[i + 20], buf[i + 21], buf[i + 22], buf[i + 23]); } if (page_len > 24) { MHVTL_DBG(3, " %02d: %02x %02x %02x %02x" " %02x %02x %02x %02x", j + 24, buf[i + 24], buf[i + 25], buf[i + 26], buf[i + 27], buf[i + 28], buf[i + 29], buf[i + 30], buf[i + 31]); } switch (page) { case MODE_DATA_COMPRESSION: if (page_len == 0x0e) set_mode_compression(cmd, &buf[i]); break; case MODE_DEVICE_CONFIGURATION: /* If this is '01' it's a subpage value * i.e. DEVICE CONFIGURATION EXTENSION * If it's 0x0e, it indicates a page length * for MODE DEVICE CONFIGURATION */ if (page_len == 0x01) { if (set_device_configuration_extension(cmd, &buf[i])) return SAM_STAT_CHECK_CONDITION; /* Subpage 1 - override default page length */ page_len = get_unaligned_be16(&buf[i + 2]); offset = 4; } else if (page_len >= 0x0e) { set_device_configuration(cmd, &buf[i]); } else { MHVTL_DBG(2, "Invalid page len: 0x%02x", page_len); sd.byte0 = SKSV; sd.field_pointer = i + 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } break; case MODE_CONTROL: if (page_len == 0x0a) { /* Control mode page - byte[1] is page len */ MHVTL_DBG(3, "Setting Mode Control"); /* Silently accept this worked, but really did not change anything */ } else { /* Otherwise, subpage handling - where page len is byte[2] & byte[3] */ page_len = get_unaligned_be16(&buf[i + 2]); if (buf[1 + i] == 0xf0) { /* Logical Block Protection */ MHVTL_DBG(2, "Setting LBP method: %d, LBP length: %d, LBP_W: %s, LBP_R: %s", buf[4 + i], buf[5 + i], (buf[6 + i] & 0x80) ? "True" : "False", (buf[6 + i] & 0x40) ? "True" : "False"); if (set_lbp(cmd, &buf[i], page_len)) return SAM_STAT_CHECK_CONDITION; } else { MHVTL_DBG(2, "Mode Control - Subpage: 0x%02x not supported", buf[i + 1]); sd.byte0 = SKSV; sd.field_pointer = i; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } } return SAM_STAT_GOOD; break; case MODE_MEDIUM_PARTITION: set_medium_partition(cmd, &buf[i]); return SAM_STAT_GOOD; default: MHVTL_DBG_PRT_CDB(1, cmd); MHVTL_LOG("Mode page 0x%02x not handled", page); sd.byte0 = SKSV; sd.field_pointer = i; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } if (page_len == 0) { /* Something wrong with data structure */ page_len = dbuf_p->sz; MHVTL_LOG("Problem with mode select data structure"); sd.byte0 = SKSV; sd.field_pointer = i + 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } i += page_len + offset; /* Next mode page */ j += page_len; } return SAM_STAT_GOOD; } uint8_t ssc_write_attributes(struct scsi_cmd *cmd) { declare_ssc_vars; int sz; MHVTL_DBG(1, "WRITE ATTRIBUTES (%ld) **", (long)dbuf_p->serialNo); switch (get_tape_load_status()) { case TAPE_UNLOADED: sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; case TAPE_LOADED: dbuf_p->sz = get_unaligned_be32(&cdb[10]); sz = retrieve_CDB_data(cmd->cdev, dbuf_p); MHVTL_DBG(1, " --> Expected to read %d bytes" ", read %d", dbuf_p->sz, sz); if (resp_write_attribute(cmd) > 0) rewriteMAM(sam_stat); break; default: sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } return SAM_STAT_GOOD; } uint8_t ssc_tur(struct scsi_cmd *cmd) { declare_ssc_vars; char str[128]; const char *suffix; size_t prefix_len; /* Large serial numbers + longest suffix ("No, Media format * corrupt") can exceed the original 64-byte buffer. Use a 128-byte * buffer with snprintf for both the prefix and the suffix append. */ prefix_len = snprintf(str, sizeof(str), "TEST UNIT READY (%ld) ** : ", (long)dbuf_p->serialNo); if (prefix_len >= sizeof(str)) prefix_len = sizeof(str) - 1; suffix = NULL; switch (get_tape_load_status()) { case TAPE_UNLOADED: suffix = "No, No tape loaded"; sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); break; case TAPE_LOADING: suffix = "No, Tape loading"; sam_not_ready(E_BECOMING_READY, sam_stat); break; case TAPE_LOADED: if (mam.MediumType == MEDIA_TYPE_CLEAN) { int state; suffix = "No, Cleaning cart loaded"; if (lu_priv->cleaning_media_state) state = *lu_priv->cleaning_media_state; else state = 0; switch (state) { case CLEAN_MOUNT_STAGE1: sam_not_ready(E_CLEANING_CART_INSTALLED, sam_stat); break; case CLEAN_MOUNT_STAGE2: sam_not_ready(E_CAUSE_NOT_REPORTABLE, sam_stat); break; case CLEAN_MOUNT_STAGE3: sam_not_ready(E_INITIALIZING_REQUIRED, sam_stat); break; default: MHVTL_ERR("Unknown cleaning media mount state"); sam_not_ready(E_CLEANING_CART_INSTALLED, sam_stat); break; } } else suffix = "Yes"; break; default: suffix = "No, Media format corrupt"; sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); break; } if (suffix) snprintf(str + prefix_len, sizeof(str) - prefix_len, "%s", suffix); MHVTL_DBG(1, "%s", str); return *sam_stat; } uint8_t ssc_rewind(struct scsi_cmd *cmd) { declare_ssc_vars; int retval; MHVTL_DBG(1, "REWINDING (%ld) **", (long)dbuf_p->serialNo); set_current_state(MHVTL_STATE_REWIND); switch (get_tape_load_status()) { case TAPE_UNLOADED: sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); break; case TAPE_LOADED: retval = rewind_tape(sam_stat); delay_opcode(DELAY_REWIND, lu_priv->delay_rewind); if (retval < 0) { sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); } break; default: sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); break; } return *sam_stat; } uint8_t ssc_read_attributes(struct scsi_cmd *cmd) { declare_ssc_vars; uint8_t service_action = cmd->scb[1] & 0b00011111; struct s_sd sd; MHVTL_DBG(1, "READ ATTRIBUTE (%ld) ** %s", (long)cmd->dbuf_p->serialNo, (service_action == 0) ? "Attribute Values" : (service_action == 1) ? "Attribute List" : (service_action == 2) ? "Logical Volume List" : (service_action == 3) ? "Partition List" : (service_action == 5) ? "Supported Attributes" : "Unknown service action"); switch (get_tape_load_status()) { case TAPE_UNLOADED: MHVTL_DBG(1, "Failed due to \"no media loaded\""); sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; case TAPE_LOADED: break; default: MHVTL_DBG(1, "Failed due to \"media corrupt\""); sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } if (cdb[7] > mam.num_partitions) { sam_illegal_request(E_INVALID_FIELD_IN_CDB, NULL, sam_stat); MHVTL_DBG(1, "Not enough partitions : requested partition %d over %d ", cdb[7], mam.num_partitions); return SAM_STAT_CHECK_CONDITION; } switch (service_action) { case 0x00: /* Attribute values */ case 0x01: /* Attribute list */ dbuf_p->sz = resp_read_attribute(cmd); break; case 0x03: /* Partition list */ put_unaligned_be16(0x0002, &buf[0]); /* Available data */ buf[1] = 0; /* First partition number */ buf[2] = mam.num_partitions; /* Number of partitions available */ dbuf_p->sz = buf[0]; break; default: sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); } return *sam_stat; } uint8_t ssc_read_block_limits(struct scsi_cmd *cmd) { declare_ssc_vars; MHVTL_DBG(1, "READ BLOCK LIMITS (%ld) **", (long)dbuf_p->serialNo); switch (get_tape_load_status()) { case TAPE_LOADED: case TAPE_UNLOADED: dbuf_p->sz = resp_read_block_limits(dbuf_p, lu_priv->bufsize); break; case TAPE_LOADING: sam_not_ready(E_BECOMING_READY, sam_stat); break; default: sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); break; } return *sam_stat; } /* SPC 6.17 - READ MEDIA SERIAL NUMBER */ uint8_t ssc_read_media_sn(struct scsi_cmd *cmd) { declare_ssc_vars; uint32_t alloc_len = get_unaligned_be32(&cdb[6]); struct s_sd sd; MHVTL_DBG(1, "READ MEDIUM SERIAL NO. (%ld) **", (long)dbuf_p->serialNo); if (cdb[1] != 1) { /* Service Action 1 only */ sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } memset_ssc_buf(cmd, alloc_len); switch (get_tape_load_status()) { case TAPE_LOADED: dbuf_p->sz = resp_read_media_serial(lu_priv->mediaSerialNo, buf, sam_stat); break; case TAPE_UNLOADED: sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); break; default: sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); break; } return *sam_stat; } #define READ_POSITION_SERVICE_ACTION 0b000111111 #define READ_POSITION_SHORT_LEN 20 #define READ_POSITION_LONG_LEN 32 #define READ_POSITION_EXTENDED_LEN 32 #define SET_BOP(ptr, blk_number) \ do { \ if ((blk_number) < 2) { \ (ptr)->BOP = 1; \ MHVTL_DBG(3, "Setting Beginning of Partition (BOP)"); \ } \ } while (0) #define SET_BPEW(ptr, offset, lu_priv) \ do { \ if ((lu_priv->pm->drive_supports_prog_early_warning) && ((offset) >= (lu_priv->prog_early_warning_position))) { \ (ptr)->BPEW = 1; \ MHVTL_DBG(3, "Drive supports prog early warning : Setting prog_early_warning of Partition"); \ } else { \ (ptr)->BPEW = 0; \ } \ } while (0) #define SET_EOP(ptr, offset, eop_pos) \ do { \ if ((offset) > (eop_pos)) { \ (ptr)->EOP = 1; \ MHVTL_DBG(3, "Setting End of Partition (EOP)"); \ } else { \ (ptr)->EOP = 0; \ } \ } while (0) #define SET_PERR(ptr, blk_number) \ do { \ if ((blk_number) > 0xFFFFFFFF) { \ (ptr)->PERR = 1; \ MHVTL_DBG(1, "More than supported number of blocks - Setting Logical Block overflow"); \ } \ } while (0) uint8_t ssc_read_position(struct scsi_cmd *cmd) { declare_ssc_vars; uint8_t service_action = cdb[1] & READ_POSITION_SERVICE_ACTION; struct s_sd sd; uint64_t filemarks = 0; struct read_position_information_short *sp; struct read_position_information_long *lp; struct read_position_information_extended *ep; MHVTL_DBG(1, "READ POSITION (%ld) ** %s form", (long)cmd->dbuf_p->serialNo, (service_action == 0) ? "Short" : (service_action == 6) ? "Long" : (service_action == 8) ? "Extended" : "Unknown"); switch (get_tape_load_status()) { case TAPE_LOADED: switch (service_action) { case 0: case 1: sp = (struct read_position_information_short *)&buf[0]; memset(buf, 0, READ_POSITION_SHORT_LEN); /* Clear 'array' */ SET_BOP(sp, c_pos->blk_number); /* Beginning of partition */ sp->LOCU = 0; /* Logical object count unknown - 0: Block count is exact */ sp->BYCU = 1; /* Logical byte count unknown - 1: Byte count is estimate */ sp->LOLU = 0; /* Logical Object Location Unknown - 0: Count is exact */ SET_PERR(sp, c_pos->blk_number); /* logical block address overflow - currently not possible as blk_number is a uint32_t */ if (sp->LOLU == 0) SET_BPEW(sp, current_tape_offset(), lu_priv); SET_EOP(sp, current_tape_offset(), lu_priv->early_warning_position); buf[1] = c_pos->partition_id; put_unaligned_be32(c_pos->blk_number, &buf[4]); /* First Logical Object Location - (current location) */ put_unaligned_be32(c_pos->blk_number, &buf[8]); /* After a write, Logical Object Location of the new write - If buffer empty: == first logical objecct */ // &buf[13] nb logical objects in object buffer // &buf[16] nb bytes in object buffer MHVTL_DBG(1, "Positioned at partition/block %u/%u", c_pos->partition_id, c_pos->blk_number); dbuf_p->sz = READ_POSITION_SHORT_LEN; break; case 6: lp = (struct read_position_information_long *)&buf[0]; memset(buf, 0, READ_POSITION_LONG_LEN); /* Clear 'array' */ SET_BOP(lp, c_pos->blk_number); /* Beginning of partition */ lp->LONU = 0; /* Set 'Logical Object Number Unknown' bit valid (block location info is valid) */ lp->MPU = 0; /* Mark Position Unknown : 0 = num filemarks is known */ SET_BPEW(lp, current_tape_offset(), lu_priv); SET_EOP(lp, current_tape_offset(), lu_priv->early_warning_position); filemarks = count_filemarks(c_pos->blk_number); put_unaligned_be32(c_pos->partition_id, &buf[4]); put_unaligned_be64(c_pos->blk_number, &buf[8]); put_unaligned_be64(filemarks, &buf[16]); // &buf[24] Logical Set Identifier - Obsolete... MHVTL_DBG(1, "Positioned at partition/block %u/%u, %lu filemarks after BOP", c_pos->partition_id, c_pos->blk_number, filemarks); dbuf_p->sz = READ_POSITION_LONG_LEN; break; case 8: ep = (struct read_position_information_extended *)&buf[0]; memset(buf, 0, READ_POSITION_EXTENDED_LEN); /* Clear 'array' */ SET_BOP(ep, c_pos->blk_number); /* Beginning of partition */ ep->BYCU = 1; /* Logical byte count unknown - 1: Byte count is estimate */ ep->LOLU = 0; /* Logical Object Location Unknown - 0: Count is exact */ if (ep->LOLU == 0) SET_BPEW(ep, current_tape_offset(), lu_priv); SET_EOP(ep, current_tape_offset(), lu_priv->early_warning_position); buf[1] = (uint8_t)c_pos->partition_id; put_unaligned_be16(0x1C, &buf[2]); /* Additional length */ // &buf[5] nb logical objects in object buffer put_unaligned_be64(c_pos->blk_number, &buf[8]); /* First Logical Object Location - (current location) */ put_unaligned_be64(c_pos->blk_number, &buf[16]); /* After a write, Logical Object Location of the new write - If buffer empty: == first logical objecct */ // &buf[24] nb bytes in object buffer MHVTL_DBG(1, "Positioned at partition/block %u/%u", c_pos->partition_id, c_pos->blk_number); cmd->dbuf_p->sz = READ_POSITION_EXTENDED_LEN; default: MHVTL_DBG(1, "service_action not supported"); sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); } break; case TAPE_UNLOADED: sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); break; default: sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); break; } return *sam_stat; } uint8_t ssc_release(struct scsi_cmd *cmd) { declare_ssc_vars; MHVTL_DBG(1, "RELEASE (%ld) **", (long)dbuf_p->serialNo); if (!SPR_Reservation_Type && SPR_Reservation_Key) return SAM_STAT_RESERVATION_CONFLICT; lu_priv->I_am_SPC_2_Reserved = 0; return SAM_STAT_GOOD; } uint8_t ssc_report_density_support(struct scsi_cmd *cmd) { declare_ssc_vars; struct s_sd sd; uint8_t media = cdb[1] & 0x01; dbuf_p->sz = 0; MHVTL_DBG(1, "REPORT %s DENSITY SUPPORT (%ld) **", (media) ? "MOUNTED MEDIA" : "DRIVE", (long)dbuf_p->serialNo); if (cdb[1] & 0x02) { /* Don't support Medium Type (yet) */ MHVTL_DBG(1, "Medium Type - not currently supported"); sd.byte0 = SKSV | CD; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (media == 1 && get_tape_load_status() != TAPE_LOADED) { MHVTL_DBG(1, "Media has to be mounted to return media density"); sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; } memset_ssc_buf(cmd, get_unaligned_be16(&cdb[7])); dbuf_p->sz = resp_report_density(lu_priv, media, dbuf_p); return SAM_STAT_GOOD; } uint8_t ssc_reserve(struct scsi_cmd *cmd) { declare_ssc_vars; MHVTL_DBG(1, "RESERVE (%ld) **", (long)dbuf_p->serialNo); if (!SPR_Reservation_Type && !SPR_Reservation_Key) lu_priv->I_am_SPC_2_Reserved = 1; if (!SPR_Reservation_Type && SPR_Reservation_Key) return SAM_STAT_RESERVATION_CONFLICT; return SAM_STAT_GOOD; } uint8_t ssc_erase(struct scsi_cmd *cmd) { declare_ssc_vars; MHVTL_DBG(1, "ERASING (%ld) **", (long)dbuf_p->serialNo); set_current_state(MHVTL_STATE_ERASE); if (!lu_priv->pm->check_restrictions(cmd)) return SAM_STAT_CHECK_CONDITION; if (c_pos->blk_number != 0) { MHVTL_LOG("Not at BOT.. Can't erase unless at BOT"); sam_not_ready(E_INVALID_FIELD_IN_CDB, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (OK_to_write) format_partition(sam_stat); else { MHVTL_LOG("Attempt to erase Write-protected media"); sam_not_ready(E_MEDIUM_OVERWRITE_ATTEMPT, sam_stat); } return *sam_stat; } uint8_t ssc_space_6(struct scsi_cmd *cmd) { declare_ssc_vars; uint32_t count; int32_t icount; uint8_t code; struct s_sd sd; set_current_state(MHVTL_STATE_POSITIONING); count = get_unaligned_be24(&cdb[2]); code = cdb[1] & 0x07; /* 'count' is only a 24-bit value. If the top bit is set, it should be treated as a twos-complement negative number. */ if (cdb[2] >= 0x80) /* MSB of the count field */ icount = -(0xffffff - count + 1); else icount = (int32_t)count; switch (code) { case 0: /* Logical blocks - supported */ case 1: /* filemarks - supported */ MHVTL_DBG(1, "SPACE 6 (%ld) ** %s %d %s%s", (long)dbuf_p->serialNo, (icount >= 0) ? "forward " : "back ", abs(icount), (code == 0) ? "block" : "filemark", (abs(icount) != 1) ? "s" : ""); break; case 3: /* End of Data - supported */ MHVTL_DBG(1, "SPACE 6 (%ld) ** %s ", (long)dbuf_p->serialNo, "to End-of-data"); break; case 2: /* Sequential filemarks currently not supported */ default: /* Unsupported or reserved option */ MHVTL_DBG(1, "SPACE 6 (%ld) ** - Unsupported option %d", (long)dbuf_p->serialNo, code); sd.byte0 = SKSV | CD | BPV | code; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_PARMS, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } if (icount != 0 || code == 3) resp_space(icount, code, sam_stat); return *sam_stat; } uint8_t ssc_space_16(struct scsi_cmd *cmd) { declare_ssc_vars; uint8_t code = cdb[1] & 0x0f; struct s_sd sd; int64_t icount = get_unaligned_be64(&cmd->scb[4]); set_current_state(MHVTL_STATE_POSITIONING); switch (code) { case 0: /* Logical blocks - supported */ case 1: /* filemarks - supported */ MHVTL_DBG(1, "SPACE 16 (%ld) ** %s %d %s%s", (long)dbuf_p->serialNo, (icount >= 0) ? "forward " : "back ", abs(icount), (code == 0) ? "block" : "filemark", (abs(icount) != 1) ? "s" : ""); break; case 3: /* End of Data - supported */ MHVTL_DBG(1, "SPACE 16 (%ld) ** %s ", (long)dbuf_p->serialNo, "to End-of-data"); break; case 2: /* Sequential filemarks currently not supported */ default: /* Unsupported or reserved option */ MHVTL_DBG(1, "SPACE 16 (%ld) ** - Unsupported option %d", (long)dbuf_p->serialNo, code); sd.byte0 = SKSV | CD | BPV | code; sd.field_pointer = 1; sam_illegal_request(E_INVALID_FIELD_IN_PARMS, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } if (icount != 0 || code == 3) resp_space(icount, code, sam_stat); return *sam_stat; } uint8_t ssc_load_unload(struct scsi_cmd *cmd) { declare_ssc_vars; struct s_sd sd; int load_request = cdb[4] & 0x01; int media_state; set_current_state((load_request) ? MHVTL_STATE_LOADING : MHVTL_STATE_UNLOADING); if (cdb[4] & 0x04) { /* EOT bit */ MHVTL_ERR("EOT bit set on load. Not supported"); sd.byte0 = SKSV | CD | BPV | 4; sd.field_pointer = 4; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } MHVTL_DBG(1, "TAPE %s (%ld) **", (load_request) ? "LOADING" : "UNLOADING", (long)cmd->dbuf_p->serialNo); change_partition(0); media_state = rewind_tape(sam_stat); switch (get_tape_load_status()) { case TAPE_UNLOADED: if (load_request) { int load_state; /* * media_state = 0 - Load OK -> Nothing to do * media_state = 1 - Already loaded -> Nothing to do */ switch (media_state) { case 0: /* * lu_priv->barcode indicates there is a tape in mouth * media not mounted, and receive a mount request - attempt * to load media */ if (!lu_priv->barcode) { sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; } load_state = loadTape(lu_priv->barcode, sam_stat); if (load_state == 2) { sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; } else if (load_state == 3) { sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; } break; case 1: break; default: sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } } else { sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; } set_lp11_medium_present(1); break; case TAPE_LOADED: if (!load_request) { /* Send library an update status 'true' */ unloadTape(TRUE, sam_stat); set_lp11_medium_present(0); } else { set_lp11_medium_present(1); } break; default: sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); break; } return *sam_stat; } uint8_t ssc_write_filemarks(struct scsi_cmd *cmd) { declare_ssc_vars; uint32_t count = get_unaligned_be24(&cdb[2]); MHVTL_DBG(1, "WRITE %d FILEMARKS (%ld) **", count, (long)cmd->dbuf_p->serialNo); if (!lu_priv->pm->check_restrictions(cmd)) { /* If restrictions & WORM media at block 0.. OK * Otherwise return CHECK_CONDITION. * check_restrictions() * was nice enough to set correct sense status for us. */ if ((mam.MediumType == MEDIA_TYPE_WORM) && (c_pos->blk_number == 0)) { MHVTL_DBG(1, "Erasing WORM media"); } else return SAM_STAT_CHECK_CONDITION; } write_filemarks(count, sam_stat); if (count) { if (current_tape_offset() >= get_unaligned_be64(&mam.max_capacity)) { mam.remaining_capacity = 0L; MHVTL_DBG(2, "Setting EOM flag"); sam_no_sense(SD_EOM, NO_ADDITIONAL_SENSE, sam_stat); } } return *sam_stat; } uint8_t ssc_pr_in(struct scsi_cmd *cmd) { declare_ssc_vars; MHVTL_DBG(1, "PERSISTENT RESERVE IN (%ld) **", (long)dbuf_p->serialNo); if (lu_priv->I_am_SPC_2_Reserved) return SAM_STAT_RESERVATION_CONFLICT; else return resp_spc_pri(cdb, dbuf_p); } uint8_t ssc_log_sense(struct scsi_cmd *cmd) { declare_ssc_vars; uint8_t page = cdb[2] & 0x3f; struct log_pg_list *l; struct s_sd sd; MHVTL_DBG(1, "LOG SENSE (%ld) ** %s", (long)dbuf_p->serialNo, log_page_desc[page]); if (page == SUPPORTED_LOG_PAGES) { /* Send supported pages */ int i = 4; memset(buf, 0, 4); /* Clear first few (4) bytes */ buf[i++] = 0; /* b[0] is log page '0' (this one) */ list_for_each_entry(l, &lu->log_pg, siblings) { MHVTL_DBG(3, "found page 0x%02x : %s", l->log_page_num, log_page_desc[l->log_page_num]); buf[i] = l->log_page_num; i++; } put_unaligned_be16(i - 4, &buf[2]); /* number of entries stored in b*/ dbuf_p->sz = i; } else { l = lookup_log_pg(&lu->log_pg, page, NO_SUBPAGE); if (!l) goto log_page_not_found; dbuf_p->sz = l->size; buf = memcpy(buf, l->p, l->size); } switch (page) { case SUPPORTED_LOG_PAGES: case WRITE_ERROR_COUNTER: case READ_ERROR_COUNTER: break; case SEQUENTIAL_ACCESS_DEVICE: update_SequentialAccessDevice((struct SequentialAccessDevice_pg *)buf); break; case TEMPERATURE_PAGE: case SELFTEST_RESULTS: case DEVICE_STATUS: break; case VOLUME_STATISTICS: update_VolumeStatistics((struct VolumeStatistics_pg *)buf, lu_priv); break; case TAPE_ALERT: if (get_unaligned_be16(&cdb[7]) > 4) /* Checking Allocation Length */ set_TapeAlert(TA_NONE); else MHVTL_DBG(1, "TapeAlert : Alloc len short -" " Not clearing TapeAlert flags."); break; case TAPE_USAGE: update_TapeUsage((struct TapeUsage_pg *)buf); break; case TAPE_CAPACITY: case DATA_COMPRESSION: break; case PERFORMANCE_CHARACTERISTICS: break; default: MHVTL_DBG(1, "Unknown/Unimplemented log page : 0x%02x", page); goto log_page_not_found; break; } return SAM_STAT_GOOD; log_page_not_found: dbuf_p->sz = 0; sd.byte0 = SKSV | CD; sd.field_pointer = 2; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } uint8_t ssc_recv_diagnostics(struct scsi_cmd *cmd) { declare_ssc_vars; struct s_sd sd; MHVTL_DBG(1, "SSC RECEIVE DIAGNOSTICS (%ld) **", (long)dbuf_p->serialNo); dbuf_p->sz = 0; sd.byte0 = SKSV | CD; sd.field_pointer = 2; sam_illegal_request(E_INVALID_FIELD_IN_CDB, &sd, sam_stat); return SAM_STAT_CHECK_CONDITION; } uint32_t GenerateRSCRC(uint32_t seed, int sz, const uint8_t *buf); uint32_t crc32c(uint32_t seed, const uint8_t *buf, size_t sz); uint8_t ssc_send_diagnostics(struct scsi_cmd *cmd) { declare_ssc_vars; uint32_t computedCRC1; uint32_t computedCRC2; uint32_t computedCRC3; int crc_check_failed = 0; const uint8_t block1[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157}; const uint8_t block2[] = {163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251}; MHVTL_DBG(1, "SSC SEND DIAGNOSTICS (%ld) **", (long)cmd->dbuf_p->serialNo); /* Now check the CRC32C routines pass basic sanity check */ computedCRC1 = crc32c(0, block1, sizeof(block1)); computedCRC2 = crc32c(~computedCRC1, block2, sizeof(block2)); computedCRC3 = crc32c(~crc32c(0, block1, sizeof(block1)), block2, sizeof(block2)); if (computedCRC1 != 0xE8174F48) { MHVTL_ERR("CRC32C #1 error"); crc_check_failed = 1; } if (computedCRC2 != 0x56DAB0A6) { MHVTL_ERR("CRC32C #2 error"); crc_check_failed = 1; } if (computedCRC3 != 0x56DAB0A6) { MHVTL_ERR("CRC32C #3 error"); crc_check_failed = 1; } /* Now check the Reed/Solomon CRC routines pass basic sanity check */ computedCRC1 = GenerateRSCRC(0, sizeof(block1), block1); computedCRC2 = GenerateRSCRC(computedCRC1, sizeof(block2), block2); computedCRC3 = GenerateRSCRC(GenerateRSCRC(0, sizeof(block1), block1), sizeof(block2), block2); if (computedCRC1 != 0x733D4DCA) { MHVTL_ERR("RS-CRC #1 error"); crc_check_failed = 1; } if (computedCRC2 != 0x754ED37E) { MHVTL_ERR("RS-CRC #2 error"); crc_check_failed = 1; } if (computedCRC3 != 0x754ED37E) { MHVTL_ERR("RS-CRC #3 error"); crc_check_failed = 1; } if (crc_check_failed) { sam_hardware_error(E_INTERNAL_TARGET_FAILURE, sam_stat); return SAM_STAT_CHECK_CONDITION; } return SAM_STAT_GOOD; } uint8_t ssc_set_capacity(struct scsi_cmd *cmd) { declare_ssc_vars; /* TODO : implement medium_for_use_proportion_value */ /* uint16_t medium_for_use_proportion_value = get_unaligned_be16(&cmd->scb[3]); */ if (c_pos->blk_number != 0) { /* not at Beginnning Of Partition (BOP) */ sam_illegal_request(E_POSITION_PAST_BOM, NULL, sam_stat); return SAM_STAT_CHECK_CONDITION; } /* Check that there is a piece of media loaded.. */ switch (get_tape_load_status(lu_priv)) { case TAPE_LOADED: break; case TAPE_LOADING: sam_not_ready(E_BECOMING_READY, sam_stat); return SAM_STAT_CHECK_CONDITION; case TAPE_UNLOADED: sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; default: sam_not_ready(E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; } mam.num_partitions = 1; format_tape(sam_stat); return SAM_STAT_GOOD; } ================================================ FILE: usr/utils/README.LZO ================================================ ============================================================================ miniLZO -- mini subset of the LZO real-time data compression library ============================================================================ Author : Markus Franz Xaver Johannes Oberhumer http://www.oberhumer.com/opensource/lzo/ Version : 2.09 Date : 04 Feb 2015 I've created miniLZO for projects where it is inconvenient to include (or require) the full LZO source code just because you want to add a little bit of data compression to your application. miniLZO implements the LZO1X-1 compressor and both the standard and safe LZO1X decompressor. Apart from fast compression it also useful for situations where you want to use pre-compressed data files (which must have been compressed with LZO1X-999). miniLZO consists of one C source file and three header files: minilzo.c minilzo.h, lzoconf.h, lzodefs.h To use miniLZO just copy these files into your source directory, add minilzo.c to your Makefile and #include minilzo.h from your program. Note: you also must distribute this file ('README.LZO') with your project. minilzo.o compiles to about 6 KiB (using gcc or Visual C on an i386), and the sources are about 30 KiB when packed with zip - so there's no more excuse that your application doesn't support data compression :-) For more information, documentation, example programs and other support files (like Makefiles and build scripts) please download the full LZO package from http://www.oberhumer.com/opensource/lzo/ Have fun, Markus P.S. minilzo.c is generated automatically from the LZO sources and therefore functionality is completely identical Appendix A: building miniLZO ---------------------------- miniLZO is written such a way that it should compile and run out-of-the-box on most machines. If you are running on a very unusual architecture and lzo_init() fails then you should first recompile with '-DLZO_DEBUG' to see what causes the failure. The most probable case is something like 'sizeof(void *) != sizeof(size_t)'. After identifying the problem you can compile by adding some defines like '-DSIZEOF_VOID_P=8' to your Makefile. The best solution is (of course) using Autoconf - if your project uses Autoconf anyway just add '-DMINILZO_HAVE_CONFIG_H' to your compiler flags when compiling minilzo.c. See the LZO distribution for an example how to set up configure.ac. Appendix B: list of public functions available in miniLZO --------------------------------------------------------- Library initialization lzo_init() Compression lzo1x_1_compress() Decompression lzo1x_decompress() lzo1x_decompress_safe() Checksum functions lzo_adler32() Version functions lzo_version() lzo_version_string() lzo_version_date() Portable (but slow) string functions lzo_memcmp() lzo_memcpy() lzo_memmove() lzo_memset() Appendix C: suggested macros for 'configure.ac' when using Autoconf ------------------------------------------------------------------- Checks for typedefs and structures AC_CHECK_TYPE(ptrdiff_t,long) AC_TYPE_SIZE_T AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) AC_CHECK_SIZEOF(__int64) AC_CHECK_SIZEOF(void *) AC_CHECK_SIZEOF(size_t) AC_CHECK_SIZEOF(ptrdiff_t) Checks for compiler characteristics AC_C_CONST Checks for library functions AC_CHECK_FUNCS(memcmp memcpy memmove memset) Appendix D: Copyright --------------------- LZO and miniLZO are Copyright (C) 1996-2015 Markus Franz Xaver Oberhumer All Rights Reserved. LZO and miniLZO are distributed under the terms of the GNU General Public License (GPL). See the file COPYING. Special licenses for commercial and other applications which are not willing to accept the GNU General Public License are available by contacting the author. ================================================ FILE: usr/utils/crc32c.c ================================================ /* MIT (BSD) license - see LICENSE file for details */ /* crc32c.c -- compute CRC-32C using the Intel crc32 instruction * Copyright (C) 2013, 2015 Mark Adler * Version 1.3 31 Dec 2015 Mark Adler */ /* This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Mark Adler madler@alumni.caltech.edu */ /* Use hardware CRC instruction on Intel SSE 4.2 processors. This computes a CRC-32C, *not* the CRC-32 used by Ethernet and zip, gzip, etc. A software version is provided as a fall-back, as well as for speed comparisons. */ /* Version history: 1.0 10 Feb 2013 First version 1.1 1 Aug 2013 Correct comments on why three crc instructions in parallel 1.2 1 Nov 2015 Add const qualifier to avoid compiler warning Load entire input into memory (test code) Argument gives number of times to repeat (test code) Argument < 0 forces software implementation (test code) 1.3 31 Dec 2015 Check for Intel architecture using compiler macro Support big-endian processors in software calculation Add header for external use */ #include "ccan/crc32c/crc32c.h" #include static uint32_t crc32c_sw(uint32_t crc, void const *buf, size_t len); /* CRC-32C (iSCSI) polynomial in reversed bit order. */ #define POLY 0x82f63b78 #ifdef __x86_64__ /* Hardware CRC-32C for Intel and compatible processors. */ /* Multiply a matrix times a vector over the Galois field of two elements, GF(2). Each element is a bit in an unsigned integer. mat must have at least as many entries as the power of two for most significant one bit in vec. */ static inline uint32_t gf2_matrix_times(uint32_t *mat, uint32_t vec) { uint32_t sum = 0; while (vec) { if (vec & 1) sum ^= *mat; vec >>= 1; mat++; } return sum; } /* Multiply a matrix by itself over GF(2). Both mat and square must have 32 rows. */ static inline void gf2_matrix_square(uint32_t *square, uint32_t *mat) { for (unsigned n = 0; n < 32; n++) square[n] = gf2_matrix_times(mat, mat[n]); } /* Construct an operator to apply len zeros to a crc. len must be a power of two. If len is not a power of two, then the result is the same as for the largest power of two less than len. The result for len == 0 is the same as for len == 1. A version of this routine could be easily written for any len, but that is not needed for this application. */ static void crc32c_zeros_op(uint32_t *even, size_t len) { uint32_t odd[32]; /* odd-power-of-two zeros operator */ /* put operator for one zero bit in odd */ odd[0] = POLY; /* CRC-32C polynomial */ uint32_t row = 1; for (unsigned n = 1; n < 32; n++) { odd[n] = row; row <<= 1; } /* put operator for two zero bits in even */ gf2_matrix_square(even, odd); /* put operator for four zero bits in odd */ gf2_matrix_square(odd, even); /* first square will put the operator for one zero byte (eight zero bits), in even -- next square puts operator for two zero bytes in odd, and so on, until len has been rotated down to zero */ do { gf2_matrix_square(even, odd); len >>= 1; if (len == 0) return; gf2_matrix_square(odd, even); len >>= 1; } while (len); /* answer ended up in odd -- copy to even */ for (unsigned n = 0; n < 32; n++) even[n] = odd[n]; } /* Take a length and build four lookup tables for applying the zeros operator for that length, byte-by-byte on the operand. */ static void crc32c_zeros(uint32_t zeros[][256], size_t len) { uint32_t op[32]; crc32c_zeros_op(op, len); for (unsigned n = 0; n < 256; n++) { zeros[0][n] = gf2_matrix_times(op, n); zeros[1][n] = gf2_matrix_times(op, n << 8); zeros[2][n] = gf2_matrix_times(op, n << 16); zeros[3][n] = gf2_matrix_times(op, n << 24); } } /* Apply the zeros operator table to crc. */ static inline uint32_t crc32c_shift(uint32_t zeros[][256], uint32_t crc) { return zeros[0][crc & 0xff] ^ zeros[1][(crc >> 8) & 0xff] ^ zeros[2][(crc >> 16) & 0xff] ^ zeros[3][crc >> 24]; } /* Block sizes for three-way parallel crc computation. LONG and SHORT must both be powers of two. The associated string constants must be set accordingly, for use in constructing the assembler instructions. */ #define LONG 8192 #define LONGx1 "8192" #define LONGx2 "16384" #define SHORT 256 #define SHORTx1 "256" #define SHORTx2 "512" /* Tables for hardware crc that shift a crc by LONG and SHORT zeros. */ static bool crc32c_once_hw; static uint32_t crc32c_long[4][256]; static uint32_t crc32c_short[4][256]; /* Initialize tables for shifting crcs. */ static void crc32c_init_hw(void) { crc32c_once_hw = true; crc32c_zeros(crc32c_long, LONG); crc32c_zeros(crc32c_short, SHORT); } /* Compute CRC-32C using the Intel hardware instruction. */ static uint32_t crc32c_hw(uint32_t crc, void const *buf, size_t len) { /* populate shift tables the first time through */ if (!crc32c_once_hw) crc32c_init_hw(); /* pre-process the crc */ crc = ~crc; uint64_t crc0 = crc; /* 64-bits for crc32q instruction */ /* compute the crc for up to seven leading bytes to bring the data pointer to an eight-byte boundary */ unsigned char const *next = buf; while (len && ((uintptr_t)next & 7) != 0) { __asm__("crc32b\t" "(%1), %0" : "=r"(crc0) : "r"(next), "0"(crc0)); next++; len--; } /* compute the crc on sets of LONG*3 bytes, executing three independent crc instructions, each on LONG bytes -- this is optimized for the Nehalem, Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a throughput of one crc per cycle, but a latency of three cycles */ while (len >= LONG * 3) { uint64_t crc1 = 0; uint64_t crc2 = 0; unsigned char const *const end = next + LONG; do { __asm__("crc32q\t" "(%3), %0\n\t" "crc32q\t" LONGx1 "(%3), %1\n\t" "crc32q\t" LONGx2 "(%3), %2" : "=r"(crc0), "=r"(crc1), "=r"(crc2) : "r"(next), "0"(crc0), "1"(crc1), "2"(crc2)); next += 8; } while (next < end); crc0 = crc32c_shift(crc32c_long, crc0) ^ crc1; crc0 = crc32c_shift(crc32c_long, crc0) ^ crc2; next += LONG * 2; len -= LONG * 3; } /* do the same thing, but now on SHORT*3 blocks for the remaining data less than a LONG*3 block */ while (len >= SHORT * 3) { uint64_t crc1 = 0; uint64_t crc2 = 0; unsigned char const *const end = next + SHORT; do { __asm__("crc32q\t" "(%3), %0\n\t" "crc32q\t" SHORTx1 "(%3), %1\n\t" "crc32q\t" SHORTx2 "(%3), %2" : "=r"(crc0), "=r"(crc1), "=r"(crc2) : "r"(next), "0"(crc0), "1"(crc1), "2"(crc2)); next += 8; } while (next < end); crc0 = crc32c_shift(crc32c_short, crc0) ^ crc1; crc0 = crc32c_shift(crc32c_short, crc0) ^ crc2; next += SHORT * 2; len -= SHORT * 3; } /* compute the crc on the remaining eight-byte units less than a SHORT*3 block */ { unsigned char const *const end = next + (len - (len & 7)); while (next < end) { __asm__("crc32q\t" "(%1), %0" : "=r"(crc0) : "r"(next), "0"(crc0)); next += 8; } len &= 7; } /* compute the crc for up to seven trailing bytes */ while (len) { __asm__("crc32b\t" "(%1), %0" : "=r"(crc0) : "r"(next), "0"(crc0)); next++; len--; } /* return a post-processed crc */ return ~crc0; } /* Compute a CRC-32C. If the crc32 instruction is available, use the hardware version. Otherwise, use the software version. */ uint32_t crc32c(uint32_t crc, void const *buf, size_t len) { return __builtin_cpu_supports("sse4.2") ? crc32c_hw(crc, buf, len) : crc32c_sw(crc, buf, len); } #else /* !__x86_64__ */ uint32_t crc32c(uint32_t crc, void const *buf, size_t len) { return crc32c_sw(crc, buf, len); } #endif /* Construct table for software CRC-32C little-endian calculation. */ static bool crc32c_once_little; static uint32_t crc32c_table_little[8][256]; static void crc32c_init_sw_little(void) { for (unsigned n = 0; n < 256; n++) { uint32_t crc = n; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc32c_table_little[0][n] = crc; } for (unsigned n = 0; n < 256; n++) { uint32_t crc = crc32c_table_little[0][n]; for (unsigned k = 1; k < 8; k++) { crc = crc32c_table_little[0][crc & 0xff] ^ (crc >> 8); crc32c_table_little[k][n] = crc; } } crc32c_once_little = true; } /* Compute a CRC-32C in software assuming a little-endian architecture, constructing the required table if that hasn't already been done. */ static uint32_t crc32c_sw_little(uint32_t crc, void const *buf, size_t len) { unsigned char const *next = buf; if (!crc32c_once_little) crc32c_init_sw_little(); crc = ~crc; while (len && ((uintptr_t)next & 7) != 0) { crc = crc32c_table_little[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); len--; } if (len >= 8) { uint64_t crcw = crc; do { crcw ^= *(uint64_t const *)next; crcw = crc32c_table_little[7][crcw & 0xff] ^ crc32c_table_little[6][(crcw >> 8) & 0xff] ^ crc32c_table_little[5][(crcw >> 16) & 0xff] ^ crc32c_table_little[4][(crcw >> 24) & 0xff] ^ crc32c_table_little[3][(crcw >> 32) & 0xff] ^ crc32c_table_little[2][(crcw >> 40) & 0xff] ^ crc32c_table_little[1][(crcw >> 48) & 0xff] ^ crc32c_table_little[0][crcw >> 56]; next += 8; len -= 8; } while (len >= 8); crc = crcw; } while (len) { crc = crc32c_table_little[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); len--; } return ~crc; } /* Swap the bytes in a uint64_t. (Only for big-endian.) */ #if defined(__has_builtin) || (defined(__GNUC__) && \ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) #define swap __builtin_bswap64 #else static inline uint64_t swap(uint64_t x) { x = ((x << 8) & 0xff00ff00ff00ff00) | ((x >> 8) & 0xff00ff00ff00ff); x = ((x << 16) & 0xffff0000ffff0000) | ((x >> 16) & 0xffff0000ffff); return (x << 32) | (x >> 32); } #endif /* Construct tables for software CRC-32C big-endian calculation. */ static bool crc32c_once_big; static uint32_t crc32c_table_big_byte[256]; static uint64_t crc32c_table_big[8][256]; static void crc32c_init_sw_big(void) { for (unsigned n = 0; n < 256; n++) { uint32_t crc = n; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc32c_table_big_byte[n] = crc; } for (unsigned n = 0; n < 256; n++) { uint32_t crc = crc32c_table_big_byte[n]; crc32c_table_big[0][n] = swap(crc); for (unsigned k = 1; k < 8; k++) { crc = crc32c_table_big_byte[crc & 0xff] ^ (crc >> 8); crc32c_table_big[k][n] = swap(crc); } } crc32c_once_big = true; } /* Compute a CRC-32C in software assuming a big-endian architecture, constructing the required tables if that hasn't already been done. */ static uint32_t crc32c_sw_big(uint32_t crc, void const *buf, size_t len) { unsigned char const *next = buf; if (!crc32c_once_big) crc32c_init_sw_big(); crc = ~crc; while (len && ((uintptr_t)next & 7) != 0) { crc = crc32c_table_big_byte[(crc ^ *next++) & 0xff] ^ (crc >> 8); len--; } if (len >= 8) { uint64_t crcw = swap(crc); do { crcw ^= *(uint64_t const *)next; crcw = crc32c_table_big[0][crcw & 0xff] ^ crc32c_table_big[1][(crcw >> 8) & 0xff] ^ crc32c_table_big[2][(crcw >> 16) & 0xff] ^ crc32c_table_big[3][(crcw >> 24) & 0xff] ^ crc32c_table_big[4][(crcw >> 32) & 0xff] ^ crc32c_table_big[5][(crcw >> 40) & 0xff] ^ crc32c_table_big[6][(crcw >> 48) & 0xff] ^ crc32c_table_big[7][(crcw >> 56)]; next += 8; len -= 8; } while (len >= 8); crc = swap(crcw); } while (len) { crc = crc32c_table_big_byte[(crc ^ *next++) & 0xff] ^ (crc >> 8); len--; } return ~crc; } /* Table-driven software CRC-32C. This is about 15 times slower than using the hardware instructions. Determine the endianess of the processor and proceed accordingly. Ideally the endianess will be determined at compile time, in which case the unused functions and tables for the other endianess will be removed by the optimizer. If not, then the proper routines and tables will be used, even if the endianess is changed mid-stream. (Yes, there are processors that permit that -- go figure.) */ static uint32_t crc32c_sw(uint32_t crc, void const *buf, size_t len) { static int const little = 1; if (*(char const *)&little) return crc32c_sw_little(crc, buf, len); else return crc32c_sw_big(crc, buf, len); } ================================================ FILE: usr/utils/mhvtl_update.c ================================================ /* * Functions to update the tape format and mam format to new versions * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #define _FILE_OFFSET_BITS 64 #include #include #include #include "logging.h" #include "vtlcart.h" #include "mhvtl_update.h" #include "vtllib.h" struct MAM_tapeFmtV3 { uint32_t tape_fmt_version; uint32_t mam_fmt_version; uint64_t remaining_capacity; uint64_t max_capacity; uint64_t TapeAlert; uint64_t LoadCount; uint64_t MAMSpaceRemaining; uint8_t AssigningOrganization_1[8]; uint8_t InitializationCount[2]; uint8_t DevMakeSerialLastLoad[40]; uint8_t DevMakeSerialLastLoad1[40]; uint8_t DevMakeSerialLastLoad2[40]; uint8_t DevMakeSerialLastLoad3[40]; uint64_t WrittenInMediumLife; uint64_t ReadInMediumLife; uint64_t WrittenInLastLoad; uint64_t ReadInLastLoad; uint8_t MediumManufacturer[8]; uint8_t MediumSerialNumber[32]; uint32_t MediumLength; uint32_t MediumWidth; uint8_t AssigningOrganization_2[8]; uint8_t MediumManufactureDate[12]; uint8_t FormattedDensityCode; uint8_t MediumDensityCode; uint8_t MediumType; /* 0 -> Data, 1 -> WORM, 6 -> Clean */ uint8_t MediaType; /* LTO1, LTO2, AIT etc (Media_Type_list) */ uint64_t MAMCapacity; uint16_t MediumTypeInformation; /* If Clean, max mount */ uint8_t ApplicationVendor[8]; uint8_t ApplicationName[32]; uint8_t ApplicationVersion[8]; uint8_t UserMediumTextLabel[160]; uint8_t DateTimeLastWritten[12]; uint8_t LocalizationIdentifier; uint8_t Barcode[32]; uint8_t OwningHostTextualName[80]; uint8_t MediaPool[160]; uint8_t record_dirty; /* 0 = Record clean, non-zero umount failed. */ uint16_t Flags; struct uniq_media_info_tapeFmtV3 { uint32_t bits_per_mm; uint16_t tracks; char density_name[8]; char description[32]; } media_info; uint8_t max_partitions; uint8_t num_partitions; /* Pad to keep MAM to 1024 bytes */ uint8_t pad[1024 - 878]; } __attribute__((packed)); /* * Assuming mam.tape_fmt_version == 3, * extract mam from "meta" and create separate "mam" file * Sets mam.tape_fmt_version to 4 * * Returns: * == 0 -> Successfully extracted mam * == 1 -> Failed to extract mam from meta file * == 2 -> could not find meta file : format corrupt */ int try_extract_mam(char *currentPCL) { struct MAM_tapeFmtV3 mam_v3; char meta_path[1024]; char mam_path[1024]; char tmp_path[1024]; int metafile = -1; int mamfile = -1; int tmpfile = -1; int rc = 1; /* default: failed to extract */ snprintf(meta_path, sizeof(meta_path), "%s/meta", currentPCL); snprintf(mam_path, sizeof(mam_path), "%s/mam", currentPCL); snprintf(tmp_path, sizeof(tmp_path), "%s/meta.new", currentPCL); metafile = open(meta_path, O_RDWR | O_LARGEFILE); if (metafile < 0) { MHVTL_ERR("open of file %s failed: %s", meta_path, strerror(errno)); return 2; } if (read(metafile, &mam_v3, sizeof(struct MAM_tapeFmtV3)) != sizeof(struct MAM_tapeFmtV3)) { MHVTL_ERR("Error reading pcl %s MAM from meta file: %s", currentPCL, strerror(errno)); goto cleanup; } /* Checking Tape Format Version */ if (mam_v3.tape_fmt_version != 3) { MHVTL_ERR("Error : Tape Format Version : %d , expected 3.\ \nCannot handle conversion of %s tape format to version 4", mam_v3.tape_fmt_version, currentPCL); goto cleanup; } /* create mam file */ mamfile = open(mam_path, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (mamfile < 0) { MHVTL_ERR("Failed to create file %s: %s", mam_path, strerror(errno)); goto cleanup; } /* Update Tape Format Version and write mam in "mam" file */ mam_v3.tape_fmt_version = 4; if (write(mamfile, &mam_v3, sizeof(struct MAM_tapeFmtV3)) != sizeof(struct MAM_tapeFmtV3)) { MHVTL_ERR("Failed to initialize file %s: %s", mam_path, strerror(errno)); goto cleanup; } /* Rewrite meta without mam : writing content to meta.tmp, then renaming to meta */ { size_t remaining_meta; char buf[4096]; remaining_meta = lseek(metafile, 0, SEEK_END) - sizeof(struct MAM_tapeFmtV3); if (remaining_meta < 0) { MHVTL_ERR("Error : lseek failed on %s: %s", meta_path, strerror(errno)); goto cleanup; } /* Positioning at the beginning of the meta data */ if (lseek(metafile, sizeof(struct MAM_tapeFmtV3), SEEK_SET) < 0) { MHVTL_ERR("Error : lseek failed on %s: %s", meta_path, strerror(errno)); goto cleanup; } tmpfile = open(tmp_path, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (tmpfile < 0) { MHVTL_ERR("Failed to create temp meta file %s: %s", tmp_path, strerror(errno)); goto cleanup; } while (remaining_meta > 0) { ssize_t nread = read(metafile, buf, remaining_meta > sizeof(buf) ? sizeof(buf) : remaining_meta); if (nread <= 0) { MHVTL_ERR("Error reading meta file %s: %s", meta_path, strerror(errno)); goto cleanup; } if (write(tmpfile, buf, nread) != nread) { MHVTL_ERR("Error writing temp meta file %s: %s", tmp_path, strerror(errno)); goto cleanup; } remaining_meta -= nread; } if (fsync(tmpfile) < 0) { MHVTL_ERR("Error doing fsync of temp meta file %s: %s", tmp_path, strerror(errno)); goto cleanup; } if (rename(tmp_path, meta_path) < 0) { MHVTL_ERR("rename %s -> %s failed: %s", tmp_path, meta_path, strerror(errno)); goto cleanup; } } rc = 0; /* success */ cleanup: if (mamfile >= 0) close(mamfile); if (metafile >= 0) close(metafile); if (tmpfile >= 0) close(tmpfile); if (rc != 0) { unlink(mam_path); unlink(tmp_path); } return rc; } /* * Assuming mam.mam_fmt_version == 3, * Update mam from mam_fmt_version 3 to 4 * Splitting mam into mam/mhvtl_data : updating tape_fmt_version from version 4 to 5 * turning mam into an auto-descriptive format * * Returns: * == 0 -> Successfully updated mam * == 1 -> Failed to update mam * == 2 -> could not find mam file : format corrupt */ int try_update_mam(char *currentPCL) { struct MAM_tapeFmtV3 mam_v3; char mam_path[1024]; char mhvtl_data_path[1024]; char tmp_path[1024]; int mamfile = -1; int mhvtlfile = -1; int tmpfile = -1; ssize_t nread; int rc = 1; snprintf(mam_path, sizeof(mam_path), "%s/mam", currentPCL); snprintf(mhvtl_data_path, sizeof(mhvtl_data_path), "%s/mhvtl_data", currentPCL); snprintf(tmp_path, sizeof(tmp_path), "%s/mam.tmp", currentPCL); mamfile = open(mam_path, O_RDWR | O_LARGEFILE); if (mamfile < 0) { MHVTL_ERR("open of file %s failed: %s", mam_path, strerror(errno)); return 2; } nread = read(mamfile, &mam_v3, sizeof(struct MAM_tapeFmtV3)); if (nread != sizeof(struct MAM_tapeFmtV3)) { MHVTL_ERR("Error reading pcl %s MAM from mam file: %s", currentPCL, strerror(errno)); goto cleanup; } /* Checking MAM Format Version */ if (mam_v3.mam_fmt_version != 3) { MHVTL_ERR("Error : MAM Format Version : %d , expected 3.\ \nCannot handle conversion of %s MAM format to version 4", mam_v3.mam_fmt_version, currentPCL); goto cleanup; } mam.mam_fmt_version = MAM_VERSION; mam.tape_fmt_version = 5; /* Copying attributes to new format */ mam.remaining_capacity = mam_v3.remaining_capacity; mam.max_capacity = mam_v3.max_capacity; mam.TapeAlert = mam_v3.TapeAlert; mam.LoadCount = mam_v3.LoadCount; mam.MAMSpaceRemaining = mam_v3.MAMSpaceRemaining; memcpy(mam.AssigningOrganization_1, mam_v3.AssigningOrganization_1, sizeof(mam.AssigningOrganization_1)); memcpy(mam.InitializationCount, mam_v3.InitializationCount, sizeof(mam.InitializationCount)); memcpy(mam.DevMakeSerialLastLoad, mam_v3.DevMakeSerialLastLoad, sizeof(mam.DevMakeSerialLastLoad)); memcpy(mam.DevMakeSerialLastLoad1, mam_v3.DevMakeSerialLastLoad1, sizeof(mam.DevMakeSerialLastLoad1)); memcpy(mam.DevMakeSerialLastLoad2, mam_v3.DevMakeSerialLastLoad2, sizeof(mam.DevMakeSerialLastLoad2)); memcpy(mam.DevMakeSerialLastLoad3, mam_v3.DevMakeSerialLastLoad3, sizeof(mam.DevMakeSerialLastLoad3)); mam.WrittenInMediumLife = mam_v3.WrittenInMediumLife; mam.ReadInMediumLife = mam_v3.ReadInMediumLife; mam.WrittenInLastLoad = mam_v3.WrittenInLastLoad; mam.ReadInLastLoad = mam_v3.ReadInLastLoad; memcpy(mam.MediumManufacturer, mam_v3.MediumManufacturer, sizeof(mam.MediumManufacturer)); memcpy(mam.MediumSerialNumber, mam_v3.MediumSerialNumber, sizeof(mam.MediumSerialNumber)); mam.MediumLength = mam_v3.MediumLength; mam.MediumWidth = mam_v3.MediumWidth; memcpy(mam.AssigningOrganization_2, mam_v3.AssigningOrganization_2, sizeof(mam.AssigningOrganization_2)); memcpy(mam.MediumManufactureDate, mam_v3.MediumManufactureDate, sizeof(mam.MediumManufactureDate)); mam.FormattedDensityCode = mam_v3.FormattedDensityCode; mam.MediumDensityCode = mam_v3.MediumDensityCode; mam.MediumType = mam_v3.MediumType; mam.MediaType = mam_v3.MediaType; mam.MAMCapacity = mam_v3.MAMCapacity; mam.MediumTypeInformation = mam_v3.MediumTypeInformation; memcpy(mam.ApplicationVendor, mam_v3.ApplicationVendor, sizeof(mam.ApplicationVendor)); memcpy(mam.ApplicationName, mam_v3.ApplicationName, sizeof(mam.ApplicationName)); memcpy(mam.ApplicationVersion, mam_v3.ApplicationVersion, sizeof(mam.ApplicationVersion)); memcpy(mam.UserMediumTextLabel, mam_v3.UserMediumTextLabel, sizeof(mam.UserMediumTextLabel)); memcpy(mam.DateTimeLastWritten, mam_v3.DateTimeLastWritten, sizeof(mam.DateTimeLastWritten)); mam.LocalizationIdentifier = mam_v3.LocalizationIdentifier; memcpy(mam.Barcode, mam_v3.Barcode, sizeof(mam.Barcode)); memcpy(mam.OwningHostTextualName, mam_v3.OwningHostTextualName, sizeof(mam.OwningHostTextualName)); memcpy(mam.MediaPool, mam_v3.MediaPool, sizeof(mam.MediaPool)); mam.record_dirty = mam_v3.record_dirty; mam.Flags = mam_v3.Flags; mam.media_info.bits_per_mm = mam_v3.media_info.bits_per_mm; mam.media_info.tracks = mam_v3.media_info.tracks; memcpy(mam.media_info.density_name, mam_v3.media_info.density_name, sizeof(mam.media_info.density_name)); memcpy(mam.media_info.description, mam_v3.media_info.description, sizeof(mam.media_info.description)); mam.max_partitions = mam_v3.max_partitions; mam.num_partitions = mam_v3.num_partitions; /* create mhvtl_data file */ mhvtlfile = open(mhvtl_data_path, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (mhvtlfile < 0) { MHVTL_ERR("Failed to create mhvtl_data file %s: %s", mhvtl_data_path, strerror(errno)); goto cleanup; } /* Updating mam : * - writing content to mam.tmp, then renaming to mam * - filling mhvtl_data */ { tmpfile = open(tmp_path, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (tmpfile < 0) { MHVTL_ERR("Failed to create temp mam file %s: %s", tmp_path, strerror(errno)); goto cleanup; } write_mam(tmpfile, mhvtlfile); if (fsync(tmpfile) < 0) { MHVTL_ERR("Error doing fsync of temp mhvtl_data file %s: %s", mhvtl_data_path, strerror(errno)); goto cleanup; } if (fsync(tmpfile) < 0) { MHVTL_ERR("Error doing fsync of temp mam file %s: %s", tmp_path, strerror(errno)); goto cleanup; } if (rename(tmp_path, mam_path) < 0) { MHVTL_ERR("rename %s -> %s failed: %s", tmp_path, mam_path, strerror(errno)); goto cleanup; } } rc = 0; /* success */ cleanup: if (mamfile >= 0) close(mamfile); if (mhvtlfile >= 0) close(mhvtlfile); if (tmpfile >= 0) close(tmpfile); if (rc != 0) { unlink(tmp_path); unlink(mhvtl_data_path); } return rc; } /* * Assuming mam.tape_fmt_version == 5, * Update from tape_fmt_version 5 to 6 * Renaming {data, indx, meta} to {data, indx, meta}.0 as they correspond * to partition 0 instead of the whole tape * * Returns: * == 0 -> Successfully updated tape * == 1 -> Failed to update tape */ int try_update_tape(char *currentPCL) { char oldpath[1024]; char path[1024 + 2]; const char *file_name[] = {"data", "indx", "meta"}; /* Checking Tape Format Version */ if (mam.tape_fmt_version != 5) { MHVTL_ERR("Error : Tape Format Version : %d , expected 5.\ \nCannot handle conversion of %s tape format to version 6", mam.tape_fmt_version, currentPCL); return 1; } /* renaming to .0 */ for (int k = 0; k < 3; k++) { snprintf(oldpath, sizeof(oldpath), "%s/%s", currentPCL, file_name[k]); snprintf(path, sizeof(path), "%s.0", oldpath); if (rename(oldpath, path) < 0) { MHVTL_ERR("rename %s -> %s failed: %s", oldpath, path, strerror(errno)); return 1; } } mam.tape_fmt_version = 6; return 0; } ================================================ FILE: usr/utils/minilzo.c ================================================ /* minilzo.c -- mini subset of the LZO real-time data compression library This file is part of the LZO real-time data compression library. Copyright (C) 1996-2015 Markus Franz Xaver Johannes Oberhumer All Rights Reserved. The LZO library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The LZO 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 General Public License for more details. You should have received a copy of the GNU General Public License along with the LZO library; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Markus F.X.J. Oberhumer http://www.oberhumer.com/opensource/lzo/ */ /* * NOTE: * the full LZO package can be found at * http://www.oberhumer.com/opensource/lzo/ */ #define __LZO_IN_MINILZO 1 #if defined(LZO_CFG_FREESTANDING) # undef MINILZO_HAVE_CONFIG_H # define LZO_LIBC_FREESTANDING 1 # define LZO_OS_FREESTANDING 1 #endif #ifdef MINILZO_HAVE_CONFIG_H # include #endif #include #include #if defined(MINILZO_CFG_USE_INTERNAL_LZODEFS) #ifndef __LZODEFS_H_INCLUDED #define __LZODEFS_H_INCLUDED 1 #if defined(__CYGWIN32__) && !defined(__CYGWIN__) # define __CYGWIN__ __CYGWIN32__ #endif #if 1 && defined(__INTERIX) && defined(__GNUC__) && !defined(_ALL_SOURCE) # define _ALL_SOURCE 1 #endif #if defined(__mips__) && defined(__R5900__) # if !defined(__LONG_MAX__) # define __LONG_MAX__ 9223372036854775807L # endif #endif #if !defined(LZO_CFG_NO_DISABLE_WUNDEF) #if defined(__ARMCC_VERSION) # pragma diag_suppress 193 #elif defined(__clang__) && defined(__clang_minor__) # pragma clang diagnostic ignored "-Wundef" #elif defined(__INTEL_COMPILER) # pragma warning(disable: 193) #elif defined(__KEIL__) && defined(__C166__) # pragma warning disable = 322 #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && !defined(__PATHSCALE__) # if ((__GNUC__-0) >= 5 || ((__GNUC__-0) == 4 && (__GNUC_MINOR__-0) >= 2)) # pragma GCC diagnostic ignored "-Wundef" # endif #elif defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__MWERKS__) # if ((_MSC_VER-0) >= 1300) # pragma warning(disable: 4668) # endif #endif #endif #if 0 && defined(__POCC__) && defined(_WIN32) # if (__POCC__ >= 400) # pragma warn(disable: 2216) # endif #endif #if 0 && defined(__WATCOMC__) # if (__WATCOMC__ >= 1050) && (__WATCOMC__ < 1060) # pragma warning 203 9 # endif #endif #if defined(__BORLANDC__) && defined(__MSDOS__) && !defined(__FLAT__) # pragma option -h #endif #if !(LZO_CFG_NO_DISABLE_WCRTNONSTDC) #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE 1 #endif #ifndef _CRT_NONSTDC_NO_WARNINGS #define _CRT_NONSTDC_NO_WARNINGS 1 #endif #ifndef _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_DEPRECATE 1 #endif #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS 1 #endif #endif #if 0 #define LZO_0xffffUL 0xfffful #define LZO_0xffffffffUL 0xfffffffful #else #define LZO_0xffffUL 65535ul #define LZO_0xffffffffUL 4294967295ul #endif #define LZO_0xffffL LZO_0xffffUL #define LZO_0xffffffffL LZO_0xffffffffUL #if (LZO_0xffffL == LZO_0xffffffffL) # error "your preprocessor is broken 1" #endif #if (16ul * 16384ul != 262144ul) # error "your preprocessor is broken 2" #endif #if 0 #if (32767 >= 4294967295ul) # error "your preprocessor is broken 3" #endif #if (65535u >= 4294967295ul) # error "your preprocessor is broken 4" #endif #endif #if defined(__COUNTER__) # ifndef LZO_CFG_USE_COUNTER # define LZO_CFG_USE_COUNTER 1 # endif #else # undef LZO_CFG_USE_COUNTER #endif #if (UINT_MAX == LZO_0xffffL) #if defined(__ZTC__) && defined(__I86__) && !defined(__OS2__) # if !defined(MSDOS) # define MSDOS 1 # endif # if !defined(_MSDOS) # define _MSDOS 1 # endif #elif 0 && defined(__VERSION) && defined(MB_LEN_MAX) # if (__VERSION == 520) && (MB_LEN_MAX == 1) # if !defined(__AZTEC_C__) # define __AZTEC_C__ __VERSION # endif # if !defined(__DOS__) # define __DOS__ 1 # endif # endif #endif #endif #if defined(_MSC_VER) && defined(M_I86HM) && (UINT_MAX == LZO_0xffffL) # define ptrdiff_t long # define _PTRDIFF_T_DEFINED 1 #endif #if (UINT_MAX == LZO_0xffffL) # undef __LZO_RENAME_A # undef __LZO_RENAME_B # if defined(__AZTEC_C__) && defined(__DOS__) # define __LZO_RENAME_A 1 # elif defined(_MSC_VER) && defined(MSDOS) # if (_MSC_VER < 600) # define __LZO_RENAME_A 1 # elif (_MSC_VER < 700) # define __LZO_RENAME_B 1 # endif # elif defined(__TSC__) && defined(__OS2__) # define __LZO_RENAME_A 1 # elif defined(__MSDOS__) && defined(__TURBOC__) && (__TURBOC__ < 0x0410) # define __LZO_RENAME_A 1 # elif defined(__PACIFIC__) && defined(DOS) # if !defined(__far) # define __far far # endif # if !defined(__near) # define __near near # endif # endif # if defined(__LZO_RENAME_A) # if !defined(__cdecl) # define __cdecl cdecl # endif # if !defined(__far) # define __far far # endif # if !defined(__huge) # define __huge huge # endif # if !defined(__near) # define __near near # endif # if !defined(__pascal) # define __pascal pascal # endif # if !defined(__huge) # define __huge huge # endif # elif defined(__LZO_RENAME_B) # if !defined(__cdecl) # define __cdecl _cdecl # endif # if !defined(__far) # define __far _far # endif # if !defined(__huge) # define __huge _huge # endif # if !defined(__near) # define __near _near # endif # if !defined(__pascal) # define __pascal _pascal # endif # elif (defined(__PUREC__) || defined(__TURBOC__)) && defined(__TOS__) # if !defined(__cdecl) # define __cdecl cdecl # endif # if !defined(__pascal) # define __pascal pascal # endif # endif # undef __LZO_RENAME_A # undef __LZO_RENAME_B #endif #if (UINT_MAX == LZO_0xffffL) #if defined(__AZTEC_C__) && defined(__DOS__) # define LZO_BROKEN_CDECL_ALT_SYNTAX 1 #elif defined(_MSC_VER) && defined(MSDOS) # if (_MSC_VER < 600) # define LZO_BROKEN_INTEGRAL_CONSTANTS 1 # endif # if (_MSC_VER < 700) # define LZO_BROKEN_INTEGRAL_PROMOTION 1 # define LZO_BROKEN_SIZEOF 1 # endif #elif defined(__PACIFIC__) && defined(DOS) # define LZO_BROKEN_INTEGRAL_CONSTANTS 1 #elif defined(__TURBOC__) && defined(__MSDOS__) # if (__TURBOC__ < 0x0150) # define LZO_BROKEN_CDECL_ALT_SYNTAX 1 # define LZO_BROKEN_INTEGRAL_CONSTANTS 1 # define LZO_BROKEN_INTEGRAL_PROMOTION 1 # endif # if (__TURBOC__ < 0x0200) # define LZO_BROKEN_SIZEOF 1 # endif # if (__TURBOC__ < 0x0400) && defined(__cplusplus) # define LZO_BROKEN_CDECL_ALT_SYNTAX 1 # endif #elif (defined(__PUREC__) || defined(__TURBOC__)) && defined(__TOS__) # define LZO_BROKEN_CDECL_ALT_SYNTAX 1 # define LZO_BROKEN_SIZEOF 1 #endif #endif #if defined(__WATCOMC__) && (__WATCOMC__ < 900) # define LZO_BROKEN_INTEGRAL_CONSTANTS 1 #endif #if defined(_CRAY) && defined(_CRAY1) # define LZO_BROKEN_SIGNED_RIGHT_SHIFT 1 #endif #define LZO_PP_STRINGIZE(x) #x #define LZO_PP_MACRO_EXPAND(x) LZO_PP_STRINGIZE(x) #define LZO_PP_CONCAT0() /*empty*/ #define LZO_PP_CONCAT1(a) a #define LZO_PP_CONCAT2(a,b) a ## b #define LZO_PP_CONCAT3(a,b,c) a ## b ## c #define LZO_PP_CONCAT4(a,b,c,d) a ## b ## c ## d #define LZO_PP_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e #define LZO_PP_CONCAT6(a,b,c,d,e,f) a ## b ## c ## d ## e ## f #define LZO_PP_CONCAT7(a,b,c,d,e,f,g) a ## b ## c ## d ## e ## f ## g #define LZO_PP_ECONCAT0() LZO_PP_CONCAT0() #define LZO_PP_ECONCAT1(a) LZO_PP_CONCAT1(a) #define LZO_PP_ECONCAT2(a,b) LZO_PP_CONCAT2(a,b) #define LZO_PP_ECONCAT3(a,b,c) LZO_PP_CONCAT3(a,b,c) #define LZO_PP_ECONCAT4(a,b,c,d) LZO_PP_CONCAT4(a,b,c,d) #define LZO_PP_ECONCAT5(a,b,c,d,e) LZO_PP_CONCAT5(a,b,c,d,e) #define LZO_PP_ECONCAT6(a,b,c,d,e,f) LZO_PP_CONCAT6(a,b,c,d,e,f) #define LZO_PP_ECONCAT7(a,b,c,d,e,f,g) LZO_PP_CONCAT7(a,b,c,d,e,f,g) #define LZO_PP_EMPTY /*empty*/ #define LZO_PP_EMPTY0() /*empty*/ #define LZO_PP_EMPTY1(a) /*empty*/ #define LZO_PP_EMPTY2(a,b) /*empty*/ #define LZO_PP_EMPTY3(a,b,c) /*empty*/ #define LZO_PP_EMPTY4(a,b,c,d) /*empty*/ #define LZO_PP_EMPTY5(a,b,c,d,e) /*empty*/ #define LZO_PP_EMPTY6(a,b,c,d,e,f) /*empty*/ #define LZO_PP_EMPTY7(a,b,c,d,e,f,g) /*empty*/ #if 1 #define LZO_CPP_STRINGIZE(x) #x #define LZO_CPP_MACRO_EXPAND(x) LZO_CPP_STRINGIZE(x) #define LZO_CPP_CONCAT2(a,b) a ## b #define LZO_CPP_CONCAT3(a,b,c) a ## b ## c #define LZO_CPP_CONCAT4(a,b,c,d) a ## b ## c ## d #define LZO_CPP_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e #define LZO_CPP_CONCAT6(a,b,c,d,e,f) a ## b ## c ## d ## e ## f #define LZO_CPP_CONCAT7(a,b,c,d,e,f,g) a ## b ## c ## d ## e ## f ## g #define LZO_CPP_ECONCAT2(a,b) LZO_CPP_CONCAT2(a,b) #define LZO_CPP_ECONCAT3(a,b,c) LZO_CPP_CONCAT3(a,b,c) #define LZO_CPP_ECONCAT4(a,b,c,d) LZO_CPP_CONCAT4(a,b,c,d) #define LZO_CPP_ECONCAT5(a,b,c,d,e) LZO_CPP_CONCAT5(a,b,c,d,e) #define LZO_CPP_ECONCAT6(a,b,c,d,e,f) LZO_CPP_CONCAT6(a,b,c,d,e,f) #define LZO_CPP_ECONCAT7(a,b,c,d,e,f,g) LZO_CPP_CONCAT7(a,b,c,d,e,f,g) #endif #define __LZO_MASK_GEN(o,b) (((((o) << ((b)-!!(b))) - (o)) << 1) + (o)*!!(b)) #if 1 && defined(__cplusplus) # if !defined(__STDC_CONSTANT_MACROS) # define __STDC_CONSTANT_MACROS 1 # endif # if !defined(__STDC_LIMIT_MACROS) # define __STDC_LIMIT_MACROS 1 # endif #endif #if defined(__cplusplus) # define LZO_EXTERN_C extern "C" # define LZO_EXTERN_C_BEGIN extern "C" { # define LZO_EXTERN_C_END } #else # define LZO_EXTERN_C extern # define LZO_EXTERN_C_BEGIN /*empty*/ # define LZO_EXTERN_C_END /*empty*/ #endif #if !defined(__LZO_OS_OVERRIDE) #if (LZO_OS_FREESTANDING) # define LZO_INFO_OS "freestanding" #elif (LZO_OS_EMBEDDED) # define LZO_INFO_OS "embedded" #elif 1 && defined(__IAR_SYSTEMS_ICC__) # define LZO_OS_EMBEDDED 1 # define LZO_INFO_OS "embedded" #elif defined(__CYGWIN__) && defined(__GNUC__) # define LZO_OS_CYGWIN 1 # define LZO_INFO_OS "cygwin" #elif defined(__EMX__) && defined(__GNUC__) # define LZO_OS_EMX 1 # define LZO_INFO_OS "emx" #elif defined(__BEOS__) # define LZO_OS_BEOS 1 # define LZO_INFO_OS "beos" #elif defined(__Lynx__) # define LZO_OS_LYNXOS 1 # define LZO_INFO_OS "lynxos" #elif defined(__OS400__) # define LZO_OS_OS400 1 # define LZO_INFO_OS "os400" #elif defined(__QNX__) # define LZO_OS_QNX 1 # define LZO_INFO_OS "qnx" #elif defined(__BORLANDC__) && defined(__DPMI32__) && (__BORLANDC__ >= 0x0460) # define LZO_OS_DOS32 1 # define LZO_INFO_OS "dos32" #elif defined(__BORLANDC__) && defined(__DPMI16__) # define LZO_OS_DOS16 1 # define LZO_INFO_OS "dos16" #elif defined(__ZTC__) && defined(DOS386) # define LZO_OS_DOS32 1 # define LZO_INFO_OS "dos32" #elif defined(__OS2__) || defined(__OS2V2__) # if (UINT_MAX == LZO_0xffffL) # define LZO_OS_OS216 1 # define LZO_INFO_OS "os216" # elif (UINT_MAX == LZO_0xffffffffL) # define LZO_OS_OS2 1 # define LZO_INFO_OS "os2" # else # error "check your limits.h header" # endif #elif defined(__WIN64__) || defined(_WIN64) || defined(WIN64) # define LZO_OS_WIN64 1 # define LZO_INFO_OS "win64" #elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32) || defined(__WINDOWS_386__) # define LZO_OS_WIN32 1 # define LZO_INFO_OS "win32" #elif defined(__MWERKS__) && defined(__INTEL__) # define LZO_OS_WIN32 1 # define LZO_INFO_OS "win32" #elif defined(__WINDOWS__) || defined(_WINDOWS) || defined(_Windows) # if (UINT_MAX == LZO_0xffffL) # define LZO_OS_WIN16 1 # define LZO_INFO_OS "win16" # elif (UINT_MAX == LZO_0xffffffffL) # define LZO_OS_WIN32 1 # define LZO_INFO_OS "win32" # else # error "check your limits.h header" # endif #elif defined(__DOS__) || defined(__MSDOS__) || defined(_MSDOS) || defined(MSDOS) || (defined(__PACIFIC__) && defined(DOS)) # if (UINT_MAX == LZO_0xffffL) # define LZO_OS_DOS16 1 # define LZO_INFO_OS "dos16" # elif (UINT_MAX == LZO_0xffffffffL) # define LZO_OS_DOS32 1 # define LZO_INFO_OS "dos32" # else # error "check your limits.h header" # endif #elif defined(__WATCOMC__) # if defined(__NT__) && (UINT_MAX == LZO_0xffffL) # define LZO_OS_DOS16 1 # define LZO_INFO_OS "dos16" # elif defined(__NT__) && (__WATCOMC__ < 1100) # define LZO_OS_WIN32 1 # define LZO_INFO_OS "win32" # elif defined(__linux__) || defined(__LINUX__) # define LZO_OS_POSIX 1 # define LZO_INFO_OS "posix" # else # error "please specify a target using the -bt compiler option" # endif #elif defined(__palmos__) # define LZO_OS_PALMOS 1 # define LZO_INFO_OS "palmos" #elif defined(__TOS__) || defined(__atarist__) # define LZO_OS_TOS 1 # define LZO_INFO_OS "tos" #elif defined(macintosh) && !defined(__arm__) && !defined(__i386__) && !defined(__ppc__) && !defined(__x64_64__) # define LZO_OS_MACCLASSIC 1 # define LZO_INFO_OS "macclassic" #elif defined(__VMS) # define LZO_OS_VMS 1 # define LZO_INFO_OS "vms" #elif (defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__) # define LZO_OS_CONSOLE 1 # define LZO_OS_CONSOLE_PS2 1 # define LZO_INFO_OS "console" # define LZO_INFO_OS_CONSOLE "ps2" #elif defined(__mips__) && defined(__psp__) # define LZO_OS_CONSOLE 1 # define LZO_OS_CONSOLE_PSP 1 # define LZO_INFO_OS "console" # define LZO_INFO_OS_CONSOLE "psp" #else # define LZO_OS_POSIX 1 # define LZO_INFO_OS "posix" #endif #if (LZO_OS_POSIX) # if defined(_AIX) || defined(__AIX__) || defined(__aix__) # define LZO_OS_POSIX_AIX 1 # define LZO_INFO_OS_POSIX "aix" # elif defined(__FreeBSD__) # define LZO_OS_POSIX_FREEBSD 1 # define LZO_INFO_OS_POSIX "freebsd" # elif defined(__hpux__) || defined(__hpux) # define LZO_OS_POSIX_HPUX 1 # define LZO_INFO_OS_POSIX "hpux" # elif defined(__INTERIX) # define LZO_OS_POSIX_INTERIX 1 # define LZO_INFO_OS_POSIX "interix" # elif defined(__IRIX__) || defined(__irix__) # define LZO_OS_POSIX_IRIX 1 # define LZO_INFO_OS_POSIX "irix" # elif defined(__linux__) || defined(__linux) || defined(__LINUX__) # define LZO_OS_POSIX_LINUX 1 # define LZO_INFO_OS_POSIX "linux" # elif defined(__APPLE__) && defined(__MACH__) # if ((__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__-0) >= 20000) # define LZO_OS_POSIX_DARWIN 1040 # define LZO_INFO_OS_POSIX "darwin_iphone" # elif ((__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__-0) >= 1040) # define LZO_OS_POSIX_DARWIN __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ # define LZO_INFO_OS_POSIX "darwin" # else # define LZO_OS_POSIX_DARWIN 1 # define LZO_INFO_OS_POSIX "darwin" # endif # define LZO_OS_POSIX_MACOSX LZO_OS_POSIX_DARWIN # elif defined(__minix__) || defined(__minix) # define LZO_OS_POSIX_MINIX 1 # define LZO_INFO_OS_POSIX "minix" # elif defined(__NetBSD__) # define LZO_OS_POSIX_NETBSD 1 # define LZO_INFO_OS_POSIX "netbsd" # elif defined(__OpenBSD__) # define LZO_OS_POSIX_OPENBSD 1 # define LZO_INFO_OS_POSIX "openbsd" # elif defined(__osf__) # define LZO_OS_POSIX_OSF 1 # define LZO_INFO_OS_POSIX "osf" # elif defined(__solaris__) || defined(__sun) # if defined(__SVR4) || defined(__svr4__) # define LZO_OS_POSIX_SOLARIS 1 # define LZO_INFO_OS_POSIX "solaris" # else # define LZO_OS_POSIX_SUNOS 1 # define LZO_INFO_OS_POSIX "sunos" # endif # elif defined(__ultrix__) || defined(__ultrix) # define LZO_OS_POSIX_ULTRIX 1 # define LZO_INFO_OS_POSIX "ultrix" # elif defined(_UNICOS) # define LZO_OS_POSIX_UNICOS 1 # define LZO_INFO_OS_POSIX "unicos" # else # define LZO_OS_POSIX_UNKNOWN 1 # define LZO_INFO_OS_POSIX "unknown" # endif #endif #endif #if (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) # if (UINT_MAX != LZO_0xffffL) # error "unexpected configuration - check your compiler defines" # endif # if (ULONG_MAX != LZO_0xffffffffL) # error "unexpected configuration - check your compiler defines" # endif #endif #if (LZO_OS_DOS32 || LZO_OS_OS2 || LZO_OS_WIN32 || LZO_OS_WIN64) # if (UINT_MAX != LZO_0xffffffffL) # error "unexpected configuration - check your compiler defines" # endif # if (ULONG_MAX != LZO_0xffffffffL) # error "unexpected configuration - check your compiler defines" # endif #endif #if defined(CIL) && defined(_GNUCC) && defined(__GNUC__) # define LZO_CC_CILLY 1 # define LZO_INFO_CC "Cilly" # if defined(__CILLY__) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__CILLY__) # else # define LZO_INFO_CCVER "unknown" # endif #elif 0 && defined(SDCC) && defined(__VERSION__) && !defined(__GNUC__) # define LZO_CC_SDCC 1 # define LZO_INFO_CC "sdcc" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(SDCC) #elif defined(__PATHSCALE__) && defined(__PATHCC_PATCHLEVEL__) # define LZO_CC_PATHSCALE (__PATHCC__ * 0x10000L + (__PATHCC_MINOR__-0) * 0x100 + (__PATHCC_PATCHLEVEL__-0)) # define LZO_INFO_CC "Pathscale C" # define LZO_INFO_CCVER __PATHSCALE__ # if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # define LZO_CC_PATHSCALE_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # endif #elif defined(__INTEL_COMPILER) && ((__INTEL_COMPILER-0) > 0) # define LZO_CC_INTELC __INTEL_COMPILER # define LZO_INFO_CC "Intel C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__INTEL_COMPILER) # if defined(_MSC_VER) && ((_MSC_VER-0) > 0) # define LZO_CC_INTELC_MSC _MSC_VER # elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # define LZO_CC_INTELC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # endif #elif defined(__POCC__) && defined(_WIN32) # define LZO_CC_PELLESC 1 # define LZO_INFO_CC "Pelles C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__POCC__) #elif defined(__ARMCC_VERSION) && defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # if defined(__GNUC_PATCHLEVEL__) # define LZO_CC_ARMCC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # else # define LZO_CC_ARMCC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) # endif # define LZO_CC_ARMCC __ARMCC_VERSION # define LZO_INFO_CC "ARM C Compiler" # define LZO_INFO_CCVER __VERSION__ #elif defined(__clang__) && defined(__llvm__) && defined(__VERSION__) # if defined(__clang_major__) && defined(__clang_minor__) && defined(__clang_patchlevel__) # define LZO_CC_CLANG (__clang_major__ * 0x10000L + (__clang_minor__-0) * 0x100 + (__clang_patchlevel__-0)) # else # define LZO_CC_CLANG 0x010000L # endif # if defined(_MSC_VER) && ((_MSC_VER-0) > 0) # define LZO_CC_CLANG_MSC _MSC_VER # elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # define LZO_CC_CLANG_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # endif # define LZO_INFO_CC "clang" # define LZO_INFO_CCVER __VERSION__ #elif defined(__llvm__) && defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # if defined(__GNUC_PATCHLEVEL__) # define LZO_CC_LLVM_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # else # define LZO_CC_LLVM_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) # endif # define LZO_CC_LLVM LZO_CC_LLVM_GNUC # define LZO_INFO_CC "llvm-gcc" # define LZO_INFO_CCVER __VERSION__ #elif defined(__ACK__) && defined(_ACK) # define LZO_CC_ACK 1 # define LZO_INFO_CC "Amsterdam Compiler Kit C" # define LZO_INFO_CCVER "unknown" #elif defined(__ARMCC_VERSION) && !defined(__GNUC__) # define LZO_CC_ARMCC __ARMCC_VERSION # define LZO_CC_ARMCC_ARMCC __ARMCC_VERSION # define LZO_INFO_CC "ARM C Compiler" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__ARMCC_VERSION) #elif defined(__AZTEC_C__) # define LZO_CC_AZTECC 1 # define LZO_INFO_CC "Aztec C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__AZTEC_C__) #elif defined(__CODEGEARC__) # define LZO_CC_CODEGEARC 1 # define LZO_INFO_CC "CodeGear C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__CODEGEARC__) #elif defined(__BORLANDC__) # define LZO_CC_BORLANDC 1 # define LZO_INFO_CC "Borland C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__BORLANDC__) #elif defined(_CRAYC) && defined(_RELEASE) # define LZO_CC_CRAYC 1 # define LZO_INFO_CC "Cray C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_RELEASE) #elif defined(__DMC__) && defined(__SC__) # define LZO_CC_DMC 1 # define LZO_INFO_CC "Digital Mars C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__DMC__) #elif defined(__DECC) # define LZO_CC_DECC 1 # define LZO_INFO_CC "DEC C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__DECC) #elif (defined(__ghs) || defined(__ghs__)) && defined(__GHS_VERSION_NUMBER) && ((__GHS_VERSION_NUMBER-0) > 0) # define LZO_CC_GHS 1 # define LZO_INFO_CC "Green Hills C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__GHS_VERSION_NUMBER) # if defined(_MSC_VER) && ((_MSC_VER-0) > 0) # define LZO_CC_GHS_MSC _MSC_VER # elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) # define LZO_CC_GHS_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # endif #elif defined(__HIGHC__) # define LZO_CC_HIGHC 1 # define LZO_INFO_CC "MetaWare High C" # define LZO_INFO_CCVER "unknown" #elif defined(__HP_aCC) && ((__HP_aCC-0) > 0) # define LZO_CC_HPACC __HP_aCC # define LZO_INFO_CC "HP aCC" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__HP_aCC) #elif defined(__IAR_SYSTEMS_ICC__) # define LZO_CC_IARC 1 # define LZO_INFO_CC "IAR C" # if defined(__VER__) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__VER__) # else # define LZO_INFO_CCVER "unknown" # endif #elif defined(__IBMC__) && ((__IBMC__-0) > 0) # define LZO_CC_IBMC __IBMC__ # define LZO_INFO_CC "IBM C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__IBMC__) #elif defined(__IBMCPP__) && ((__IBMCPP__-0) > 0) # define LZO_CC_IBMC __IBMCPP__ # define LZO_INFO_CC "IBM C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__IBMCPP__) #elif defined(__KEIL__) && defined(__C166__) # define LZO_CC_KEILC 1 # define LZO_INFO_CC "Keil C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__C166__) #elif defined(__LCC__) && defined(_WIN32) && defined(__LCCOPTIMLEVEL) # define LZO_CC_LCCWIN32 1 # define LZO_INFO_CC "lcc-win32" # define LZO_INFO_CCVER "unknown" #elif defined(__LCC__) # define LZO_CC_LCC 1 # define LZO_INFO_CC "lcc" # if defined(__LCC_VERSION__) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__LCC_VERSION__) # else # define LZO_INFO_CCVER "unknown" # endif #elif defined(__MWERKS__) && ((__MWERKS__-0) > 0) # define LZO_CC_MWERKS __MWERKS__ # define LZO_INFO_CC "Metrowerks C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__MWERKS__) #elif (defined(__NDPC__) || defined(__NDPX__)) && defined(__i386) # define LZO_CC_NDPC 1 # define LZO_INFO_CC "Microway NDP C" # define LZO_INFO_CCVER "unknown" #elif defined(__PACIFIC__) # define LZO_CC_PACIFICC 1 # define LZO_INFO_CC "Pacific C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PACIFIC__) #elif defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) # if defined(__PGIC_PATCHLEVEL__) # define LZO_CC_PGI (__PGIC__ * 0x10000L + (__PGIC_MINOR__-0) * 0x100 + (__PGIC_PATCHLEVEL__-0)) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PGIC__) "." LZO_PP_MACRO_EXPAND(__PGIC_MINOR__) "." LZO_PP_MACRO_EXPAND(__PGIC_PATCHLEVEL__) # else # define LZO_CC_PGI (__PGIC__ * 0x10000L + (__PGIC_MINOR__-0) * 0x100) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PGIC__) "." LZO_PP_MACRO_EXPAND(__PGIC_MINOR__) ".0" # endif # define LZO_INFO_CC "Portland Group PGI C" #elif defined(__PGI) && (defined(__linux__) || defined(__WIN32__)) # define LZO_CC_PGI 1 # define LZO_INFO_CC "Portland Group PGI C" # define LZO_INFO_CCVER "unknown" #elif defined(__PUREC__) && defined(__TOS__) # define LZO_CC_PUREC 1 # define LZO_INFO_CC "Pure C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PUREC__) #elif defined(__SC__) && defined(__ZTC__) # define LZO_CC_SYMANTECC 1 # define LZO_INFO_CC "Symantec C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SC__) #elif defined(__SUNPRO_C) # define LZO_INFO_CC "SunPro C" # if ((__SUNPRO_C-0) > 0) # define LZO_CC_SUNPROC __SUNPRO_C # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SUNPRO_C) # else # define LZO_CC_SUNPROC 1 # define LZO_INFO_CCVER "unknown" # endif #elif defined(__SUNPRO_CC) # define LZO_INFO_CC "SunPro C" # if ((__SUNPRO_CC-0) > 0) # define LZO_CC_SUNPROC __SUNPRO_CC # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SUNPRO_CC) # else # define LZO_CC_SUNPROC 1 # define LZO_INFO_CCVER "unknown" # endif #elif defined(__TINYC__) # define LZO_CC_TINYC 1 # define LZO_INFO_CC "Tiny C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TINYC__) #elif defined(__TSC__) # define LZO_CC_TOPSPEEDC 1 # define LZO_INFO_CC "TopSpeed C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TSC__) #elif defined(__WATCOMC__) # define LZO_CC_WATCOMC 1 # define LZO_INFO_CC "Watcom C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__WATCOMC__) #elif defined(__TURBOC__) # define LZO_CC_TURBOC 1 # define LZO_INFO_CC "Turbo C" # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TURBOC__) #elif defined(__ZTC__) # define LZO_CC_ZORTECHC 1 # define LZO_INFO_CC "Zortech C" # if ((__ZTC__-0) == 0x310) # define LZO_INFO_CCVER "0x310" # else # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__ZTC__) # endif #elif defined(__GNUC__) && defined(__VERSION__) # if defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) # define LZO_CC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) # elif defined(__GNUC_MINOR__) # define LZO_CC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) # else # define LZO_CC_GNUC (__GNUC__ * 0x10000L) # endif # define LZO_INFO_CC "gcc" # define LZO_INFO_CCVER __VERSION__ #elif defined(_MSC_VER) && ((_MSC_VER-0) > 0) # define LZO_CC_MSC _MSC_VER # define LZO_INFO_CC "Microsoft C" # if defined(_MSC_FULL_VER) # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_MSC_VER) "." LZO_PP_MACRO_EXPAND(_MSC_FULL_VER) # else # define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_MSC_VER) # endif #else # define LZO_CC_UNKNOWN 1 # define LZO_INFO_CC "unknown" # define LZO_INFO_CCVER "unknown" #endif #if (LZO_CC_GNUC) && defined(__OPEN64__) # if defined(__OPENCC__) && defined(__OPENCC_MINOR__) && defined(__OPENCC_PATCHLEVEL__) # define LZO_CC_OPEN64 (__OPENCC__ * 0x10000L + (__OPENCC_MINOR__-0) * 0x100 + (__OPENCC_PATCHLEVEL__-0)) # define LZO_CC_OPEN64_GNUC LZO_CC_GNUC # endif #endif #if (LZO_CC_GNUC) && defined(__PCC__) # if defined(__PCC__) && defined(__PCC_MINOR__) && defined(__PCC_MINORMINOR__) # define LZO_CC_PCC (__PCC__ * 0x10000L + (__PCC_MINOR__-0) * 0x100 + (__PCC_MINORMINOR__-0)) # define LZO_CC_PCC_GNUC LZO_CC_GNUC # endif #endif #if 0 && (LZO_CC_MSC && (_MSC_VER >= 1200)) && !defined(_MSC_FULL_VER) # error "LZO_CC_MSC: _MSC_FULL_VER is not defined" #endif #if !defined(__LZO_ARCH_OVERRIDE) && !(LZO_ARCH_GENERIC) && defined(_CRAY) # if (UINT_MAX > LZO_0xffffffffL) && defined(_CRAY) # if defined(_CRAYMPP) || defined(_CRAYT3D) || defined(_CRAYT3E) # define LZO_ARCH_CRAY_MPP 1 # elif defined(_CRAY1) # define LZO_ARCH_CRAY_PVP 1 # endif # endif #endif #if !defined(__LZO_ARCH_OVERRIDE) #if (LZO_ARCH_GENERIC) # define LZO_INFO_ARCH "generic" #elif (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) # define LZO_ARCH_I086 1 # define LZO_INFO_ARCH "i086" #elif defined(__aarch64__) # define LZO_ARCH_ARM64 1 # define LZO_INFO_ARCH "arm64" #elif defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA) # define LZO_ARCH_ALPHA 1 # define LZO_INFO_ARCH "alpha" #elif (LZO_ARCH_CRAY_MPP) && (defined(_CRAYT3D) || defined(_CRAYT3E)) # define LZO_ARCH_ALPHA 1 # define LZO_INFO_ARCH "alpha" #elif defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64) # define LZO_ARCH_AMD64 1 # define LZO_INFO_ARCH "amd64" #elif defined(__arm__) || defined(_M_ARM) # define LZO_ARCH_ARM 1 # define LZO_INFO_ARCH "arm" #elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCARM__) # define LZO_ARCH_ARM 1 # define LZO_INFO_ARCH "arm" #elif (UINT_MAX <= LZO_0xffffL) && defined(__AVR__) # define LZO_ARCH_AVR 1 # define LZO_INFO_ARCH "avr" #elif defined(__avr32__) || defined(__AVR32__) # define LZO_ARCH_AVR32 1 # define LZO_INFO_ARCH "avr32" #elif defined(__bfin__) # define LZO_ARCH_BLACKFIN 1 # define LZO_INFO_ARCH "blackfin" #elif (UINT_MAX == LZO_0xffffL) && defined(__C166__) # define LZO_ARCH_C166 1 # define LZO_INFO_ARCH "c166" #elif defined(__cris__) # define LZO_ARCH_CRIS 1 # define LZO_INFO_ARCH "cris" #elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCEZ80__) # define LZO_ARCH_EZ80 1 # define LZO_INFO_ARCH "ez80" #elif defined(__H8300__) || defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) # define LZO_ARCH_H8300 1 # define LZO_INFO_ARCH "h8300" #elif defined(__hppa__) || defined(__hppa) # define LZO_ARCH_HPPA 1 # define LZO_INFO_ARCH "hppa" #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386) # define LZO_ARCH_I386 1 # define LZO_ARCH_IA32 1 # define LZO_INFO_ARCH "i386" #elif (LZO_CC_ZORTECHC && defined(__I86__)) # define LZO_ARCH_I386 1 # define LZO_ARCH_IA32 1 # define LZO_INFO_ARCH "i386" #elif (LZO_OS_DOS32 && LZO_CC_HIGHC) && defined(_I386) # define LZO_ARCH_I386 1 # define LZO_ARCH_IA32 1 # define LZO_INFO_ARCH "i386" #elif defined(__ia64__) || defined(__ia64) || defined(_M_IA64) # define LZO_ARCH_IA64 1 # define LZO_INFO_ARCH "ia64" #elif (UINT_MAX == LZO_0xffffL) && defined(__m32c__) # define LZO_ARCH_M16C 1 # define LZO_INFO_ARCH "m16c" #elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCM16C__) # define LZO_ARCH_M16C 1 # define LZO_INFO_ARCH "m16c" #elif defined(__m32r__) # define LZO_ARCH_M32R 1 # define LZO_INFO_ARCH "m32r" #elif (LZO_OS_TOS) || defined(__m68k__) || defined(__m68000__) || defined(__mc68000__) || defined(__mc68020__) || defined(_M_M68K) # define LZO_ARCH_M68K 1 # define LZO_INFO_ARCH "m68k" #elif (UINT_MAX == LZO_0xffffL) && defined(__C251__) # define LZO_ARCH_MCS251 1 # define LZO_INFO_ARCH "mcs251" #elif (UINT_MAX == LZO_0xffffL) && defined(__C51__) # define LZO_ARCH_MCS51 1 # define LZO_INFO_ARCH "mcs51" #elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICC8051__) # define LZO_ARCH_MCS51 1 # define LZO_INFO_ARCH "mcs51" #elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000) # define LZO_ARCH_MIPS 1 # define LZO_INFO_ARCH "mips" #elif (UINT_MAX == LZO_0xffffL) && defined(__MSP430__) # define LZO_ARCH_MSP430 1 # define LZO_INFO_ARCH "msp430" #elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICC430__) # define LZO_ARCH_MSP430 1 # define LZO_INFO_ARCH "msp430" #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR) # define LZO_ARCH_POWERPC 1 # define LZO_INFO_ARCH "powerpc" #elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x) # define LZO_ARCH_S390 1 # define LZO_INFO_ARCH "s390" #elif defined(__sh__) || defined(_M_SH) # define LZO_ARCH_SH 1 # define LZO_INFO_ARCH "sh" #elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8) # define LZO_ARCH_SPARC 1 # define LZO_INFO_ARCH "sparc" #elif defined(__SPU__) # define LZO_ARCH_SPU 1 # define LZO_INFO_ARCH "spu" #elif (UINT_MAX == LZO_0xffffL) && defined(__z80) # define LZO_ARCH_Z80 1 # define LZO_INFO_ARCH "z80" #elif (LZO_ARCH_CRAY_PVP) # if defined(_CRAYSV1) # define LZO_ARCH_CRAY_SV1 1 # define LZO_INFO_ARCH "cray_sv1" # elif (_ADDR64) # define LZO_ARCH_CRAY_T90 1 # define LZO_INFO_ARCH "cray_t90" # elif (_ADDR32) # define LZO_ARCH_CRAY_YMP 1 # define LZO_INFO_ARCH "cray_ymp" # else # define LZO_ARCH_CRAY_XMP 1 # define LZO_INFO_ARCH "cray_xmp" # endif #else # define LZO_ARCH_UNKNOWN 1 # define LZO_INFO_ARCH "unknown" #endif #endif #if !defined(LZO_ARCH_ARM_THUMB2) #if (LZO_ARCH_ARM) # if defined(__ARM_ARCH_ISA_THUMB) # if ((__ARM_ARCH_ISA_THUMB)+0 >= 2) # define LZO_ARCH_ARM_THUMB2 1 # endif # elif 1 && defined(__thumb2__) # define LZO_ARCH_ARM_THUMB2 1 # elif 1 && defined(__TARGET_ARCH_THUMB) && ((__TARGET_ARCH_THUMB)+0 >= 4) # define LZO_ARCH_ARM_THUMB2 1 # endif #endif #endif #if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_DOS32 || LZO_OS_OS2) # error "FIXME - missing define for CPU architecture" #endif #if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_WIN32) # error "FIXME - missing LZO_OS_WIN32 define for CPU architecture" #endif #if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_WIN64) # error "FIXME - missing LZO_OS_WIN64 define for CPU architecture" #endif #if (LZO_OS_OS216 || LZO_OS_WIN16) # define LZO_ARCH_I086PM 1 #elif 1 && (LZO_OS_DOS16 && defined(BLX286)) # define LZO_ARCH_I086PM 1 #elif 1 && (LZO_OS_DOS16 && defined(DOSX286)) # define LZO_ARCH_I086PM 1 #elif 1 && (LZO_OS_DOS16 && LZO_CC_BORLANDC && defined(__DPMI16__)) # define LZO_ARCH_I086PM 1 #endif #if (LZO_ARCH_AMD64 && !LZO_ARCH_X64) # define LZO_ARCH_X64 1 #elif (!LZO_ARCH_AMD64 && LZO_ARCH_X64) && defined(__LZO_ARCH_OVERRIDE) # define LZO_ARCH_AMD64 1 #endif #if (LZO_ARCH_ARM64 && !LZO_ARCH_AARCH64) # define LZO_ARCH_AARCH64 1 #elif (!LZO_ARCH_ARM64 && LZO_ARCH_AARCH64) && defined(__LZO_ARCH_OVERRIDE) # define LZO_ARCH_ARM64 1 #endif #if (LZO_ARCH_I386 && !LZO_ARCH_X86) # define LZO_ARCH_X86 1 #elif (!LZO_ARCH_I386 && LZO_ARCH_X86) && defined(__LZO_ARCH_OVERRIDE) # define LZO_ARCH_I386 1 #endif #if (LZO_ARCH_AMD64 && !LZO_ARCH_X64) || (!LZO_ARCH_AMD64 && LZO_ARCH_X64) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_ARM64 && !LZO_ARCH_AARCH64) || (!LZO_ARCH_ARM64 && LZO_ARCH_AARCH64) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_I386 && !LZO_ARCH_X86) || (!LZO_ARCH_I386 && LZO_ARCH_X86) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_ARM_THUMB1 && !LZO_ARCH_ARM) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_ARM_THUMB2 && !LZO_ARCH_ARM) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_ARM_THUMB1 && LZO_ARCH_ARM_THUMB2) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_I086PM && !LZO_ARCH_I086) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_I086) # if (UINT_MAX != LZO_0xffffL) # error "unexpected configuration - check your compiler defines" # endif # if (ULONG_MAX != LZO_0xffffffffL) # error "unexpected configuration - check your compiler defines" # endif #endif #if (LZO_ARCH_I386) # if (UINT_MAX != LZO_0xffffL) && defined(__i386_int16__) # error "unexpected configuration - check your compiler defines" # endif # if (UINT_MAX != LZO_0xffffffffL) && !defined(__i386_int16__) # error "unexpected configuration - check your compiler defines" # endif # if (ULONG_MAX != LZO_0xffffffffL) # error "unexpected configuration - check your compiler defines" # endif #endif #if (LZO_ARCH_AMD64 || LZO_ARCH_I386) # if !defined(LZO_TARGET_FEATURE_SSE2) # if defined(__SSE2__) # define LZO_TARGET_FEATURE_SSE2 1 # elif defined(_MSC_VER) && (defined(_M_IX86_FP) && ((_M_IX86_FP)+0 >= 2)) # define LZO_TARGET_FEATURE_SSE2 1 # elif (LZO_CC_INTELC_MSC || LZO_CC_MSC) && defined(_M_AMD64) # define LZO_TARGET_FEATURE_SSE2 1 # endif # endif # if !defined(LZO_TARGET_FEATURE_SSSE3) # if (LZO_TARGET_FEATURE_SSE2) # if defined(__SSSE3__) # define LZO_TARGET_FEATURE_SSSE3 1 # elif defined(_MSC_VER) && defined(__AVX__) # define LZO_TARGET_FEATURE_SSSE3 1 # endif # endif # endif # if !defined(LZO_TARGET_FEATURE_SSE4_2) # if (LZO_TARGET_FEATURE_SSSE3) # if defined(__SSE4_2__) # define LZO_TARGET_FEATURE_SSE4_2 1 # endif # endif # endif # if !defined(LZO_TARGET_FEATURE_AVX) # if (LZO_TARGET_FEATURE_SSSE3) # if defined(__AVX__) # define LZO_TARGET_FEATURE_AVX 1 # endif # endif # endif # if !defined(LZO_TARGET_FEATURE_AVX2) # if (LZO_TARGET_FEATURE_AVX) # if defined(__AVX2__) # define LZO_TARGET_FEATURE_AVX2 1 # endif # endif # endif #endif #if (LZO_TARGET_FEATURE_SSSE3 && !(LZO_TARGET_FEATURE_SSE2)) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_TARGET_FEATURE_SSE4_2 && !(LZO_TARGET_FEATURE_SSSE3)) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_TARGET_FEATURE_AVX && !(LZO_TARGET_FEATURE_SSSE3)) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_TARGET_FEATURE_AVX2 && !(LZO_TARGET_FEATURE_AVX)) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ARCH_ARM) # if !defined(LZO_TARGET_FEATURE_NEON) # if defined(__ARM_NEON) && ((__ARM_NEON)+0) # define LZO_TARGET_FEATURE_NEON 1 # elif 1 && defined(__ARM_NEON__) && ((__ARM_NEON__)+0) # define LZO_TARGET_FEATURE_NEON 1 # elif 1 && defined(__TARGET_FEATURE_NEON) && ((__TARGET_FEATURE_NEON)+0) # define LZO_TARGET_FEATURE_NEON 1 # endif # endif #elif (LZO_ARCH_ARM64) # if !defined(LZO_TARGET_FEATURE_NEON) # if 1 # define LZO_TARGET_FEATURE_NEON 1 # endif # endif #endif #if 0 #elif !defined(__LZO_MM_OVERRIDE) #if (LZO_ARCH_I086) #if (UINT_MAX != LZO_0xffffL) # error "unexpected configuration - check your compiler defines" #endif #if defined(__TINY__) || defined(M_I86TM) || defined(_M_I86TM) # define LZO_MM_TINY 1 #elif defined(__HUGE__) || defined(_HUGE_) || defined(M_I86HM) || defined(_M_I86HM) # define LZO_MM_HUGE 1 #elif defined(__SMALL__) || defined(M_I86SM) || defined(_M_I86SM) || defined(SMALL_MODEL) # define LZO_MM_SMALL 1 #elif defined(__MEDIUM__) || defined(M_I86MM) || defined(_M_I86MM) # define LZO_MM_MEDIUM 1 #elif defined(__COMPACT__) || defined(M_I86CM) || defined(_M_I86CM) # define LZO_MM_COMPACT 1 #elif defined(__LARGE__) || defined(M_I86LM) || defined(_M_I86LM) || defined(LARGE_MODEL) # define LZO_MM_LARGE 1 #elif (LZO_CC_AZTECC) # if defined(_LARGE_CODE) && defined(_LARGE_DATA) # define LZO_MM_LARGE 1 # elif defined(_LARGE_CODE) # define LZO_MM_MEDIUM 1 # elif defined(_LARGE_DATA) # define LZO_MM_COMPACT 1 # else # define LZO_MM_SMALL 1 # endif #elif (LZO_CC_ZORTECHC && defined(__VCM__)) # define LZO_MM_LARGE 1 #else # error "unknown LZO_ARCH_I086 memory model" #endif #if (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) #define LZO_HAVE_MM_HUGE_PTR 1 #define LZO_HAVE_MM_HUGE_ARRAY 1 #if (LZO_MM_TINY) # undef LZO_HAVE_MM_HUGE_ARRAY #endif #if (LZO_CC_AZTECC || LZO_CC_PACIFICC || LZO_CC_ZORTECHC) # undef LZO_HAVE_MM_HUGE_PTR # undef LZO_HAVE_MM_HUGE_ARRAY #elif (LZO_CC_DMC || LZO_CC_SYMANTECC) # undef LZO_HAVE_MM_HUGE_ARRAY #elif (LZO_CC_MSC && defined(_QC)) # undef LZO_HAVE_MM_HUGE_ARRAY # if (_MSC_VER < 600) # undef LZO_HAVE_MM_HUGE_PTR # endif #elif (LZO_CC_TURBOC && (__TURBOC__ < 0x0295)) # undef LZO_HAVE_MM_HUGE_ARRAY #endif #if (LZO_ARCH_I086PM) && !(LZO_HAVE_MM_HUGE_PTR) # if (LZO_OS_DOS16) # error "unexpected configuration - check your compiler defines" # elif (LZO_CC_ZORTECHC) # else # error "unexpected configuration - check your compiler defines" # endif #endif #ifdef __cplusplus extern "C" { #endif #if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0200)) extern void __near __cdecl _AHSHIFT(void); # define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) #elif (LZO_CC_DMC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) extern void __near __cdecl _AHSHIFT(void); # define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) #elif (LZO_CC_MSC || LZO_CC_TOPSPEEDC) extern void __near __cdecl _AHSHIFT(void); # define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) #elif (LZO_CC_TURBOC && (__TURBOC__ >= 0x0295)) extern void __near __cdecl _AHSHIFT(void); # define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) #elif ((LZO_CC_AZTECC || LZO_CC_PACIFICC || LZO_CC_TURBOC) && LZO_OS_DOS16) # define LZO_MM_AHSHIFT 12 #elif (LZO_CC_WATCOMC) extern unsigned char _HShift; # define LZO_MM_AHSHIFT ((unsigned) _HShift) #else # error "FIXME - implement LZO_MM_AHSHIFT" #endif #ifdef __cplusplus } #endif #endif #elif (LZO_ARCH_C166) #if !defined(__MODEL__) # error "FIXME - LZO_ARCH_C166 __MODEL__" #elif ((__MODEL__) == 0) # define LZO_MM_SMALL 1 #elif ((__MODEL__) == 1) # define LZO_MM_SMALL 1 #elif ((__MODEL__) == 2) # define LZO_MM_LARGE 1 #elif ((__MODEL__) == 3) # define LZO_MM_TINY 1 #elif ((__MODEL__) == 4) # define LZO_MM_XTINY 1 #elif ((__MODEL__) == 5) # define LZO_MM_XSMALL 1 #else # error "FIXME - LZO_ARCH_C166 __MODEL__" #endif #elif (LZO_ARCH_MCS251) #if !defined(__MODEL__) # error "FIXME - LZO_ARCH_MCS251 __MODEL__" #elif ((__MODEL__) == 0) # define LZO_MM_SMALL 1 #elif ((__MODEL__) == 2) # define LZO_MM_LARGE 1 #elif ((__MODEL__) == 3) # define LZO_MM_TINY 1 #elif ((__MODEL__) == 4) # define LZO_MM_XTINY 1 #elif ((__MODEL__) == 5) # define LZO_MM_XSMALL 1 #else # error "FIXME - LZO_ARCH_MCS251 __MODEL__" #endif #elif (LZO_ARCH_MCS51) #if !defined(__MODEL__) # error "FIXME - LZO_ARCH_MCS51 __MODEL__" #elif ((__MODEL__) == 1) # define LZO_MM_SMALL 1 #elif ((__MODEL__) == 2) # define LZO_MM_LARGE 1 #elif ((__MODEL__) == 3) # define LZO_MM_TINY 1 #elif ((__MODEL__) == 4) # define LZO_MM_XTINY 1 #elif ((__MODEL__) == 5) # define LZO_MM_XSMALL 1 #else # error "FIXME - LZO_ARCH_MCS51 __MODEL__" #endif #elif (LZO_ARCH_CRAY_PVP) # define LZO_MM_PVP 1 #else # define LZO_MM_FLAT 1 #endif #if (LZO_MM_COMPACT) # define LZO_INFO_MM "compact" #elif (LZO_MM_FLAT) # define LZO_INFO_MM "flat" #elif (LZO_MM_HUGE) # define LZO_INFO_MM "huge" #elif (LZO_MM_LARGE) # define LZO_INFO_MM "large" #elif (LZO_MM_MEDIUM) # define LZO_INFO_MM "medium" #elif (LZO_MM_PVP) # define LZO_INFO_MM "pvp" #elif (LZO_MM_SMALL) # define LZO_INFO_MM "small" #elif (LZO_MM_TINY) # define LZO_INFO_MM "tiny" #else # error "unknown memory model" #endif #endif #if !defined(__lzo_gnuc_extension__) #if (LZO_CC_GNUC >= 0x020800ul) # define __lzo_gnuc_extension__ __extension__ #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_gnuc_extension__ __extension__ #elif (LZO_CC_IBMC >= 600) # define __lzo_gnuc_extension__ __extension__ #else #endif #endif #if !defined(__lzo_gnuc_extension__) # define __lzo_gnuc_extension__ /*empty*/ #endif #if !defined(lzo_has_builtin) #if (LZO_CC_CLANG) && defined(__has_builtin) # define lzo_has_builtin __has_builtin #endif #endif #if !defined(lzo_has_builtin) # define lzo_has_builtin(x) 0 #endif #if !defined(lzo_has_attribute) #if (LZO_CC_CLANG) && defined(__has_attribute) # define lzo_has_attribute __has_attribute #endif #endif #if !defined(lzo_has_attribute) # define lzo_has_attribute(x) 0 #endif #if !defined(lzo_has_declspec_attribute) #if (LZO_CC_CLANG) && defined(__has_declspec_attribute) # define lzo_has_declspec_attribute __has_declspec_attribute #endif #endif #if !defined(lzo_has_declspec_attribute) # define lzo_has_declspec_attribute(x) 0 #endif #if !defined(lzo_has_feature) #if (LZO_CC_CLANG) && defined(__has_feature) # define lzo_has_feature __has_feature #endif #endif #if !defined(lzo_has_feature) # define lzo_has_feature(x) 0 #endif #if !defined(lzo_has_extension) #if (LZO_CC_CLANG) && defined(__has_extension) # define lzo_has_extension __has_extension #elif (LZO_CC_CLANG) && defined(__has_feature) # define lzo_has_extension __has_feature #endif #endif #if !defined(lzo_has_extension) # define lzo_has_extension 0 #endif #if !defined(LZO_CFG_USE_NEW_STYLE_CASTS) && defined(__cplusplus) && 0 # if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) # define LZO_CFG_USE_NEW_STYLE_CASTS 0 # elif (LZO_CC_INTELC && (__INTEL_COMPILER < 1200)) # define LZO_CFG_USE_NEW_STYLE_CASTS 0 # else # define LZO_CFG_USE_NEW_STYLE_CASTS 1 # endif #endif #if !defined(LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_CFG_USE_NEW_STYLE_CASTS 0 #endif #if !defined(__cplusplus) # if defined(LZO_CFG_USE_NEW_STYLE_CASTS) # undef LZO_CFG_USE_NEW_STYLE_CASTS # endif # define LZO_CFG_USE_NEW_STYLE_CASTS 0 #endif #if !defined(LZO_REINTERPRET_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_REINTERPRET_CAST(t,e) (reinterpret_cast (e)) # endif #endif #if !defined(LZO_REINTERPRET_CAST) # define LZO_REINTERPRET_CAST(t,e) ((t) (e)) #endif #if !defined(LZO_STATIC_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_STATIC_CAST(t,e) (static_cast (e)) # endif #endif #if !defined(LZO_STATIC_CAST) # define LZO_STATIC_CAST(t,e) ((t) (e)) #endif #if !defined(LZO_STATIC_CAST2) # define LZO_STATIC_CAST2(t1,t2,e) LZO_STATIC_CAST(t1, LZO_STATIC_CAST(t2, e)) #endif #if !defined(LZO_UNCONST_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_UNCONST_CAST(t,e) (const_cast (e)) # elif (LZO_HAVE_MM_HUGE_PTR) # define LZO_UNCONST_CAST(t,e) ((t) (e)) # elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_UNCONST_CAST(t,e) ((t) ((void *) ((lzo_uintptr_t) ((const void *) (e))))) # endif #endif #if !defined(LZO_UNCONST_CAST) # define LZO_UNCONST_CAST(t,e) ((t) ((void *) ((const void *) (e)))) #endif #if !defined(LZO_UNCONST_VOLATILE_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_UNCONST_VOLATILE_CAST(t,e) (const_cast (e)) # elif (LZO_HAVE_MM_HUGE_PTR) # define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) (e)) # elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) ((volatile void *) ((lzo_uintptr_t) ((volatile const void *) (e))))) # endif #endif #if !defined(LZO_UNCONST_VOLATILE_CAST) # define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) ((volatile void *) ((volatile const void *) (e)))) #endif #if !defined(LZO_UNVOLATILE_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_UNVOLATILE_CAST(t,e) (const_cast (e)) # elif (LZO_HAVE_MM_HUGE_PTR) # define LZO_UNVOLATILE_CAST(t,e) ((t) (e)) # elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_UNVOLATILE_CAST(t,e) ((t) ((void *) ((lzo_uintptr_t) ((volatile void *) (e))))) # endif #endif #if !defined(LZO_UNVOLATILE_CAST) # define LZO_UNVOLATILE_CAST(t,e) ((t) ((void *) ((volatile void *) (e)))) #endif #if !defined(LZO_UNVOLATILE_CONST_CAST) # if (LZO_CFG_USE_NEW_STYLE_CASTS) # define LZO_UNVOLATILE_CONST_CAST(t,e) (const_cast (e)) # elif (LZO_HAVE_MM_HUGE_PTR) # define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) (e)) # elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) ((const void *) ((lzo_uintptr_t) ((volatile const void *) (e))))) # endif #endif #if !defined(LZO_UNVOLATILE_CONST_CAST) # define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) ((const void *) ((volatile const void *) (e)))) #endif #if !defined(LZO_PCAST) # if (LZO_HAVE_MM_HUGE_PTR) # define LZO_PCAST(t,e) ((t) (e)) # endif #endif #if !defined(LZO_PCAST) # define LZO_PCAST(t,e) LZO_STATIC_CAST(t, LZO_STATIC_CAST(void *, e)) #endif #if !defined(LZO_CCAST) # if (LZO_HAVE_MM_HUGE_PTR) # define LZO_CCAST(t,e) ((t) (e)) # endif #endif #if !defined(LZO_CCAST) # define LZO_CCAST(t,e) LZO_STATIC_CAST(t, LZO_STATIC_CAST(const void *, e)) #endif #if !defined(LZO_ICONV) # define LZO_ICONV(t,e) LZO_STATIC_CAST(t, e) #endif #if !defined(LZO_ICAST) # define LZO_ICAST(t,e) LZO_STATIC_CAST(t, e) #endif #if !defined(LZO_ITRUNC) # define LZO_ITRUNC(t,e) LZO_STATIC_CAST(t, e) #endif #if !defined(__lzo_cte) # if (LZO_CC_MSC || LZO_CC_WATCOMC) # define __lzo_cte(e) ((void)0,(e)) # elif 1 # define __lzo_cte(e) ((void)0,(e)) # endif #endif #if !defined(__lzo_cte) # define __lzo_cte(e) (e) #endif #if !defined(LZO_BLOCK_BEGIN) # define LZO_BLOCK_BEGIN do { # define LZO_BLOCK_END } while __lzo_cte(0) #endif #if !defined(LZO_UNUSED) # if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0600)) # define LZO_UNUSED(var) ((void) &var) # elif (LZO_CC_BORLANDC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PELLESC || LZO_CC_TURBOC) # define LZO_UNUSED(var) if (&var) ; else # elif (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x030200ul)) # define LZO_UNUSED(var) ((void) &var) # elif (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_UNUSED(var) ((void) var) # elif (LZO_CC_MSC && (_MSC_VER < 900)) # define LZO_UNUSED(var) if (&var) ; else # elif (LZO_CC_KEILC) # define LZO_UNUSED(var) {extern int lzo_unused__[1-2*!(sizeof(var)>0)]; (void)lzo_unused__;} # elif (LZO_CC_PACIFICC) # define LZO_UNUSED(var) ((void) sizeof(var)) # elif (LZO_CC_WATCOMC) && defined(__cplusplus) # define LZO_UNUSED(var) ((void) var) # else # define LZO_UNUSED(var) ((void) &var) # endif #endif #if !defined(LZO_UNUSED_FUNC) # if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0600)) # define LZO_UNUSED_FUNC(func) ((void) func) # elif (LZO_CC_BORLANDC || LZO_CC_NDPC || LZO_CC_TURBOC) # define LZO_UNUSED_FUNC(func) if (func) ; else # elif (LZO_CC_CLANG || LZO_CC_LLVM) # define LZO_UNUSED_FUNC(func) ((void) &func) # elif (LZO_CC_MSC && (_MSC_VER < 900)) # define LZO_UNUSED_FUNC(func) if (func) ; else # elif (LZO_CC_MSC) # define LZO_UNUSED_FUNC(func) ((void) &func) # elif (LZO_CC_KEILC || LZO_CC_PELLESC) # define LZO_UNUSED_FUNC(func) {extern int lzo_unused__[1-2*!(sizeof((int)func)>0)]; (void)lzo_unused__;} # else # define LZO_UNUSED_FUNC(func) ((void) func) # endif #endif #if !defined(LZO_UNUSED_LABEL) # if (LZO_CC_CLANG >= 0x020800ul) # define LZO_UNUSED_LABEL(l) (__lzo_gnuc_extension__ ((void) ((const void *) &&l))) # elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_INTELC || LZO_CC_WATCOMC) # define LZO_UNUSED_LABEL(l) if __lzo_cte(0) goto l # else # define LZO_UNUSED_LABEL(l) switch (0) case 1:goto l # endif #endif #if !defined(LZO_DEFINE_UNINITIALIZED_VAR) # if 0 # define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var # elif 0 && (LZO_CC_GNUC) # define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var = var # else # define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var = init # endif #endif #if !defined(__lzo_inline) #if (LZO_CC_TURBOC && (__TURBOC__ <= 0x0295)) #elif defined(__cplusplus) # define __lzo_inline inline #elif defined(__STDC_VERSION__) && (__STDC_VERSION__-0 >= 199901L) # define __lzo_inline inline #elif (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0550)) # define __lzo_inline __inline #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) # define __lzo_inline __inline__ #elif (LZO_CC_DMC) # define __lzo_inline __inline #elif (LZO_CC_GHS) # define __lzo_inline __inline__ #elif (LZO_CC_IBMC >= 600) # define __lzo_inline __inline__ #elif (LZO_CC_INTELC) # define __lzo_inline __inline #elif (LZO_CC_MWERKS && (__MWERKS__ >= 0x2405)) # define __lzo_inline __inline #elif (LZO_CC_MSC && (_MSC_VER >= 900)) # define __lzo_inline __inline #elif (LZO_CC_SUNPROC >= 0x5100) # define __lzo_inline __inline__ #endif #endif #if defined(__lzo_inline) # ifndef __lzo_HAVE_inline # define __lzo_HAVE_inline 1 # endif #else # define __lzo_inline /*empty*/ #endif #if !defined(__lzo_forceinline) #if (LZO_CC_GNUC >= 0x030200ul) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #elif (LZO_CC_IBMC >= 700) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) # define __lzo_forceinline __forceinline #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #elif (LZO_CC_MSC && (_MSC_VER >= 1200)) # define __lzo_forceinline __forceinline #elif (LZO_CC_PGI >= 0x0d0a00ul) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #elif (LZO_CC_SUNPROC >= 0x5100) # define __lzo_forceinline __inline__ __attribute__((__always_inline__)) #endif #endif #if defined(__lzo_forceinline) # ifndef __lzo_HAVE_forceinline # define __lzo_HAVE_forceinline 1 # endif #else # define __lzo_forceinline __lzo_inline #endif #if !defined(__lzo_noinline) #if 1 && (LZO_ARCH_I386) && (LZO_CC_GNUC >= 0x040000ul) && (LZO_CC_GNUC < 0x040003ul) # define __lzo_noinline __attribute__((__noinline__,__used__)) #elif (LZO_CC_GNUC >= 0x030200ul) # define __lzo_noinline __attribute__((__noinline__)) #elif (LZO_CC_IBMC >= 700) # define __lzo_noinline __attribute__((__noinline__)) #elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 600)) # define __lzo_noinline __declspec(noinline) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) # define __lzo_noinline __attribute__((__noinline__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_noinline __attribute__((__noinline__)) #elif (LZO_CC_MSC && (_MSC_VER >= 1300)) # define __lzo_noinline __declspec(noinline) #elif (LZO_CC_MWERKS && (__MWERKS__ >= 0x3200) && (LZO_OS_WIN32 || LZO_OS_WIN64)) # if defined(__cplusplus) # else # define __lzo_noinline __declspec(noinline) # endif #elif (LZO_CC_PGI >= 0x0d0a00ul) # define __lzo_noinline __attribute__((__noinline__)) #elif (LZO_CC_SUNPROC >= 0x5100) # define __lzo_noinline __attribute__((__noinline__)) #endif #endif #if defined(__lzo_noinline) # ifndef __lzo_HAVE_noinline # define __lzo_HAVE_noinline 1 # endif #else # define __lzo_noinline /*empty*/ #endif #if (__lzo_HAVE_forceinline || __lzo_HAVE_noinline) && !(__lzo_HAVE_inline) # error "unexpected configuration - check your compiler defines" #endif #if !defined(__lzo_static_inline) #if (LZO_CC_IBMC) # define __lzo_static_inline __lzo_gnuc_extension__ static __lzo_inline #endif #endif #if !defined(__lzo_static_inline) # define __lzo_static_inline static __lzo_inline #endif #if !defined(__lzo_static_forceinline) #if (LZO_CC_IBMC) # define __lzo_static_forceinline __lzo_gnuc_extension__ static __lzo_forceinline #endif #endif #if !defined(__lzo_static_forceinline) # define __lzo_static_forceinline static __lzo_forceinline #endif #if !defined(__lzo_static_noinline) #if (LZO_CC_IBMC) # define __lzo_static_noinline __lzo_gnuc_extension__ static __lzo_noinline #endif #endif #if !defined(__lzo_static_noinline) # define __lzo_static_noinline static __lzo_noinline #endif #if !defined(__lzo_c99_extern_inline) #if defined(__GNUC_GNU_INLINE__) # define __lzo_c99_extern_inline __lzo_inline #elif defined(__GNUC_STDC_INLINE__) # define __lzo_c99_extern_inline extern __lzo_inline #elif defined(__STDC_VERSION__) && (__STDC_VERSION__-0 >= 199901L) # define __lzo_c99_extern_inline extern __lzo_inline #endif #if !defined(__lzo_c99_extern_inline) && (__lzo_HAVE_inline) # define __lzo_c99_extern_inline __lzo_inline #endif #endif #if defined(__lzo_c99_extern_inline) # ifndef __lzo_HAVE_c99_extern_inline # define __lzo_HAVE_c99_extern_inline 1 # endif #else # define __lzo_c99_extern_inline /*empty*/ #endif #if !defined(__lzo_may_alias) #if (LZO_CC_GNUC >= 0x030400ul) # define __lzo_may_alias __attribute__((__may_alias__)) #elif (LZO_CC_CLANG >= 0x020900ul) # define __lzo_may_alias __attribute__((__may_alias__)) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 1210)) && 0 # define __lzo_may_alias __attribute__((__may_alias__)) #elif (LZO_CC_PGI >= 0x0d0a00ul) && 0 # define __lzo_may_alias __attribute__((__may_alias__)) #endif #endif #if defined(__lzo_may_alias) # ifndef __lzo_HAVE_may_alias # define __lzo_HAVE_may_alias 1 # endif #else # define __lzo_may_alias /*empty*/ #endif #if !defined(__lzo_noreturn) #if (LZO_CC_GNUC >= 0x020700ul) # define __lzo_noreturn __attribute__((__noreturn__)) #elif (LZO_CC_IBMC >= 700) # define __lzo_noreturn __attribute__((__noreturn__)) #elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) # define __lzo_noreturn __declspec(noreturn) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 600)) # define __lzo_noreturn __attribute__((__noreturn__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_noreturn __attribute__((__noreturn__)) #elif (LZO_CC_MSC && (_MSC_VER >= 1200)) # define __lzo_noreturn __declspec(noreturn) #elif (LZO_CC_PGI >= 0x0d0a00ul) # define __lzo_noreturn __attribute__((__noreturn__)) #endif #endif #if defined(__lzo_noreturn) # ifndef __lzo_HAVE_noreturn # define __lzo_HAVE_noreturn 1 # endif #else # define __lzo_noreturn /*empty*/ #endif #if !defined(__lzo_nothrow) #if (LZO_CC_GNUC >= 0x030300ul) # define __lzo_nothrow __attribute__((__nothrow__)) #elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) && defined(__cplusplus) # define __lzo_nothrow __declspec(nothrow) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 900)) # define __lzo_nothrow __attribute__((__nothrow__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_nothrow __attribute__((__nothrow__)) #elif (LZO_CC_MSC && (_MSC_VER >= 1200)) && defined(__cplusplus) # define __lzo_nothrow __declspec(nothrow) #endif #endif #if defined(__lzo_nothrow) # ifndef __lzo_HAVE_nothrow # define __lzo_HAVE_nothrow 1 # endif #else # define __lzo_nothrow /*empty*/ #endif #if !defined(__lzo_restrict) #if (LZO_CC_GNUC >= 0x030400ul) # define __lzo_restrict __restrict__ #elif (LZO_CC_IBMC >= 800) && !defined(__cplusplus) # define __lzo_restrict __restrict__ #elif (LZO_CC_IBMC >= 1210) # define __lzo_restrict __restrict__ #elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 600)) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 600)) # define __lzo_restrict __restrict__ #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM) # define __lzo_restrict __restrict__ #elif (LZO_CC_MSC && (_MSC_VER >= 1400)) # define __lzo_restrict __restrict #elif (LZO_CC_PGI >= 0x0d0a00ul) # define __lzo_restrict __restrict__ #endif #endif #if defined(__lzo_restrict) # ifndef __lzo_HAVE_restrict # define __lzo_HAVE_restrict 1 # endif #else # define __lzo_restrict /*empty*/ #endif #if !defined(__lzo_alignof) #if (LZO_CC_ARMCC || LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) # define __lzo_alignof(e) __alignof__(e) #elif (LZO_CC_GHS) && !defined(__cplusplus) # define __lzo_alignof(e) __alignof__(e) #elif (LZO_CC_IBMC >= 600) # define __lzo_alignof(e) (__lzo_gnuc_extension__ __alignof__(e)) #elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 700)) # define __lzo_alignof(e) __alignof__(e) #elif (LZO_CC_MSC && (_MSC_VER >= 1300)) # define __lzo_alignof(e) __alignof(e) #elif (LZO_CC_SUNPROC >= 0x5100) # define __lzo_alignof(e) __alignof__(e) #endif #endif #if defined(__lzo_alignof) # ifndef __lzo_HAVE_alignof # define __lzo_HAVE_alignof 1 # endif #endif #if !defined(__lzo_struct_packed) #if (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) #elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020700ul)) #elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) && defined(__cplusplus) #elif (LZO_CC_PCC && (LZO_CC_PCC < 0x010100ul)) #elif (LZO_CC_SUNPROC && (LZO_CC_SUNPROC < 0x5110)) && !defined(__cplusplus) #elif (LZO_CC_GNUC >= 0x030400ul) && !(LZO_CC_PCC_GNUC) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) # define __lzo_struct_packed(s) struct s { # define __lzo_struct_packed_end() } __attribute__((__gcc_struct__,__packed__)); # define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__gcc_struct__,__packed__)); #elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || (LZO_CC_PGI >= 0x0d0a00ul) || (LZO_CC_SUNPROC >= 0x5100)) # define __lzo_struct_packed(s) struct s { # define __lzo_struct_packed_end() } __attribute__((__packed__)); # define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__packed__)); #elif (LZO_CC_IBMC >= 700) # define __lzo_struct_packed(s) __lzo_gnuc_extension__ struct s { # define __lzo_struct_packed_end() } __attribute__((__packed__)); # define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__packed__)); #elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) # define __lzo_struct_packed(s) __pragma(pack(push,1)) struct s { # define __lzo_struct_packed_end() } __pragma(pack(pop)); #elif (LZO_CC_WATCOMC && (__WATCOMC__ >= 900)) # define __lzo_struct_packed(s) _Packed struct s { # define __lzo_struct_packed_end() }; #endif #endif #if defined(__lzo_struct_packed) && !defined(__lzo_struct_packed_ma) # define __lzo_struct_packed_ma(s) __lzo_struct_packed(s) #endif #if defined(__lzo_struct_packed_end) && !defined(__lzo_struct_packed_ma_end) # define __lzo_struct_packed_ma_end() __lzo_struct_packed_end() #endif #if !defined(__lzo_byte_struct) #if defined(__lzo_struct_packed) # define __lzo_byte_struct(s,n) __lzo_struct_packed(s) unsigned char a[n]; __lzo_struct_packed_end() # define __lzo_byte_struct_ma(s,n) __lzo_struct_packed_ma(s) unsigned char a[n]; __lzo_struct_packed_ma_end() #elif (LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_PGI || (LZO_CC_SUNPROC >= 0x5100)) # define __lzo_byte_struct(s,n) struct s { unsigned char a[n]; } __attribute__((__packed__)); # define __lzo_byte_struct_ma(s,n) struct s { unsigned char a[n]; } __lzo_may_alias __attribute__((__packed__)); #endif #endif #if defined(__lzo_byte_struct) && !defined(__lzo_byte_struct_ma) # define __lzo_byte_struct_ma(s,n) __lzo_byte_struct(s,n) #endif #if !defined(__lzo_struct_align16) && (__lzo_HAVE_alignof) #if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x030000ul)) #elif (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) #elif (LZO_CC_CILLY || LZO_CC_PCC) #elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) # define __lzo_struct_align16(s) struct __declspec(align(16)) s { # define __lzo_struct_align16_end() }; # define __lzo_struct_align32(s) struct __declspec(align(32)) s { # define __lzo_struct_align32_end() }; # define __lzo_struct_align64(s) struct __declspec(align(64)) s { # define __lzo_struct_align64_end() }; #elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || (LZO_CC_IBMC >= 700) || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_struct_align16(s) struct s { # define __lzo_struct_align16_end() } __attribute__((__aligned__(16))); # define __lzo_struct_align32(s) struct s { # define __lzo_struct_align32_end() } __attribute__((__aligned__(32))); # define __lzo_struct_align64(s) struct s { # define __lzo_struct_align64_end() } __attribute__((__aligned__(64))); #endif #endif #if !defined(__lzo_union_um) #if (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) #elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020700ul)) #elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) && defined(__cplusplus) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER < 810)) #elif (LZO_CC_PCC && (LZO_CC_PCC < 0x010100ul)) #elif (LZO_CC_SUNPROC && (LZO_CC_SUNPROC < 0x5110)) && !defined(__cplusplus) #elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || (LZO_CC_PGI >= 0x0d0a00ul) || (LZO_CC_SUNPROC >= 0x5100)) # define __lzo_union_am(s) union s { # define __lzo_union_am_end() } __lzo_may_alias; # define __lzo_union_um(s) union s { # define __lzo_union_um_end() } __lzo_may_alias __attribute__((__packed__)); #elif (LZO_CC_IBMC >= 700) # define __lzo_union_am(s) __lzo_gnuc_extension__ union s { # define __lzo_union_am_end() } __lzo_may_alias; # define __lzo_union_um(s) __lzo_gnuc_extension__ union s { # define __lzo_union_um_end() } __lzo_may_alias __attribute__((__packed__)); #elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) # define __lzo_union_um(s) __pragma(pack(push,1)) union s { # define __lzo_union_um_end() } __pragma(pack(pop)); #elif (LZO_CC_WATCOMC && (__WATCOMC__ >= 900)) # define __lzo_union_um(s) _Packed union s { # define __lzo_union_um_end() }; #endif #endif #if !defined(__lzo_union_am) # define __lzo_union_am(s) union s { # define __lzo_union_am_end() }; #endif #if !defined(__lzo_constructor) #if (LZO_CC_GNUC >= 0x030400ul) # define __lzo_constructor __attribute__((__constructor__,__used__)) #elif (LZO_CC_GNUC >= 0x020700ul) # define __lzo_constructor __attribute__((__constructor__)) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) # define __lzo_constructor __attribute__((__constructor__,__used__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_constructor __attribute__((__constructor__)) #endif #endif #if defined(__lzo_constructor) # ifndef __lzo_HAVE_constructor # define __lzo_HAVE_constructor 1 # endif #endif #if !defined(__lzo_destructor) #if (LZO_CC_GNUC >= 0x030400ul) # define __lzo_destructor __attribute__((__destructor__,__used__)) #elif (LZO_CC_GNUC >= 0x020700ul) # define __lzo_destructor __attribute__((__destructor__)) #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) # define __lzo_destructor __attribute__((__destructor__,__used__)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_destructor __attribute__((__destructor__)) #endif #endif #if defined(__lzo_destructor) # ifndef __lzo_HAVE_destructor # define __lzo_HAVE_destructor 1 # endif #endif #if (__lzo_HAVE_destructor) && !(__lzo_HAVE_constructor) # error "unexpected configuration - check your compiler defines" #endif #if !defined(__lzo_likely) && !defined(__lzo_unlikely) #if (LZO_CC_GNUC >= 0x030200ul) # define __lzo_likely(e) (__builtin_expect(!!(e),1)) # define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) #elif (LZO_CC_IBMC >= 1010) # define __lzo_likely(e) (__builtin_expect(!!(e),1)) # define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) #elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 800)) # define __lzo_likely(e) (__builtin_expect(!!(e),1)) # define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define __lzo_likely(e) (__builtin_expect(!!(e),1)) # define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) #endif #endif #if defined(__lzo_likely) # ifndef __lzo_HAVE_likely # define __lzo_HAVE_likely 1 # endif #else # define __lzo_likely(e) (e) #endif #if defined(__lzo_very_likely) # ifndef __lzo_HAVE_very_likely # define __lzo_HAVE_very_likely 1 # endif #else # define __lzo_very_likely(e) __lzo_likely(e) #endif #if defined(__lzo_unlikely) # ifndef __lzo_HAVE_unlikely # define __lzo_HAVE_unlikely 1 # endif #else # define __lzo_unlikely(e) (e) #endif #if defined(__lzo_very_unlikely) # ifndef __lzo_HAVE_very_unlikely # define __lzo_HAVE_very_unlikely 1 # endif #else # define __lzo_very_unlikely(e) __lzo_unlikely(e) #endif #if !defined(__lzo_loop_forever) # if (LZO_CC_IBMC) # define __lzo_loop_forever() LZO_BLOCK_BEGIN for (;;) { ; } LZO_BLOCK_END # else # define __lzo_loop_forever() do { ; } while __lzo_cte(1) # endif #endif #if !defined(__lzo_unreachable) #if (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x020800ul)) && lzo_has_builtin(__builtin_unreachable) # define __lzo_unreachable() __builtin_unreachable(); #elif (LZO_CC_GNUC >= 0x040500ul) # define __lzo_unreachable() __builtin_unreachable(); #elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 1300)) && 1 # define __lzo_unreachable() __builtin_unreachable(); #endif #endif #if defined(__lzo_unreachable) # ifndef __lzo_HAVE_unreachable # define __lzo_HAVE_unreachable 1 # endif #else # if 0 # define __lzo_unreachable() ((void)0); # else # define __lzo_unreachable() __lzo_loop_forever(); # endif #endif #if !defined(lzo_unused_funcs_impl) # if 1 && (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || (LZO_CC_GNUC >= 0x020700ul) || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) # define lzo_unused_funcs_impl(r,f) static r __attribute__((__unused__)) f # elif 1 && (LZO_CC_BORLANDC || LZO_CC_GNUC) # define lzo_unused_funcs_impl(r,f) static r f # else # define lzo_unused_funcs_impl(r,f) __lzo_static_forceinline r f # endif #endif #ifndef __LZO_CTA_NAME #if (LZO_CFG_USE_COUNTER) # define __LZO_CTA_NAME(a) LZO_PP_ECONCAT2(a,__COUNTER__) #else # define __LZO_CTA_NAME(a) LZO_PP_ECONCAT2(a,__LINE__) #endif #endif #if !defined(LZO_COMPILE_TIME_ASSERT_HEADER) # if (LZO_CC_AZTECC || LZO_CC_ZORTECHC) # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-!(e)]; LZO_EXTERN_C_END # elif (LZO_CC_DMC || LZO_CC_SYMANTECC) # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1u-2*!(e)]; LZO_EXTERN_C_END # elif (LZO_CC_TURBOC && (__TURBOC__ == 0x0295)) # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-!(e)]; LZO_EXTERN_C_END # elif (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020900ul)) && defined(__cplusplus) # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN int __LZO_CTA_NAME(lzo_cta_f__)(int [1-2*!(e)]); LZO_EXTERN_C_END # elif (LZO_CC_GNUC) && defined(__CHECKER__) && defined(__SPARSE_CHECKER__) # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN enum {__LZO_CTA_NAME(lzo_cta_e__)=1/!!(e)} __attribute__((__unused__)); LZO_EXTERN_C_END # else # define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-2*!(e)]; LZO_EXTERN_C_END # endif #endif #if !defined(LZO_COMPILE_TIME_ASSERT) # if (LZO_CC_AZTECC) # define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-!(e)];} # elif (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x030000ul)) # define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)] __attribute__((__unused__));} # elif (LZO_CC_DMC || LZO_CC_PACIFICC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) # define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; # elif (LZO_CC_GNUC) && defined(__CHECKER__) && defined(__SPARSE_CHECKER__) # define LZO_COMPILE_TIME_ASSERT(e) {(void) (0/!!(e));} # elif (LZO_CC_GNUC >= 0x040700ul) && (LZO_CFG_USE_COUNTER) && defined(__cplusplus) # define LZO_COMPILE_TIME_ASSERT(e) {enum {__LZO_CTA_NAME(lzo_cta_e__)=1/!!(e)} __attribute__((__unused__));} # elif (LZO_CC_GNUC >= 0x040700ul) # define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)] __attribute__((__unused__));} # elif (LZO_CC_MSC && (_MSC_VER < 900)) # define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; # elif (LZO_CC_TURBOC && (__TURBOC__ == 0x0295)) # define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; # else # define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)];} # endif #endif LZO_COMPILE_TIME_ASSERT_HEADER(1 == 1) #if defined(__cplusplus) extern "C" { LZO_COMPILE_TIME_ASSERT_HEADER(2 == 2) } #endif LZO_COMPILE_TIME_ASSERT_HEADER(3 == 3) #if (LZO_ARCH_I086 || LZO_ARCH_I386) && (LZO_OS_DOS16 || LZO_OS_DOS32 || LZO_OS_OS2 || LZO_OS_OS216 || LZO_OS_WIN16 || LZO_OS_WIN32 || LZO_OS_WIN64) # if (LZO_CC_GNUC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PACIFICC) # elif (LZO_CC_DMC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) # define __lzo_cdecl __cdecl # define __lzo_cdecl_atexit /*empty*/ # define __lzo_cdecl_main __cdecl # if (LZO_OS_OS2 && (LZO_CC_DMC || LZO_CC_SYMANTECC)) # define __lzo_cdecl_qsort __pascal # elif (LZO_OS_OS2 && (LZO_CC_ZORTECHC)) # define __lzo_cdecl_qsort _stdcall # else # define __lzo_cdecl_qsort __cdecl # endif # elif (LZO_CC_WATCOMC) # define __lzo_cdecl __cdecl # else # define __lzo_cdecl __cdecl # define __lzo_cdecl_atexit __cdecl # define __lzo_cdecl_main __cdecl # define __lzo_cdecl_qsort __cdecl # endif # if (LZO_CC_GNUC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PACIFICC || LZO_CC_WATCOMC) # elif (LZO_OS_OS2 && (LZO_CC_DMC || LZO_CC_SYMANTECC)) # define __lzo_cdecl_sighandler __pascal # elif (LZO_OS_OS2 && (LZO_CC_ZORTECHC)) # define __lzo_cdecl_sighandler _stdcall # elif (LZO_CC_MSC && (_MSC_VER >= 1400)) && defined(_M_CEE_PURE) # define __lzo_cdecl_sighandler __clrcall # elif (LZO_CC_MSC && (_MSC_VER >= 600 && _MSC_VER < 700)) # if defined(_DLL) # define __lzo_cdecl_sighandler _far _cdecl _loadds # elif defined(_MT) # define __lzo_cdecl_sighandler _far _cdecl # else # define __lzo_cdecl_sighandler _cdecl # endif # else # define __lzo_cdecl_sighandler __cdecl # endif #elif (LZO_ARCH_I386) && (LZO_CC_WATCOMC) # define __lzo_cdecl __cdecl #elif (LZO_ARCH_M68K && LZO_OS_TOS && (LZO_CC_PUREC || LZO_CC_TURBOC)) # define __lzo_cdecl cdecl #endif #if !defined(__lzo_cdecl) # define __lzo_cdecl /*empty*/ #endif #if !defined(__lzo_cdecl_atexit) # define __lzo_cdecl_atexit /*empty*/ #endif #if !defined(__lzo_cdecl_main) # define __lzo_cdecl_main /*empty*/ #endif #if !defined(__lzo_cdecl_qsort) # define __lzo_cdecl_qsort /*empty*/ #endif #if !defined(__lzo_cdecl_sighandler) # define __lzo_cdecl_sighandler /*empty*/ #endif #if !defined(__lzo_cdecl_va) # define __lzo_cdecl_va __lzo_cdecl #endif #if !(LZO_CFG_NO_WINDOWS_H) #if !defined(LZO_HAVE_WINDOWS_H) #if (LZO_OS_CYGWIN || (LZO_OS_EMX && defined(__RSXNT__)) || LZO_OS_WIN32 || LZO_OS_WIN64) # if (LZO_CC_WATCOMC && (__WATCOMC__ < 1000)) # elif ((LZO_OS_WIN32 && defined(__PW32__)) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x030000ul))) # elif ((LZO_OS_CYGWIN || defined(__MINGW32__)) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x025f00ul))) # else # define LZO_HAVE_WINDOWS_H 1 # endif #endif #endif #endif #ifndef LZO_SIZEOF_SHORT #if defined(SIZEOF_SHORT) # define LZO_SIZEOF_SHORT (SIZEOF_SHORT) #elif defined(__SIZEOF_SHORT__) # define LZO_SIZEOF_SHORT (__SIZEOF_SHORT__) #endif #endif #ifndef LZO_SIZEOF_INT #if defined(SIZEOF_INT) # define LZO_SIZEOF_INT (SIZEOF_INT) #elif defined(__SIZEOF_INT__) # define LZO_SIZEOF_INT (__SIZEOF_INT__) #endif #endif #ifndef LZO_SIZEOF_LONG #if defined(SIZEOF_LONG) # define LZO_SIZEOF_LONG (SIZEOF_LONG) #elif defined(__SIZEOF_LONG__) # define LZO_SIZEOF_LONG (__SIZEOF_LONG__) #endif #endif #ifndef LZO_SIZEOF_LONG_LONG #if defined(SIZEOF_LONG_LONG) # define LZO_SIZEOF_LONG_LONG (SIZEOF_LONG_LONG) #elif defined(__SIZEOF_LONG_LONG__) # define LZO_SIZEOF_LONG_LONG (__SIZEOF_LONG_LONG__) #endif #endif #ifndef LZO_SIZEOF___INT16 #if defined(SIZEOF___INT16) # define LZO_SIZEOF___INT16 (SIZEOF___INT16) #endif #endif #ifndef LZO_SIZEOF___INT32 #if defined(SIZEOF___INT32) # define LZO_SIZEOF___INT32 (SIZEOF___INT32) #endif #endif #ifndef LZO_SIZEOF___INT64 #if defined(SIZEOF___INT64) # define LZO_SIZEOF___INT64 (SIZEOF___INT64) #endif #endif #ifndef LZO_SIZEOF_VOID_P #if defined(SIZEOF_VOID_P) # define LZO_SIZEOF_VOID_P (SIZEOF_VOID_P) #elif defined(__SIZEOF_POINTER__) # define LZO_SIZEOF_VOID_P (__SIZEOF_POINTER__) #endif #endif #ifndef LZO_SIZEOF_SIZE_T #if defined(SIZEOF_SIZE_T) # define LZO_SIZEOF_SIZE_T (SIZEOF_SIZE_T) #elif defined(__SIZEOF_SIZE_T__) # define LZO_SIZEOF_SIZE_T (__SIZEOF_SIZE_T__) #endif #endif #ifndef LZO_SIZEOF_PTRDIFF_T #if defined(SIZEOF_PTRDIFF_T) # define LZO_SIZEOF_PTRDIFF_T (SIZEOF_PTRDIFF_T) #elif defined(__SIZEOF_PTRDIFF_T__) # define LZO_SIZEOF_PTRDIFF_T (__SIZEOF_PTRDIFF_T__) #endif #endif #define __LZO_LSR(x,b) (((x)+0ul) >> (b)) #if !defined(LZO_SIZEOF_SHORT) # if (LZO_ARCH_CRAY_PVP) # define LZO_SIZEOF_SHORT 8 # elif (USHRT_MAX == LZO_0xffffL) # define LZO_SIZEOF_SHORT 2 # elif (__LZO_LSR(USHRT_MAX,7) == 1) # define LZO_SIZEOF_SHORT 1 # elif (__LZO_LSR(USHRT_MAX,15) == 1) # define LZO_SIZEOF_SHORT 2 # elif (__LZO_LSR(USHRT_MAX,31) == 1) # define LZO_SIZEOF_SHORT 4 # elif (__LZO_LSR(USHRT_MAX,63) == 1) # define LZO_SIZEOF_SHORT 8 # elif (__LZO_LSR(USHRT_MAX,127) == 1) # define LZO_SIZEOF_SHORT 16 # else # error "LZO_SIZEOF_SHORT" # endif #endif LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_SHORT == sizeof(short)) #if !defined(LZO_SIZEOF_INT) # if (LZO_ARCH_CRAY_PVP) # define LZO_SIZEOF_INT 8 # elif (UINT_MAX == LZO_0xffffL) # define LZO_SIZEOF_INT 2 # elif (UINT_MAX == LZO_0xffffffffL) # define LZO_SIZEOF_INT 4 # elif (__LZO_LSR(UINT_MAX,7) == 1) # define LZO_SIZEOF_INT 1 # elif (__LZO_LSR(UINT_MAX,15) == 1) # define LZO_SIZEOF_INT 2 # elif (__LZO_LSR(UINT_MAX,31) == 1) # define LZO_SIZEOF_INT 4 # elif (__LZO_LSR(UINT_MAX,63) == 1) # define LZO_SIZEOF_INT 8 # elif (__LZO_LSR(UINT_MAX,127) == 1) # define LZO_SIZEOF_INT 16 # else # error "LZO_SIZEOF_INT" # endif #endif LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_INT == sizeof(int)) #if !defined(LZO_SIZEOF_LONG) # if (ULONG_MAX == LZO_0xffffffffL) # define LZO_SIZEOF_LONG 4 # elif (__LZO_LSR(ULONG_MAX,7) == 1) # define LZO_SIZEOF_LONG 1 # elif (__LZO_LSR(ULONG_MAX,15) == 1) # define LZO_SIZEOF_LONG 2 # elif (__LZO_LSR(ULONG_MAX,31) == 1) # define LZO_SIZEOF_LONG 4 # elif (__LZO_LSR(ULONG_MAX,39) == 1) # define LZO_SIZEOF_LONG 5 # elif (__LZO_LSR(ULONG_MAX,63) == 1) # define LZO_SIZEOF_LONG 8 # elif (__LZO_LSR(ULONG_MAX,127) == 1) # define LZO_SIZEOF_LONG 16 # else # error "LZO_SIZEOF_LONG" # endif #endif LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_LONG == sizeof(long)) #if !defined(LZO_SIZEOF_LONG_LONG) && !defined(LZO_SIZEOF___INT64) #if (LZO_SIZEOF_LONG > 0 && LZO_SIZEOF_LONG < 8) # if defined(__LONG_MAX__) && defined(__LONG_LONG_MAX__) # if (LZO_CC_GNUC >= 0x030300ul) # if ((__LONG_MAX__-0) == (__LONG_LONG_MAX__-0)) # define LZO_SIZEOF_LONG_LONG LZO_SIZEOF_LONG # elif (__LZO_LSR(__LONG_LONG_MAX__,30) == 1) # define LZO_SIZEOF_LONG_LONG 4 # endif # endif # endif #endif #endif #if !defined(LZO_SIZEOF_LONG_LONG) && !defined(LZO_SIZEOF___INT64) #if (LZO_SIZEOF_LONG > 0 && LZO_SIZEOF_LONG < 8) #if (LZO_ARCH_I086 && LZO_CC_DMC) #elif (LZO_CC_CILLY) && defined(__GNUC__) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) # define LZO_SIZEOF_LONG_LONG 8 #elif ((LZO_OS_WIN32 || LZO_OS_WIN64 || defined(_WIN32)) && LZO_CC_MSC && (_MSC_VER >= 1400)) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_OS_WIN64 || defined(_WIN64)) # define LZO_SIZEOF___INT64 8 #elif (LZO_ARCH_I386 && (LZO_CC_DMC)) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_ARCH_I386 && (LZO_CC_SYMANTECC && (__SC__ >= 0x700))) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_ARCH_I386 && (LZO_CC_INTELC && defined(__linux__))) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_ARCH_I386 && (LZO_CC_MWERKS || LZO_CC_PELLESC || LZO_CC_PGI || LZO_CC_SUNPROC)) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_ARCH_I386 && (LZO_CC_INTELC || LZO_CC_MSC)) # define LZO_SIZEOF___INT64 8 #elif ((LZO_OS_WIN32 || defined(_WIN32)) && (LZO_CC_MSC)) # define LZO_SIZEOF___INT64 8 #elif (LZO_ARCH_I386 && (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0520))) # define LZO_SIZEOF___INT64 8 #elif (LZO_ARCH_I386 && (LZO_CC_WATCOMC && (__WATCOMC__ >= 1100))) # define LZO_SIZEOF___INT64 8 #elif (LZO_CC_GHS && defined(__LLONG_BIT) && ((__LLONG_BIT-0) == 64)) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_CC_WATCOMC && defined(_INTEGRAL_MAX_BITS) && ((_INTEGRAL_MAX_BITS-0) == 64)) # define LZO_SIZEOF___INT64 8 #elif (LZO_OS_OS400 || defined(__OS400__)) && defined(__LLP64_IFC__) # define LZO_SIZEOF_LONG_LONG 8 #elif (defined(__vms) || defined(__VMS)) && ((__INITIAL_POINTER_SIZE-0) == 64) # define LZO_SIZEOF_LONG_LONG 8 #elif (LZO_CC_SDCC) && (LZO_SIZEOF_INT == 2) #elif 1 && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) # define LZO_SIZEOF_LONG_LONG 8 #endif #endif #endif #if defined(__cplusplus) && (LZO_CC_GNUC) # if (LZO_CC_GNUC < 0x020800ul) # undef LZO_SIZEOF_LONG_LONG # endif #endif #if (LZO_CFG_NO_LONG_LONG) # undef LZO_SIZEOF_LONG_LONG #elif defined(__NO_LONG_LONG) # undef LZO_SIZEOF_LONG_LONG #elif defined(_NO_LONGLONG) # undef LZO_SIZEOF_LONG_LONG #endif #if !defined(LZO_WORDSIZE) #if (LZO_ARCH_ALPHA) # define LZO_WORDSIZE 8 #elif (LZO_ARCH_AMD64) # define LZO_WORDSIZE 8 #elif (LZO_ARCH_AVR) # define LZO_WORDSIZE 1 #elif (LZO_ARCH_H8300) # if defined(__NORMAL_MODE__) # define LZO_WORDSIZE 4 # elif defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) # define LZO_WORDSIZE 4 # else # define LZO_WORDSIZE 2 # endif #elif (LZO_ARCH_I086) # define LZO_WORDSIZE 2 #elif (LZO_ARCH_IA64) # define LZO_WORDSIZE 8 #elif (LZO_ARCH_M16C) # define LZO_WORDSIZE 2 #elif (LZO_ARCH_SPU) # define LZO_WORDSIZE 4 #elif (LZO_ARCH_Z80) # define LZO_WORDSIZE 1 #elif (LZO_SIZEOF_LONG == 8) && ((defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__)) # define LZO_WORDSIZE 8 #elif (LZO_OS_OS400 || defined(__OS400__)) # define LZO_WORDSIZE 8 #elif (defined(__vms) || defined(__VMS)) && (__INITIAL_POINTER_SIZE+0 == 64) # define LZO_WORDSIZE 8 #endif #endif #if !defined(LZO_SIZEOF_VOID_P) #if defined(__ILP32__) || defined(__ILP32) || defined(_ILP32) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(int) == 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 4) # define LZO_SIZEOF_VOID_P 4 #elif defined(__ILP64__) || defined(__ILP64) || defined(_ILP64) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(int) == 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 8) # define LZO_SIZEOF_VOID_P 8 #elif defined(__LLP64__) || defined(__LLP64) || defined(_LLP64) || defined(_WIN64) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 4) # define LZO_SIZEOF_VOID_P 8 #elif defined(__LP64__) || defined(__LP64) || defined(_LP64) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 8) # define LZO_SIZEOF_VOID_P 8 #elif (LZO_ARCH_AVR) # define LZO_SIZEOF_VOID_P 2 #elif (LZO_ARCH_C166 || LZO_ARCH_MCS51 || LZO_ARCH_MCS251 || LZO_ARCH_MSP430) # define LZO_SIZEOF_VOID_P 2 #elif (LZO_ARCH_H8300) # if defined(__NORMAL_MODE__) # define LZO_SIZEOF_VOID_P 2 # elif defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) # define LZO_SIZEOF_VOID_P 4 # else # define LZO_SIZEOF_VOID_P 2 # endif # if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x040000ul)) && (LZO_SIZEOF_INT == 4) # define LZO_SIZEOF_SIZE_T LZO_SIZEOF_INT # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_INT # endif #elif (LZO_ARCH_I086) # if (LZO_MM_TINY || LZO_MM_SMALL || LZO_MM_MEDIUM) # define LZO_SIZEOF_VOID_P 2 # elif (LZO_MM_COMPACT || LZO_MM_LARGE || LZO_MM_HUGE) # define LZO_SIZEOF_VOID_P 4 # else # error "invalid LZO_ARCH_I086 memory model" # endif #elif (LZO_ARCH_M16C) # if defined(__m32c_cpu__) || defined(__m32cm_cpu__) # define LZO_SIZEOF_VOID_P 4 # else # define LZO_SIZEOF_VOID_P 2 # endif #elif (LZO_ARCH_SPU) # define LZO_SIZEOF_VOID_P 4 #elif (LZO_ARCH_Z80) # define LZO_SIZEOF_VOID_P 2 #elif (LZO_SIZEOF_LONG == 8) && ((defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__)) # define LZO_SIZEOF_VOID_P 4 #elif (LZO_OS_OS400 || defined(__OS400__)) # if defined(__LLP64_IFC__) # define LZO_SIZEOF_VOID_P 8 # define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG # else # define LZO_SIZEOF_VOID_P 16 # define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG # endif #elif (defined(__vms) || defined(__VMS)) && (__INITIAL_POINTER_SIZE+0 == 64) # define LZO_SIZEOF_VOID_P 8 # define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG #endif #endif #if !defined(LZO_SIZEOF_VOID_P) # define LZO_SIZEOF_VOID_P LZO_SIZEOF_LONG #endif LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_VOID_P == sizeof(void *)) #if !defined(LZO_SIZEOF_SIZE_T) #if (LZO_ARCH_I086 || LZO_ARCH_M16C) # define LZO_SIZEOF_SIZE_T 2 #endif #endif #if !defined(LZO_SIZEOF_SIZE_T) # define LZO_SIZEOF_SIZE_T LZO_SIZEOF_VOID_P #endif #if defined(offsetof) LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_SIZE_T == sizeof(size_t)) #endif #if !defined(LZO_SIZEOF_PTRDIFF_T) #if (LZO_ARCH_I086) # if (LZO_MM_TINY || LZO_MM_SMALL || LZO_MM_MEDIUM || LZO_MM_HUGE) # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_VOID_P # elif (LZO_MM_COMPACT || LZO_MM_LARGE) # if (LZO_CC_BORLANDC || LZO_CC_TURBOC) # define LZO_SIZEOF_PTRDIFF_T 4 # else # define LZO_SIZEOF_PTRDIFF_T 2 # endif # else # error "invalid LZO_ARCH_I086 memory model" # endif #endif #endif #if !defined(LZO_SIZEOF_PTRDIFF_T) # define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_SIZE_T #endif #if defined(offsetof) LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_PTRDIFF_T == sizeof(ptrdiff_t)) #endif #if !defined(LZO_WORDSIZE) # define LZO_WORDSIZE LZO_SIZEOF_VOID_P #endif #if (LZO_ABI_NEUTRAL_ENDIAN) # undef LZO_ABI_BIG_ENDIAN # undef LZO_ABI_LITTLE_ENDIAN #elif !(LZO_ABI_BIG_ENDIAN) && !(LZO_ABI_LITTLE_ENDIAN) #if (LZO_ARCH_ALPHA) && (LZO_ARCH_CRAY_MPP) # define LZO_ABI_BIG_ENDIAN 1 #elif (LZO_ARCH_IA64) && (LZO_OS_POSIX_LINUX || LZO_OS_WIN64) # define LZO_ABI_LITTLE_ENDIAN 1 #elif (LZO_ARCH_ALPHA || LZO_ARCH_AMD64 || LZO_ARCH_BLACKFIN || LZO_ARCH_CRIS || LZO_ARCH_I086 || LZO_ARCH_I386 || LZO_ARCH_MSP430) # define LZO_ABI_LITTLE_ENDIAN 1 #elif (LZO_ARCH_AVR32 || LZO_ARCH_M68K || LZO_ARCH_S390 || LZO_ARCH_SPU) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && defined(__IAR_SYSTEMS_ICC__) && defined(__LITTLE_ENDIAN__) # if (__LITTLE_ENDIAN__ == 1) # define LZO_ABI_LITTLE_ENDIAN 1 # else # define LZO_ABI_BIG_ENDIAN 1 # endif #elif 1 && defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) # define LZO_ABI_LITTLE_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM) && defined(__ARM_BIG_ENDIAN) && ((__ARM_BIG_ENDIAN)+0) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM) && defined(__ARMEB__) && !defined(__ARMEL__) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM) && defined(__ARMEL__) && !defined(__ARMEB__) # define LZO_ABI_LITTLE_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM && LZO_CC_ARMCC_ARMCC) # if defined(__BIG_ENDIAN) && defined(__LITTLE_ENDIAN) # error "unexpected configuration - check your compiler defines" # elif defined(__BIG_ENDIAN) # define LZO_ABI_BIG_ENDIAN 1 # else # define LZO_ABI_LITTLE_ENDIAN 1 # endif # define LZO_ABI_LITTLE_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM64) && defined(__ARM_BIG_ENDIAN) && ((__ARM_BIG_ENDIAN)+0) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM64) && defined(__AARCH64EB__) && !defined(__AARCH64EL__) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && (LZO_ARCH_ARM64) && defined(__AARCH64EL__) && !defined(__AARCH64EB__) # define LZO_ABI_LITTLE_ENDIAN 1 #elif 1 && (LZO_ARCH_MIPS) && defined(__MIPSEB__) && !defined(__MIPSEL__) # define LZO_ABI_BIG_ENDIAN 1 #elif 1 && (LZO_ARCH_MIPS) && defined(__MIPSEL__) && !defined(__MIPSEB__) # define LZO_ABI_LITTLE_ENDIAN 1 #endif #endif #if (LZO_ABI_BIG_ENDIAN) && (LZO_ABI_LITTLE_ENDIAN) # error "unexpected configuration - check your compiler defines" #endif #if (LZO_ABI_BIG_ENDIAN) # define LZO_INFO_ABI_ENDIAN "be" #elif (LZO_ABI_LITTLE_ENDIAN) # define LZO_INFO_ABI_ENDIAN "le" #elif (LZO_ABI_NEUTRAL_ENDIAN) # define LZO_INFO_ABI_ENDIAN "neutral" #endif #if (LZO_SIZEOF_INT == 1 && LZO_SIZEOF_LONG == 2 && LZO_SIZEOF_VOID_P == 2) # define LZO_ABI_I8LP16 1 # define LZO_INFO_ABI_PM "i8lp16" #elif (LZO_SIZEOF_INT == 2 && LZO_SIZEOF_LONG == 2 && LZO_SIZEOF_VOID_P == 2) # define LZO_ABI_ILP16 1 # define LZO_INFO_ABI_PM "ilp16" #elif (LZO_SIZEOF_INT == 2 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 4) # define LZO_ABI_LP32 1 # define LZO_INFO_ABI_PM "lp32" #elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 4) # define LZO_ABI_ILP32 1 # define LZO_INFO_ABI_PM "ilp32" #elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 8 && LZO_SIZEOF_SIZE_T == 8) # define LZO_ABI_LLP64 1 # define LZO_INFO_ABI_PM "llp64" #elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 8) # define LZO_ABI_LP64 1 # define LZO_INFO_ABI_PM "lp64" #elif (LZO_SIZEOF_INT == 8 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 8) # define LZO_ABI_ILP64 1 # define LZO_INFO_ABI_PM "ilp64" #elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 4) # define LZO_ABI_IP32L64 1 # define LZO_INFO_ABI_PM "ip32l64" #endif #if 0 #elif !defined(__LZO_LIBC_OVERRIDE) #if (LZO_LIBC_NAKED) # define LZO_INFO_LIBC "naked" #elif (LZO_LIBC_FREESTANDING) # define LZO_INFO_LIBC "freestanding" #elif (LZO_LIBC_MOSTLY_FREESTANDING) # define LZO_INFO_LIBC "mfreestanding" #elif (LZO_LIBC_ISOC90) # define LZO_INFO_LIBC "isoc90" #elif (LZO_LIBC_ISOC99) # define LZO_INFO_LIBC "isoc99" #elif (LZO_CC_ARMCC_ARMCC) && defined(__ARMCLIB_VERSION) # define LZO_LIBC_ISOC90 1 # define LZO_INFO_LIBC "isoc90" #elif defined(__dietlibc__) # define LZO_LIBC_DIETLIBC 1 # define LZO_INFO_LIBC "dietlibc" #elif defined(_NEWLIB_VERSION) # define LZO_LIBC_NEWLIB 1 # define LZO_INFO_LIBC "newlib" #elif defined(__UCLIBC__) && defined(__UCLIBC_MAJOR__) && defined(__UCLIBC_MINOR__) # if defined(__UCLIBC_SUBLEVEL__) # define LZO_LIBC_UCLIBC (__UCLIBC_MAJOR__ * 0x10000L + (__UCLIBC_MINOR__-0) * 0x100 + (__UCLIBC_SUBLEVEL__-0)) # else # define LZO_LIBC_UCLIBC 0x00090bL # endif # define LZO_INFO_LIBC "uc" "libc" #elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) # define LZO_LIBC_GLIBC (__GLIBC__ * 0x10000L + (__GLIBC_MINOR__-0) * 0x100) # define LZO_INFO_LIBC "glibc" #elif (LZO_CC_MWERKS) && defined(__MSL__) # define LZO_LIBC_MSL __MSL__ # define LZO_INFO_LIBC "msl" #elif 1 && defined(__IAR_SYSTEMS_ICC__) # define LZO_LIBC_ISOC90 1 # define LZO_INFO_LIBC "isoc90" #else # define LZO_LIBC_DEFAULT 1 # define LZO_INFO_LIBC "default" #endif #endif #if (LZO_ARCH_I386 && (LZO_OS_DOS32 || LZO_OS_WIN32) && (LZO_CC_DMC || LZO_CC_INTELC || LZO_CC_MSC || LZO_CC_PELLESC)) # define LZO_ASM_SYNTAX_MSC 1 #elif (LZO_OS_WIN64 && (LZO_CC_DMC || LZO_CC_INTELC || LZO_CC_MSC || LZO_CC_PELLESC)) #elif (LZO_ARCH_I386 && LZO_CC_GNUC && (LZO_CC_GNUC == 0x011f00ul)) #elif (LZO_ARCH_I386 && (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC || LZO_CC_PATHSCALE)) # define LZO_ASM_SYNTAX_GNUC 1 #elif (LZO_ARCH_AMD64 && (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC || LZO_CC_PATHSCALE)) # define LZO_ASM_SYNTAX_GNUC 1 #elif (LZO_CC_GNUC) # define LZO_ASM_SYNTAX_GNUC 1 #endif #if (LZO_ASM_SYNTAX_GNUC) #if (LZO_ARCH_I386 && LZO_CC_GNUC && (LZO_CC_GNUC < 0x020000ul)) # define __LZO_ASM_CLOBBER "ax" # define __LZO_ASM_CLOBBER_LIST_CC /*empty*/ # define __LZO_ASM_CLOBBER_LIST_CC_MEMORY /*empty*/ # define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ #elif (LZO_CC_INTELC && (__INTEL_COMPILER < 1000)) # define __LZO_ASM_CLOBBER "memory" # define __LZO_ASM_CLOBBER_LIST_CC /*empty*/ # define __LZO_ASM_CLOBBER_LIST_CC_MEMORY : "memory" # define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ #else # define __LZO_ASM_CLOBBER "cc", "memory" # define __LZO_ASM_CLOBBER_LIST_CC : "cc" # define __LZO_ASM_CLOBBER_LIST_CC_MEMORY : "cc", "memory" # define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ #endif #endif #if (LZO_ARCH_ALPHA) # define LZO_OPT_AVOID_UINT_INDEX 1 #elif (LZO_ARCH_AMD64) # define LZO_OPT_AVOID_INT_INDEX 1 # define LZO_OPT_AVOID_UINT_INDEX 1 # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # ifndef LZO_OPT_UNALIGNED64 # define LZO_OPT_UNALIGNED64 1 # endif #elif (LZO_ARCH_ARM) # if defined(__ARM_FEATURE_UNALIGNED) # if ((__ARM_FEATURE_UNALIGNED)+0) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # endif # elif 1 && (LZO_ARCH_ARM_THUMB2) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # elif 1 && defined(__TARGET_ARCH_ARM) && ((__TARGET_ARCH_ARM)+0 >= 7) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # elif 1 && defined(__TARGET_ARCH_ARM) && ((__TARGET_ARCH_ARM)+0 >= 6) && (defined(__TARGET_PROFILE_A) || defined(__TARGET_PROFILE_R)) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # endif #elif (LZO_ARCH_ARM64) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # ifndef LZO_OPT_UNALIGNED64 # define LZO_OPT_UNALIGNED64 1 # endif #elif (LZO_ARCH_CRIS) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif #elif (LZO_ARCH_I386) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif #elif (LZO_ARCH_IA64) # define LZO_OPT_AVOID_INT_INDEX 1 # define LZO_OPT_AVOID_UINT_INDEX 1 # define LZO_OPT_PREFER_POSTINC 1 #elif (LZO_ARCH_M68K) # define LZO_OPT_PREFER_POSTINC 1 # define LZO_OPT_PREFER_PREDEC 1 # if defined(__mc68020__) && !defined(__mcoldfire__) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # endif #elif (LZO_ARCH_MIPS) # define LZO_OPT_AVOID_UINT_INDEX 1 #elif (LZO_ARCH_POWERPC) # define LZO_OPT_PREFER_PREINC 1 # define LZO_OPT_PREFER_PREDEC 1 # if (LZO_ABI_BIG_ENDIAN) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # if (LZO_WORDSIZE == 8) # ifndef LZO_OPT_UNALIGNED64 # define LZO_OPT_UNALIGNED64 1 # endif # endif # endif #elif (LZO_ARCH_S390) # ifndef LZO_OPT_UNALIGNED16 # define LZO_OPT_UNALIGNED16 1 # endif # ifndef LZO_OPT_UNALIGNED32 # define LZO_OPT_UNALIGNED32 1 # endif # if (LZO_WORDSIZE == 8) # ifndef LZO_OPT_UNALIGNED64 # define LZO_OPT_UNALIGNED64 1 # endif # endif #elif (LZO_ARCH_SH) # define LZO_OPT_PREFER_POSTINC 1 # define LZO_OPT_PREFER_PREDEC 1 #endif #ifndef LZO_CFG_NO_INLINE_ASM #if (LZO_ABI_NEUTRAL_ENDIAN) || (LZO_ARCH_GENERIC) # define LZO_CFG_NO_INLINE_ASM 1 #elif (LZO_CC_LLVM) # define LZO_CFG_NO_INLINE_ASM 1 #endif #endif #if (LZO_CFG_NO_INLINE_ASM) # undef LZO_ASM_SYNTAX_MSC # undef LZO_ASM_SYNTAX_GNUC # undef __LZO_ASM_CLOBBER # undef __LZO_ASM_CLOBBER_LIST_CC # undef __LZO_ASM_CLOBBER_LIST_CC_MEMORY # undef __LZO_ASM_CLOBBER_LIST_EMPTY #endif #ifndef LZO_CFG_NO_UNALIGNED #if (LZO_ABI_NEUTRAL_ENDIAN) || (LZO_ARCH_GENERIC) # define LZO_CFG_NO_UNALIGNED 1 #endif #endif #if (LZO_CFG_NO_UNALIGNED) # undef LZO_OPT_UNALIGNED16 # undef LZO_OPT_UNALIGNED32 # undef LZO_OPT_UNALIGNED64 #endif #if defined(__LZO_INFOSTR_MM) #elif (LZO_MM_FLAT) && (defined(__LZO_INFOSTR_PM) || defined(LZO_INFO_ABI_PM)) # define __LZO_INFOSTR_MM "" #elif defined(LZO_INFO_MM) # define __LZO_INFOSTR_MM "." LZO_INFO_MM #else # define __LZO_INFOSTR_MM "" #endif #if defined(__LZO_INFOSTR_PM) #elif defined(LZO_INFO_ABI_PM) # define __LZO_INFOSTR_PM "." LZO_INFO_ABI_PM #else # define __LZO_INFOSTR_PM "" #endif #if defined(__LZO_INFOSTR_ENDIAN) #elif defined(LZO_INFO_ABI_ENDIAN) # define __LZO_INFOSTR_ENDIAN "." LZO_INFO_ABI_ENDIAN #else # define __LZO_INFOSTR_ENDIAN "" #endif #if defined(__LZO_INFOSTR_OSNAME) #elif defined(LZO_INFO_OS_CONSOLE) # define __LZO_INFOSTR_OSNAME LZO_INFO_OS "." LZO_INFO_OS_CONSOLE #elif defined(LZO_INFO_OS_POSIX) # define __LZO_INFOSTR_OSNAME LZO_INFO_OS "." LZO_INFO_OS_POSIX #else # define __LZO_INFOSTR_OSNAME LZO_INFO_OS #endif #if defined(__LZO_INFOSTR_LIBC) #elif defined(LZO_INFO_LIBC) # define __LZO_INFOSTR_LIBC "." LZO_INFO_LIBC #else # define __LZO_INFOSTR_LIBC "" #endif #if defined(__LZO_INFOSTR_CCVER) #elif defined(LZO_INFO_CCVER) # define __LZO_INFOSTR_CCVER " " LZO_INFO_CCVER #else # define __LZO_INFOSTR_CCVER "" #endif #define LZO_INFO_STRING \ LZO_INFO_ARCH __LZO_INFOSTR_MM __LZO_INFOSTR_PM __LZO_INFOSTR_ENDIAN \ " " __LZO_INFOSTR_OSNAME __LZO_INFOSTR_LIBC " " LZO_INFO_CC __LZO_INFOSTR_CCVER #if !(LZO_CFG_SKIP_LZO_TYPES) #if (!(LZO_SIZEOF_SHORT+0 > 0 && LZO_SIZEOF_INT+0 > 0 && LZO_SIZEOF_LONG+0 > 0)) # error "missing defines for sizes" #endif #if (!(LZO_SIZEOF_PTRDIFF_T+0 > 0 && LZO_SIZEOF_SIZE_T+0 > 0 && LZO_SIZEOF_VOID_P+0 > 0)) # error "missing defines for sizes" #endif #define LZO_TYPEOF_CHAR 1u #define LZO_TYPEOF_SHORT 2u #define LZO_TYPEOF_INT 3u #define LZO_TYPEOF_LONG 4u #define LZO_TYPEOF_LONG_LONG 5u #define LZO_TYPEOF___INT8 17u #define LZO_TYPEOF___INT16 18u #define LZO_TYPEOF___INT32 19u #define LZO_TYPEOF___INT64 20u #define LZO_TYPEOF___INT128 21u #define LZO_TYPEOF___INT256 22u #define LZO_TYPEOF___MODE_QI 33u #define LZO_TYPEOF___MODE_HI 34u #define LZO_TYPEOF___MODE_SI 35u #define LZO_TYPEOF___MODE_DI 36u #define LZO_TYPEOF___MODE_TI 37u #define LZO_TYPEOF_CHAR_P 129u #if !defined(lzo_llong_t) #if (LZO_SIZEOF_LONG_LONG+0 > 0) __lzo_gnuc_extension__ typedef long long lzo_llong_t__; __lzo_gnuc_extension__ typedef unsigned long long lzo_ullong_t__; # define lzo_llong_t lzo_llong_t__ # define lzo_ullong_t lzo_ullong_t__ #endif #endif #if !defined(lzo_int16e_t) #if (LZO_SIZEOF_LONG == 2) # define lzo_int16e_t long # define lzo_uint16e_t unsigned long # define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_LONG #elif (LZO_SIZEOF_INT == 2) # define lzo_int16e_t int # define lzo_uint16e_t unsigned int # define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_INT #elif (LZO_SIZEOF_SHORT == 2) # define lzo_int16e_t short int # define lzo_uint16e_t unsigned short int # define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_SHORT #elif 1 && !(LZO_CFG_TYPE_NO_MODE_HI) && (LZO_CC_CLANG || (LZO_CC_GNUC >= 0x025f00ul) || LZO_CC_LLVM) typedef int lzo_int16e_hi_t__ __attribute__((__mode__(__HI__))); typedef unsigned int lzo_uint16e_hi_t__ __attribute__((__mode__(__HI__))); # define lzo_int16e_t lzo_int16e_hi_t__ # define lzo_uint16e_t lzo_uint16e_hi_t__ # define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF___MODE_HI #elif (LZO_SIZEOF___INT16 == 2) # define lzo_int16e_t __int16 # define lzo_uint16e_t unsigned __int16 # define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF___INT16 #else #endif #endif #if defined(lzo_int16e_t) # define LZO_SIZEOF_LZO_INT16E_T 2 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16e_t) == 2) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16e_t) == LZO_SIZEOF_LZO_INT16E_T) #endif #if !defined(lzo_int32e_t) #if (LZO_SIZEOF_LONG == 4) # define lzo_int32e_t long int # define lzo_uint32e_t unsigned long int # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_LONG #elif (LZO_SIZEOF_INT == 4) # define lzo_int32e_t int # define lzo_uint32e_t unsigned int # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_INT #elif (LZO_SIZEOF_SHORT == 4) # define lzo_int32e_t short int # define lzo_uint32e_t unsigned short int # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_SHORT #elif (LZO_SIZEOF_LONG_LONG == 4) # define lzo_int32e_t lzo_llong_t # define lzo_uint32e_t lzo_ullong_t # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_LONG_LONG #elif 1 && !(LZO_CFG_TYPE_NO_MODE_SI) && (LZO_CC_CLANG || (LZO_CC_GNUC >= 0x025f00ul) || LZO_CC_LLVM) && (__INT_MAX__+0 > 2147483647L) typedef int lzo_int32e_si_t__ __attribute__((__mode__(__SI__))); typedef unsigned int lzo_uint32e_si_t__ __attribute__((__mode__(__SI__))); # define lzo_int32e_t lzo_int32e_si_t__ # define lzo_uint32e_t lzo_uint32e_si_t__ # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___MODE_SI #elif 1 && !(LZO_CFG_TYPE_NO_MODE_SI) && (LZO_CC_GNUC >= 0x025f00ul) && defined(__AVR__) && (__LONG_MAX__+0 == 32767L) typedef int lzo_int32e_si_t__ __attribute__((__mode__(__SI__))); typedef unsigned int lzo_uint32e_si_t__ __attribute__((__mode__(__SI__))); # define lzo_int32e_t lzo_int32e_si_t__ # define lzo_uint32e_t lzo_uint32e_si_t__ # define LZO_INT32_C(c) (c##LL) # define LZO_UINT32_C(c) (c##ULL) # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___MODE_SI #elif (LZO_SIZEOF___INT32 == 4) # define lzo_int32e_t __int32 # define lzo_uint32e_t unsigned __int32 # define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___INT32 #else #endif #endif #if defined(lzo_int32e_t) # define LZO_SIZEOF_LZO_INT32E_T 4 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32e_t) == 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32e_t) == LZO_SIZEOF_LZO_INT32E_T) #endif #if !defined(lzo_int64e_t) #if (LZO_SIZEOF___INT64 == 8) # if (LZO_CC_BORLANDC) && !(LZO_CFG_TYPE_PREFER___INT64) # define LZO_CFG_TYPE_PREFER___INT64 1 # endif #endif #if (LZO_SIZEOF_INT == 8) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) # define lzo_int64e_t int # define lzo_uint64e_t unsigned int # define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_INT #elif (LZO_SIZEOF_LONG == 8) # define lzo_int64e_t long int # define lzo_uint64e_t unsigned long int # define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_LONG #elif (LZO_SIZEOF_LONG_LONG == 8) && !(LZO_CFG_TYPE_PREFER___INT64) # define lzo_int64e_t lzo_llong_t # define lzo_uint64e_t lzo_ullong_t # define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_LONG_LONG # if (LZO_CC_BORLANDC) # define LZO_INT64_C(c) ((c) + 0ll) # define LZO_UINT64_C(c) ((c) + 0ull) # elif 0 # define LZO_INT64_C(c) (__lzo_gnuc_extension__ (c##LL)) # define LZO_UINT64_C(c) (__lzo_gnuc_extension__ (c##ULL)) # else # define LZO_INT64_C(c) (c##LL) # define LZO_UINT64_C(c) (c##ULL) # endif #elif (LZO_SIZEOF___INT64 == 8) # define lzo_int64e_t __int64 # define lzo_uint64e_t unsigned __int64 # define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF___INT64 # if (LZO_CC_BORLANDC) # define LZO_INT64_C(c) ((c) + 0i64) # define LZO_UINT64_C(c) ((c) + 0ui64) # else # define LZO_INT64_C(c) (c##i64) # define LZO_UINT64_C(c) (c##ui64) # endif #else #endif #endif #if defined(lzo_int64e_t) # define LZO_SIZEOF_LZO_INT64E_T 8 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64e_t) == 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64e_t) == LZO_SIZEOF_LZO_INT64E_T) #endif #if !defined(lzo_int32l_t) #if defined(lzo_int32e_t) # define lzo_int32l_t lzo_int32e_t # define lzo_uint32l_t lzo_uint32e_t # define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_LZO_INT32E_T # define LZO_TYPEOF_LZO_INT32L_T LZO_TYPEOF_LZO_INT32E_T #elif (LZO_SIZEOF_INT >= 4) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) # define lzo_int32l_t int # define lzo_uint32l_t unsigned int # define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_INT # define LZO_TYPEOF_LZO_INT32L_T LZO_SIZEOF_INT #elif (LZO_SIZEOF_LONG >= 4) # define lzo_int32l_t long int # define lzo_uint32l_t unsigned long int # define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_LONG # define LZO_TYPEOF_LZO_INT32L_T LZO_SIZEOF_LONG #else # error "lzo_int32l_t" #endif #endif #if 1 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32l_t) >= 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32l_t) == LZO_SIZEOF_LZO_INT32L_T) #endif #if !defined(lzo_int64l_t) #if defined(lzo_int64e_t) # define lzo_int64l_t lzo_int64e_t # define lzo_uint64l_t lzo_uint64e_t # define LZO_SIZEOF_LZO_INT64L_T LZO_SIZEOF_LZO_INT64E_T # define LZO_TYPEOF_LZO_INT64L_T LZO_TYPEOF_LZO_INT64E_T #else #endif #endif #if defined(lzo_int64l_t) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64l_t) >= 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64l_t) == LZO_SIZEOF_LZO_INT64L_T) #endif #if !defined(lzo_int32f_t) #if (LZO_SIZEOF_SIZE_T >= 8) # define lzo_int32f_t lzo_int64l_t # define lzo_uint32f_t lzo_uint64l_t # define LZO_SIZEOF_LZO_INT32F_T LZO_SIZEOF_LZO_INT64L_T # define LZO_TYPEOF_LZO_INT32F_T LZO_TYPEOF_LZO_INT64L_T #else # define lzo_int32f_t lzo_int32l_t # define lzo_uint32f_t lzo_uint32l_t # define LZO_SIZEOF_LZO_INT32F_T LZO_SIZEOF_LZO_INT32L_T # define LZO_TYPEOF_LZO_INT32F_T LZO_TYPEOF_LZO_INT32L_T #endif #endif #if 1 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32f_t) >= 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32f_t) == LZO_SIZEOF_LZO_INT32F_T) #endif #if !defined(lzo_int64f_t) #if defined(lzo_int64l_t) # define lzo_int64f_t lzo_int64l_t # define lzo_uint64f_t lzo_uint64l_t # define LZO_SIZEOF_LZO_INT64F_T LZO_SIZEOF_LZO_INT64L_T # define LZO_TYPEOF_LZO_INT64F_T LZO_TYPEOF_LZO_INT64L_T #else #endif #endif #if defined(lzo_int64f_t) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64f_t) >= 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64f_t) == LZO_SIZEOF_LZO_INT64F_T) #endif #if !defined(lzo_intptr_t) #if 1 && (LZO_OS_OS400 && (LZO_SIZEOF_VOID_P == 16)) # define __LZO_INTPTR_T_IS_POINTER 1 typedef char * lzo_intptr_t; typedef char * lzo_uintptr_t; # define lzo_intptr_t lzo_intptr_t # define lzo_uintptr_t lzo_uintptr_t # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_VOID_P # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_CHAR_P #elif (LZO_CC_MSC && (_MSC_VER >= 1300) && (LZO_SIZEOF_VOID_P == 4) && (LZO_SIZEOF_INT == 4)) typedef __w64 int lzo_intptr_t; typedef __w64 unsigned int lzo_uintptr_t; # define lzo_intptr_t lzo_intptr_t # define lzo_uintptr_t lzo_uintptr_t # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_INT # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_INT #elif (LZO_SIZEOF_SHORT == LZO_SIZEOF_VOID_P) && (LZO_SIZEOF_INT > LZO_SIZEOF_VOID_P) # define lzo_intptr_t short # define lzo_uintptr_t unsigned short # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_SHORT # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_SHORT #elif (LZO_SIZEOF_INT >= LZO_SIZEOF_VOID_P) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) # define lzo_intptr_t int # define lzo_uintptr_t unsigned int # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_INT # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_INT #elif (LZO_SIZEOF_LONG >= LZO_SIZEOF_VOID_P) # define lzo_intptr_t long # define lzo_uintptr_t unsigned long # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_LONG # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_LONG #elif (LZO_SIZEOF_LZO_INT64L_T >= LZO_SIZEOF_VOID_P) # define lzo_intptr_t lzo_int64l_t # define lzo_uintptr_t lzo_uint64l_t # define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_LZO_INT64L_T # define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_LZO_INT64L_T #else # error "lzo_intptr_t" #endif #endif #if 1 LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_intptr_t) >= sizeof(void *)) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_intptr_t) == sizeof(lzo_uintptr_t)) #endif #if !defined(lzo_word_t) #if defined(LZO_WORDSIZE) && (LZO_WORDSIZE+0 > 0) #if (LZO_WORDSIZE == LZO_SIZEOF_LZO_INTPTR_T) && !(__LZO_INTPTR_T_IS_POINTER) # define lzo_word_t lzo_uintptr_t # define lzo_sword_t lzo_intptr_t # define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LZO_INTPTR_T # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_LZO_INTPTR_T #elif (LZO_WORDSIZE == LZO_SIZEOF_LONG) # define lzo_word_t unsigned long # define lzo_sword_t long # define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LONG # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_LONG #elif (LZO_WORDSIZE == LZO_SIZEOF_INT) # define lzo_word_t unsigned int # define lzo_sword_t int # define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_INT # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_INT #elif (LZO_WORDSIZE == LZO_SIZEOF_SHORT) # define lzo_word_t unsigned short # define lzo_sword_t short # define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_SHORT # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_SHORT #elif (LZO_WORDSIZE == 1) # define lzo_word_t unsigned char # define lzo_sword_t signed char # define LZO_SIZEOF_LZO_WORD_T 1 # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_CHAR #elif (LZO_WORDSIZE == LZO_SIZEOF_LZO_INT64L_T) # define lzo_word_t lzo_uint64l_t # define lzo_sword_t lzo_int64l_t # define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LZO_INT64L_T # define LZO_TYPEOF_LZO_WORD_T LZO_SIZEOF_LZO_INT64L_T #elif (LZO_ARCH_SPU) && (LZO_CC_GNUC) #if 0 typedef unsigned lzo_word_t __attribute__((__mode__(__V16QI__))); typedef int lzo_sword_t __attribute__((__mode__(__V16QI__))); # define lzo_word_t lzo_word_t # define lzo_sword_t lzo_sword_t # define LZO_SIZEOF_LZO_WORD_T 16 # define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF___MODE_V16QI #endif #else # error "lzo_word_t" #endif #endif #endif #if 1 && defined(lzo_word_t) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_word_t) == LZO_WORDSIZE) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_sword_t) == LZO_WORDSIZE) #endif #if 1 #define lzo_int8_t signed char #define lzo_uint8_t unsigned char #define LZO_SIZEOF_LZO_INT8_T 1 #define LZO_TYPEOF_LZO_INT8_T LZO_TYPEOF_CHAR LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int8_t) == 1) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int8_t) == sizeof(lzo_uint8_t)) #endif #if defined(lzo_int16e_t) #define lzo_int16_t lzo_int16e_t #define lzo_uint16_t lzo_uint16e_t #define LZO_SIZEOF_LZO_INT16_T LZO_SIZEOF_LZO_INT16E_T #define LZO_TYPEOF_LZO_INT16_T LZO_TYPEOF_LZO_INT16E_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16_t) == 2) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16_t) == sizeof(lzo_uint16_t)) #endif #if defined(lzo_int32e_t) #define lzo_int32_t lzo_int32e_t #define lzo_uint32_t lzo_uint32e_t #define LZO_SIZEOF_LZO_INT32_T LZO_SIZEOF_LZO_INT32E_T #define LZO_TYPEOF_LZO_INT32_T LZO_TYPEOF_LZO_INT32E_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32_t) == 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32_t) == sizeof(lzo_uint32_t)) #endif #if defined(lzo_int64e_t) #define lzo_int64_t lzo_int64e_t #define lzo_uint64_t lzo_uint64e_t #define LZO_SIZEOF_LZO_INT64_T LZO_SIZEOF_LZO_INT64E_T #define LZO_TYPEOF_LZO_INT64_T LZO_TYPEOF_LZO_INT64E_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64_t) == 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64_t) == sizeof(lzo_uint64_t)) #endif #if 1 #define lzo_int_least32_t lzo_int32l_t #define lzo_uint_least32_t lzo_uint32l_t #define LZO_SIZEOF_LZO_INT_LEAST32_T LZO_SIZEOF_LZO_INT32L_T #define LZO_TYPEOF_LZO_INT_LEAST32_T LZO_TYPEOF_LZO_INT32L_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least32_t) >= 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least32_t) == sizeof(lzo_uint_least32_t)) #endif #if defined(lzo_int64l_t) #define lzo_int_least64_t lzo_int64l_t #define lzo_uint_least64_t lzo_uint64l_t #define LZO_SIZEOF_LZO_INT_LEAST64_T LZO_SIZEOF_LZO_INT64L_T #define LZO_TYPEOF_LZO_INT_LEAST64_T LZO_TYPEOF_LZO_INT64L_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least64_t) >= 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least64_t) == sizeof(lzo_uint_least64_t)) #endif #if 1 #define lzo_int_fast32_t lzo_int32f_t #define lzo_uint_fast32_t lzo_uint32f_t #define LZO_SIZEOF_LZO_INT_FAST32_T LZO_SIZEOF_LZO_INT32F_T #define LZO_TYPEOF_LZO_INT_FAST32_T LZO_TYPEOF_LZO_INT32F_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast32_t) >= 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast32_t) == sizeof(lzo_uint_fast32_t)) #endif #if defined(lzo_int64f_t) #define lzo_int_fast64_t lzo_int64f_t #define lzo_uint_fast64_t lzo_uint64f_t #define LZO_SIZEOF_LZO_INT_FAST64_T LZO_SIZEOF_LZO_INT64F_T #define LZO_TYPEOF_LZO_INT_FAST64_T LZO_TYPEOF_LZO_INT64F_T LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast64_t) >= 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast64_t) == sizeof(lzo_uint_fast64_t)) #endif #if !defined(LZO_INT16_C) # if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 2) # define LZO_INT16_C(c) ((c) + 0) # define LZO_UINT16_C(c) ((c) + 0U) # elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 2) # define LZO_INT16_C(c) ((c) + 0L) # define LZO_UINT16_C(c) ((c) + 0UL) # elif (LZO_SIZEOF_INT >= 2) # define LZO_INT16_C(c) (c) # define LZO_UINT16_C(c) (c##U) # elif (LZO_SIZEOF_LONG >= 2) # define LZO_INT16_C(c) (c##L) # define LZO_UINT16_C(c) (c##UL) # else # error "LZO_INT16_C" # endif #endif #if !defined(LZO_INT32_C) # if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 4) # define LZO_INT32_C(c) ((c) + 0) # define LZO_UINT32_C(c) ((c) + 0U) # elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 4) # define LZO_INT32_C(c) ((c) + 0L) # define LZO_UINT32_C(c) ((c) + 0UL) # elif (LZO_SIZEOF_INT >= 4) # define LZO_INT32_C(c) (c) # define LZO_UINT32_C(c) (c##U) # elif (LZO_SIZEOF_LONG >= 4) # define LZO_INT32_C(c) (c##L) # define LZO_UINT32_C(c) (c##UL) # elif (LZO_SIZEOF_LONG_LONG >= 4) # define LZO_INT32_C(c) (c##LL) # define LZO_UINT32_C(c) (c##ULL) # else # error "LZO_INT32_C" # endif #endif #if !defined(LZO_INT64_C) && defined(lzo_int64l_t) # if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 8) # define LZO_INT64_C(c) ((c) + 0) # define LZO_UINT64_C(c) ((c) + 0U) # elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 8) # define LZO_INT64_C(c) ((c) + 0L) # define LZO_UINT64_C(c) ((c) + 0UL) # elif (LZO_SIZEOF_INT >= 8) # define LZO_INT64_C(c) (c) # define LZO_UINT64_C(c) (c##U) # elif (LZO_SIZEOF_LONG >= 8) # define LZO_INT64_C(c) (c##L) # define LZO_UINT64_C(c) (c##UL) # else # error "LZO_INT64_C" # endif #endif #endif #endif #endif #undef LZO_HAVE_CONFIG_H #include "minilzo.h" #if !defined(MINILZO_VERSION) || (MINILZO_VERSION != 0x2090) # error "version mismatch in miniLZO source files" #endif #ifdef MINILZO_HAVE_CONFIG_H # define LZO_HAVE_CONFIG_H 1 #endif #ifndef __LZO_CONF_H #define __LZO_CONF_H 1 #if !defined(__LZO_IN_MINILZO) #if defined(LZO_CFG_FREESTANDING) && (LZO_CFG_FREESTANDING) # define LZO_LIBC_FREESTANDING 1 # define LZO_OS_FREESTANDING 1 #endif #if defined(LZO_CFG_EXTRA_CONFIG_HEADER) # include LZO_CFG_EXTRA_CONFIG_HEADER #endif #if defined(__LZOCONF_H) || defined(__LZOCONF_H_INCLUDED) # error "include this file first" #endif #if defined(LZO_CFG_BUILD_DLL) && (LZO_CFG_BUILD_DLL+0) && !defined(__LZO_EXPORT1) && !defined(__LZO_EXPORT2) && 0 #ifndef __LZODEFS_H_INCLUDED #if defined(LZO_HAVE_CONFIG_H) # include #endif #include #include #include #endif #endif #include #if defined(LZO_CFG_EXTRA_CONFIG_HEADER2) # include LZO_CFG_EXTRA_CONFIG_HEADER2 #endif #endif #if !defined(__LZOCONF_H_INCLUDED) || (LZO_VERSION+0 != 0x2090) # error "version mismatch" #endif #if (LZO_CC_MSC && (_MSC_VER >= 1000 && _MSC_VER < 1100)) # pragma warning(disable: 4702) #endif #if (LZO_CC_MSC && (_MSC_VER >= 1000)) # pragma warning(disable: 4127 4701) # pragma warning(disable: 4514 4710 4711) #endif #if (LZO_CC_MSC && (_MSC_VER >= 1300)) # pragma warning(disable: 4820) #endif #if (LZO_CC_MSC && (_MSC_VER >= 1800)) # pragma warning(disable: 4746) #endif #if (LZO_CC_INTELC && (__INTEL_COMPILER >= 900)) # pragma warning(disable: 1684) #endif #if (LZO_CC_SUNPROC) #if !defined(__cplusplus) # pragma error_messages(off,E_END_OF_LOOP_CODE_NOT_REACHED) # pragma error_messages(off,E_LOOP_NOT_ENTERED_AT_TOP) # pragma error_messages(off,E_STATEMENT_NOT_REACHED) #endif #endif #if !defined(__LZO_NOEXPORT1) # define __LZO_NOEXPORT1 /*empty*/ #endif #if !defined(__LZO_NOEXPORT2) # define __LZO_NOEXPORT2 /*empty*/ #endif #if 1 # define LZO_PUBLIC_DECL(r) LZO_EXTERN(r) #endif #if 1 # define LZO_PUBLIC_IMPL(r) LZO_PUBLIC(r) #endif #if !defined(LZO_LOCAL_DECL) # define LZO_LOCAL_DECL(r) __LZO_EXTERN_C LZO_LOCAL_IMPL(r) #endif #if !defined(LZO_LOCAL_IMPL) # define LZO_LOCAL_IMPL(r) __LZO_NOEXPORT1 r __LZO_NOEXPORT2 __LZO_CDECL #endif #if 1 # define LZO_STATIC_DECL(r) LZO_PRIVATE(r) #endif #if 1 # define LZO_STATIC_IMPL(r) LZO_PRIVATE(r) #endif #if defined(__LZO_IN_MINILZO) || (LZO_CFG_FREESTANDING) #elif 1 # include #else # define LZO_WANT_ACC_INCD_H 1 #endif #if defined(LZO_HAVE_CONFIG_H) # define LZO_CFG_NO_CONFIG_HEADER 1 #endif #if 1 && !defined(LZO_CFG_FREESTANDING) #if 1 && !defined(HAVE_STRING_H) #define HAVE_STRING_H 1 #endif #if 1 && !defined(HAVE_MEMCMP) #define HAVE_MEMCMP 1 #endif #if 1 && !defined(HAVE_MEMCPY) #define HAVE_MEMCPY 1 #endif #if 1 && !defined(HAVE_MEMMOVE) #define HAVE_MEMMOVE 1 #endif #if 1 && !defined(HAVE_MEMSET) #define HAVE_MEMSET 1 #endif #endif #if 1 && defined(HAVE_STRING_H) #include #endif #if 1 || defined(lzo_int8_t) || defined(lzo_uint8_t) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int8_t) == 1) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint8_t) == 1) #endif #if 1 || defined(lzo_int16_t) || defined(lzo_uint16_t) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16_t) == 2) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint16_t) == 2) #endif #if 1 || defined(lzo_int32_t) || defined(lzo_uint32_t) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32_t) == 4) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint32_t) == 4) #endif #if defined(lzo_int64_t) || defined(lzo_uint64_t) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64_t) == 8) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint64_t) == 8) #endif #if (LZO_CFG_FREESTANDING) # undef HAVE_MEMCMP # undef HAVE_MEMCPY # undef HAVE_MEMMOVE # undef HAVE_MEMSET #endif #if !(HAVE_MEMCMP) # undef memcmp # define memcmp(a,b,c) lzo_memcmp(a,b,c) #else # undef lzo_memcmp # define lzo_memcmp(a,b,c) memcmp(a,b,c) #endif #if !(HAVE_MEMCPY) # undef memcpy # define memcpy(a,b,c) lzo_memcpy(a,b,c) #else # undef lzo_memcpy # define lzo_memcpy(a,b,c) memcpy(a,b,c) #endif #if !(HAVE_MEMMOVE) # undef memmove # define memmove(a,b,c) lzo_memmove(a,b,c) #else # undef lzo_memmove # define lzo_memmove(a,b,c) memmove(a,b,c) #endif #if !(HAVE_MEMSET) # undef memset # define memset(a,b,c) lzo_memset(a,b,c) #else # undef lzo_memset # define lzo_memset(a,b,c) memset(a,b,c) #endif #undef NDEBUG #if (LZO_CFG_FREESTANDING) # undef LZO_DEBUG # define NDEBUG 1 # undef assert # define assert(e) ((void)0) #else # if !defined(LZO_DEBUG) # define NDEBUG 1 # endif # include #endif #if 0 && defined(__BOUNDS_CHECKING_ON) # include #else # define BOUNDS_CHECKING_OFF_DURING(stmt) stmt # define BOUNDS_CHECKING_OFF_IN_EXPR(expr) (expr) #endif #if (LZO_CFG_PGO) # undef __lzo_likely # undef __lzo_unlikely # define __lzo_likely(e) (e) # define __lzo_unlikely(e) (e) #endif #undef _ #undef __ #undef ___ #undef ____ #undef _p0 #undef _p1 #undef _p2 #undef _p3 #undef _p4 #undef _s0 #undef _s1 #undef _s2 #undef _s3 #undef _s4 #undef _ww #if 1 # define LZO_BYTE(x) ((unsigned char) (x)) #else # define LZO_BYTE(x) ((unsigned char) ((x) & 0xff)) #endif #define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) #define LZO_MIN(a,b) ((a) <= (b) ? (a) : (b)) #define LZO_MAX3(a,b,c) ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c)) #define LZO_MIN3(a,b,c) ((a) <= (b) ? LZO_MIN(a,c) : LZO_MIN(b,c)) #define lzo_sizeof(type) ((lzo_uint) (sizeof(type))) #define LZO_HIGH(array) ((lzo_uint) (sizeof(array)/sizeof(*(array)))) #define LZO_SIZE(bits) (1u << (bits)) #define LZO_MASK(bits) (LZO_SIZE(bits) - 1) #define LZO_USIZE(bits) ((lzo_uint) 1 << (bits)) #define LZO_UMASK(bits) (LZO_USIZE(bits) - 1) #if !defined(DMUL) #if 0 # define DMUL(a,b) ((lzo_xint) ((lzo_uint32_t)(a) * (lzo_uint32_t)(b))) #else # define DMUL(a,b) ((lzo_xint) ((a) * (b))) #endif #endif #ifndef __LZO_FUNC_H #define __LZO_FUNC_H 1 #if !defined(LZO_BITOPS_USE_ASM_BITSCAN) && !defined(LZO_BITOPS_USE_GNUC_BITSCAN) && !defined(LZO_BITOPS_USE_MSC_BITSCAN) #if 1 && (LZO_ARCH_AMD64) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x040000ul)) && (LZO_ASM_SYNTAX_GNUC) #define LZO_BITOPS_USE_ASM_BITSCAN 1 #elif (LZO_CC_CLANG || (LZO_CC_GNUC >= 0x030400ul) || (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 1000)) || (LZO_CC_LLVM && (!defined(__llvm_tools_version__) || (__llvm_tools_version__+0 >= 0x010500ul)))) #define LZO_BITOPS_USE_GNUC_BITSCAN 1 #elif (LZO_OS_WIN32 || LZO_OS_WIN64) && ((LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 1010)) || (LZO_CC_MSC && (_MSC_VER >= 1400))) #define LZO_BITOPS_USE_MSC_BITSCAN 1 #if (LZO_CC_MSC) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) #include #endif #if (LZO_CC_MSC) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) #pragma intrinsic(_BitScanReverse) #pragma intrinsic(_BitScanForward) #endif #if (LZO_CC_MSC) && (LZO_ARCH_AMD64) #pragma intrinsic(_BitScanReverse64) #pragma intrinsic(_BitScanForward64) #endif #endif #endif __lzo_static_forceinline unsigned lzo_bitops_ctlz32_func(lzo_uint32_t v) { #if (LZO_BITOPS_USE_MSC_BITSCAN) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) unsigned long r; (void) _BitScanReverse(&r, v); return (unsigned) r ^ 31; #define lzo_bitops_ctlz32(v) lzo_bitops_ctlz32_func(v) #elif (LZO_BITOPS_USE_ASM_BITSCAN) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) && (LZO_ASM_SYNTAX_GNUC) lzo_uint32_t r; __asm__("bsr %1,%0" : "=r" (r) : "rm" (v) __LZO_ASM_CLOBBER_LIST_CC); return (unsigned) r ^ 31; #define lzo_bitops_ctlz32(v) lzo_bitops_ctlz32_func(v) #elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_INT == 4) unsigned r; r = (unsigned) __builtin_clz(v); return r; #define lzo_bitops_ctlz32(v) ((unsigned) __builtin_clz(v)) #elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_LONG == 8) && (LZO_WORDSIZE >= 8) unsigned r; r = (unsigned) __builtin_clzl(v); return r ^ 32; #define lzo_bitops_ctlz32(v) (((unsigned) __builtin_clzl(v)) ^ 32) #else LZO_UNUSED(v); return 0; #endif } #if defined(lzo_uint64_t) __lzo_static_forceinline unsigned lzo_bitops_ctlz64_func(lzo_uint64_t v) { #if (LZO_BITOPS_USE_MSC_BITSCAN) && (LZO_ARCH_AMD64) unsigned long r; (void) _BitScanReverse64(&r, v); return (unsigned) r ^ 63; #define lzo_bitops_ctlz64(v) lzo_bitops_ctlz64_func(v) #elif (LZO_BITOPS_USE_ASM_BITSCAN) && (LZO_ARCH_AMD64) && (LZO_ASM_SYNTAX_GNUC) lzo_uint64_t r; __asm__("bsr %1,%0" : "=r" (r) : "rm" (v) __LZO_ASM_CLOBBER_LIST_CC); return (unsigned) r ^ 63; #define lzo_bitops_ctlz64(v) lzo_bitops_ctlz64_func(v) #elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_LONG == 8) && (LZO_WORDSIZE >= 8) unsigned r; r = (unsigned) __builtin_clzl(v); return r; #define lzo_bitops_ctlz64(v) ((unsigned) __builtin_clzl(v)) #elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_LONG_LONG == 8) && (LZO_WORDSIZE >= 8) unsigned r; r = (unsigned) __builtin_clzll(v); return r; #define lzo_bitops_ctlz64(v) ((unsigned) __builtin_clzll(v)) #else LZO_UNUSED(v); return 0; #endif } #endif __lzo_static_forceinline unsigned lzo_bitops_cttz32_func(lzo_uint32_t v) { #if (LZO_BITOPS_USE_MSC_BITSCAN) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) unsigned long r; (void) _BitScanForward(&r, v); return (unsigned) r; #define lzo_bitops_cttz32(v) lzo_bitops_cttz32_func(v) #elif (LZO_BITOPS_USE_ASM_BITSCAN) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) && (LZO_ASM_SYNTAX_GNUC) lzo_uint32_t r; __asm__("bsf %1,%0" : "=r" (r) : "rm" (v) __LZO_ASM_CLOBBER_LIST_CC); return (unsigned) r; #define lzo_bitops_cttz32(v) lzo_bitops_cttz32_func(v) #elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_INT >= 4) unsigned r; r = (unsigned) __builtin_ctz(v); return r; #define lzo_bitops_cttz32(v) ((unsigned) __builtin_ctz(v)) #else LZO_UNUSED(v); return 0; #endif } #if defined(lzo_uint64_t) __lzo_static_forceinline unsigned lzo_bitops_cttz64_func(lzo_uint64_t v) { #if (LZO_BITOPS_USE_MSC_BITSCAN) && (LZO_ARCH_AMD64) unsigned long r; (void) _BitScanForward64(&r, v); return (unsigned) r; #define lzo_bitops_cttz64(v) lzo_bitops_cttz64_func(v) #elif (LZO_BITOPS_USE_ASM_BITSCAN) && (LZO_ARCH_AMD64) && (LZO_ASM_SYNTAX_GNUC) lzo_uint64_t r; __asm__("bsf %1,%0" : "=r" (r) : "rm" (v) __LZO_ASM_CLOBBER_LIST_CC); return (unsigned) r; #define lzo_bitops_cttz64(v) lzo_bitops_cttz64_func(v) #elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_LONG >= 8) && (LZO_WORDSIZE >= 8) unsigned r; r = (unsigned) __builtin_ctzl(v); return r; #define lzo_bitops_cttz64(v) ((unsigned) __builtin_ctzl(v)) #elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_LONG_LONG >= 8) && (LZO_WORDSIZE >= 8) unsigned r; r = (unsigned) __builtin_ctzll(v); return r; #define lzo_bitops_cttz64(v) ((unsigned) __builtin_ctzll(v)) #else LZO_UNUSED(v); return 0; #endif } #endif lzo_unused_funcs_impl(void, lzo_bitops_unused_funcs)(void) { LZO_UNUSED_FUNC(lzo_bitops_unused_funcs); LZO_UNUSED_FUNC(lzo_bitops_ctlz32_func); LZO_UNUSED_FUNC(lzo_bitops_cttz32_func); #if defined(lzo_uint64_t) LZO_UNUSED_FUNC(lzo_bitops_ctlz64_func); LZO_UNUSED_FUNC(lzo_bitops_cttz64_func); #endif } #if defined(__lzo_alignof) && !(LZO_CFG_NO_UNALIGNED) #if !defined(lzo_memops_tcheck__) && 0 #define lzo_memops_tcheck__(t,a,b) ((void)0, sizeof(t) == (a) && __lzo_alignof(t) == (b)) #endif #endif #ifndef lzo_memops_TU0p #define lzo_memops_TU0p void __LZO_MMODEL * #endif #ifndef lzo_memops_TU1p #define lzo_memops_TU1p unsigned char __LZO_MMODEL * #endif #ifndef lzo_memops_TU2p #if (LZO_OPT_UNALIGNED16) typedef lzo_uint16_t __lzo_may_alias lzo_memops_TU2; #define lzo_memops_TU2p volatile lzo_memops_TU2 * #elif defined(__lzo_byte_struct) __lzo_byte_struct(lzo_memops_TU2_struct,2) typedef struct lzo_memops_TU2_struct lzo_memops_TU2; #else struct lzo_memops_TU2_struct { unsigned char a[2]; } __lzo_may_alias; typedef struct lzo_memops_TU2_struct lzo_memops_TU2; #endif #ifndef lzo_memops_TU2p #define lzo_memops_TU2p lzo_memops_TU2 * #endif #endif #ifndef lzo_memops_TU4p #if (LZO_OPT_UNALIGNED32) typedef lzo_uint32_t __lzo_may_alias lzo_memops_TU4; #define lzo_memops_TU4p volatile lzo_memops_TU4 __LZO_MMODEL * #elif defined(__lzo_byte_struct) __lzo_byte_struct(lzo_memops_TU4_struct,4) typedef struct lzo_memops_TU4_struct lzo_memops_TU4; #else struct lzo_memops_TU4_struct { unsigned char a[4]; } __lzo_may_alias; typedef struct lzo_memops_TU4_struct lzo_memops_TU4; #endif #ifndef lzo_memops_TU4p #define lzo_memops_TU4p lzo_memops_TU4 __LZO_MMODEL * #endif #endif #ifndef lzo_memops_TU8p #if (LZO_OPT_UNALIGNED64) typedef lzo_uint64_t __lzo_may_alias lzo_memops_TU8; #define lzo_memops_TU8p volatile lzo_memops_TU8 __LZO_MMODEL * #elif defined(__lzo_byte_struct) __lzo_byte_struct(lzo_memops_TU8_struct,8) typedef struct lzo_memops_TU8_struct lzo_memops_TU8; #else struct lzo_memops_TU8_struct { unsigned char a[8]; } __lzo_may_alias; typedef struct lzo_memops_TU8_struct lzo_memops_TU8; #endif #ifndef lzo_memops_TU8p #define lzo_memops_TU8p lzo_memops_TU8 __LZO_MMODEL * #endif #endif #ifndef lzo_memops_set_TU1p #define lzo_memops_set_TU1p volatile lzo_memops_TU1p #endif #ifndef lzo_memops_move_TU1p #define lzo_memops_move_TU1p lzo_memops_TU1p #endif #define LZO_MEMOPS_SET1(dd,cc) \ LZO_BLOCK_BEGIN \ lzo_memops_set_TU1p d__1 = (lzo_memops_set_TU1p) (lzo_memops_TU0p) (dd); \ d__1[0] = LZO_BYTE(cc); \ LZO_BLOCK_END #define LZO_MEMOPS_SET2(dd,cc) \ LZO_BLOCK_BEGIN \ lzo_memops_set_TU1p d__2 = (lzo_memops_set_TU1p) (lzo_memops_TU0p) (dd); \ d__2[0] = LZO_BYTE(cc); d__2[1] = LZO_BYTE(cc); \ LZO_BLOCK_END #define LZO_MEMOPS_SET3(dd,cc) \ LZO_BLOCK_BEGIN \ lzo_memops_set_TU1p d__3 = (lzo_memops_set_TU1p) (lzo_memops_TU0p) (dd); \ d__3[0] = LZO_BYTE(cc); d__3[1] = LZO_BYTE(cc); d__3[2] = LZO_BYTE(cc); \ LZO_BLOCK_END #define LZO_MEMOPS_SET4(dd,cc) \ LZO_BLOCK_BEGIN \ lzo_memops_set_TU1p d__4 = (lzo_memops_set_TU1p) (lzo_memops_TU0p) (dd); \ d__4[0] = LZO_BYTE(cc); d__4[1] = LZO_BYTE(cc); d__4[2] = LZO_BYTE(cc); d__4[3] = LZO_BYTE(cc); \ LZO_BLOCK_END #define LZO_MEMOPS_MOVE1(dd,ss) \ LZO_BLOCK_BEGIN \ lzo_memops_move_TU1p d__1 = (lzo_memops_move_TU1p) (lzo_memops_TU0p) (dd); \ const lzo_memops_move_TU1p s__1 = (const lzo_memops_move_TU1p) (const lzo_memops_TU0p) (ss); \ d__1[0] = s__1[0]; \ LZO_BLOCK_END #define LZO_MEMOPS_MOVE2(dd,ss) \ LZO_BLOCK_BEGIN \ lzo_memops_move_TU1p d__2 = (lzo_memops_move_TU1p) (lzo_memops_TU0p) (dd); \ const lzo_memops_move_TU1p s__2 = (const lzo_memops_move_TU1p) (const lzo_memops_TU0p) (ss); \ d__2[0] = s__2[0]; d__2[1] = s__2[1]; \ LZO_BLOCK_END #define LZO_MEMOPS_MOVE3(dd,ss) \ LZO_BLOCK_BEGIN \ lzo_memops_move_TU1p d__3 = (lzo_memops_move_TU1p) (lzo_memops_TU0p) (dd); \ const lzo_memops_move_TU1p s__3 = (const lzo_memops_move_TU1p) (const lzo_memops_TU0p) (ss); \ d__3[0] = s__3[0]; d__3[1] = s__3[1]; d__3[2] = s__3[2]; \ LZO_BLOCK_END #define LZO_MEMOPS_MOVE4(dd,ss) \ LZO_BLOCK_BEGIN \ lzo_memops_move_TU1p d__4 = (lzo_memops_move_TU1p) (lzo_memops_TU0p) (dd); \ const lzo_memops_move_TU1p s__4 = (const lzo_memops_move_TU1p) (const lzo_memops_TU0p) (ss); \ d__4[0] = s__4[0]; d__4[1] = s__4[1]; d__4[2] = s__4[2]; d__4[3] = s__4[3]; \ LZO_BLOCK_END #define LZO_MEMOPS_MOVE8(dd,ss) \ LZO_BLOCK_BEGIN \ lzo_memops_move_TU1p d__8 = (lzo_memops_move_TU1p) (lzo_memops_TU0p) (dd); \ const lzo_memops_move_TU1p s__8 = (const lzo_memops_move_TU1p) (const lzo_memops_TU0p) (ss); \ d__8[0] = s__8[0]; d__8[1] = s__8[1]; d__8[2] = s__8[2]; d__8[3] = s__8[3]; \ d__8[4] = s__8[4]; d__8[5] = s__8[5]; d__8[6] = s__8[6]; d__8[7] = s__8[7]; \ LZO_BLOCK_END LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU1p)0)==1) #define LZO_MEMOPS_COPY1(dd,ss) LZO_MEMOPS_MOVE1(dd,ss) #if (LZO_OPT_UNALIGNED16) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU2p)0)==2) #define LZO_MEMOPS_COPY2(dd,ss) \ * (lzo_memops_TU2p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU2p) (const lzo_memops_TU0p) (ss) #elif defined(lzo_memops_tcheck__) #define LZO_MEMOPS_COPY2(dd,ss) \ LZO_BLOCK_BEGIN if (lzo_memops_tcheck__(lzo_memops_TU2,2,1)) { \ * (lzo_memops_TU2p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU2p) (const lzo_memops_TU0p) (ss); \ } else { LZO_MEMOPS_MOVE2(dd,ss); } LZO_BLOCK_END #else #define LZO_MEMOPS_COPY2(dd,ss) LZO_MEMOPS_MOVE2(dd,ss) #endif #if (LZO_OPT_UNALIGNED32) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU4p)0)==4) #define LZO_MEMOPS_COPY4(dd,ss) \ * (lzo_memops_TU4p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU4p) (const lzo_memops_TU0p) (ss) #elif defined(lzo_memops_tcheck__) #define LZO_MEMOPS_COPY4(dd,ss) \ LZO_BLOCK_BEGIN if (lzo_memops_tcheck__(lzo_memops_TU4,4,1)) { \ * (lzo_memops_TU4p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU4p) (const lzo_memops_TU0p) (ss); \ } else { LZO_MEMOPS_MOVE4(dd,ss); } LZO_BLOCK_END #else #define LZO_MEMOPS_COPY4(dd,ss) LZO_MEMOPS_MOVE4(dd,ss) #endif #if (LZO_WORDSIZE != 8) #define LZO_MEMOPS_COPY8(dd,ss) \ LZO_BLOCK_BEGIN LZO_MEMOPS_COPY4(dd,ss); LZO_MEMOPS_COPY4((lzo_memops_TU1p)(lzo_memops_TU0p)(dd)+4,(const lzo_memops_TU1p)(const lzo_memops_TU0p)(ss)+4); LZO_BLOCK_END #else #if (LZO_OPT_UNALIGNED64) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU8p)0)==8) #define LZO_MEMOPS_COPY8(dd,ss) \ * (lzo_memops_TU8p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU8p) (const lzo_memops_TU0p) (ss) #elif (LZO_OPT_UNALIGNED32) #define LZO_MEMOPS_COPY8(dd,ss) \ LZO_BLOCK_BEGIN LZO_MEMOPS_COPY4(dd,ss); LZO_MEMOPS_COPY4((lzo_memops_TU1p)(lzo_memops_TU0p)(dd)+4,(const lzo_memops_TU1p)(const lzo_memops_TU0p)(ss)+4); LZO_BLOCK_END #elif defined(lzo_memops_tcheck__) #define LZO_MEMOPS_COPY8(dd,ss) \ LZO_BLOCK_BEGIN if (lzo_memops_tcheck__(lzo_memops_TU8,8,1)) { \ * (lzo_memops_TU8p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU8p) (const lzo_memops_TU0p) (ss); \ } else { LZO_MEMOPS_MOVE8(dd,ss); } LZO_BLOCK_END #else #define LZO_MEMOPS_COPY8(dd,ss) LZO_MEMOPS_MOVE8(dd,ss) #endif #endif #define LZO_MEMOPS_COPYN(dd,ss,nn) \ LZO_BLOCK_BEGIN \ lzo_memops_TU1p d__n = (lzo_memops_TU1p) (lzo_memops_TU0p) (dd); \ const lzo_memops_TU1p s__n = (const lzo_memops_TU1p) (const lzo_memops_TU0p) (ss); \ lzo_uint n__n = (nn); \ while ((void)0, n__n >= 8) { LZO_MEMOPS_COPY8(d__n, s__n); d__n += 8; s__n += 8; n__n -= 8; } \ if ((void)0, n__n >= 4) { LZO_MEMOPS_COPY4(d__n, s__n); d__n += 4; s__n += 4; n__n -= 4; } \ if ((void)0, n__n > 0) do { *d__n++ = *s__n++; } while (--n__n > 0); \ LZO_BLOCK_END __lzo_static_forceinline lzo_uint16_t lzo_memops_get_le16(const lzo_voidp ss) { lzo_uint16_t v; #if (LZO_ABI_LITTLE_ENDIAN) LZO_MEMOPS_COPY2(&v, ss); #elif (LZO_OPT_UNALIGNED16 && LZO_ARCH_POWERPC && LZO_ABI_BIG_ENDIAN) && (LZO_ASM_SYNTAX_GNUC) const lzo_memops_TU2p s = (const lzo_memops_TU2p) ss; unsigned long vv; __asm__("lhbrx %0,0,%1" : "=r" (vv) : "r" (s), "m" (*s)); v = (lzo_uint16_t) vv; #else const lzo_memops_TU1p s = (const lzo_memops_TU1p) ss; v = (lzo_uint16_t) (((lzo_uint16_t)s[0]) | ((lzo_uint16_t)s[1] << 8)); #endif return v; } #if (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) #define LZO_MEMOPS_GET_LE16(ss) (* (const lzo_memops_TU2p) (const lzo_memops_TU0p) (ss)) #else #define LZO_MEMOPS_GET_LE16(ss) lzo_memops_get_le16(ss) #endif __lzo_static_forceinline lzo_uint32_t lzo_memops_get_le32(const lzo_voidp ss) { lzo_uint32_t v; #if (LZO_ABI_LITTLE_ENDIAN) LZO_MEMOPS_COPY4(&v, ss); #elif (LZO_OPT_UNALIGNED32 && LZO_ARCH_POWERPC && LZO_ABI_BIG_ENDIAN) && (LZO_ASM_SYNTAX_GNUC) const lzo_memops_TU4p s = (const lzo_memops_TU4p) ss; unsigned long vv; __asm__("lwbrx %0,0,%1" : "=r" (vv) : "r" (s), "m" (*s)); v = (lzo_uint32_t) vv; #else const lzo_memops_TU1p s = (const lzo_memops_TU1p) ss; v = (lzo_uint32_t) (((lzo_uint32_t)s[0]) | ((lzo_uint32_t)s[1] << 8) | ((lzo_uint32_t)s[2] << 16) | ((lzo_uint32_t)s[3] << 24)); #endif return v; } #if (LZO_OPT_UNALIGNED32) && (LZO_ABI_LITTLE_ENDIAN) #define LZO_MEMOPS_GET_LE32(ss) (* (const lzo_memops_TU4p) (const lzo_memops_TU0p) (ss)) #else #define LZO_MEMOPS_GET_LE32(ss) lzo_memops_get_le32(ss) #endif #if (LZO_OPT_UNALIGNED64) && (LZO_ABI_LITTLE_ENDIAN) #define LZO_MEMOPS_GET_LE64(ss) (* (const lzo_memops_TU8p) (const lzo_memops_TU0p) (ss)) #endif __lzo_static_forceinline lzo_uint16_t lzo_memops_get_ne16(const lzo_voidp ss) { lzo_uint16_t v; LZO_MEMOPS_COPY2(&v, ss); return v; } #if (LZO_OPT_UNALIGNED16) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU2p)0)==2) #define LZO_MEMOPS_GET_NE16(ss) (* (const lzo_memops_TU2p) (const lzo_memops_TU0p) (ss)) #else #define LZO_MEMOPS_GET_NE16(ss) lzo_memops_get_ne16(ss) #endif __lzo_static_forceinline lzo_uint32_t lzo_memops_get_ne32(const lzo_voidp ss) { lzo_uint32_t v; LZO_MEMOPS_COPY4(&v, ss); return v; } #if (LZO_OPT_UNALIGNED32) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU4p)0)==4) #define LZO_MEMOPS_GET_NE32(ss) (* (const lzo_memops_TU4p) (const lzo_memops_TU0p) (ss)) #else #define LZO_MEMOPS_GET_NE32(ss) lzo_memops_get_ne32(ss) #endif #if (LZO_OPT_UNALIGNED64) LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU8p)0)==8) #define LZO_MEMOPS_GET_NE64(ss) (* (const lzo_memops_TU8p) (const lzo_memops_TU0p) (ss)) #endif __lzo_static_forceinline void lzo_memops_put_le16(lzo_voidp dd, lzo_uint16_t vv) { #if (LZO_ABI_LITTLE_ENDIAN) LZO_MEMOPS_COPY2(dd, &vv); #elif (LZO_OPT_UNALIGNED16 && LZO_ARCH_POWERPC && LZO_ABI_BIG_ENDIAN) && (LZO_ASM_SYNTAX_GNUC) lzo_memops_TU2p d = (lzo_memops_TU2p) dd; unsigned long v = vv; __asm__("sthbrx %2,0,%1" : "=m" (*d) : "r" (d), "r" (v)); #else lzo_memops_TU1p d = (lzo_memops_TU1p) dd; d[0] = LZO_BYTE((vv ) & 0xff); d[1] = LZO_BYTE((vv >> 8) & 0xff); #endif } #if (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) #define LZO_MEMOPS_PUT_LE16(dd,vv) (* (lzo_memops_TU2p) (lzo_memops_TU0p) (dd) = (vv)) #else #define LZO_MEMOPS_PUT_LE16(dd,vv) lzo_memops_put_le16(dd,vv) #endif __lzo_static_forceinline void lzo_memops_put_le32(lzo_voidp dd, lzo_uint32_t vv) { #if (LZO_ABI_LITTLE_ENDIAN) LZO_MEMOPS_COPY4(dd, &vv); #elif (LZO_OPT_UNALIGNED32 && LZO_ARCH_POWERPC && LZO_ABI_BIG_ENDIAN) && (LZO_ASM_SYNTAX_GNUC) lzo_memops_TU4p d = (lzo_memops_TU4p) dd; unsigned long v = vv; __asm__("stwbrx %2,0,%1" : "=m" (*d) : "r" (d), "r" (v)); #else lzo_memops_TU1p d = (lzo_memops_TU1p) dd; d[0] = LZO_BYTE((vv ) & 0xff); d[1] = LZO_BYTE((vv >> 8) & 0xff); d[2] = LZO_BYTE((vv >> 16) & 0xff); d[3] = LZO_BYTE((vv >> 24) & 0xff); #endif } #if (LZO_OPT_UNALIGNED32) && (LZO_ABI_LITTLE_ENDIAN) #define LZO_MEMOPS_PUT_LE32(dd,vv) (* (lzo_memops_TU4p) (lzo_memops_TU0p) (dd) = (vv)) #else #define LZO_MEMOPS_PUT_LE32(dd,vv) lzo_memops_put_le32(dd,vv) #endif __lzo_static_forceinline void lzo_memops_put_ne16(lzo_voidp dd, lzo_uint16_t vv) { LZO_MEMOPS_COPY2(dd, &vv); } #if (LZO_OPT_UNALIGNED16) #define LZO_MEMOPS_PUT_NE16(dd,vv) (* (lzo_memops_TU2p) (lzo_memops_TU0p) (dd) = (vv)) #else #define LZO_MEMOPS_PUT_NE16(dd,vv) lzo_memops_put_ne16(dd,vv) #endif __lzo_static_forceinline void lzo_memops_put_ne32(lzo_voidp dd, lzo_uint32_t vv) { LZO_MEMOPS_COPY4(dd, &vv); } #if (LZO_OPT_UNALIGNED32) #define LZO_MEMOPS_PUT_NE32(dd,vv) (* (lzo_memops_TU4p) (lzo_memops_TU0p) (dd) = (vv)) #else #define LZO_MEMOPS_PUT_NE32(dd,vv) lzo_memops_put_ne32(dd,vv) #endif lzo_unused_funcs_impl(void, lzo_memops_unused_funcs)(void) { LZO_UNUSED_FUNC(lzo_memops_unused_funcs); LZO_UNUSED_FUNC(lzo_memops_get_le16); LZO_UNUSED_FUNC(lzo_memops_get_le32); LZO_UNUSED_FUNC(lzo_memops_get_ne16); LZO_UNUSED_FUNC(lzo_memops_get_ne32); LZO_UNUSED_FUNC(lzo_memops_put_le16); LZO_UNUSED_FUNC(lzo_memops_put_le32); LZO_UNUSED_FUNC(lzo_memops_put_ne16); LZO_UNUSED_FUNC(lzo_memops_put_ne32); } #endif #ifndef UA_SET1 #define UA_SET1 LZO_MEMOPS_SET1 #endif #ifndef UA_SET2 #define UA_SET2 LZO_MEMOPS_SET2 #endif #ifndef UA_SET3 #define UA_SET3 LZO_MEMOPS_SET3 #endif #ifndef UA_SET4 #define UA_SET4 LZO_MEMOPS_SET4 #endif #ifndef UA_MOVE1 #define UA_MOVE1 LZO_MEMOPS_MOVE1 #endif #ifndef UA_MOVE2 #define UA_MOVE2 LZO_MEMOPS_MOVE2 #endif #ifndef UA_MOVE3 #define UA_MOVE3 LZO_MEMOPS_MOVE3 #endif #ifndef UA_MOVE4 #define UA_MOVE4 LZO_MEMOPS_MOVE4 #endif #ifndef UA_MOVE8 #define UA_MOVE8 LZO_MEMOPS_MOVE8 #endif #ifndef UA_COPY1 #define UA_COPY1 LZO_MEMOPS_COPY1 #endif #ifndef UA_COPY2 #define UA_COPY2 LZO_MEMOPS_COPY2 #endif #ifndef UA_COPY3 #define UA_COPY3 LZO_MEMOPS_COPY3 #endif #ifndef UA_COPY4 #define UA_COPY4 LZO_MEMOPS_COPY4 #endif #ifndef UA_COPY8 #define UA_COPY8 LZO_MEMOPS_COPY8 #endif #ifndef UA_COPYN #define UA_COPYN LZO_MEMOPS_COPYN #endif #ifndef UA_COPYN_X #define UA_COPYN_X LZO_MEMOPS_COPYN #endif #ifndef UA_GET_LE16 #define UA_GET_LE16 LZO_MEMOPS_GET_LE16 #endif #ifndef UA_GET_LE32 #define UA_GET_LE32 LZO_MEMOPS_GET_LE32 #endif #ifdef LZO_MEMOPS_GET_LE64 #ifndef UA_GET_LE64 #define UA_GET_LE64 LZO_MEMOPS_GET_LE64 #endif #endif #ifndef UA_GET_NE16 #define UA_GET_NE16 LZO_MEMOPS_GET_NE16 #endif #ifndef UA_GET_NE32 #define UA_GET_NE32 LZO_MEMOPS_GET_NE32 #endif #ifdef LZO_MEMOPS_GET_NE64 #ifndef UA_GET_NE64 #define UA_GET_NE64 LZO_MEMOPS_GET_NE64 #endif #endif #ifndef UA_PUT_LE16 #define UA_PUT_LE16 LZO_MEMOPS_PUT_LE16 #endif #ifndef UA_PUT_LE32 #define UA_PUT_LE32 LZO_MEMOPS_PUT_LE32 #endif #ifndef UA_PUT_NE16 #define UA_PUT_NE16 LZO_MEMOPS_PUT_NE16 #endif #ifndef UA_PUT_NE32 #define UA_PUT_NE32 LZO_MEMOPS_PUT_NE32 #endif #define MEMCPY8_DS(dest,src,len) \ lzo_memcpy(dest,src,len); dest += len; src += len #define BZERO8_PTR(s,l,n) \ lzo_memset((lzo_voidp)(s),0,(lzo_uint)(l)*(n)) #define MEMCPY_DS(dest,src,len) \ do *dest++ = *src++; while (--len > 0) LZO_EXTERN(const lzo_bytep) lzo_copyright(void); #ifndef __LZO_PTR_H #define __LZO_PTR_H 1 #ifdef __cplusplus extern "C" { #endif #if (LZO_ARCH_I086) #error "LZO_ARCH_I086 is unsupported" #elif (LZO_MM_PVP) #error "LZO_MM_PVP is unsupported" #else #define PTR(a) ((lzo_uintptr_t) (a)) #define PTR_LINEAR(a) PTR(a) #define PTR_ALIGNED_4(a) ((PTR_LINEAR(a) & 3) == 0) #define PTR_ALIGNED_8(a) ((PTR_LINEAR(a) & 7) == 0) #define PTR_ALIGNED2_4(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0) #define PTR_ALIGNED2_8(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0) #endif #define PTR_LT(a,b) (PTR(a) < PTR(b)) #define PTR_GE(a,b) (PTR(a) >= PTR(b)) #define PTR_DIFF(a,b) (PTR(a) - PTR(b)) #define pd(a,b) ((lzo_uint) ((a)-(b))) LZO_EXTERN(lzo_uintptr_t) __lzo_ptr_linear(const lzo_voidp ptr); typedef union { char a_char; unsigned char a_uchar; short a_short; unsigned short a_ushort; int a_int; unsigned int a_uint; long a_long; unsigned long a_ulong; lzo_int a_lzo_int; lzo_uint a_lzo_uint; lzo_xint a_lzo_xint; lzo_int16_t a_lzo_int16_t; lzo_uint16_t a_lzo_uint16_t; lzo_int32_t a_lzo_int32_t; lzo_uint32_t a_lzo_uint32_t; #if defined(lzo_uint64_t) lzo_int64_t a_lzo_int64_t; lzo_uint64_t a_lzo_uint64_t; #endif size_t a_size_t; ptrdiff_t a_ptrdiff_t; lzo_uintptr_t a_lzo_uintptr_t; void * a_void_p; char * a_char_p; unsigned char * a_uchar_p; const void * a_c_void_p; const char * a_c_char_p; const unsigned char * a_c_uchar_p; lzo_voidp a_lzo_voidp; lzo_bytep a_lzo_bytep; const lzo_voidp a_c_lzo_voidp; const lzo_bytep a_c_lzo_bytep; } lzo_full_align_t; #ifdef __cplusplus } #endif #endif #ifndef LZO_DETERMINISTIC #define LZO_DETERMINISTIC 1 #endif #ifndef LZO_DICT_USE_PTR #define LZO_DICT_USE_PTR 1 #endif #if (LZO_DICT_USE_PTR) # define lzo_dict_t const lzo_bytep # define lzo_dict_p lzo_dict_t * #else # define lzo_dict_t lzo_uint # define lzo_dict_p lzo_dict_t * #endif #endif #if !defined(MINILZO_CFG_SKIP_LZO_PTR) LZO_PUBLIC(lzo_uintptr_t) __lzo_ptr_linear(const lzo_voidp ptr) { lzo_uintptr_t p; #if (LZO_ARCH_I086) #error "LZO_ARCH_I086 is unsupported" #elif (LZO_MM_PVP) #error "LZO_MM_PVP is unsupported" #else p = (lzo_uintptr_t) PTR_LINEAR(ptr); #endif return p; } LZO_PUBLIC(unsigned) __lzo_align_gap(const lzo_voidp ptr, lzo_uint size) { #if (__LZO_UINTPTR_T_IS_POINTER) #error "__LZO_UINTPTR_T_IS_POINTER is unsupported" #else lzo_uintptr_t p, n; if (size < 2) return 0; p = __lzo_ptr_linear(ptr); #if 0 n = (((p + size - 1) / size) * size) - p; #else if ((size & (size - 1)) != 0) return 0; n = size; n = ((p + n - 1) & ~(n - 1)) - p; #endif #endif assert((long)n >= 0); assert(n <= size); return (unsigned)n; } #endif #if !defined(MINILZO_CFG_SKIP_LZO_UTIL) /* If you use the LZO library in a product, I would appreciate that you * keep this copyright string in the executable of your product. */ static const char lzo_copyright_[] = #if !defined(__LZO_IN_MINLZO) LZO_VERSION_STRING; #else "\r\n\n" "LZO data compression library.\n" "$Copyright: LZO Copyright (C) 1996-2015 Markus Franz Xaver Johannes Oberhumer\n" "\n" "http://www.oberhumer.com $\n\n" "$Id: LZO version: v" LZO_VERSION_STRING ", " LZO_VERSION_DATE " $\n" "$Info: " LZO_INFO_STRING " $\n"; #endif static const char lzo_version_string_[] = LZO_VERSION_STRING; static const char lzo_version_date_[] = LZO_VERSION_DATE; LZO_PUBLIC(const lzo_bytep) lzo_copyright(void) { return (const lzo_bytep) lzo_copyright_; } LZO_PUBLIC(unsigned) lzo_version(void) { return LZO_VERSION; } LZO_PUBLIC(const char *) lzo_version_string(void) { return lzo_version_string_; } LZO_PUBLIC(const char *) lzo_version_date(void) { return lzo_version_date_; } LZO_PUBLIC(const lzo_charp) _lzo_version_string(void) { return lzo_version_string_; } LZO_PUBLIC(const lzo_charp) _lzo_version_date(void) { return lzo_version_date_; } #define LZO_BASE 65521u #define LZO_NMAX 5552 #define LZO_DO1(buf,i) s1 += buf[i]; s2 += s1 #define LZO_DO2(buf,i) LZO_DO1(buf,i); LZO_DO1(buf,i+1) #define LZO_DO4(buf,i) LZO_DO2(buf,i); LZO_DO2(buf,i+2) #define LZO_DO8(buf,i) LZO_DO4(buf,i); LZO_DO4(buf,i+4) #define LZO_DO16(buf,i) LZO_DO8(buf,i); LZO_DO8(buf,i+8) LZO_PUBLIC(lzo_uint32_t) lzo_adler32(lzo_uint32_t adler, const lzo_bytep buf, lzo_uint len) { lzo_uint32_t s1 = adler & 0xffff; lzo_uint32_t s2 = (adler >> 16) & 0xffff; unsigned k; if (buf == NULL) return 1; while (len > 0) { k = len < LZO_NMAX ? (unsigned) len : LZO_NMAX; len -= k; if (k >= 16) do { LZO_DO16(buf,0); buf += 16; k -= 16; } while (k >= 16); if (k != 0) do { s1 += *buf++; s2 += s1; } while (--k > 0); s1 %= LZO_BASE; s2 %= LZO_BASE; } return (s2 << 16) | s1; } #undef LZO_DO1 #undef LZO_DO2 #undef LZO_DO4 #undef LZO_DO8 #undef LZO_DO16 #endif #if !defined(MINILZO_CFG_SKIP_LZO_STRING) #undef lzo_memcmp #undef lzo_memcpy #undef lzo_memmove #undef lzo_memset #if !defined(__LZO_MMODEL_HUGE) # undef LZO_HAVE_MM_HUGE_PTR #endif #define lzo_hsize_t lzo_uint #define lzo_hvoid_p lzo_voidp #define lzo_hbyte_p lzo_bytep #define LZOLIB_PUBLIC(r,f) LZO_PUBLIC(r) f #define lzo_hmemcmp lzo_memcmp #define lzo_hmemcpy lzo_memcpy #define lzo_hmemmove lzo_memmove #define lzo_hmemset lzo_memset #define __LZOLIB_HMEMCPY_CH_INCLUDED 1 #if !defined(LZOLIB_PUBLIC) # define LZOLIB_PUBLIC(r,f) r __LZOLIB_FUNCNAME(f) #endif LZOLIB_PUBLIC(int, lzo_hmemcmp) (const lzo_hvoid_p s1, const lzo_hvoid_p s2, lzo_hsize_t len) { #if (LZO_HAVE_MM_HUGE_PTR) || !(HAVE_MEMCMP) const lzo_hbyte_p p1 = LZO_STATIC_CAST(const lzo_hbyte_p, s1); const lzo_hbyte_p p2 = LZO_STATIC_CAST(const lzo_hbyte_p, s2); if __lzo_likely(len > 0) do { int d = *p1 - *p2; if (d != 0) return d; p1++; p2++; } while __lzo_likely(--len > 0); return 0; #else return memcmp(s1, s2, len); #endif } LZOLIB_PUBLIC(lzo_hvoid_p, lzo_hmemcpy) (lzo_hvoid_p dest, const lzo_hvoid_p src, lzo_hsize_t len) { #if (LZO_HAVE_MM_HUGE_PTR) || !(HAVE_MEMCPY) lzo_hbyte_p p1 = LZO_STATIC_CAST(lzo_hbyte_p, dest); const lzo_hbyte_p p2 = LZO_STATIC_CAST(const lzo_hbyte_p, src); if (!(len > 0) || p1 == p2) return dest; do *p1++ = *p2++; while __lzo_likely(--len > 0); return dest; #else return memcpy(dest, src, len); #endif } LZOLIB_PUBLIC(lzo_hvoid_p, lzo_hmemmove) (lzo_hvoid_p dest, const lzo_hvoid_p src, lzo_hsize_t len) { #if (LZO_HAVE_MM_HUGE_PTR) || !(HAVE_MEMMOVE) lzo_hbyte_p p1 = LZO_STATIC_CAST(lzo_hbyte_p, dest); const lzo_hbyte_p p2 = LZO_STATIC_CAST(const lzo_hbyte_p, src); if (!(len > 0) || p1 == p2) return dest; if (p1 < p2) { do *p1++ = *p2++; while __lzo_likely(--len > 0); } else { p1 += len; p2 += len; do *--p1 = *--p2; while __lzo_likely(--len > 0); } return dest; #else return memmove(dest, src, len); #endif } LZOLIB_PUBLIC(lzo_hvoid_p, lzo_hmemset) (lzo_hvoid_p s, int cc, lzo_hsize_t len) { #if (LZO_HAVE_MM_HUGE_PTR) || !(HAVE_MEMSET) lzo_hbyte_p p = LZO_STATIC_CAST(lzo_hbyte_p, s); unsigned char c = LZO_ITRUNC(unsigned char, cc); if __lzo_likely(len > 0) do *p++ = c; while __lzo_likely(--len > 0); return s; #else return memset(s, cc, len); #endif } #undef LZOLIB_PUBLIC #endif #if !defined(MINILZO_CFG_SKIP_LZO_INIT) #if !defined(__LZO_IN_MINILZO) #define LZO_WANT_ACC_CHK_CH 1 #undef LZOCHK_ASSERT LZOCHK_ASSERT((LZO_UINT32_C(1) << (int)(8*sizeof(LZO_UINT32_C(1))-1)) > 0) LZOCHK_ASSERT_IS_SIGNED_T(lzo_int) LZOCHK_ASSERT_IS_UNSIGNED_T(lzo_uint) #if !(__LZO_UINTPTR_T_IS_POINTER) LZOCHK_ASSERT_IS_UNSIGNED_T(lzo_uintptr_t) #endif LZOCHK_ASSERT(sizeof(lzo_uintptr_t) >= sizeof(lzo_voidp)) LZOCHK_ASSERT_IS_UNSIGNED_T(lzo_xint) #endif #undef LZOCHK_ASSERT union lzo_config_check_union { lzo_uint a[2]; unsigned char b[2*LZO_MAX(8,sizeof(lzo_uint))]; #if defined(lzo_uint64_t) lzo_uint64_t c[2]; #endif }; #if 0 #define u2p(ptr,off) ((lzo_voidp) (((lzo_bytep)(lzo_voidp)(ptr)) + (off))) #else static __lzo_noinline lzo_voidp u2p(lzo_voidp ptr, lzo_uint off) { return (lzo_voidp) ((lzo_bytep) ptr + off); } #endif LZO_PUBLIC(int) _lzo_config_check(void) { #if (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x030100ul && LZO_CC_CLANG < 0x030300ul)) # if 0 volatile # endif #endif union lzo_config_check_union u; lzo_voidp p; unsigned r = 1; u.a[0] = u.a[1] = 0; p = u2p(&u, 0); r &= ((* (lzo_bytep) p) == 0); #if !(LZO_CFG_NO_CONFIG_CHECK) #if (LZO_ABI_BIG_ENDIAN) u.a[0] = u.a[1] = 0; u.b[sizeof(lzo_uint) - 1] = 128; p = u2p(&u, 0); r &= ((* (lzo_uintp) p) == 128); #endif #if (LZO_ABI_LITTLE_ENDIAN) u.a[0] = u.a[1] = 0; u.b[0] = 128; p = u2p(&u, 0); r &= ((* (lzo_uintp) p) == 128); #endif u.a[0] = u.a[1] = 0; u.b[0] = 1; u.b[3] = 2; p = u2p(&u, 1); r &= UA_GET_NE16(p) == 0; r &= UA_GET_LE16(p) == 0; u.b[1] = 128; r &= UA_GET_LE16(p) == 128; u.b[2] = 129; r &= UA_GET_LE16(p) == LZO_UINT16_C(0x8180); #if (LZO_ABI_BIG_ENDIAN) r &= UA_GET_NE16(p) == LZO_UINT16_C(0x8081); #endif #if (LZO_ABI_LITTLE_ENDIAN) r &= UA_GET_NE16(p) == LZO_UINT16_C(0x8180); #endif u.a[0] = u.a[1] = 0; u.b[0] = 3; u.b[5] = 4; p = u2p(&u, 1); r &= UA_GET_NE32(p) == 0; r &= UA_GET_LE32(p) == 0; u.b[1] = 128; r &= UA_GET_LE32(p) == 128; u.b[2] = 129; u.b[3] = 130; u.b[4] = 131; r &= UA_GET_LE32(p) == LZO_UINT32_C(0x83828180); #if (LZO_ABI_BIG_ENDIAN) r &= UA_GET_NE32(p) == LZO_UINT32_C(0x80818283); #endif #if (LZO_ABI_LITTLE_ENDIAN) r &= UA_GET_NE32(p) == LZO_UINT32_C(0x83828180); #endif #if defined(UA_GET_NE64) u.c[0] = u.c[1] = 0; u.b[0] = 5; u.b[9] = 6; p = u2p(&u, 1); u.c[0] = u.c[1] = 0; r &= UA_GET_NE64(p) == 0; #if defined(UA_GET_LE64) r &= UA_GET_LE64(p) == 0; u.b[1] = 128; r &= UA_GET_LE64(p) == 128; #endif #endif #if defined(lzo_bitops_ctlz32) { unsigned i = 0; lzo_uint32_t v; for (v = 1; v != 0 && r == 1; v <<= 1, i++) { r &= lzo_bitops_ctlz32(v) == 31 - i; r &= lzo_bitops_ctlz32_func(v) == 31 - i; }} #endif #if defined(lzo_bitops_ctlz64) { unsigned i = 0; lzo_uint64_t v; for (v = 1; v != 0 && r == 1; v <<= 1, i++) { r &= lzo_bitops_ctlz64(v) == 63 - i; r &= lzo_bitops_ctlz64_func(v) == 63 - i; }} #endif #if defined(lzo_bitops_cttz32) { unsigned i = 0; lzo_uint32_t v; for (v = 1; v != 0 && r == 1; v <<= 1, i++) { r &= lzo_bitops_cttz32(v) == i; r &= lzo_bitops_cttz32_func(v) == i; }} #endif #if defined(lzo_bitops_cttz64) { unsigned i = 0; lzo_uint64_t v; for (v = 1; v != 0 && r == 1; v <<= 1, i++) { r &= lzo_bitops_cttz64(v) == i; r &= lzo_bitops_cttz64_func(v) == i; }} #endif #endif LZO_UNUSED_FUNC(lzo_bitops_unused_funcs); return r == 1 ? LZO_E_OK : LZO_E_ERROR; } LZO_PUBLIC(int) __lzo_init_v2(unsigned v, int s1, int s2, int s3, int s4, int s5, int s6, int s7, int s8, int s9) { int r; #if defined(__LZO_IN_MINILZO) #elif (LZO_CC_MSC && ((_MSC_VER) < 700)) #else #define LZO_WANT_ACC_CHK_CH 1 #undef LZOCHK_ASSERT #define LZOCHK_ASSERT(expr) LZO_COMPILE_TIME_ASSERT(expr) #endif #undef LZOCHK_ASSERT if (v == 0) return LZO_E_ERROR; r = (s1 == -1 || s1 == (int) sizeof(short)) && (s2 == -1 || s2 == (int) sizeof(int)) && (s3 == -1 || s3 == (int) sizeof(long)) && (s4 == -1 || s4 == (int) sizeof(lzo_uint32_t)) && (s5 == -1 || s5 == (int) sizeof(lzo_uint)) && (s6 == -1 || s6 == (int) lzo_sizeof_dict_t) && (s7 == -1 || s7 == (int) sizeof(char *)) && (s8 == -1 || s8 == (int) sizeof(lzo_voidp)) && (s9 == -1 || s9 == (int) sizeof(lzo_callback_t)); if (!r) return LZO_E_ERROR; r = _lzo_config_check(); if (r != LZO_E_OK) return r; return r; } #if !defined(__LZO_IN_MINILZO) #if (LZO_OS_WIN16 && LZO_CC_WATCOMC) && defined(__SW_BD) #if 0 BOOL FAR PASCAL LibMain ( HANDLE hInstance, WORD wDataSegment, WORD wHeapSize, LPSTR lpszCmdLine ) #else int __far __pascal LibMain ( int a, short b, short c, long d ) #endif { LZO_UNUSED(a); LZO_UNUSED(b); LZO_UNUSED(c); LZO_UNUSED(d); return 1; } #endif #endif #endif #define LZO1X 1 #define LZO_EOF_CODE 1 #define M2_MAX_OFFSET 0x0800 #if !defined(MINILZO_CFG_SKIP_LZO1X_1_COMPRESS) #if 1 && defined(UA_GET_LE32) #undef LZO_DICT_USE_PTR #define LZO_DICT_USE_PTR 0 #undef lzo_dict_t #define lzo_dict_t lzo_uint16_t #endif #define LZO_NEED_DICT_H 1 #ifndef D_BITS #define D_BITS 14 #endif #define D_INDEX1(d,p) d = DM(DMUL(0x21,DX3(p,5,5,6)) >> 5) #define D_INDEX2(d,p) d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f) #if 1 #define DINDEX(dv,p) DM(((DMUL(0x1824429d,dv)) >> (32-D_BITS))) #else #define DINDEX(dv,p) DM((dv) + ((dv) >> (32-D_BITS))) #endif #ifndef __LZO_CONFIG1X_H #define __LZO_CONFIG1X_H 1 #if !defined(LZO1X) && !defined(LZO1Y) && !defined(LZO1Z) # define LZO1X 1 #endif #if !defined(__LZO_IN_MINILZO) #include #endif #ifndef LZO_EOF_CODE #define LZO_EOF_CODE 1 #endif #undef LZO_DETERMINISTIC #define M1_MAX_OFFSET 0x0400 #ifndef M2_MAX_OFFSET #define M2_MAX_OFFSET 0x0800 #endif #define M3_MAX_OFFSET 0x4000 #define M4_MAX_OFFSET 0xbfff #define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET) #define M1_MIN_LEN 2 #define M1_MAX_LEN 2 #define M2_MIN_LEN 3 #ifndef M2_MAX_LEN #define M2_MAX_LEN 8 #endif #define M3_MIN_LEN 3 #define M3_MAX_LEN 33 #define M4_MIN_LEN 3 #define M4_MAX_LEN 9 #define M1_MARKER 0 #define M2_MARKER 64 #define M3_MARKER 32 #define M4_MARKER 16 #ifndef MIN_LOOKAHEAD #define MIN_LOOKAHEAD (M2_MAX_LEN + 1) #endif #if defined(LZO_NEED_DICT_H) #ifndef LZO_HASH #define LZO_HASH LZO_HASH_LZO_INCREMENTAL_B #endif #define DL_MIN_LEN M2_MIN_LEN #ifndef __LZO_DICT_H #define __LZO_DICT_H 1 #ifdef __cplusplus extern "C" { #endif #if !defined(D_BITS) && defined(DBITS) # define D_BITS DBITS #endif #if !defined(D_BITS) # error "D_BITS is not defined" #endif #if (D_BITS < 16) # define D_SIZE LZO_SIZE(D_BITS) # define D_MASK LZO_MASK(D_BITS) #else # define D_SIZE LZO_USIZE(D_BITS) # define D_MASK LZO_UMASK(D_BITS) #endif #define D_HIGH ((D_MASK >> 1) + 1) #if !defined(DD_BITS) # define DD_BITS 0 #endif #define DD_SIZE LZO_SIZE(DD_BITS) #define DD_MASK LZO_MASK(DD_BITS) #if !defined(DL_BITS) # define DL_BITS (D_BITS - DD_BITS) #endif #if (DL_BITS < 16) # define DL_SIZE LZO_SIZE(DL_BITS) # define DL_MASK LZO_MASK(DL_BITS) #else # define DL_SIZE LZO_USIZE(DL_BITS) # define DL_MASK LZO_UMASK(DL_BITS) #endif #if (D_BITS != DL_BITS + DD_BITS) # error "D_BITS does not match" #endif #if (D_BITS < 6 || D_BITS > 18) # error "invalid D_BITS" #endif #if (DL_BITS < 6 || DL_BITS > 20) # error "invalid DL_BITS" #endif #if (DD_BITS < 0 || DD_BITS > 6) # error "invalid DD_BITS" #endif #if !defined(DL_MIN_LEN) # define DL_MIN_LEN 3 #endif #if !defined(DL_SHIFT) # define DL_SHIFT ((DL_BITS + (DL_MIN_LEN - 1)) / DL_MIN_LEN) #endif #define LZO_HASH_GZIP 1 #define LZO_HASH_GZIP_INCREMENTAL 2 #define LZO_HASH_LZO_INCREMENTAL_A 3 #define LZO_HASH_LZO_INCREMENTAL_B 4 #if !defined(LZO_HASH) # error "choose a hashing strategy" #endif #undef DM #undef DX #if (DL_MIN_LEN == 3) # define _DV2_A(p,shift1,shift2) \ (((( (lzo_xint)((p)[0]) << shift1) ^ (p)[1]) << shift2) ^ (p)[2]) # define _DV2_B(p,shift1,shift2) \ (((( (lzo_xint)((p)[2]) << shift1) ^ (p)[1]) << shift2) ^ (p)[0]) # define _DV3_B(p,shift1,shift2,shift3) \ ((_DV2_B((p)+1,shift1,shift2) << (shift3)) ^ (p)[0]) #elif (DL_MIN_LEN == 2) # define _DV2_A(p,shift1,shift2) \ (( (lzo_xint)(p[0]) << shift1) ^ p[1]) # define _DV2_B(p,shift1,shift2) \ (( (lzo_xint)(p[1]) << shift1) ^ p[2]) #else # error "invalid DL_MIN_LEN" #endif #define _DV_A(p,shift) _DV2_A(p,shift,shift) #define _DV_B(p,shift) _DV2_B(p,shift,shift) #define DA2(p,s1,s2) \ (((((lzo_xint)((p)[2]) << (s2)) + (p)[1]) << (s1)) + (p)[0]) #define DS2(p,s1,s2) \ (((((lzo_xint)((p)[2]) << (s2)) - (p)[1]) << (s1)) - (p)[0]) #define DX2(p,s1,s2) \ (((((lzo_xint)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0]) #define DA3(p,s1,s2,s3) ((DA2((p)+1,s2,s3) << (s1)) + (p)[0]) #define DS3(p,s1,s2,s3) ((DS2((p)+1,s2,s3) << (s1)) - (p)[0]) #define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0]) #define DMS(v,s) ((lzo_uint) (((v) & (D_MASK >> (s))) << (s))) #define DM(v) DMS(v,0) #if (LZO_HASH == LZO_HASH_GZIP) # define _DINDEX(dv,p) (_DV_A((p),DL_SHIFT)) #elif (LZO_HASH == LZO_HASH_GZIP_INCREMENTAL) # define __LZO_HASH_INCREMENTAL 1 # define DVAL_FIRST(dv,p) dv = _DV_A((p),DL_SHIFT) # define DVAL_NEXT(dv,p) dv = (((dv) << DL_SHIFT) ^ p[2]) # define _DINDEX(dv,p) (dv) # define DVAL_LOOKAHEAD DL_MIN_LEN #elif (LZO_HASH == LZO_HASH_LZO_INCREMENTAL_A) # define __LZO_HASH_INCREMENTAL 1 # define DVAL_FIRST(dv,p) dv = _DV_A((p),5) # define DVAL_NEXT(dv,p) \ dv ^= (lzo_xint)(p[-1]) << (2*5); dv = (((dv) << 5) ^ p[2]) # define _DINDEX(dv,p) ((DMUL(0x9f5f,dv)) >> 5) # define DVAL_LOOKAHEAD DL_MIN_LEN #elif (LZO_HASH == LZO_HASH_LZO_INCREMENTAL_B) # define __LZO_HASH_INCREMENTAL 1 # define DVAL_FIRST(dv,p) dv = _DV_B((p),5) # define DVAL_NEXT(dv,p) \ dv ^= p[-1]; dv = (((dv) >> 5) ^ ((lzo_xint)(p[2]) << (2*5))) # define _DINDEX(dv,p) ((DMUL(0x9f5f,dv)) >> 5) # define DVAL_LOOKAHEAD DL_MIN_LEN #else # error "choose a hashing strategy" #endif #ifndef DINDEX #define DINDEX(dv,p) ((lzo_uint)((_DINDEX(dv,p)) & DL_MASK) << DD_BITS) #endif #if !defined(DINDEX1) && defined(D_INDEX1) #define DINDEX1 D_INDEX1 #endif #if !defined(DINDEX2) && defined(D_INDEX2) #define DINDEX2 D_INDEX2 #endif #if !defined(__LZO_HASH_INCREMENTAL) # define DVAL_FIRST(dv,p) ((void) 0) # define DVAL_NEXT(dv,p) ((void) 0) # define DVAL_LOOKAHEAD 0 #endif #if !defined(DVAL_ASSERT) #if defined(__LZO_HASH_INCREMENTAL) && !defined(NDEBUG) #if 1 && (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || (LZO_CC_GNUC >= 0x020700ul) || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) static void __attribute__((__unused__)) #else static void #endif DVAL_ASSERT(lzo_xint dv, const lzo_bytep p) { lzo_xint df; DVAL_FIRST(df,(p)); assert(DINDEX(dv,p) == DINDEX(df,p)); } #else # define DVAL_ASSERT(dv,p) ((void) 0) #endif #endif #if (LZO_DICT_USE_PTR) # define DENTRY(p,in) (p) # define GINDEX(m_pos,m_off,dict,dindex,in) m_pos = dict[dindex] #else # define DENTRY(p,in) ((lzo_dict_t) pd(p, in)) # define GINDEX(m_pos,m_off,dict,dindex,in) m_off = dict[dindex] #endif #if (DD_BITS == 0) # define UPDATE_D(dict,drun,dv,p,in) dict[ DINDEX(dv,p) ] = DENTRY(p,in) # define UPDATE_I(dict,drun,index,p,in) dict[index] = DENTRY(p,in) # define UPDATE_P(ptr,drun,p,in) (ptr)[0] = DENTRY(p,in) #else # define UPDATE_D(dict,drun,dv,p,in) \ dict[ DINDEX(dv,p) + drun++ ] = DENTRY(p,in); drun &= DD_MASK # define UPDATE_I(dict,drun,index,p,in) \ dict[ (index) + drun++ ] = DENTRY(p,in); drun &= DD_MASK # define UPDATE_P(ptr,drun,p,in) \ (ptr) [ drun++ ] = DENTRY(p,in); drun &= DD_MASK #endif #if (LZO_DICT_USE_PTR) #define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ (m_pos == NULL || (m_off = pd(ip, m_pos)) > max_offset) #define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ (BOUNDS_CHECKING_OFF_IN_EXPR(( \ m_pos = ip - (lzo_uint) PTR_DIFF(ip,m_pos), \ PTR_LT(m_pos,in) || \ (m_off = (lzo_uint) PTR_DIFF(ip,m_pos)) == 0 || \ m_off > max_offset ))) #else #define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ (m_off == 0 || \ ((m_off = pd(ip, in) - m_off) > max_offset) || \ (m_pos = (ip) - (m_off), 0) ) #define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ (pd(ip, in) <= m_off || \ ((m_off = pd(ip, in) - m_off) > max_offset) || \ (m_pos = (ip) - (m_off), 0) ) #endif #if (LZO_DETERMINISTIC) # define LZO_CHECK_MPOS LZO_CHECK_MPOS_DET #else # define LZO_CHECK_MPOS LZO_CHECK_MPOS_NON_DET #endif #ifdef __cplusplus } #endif #endif #endif #endif #define LZO_DETERMINISTIC !(LZO_DICT_USE_PTR) #ifndef DO_COMPRESS #define DO_COMPRESS lzo1x_1_compress #endif #if 1 && defined(DO_COMPRESS) && !defined(do_compress) # define do_compress LZO_PP_ECONCAT2(DO_COMPRESS,_core) #endif static __lzo_noinline lzo_uint do_compress ( const lzo_bytep in , lzo_uint in_len, lzo_bytep out, lzo_uintp out_len, lzo_uint ti, lzo_voidp wrkmem) { const lzo_bytep ip; lzo_bytep op; const lzo_bytep const in_end = in + in_len; const lzo_bytep const ip_end = in + in_len - 20; const lzo_bytep ii; lzo_dict_p const dict = (lzo_dict_p) wrkmem; op = out; ip = in; ii = ip; ip += ti < 4 ? 4 - ti : 0; for (;;) { const lzo_bytep m_pos; #if !(LZO_DETERMINISTIC) LZO_DEFINE_UNINITIALIZED_VAR(lzo_uint, m_off, 0); lzo_uint m_len; lzo_uint dindex; next: if __lzo_unlikely(ip >= ip_end) break; DINDEX1(dindex,ip); GINDEX(m_pos,m_off,dict,dindex,in); if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) goto literal; #if 1 if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) goto try_match; DINDEX2(dindex,ip); #endif GINDEX(m_pos,m_off,dict,dindex,in); if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) goto literal; if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) goto try_match; goto literal; try_match: #if (LZO_OPT_UNALIGNED32) if (UA_GET_NE32(m_pos) != UA_GET_NE32(ip)) #else if (m_pos[0] != ip[0] || m_pos[1] != ip[1] || m_pos[2] != ip[2] || m_pos[3] != ip[3]) #endif { literal: UPDATE_I(dict,0,dindex,ip,in); ip += 1 + ((ip - ii) >> 5); continue; } UPDATE_I(dict,0,dindex,ip,in); #else lzo_uint m_off; lzo_uint m_len; { lzo_uint32_t dv; lzo_uint dindex; literal: ip += 1 + ((ip - ii) >> 5); next: if __lzo_unlikely(ip >= ip_end) break; dv = UA_GET_LE32(ip); dindex = DINDEX(dv,ip); GINDEX(m_off,m_pos,in+dict,dindex,in); UPDATE_I(dict,0,dindex,ip,in); if __lzo_unlikely(dv != UA_GET_LE32(m_pos)) goto literal; } #endif ii -= ti; ti = 0; { lzo_uint t = pd(ip,ii); if (t != 0) { if (t <= 3) { op[-2] = LZO_BYTE(op[-2] | t); #if (LZO_OPT_UNALIGNED32) UA_COPY4(op, ii); op += t; #else { do *op++ = *ii++; while (--t > 0); } #endif } #if (LZO_OPT_UNALIGNED32) || (LZO_OPT_UNALIGNED64) else if (t <= 16) { *op++ = LZO_BYTE(t - 3); UA_COPY8(op, ii); UA_COPY8(op+8, ii+8); op += t; } #endif else { if (t <= 18) *op++ = LZO_BYTE(t - 3); else { lzo_uint tt = t - 18; *op++ = 0; while __lzo_unlikely(tt > 255) { tt -= 255; UA_SET1(op, 0); op++; } assert(tt > 0); *op++ = LZO_BYTE(tt); } #if (LZO_OPT_UNALIGNED32) || (LZO_OPT_UNALIGNED64) do { UA_COPY8(op, ii); UA_COPY8(op+8, ii+8); op += 16; ii += 16; t -= 16; } while (t >= 16); if (t > 0) #endif { do *op++ = *ii++; while (--t > 0); } } } } m_len = 4; { #if (LZO_OPT_UNALIGNED64) lzo_uint64_t v; v = UA_GET_NE64(ip + m_len) ^ UA_GET_NE64(m_pos + m_len); if __lzo_unlikely(v == 0) { do { m_len += 8; v = UA_GET_NE64(ip + m_len) ^ UA_GET_NE64(m_pos + m_len); if __lzo_unlikely(ip + m_len >= ip_end) goto m_len_done; } while (v == 0); } #if (LZO_ABI_BIG_ENDIAN) && defined(lzo_bitops_ctlz64) m_len += lzo_bitops_ctlz64(v) / CHAR_BIT; #elif (LZO_ABI_BIG_ENDIAN) if ((v >> (64 - CHAR_BIT)) == 0) do { v <<= CHAR_BIT; m_len += 1; } while ((v >> (64 - CHAR_BIT)) == 0); #elif (LZO_ABI_LITTLE_ENDIAN) && defined(lzo_bitops_cttz64) m_len += lzo_bitops_cttz64(v) / CHAR_BIT; #elif (LZO_ABI_LITTLE_ENDIAN) if ((v & UCHAR_MAX) == 0) do { v >>= CHAR_BIT; m_len += 1; } while ((v & UCHAR_MAX) == 0); #else if (ip[m_len] == m_pos[m_len]) do { m_len += 1; } while (ip[m_len] == m_pos[m_len]); #endif #elif (LZO_OPT_UNALIGNED32) lzo_uint32_t v; v = UA_GET_NE32(ip + m_len) ^ UA_GET_NE32(m_pos + m_len); if __lzo_unlikely(v == 0) { do { m_len += 4; v = UA_GET_NE32(ip + m_len) ^ UA_GET_NE32(m_pos + m_len); if (v != 0) break; m_len += 4; v = UA_GET_NE32(ip + m_len) ^ UA_GET_NE32(m_pos + m_len); if __lzo_unlikely(ip + m_len >= ip_end) goto m_len_done; } while (v == 0); } #if (LZO_ABI_BIG_ENDIAN) && defined(lzo_bitops_ctlz32) m_len += lzo_bitops_ctlz32(v) / CHAR_BIT; #elif (LZO_ABI_BIG_ENDIAN) if ((v >> (32 - CHAR_BIT)) == 0) do { v <<= CHAR_BIT; m_len += 1; } while ((v >> (32 - CHAR_BIT)) == 0); #elif (LZO_ABI_LITTLE_ENDIAN) && defined(lzo_bitops_cttz32) m_len += lzo_bitops_cttz32(v) / CHAR_BIT; #elif (LZO_ABI_LITTLE_ENDIAN) if ((v & UCHAR_MAX) == 0) do { v >>= CHAR_BIT; m_len += 1; } while ((v & UCHAR_MAX) == 0); #else if (ip[m_len] == m_pos[m_len]) do { m_len += 1; } while (ip[m_len] == m_pos[m_len]); #endif #else if __lzo_unlikely(ip[m_len] == m_pos[m_len]) { do { m_len += 1; if (ip[m_len] != m_pos[m_len]) break; m_len += 1; if (ip[m_len] != m_pos[m_len]) break; m_len += 1; if (ip[m_len] != m_pos[m_len]) break; m_len += 1; if (ip[m_len] != m_pos[m_len]) break; m_len += 1; if (ip[m_len] != m_pos[m_len]) break; m_len += 1; if (ip[m_len] != m_pos[m_len]) break; m_len += 1; if (ip[m_len] != m_pos[m_len]) break; m_len += 1; if __lzo_unlikely(ip + m_len >= ip_end) goto m_len_done; } while (ip[m_len] == m_pos[m_len]); } #endif } m_len_done: m_off = pd(ip,m_pos); ip += m_len; ii = ip; if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) { m_off -= 1; #if defined(LZO1X) *op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2)); *op++ = LZO_BYTE(m_off >> 3); #elif defined(LZO1Y) *op++ = LZO_BYTE(((m_len + 1) << 4) | ((m_off & 3) << 2)); *op++ = LZO_BYTE(m_off >> 2); #endif } else if (m_off <= M3_MAX_OFFSET) { m_off -= 1; if (m_len <= M3_MAX_LEN) *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); else { m_len -= M3_MAX_LEN; *op++ = M3_MARKER | 0; while __lzo_unlikely(m_len > 255) { m_len -= 255; UA_SET1(op, 0); op++; } *op++ = LZO_BYTE(m_len); } *op++ = LZO_BYTE(m_off << 2); *op++ = LZO_BYTE(m_off >> 6); } else { m_off -= 0x4000; if (m_len <= M4_MAX_LEN) *op++ = LZO_BYTE(M4_MARKER | ((m_off >> 11) & 8) | (m_len - 2)); else { m_len -= M4_MAX_LEN; *op++ = LZO_BYTE(M4_MARKER | ((m_off >> 11) & 8)); while __lzo_unlikely(m_len > 255) { m_len -= 255; UA_SET1(op, 0); op++; } *op++ = LZO_BYTE(m_len); } *op++ = LZO_BYTE(m_off << 2); *op++ = LZO_BYTE(m_off >> 6); } goto next; } *out_len = pd(op, out); return pd(in_end,ii-ti); } LZO_PUBLIC(int) DO_COMPRESS ( const lzo_bytep in , lzo_uint in_len, lzo_bytep out, lzo_uintp out_len, lzo_voidp wrkmem ) { const lzo_bytep ip = in; lzo_bytep op = out; lzo_uint l = in_len; lzo_uint t = 0; while (l > 20) { lzo_uint ll = l; lzo_uintptr_t ll_end; #if 0 || (LZO_DETERMINISTIC) ll = LZO_MIN(ll, 49152); #endif ll_end = (lzo_uintptr_t)ip + ll; if ((ll_end + ((t + ll) >> 5)) <= ll_end || (const lzo_bytep)(ll_end + ((t + ll) >> 5)) <= ip + ll) break; #if (LZO_DETERMINISTIC) lzo_memset(wrkmem, 0, ((lzo_uint)1 << D_BITS) * sizeof(lzo_dict_t)); #endif t = do_compress(ip,ll,op,out_len,t,wrkmem); ip += ll; op += *out_len; l -= ll; } t += l; if (t > 0) { const lzo_bytep ii = in + in_len - t; if (op == out && t <= 238) *op++ = LZO_BYTE(17 + t); else if (t <= 3) op[-2] = LZO_BYTE(op[-2] | t); else if (t <= 18) *op++ = LZO_BYTE(t - 3); else { lzo_uint tt = t - 18; *op++ = 0; while (tt > 255) { tt -= 255; UA_SET1(op, 0); op++; } assert(tt > 0); *op++ = LZO_BYTE(tt); } UA_COPYN(op, ii, t); op += t; } *op++ = M4_MARKER | 1; *op++ = 0; *op++ = 0; *out_len = pd(op, out); return LZO_E_OK; } #endif #undef do_compress #undef DO_COMPRESS #undef LZO_HASH #undef LZO_TEST_OVERRUN #undef DO_DECOMPRESS #define DO_DECOMPRESS lzo1x_decompress #if !defined(MINILZO_CFG_SKIP_LZO1X_DECOMPRESS) #if defined(LZO_TEST_OVERRUN) # if !defined(LZO_TEST_OVERRUN_INPUT) # define LZO_TEST_OVERRUN_INPUT 2 # endif # if !defined(LZO_TEST_OVERRUN_OUTPUT) # define LZO_TEST_OVERRUN_OUTPUT 2 # endif # if !defined(LZO_TEST_OVERRUN_LOOKBEHIND) # define LZO_TEST_OVERRUN_LOOKBEHIND 1 # endif #endif #undef TEST_IP #undef TEST_OP #undef TEST_IP_AND_TEST_OP #undef TEST_LB #undef TEST_LBO #undef NEED_IP #undef NEED_OP #undef TEST_IV #undef TEST_OV #undef HAVE_TEST_IP #undef HAVE_TEST_OP #undef HAVE_NEED_IP #undef HAVE_NEED_OP #undef HAVE_ANY_IP #undef HAVE_ANY_OP #if defined(LZO_TEST_OVERRUN_INPUT) # if (LZO_TEST_OVERRUN_INPUT >= 1) # define TEST_IP (ip < ip_end) # endif # if (LZO_TEST_OVERRUN_INPUT >= 2) # define NEED_IP(x) \ if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x)) goto input_overrun # define TEST_IV(x) if ((x) > (lzo_uint)0 - (511)) goto input_overrun # endif #endif #if defined(LZO_TEST_OVERRUN_OUTPUT) # if (LZO_TEST_OVERRUN_OUTPUT >= 1) # define TEST_OP (op <= op_end) # endif # if (LZO_TEST_OVERRUN_OUTPUT >= 2) # undef TEST_OP # define NEED_OP(x) \ if ((lzo_uint)(op_end - op) < (lzo_uint)(x)) goto output_overrun # define TEST_OV(x) if ((x) > (lzo_uint)0 - (511)) goto output_overrun # endif #endif #if defined(LZO_TEST_OVERRUN_LOOKBEHIND) # define TEST_LB(m_pos) if (PTR_LT(m_pos,out) || PTR_GE(m_pos,op)) goto lookbehind_overrun # define TEST_LBO(m_pos,o) if (PTR_LT(m_pos,out) || PTR_GE(m_pos,op-(o))) goto lookbehind_overrun #else # define TEST_LB(m_pos) ((void) 0) # define TEST_LBO(m_pos,o) ((void) 0) #endif #if !defined(LZO_EOF_CODE) && !defined(TEST_IP) # define TEST_IP (ip < ip_end) #endif #if defined(TEST_IP) # define HAVE_TEST_IP 1 #else # define TEST_IP 1 #endif #if defined(TEST_OP) # define HAVE_TEST_OP 1 #else # define TEST_OP 1 #endif #if defined(HAVE_TEST_IP) && defined(HAVE_TEST_OP) # define TEST_IP_AND_TEST_OP (TEST_IP && TEST_OP) #elif defined(HAVE_TEST_IP) # define TEST_IP_AND_TEST_OP TEST_IP #elif defined(HAVE_TEST_OP) # define TEST_IP_AND_TEST_OP TEST_OP #else # define TEST_IP_AND_TEST_OP 1 #endif #if defined(NEED_IP) # define HAVE_NEED_IP 1 #else # define NEED_IP(x) ((void) 0) # define TEST_IV(x) ((void) 0) #endif #if defined(NEED_OP) # define HAVE_NEED_OP 1 #else # define NEED_OP(x) ((void) 0) # define TEST_OV(x) ((void) 0) #endif #if defined(HAVE_TEST_IP) || defined(HAVE_NEED_IP) # define HAVE_ANY_IP 1 #endif #if defined(HAVE_TEST_OP) || defined(HAVE_NEED_OP) # define HAVE_ANY_OP 1 #endif #if defined(DO_DECOMPRESS) LZO_PUBLIC(int) DO_DECOMPRESS ( const lzo_bytep in , lzo_uint in_len, lzo_bytep out, lzo_uintp out_len, lzo_voidp wrkmem ) #endif { lzo_bytep op; const lzo_bytep ip; lzo_uint t; #if defined(COPY_DICT) lzo_uint m_off; const lzo_bytep dict_end; #else const lzo_bytep m_pos; #endif const lzo_bytep const ip_end = in + in_len; #if defined(HAVE_ANY_OP) lzo_bytep const op_end = out + *out_len; #endif #if defined(LZO1Z) lzo_uint last_m_off = 0; #endif LZO_UNUSED(wrkmem); #if defined(COPY_DICT) if (dict) { if (dict_len > M4_MAX_OFFSET) { dict += dict_len - M4_MAX_OFFSET; dict_len = M4_MAX_OFFSET; } dict_end = dict + dict_len; } else { dict_len = 0; dict_end = NULL; } #endif *out_len = 0; op = out; ip = in; NEED_IP(1); if (*ip > 17) { t = *ip++ - 17; if (t < 4) goto match_next; assert(t > 0); NEED_OP(t); NEED_IP(t+3); do *op++ = *ip++; while (--t > 0); goto first_literal_run; } for (;;) { NEED_IP(3); t = *ip++; if (t >= 16) goto match; if (t == 0) { while (*ip == 0) { t += 255; ip++; TEST_IV(t); NEED_IP(1); } t += 15 + *ip++; } assert(t > 0); NEED_OP(t+3); NEED_IP(t+6); #if (LZO_OPT_UNALIGNED64) && (LZO_OPT_UNALIGNED32) t += 3; if (t >= 8) do { UA_COPY8(op,ip); op += 8; ip += 8; t -= 8; } while (t >= 8); if (t >= 4) { UA_COPY4(op,ip); op += 4; ip += 4; t -= 4; } if (t > 0) { *op++ = *ip++; if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } } } #elif (LZO_OPT_UNALIGNED32) || (LZO_ALIGNED_OK_4) #if !(LZO_OPT_UNALIGNED32) if (PTR_ALIGNED2_4(op,ip)) { #endif UA_COPY4(op,ip); op += 4; ip += 4; if (--t > 0) { if (t >= 4) { do { UA_COPY4(op,ip); op += 4; ip += 4; t -= 4; } while (t >= 4); if (t > 0) do *op++ = *ip++; while (--t > 0); } else do *op++ = *ip++; while (--t > 0); } #if !(LZO_OPT_UNALIGNED32) } else #endif #endif #if !(LZO_OPT_UNALIGNED32) { *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; do *op++ = *ip++; while (--t > 0); } #endif first_literal_run: t = *ip++; if (t >= 16) goto match; #if defined(COPY_DICT) #if defined(LZO1Z) m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); last_m_off = m_off; #else m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2); #endif NEED_OP(3); t = 3; COPY_DICT(t,m_off) #else #if defined(LZO1Z) t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); m_pos = op - t; last_m_off = t; #else m_pos = op - (1 + M2_MAX_OFFSET); m_pos -= t >> 2; m_pos -= *ip++ << 2; #endif TEST_LB(m_pos); NEED_OP(3); *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos; #endif goto match_done; for (;;) { match: if (t >= 64) { #if defined(COPY_DICT) #if defined(LZO1X) m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3); t = (t >> 5) - 1; #elif defined(LZO1Y) m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2); t = (t >> 4) - 3; #elif defined(LZO1Z) m_off = t & 0x1f; if (m_off >= 0x1c) m_off = last_m_off; else { m_off = 1 + (m_off << 6) + (*ip++ >> 2); last_m_off = m_off; } t = (t >> 5) - 1; #endif #else #if defined(LZO1X) m_pos = op - 1; m_pos -= (t >> 2) & 7; m_pos -= *ip++ << 3; t = (t >> 5) - 1; #elif defined(LZO1Y) m_pos = op - 1; m_pos -= (t >> 2) & 3; m_pos -= *ip++ << 2; t = (t >> 4) - 3; #elif defined(LZO1Z) { lzo_uint off = t & 0x1f; m_pos = op; if (off >= 0x1c) { assert(last_m_off > 0); m_pos -= last_m_off; } else { off = 1 + (off << 6) + (*ip++ >> 2); m_pos -= off; last_m_off = off; } } t = (t >> 5) - 1; #endif TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); goto copy_match; #endif } else if (t >= 32) { t &= 31; if (t == 0) { while (*ip == 0) { t += 255; ip++; TEST_OV(t); NEED_IP(1); } t += 31 + *ip++; NEED_IP(2); } #if defined(COPY_DICT) #if defined(LZO1Z) m_off = 1 + (ip[0] << 6) + (ip[1] >> 2); last_m_off = m_off; #else m_off = 1 + (ip[0] >> 2) + (ip[1] << 6); #endif #else #if defined(LZO1Z) { lzo_uint off = 1 + (ip[0] << 6) + (ip[1] >> 2); m_pos = op - off; last_m_off = off; } #elif (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) m_pos = op - 1; m_pos -= UA_GET_LE16(ip) >> 2; #else m_pos = op - 1; m_pos -= (ip[0] >> 2) + (ip[1] << 6); #endif #endif ip += 2; } else if (t >= 16) { #if defined(COPY_DICT) m_off = (t & 8) << 11; #else m_pos = op; m_pos -= (t & 8) << 11; #endif t &= 7; if (t == 0) { while (*ip == 0) { t += 255; ip++; TEST_OV(t); NEED_IP(1); } t += 7 + *ip++; NEED_IP(2); } #if defined(COPY_DICT) #if defined(LZO1Z) m_off += (ip[0] << 6) + (ip[1] >> 2); #else m_off += (ip[0] >> 2) + (ip[1] << 6); #endif ip += 2; if (m_off == 0) goto eof_found; m_off += 0x4000; #if defined(LZO1Z) last_m_off = m_off; #endif #else #if defined(LZO1Z) m_pos -= (ip[0] << 6) + (ip[1] >> 2); #elif (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) m_pos -= UA_GET_LE16(ip) >> 2; #else m_pos -= (ip[0] >> 2) + (ip[1] << 6); #endif ip += 2; if (m_pos == op) goto eof_found; m_pos -= 0x4000; #if defined(LZO1Z) last_m_off = pd((const lzo_bytep)op, m_pos); #endif #endif } else { #if defined(COPY_DICT) #if defined(LZO1Z) m_off = 1 + (t << 6) + (*ip++ >> 2); last_m_off = m_off; #else m_off = 1 + (t >> 2) + (*ip++ << 2); #endif NEED_OP(2); t = 2; COPY_DICT(t,m_off) #else #if defined(LZO1Z) t = 1 + (t << 6) + (*ip++ >> 2); m_pos = op - t; last_m_off = t; #else m_pos = op - 1; m_pos -= t >> 2; m_pos -= *ip++ << 2; #endif TEST_LB(m_pos); NEED_OP(2); *op++ = *m_pos++; *op++ = *m_pos; #endif goto match_done; } #if defined(COPY_DICT) NEED_OP(t+3-1); t += 3-1; COPY_DICT(t,m_off) #else TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); #if (LZO_OPT_UNALIGNED64) && (LZO_OPT_UNALIGNED32) if (op - m_pos >= 8) { t += (3 - 1); if (t >= 8) do { UA_COPY8(op,m_pos); op += 8; m_pos += 8; t -= 8; } while (t >= 8); if (t >= 4) { UA_COPY4(op,m_pos); op += 4; m_pos += 4; t -= 4; } if (t > 0) { *op++ = m_pos[0]; if (t > 1) { *op++ = m_pos[1]; if (t > 2) { *op++ = m_pos[2]; } } } } else #elif (LZO_OPT_UNALIGNED32) || (LZO_ALIGNED_OK_4) #if !(LZO_OPT_UNALIGNED32) if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos)) { assert((op - m_pos) >= 4); #else if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) { #endif UA_COPY4(op,m_pos); op += 4; m_pos += 4; t -= 4 - (3 - 1); do { UA_COPY4(op,m_pos); op += 4; m_pos += 4; t -= 4; } while (t >= 4); if (t > 0) do *op++ = *m_pos++; while (--t > 0); } else #endif { copy_match: *op++ = *m_pos++; *op++ = *m_pos++; do *op++ = *m_pos++; while (--t > 0); } #endif match_done: #if defined(LZO1Z) t = ip[-1] & 3; #else t = ip[-2] & 3; #endif if (t == 0) break; match_next: assert(t > 0); assert(t < 4); NEED_OP(t); NEED_IP(t+3); #if 0 do *op++ = *ip++; while (--t > 0); #else *op++ = *ip++; if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } } #endif t = *ip++; } } eof_found: *out_len = pd(op, out); return (ip == ip_end ? LZO_E_OK : (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); #if defined(HAVE_NEED_IP) input_overrun: *out_len = pd(op, out); return LZO_E_INPUT_OVERRUN; #endif #if defined(HAVE_NEED_OP) output_overrun: *out_len = pd(op, out); return LZO_E_OUTPUT_OVERRUN; #endif #if defined(LZO_TEST_OVERRUN_LOOKBEHIND) lookbehind_overrun: *out_len = pd(op, out); return LZO_E_LOOKBEHIND_OVERRUN; #endif } #endif #define LZO_TEST_OVERRUN 1 #undef DO_DECOMPRESS #define DO_DECOMPRESS lzo1x_decompress_safe #if !defined(MINILZO_CFG_SKIP_LZO1X_DECOMPRESS_SAFE) #if defined(LZO_TEST_OVERRUN) # if !defined(LZO_TEST_OVERRUN_INPUT) # define LZO_TEST_OVERRUN_INPUT 2 # endif # if !defined(LZO_TEST_OVERRUN_OUTPUT) # define LZO_TEST_OVERRUN_OUTPUT 2 # endif # if !defined(LZO_TEST_OVERRUN_LOOKBEHIND) # define LZO_TEST_OVERRUN_LOOKBEHIND 1 # endif #endif #undef TEST_IP #undef TEST_OP #undef TEST_IP_AND_TEST_OP #undef TEST_LB #undef TEST_LBO #undef NEED_IP #undef NEED_OP #undef TEST_IV #undef TEST_OV #undef HAVE_TEST_IP #undef HAVE_TEST_OP #undef HAVE_NEED_IP #undef HAVE_NEED_OP #undef HAVE_ANY_IP #undef HAVE_ANY_OP #if defined(LZO_TEST_OVERRUN_INPUT) # if (LZO_TEST_OVERRUN_INPUT >= 1) # define TEST_IP (ip < ip_end) # endif # if (LZO_TEST_OVERRUN_INPUT >= 2) # define NEED_IP(x) \ if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x)) goto input_overrun # define TEST_IV(x) if ((x) > (lzo_uint)0 - (511)) goto input_overrun # endif #endif #if defined(LZO_TEST_OVERRUN_OUTPUT) # if (LZO_TEST_OVERRUN_OUTPUT >= 1) # define TEST_OP (op <= op_end) # endif # if (LZO_TEST_OVERRUN_OUTPUT >= 2) # undef TEST_OP # define NEED_OP(x) \ if ((lzo_uint)(op_end - op) < (lzo_uint)(x)) goto output_overrun # define TEST_OV(x) if ((x) > (lzo_uint)0 - (511)) goto output_overrun # endif #endif #if defined(LZO_TEST_OVERRUN_LOOKBEHIND) # define TEST_LB(m_pos) if (PTR_LT(m_pos,out) || PTR_GE(m_pos,op)) goto lookbehind_overrun # define TEST_LBO(m_pos,o) if (PTR_LT(m_pos,out) || PTR_GE(m_pos,op-(o))) goto lookbehind_overrun #else # define TEST_LB(m_pos) ((void) 0) # define TEST_LBO(m_pos,o) ((void) 0) #endif #if !defined(LZO_EOF_CODE) && !defined(TEST_IP) # define TEST_IP (ip < ip_end) #endif #if defined(TEST_IP) # define HAVE_TEST_IP 1 #else # define TEST_IP 1 #endif #if defined(TEST_OP) # define HAVE_TEST_OP 1 #else # define TEST_OP 1 #endif #if defined(HAVE_TEST_IP) && defined(HAVE_TEST_OP) # define TEST_IP_AND_TEST_OP (TEST_IP && TEST_OP) #elif defined(HAVE_TEST_IP) # define TEST_IP_AND_TEST_OP TEST_IP #elif defined(HAVE_TEST_OP) # define TEST_IP_AND_TEST_OP TEST_OP #else # define TEST_IP_AND_TEST_OP 1 #endif #if defined(NEED_IP) # define HAVE_NEED_IP 1 #else # define NEED_IP(x) ((void) 0) # define TEST_IV(x) ((void) 0) #endif #if defined(NEED_OP) # define HAVE_NEED_OP 1 #else # define NEED_OP(x) ((void) 0) # define TEST_OV(x) ((void) 0) #endif #if defined(HAVE_TEST_IP) || defined(HAVE_NEED_IP) # define HAVE_ANY_IP 1 #endif #if defined(HAVE_TEST_OP) || defined(HAVE_NEED_OP) # define HAVE_ANY_OP 1 #endif #if defined(DO_DECOMPRESS) LZO_PUBLIC(int) DO_DECOMPRESS ( const lzo_bytep in , lzo_uint in_len, lzo_bytep out, lzo_uintp out_len, lzo_voidp wrkmem ) #endif { lzo_bytep op; const lzo_bytep ip; lzo_uint t; #if defined(COPY_DICT) lzo_uint m_off; const lzo_bytep dict_end; #else const lzo_bytep m_pos; #endif const lzo_bytep const ip_end = in + in_len; #if defined(HAVE_ANY_OP) lzo_bytep const op_end = out + *out_len; #endif #if defined(LZO1Z) lzo_uint last_m_off = 0; #endif LZO_UNUSED(wrkmem); #if defined(COPY_DICT) if (dict) { if (dict_len > M4_MAX_OFFSET) { dict += dict_len - M4_MAX_OFFSET; dict_len = M4_MAX_OFFSET; } dict_end = dict + dict_len; } else { dict_len = 0; dict_end = NULL; } #endif *out_len = 0; op = out; ip = in; NEED_IP(1); if (*ip > 17) { t = *ip++ - 17; if (t < 4) goto match_next; assert(t > 0); NEED_OP(t); NEED_IP(t+3); do *op++ = *ip++; while (--t > 0); goto first_literal_run; } for (;;) { NEED_IP(3); t = *ip++; if (t >= 16) goto match; if (t == 0) { while (*ip == 0) { t += 255; ip++; TEST_IV(t); NEED_IP(1); } t += 15 + *ip++; } assert(t > 0); NEED_OP(t+3); NEED_IP(t+6); #if (LZO_OPT_UNALIGNED64) && (LZO_OPT_UNALIGNED32) t += 3; if (t >= 8) do { UA_COPY8(op,ip); op += 8; ip += 8; t -= 8; } while (t >= 8); if (t >= 4) { UA_COPY4(op,ip); op += 4; ip += 4; t -= 4; } if (t > 0) { *op++ = *ip++; if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } } } #elif (LZO_OPT_UNALIGNED32) || (LZO_ALIGNED_OK_4) #if !(LZO_OPT_UNALIGNED32) if (PTR_ALIGNED2_4(op,ip)) { #endif UA_COPY4(op,ip); op += 4; ip += 4; if (--t > 0) { if (t >= 4) { do { UA_COPY4(op,ip); op += 4; ip += 4; t -= 4; } while (t >= 4); if (t > 0) do *op++ = *ip++; while (--t > 0); } else do *op++ = *ip++; while (--t > 0); } #if !(LZO_OPT_UNALIGNED32) } else #endif #endif #if !(LZO_OPT_UNALIGNED32) { *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; do *op++ = *ip++; while (--t > 0); } #endif first_literal_run: t = *ip++; if (t >= 16) goto match; #if defined(COPY_DICT) #if defined(LZO1Z) m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); last_m_off = m_off; #else m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2); #endif NEED_OP(3); t = 3; COPY_DICT(t,m_off) #else #if defined(LZO1Z) t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); m_pos = op - t; last_m_off = t; #else m_pos = op - (1 + M2_MAX_OFFSET); m_pos -= t >> 2; m_pos -= *ip++ << 2; #endif TEST_LB(m_pos); NEED_OP(3); *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos; #endif goto match_done; for (;;) { match: if (t >= 64) { #if defined(COPY_DICT) #if defined(LZO1X) m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3); t = (t >> 5) - 1; #elif defined(LZO1Y) m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2); t = (t >> 4) - 3; #elif defined(LZO1Z) m_off = t & 0x1f; if (m_off >= 0x1c) m_off = last_m_off; else { m_off = 1 + (m_off << 6) + (*ip++ >> 2); last_m_off = m_off; } t = (t >> 5) - 1; #endif #else #if defined(LZO1X) m_pos = op - 1; m_pos -= (t >> 2) & 7; m_pos -= *ip++ << 3; t = (t >> 5) - 1; #elif defined(LZO1Y) m_pos = op - 1; m_pos -= (t >> 2) & 3; m_pos -= *ip++ << 2; t = (t >> 4) - 3; #elif defined(LZO1Z) { lzo_uint off = t & 0x1f; m_pos = op; if (off >= 0x1c) { assert(last_m_off > 0); m_pos -= last_m_off; } else { off = 1 + (off << 6) + (*ip++ >> 2); m_pos -= off; last_m_off = off; } } t = (t >> 5) - 1; #endif TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); goto copy_match; #endif } else if (t >= 32) { t &= 31; if (t == 0) { while (*ip == 0) { t += 255; ip++; TEST_OV(t); NEED_IP(1); } t += 31 + *ip++; NEED_IP(2); } #if defined(COPY_DICT) #if defined(LZO1Z) m_off = 1 + (ip[0] << 6) + (ip[1] >> 2); last_m_off = m_off; #else m_off = 1 + (ip[0] >> 2) + (ip[1] << 6); #endif #else #if defined(LZO1Z) { lzo_uint off = 1 + (ip[0] << 6) + (ip[1] >> 2); m_pos = op - off; last_m_off = off; } #elif (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) m_pos = op - 1; m_pos -= UA_GET_LE16(ip) >> 2; #else m_pos = op - 1; m_pos -= (ip[0] >> 2) + (ip[1] << 6); #endif #endif ip += 2; } else if (t >= 16) { #if defined(COPY_DICT) m_off = (t & 8) << 11; #else m_pos = op; m_pos -= (t & 8) << 11; #endif t &= 7; if (t == 0) { while (*ip == 0) { t += 255; ip++; TEST_OV(t); NEED_IP(1); } t += 7 + *ip++; NEED_IP(2); } #if defined(COPY_DICT) #if defined(LZO1Z) m_off += (ip[0] << 6) + (ip[1] >> 2); #else m_off += (ip[0] >> 2) + (ip[1] << 6); #endif ip += 2; if (m_off == 0) goto eof_found; m_off += 0x4000; #if defined(LZO1Z) last_m_off = m_off; #endif #else #if defined(LZO1Z) m_pos -= (ip[0] << 6) + (ip[1] >> 2); #elif (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) m_pos -= UA_GET_LE16(ip) >> 2; #else m_pos -= (ip[0] >> 2) + (ip[1] << 6); #endif ip += 2; if (m_pos == op) goto eof_found; m_pos -= 0x4000; #if defined(LZO1Z) last_m_off = pd((const lzo_bytep)op, m_pos); #endif #endif } else { #if defined(COPY_DICT) #if defined(LZO1Z) m_off = 1 + (t << 6) + (*ip++ >> 2); last_m_off = m_off; #else m_off = 1 + (t >> 2) + (*ip++ << 2); #endif NEED_OP(2); t = 2; COPY_DICT(t,m_off) #else #if defined(LZO1Z) t = 1 + (t << 6) + (*ip++ >> 2); m_pos = op - t; last_m_off = t; #else m_pos = op - 1; m_pos -= t >> 2; m_pos -= *ip++ << 2; #endif TEST_LB(m_pos); NEED_OP(2); *op++ = *m_pos++; *op++ = *m_pos; #endif goto match_done; } #if defined(COPY_DICT) NEED_OP(t+3-1); t += 3-1; COPY_DICT(t,m_off) #else TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); #if (LZO_OPT_UNALIGNED64) && (LZO_OPT_UNALIGNED32) if (op - m_pos >= 8) { t += (3 - 1); if (t >= 8) do { UA_COPY8(op,m_pos); op += 8; m_pos += 8; t -= 8; } while (t >= 8); if (t >= 4) { UA_COPY4(op,m_pos); op += 4; m_pos += 4; t -= 4; } if (t > 0) { *op++ = m_pos[0]; if (t > 1) { *op++ = m_pos[1]; if (t > 2) { *op++ = m_pos[2]; } } } } else #elif (LZO_OPT_UNALIGNED32) || (LZO_ALIGNED_OK_4) #if !(LZO_OPT_UNALIGNED32) if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos)) { assert((op - m_pos) >= 4); #else if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) { #endif UA_COPY4(op,m_pos); op += 4; m_pos += 4; t -= 4 - (3 - 1); do { UA_COPY4(op,m_pos); op += 4; m_pos += 4; t -= 4; } while (t >= 4); if (t > 0) do *op++ = *m_pos++; while (--t > 0); } else #endif { copy_match: *op++ = *m_pos++; *op++ = *m_pos++; do *op++ = *m_pos++; while (--t > 0); } #endif match_done: #if defined(LZO1Z) t = ip[-1] & 3; #else t = ip[-2] & 3; #endif if (t == 0) break; match_next: assert(t > 0); assert(t < 4); NEED_OP(t); NEED_IP(t+3); #if 0 do *op++ = *ip++; while (--t > 0); #else *op++ = *ip++; if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } } #endif t = *ip++; } } eof_found: *out_len = pd(op, out); return (ip == ip_end ? LZO_E_OK : (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); #if defined(HAVE_NEED_IP) input_overrun: *out_len = pd(op, out); return LZO_E_INPUT_OVERRUN; #endif #if defined(HAVE_NEED_OP) output_overrun: *out_len = pd(op, out); return LZO_E_OUTPUT_OVERRUN; #endif #if defined(LZO_TEST_OVERRUN_LOOKBEHIND) lookbehind_overrun: *out_len = pd(op, out); return LZO_E_LOOKBEHIND_OVERRUN; #endif } #endif /***** End of minilzo.c *****/ ================================================ FILE: usr/utils/q.c ================================================ /* * From Unix System Programming - * Starting from Pg 195 * * Advanced inter-process communications */ #include #include #include #include #include #include #include "q.h" extern int debug; extern char mhvtl_driver_name[]; #define MHVTL_ERR(format, arg...) \ { \ if (debug) \ printf("%s: ERROR %s: " format "\n", \ mhvtl_driver_name, __func__, ##arg); \ else \ syslog(LOG_DAEMON | LOG_ERR, "ERROR %s: " format, \ __func__, ##arg); \ } static void warn(char *s) { fprintf(stderr, "Warning: %s\n", s); } int init_queue(void) { int queue_id; /* Attempt to create or open message queue */ queue_id = msgget(QKEY, IPC_CREAT | QPERM); if (queue_id == -1) { char s[245]; switch (errno) { case EACCES: strcpy(s, "Operation not permitted"); break; case EEXIST: strcpy(s, "Message Q already exists"); break; case ENOENT: strcpy(s, "Message Q does not exist"); break; case ENOSPC: strcpy(s, "Exceeded max num of message queues"); break; default: strcpy(s, "errno not valid"); break; } MHVTL_ERR("msgget(%d) failed %s, %s", QKEY, strerror(errno), s); } return queue_id; } int send_msg(char *cmd, long rcv_id) { int len, s_qid; struct q_entry s_entry; /* Match enter()'s length guard: refuse messages that would not * fit into s_entry.msg.text[MAXTEXTLEN + 1] and prevent an * unbounded strcpy into the fixed-size queue message slot. */ if (strlen(cmd) > MAXTEXTLEN) { MHVTL_ERR("send_msg: command too long (%zu > %d)", strlen(cmd), MAXTEXTLEN); return -1; } memset(&s_entry, 0, sizeof(struct q_entry)); s_qid = init_queue(); if (s_qid == -1) return -1; s_entry.rcv_id = rcv_id; s_entry.msg.snd_id = my_id; strncpy(s_entry.msg.text, cmd, MAXTEXTLEN); s_entry.msg.text[MAXTEXTLEN] = '\0'; len = strlen(s_entry.msg.text) + 1 + offsetof(struct q_entry, msg.text); if (msgsnd(s_qid, &s_entry, len, 0) == -1) { MHVTL_ERR("msgsnd failed: %s", strerror(errno)); return -1; } return 0; } static void proc_obj(struct q_entry *q_entry) { printf("rcv_id: %ld, snd_id: %ld, text: %s\n", q_entry->rcv_id, q_entry->msg.snd_id, q_entry->msg.text); } int enter(char *objname, long rcv_id) { int len, s_qid; struct q_entry s_entry; /* Structure to hold message */ /* Validate name length, rcv_id */ if (strlen(objname) > MAXTEXTLEN) { warn("Name too long"); return -1; } if (rcv_id > 32764 || rcv_id < 0) { warn("Invalid rcv_id"); return -1; } /* Initialize message queue as nessary */ s_qid = init_queue(); if (s_qid == -1) return -1; /* Initialize s_entry */ s_entry.rcv_id = rcv_id; s_entry.msg.snd_id = my_id; strcpy(s_entry.msg.text, objname); len = strlen(s_entry.msg.text) + 1 + offsetof(struct q_msg, text); /* Send message, waiting if nessary */ if (msgsnd(s_qid, &s_entry, len, 0) == -1) { MHVTL_ERR("msgsnd failed: %s", strerror(errno)); return -1; } return 0; } int serve(void) { int mlen, r_qid; struct q_entry r_entry; /* Initialise message queue as necessary */ r_qid = init_queue(); if (r_qid == -1) return -1; /* Get and process next message, waiting if necessary */ for (;;) { mlen = msgrcv(r_qid, &r_entry, MAXOBN, (-1 * MAXPRIOR), MSG_NOERROR); if (mlen == -1) { MHVTL_ERR("msgsnd failed: %s", strerror(errno)); return -1; } else { /* Process object name */ proc_obj(&r_entry); } } } ================================================ FILE: usr/utils/reed-solomon.c ================================================ /* * Reed-Solomon CRC defined in ECMA-319 * * Lifted (copied) from IBM LTO reference guide Appendix D. */ #include #include /*---------------------------------------------------------------------------- ** ABSTRACT: function to compute interim LBP CRC ** INPUTS: crc - initial crc (0 for fresh) (i.e., seed) ** cnt - the number of data bytes to compute CRC for ** start - the starting address of the data bytes (e.g., data buffer) ** OUTPUTS: uint32_t - crc in big endian (MSB is first byte) */ uint32_t GenerateRSCRC(uint32_t crc, uint32_t cnt, const void *start) { static const uint32_t crcTable[256] = { 0x00000000, 0x38CF3801, 0x70837002, 0x484C4803, 0xE01BE004, 0xD8D4D805, 0x90989006, 0xA857A807, 0xDD36DD08, 0xE5F9E509, 0xADB5AD0A, 0x957A950B, 0x3D2D3D0C, 0x05E2050D, 0x4DAE4D0E, 0x7561750F, 0xA76CA710, 0x9FA39F11, 0xD7EFD712, 0xEF20EF13, 0x47774714, 0x7FB87F15, 0x37F43716, 0x0F3B0F17, 0x7A5A7A18, 0x42954219, 0x0AD90A1A, 0x3216321B, 0x9A419A1C, 0xA28EA21D, 0xEAC2EA1E, 0xD20DD21F, 0x53D85320, 0x6B176B21, 0x235B2322, 0x1B941B23, 0xB3C3B324, 0x8B0C8B25, 0xC340C326, 0xFB8FFB27, 0x8EEE8E28, 0xB621B629, 0xFE6DFE2A, 0xC6A2C62B, 0x6EF56E2C, 0x563A562D, 0x1E761E2E, 0x26B9262F, 0xF4B4F430, 0xCC7BCC31, 0x84378432, 0xBCF8BC33, 0x14AF1434, 0x2C602C35, 0x642C6436, 0x5CE35C37, 0x29822938, 0x114D1139, 0x5901593A, 0x61CE613B, 0xC999C93C, 0xF156F13D, 0xB91AB93E, 0x81D5813F, 0xA6ADA640, 0x9E629E41, 0xD62ED642, 0xEEE1EE43, 0x46B64644, 0x7E797E45, 0x36353646, 0x0EFA0E47, 0x7B9B7B48, 0x43544349, 0x0B180B4A, 0x33D7334B, 0x9B809B4C, 0xA34FA34D, 0xEB03EB4E, 0xD3CCD34F, 0x01C10150, 0x390E3951, 0x71427152, 0x498D4953, 0xE1DAE154, 0xD915D955, 0x91599156, 0xA996A957, 0xDCF7DC58, 0xE438E459, 0xAC74AC5A, 0x94BB945B, 0x3CEC3C5C, 0x0423045D, 0x4C6F4C5E, 0x74A0745F, 0xF575F560, 0xCDBACD61, 0x85F68562, 0xBD39BD63, 0x156E1564, 0x2DA12D65, 0x65ED6566, 0x5D225D67, 0x28432868, 0x108C1069, 0x58C0586A, 0x600F606B, 0xC858C86C, 0xF097F06D, 0xB8DBB86E, 0x8014806F, 0x52195270, 0x6AD66A71, 0x229A2272, 0x1A551A73, 0xB202B274, 0x8ACD8A75, 0xC281C276, 0xFA4EFA77, 0x8F2F8F78, 0xB7E0B779, 0xFFACFF7A, 0xC763C77B, 0x6F346F7C, 0x57FB577D, 0x1FB71F7E, 0x2778277F, 0x51475180, 0x69886981, 0x21C42182, 0x190B1983, 0xB15CB184, 0x89938985, 0xC1DFC186, 0xF910F987, 0x8C718C88, 0xB4BEB489, 0xFCF2FC8A, 0xC43DC48B, 0x6C6A6C8C, 0x54A5548D, 0x1CE91C8E, 0x2426248F, 0xF62BF690, 0xCEE4CE91, 0x86A88692, 0xBE67BE93, 0x16301694, 0x2EFF2E95, 0x66B36696, 0x5E7C5E97, 0x2B1D2B98, 0x13D21399, 0x5B9E5B9A, 0x6351639B, 0xCB06CB9C, 0xF3C9F39D, 0xBB85BB9E, 0x834A839F, 0x029F02A0, 0x3A503AA1, 0x721C72A2, 0x4AD34AA3, 0xE284E2A4, 0xDA4BDAA5, 0x920792A6, 0xAAC8AAA7, 0xDFA9DFA8, 0xE766E7A9, 0xAF2AAFAA, 0x97E597AB, 0x3FB23FAC, 0x077D07AD, 0x4F314FAE, 0x77FE77AF, 0xA5F3A5B0, 0x9D3C9DB1, 0xD570D5B2, 0xEDBFEDB3, 0x45E845B4, 0x7D277DB5, 0x356B35B6, 0x0DA40DB7, 0x78C578B8, 0x400A40B9, 0x084608BA, 0x308930BB, 0x98DE98BC, 0xA011A0BD, 0xE85DE8BE, 0xD092D0BF, 0xF7EAF7C0, 0xCF25CFC1, 0x876987C2, 0xBFA6BFC3, 0x17F117C4, 0x2F3E2FC5, 0x677267C6, 0x5FBD5FC7, 0x2ADC2AC8, 0x121312C9, 0x5A5F5ACA, 0x629062CB, 0xCAC7CACC, 0xF208F2CD, 0xBA44BACE, 0x828B82CF, 0x508650D0, 0x684968D1, 0x200520D2, 0x18CA18D3, 0xB09DB0D4, 0x885288D5, 0xC01EC0D6, 0xF8D1F8D7, 0x8DB08DD8, 0xB57FB5D9, 0xFD33FDDA, 0xC5FCC5DB, 0x6DAB6DDC, 0x556455DD, 0x1D281DDE, 0x25E725DF, 0xA432A4E0, 0x9CFD9CE1, 0xD4B1D4E2, 0xEC7EECE3, 0x442944E4, 0x7CE67CE5, 0x34AA34E6, 0x0C650CE7, 0x790479E8, 0x41CB41E9, 0x098709EA, 0x314831EB, 0x991F99EC, 0xA1D0A1ED, 0xE99CE9EE, 0xD153D1EF, 0x035E03F0, 0x3B913BF1, 0x73DD73F2, 0x4B124BF3, 0xE345E3F4, 0xDB8ADBF5, 0x93C693F6, 0xAB09ABF7, 0xDE68DEF8, 0xE6A7E6F9, 0xAEEBAEFA, 0x962496FB, 0x3E733EFC, 0x06BC06FD, 0x4EF04EFE, 0x763F76FF}; int i; const uint8_t *d = start; for (i = 0; i < cnt; i++) { crc = (crc << 8) ^ crcTable[*d ^ (crc >> 24)]; d++; } return crc; } /*---------------------------------------------------------------------------- ** ABSTRACT: function to compute and append LBP CRC to a data block ** INPUTS: blkbuf - starting address of the data block to protect ** blklen - length of block to protect (NOT including CRC) ** bigendian - Check CRC in big/little endian ** OUTPUTS: uint32_t - length of protected block (to write) including LBP CRC */ uint32_t BlockProtectRSCRC(uint8_t *blkbuf, uint32_t blklen, int32_t bigendian) { uint32_t crc = GenerateRSCRC(0x00000000, blklen, blkbuf); if (blklen == 0) return 0; /* no such thing as a zero length block in SSC (write NOP) */ if (bigendian) { /* append CRC in proper byte order (regardless of system endian-ness) */ blkbuf[blklen + 0] = (crc >> 24) & 0xFF; blkbuf[blklen + 1] = (crc >> 16) & 0xFF; blkbuf[blklen + 2] = (crc >> 8) & 0xFF; blkbuf[blklen + 3] = (crc >> 0) & 0xFF; } else { blkbuf[blklen + 0] = (crc >> 0) & 0xFF; blkbuf[blklen + 1] = (crc >> 8) & 0xFF; blkbuf[blklen + 2] = (crc >> 16) & 0xFF; blkbuf[blklen + 3] = (crc >> 24) & 0xFF; } return (blklen + 4); /* size of block to be written includes CRC */ } /*---------------------------------------------------------------------------- ** ABSTRACT: function to verify block with LBP CRC ** INPUTS: blkbuf - starting address of the data block to protect ** blklen - length of block to verify (INCLUDING CRC) ** bigendian - Check CRC in big/little endian ** OUTPUTS: uint32_t - length of block w/o CRC (0 if verify failed) */ uint32_t BlockVerifyRSCRC(const uint8_t *blkbuf, uint32_t blklen, int32_t bigendian) { if (blklen <= 4) return 0; /* block is too small to be valid, cannot check CRC */ blklen -= 4; /* user data portion does not include CRC */ uint32_t crccmp; uint32_t crcblk; /* this matches the append method in the function above */ crcblk = (blkbuf[blklen + 0] << 24) | (blkbuf[blklen + 1] << 16) | (blkbuf[blklen + 2] << 8) | (blkbuf[blklen + 3] << 0); if (bigendian) { crccmp = GenerateRSCRC(0x00000000, blklen, blkbuf); } else { crccmp = __bswap_32(GenerateRSCRC(0x00000000, blklen, blkbuf)); } if (crccmp != crcblk) return 0; /* block CRC is incorrect */ return (blklen); #if 2 /* method 2: calculate including CRC and check magic constant */ { if (GenerateRSCRC(0x00000000, blklen + 4, blkbuf) != 0x00000000) return 0; /* block CRC is incorrect (CRC did not neutralize) */ return (blklen); } #endif } ================================================ FILE: usr/utils/subprocess.c ================================================ /* * Run subprocess * * Copyright (C) 2012 Ivo De Decker ivo.dedecker@ugent.be * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include "logging.h" static pid_t pid; static int timedout; void alarm_timeout(int sig) { alarm(0); timedout = 1; if (pid) kill(pid, 9); } int run_command(char *command, int timeout) { pid = fork(); if (!pid) { /* child */ execlp("/bin/sh", "/bin/sh", "-c", command, (char *)NULL); } else if (pid < 0) { /* TODO error handling */ return -1; } else { signal(SIGALRM, alarm_timeout); timedout = 0; alarm(timeout); int status; while (waitpid(pid, &status, 0) <= 0) usleep(1); alarm(0); if (WIFEXITED(status)) { int res = WEXITSTATUS(status); return res; } else if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); MHVTL_DBG(1, "command died with signal: %d " "(timedout: %d)\n", sig, timedout); return -sig; } } return -1; } ================================================ FILE: usr/utils/validate_crc.c ================================================ /* * Validate Reed-Solomon CRC and CRC32C routines pass * basic sanity check at compile time * * Shamelessly lifted/copied from CASTOR utils/CRCtest.cpp * * Designed to abort if CRC32C or RS-CRC fails basic sanity check */ #include #include #include uint32_t crc32c(uint32_t seed, const uint8_t *buf, size_t sz); uint32_t GenerateRSCRC(uint32_t seed, int sz, const uint8_t *buf); int main(int argc, char *argv[]) { const uint8_t block1[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157}; const uint8_t block2[] = {163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251}; uint32_t computedCRC1 = crc32c(0, block1, sizeof(block1)); uint32_t computedCRC2 = crc32c(~computedCRC1, block2, sizeof(block2)); uint32_t computedCRC3 = crc32c(~crc32c(0, block1, sizeof(block1)), block2, sizeof(block2)); assert(computedCRC1 == 0xE8174F48); assert(computedCRC2 == 0x56DAB0A6); assert(computedCRC3 == 0x56DAB0A6); computedCRC1 = GenerateRSCRC(0, sizeof(block1), block1); computedCRC2 = GenerateRSCRC(computedCRC1, sizeof(block2), block2); computedCRC3 = GenerateRSCRC(GenerateRSCRC(0, sizeof(block1), block1), sizeof(block2), block2); assert(computedCRC1 == 0x733D4DCA); assert(computedCRC2 == 0x754ED37E); assert(computedCRC3 == 0x754ED37E); return 0; } ================================================ FILE: usr/vtl_cart_type.c ================================================ /* * Version 2 of tape format. * * Each media contains 3 files. * - The .data file contains each block of data written to the media * - The .indx file consists of an array of one raw_header structure per * written tape block or filemark. * - The .meta file consists of a MAM structure followed by a meta_header * structure, followed by a variable-length array of filemark block numbers. * * Copyright (C) 2009 - 2010 Kevan Rehm * * This is a vtllibrary 'helper'. This contains a function to open the media * metadata file and return the media type (Data, WORM, Cleaning etc) * Required to fill in details of READ ELEMENT STATUS - instead of just guessing * based on barcode, open the actual media metadata and read 'from the horses * mouth' * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _FILE_OFFSET_BITS 64 #define __STDC_FORMAT_MACROS /* for PRId64 */ /* for unistd.h pread/pwrite and fcntl.h posix_fadvise */ #define _XOPEN_SOURCE 600 #include #include #include #include #include #include #include #include "logging.h" #include "vtllib.h" #include "vtlcart.h" static char currentPCL[HOME_DIR_PATH_SZ + MAX_BARCODE_LEN + 3]; /* make room for home_dir plus some */ /* * Attempt to open PCL metadata and read cart type * * Returns valid SMC media type values (READ ELEMENT STATUS): * == 0 -> open failed or unknown cart type * == 1 -> Data Cart type * == 2 -> Cleaning * == 3 -> Diagnostics * == 4 -> WORM * * == 5 -> NULL media type (SMC specifies values > 4 as 'reserved') */ void update_home_dir(long lib_id) { if (strlen(home_directory) < 2) { find_media_home_directory(NULL, lib_id); MHVTL_DBG(3, "Setting home dir to %s", home_directory); } } int get_cart_type(const char *barcode) { char pcl[MAX_BARCODE_LEN + 1]; char path[HOME_DIR_PATH_SZ + MAX_BARCODE_LEN + 4 + 10]; int rc = 0; int mamfile = -1; struct MAM tmp_mam; /* init tmp_mam */ init_mam(&tmp_mam); /* copy barcode to &pcl and terminate at either ' ' or '\0' */ for (int i = 0; i < MAX_BARCODE_LEN; i++) { pcl[i] = barcode[i]; if (pcl[i] == ' ' || pcl[i] == '\0') { pcl[i] = '\0'; break; } } snprintf(currentPCL, ARRAY_SIZE(currentPCL), "%s/%s", strlen(home_directory) ? home_directory : MHVTL_HOME_PATH, pcl); /* Open mam file */ snprintf(path, ARRAY_SIZE(path), "%s/mam", currentPCL); mamfile = open(path, O_RDWR | O_LARGEFILE); if (mamfile == -1) { MHVTL_ERR("open of file %s failed: %s", path, strerror(errno)); rc = 0; goto failed; } /* Read in the MAM */ read_mam(mamfile, -1, &tmp_mam); switch (tmp_mam.MediumType) { case MEDIA_TYPE_NULL: rc = 5; /* Reserved */ break; case MEDIA_TYPE_DATA: rc = 1; break; case MEDIA_TYPE_CLEAN: rc = 2; break; case MEDIA_TYPE_DIAGNOSTIC: rc = 3; break; case MEDIA_TYPE_WORM: rc = 4; break; default: rc = 0; break; } failed: if (mamfile >= 0) close(mamfile); MHVTL_DBG(3, "Opening media: %s (barcode %s), returning type %d", currentPCL, barcode, rc); return rc; } ================================================ FILE: usr/vtlcart.c ================================================ /* * Version 2 of tape format. * * Each media contains 3 files. * - The .data file contains each block of data written to the media * - The .indx file consists of an array of one raw_header structure per * written tape block or filemark. * - The .meta file consists of a MAM structure followed by a meta_header * structure, followed by a variable-length array of filemark block numbers. * * Copyright (C) 2009 - 2010 Kevan Rehm * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _FILE_OFFSET_BITS 64 #define __STDC_FORMAT_MACROS /* for PRId64 */ /* for unistd.h pread/pwrite and fcntl.h posix_fadvise */ #define _XOPEN_SOURCE 600 #include #include #include #include #include #include #include #include #include "logging.h" #include "mhvtl_scsi.h" #include "vtlcart.h" #include "vtllib.h" #include "mhvtl_update.h" #include "be_byteshift.h" /* The .indx file consists of an array of one raw_header structure per written tape block or filemark. There is no separate raw_header structure required for BOT or EOM. The raw_header structure is padded out to 512 bytes to allow for the addition of fields in the future without breaking backwards compatibility with existing PCL indx files. */ struct raw_header { loff_t data_offset; struct blk_header hdr; char pad[512 - sizeof(loff_t) - sizeof(struct blk_header)]; }; /* The .meta file consists of a meta_header structure, followed by a variable-length array of filemark block numbers. Both the MAM and meta_header structures also contain padding to allow for future expansion with backwards compatibility. */ struct meta_header { uint32_t filemark_count; char pad[512 - sizeof(uint32_t)]; }; static char *currentPCL = NULL; static int datafile[MAX_PARTITIONS] = {[0 ... MAX_PARTITIONS - 1] = -1}; static int indxfile[MAX_PARTITIONS] = {[0 ... MAX_PARTITIONS - 1] = -1}; static int metafile[MAX_PARTITIONS] = {[0 ... MAX_PARTITIONS - 1] = -1}; static int mamfile = -1; static int mhvtlfile = -1; static struct raw_header raw_pos; static struct meta_header meta[MAX_PARTITIONS]; static uint64_t eod_data_offset[MAX_PARTITIONS]; static uint32_t eod_blk_number[MAX_PARTITIONS]; #define FM_DELTA 500 static int filemark_alloc[MAX_PARTITIONS] = {[0 ... MAX_PARTITIONS - 1] = 0}; static uint32_t *filemarks[MAX_PARTITIONS] = {[0 ... MAX_PARTITIONS - 1] = NULL}; /* Initialisation of current position (global blk_header) */ struct blk_header *c_pos = &raw_pos.hdr; #ifdef MHVTL_DEBUG static char *mhvtl_block_type_desc(int blk_type) { unsigned int i; static const struct { int blk_type; char *desc; } block_type_desc[] = { {B_FILEMARK, "FILEMARK"}, {B_EOD, "END OF DATA"}, {B_NOOP, "NO OP"}, {B_DATA, "DATA"}, }; for (i = 0; i < ARRAY_SIZE(block_type_desc); i++) if (block_type_desc[i].blk_type == blk_type) return block_type_desc[i].desc; return NULL; } #endif /* * Returns: * == 0, success * != 0, failure */ static int mkEODHeader(uint32_t blk_number, uint64_t data_offset) { uint32_t partition_id = c_pos->partition_id; memset(&raw_pos, 0, sizeof(raw_pos)); raw_pos.data_offset = data_offset; c_pos->blk_type = B_EOD; c_pos->blk_number = blk_number; c_pos->partition_id = partition_id; eod_blk_number[c_pos->partition_id] = blk_number; eod_data_offset[c_pos->partition_id] = data_offset; OK_to_write = 1; return 0; } /* * Returns: * == 0, success * != 0, failure */ static int read_header(uint32_t blk_number, uint8_t *sam_stat) { loff_t nread; MHVTL_DBG(3, "Reading header partition/block %d/%d", c_pos->partition_id, blk_number); if (blk_number > eod_blk_number[c_pos->partition_id]) { MHVTL_ERR("Attempt to seek [%d] beyond EOD [%d]", blk_number, eod_blk_number[c_pos->partition_id]); } else if (blk_number == eod_blk_number[c_pos->partition_id]) mkEODHeader(eod_blk_number[c_pos->partition_id], eod_data_offset[c_pos->partition_id]); else { nread = pread(indxfile[c_pos->partition_id], &raw_pos, sizeof(raw_pos), blk_number * sizeof(raw_pos)); if (nread < 0) { MHVTL_ERR("Medium format corrupt"); sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return -1; } else if (nread != sizeof(raw_pos)) { MHVTL_ERR("Failed to read next header"); sam_medium_error(E_END_OF_DATA, sam_stat); return -1; } } MHVTL_DBG(3, "Reading header partition/block %u/%u at disk offset %ld, type: %s, size: %d", c_pos->partition_id, raw_pos.hdr.blk_number, (unsigned long)raw_pos.data_offset, mhvtl_block_type_desc(raw_pos.hdr.blk_type), raw_pos.hdr.blk_size); return 0; } static int tape_loaded(uint8_t *sam_stat) { if (datafile[c_pos->partition_id] != -1) return 1; sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return 0; } static int rewrite_meta_file(void) { ssize_t io_size, nwrite; size_t io_offset; io_size = sizeof(struct meta_header); nwrite = pwrite(metafile[c_pos->partition_id], &meta[c_pos->partition_id], io_size, 0); if (nwrite < 0) { MHVTL_ERR("Error writing meta_header to metafile: %s", strerror(errno)); return -1; } if (nwrite != io_size) { MHVTL_ERR("Error writing meta_header map to metafile." " Expected to write %d bytes", (int)io_size); return -1; } io_size = meta[c_pos->partition_id].filemark_count * sizeof(*filemarks[c_pos->partition_id]); io_offset = sizeof(struct meta_header); if (io_size) { nwrite = pwrite(metafile[c_pos->partition_id], filemarks[c_pos->partition_id], io_size, io_offset); if (nwrite < 0) { MHVTL_ERR("Error writing filemark map to metafile: %s", strerror(errno)); return -1; } if (nwrite != io_size) { MHVTL_ERR("Error writing filemark map to metafile." " Expected to write %d bytes", (int)io_size); return -1; } } /* If filemarks were overwritten, the meta file may need to be shorter than before. */ if (ftruncate(metafile[c_pos->partition_id], io_offset + io_size) < 0) { MHVTL_ERR("Error truncating metafile: %s", strerror(errno)); return -1; } return 0; } static int check_for_overwrite(uint8_t *sam_stat) { uint32_t blk_number; uint64_t data_offset; unsigned int i; if (c_pos->blk_type == B_EOD) return 0; MHVTL_DBG(2, "At block %ld", (unsigned long)c_pos->blk_number); /* We aren't at EOD so we are performing a rewrite. Truncate the data and index files back to the current length. */ blk_number = c_pos->blk_number; data_offset = raw_pos.data_offset; if (ftruncate(indxfile[c_pos->partition_id], blk_number * sizeof(raw_pos))) { sam_medium_error(E_WRITE_ERROR, sam_stat); MHVTL_ERR("Index file ftruncate failure, pos: " "%" PRId64 ": %s", (uint64_t)blk_number * sizeof(raw_pos), strerror(errno)); return -1; } if (ftruncate(datafile[c_pos->partition_id], data_offset)) { sam_medium_error(E_WRITE_ERROR, sam_stat); MHVTL_ERR("Data file ftruncate failure, pos: " "%" PRId64 ": %s", data_offset, strerror(errno)); return -1; } /* Update the filemark map removing any filemarks which will be overwritten. Rewrite the filemark map so that the on-disk image of the map is consistent with the new sizes of the other two files. */ for (i = 0; i < meta[c_pos->partition_id].filemark_count; i++) { MHVTL_DBG(2, "filemarks[c_pos->partition_id][%d] %d", i, filemarks[c_pos->partition_id][i]); if (filemarks[c_pos->partition_id][i] >= blk_number) { MHVTL_DBG(2, "Setting filemark_count from %d to %d", meta[c_pos->partition_id].filemark_count, i); meta[c_pos->partition_id].filemark_count = i; return rewrite_meta_file(); } } return 0; } static int check_filemarks_alloc(uint32_t count) { uint32_t new_size; /* See if we have enough space allocated to hold 'count' filemarks. If not, realloc now. */ if (count > (uint32_t)filemark_alloc[c_pos->partition_id]) { new_size = ((count + FM_DELTA - 1) / FM_DELTA) * FM_DELTA; filemarks[c_pos->partition_id] = (uint32_t *)realloc(filemarks[c_pos->partition_id], new_size * sizeof(*filemarks[c_pos->partition_id])); if (filemarks[c_pos->partition_id] == NULL) { MHVTL_ERR("filemark map realloc failed, %s", strerror(errno)); return -1; } filemark_alloc[c_pos->partition_id] = new_size; } return 0; } static int add_filemark(uint32_t blk_number) { /* See if we have enough space remaining to add the new filemark. If not, realloc now. */ if (check_filemarks_alloc(meta[c_pos->partition_id].filemark_count + 1)) return -1; filemarks[c_pos->partition_id][meta[c_pos->partition_id].filemark_count++] = blk_number; /* Now rewrite the meta_header structure and the filemark map. */ return rewrite_meta_file(); } /* * Return 0 -> Not loaded. * 1 -> Load OK * 2 -> format corrupt. */ int rewind_tape(uint8_t *sam_stat) { if (!tape_loaded(sam_stat)) return -1; if (read_header(0, sam_stat)) return -1; switch (mam.MediumType) { case MEDIA_TYPE_CLEAN: OK_to_write = 0; break; case MEDIA_TYPE_WORM: /* Check if this header is a filemark and the next header * is End of Data. If it is, we are OK to write */ if (c_pos->blk_type == B_EOD || (c_pos->blk_type == B_FILEMARK && eod_blk_number[c_pos->partition_id] == 1)) OK_to_write = 1; else OK_to_write = 0; break; case MEDIA_TYPE_DATA: OK_to_write = 1; /* Reset flag to OK. */ break; } MHVTL_DBG(1, "Media is%s writable", (OK_to_write) ? "" : " not"); return 1; } /* * Returns: * == 0, success * != 0, failure */ int position_to_eod(uint8_t *sam_stat) { if (!tape_loaded(sam_stat)) return -1; if (read_header(eod_blk_number[c_pos->partition_id], sam_stat)) return -1; if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 1; return 0; } /* * Returns: * == 0, success * != 0, failure */ int position_to_block(uint32_t blk_number, uint8_t *sam_stat) { if (!tape_loaded(sam_stat)) return -1; MHVTL_DBG(2, "Position to partition/block %u/%u", c_pos->partition_id, blk_number); if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; if (blk_number > eod_blk_number[c_pos->partition_id]) { sam_blank_check(E_END_OF_DATA, sam_stat); MHVTL_DBG(1, "End of data detected while positioning"); return position_to_eod(sam_stat); } /* Treat a position to block zero specially, as it has different semantics than other blocks when the tape is WORM. */ if (blk_number == 0) return rewind_tape(sam_stat); else return read_header(blk_number, sam_stat); } /* * Returns: * == 0, success * != 0, failure */ int position_blocks_forw(uint64_t count, uint8_t *sam_stat) { uint32_t residual; uint32_t blk_target; unsigned int i; if (!tape_loaded(sam_stat)) return -1; MHVTL_DBG(3, "Moving %lu blocks forward from block %u/%u", count, c_pos->partition_id, c_pos->blk_number); if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; blk_target = c_pos->blk_number + count; /* Find the first filemark forward from our current position, if any. */ for (i = 0; i < meta[c_pos->partition_id].filemark_count; i++) { MHVTL_DBG(3, "filemark at %ld", (unsigned long)filemarks[c_pos->partition_id][i]); if (filemarks[c_pos->partition_id][i] >= c_pos->blk_number) break; } /* If there is one, see if it is between our current position and our desired destination. */ if (i < meta[c_pos->partition_id].filemark_count) { if (filemarks[c_pos->partition_id][i] >= blk_target) return position_to_block(blk_target, sam_stat); residual = blk_target - c_pos->blk_number + 1; if (read_header(filemarks[c_pos->partition_id][i] + 1, sam_stat)) return -1; MHVTL_DBG(1, "Filemark encountered: block %d", filemarks[c_pos->partition_id][i]); sam_no_sense(SD_FILEMARK, E_MARK, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } if (blk_target > eod_blk_number[c_pos->partition_id]) { residual = blk_target - eod_blk_number[c_pos->partition_id]; if (read_header(eod_blk_number[c_pos->partition_id], sam_stat)) return -1; MHVTL_DBG(1, "Moving to EOD encountered at block %u", eod_blk_number[c_pos->partition_id]); sam_blank_check(E_END_OF_DATA, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } return position_to_block(blk_target, sam_stat); } /* * Returns: * == 0, success * != 0, failure */ int position_blocks_back(uint64_t count, uint8_t *sam_stat) { uint32_t residual; uint32_t blk_target; int i; if (!tape_loaded(sam_stat)) return -1; MHVTL_DBG(2, "Moving %lu blocks backward from block %u/%u", count, c_pos->partition_id, c_pos->blk_number); if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; blk_target = c_pos->blk_number - count; /* Find the first filemark prior to our current position, if any. */ for (i = meta[c_pos->partition_id].filemark_count - 1; i >= 0; i--) { if (filemarks[c_pos->partition_id][i] < c_pos->blk_number) break; } /* If there is one, see if it is between our current position and our desired destination. */ if (i >= 0) { if (filemarks[c_pos->partition_id][i] < blk_target) return position_to_block(blk_target, sam_stat); residual = c_pos->blk_number - blk_target; if (read_header(filemarks[c_pos->partition_id][i], sam_stat)) return -1; MHVTL_DBG(2, "Filemark encountered: block %d", filemarks[c_pos->partition_id][i]); sam_no_sense(SD_FILEMARK, E_MARK, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } if (blk_target < 0) { residual = count - c_pos->blk_number; if (read_header(0, sam_stat)) return -1; MHVTL_DBG(1, "Moving to BOP encountered at block 0"); sam_no_sense(SD_EOM, E_BOM, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } return position_to_block(blk_target, sam_stat); } /* * Returns: * == 0, success * != 0, failure */ int position_filemarks_forw(uint64_t count, uint8_t *sam_stat) { uint32_t residual; unsigned int i; if (!tape_loaded(sam_stat)) return -1; if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; /* Find the block number of the first filemark greater than our current position. */ for (i = 0; i < meta[c_pos->partition_id].filemark_count; i++) if (filemarks[c_pos->partition_id][i] >= c_pos->blk_number) break; if (i + count - 1 < meta[c_pos->partition_id].filemark_count) return position_to_block(filemarks[c_pos->partition_id][i + count - 1] + 1, sam_stat); else { residual = i + count - meta[c_pos->partition_id].filemark_count; if (read_header(eod_blk_number[c_pos->partition_id], sam_stat)) return -1; sam_blank_check(E_END_OF_DATA, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } } /* * Returns: * == 0, success * != 0, failure */ int position_filemarks_back(uint64_t count, uint8_t *sam_stat) { uint32_t residual; int i; if (!tape_loaded(sam_stat)) return -1; if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; /* Find the block number of the first filemark less than our current position. */ for (i = meta[c_pos->partition_id].filemark_count - 1; i >= 0; i--) if (filemarks[c_pos->partition_id][i] < c_pos->blk_number) break; if (i + 1 >= count) return position_to_block(filemarks[c_pos->partition_id][i - count + 1], sam_stat); else { residual = count - i - 1; if (read_header(0, sam_stat)) return -1; sam_no_sense(SD_EOM, E_BOM, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } } /* * Reads data in mam file to the given mam pointer * if a fd is closed, the file is not read and no error is raised * Returns 0 if successful or -1 on error */ int read_mam(int mam_fd, int mhvtl_fd, struct MAM *mamp) { if (mam_fd >= 0) { struct MAM_attr attr; if (lseek(mam_fd, 0, SEEK_SET) != 0) { perror("fseek"); return -1; } /* versions */ if (read(mam_fd, &mamp->tape_fmt_version, sizeof(mamp->tape_fmt_version)) != sizeof(mamp->tape_fmt_version)) return -1; if (read(mam_fd, &mamp->mam_fmt_version, sizeof(mamp->mam_fmt_version)) != sizeof(mamp->tape_fmt_version)) return -1; /* mam attributes */ while (read(mam_fd, &attr.attribute_id, sizeof(uint16_t)) == sizeof(uint16_t)) { int idx = -1; if (read(mam_fd, &attr.length, sizeof(uint16_t)) != sizeof(uint16_t)) { MHVTL_ERR("Error reading attribute %04x length : %s", attr.attribute_id, strerror(errno)); return -1; } for (int i = 0; i <= MAM_ATTRIBUTE_END; i++) { if (mamp->attributes[i].attribute_id == attr.attribute_id) { idx = i; break; } } /* attribute not known: skip value */ if (idx < 0) { if (attr.length) lseek(mam_fd, attr.length, SEEK_CUR); continue; } if (read(mam_fd, mamp->attributes[idx].value, attr.length) != attr.length) { MHVTL_ERR("Error reading mam attribute %04x value : %s", attr.attribute_id, strerror(errno)); return -1; } } } /* mhvtl attributes */ if (mhvtl_fd >= 0) { struct MHVTL_attr attr; if (lseek(mhvtl_fd, 0, SEEK_SET) != 0) { perror("fseek"); return -1; } while (read(mhvtl_fd, &attr.attribute_id, sizeof(uint16_t)) == sizeof(uint16_t)) { int idx = -1; if (read(mhvtl_fd, &attr.length, sizeof(uint16_t)) != sizeof(uint16_t)) { MHVTL_ERR("Error reading mhvtl attribute %04x length : %s", attr.attribute_id, strerror(errno)); return -1; } for (int i = 0; i <= MAM_MHVTL_ATTRIBUTE_END; i++) { if (mamp->mhvtl_attr[i].attribute_id == attr.attribute_id) { idx = i; break; } } /* attribute not known: skip value */ if (idx < 0) { if (attr.length) lseek(mhvtl_fd, attr.length, SEEK_CUR); continue; } if (read(mhvtl_fd, mamp->mhvtl_attr[idx].value, attr.length) != attr.length) { MHVTL_ERR("Error reading mhvtl attribute %04x value : %s", attr.attribute_id, strerror(errno)); return -1; } } } return 0; } /* * Writes data in global struct MAM in mam/mhvtl_data files * Using a Type-Length-Value format to keep mam auto-descriptive * Returns 0 if nothing written or -1 on error */ int write_mam(int mam_fd, int mhvtl_fd) { if ((lseek(mam_fd, 0, SEEK_SET) != 0) || (lseek(mhvtl_fd, 0, SEEK_SET) != 0)) { perror("fseek"); return -1; } /* versions */ if (write(mam_fd, &mam.tape_fmt_version, sizeof(mam.tape_fmt_version)) != sizeof(mam.tape_fmt_version)) return -1; if (write(mam_fd, &mam.mam_fmt_version, sizeof(mam.mam_fmt_version)) != sizeof(mam.tape_fmt_version)) return -1; /* mam attributes */ for (int i = 0; i < MAM_ATTRIBUTE_END; i++) { const struct MAM_attr *attr = &mam.attributes[i]; if (write(mam_fd, &attr->attribute_id, sizeof(uint16_t)) != sizeof(uint16_t)) return -1; if (write(mam_fd, &attr->length, sizeof(uint16_t)) != sizeof(uint16_t)) return -1; if (write(mam_fd, attr->value, attr->length) != attr->length) return -1; } /* mhvtl attributes */ for (int i = 0; i < MAM_MHVTL_ATTRIBUTE_END; i++) { const struct MHVTL_attr *attr = &mam.mhvtl_attr[i]; if (write(mhvtl_fd, &attr->attribute_id, sizeof(uint16_t)) != sizeof(uint16_t)) return -1; if (write(mhvtl_fd, &attr->length, sizeof(uint16_t)) != sizeof(uint16_t)) return -1; if (attr->length == 0) continue; if (write(mhvtl_fd, attr->value, attr->length) != attr->length) return -1; } return 0; } /* * Writes data in struct MAM in mam and mhvtl_data files if tape is loaded * Returns 0 if nothing written or -1 on error */ int rewriteMAM(uint8_t *sam_stat) { char mam_path[1024]; char mhvtl_data_path[1024]; if (!tape_loaded(sam_stat)) return -1; /* Rewrite MAM data */ snprintf(mam_path, sizeof(mam_path), "%s/mam", currentPCL); snprintf(mhvtl_data_path, sizeof(mhvtl_data_path), "%s/mhvtl_data", currentPCL); if (write_mam(mamfile, mhvtlfile) < 0) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return -1; }; return 0; } static void unlink_partition(int partition_number) { char path[1024]; if (datafile >= 0) { snprintf(path, sizeof(path), "%s/data.%d", currentPCL, partition_number); unlink(path); } if (indxfile >= 0) { snprintf(path, sizeof(path), "%s/indx.%d", currentPCL, partition_number); unlink(path); } if (metafile >= 0) { snprintf(path, sizeof(path), "%s/meta.%d", currentPCL, partition_number); unlink(path); } } static int open_partition(uint8_t partition_number) { int rc = 0; char pcl_data[1024], pcl_indx[1024], pcl_meta[1024]; const char *pcl_files[3] = {pcl_data, pcl_indx, pcl_meta}; struct stat data_stat, indx_stat, meta_stat; struct stat *stats[3] = {&data_stat, &indx_stat, &meta_stat}; int *fd_open[3] = {&datafile[partition_number], &indxfile[partition_number], &metafile[partition_number]}; snprintf(pcl_data, ARRAY_SIZE(pcl_data), "%s/data.%d", currentPCL, partition_number); snprintf(pcl_indx, ARRAY_SIZE(pcl_indx), "%s/indx.%d", currentPCL, partition_number); snprintf(pcl_meta, ARRAY_SIZE(pcl_meta), "%s/meta.%d", currentPCL, partition_number); for (int i = 0; i < 3; i++) { *fd_open[i] = open(pcl_files[i], O_RDWR | O_LARGEFILE); if (*fd_open[i] == -1) { MHVTL_ERR("open of file %s failed: %s", pcl_files[i], strerror(errno)); rc = 3; } if (fstat(*fd_open[i], stats[i]) < 0) { MHVTL_ERR("stat of pcl %s file %s failed: %s", currentPCL, pcl_files[i], strerror(errno)); rc = 3; } } return rc; } static void close_partition(uint8_t partition_number) { int *fd_close[3] = {&datafile[partition_number], &indxfile[partition_number], &metafile[partition_number]}; for (int i = 0; i < 3; i++) { if (*fd_close[i] >= 0) { close(*fd_close[i]); *fd_close[i] = -1; } } } int change_partition(uint8_t partition_number) { uint8_t *sam_stat = SAM_STAT_GOOD; int rc = 0; close_partition(c_pos->partition_id); c_pos->partition_id = partition_number; rc = open_partition(partition_number); read_header(0, sam_stat); return rc; } static void erase_partition(uint8_t *sam_stat) { /* Erasing all data instead of (ideally) putting Data Set Separator (DSS) from EOD */ c_pos->blk_number = 0; raw_pos.data_offset = 0; format_partition(sam_stat); close_partition(c_pos->partition_id); unlink_partition(c_pos->partition_id); } /* * Returns: * == 0, the new partition was successfully created. * == 2, could not create some file(s) * == 1, an error occurred. */ static int create_partition(int partition_number) { char path[1024]; int *fd[3] = {&datafile[partition_number], &indxfile[partition_number], &metafile[partition_number]}; const char *file_name[] = {"data", "indx", "meta"}; for (int k = 0; k < 3; k++) { snprintf(path, ARRAY_SIZE(path), "%s/%s.%d", currentPCL, file_name[k], partition_number); if (verbose) printf("Creating new media %s file: %s\n", file_name[k], path); *fd[k] = open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (*fd[k] == -1) { MHVTL_ERR("Failed to create file %s: %s", path, strerror(errno)); close_partition(partition_number); unlink_partition(partition_number); return 2; } } MHVTL_LOG("%s files created", currentPCL); /* Write the meta file consisting of the meta_header structure with the filemark count initialized to zero. */ memset(&meta[partition_number], 0, sizeof(struct meta_header)); meta[partition_number].filemark_count = 0; if (write(metafile[partition_number], &meta[partition_number], sizeof(struct meta_header)) != sizeof(struct meta_header)) { snprintf(path, ARRAY_SIZE(path), "%s/meta.%d", currentPCL, partition_number); MHVTL_ERR("Failed to initialize file %s: %s", path, strerror(errno)); close_partition(partition_number); unlink_partition(partition_number); return 1; } close_partition(partition_number); return 0; } /* * Returns: * == 0, the new PCL was successfully created. * == 2, could not create the directory or some file(s) * == 1, an error occurred. */ int create_tape(const char *pcl, uint8_t *sam_stat) { struct stat data_stat; char path[1024]; char mhvtl_data_path[1024]; /* Attempt to create the new PCL. This will fail if the PCL's directory or any of the PCL's three files already exist, leaving any existing files as they were. */ if (asprintf(¤tPCL, "%s/%s", home_directory, pcl) < 0) { perror("Could not allocate memory"); exit(1); } /* Check if data file already exists, nothing to create */ snprintf(path, ARRAY_SIZE(path), "%s/data.0", currentPCL); if (stat(path, &data_stat) != -1) { if (verbose) printf("error: Data file already exists for new media\n"); return 0; } if (verbose) printf("Creating new media directory: %s\n", currentPCL); if (mkdir(currentPCL, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_ISGID)) { /* No need to fail just because the parent dir exists */ if (errno != EEXIST) { MHVTL_ERR("Failed to create directory %s: %s", currentPCL, strerror(errno)); free(currentPCL); return 2; } } /* create mam/mhvtl_data files and fill them */ snprintf(path, ARRAY_SIZE(path), "%s/mam", currentPCL); snprintf(mhvtl_data_path, ARRAY_SIZE(mhvtl_data_path), "%s/mhvtl_data", currentPCL); if (verbose) printf("Creating new media mam files: %s and %s\n", path, mhvtl_data_path); mamfile = open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (mamfile == -1) { MHVTL_ERR("Failed to create file %s: %s", path, strerror(errno)); return 2; } mhvtlfile = open(mhvtl_data_path, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (mhvtlfile == -1) { MHVTL_ERR("Failed to create file %s: %s", mhvtl_data_path, strerror(errno)); return 2; } if (write_mam(mamfile, mhvtlfile) < 0) { MHVTL_ERR("Failed to initialize mam/mhvtl_data files"); }; close(mamfile); mamfile = -1; close(mhvtlfile); mhvtlfile = -1; return create_partition(0); } int load_partition(const char *pcl, uint8_t *sam_stat, uint8_t error_check, uint8_t partition_number) { int rc = 0; char pcl_data[1024], pcl_meta[1024]; struct stat data_stat, indx_stat, meta_stat; struct stat *stats[3] = {&data_stat, &indx_stat, &meta_stat}; int *fd[3] = {&datafile[partition_number], &indxfile[partition_number], &metafile[partition_number]}; uint64_t exp_size; size_t io_size; loff_t nread; /* Open all three files and stat them to get their current sizes. */ snprintf(pcl_data, ARRAY_SIZE(pcl_data), "%s/data.%d", currentPCL, partition_number); if (stat(pcl_data, &data_stat) == -1) { MHVTL_DBG(2, "Couldn't find %s, trying previous default: %s/%s", pcl_data, MHVTL_HOME_PATH, pcl); free(currentPCL); if (asprintf(¤tPCL, "%s/%s", MHVTL_HOME_PATH, pcl) < 0) { perror("Could not allocate memory"); exit(1); } } /* Opening files */ if (open_partition(partition_number) == 3) goto cleanup; for (int i = 0; i < 3; i++) { fstat(*fd[i], stats[i]); } /* Verify that the metafile size is at least reasonable. */ exp_size = sizeof(struct meta_header); if ((uint32_t)meta_stat.st_size < exp_size) { MHVTL_ERR("sizeof(struct meta_header) - " "pcl %s file %s is not the correct length, " "expected at least %" PRId64 ", actual %" PRId64, pcl, pcl_meta, exp_size, meta_stat.st_size); if (error_check) { rc = 2; goto cleanup; } } /* Read in the meta_header structure and sanity-check it. */ nread = read(metafile[partition_number], &meta[partition_number], sizeof(struct meta_header)); if (nread < 0) { MHVTL_ERR("Error reading pcl %s meta_header from " "metafile: %s", pcl, strerror(errno)); rc = 2; goto cleanup; } else if (nread != sizeof(struct meta_header)) { MHVTL_ERR("Error reading pcl %s meta header from " "metafile: unexpected read length", pcl); rc = 2; goto cleanup; } /* Now recompute the correct size of the meta file. */ exp_size += meta[partition_number].filemark_count * sizeof(*filemarks[partition_number]); if ((uint32_t)meta_stat.st_size != exp_size) { MHVTL_ERR("sizeof(struct MAM) + sizeof(struct_meta_header) + sizeof(*filemarks[c_pos->partition_id]) - " "pcl %s file %s is not the correct length, " "expected %" PRId64 ", actual %" PRId64, pcl, pcl_meta, exp_size, meta_stat.st_size); if (error_check) { rc = 2; goto cleanup; } } /* See if we have allocated enough space for the actual number of filemarks on the tape. If not, realloc now. */ if (check_filemarks_alloc(meta[partition_number].filemark_count)) { if (error_check) { rc = 3; goto cleanup; } } /* Now read in the filemark map. */ io_size = meta[partition_number].filemark_count * sizeof(*filemarks[partition_number]); if (io_size) { nread = read(metafile[partition_number], filemarks[partition_number], io_size); if (nread < 0) { MHVTL_ERR("Error reading pcl %s filemark map from " "metafile: %s", pcl, strerror(errno)); rc = 2; goto cleanup; } else if ((size_t)nread != io_size) { MHVTL_ERR("Error reading pcl %s filemark map from " "metafile: unexpected read length", pcl); if (error_check) { rc = 2; goto cleanup; } } } /* Use the size of the indx file to work out where the virtual B_EOD block resides. */ if ((indx_stat.st_size % sizeof(struct raw_header)) != 0) { MHVTL_ERR("pcl %s indx file has improper length, indicating " "possible file corruption", pcl); rc = 2; goto cleanup; } eod_blk_number[partition_number] = indx_stat.st_size / sizeof(struct raw_header); /* Make sure that the filemark map is consistent with the size of the indx file. */ if (meta[partition_number].filemark_count && eod_blk_number[partition_number] && filemarks[partition_number][meta[partition_number].filemark_count - 1] >= eod_blk_number[partition_number]) { MHVTL_ERR("pcl %s indx file has improper length as compared " "to the meta file, indicating possible file corruption", pcl); MHVTL_ERR("Filemark count: %u eod_blk_number: %u", meta[partition_number].filemark_count, eod_blk_number[partition_number]); rc = 2; goto cleanup; } /* Read in the last raw_header struct from the indx file and use that to validate the correct size of the data file. */ if (eod_blk_number[partition_number] == 0) eod_data_offset[partition_number] = 0; else { MHVTL_DBG(3, "Media format sanity check - Reading block before EOD: %u", eod_blk_number[partition_number] - 1); if (read_header(eod_blk_number[partition_number] - 1, sam_stat)) { rc = 3; goto cleanup; } eod_data_offset[partition_number] = raw_pos.data_offset + raw_pos.hdr.disk_blk_size; } if (mam.MediumType == MEDIA_TYPE_NULL) { MHVTL_LOG("Loaded NULL media type"); /* Skip check */ } else if ((uint64_t)data_stat.st_size != eod_data_offset[partition_number]) { MHVTL_ERR("st_size != eod_data_offset - " "pcl %s file %s is not the correct length, " "expected %" PRId64 ", actual %" PRId64, pcl, pcl_data, eod_data_offset[partition_number], data_stat.st_size); if (error_check) { rc = 2; goto cleanup; } } /* Give a hint to the kernel that data, once written, tends not to be accessed again immediately. */ posix_fadvise(indxfile[partition_number], 0, 0, POSIX_FADV_DONTNEED); posix_fadvise(datafile[partition_number], 0, 0, POSIX_FADV_DONTNEED); cleanup: close_partition(partition_number); return rc; } /* * Attempt to load PCL - i.e. Open datafile and read in BOT header & MAM * * Returns: * == 0 -> Load OK * == 1 -> Another tape already loaded. * == 2 -> format corrupt. * == 3 -> cartridge does not exist or cannot be opened. */ int load_tape(const char *pcl, uint8_t *sam_stat) { char path[1024]; char touch_file[128]; uint8_t error_check; struct stat data_stat; loff_t nread; snprintf(touch_file, 127, "%s/bypass_error_check", MHVTL_HOME_PATH); error_check = (stat(touch_file, &data_stat) == -1) ? FALSE : TRUE; if (error_check) { MHVTL_LOG("WARNING - touch file %s found - bypassing sanity checks on open", touch_file); } /* KFRDEBUG - sam_stat needs updates in lots of places here. */ /* If some other PCL is already open, return. */ if (datafile[c_pos->partition_id] >= 0) { MHVTL_DBG(1, "Drive already full, cannot open %s", pcl); return 1; } MHVTL_DBG(1, "Opening media: %s", pcl); /* Determining pcl filepaths */ if (!((strlen(home_directory) && asprintf(¤tPCL, "%s/%s", home_directory, pcl) >= 0) || asprintf(¤tPCL, "%s/%s", MHVTL_HOME_PATH, pcl) >= 0)) { perror("Could not allocate memory"); exit(1); } /* initialize global mam */ init_mam(&mam); /* load MAM and sanity-check it. */ snprintf(path, ARRAY_SIZE(path), "%s/mam", currentPCL); printf("mam from %s\n", path); mamfile = open(path, O_RDWR | O_LARGEFILE); if (mamfile == -1) { /* Check for MAM location update */ MHVTL_ERR("open of file %s failed: %s", path, strerror(errno)); MHVTL_LOG("Trying to find mam in the meta file and update tape format..."); if (try_extract_mam(currentPCL)) { MHVTL_ERR("Could not find or extract mam file : medium corrupted"); return 3; }; mamfile = open(path, O_RDWR | O_LARGEFILE); } /* Reading tape/mam versions by reading tape_fmt_version and mam_fmt_version */ nread = read(mamfile, &mam.tape_fmt_version, sizeof(uint32_t)); if (nread < 0) { MHVTL_ERR("Error reading pcl %s Tape Format Version from mam file: %s", pcl, strerror(errno)); if (error_check) return 2; } nread = read(mamfile, &mam.mam_fmt_version, sizeof(uint32_t)); if (nread < 0) { MHVTL_ERR("Error reading pcl %s MAM Format Version from mam file: %s", pcl, strerror(errno)); if (error_check) return 2; } if (mam.mam_fmt_version != MAM_VERSION) { /* Check for MAM Format Update */ MHVTL_ERR("pcl %s MAM contains incorrect media format", pcl); MHVTL_LOG("Trying to update mam format..."); if (try_update_mam(currentPCL)) { MHVTL_ERR("Error : MAM update failed"); sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); if (error_check) return 2; } } snprintf(path, ARRAY_SIZE(path), "%s/mhvtl_data", currentPCL); mhvtlfile = open(path, O_RDWR | O_LARGEFILE); read_mam(mamfile, mhvtlfile, &mam); if (mam.tape_fmt_version != TAPE_FMT_VERSION) { /* Check for Tape Format Update */ MHVTL_ERR("pcl %s contains incorrect media format", pcl); MHVTL_LOG("Trying to update tape format..."); if (try_update_tape(currentPCL)) { MHVTL_ERR("Error : Tape update failed"); sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); if (error_check) return 2; } } /* load all partitions */ mam.num_partitions = 0; snprintf(path, ARRAY_SIZE(path), "%s/data.%d", currentPCL, mam.num_partitions); while (access(path, F_OK) == 0) { c_pos->partition_id = mam.num_partitions; load_partition(pcl, sam_stat, error_check, mam.num_partitions); snprintf(path, ARRAY_SIZE(path), "%s/data.%d", currentPCL, ++mam.num_partitions); } change_partition(0); /* Initialise SAM STATUS */ *sam_stat = SAM_STAT_GOOD; /* Now initialize raw_pos by reading in the first header, if any. */ if (read_header(0, sam_stat)) { close_partition(c_pos->partition_id); return 3; } return 0; } void zero_filemark_count(void) { free(filemarks[c_pos->partition_id]); filemark_alloc[c_pos->partition_id] = 0; filemarks[c_pos->partition_id] = NULL; meta[c_pos->partition_id].filemark_count = 0; rewrite_meta_file(); } int format_partition(uint8_t *sam_stat) { if (!tape_loaded(sam_stat)) return -1; if (check_for_overwrite(sam_stat)) return -1; zero_filemark_count(); return mkEODHeader(c_pos->blk_number, raw_pos.data_offset); } int format_tape(uint8_t *sam_stat) { char path[1024]; int partition_number = 0; /* Erase all partitions */ snprintf(path, ARRAY_SIZE(path), "%s/data.%d", currentPCL, partition_number); while (access(path, F_OK) == 0) { MHVTL_DBG(1, "Erasing partition %d", partition_number); change_partition(partition_number); erase_partition(sam_stat); snprintf(path, ARRAY_SIZE(path), "%s/data.%d", currentPCL, ++partition_number); } /* Create partitions */ for (int j = 0; j < mam.num_partitions; ++j) { create_partition(j); } change_partition(0); return 0; } /* * Returns: * == 0, success * != 0, failure */ int write_filemarks(uint32_t count, uint8_t *sam_stat) { uint32_t blk_number; uint32_t partition_id; uint64_t data_offset; ssize_t nwrite; if (!tape_loaded(sam_stat)) return -1; /* Applications assume that writing a filemark (even writing zero filemarks) will force-flush any data buffered in the drive to media so that after the write-filemarks call returns there is no possibility that any data previously written could be lost due to a power hit. Provide a similar guarantee here. */ if (count == 0) { MHVTL_DBG(2, "Flushing data - 0 filemarks written"); fsync(datafile[c_pos->partition_id]); fsync(indxfile[c_pos->partition_id]); fsync(metafile[c_pos->partition_id]); return 0; } if (check_for_overwrite(sam_stat)) return -1; /* Preserve existing raw_pos data we need, then clear raw_pos and fill it in with new data. */ blk_number = c_pos->blk_number; partition_id = c_pos->partition_id; data_offset = raw_pos.data_offset; memset(&raw_pos, 0, sizeof(raw_pos)); raw_pos.data_offset = data_offset; c_pos->blk_type = B_FILEMARK; /* Header type */ c_pos->blk_flags = 0; c_pos->blk_number = blk_number; c_pos->blk_size = 0; c_pos->disk_blk_size = 0; c_pos->partition_id = partition_id; /* Now write out one header per filemark. */ for (; count > 0; count--, blk_number++) { c_pos->blk_number = blk_number; MHVTL_DBG(3, "Writing filemark: partition/block %u/%u", partition_id, blk_number); nwrite = pwrite(indxfile[c_pos->partition_id], &raw_pos, sizeof(raw_pos), blk_number * sizeof(raw_pos)); if (nwrite != sizeof(raw_pos)) { sam_medium_error(E_WRITE_ERROR, sam_stat); MHVTL_ERR("Index file write failure," " pos: %" PRId64 ": %s", (uint64_t)blk_number * sizeof(raw_pos), strerror(errno)); return -1; } add_filemark(blk_number); } /* Provide the force-flush guarantee. */ fsync(datafile[c_pos->partition_id]); fsync(indxfile[c_pos->partition_id]); fsync(metafile[c_pos->partition_id]); return mkEODHeader(blk_number, data_offset); } int write_tape_block(const uint8_t *buffer, uint32_t blk_size, uint32_t comp_size, const struct encryption *encryptp, uint8_t comp_type, uint8_t null_media_type, uint32_t crc, uint8_t *sam_stat) { uint32_t blk_number, disk_blk_size, partition_id; uint32_t max_blk_number; uint64_t data_offset; ssize_t nwrite; /* Medium format limits to unsigned 32bit blks */ max_blk_number = 0xfffffff0; if (!tape_loaded(sam_stat)) return -1; if (check_for_overwrite(sam_stat)) return -1; /* Preserve existing raw_pos data we need, then clear out raw_pos and fill it in with new data. */ blk_number = c_pos->blk_number; partition_id = c_pos->partition_id; data_offset = raw_pos.data_offset; if (blk_number > max_blk_number) { MHVTL_ERR("Too many tape blocks - 32bit overflow"); return -1; } memset(&raw_pos, 0, sizeof(raw_pos)); raw_pos.data_offset = data_offset; c_pos->blk_type = B_DATA; /* Header type */ c_pos->blk_flags = 0; c_pos->blk_number = blk_number; c_pos->partition_id = partition_id; c_pos->blk_size = blk_size; /* Size of uncompressed data */ c_pos->uncomp_crc = crc; c_pos->blk_flags |= BLKHDR_FLG_CRC; /* Logical Block Protection */ MHVTL_DBG(2, "CRC is 0x%08x", crc); if (comp_size) { if (comp_type == LZO) c_pos->blk_flags |= BLKHDR_FLG_LZO_COMPRESSED; else c_pos->blk_flags |= BLKHDR_FLG_ZLIB_COMPRESSED; c_pos->disk_blk_size = disk_blk_size = comp_size; } else c_pos->disk_blk_size = disk_blk_size = blk_size; if (encryptp != NULL) { unsigned int i; c_pos->blk_flags |= BLKHDR_FLG_ENCRYPTED; c_pos->blk_encryption_info.ukad_length = encryptp->ukad_length; for (i = 0; i < encryptp->ukad_length; ++i) c_pos->blk_encryption_info.ukad[i] = encryptp->ukad[i]; c_pos->blk_encryption_info.akad_length = encryptp->akad_length; for (i = 0; i < encryptp->akad_length; ++i) c_pos->blk_encryption_info.akad[i] = encryptp->akad[i]; c_pos->blk_encryption_info.key_length = encryptp->key_length; for (i = 0; i < encryptp->key_length; ++i) c_pos->blk_encryption_info.key[i] = encryptp->key[i]; } /* Now write out both the data and the header. */ if (null_media_type) { nwrite = disk_blk_size; } else nwrite = pwrite(datafile[c_pos->partition_id], buffer, disk_blk_size, data_offset); if (nwrite != disk_blk_size) { sam_medium_error(E_WRITE_ERROR, sam_stat); MHVTL_ERR("Data file write failure, pos: %" PRId64 ": %s", data_offset, strerror(errno)); /* Truncate last partital write */ MHVTL_DBG(1, "Truncating data file size: %" PRId64, data_offset); if (ftruncate(datafile[c_pos->partition_id], data_offset) < 0) { MHVTL_ERR("Error truncating data: %s", strerror(errno)); } mkEODHeader(blk_number, data_offset); return -1; } MHVTL_DBG(3, "writing partition/header %d/%u at offset %lu, type: %s, size: %u", c_pos->partition_id, c_pos->blk_number, (unsigned long)raw_pos.data_offset, mhvtl_block_type_desc(c_pos->blk_type), c_pos->blk_size); nwrite = pwrite(indxfile[c_pos->partition_id], &raw_pos, sizeof(raw_pos), blk_number * sizeof(raw_pos)); if (nwrite != sizeof(raw_pos)) { long indxsz = (blk_number - 1) * sizeof(raw_pos); sam_medium_error(E_WRITE_ERROR, sam_stat); MHVTL_ERR("Index file write failure, pos: %" PRId64 ": %s", (uint64_t)blk_number * sizeof(raw_pos), strerror(errno)); MHVTL_DBG(1, "Truncating index file size to: %ld", indxsz); if (ftruncate(indxfile[c_pos->partition_id], indxsz) < 0) { MHVTL_ERR("Error truncating indx: %s", strerror(errno)); } if (!null_media_type) { MHVTL_DBG(1, "Truncating data file size: %" PRId64, data_offset); if (ftruncate(datafile[c_pos->partition_id], data_offset) < 0) { MHVTL_ERR("Error truncating data: %s", strerror(errno)); } } mkEODHeader(blk_number, data_offset); return -1; } MHVTL_DBG(3, "Successfully wrote block: %u", blk_number); return mkEODHeader(blk_number + 1, data_offset + disk_blk_size); } void unload_tape(uint8_t *sam_stat) { for (int j = 0; j < mam.num_partitions; ++j) { MHVTL_DBG(3, "Unloading tape : partition %d", j); change_partition(j); rewrite_meta_file(); close_partition(j); if (filemarks[j]) { free(filemarks[j]); filemarks[j] = NULL; } } memset(filemark_alloc, 0, sizeof(filemark_alloc)); memset(eod_blk_number, 0, sizeof(eod_blk_number)); memset(eod_data_offset, 0, sizeof(eod_data_offset)); if (mamfile >= 0) { close(mamfile); mamfile = -1; } if (mhvtlfile >= 0) { close(mhvtlfile); mhvtlfile = -1; } free(currentPCL); } uint32_t read_tape_block(uint8_t *buf, uint32_t buf_size, uint8_t *sam_stat) { loff_t nread; uint32_t iosize; if (!tape_loaded(sam_stat)) return -1; MHVTL_DBG(3, "Reading data partition/block %u/%u, size: %d", c_pos->partition_id, c_pos->blk_number, buf_size); /* The caller should have already verified that this is a B_DATA block before issuing this read, so we shouldn't have to worry about B_EOD or B_FILEMARK here. */ if (c_pos->blk_type == B_EOD) { sam_blank_check(E_END_OF_DATA, sam_stat); MHVTL_ERR("End of data detected while reading"); return -1; } iosize = c_pos->disk_blk_size; if (iosize > buf_size) iosize = buf_size; nread = pread(datafile[c_pos->partition_id], buf, iosize, raw_pos.data_offset); if (nread != iosize) { MHVTL_ERR("Failed to read %d bytes", iosize); return -1; } /* Now position to the following block. */ MHVTL_DBG(3, "Reading data succeeded, now positioning to next header"); if (read_header(c_pos->blk_number + 1, sam_stat)) { MHVTL_ERR("Failed to read next partition/block header %u/%u", c_pos->partition_id, c_pos->blk_number + 1); return -1; } return nread; } uint64_t current_tape_offset(void) { if (datafile[c_pos->partition_id] != -1) return raw_pos.data_offset; return 0; } uint64_t current_tape_block(void) { if (datafile[c_pos->partition_id] != -1) return (uint64_t)c_pos->blk_number; return 0; } uint64_t last_block(uint8_t partition_number) { return eod_blk_number[c_pos->partition_id]; } uint64_t block_from_filemark(uint8_t partition_number, uint32_t filemark) { return filemarks[partition_number][filemark]; } /* Return number of filemarks up to 'block' : -1 for all */ uint64_t count_filemarks(int64_t block) { uint64_t count; MHVTL_DBG(3, "counting filemarks till partition/block %d/%ld (total = %d)", c_pos->partition_id, (unsigned long)block, meta[c_pos->partition_id].filemark_count); if (block == -1) return (uint64_t)meta[c_pos->partition_id].filemark_count; for (count = 0; count < meta[c_pos->partition_id].filemark_count; count++) { if (filemarks[c_pos->partition_id][count] >= block) return count; } return (uint64_t)meta[c_pos->partition_id].filemark_count; } static void enc_key_to_string(char *dst, uint8_t *key, int len) { char b[16]; int i; dst[0] = '\0'; for (i = 0; i < len; i++) { sprintf(b, "%02x", key[i]); strncat(dst, b, 16); } key[i] = '\0'; } void print_raw_header(void) { char *f = NULL; char *enc = NULL; enc = malloc(256); if (!enc) { printf("Unable to malloc 256 bytes of memory to produce dump_tape report"); MHVTL_ERR("Unable to malloc 256 bytes of memory to produce dump_tape report"); return; } f = malloc(256); if (!f) { printf("Unable to malloc 256 bytes of memory to produce dump_tape report"); MHVTL_ERR("Unable to malloc 256 bytes of memory to produce dump_tape report"); free(enc); return; } sprintf(f, "%s", "Hdr:"); switch (c_pos->blk_type) { case B_DATA: if (c_pos->blk_flags & BLKHDR_FLG_ENCRYPTED) { strncat(f, "Encrypt/", 9); } if (c_pos->blk_flags & BLKHDR_FLG_ZLIB_COMPRESSED) { strncat(f, "zlibCompressed", 15); } else if (c_pos->blk_flags & BLKHDR_FLG_LZO_COMPRESSED) { strncat(f, "lzoCompressed", 14); } else { strncat(f, "non-compressed", 15); } if (c_pos->blk_flags & BLKHDR_FLG_CRC) { strncat(f, " with crc", 10); } else { strncat(f, " no crc", 10); } break; case B_FILEMARK: strncat(f, "Filemark", 9); break; case B_EOD: strncat(f, "End of Data", 12); break; case B_NOOP: strncat(f, "No Operation", 13); break; default: strncat(f, "Unknown type", 13); break; } printf("%-35s (0x%02x/0x%02x), sz: %6d/%-6d, Blk No.: %7u, data_offset: %10" PRId64 ", CRC: %08x\n", f, c_pos->blk_type, c_pos->blk_flags, c_pos->disk_blk_size, c_pos->blk_size, c_pos->blk_number, raw_pos.data_offset, c_pos->uncomp_crc); if ((c_pos->blk_type == B_DATA) && (c_pos->blk_flags & BLKHDR_FLG_ENCRYPTED)) { printf(" => Encr key length %d, ukad length %d, " "akad length %d\n", c_pos->blk_encryption_info.key_length, c_pos->blk_encryption_info.ukad_length, c_pos->blk_encryption_info.akad_length); if (c_pos->blk_encryption_info.key_length > 0) { enc_key_to_string(enc, c_pos->blk_encryption_info.key, c_pos->blk_encryption_info.key_length); printf("%12s : %32s\n", "Key", enc); } if (c_pos->blk_encryption_info.ukad_length > 0) { enc_key_to_string(enc, c_pos->blk_encryption_info.ukad, c_pos->blk_encryption_info.ukad_length); printf("%12s : %32s\n", "Ukad", enc); } if (c_pos->blk_encryption_info.akad_length > 0) { enc_key_to_string(enc, c_pos->blk_encryption_info.akad, c_pos->blk_encryption_info.akad_length); printf("%12s : %32s\n", "Akad", enc); } } free(enc); free(f); } void print_filemark_count(void) { printf("Total num of filemarks: %d\n", meta[c_pos->partition_id].filemark_count); } void print_metadata(void) { unsigned int a; for (a = 0; a < meta[c_pos->partition_id].filemark_count; a++) printf("Filemark: %d\n", filemarks[c_pos->partition_id][a]); } /* * Cleanup entry point */ void cart_deinit(void) { for (int j = 0; j < mam.num_partitions; j++) { free(filemarks[j]); filemarks[j] = NULL; } } ================================================ FILE: usr/vtlcart_v1.c ================================================ /* * Original tape format. * * Basically a double-linked-list headers followed by the block of data. * Each header describes the header type (data, filemark, EOD etc * along with pointer to previous and next block * * Copyright (C) 2005 - 2025 Mark Harvey markh794@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* for unistd.h pread() and pwrite() prototypes */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #include "mhvtl_scsi.h" #include "vtlcart.h" #include "be_byteshift.h" #define B_BOT 14 /* Beginning of Tape TAPE_FMT_VERSION 2 */ struct raw_header { loff_t prev_blk; loff_t curr_blk; loff_t next_blk; struct blk_header hdr; char pad[512 - (3 * sizeof(loff_t)) - sizeof(struct blk_header)]; }; static struct raw_header raw_pos; static int datafile = -1; static uint8_t MediumType; static char currentMedia[1024]; /* GLobally visible variables. */ int OK_to_write; /* * Returns: * == 0, success * != 0, failure */ static int read_header(struct raw_header *h, uint8_t *sam_stat) { loff_t nread; nread = read(datafile, h, sizeof(*h)); if (nread < 0) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return -1; } else if (nread != sizeof(*h)) { sam_medium_error(E_END_OF_DATA, sam_stat); return -1; } return 0; } static loff_t position_to_curr_header(uint8_t *sam_stat) { return (lseek64(datafile, raw_pos.curr_blk, SEEK_SET)); } static int skip_to_next_header(uint8_t *sam_stat) { if (raw_pos.hdr.blk_type == B_EOD) { sam_blank_check(E_END_OF_DATA, sam_stat); MHVTL_DBG(1, "End of data detected while forward SPACEing!!"); return -1; } if (raw_pos.next_blk != lseek64(datafile, raw_pos.next_blk, SEEK_SET)) { sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); MHVTL_DBG(1, "Unable to seek to next block header"); return -1; } if (read_header(&raw_pos, sam_stat)) { sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); MHVTL_DBG(1, "Unable to read next block header"); return -1; } // Position to start of header (rewind over header) if (raw_pos.curr_blk != position_to_curr_header(sam_stat)) { sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); MHVTL_DBG(1, "Error position in datafile. Offset: %" PRId64, raw_pos.curr_blk); return -1; } return 0; } static int skip_to_prev_header(uint8_t *sam_stat) { // Position to previous header MHVTL_DBG(3, "Positioning to raw_pos.prev_blk: %" PRId64, raw_pos.prev_blk); if (raw_pos.prev_blk != lseek64(datafile, raw_pos.prev_blk, SEEK_SET)) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); MHVTL_DBG(1, "Error position in datafile !!"); return -1; } // Read in header MHVTL_DBG(3, "Reading in header: %d bytes", (int)sizeof(raw_pos)); if (read_header(&raw_pos, sam_stat)) { MHVTL_DBG(1, "Error reading datafile while reverse SPACEing"); return -1; } if (raw_pos.hdr.blk_type == B_BOT) { MHVTL_DBG(3, "Found Beginning Of Tape, " "Skipping to next header.."); skip_to_next_header(sam_stat); sam_medium_error(E_BOM, sam_stat); MHVTL_DBG(3, "Found BOT!!"); return -1; } // Position to start of header (rewind over header) if (raw_pos.curr_blk != position_to_curr_header(sam_stat)) { sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); MHVTL_DBG(1, "Error position in datafile !!"); return -1; } MHVTL_DBG(3, "Rewinding over header just read in: " "curr_position: %" PRId64, raw_pos.curr_blk); return 0; } /* * Create & write a new block header * * Returns: * == 0, success * != 0, failure */ static int mkNewHeader(uint32_t type, int blk_size, int comp_size, const struct encryption *cp, uint8_t *sam_stat) { struct raw_header h; memset(&h, 0, sizeof(h)); h.hdr.blk_type = type; /* Header type */ h.hdr.blk_flags = 0; h.hdr.blk_number = raw_pos.hdr.blk_number; if (type != B_DATA) { h.hdr.blk_size = 0; h.hdr.disk_blk_size = 0; } else { h.hdr.blk_size = blk_size; /* Size of uncompressed data */ if (comp_size) { h.hdr.blk_flags |= BLKHDR_FLG_COMPRESSED; h.hdr.disk_blk_size = comp_size; } else { h.hdr.disk_blk_size = blk_size; } if (cp != NULL) { int i; h.hdr.blk_flags |= BLKHDR_FLG_ENCRYPTED; h.hdr.encryption.ukad_length = cp->ukad_length; for (i = 0; i < cp->ukad_length; ++i) { h.hdr.encryption.ukad[i] = cp->ukad[i]; } h.hdr.encryption.akad_length = cp->akad_length; for (i = 0; i < cp->akad_length; ++i) { h.hdr.encryption.akad[i] = cp->akad[i]; } h.hdr.encryption.key_length = cp->key_length; for (i = 0; i < cp->key_length; ++i) { h.hdr.encryption.key[i] = cp->key[i]; } } } h.curr_blk = lseek64(datafile, 0, SEEK_CUR); // Update current position /* If we are writing a new EOD marker, * - then set next pointer to itself * else * - Set pointer to next header (header size + size of data) */ if (type == B_EOD) h.next_blk = h.curr_blk; else h.next_blk = h.curr_blk + h.hdr.disk_blk_size + sizeof(h); if (h.curr_blk == raw_pos.curr_blk) { /* If current pos == last header read in we are about to overwrite the * current header block */ h.prev_blk = raw_pos.prev_blk; h.hdr.blk_number = raw_pos.hdr.blk_number; } else if (h.curr_blk == raw_pos.next_blk) { /* New header block at end of data file.. */ h.prev_blk = raw_pos.curr_blk; h.hdr.blk_number = raw_pos.hdr.blk_number + 1; } else { MHVTL_DBG(1, "Position error blk No: %d, Pos: %" PRId64 ", Exp: %" PRId64, h.hdr.blk_number, h.curr_blk, raw_pos.curr_blk); sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); return -1; } if (write(datafile, &h, sizeof(h)) != sizeof(h)) { sam_medium_error(E_WRITE_ERROR, sam_stat); MHVTL_DBG(1, "Write failure, pos: %" PRId64 ": %s", h.curr_blk, strerror(errno)); return -1; } /* * Write was successful, update raw_pos with this header block. */ memcpy(&raw_pos, &h, sizeof(h)); // Update where we think we are.. return 0; } /* * Returns: * == 0, success * != 0, failure */ static int mkEODHeader(uint8_t *sam_stat) { if (mkNewHeader(B_EOD, 0, 0, NULL, sam_stat)) return -1; if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 1; /* If we have just written a END OF DATA marker, * rewind to just before it. */ // Position to start of header (rewind over header) if (raw_pos.curr_blk != position_to_curr_header(sam_stat)) { sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); MHVTL_DBG(1, "Failed to write EOD header"); return -1; } return 0; } /* * */ static int skip_prev_filemark(uint8_t *sam_stat) { if (raw_pos.hdr.blk_type == B_FILEMARK) raw_pos.hdr.blk_type = B_NOOP; while (raw_pos.hdr.blk_type != B_FILEMARK) { if (raw_pos.hdr.blk_type == B_BOT) { sam_no_sense(0, E_BOM, sam_stat); MHVTL_DBG(2, "Found Beginning of tape"); return -1; } if (skip_to_prev_header(sam_stat)) return -1; } return 0; } /* * */ static int skip_next_filemark(uint8_t *sam_stat) { // While blk header is NOT a filemark, keep skipping to next header while (raw_pos.hdr.blk_type != B_FILEMARK) { // END-OF-DATA -> Treat this as an error - return.. if (raw_pos.hdr.blk_type == B_EOD) { sam_blank_check(E_END_OF_DATA, sam_stat); MHVTL_DBG(2, "%s", "Found end of media"); if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 1; return -1; } if (skip_to_next_header(sam_stat)) return -1; // On error } // Position to header AFTER the FILEMARK.. if (skip_to_next_header(sam_stat)) return -1; return 0; } static int tape_loaded(uint8_t *sam_stat) { switch (datafile != -1) { return 1; } sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return 0; } /* * Returns: * == 0, success * != 0, failure */ int position_to_eod(uint8_t *sam_stat) { if (!tape_loaded(sam_stat)) { return -1; } while (raw_pos.hdr.blk_type != B_EOD) { if (skip_to_next_header(sam_stat)) { return -1; } } if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 1; return 0; } /* * Rewind 'tape'. */ static int rawRewind(uint8_t *sam_stat) { off64_t retval; // Start at beginning of datafile.. retval = lseek64(datafile, 0L, SEEK_SET); if (retval < 0) { MHVTL_DBG(1, "Can't seek to beginning of file: %s", strerror(errno)); return -1; } /* * Read header.. * If this is not the BOT header we are in trouble */ if (read_header(&raw_pos, sam_stat)) { return -1; } return 0; } /* * Rewind to beginning of data file and the position to first data header. * * Return 0 -> Not loaded. * 1 -> Load OK * 2 -> format corrupt. */ int rewind_tape(uint8_t *sam_stat) { if (rawRewind(sam_stat)) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return 2; } if (raw_pos.hdr.blk_type != B_BOT) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return 2; } if (skip_to_next_header(sam_stat)) return 2; switch (MediumType) { case MEDIA_TYPE_CLEAN: OK_to_write = 0; break; case MEDIA_TYPE_WORM: /* Special condition... * If we * - rewind, * - write filemark * - EOD * We set this as writable media as the tape is blank. */ if (raw_pos.hdr.blk_type != B_EOD) OK_to_write = 0; // Check that this header is a filemark and the next header // is End of Data. If it is, we are OK to write if (raw_pos.hdr.blk_type == B_FILEMARK) { skip_to_next_header(sam_stat); if (raw_pos.hdr.blk_type == B_EOD) OK_to_write = 1; } // Now we have to go thru thru the rewind again.. if (rawRewind(sam_stat)) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return 2; } // No need to do all previous error checking... skip_to_next_header(sam_stat); break; case MEDIA_TYPE_DATA: OK_to_write = 1; // Reset flag to OK. break; } MHVTL_DBG(1, "Media is %s", (OK_to_write) ? "writable" : "not writable"); return 1; } /* * Returns: * == 0, success * != 0, failure */ int position_to_block(uint32_t blk_no, uint8_t *sam_stat) { if (!tape_loaded(sam_stat)) { return -1; } if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; if (blk_no < raw_pos.hdr.blk_number && raw_pos.hdr.blk_number - blk_no > blk_no) { if (rewind_tape(sam_stat)) return -1; } while (raw_pos.hdr.blk_number != blk_no) { if (raw_pos.hdr.blk_number > blk_no) { if (skip_to_prev_header(sam_stat) == -1) return -1; } else { if (skip_to_next_header(sam_stat) == -1) return -1; } } return 0; } /* * 'count' is in the range 0xff000001 to 0x00ffffff * * Returns: * == 0, success * != 0, failure */ int position_blocks(int32_t count, uint8_t *sam_stat) { if (!tape_loaded(sam_stat)) { return -1; } if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; if (count < 0) { for (; count < 0; count++) { if (skip_to_prev_header(sam_stat)) return -1; if (raw_pos.hdr.blk_type == B_FILEMARK) { sam_no_sense(SD_FILEMARK, E_MARK, sam_stat); return -1; } } } else { for (; count > 0; count--) { if (skip_to_next_header(sam_stat)) return -1; if (raw_pos.hdr.blk_type == B_FILEMARK) { sam_no_sense(SD_FILEMARK, E_MARK, sam_stat); return -1; } } } return 0; } /* * Returns: * == 0, success * != 0, failure */ int position_filemarks(int32_t count, uint8_t *sam_stat) { if (!tape_loaded(sam_stat)) { return -1; } if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; if (count < 0) { for (; count < 0; count++) if (skip_prev_filemark(sam_stat)) return -1; } else { for (; count > 0; count--) if (skip_next_filemark(sam_stat)) return -1; } return 0; } /* * Writes data in struct MAM back to beginning of datafile.. * Returns 0 if nothing written or -1 on error */ int rewriteMAM(uint8_t *sam_stat) { loff_t nwrite = 0; // Rewrite MAM data nwrite = pwrite(datafile, &mam, sizeof(mam), sizeof(struct blk_header)); if (nwrite != sizeof(mam)) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return -1; } MediumType = mam.MediumType; return 0; } /* * Returns: * == 0, the new PCL was successfully created. * == 2, the PCL (probably) already existed. * == 1, an error occurred. */ int create_tape(const char *pcl, const struct MAM *mamp, uint8_t *sam_stat) { char newMedia[1024]; struct raw_header h; /* Attempt to create the new PCL. It will fail if the PCL already exists. */ sprintf((char *)newMedia, "%s/%s", MHVTL_HOME_PATH, pcl); syslog(LOG_DAEMON | LOG_INFO, "%s being created", newMedia); datafile = open((char *)newMedia, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (datafile == -1) { perror("Failed creating file"); return 2; } /* Write a B_BOT record consisting of the B_BOT header plus the MAM. */ memset(&h, 0, sizeof(h)); h.next_blk = sizeof(*mamp) + sizeof(h); h.hdr.blk_type = B_BOT; h.hdr.blk_size = ntohll(mamp->max_capacity) / 1048576; if (write(datafile, &h, sizeof(h)) != sizeof(h)) { perror("Unable to write header"); unlink(newMedia); return 1; } if (write(datafile, mamp, sizeof(*mamp)) != sizeof(*mamp)) { perror("Unable to write MAM"); unlink(newMedia); return 1; } /* Write a B_EOD record. */ memset(&h, 0, sizeof(h)); h.curr_blk = lseek64(datafile, 0, SEEK_CUR); h.next_blk = h.curr_blk; h.hdr.blk_type = B_EOD; if (write(datafile, &h, sizeof(h)) != sizeof(h)) { perror("Unable to write header"); unlink(newMedia); return 1; } close(datafile); datafile = -1; return 0; } /* * Attempt to load PCL - i.e. Open datafile and read in BOT header & MAM * * Returns: * == 0 -> Load OK * == 1 -> Tape already loaded. * == 2 -> format corrupt. * == 3 -> cartridge does not exist or cannot be opened. */ int load_tape(const char *pcl, uint8_t *sam_stat) { loff_t nread; /* KFRDEBUG - sam_stat needs updates in lots of places here. */ if (datafile == -1) return 1; sprintf(currentMedia, "%s/%s", MHVTL_HOME_PATH, pcl); MHVTL_DBG(2, "Opening file/media %s", currentMedia); if ((datafile = open(currentMedia, O_RDWR | O_LARGEFILE)) == -1) { MHVTL_DBG(1, "%s: open file/media failed, %s", currentMedia, strerror(errno)); return 3; // Unsuccessful load } // Now read in header information from just opened datafile nread = read(datafile, &raw_pos, sizeof(raw_pos)); if (nread < 0) { MHVTL_DBG(1, "%s: %s", "Error reading header in datafile, load failed", strerror(errno)); close(datafile); datafile = -1; return 2; // Unsuccessful load } else if (nread < sizeof(raw_pos)) { // Did not read anything... MHVTL_DBG(1, "%s: %s", "Error: Not a tape format, load failed", strerror(errno)); close(datafile); datafile = -1; return 2; } if (raw_pos.hdr.blk_type != B_BOT) { MHVTL_DBG(1, "Header type: %d not valid, load failed", raw_pos.hdr.blk_type); close(datafile); datafile = -1; return 2; } // FIXME: Need better validation checking here !! if (raw_pos.next_blk != (sizeof(raw_pos) + sizeof(mam))) { MHVTL_DBG(1, "MAM size incorrect, load failed" " - Expected size: %d, size found: %" PRId64, (int)(sizeof(raw_pos) + sizeof(mam)), raw_pos.next_blk); close(datafile); datafile = -1; return 2; // Unsuccessful load } nread = read(datafile, &mam, sizeof(mam)); if (nread < 0) { MHVTL_DBG(1, "Can not read MAM from mounted media, %s", strerror(errno)); close(datafile); datafile = -1; return 2; // Unsuccessful load } if (nread != sizeof(mam)) { MHVTL_DBG(1, "Can not read MAM from mounted media, insufficient data"); close(datafile); datafile = -1; return 2; // Unsuccessful load } if (mam.tape_fmt_version != TAPE_FMT_VERSION) { MHVTL_DBG(1, "Incorrect media format"); sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); close(datafile); datafile = -1; return 2; } MediumType = mam.MediumType; c_pos = &raw_pos.hdr; return 0; } /* * Returns: * == 0, success * != 0, failure */ int write_filemarks(uint32_t count, uint8_t *sam_stat) { if (!tape_loaded(sam_stat)) { return -1; } if (count > 0) { while (count > 0) { count--; if (mkNewHeader(B_FILEMARK, 0, 0, NULL, sam_stat)) return -1; } mkEODHeader(sam_stat); } return 0; } int write_tape_block(const uint8_t *buf, uint32_t blk_size, uint32_t comp_size, const struct encryption *cp, uint8_t *sam_stat) { loff_t nwrite; uint32_t iosize; if (!tape_loaded(sam_stat)) { return -1; } /* If comp_size is non-zero then the data is compressed, so use comp_size for the I/O size. If comp_size is zero, the data is non-compressed, so use the blk_size as the I/O size. */ iosize = comp_size ? comp_size : blk_size; if (mkNewHeader(B_DATA, blk_size, comp_size, cp, sam_stat)) { MHVTL_DBG(1, "Failed to write header"); sam_medium_error(E_WRITE_ERROR, sam_stat); return -1; } // now write the block of data.. nwrite = write(datafile, buf, iosize); if (nwrite <= 0) { MHVTL_DBG(1, "failed to write %d bytes, %s", iosize, strerror(errno)); sam_medium_error(E_WRITE_ERROR, sam_stat); return -1; } else if (nwrite != iosize) { MHVTL_DBG(1, "Did not write all data"); sam_medium_error(E_WRITE_ERROR, sam_stat); return -1; } /* Write END-OF-DATA marker */ if (mkEODHeader(sam_stat)) { MHVTL_DBG(1, "Did not write EOD"); sam_medium_error(E_WRITE_ERROR, sam_stat); return -1; } return 0; } void unload_tape(uint8_t *sam_stat) { if (datafile >= 0) { close(datafile); datafile = -1; } } uint32_t read_tape_block(uint8_t *buf, uint32_t buf_size, uint8_t *sam_stat) { loff_t nread; uint32_t size; if (!tape_loaded(sam_stat)) { return -1; } size = raw_pos.hdr.disk_blk_size; if (size > buf_size) size = buf_size; nread = read(datafile, buf, size); // Now read in subsequent header skip_to_next_header(sam_stat); return nread; } uint64_t current_tape_offset(void) { if (datafile != -1) { return raw_pos.curr_blk; } return 0; } void print_raw_header(void) { printf("Hdr:"); switch (raw_pos.hdr.blk_type) { case B_DATA: if ((raw_pos.hdr.blk_flags && (BLKHDR_FLG_COMPRESSED | BLKHDR_FLG_ENCRYPTED)) == (BLKHDR_FLG_COMPRESSED | BLKHDR_FLG_ENCRYPTED)) printf(" Encrypt/Comp data"); else if (raw_pos.hdr.blk_flags & BLKHDR_FLG_ENCRYPTED) printf(" Encrypted data"); else if (raw_pos.hdr.blk_flags & BLKHDR_FLG_COMPRESSED) printf(" Compressed data"); else printf(" data"); printf("(%02x), sz %6d/%-6d, Blk No.: %u, prev %" PRId64 ", cur %" PRId64 ", next %" PRId64 "\n", raw_pos.hdr.blk_type, raw_pos.hdr.disk_blk_size, raw_pos.hdr.blk_size, raw_pos.hdr.blk_number, raw_pos.prev_blk, raw_pos.curr_blk, raw_pos.next_blk); if (raw_pos.hdr.blk_flags & BLKHDR_FLG_ENCRYPTED) printf(" => Encr key length %d, ukad length %d, " "akad length %d\n", raw_pos.hdr.encryption.key_length, raw_pos.hdr.encryption.ukad_length, raw_pos.hdr.encryption.akad_length); break; case B_FILEMARK: printf(" Filemark"); printf("(%02x), sz %13d, Blk No.: %u, prev %" PRId64 ", cur %" PRId64 ", next %" PRId64 "\n", raw_pos.hdr.blk_type, raw_pos.hdr.blk_size, raw_pos.hdr.blk_number, raw_pos.prev_blk, raw_pos.curr_blk, raw_pos.next_blk); break; case B_BOT: printf(" Beginning of Tape"); printf("(%02x), Capacity %6d Mbytes" ", prev %" PRId64 ", cur %" PRId64 ", next %" PRId64 "\n", raw_pos.hdr.blk_type, raw_pos.hdr.blk_size, raw_pos.prev_blk, raw_pos.curr_blk, raw_pos.next_blk); return; break; case B_EOD: printf(" End of Data"); printf("(%02x), sz %13d, Blk No.: %u, prev %" PRId64 ", cur %" PRId64 ", next %" PRId64 "\n", raw_pos.hdr.blk_type, raw_pos.hdr.blk_size, raw_pos.hdr.blk_number, raw_pos.prev_blk, raw_pos.curr_blk, raw_pos.next_blk); break; case B_NOOP: printf(" No Operation"); break; default: printf(" Unknown type"); printf("(%02x), %6d/%-6d, Blk No.: %u, prev %" PRId64 ", cur %" PRId64 ", next %" PRId64 "\n", raw_pos.hdr.blk_type, raw_pos.hdr.disk_blk_size, raw_pos.hdr.blk_size, raw_pos.hdr.blk_number, raw_pos.prev_blk, raw_pos.curr_blk, raw_pos.next_blk); break; } } ================================================ FILE: usr/vtlcart_v1_mtr.c ================================================ /* * Original tape format. * * Basically a double-linked-list headers followed by the block of data. * Each header describes the header type (data, filemark, EOD etc * along with pointer to previous and next block * * Copyright (C) 2005 - 2025 Mark Harvey markh794@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* for unistd.h pread() and pwrite() prototypes */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #include "mhvtl_scsi.h" #include "mhvtl_list.h" #include "vtlcart.h" #include "be_byteshift.h" #define B_BOT 14 /* Beginning of Tape TAPE_FMT_VERSION 2 */ #ifndef MHVTL_DEBUG #error DEBUG use only ! #endif struct raw_header { loff_t prev_blk; loff_t curr_blk; loff_t next_blk; struct blk_header hdr; char pad[512 - (3 * sizeof(loff_t)) - sizeof(struct blk_header)]; }; static struct raw_header raw_pos; static int datafile = -1; static uint8_t MediumType; static char currentMedia[1024]; /* GLobally visible variables. */ struct MAM mam; struct blk_header *c_pos = &raw_pos.hdr; int OK_to_write; /* * Returns: * == 0, success * != 0, failure */ static int read_header(struct raw_header *h, uint8_t *sam_stat) { MHVTL_DBG(1, "read_header"); loff_t nread; nread = read(datafile, h, sizeof(*h)); if (nread < 0) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return -1; } else if (nread != sizeof(*h)) { sam_medium_error(E_END_OF_DATA, sam_stat); return -1; } return 0; } static loff_t position_to_curr_header(uint8_t *sam_stat) { MHVTL_DBG(1, "position_to_curr_header"); return (lseek64(datafile, raw_pos.curr_blk, SEEK_SET)); } static int skip_to_next_header(uint8_t *sam_stat) { MHVTL_DBG(1, "skip_to_next_header"); if (raw_pos.hdr.blk_type == B_EOD) { sam_blank_check(E_END_OF_DATA, sam_stat); MHVTL_DBG(1, "End of data detected while forward SPACEing!!"); return -1; } if (raw_pos.next_blk != lseek64(datafile, raw_pos.next_blk, SEEK_SET)) { sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); MHVTL_DBG(1, "Unable to seek to next block header"); return -1; } if (read_header(&raw_pos, sam_stat)) { sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); MHVTL_DBG(1, "Unable to read next block header"); return -1; } /* Position to start of header (rewind over header) */ if (raw_pos.curr_blk != position_to_curr_header(sam_stat)) { sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); MHVTL_DBG(1, "Error position in datafile. Offset: %" PRId64, raw_pos.curr_blk); return -1; } return 0; } static int skip_to_prev_header(uint8_t *sam_stat) { MHVTL_DBG(1, "skip_to_prev_header"); /* Position to previous header */ MHVTL_DBG(3, "Positioning to raw_pos.prev_blk: %" PRId64, raw_pos.prev_blk); if (raw_pos.prev_blk != lseek64(datafile, raw_pos.prev_blk, SEEK_SET)) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); MHVTL_DBG(1, "Error position in datafile !!"); return -1; } /* Read in header */ MHVTL_DBG(3, "Reading in header: %d bytes", (int)sizeof(raw_pos)); if (read_header(&raw_pos, sam_stat)) { MHVTL_DBG(1, "Error reading datafile while reverse SPACEing"); return -1; } if (raw_pos.hdr.blk_type == B_BOT) { MHVTL_DBG(3, "Found Beginning Of Tape, " "Skipping to next header.."); skip_to_next_header(sam_stat); sam_medium_error(E_BOM, sam_stat); MHVTL_DBG(3, "Found BOT!!"); return -1; } /* Position to start of header (rewind over header) */ if (raw_pos.curr_blk != position_to_curr_header(sam_stat)) { sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); MHVTL_DBG(1, "Error position in datafile !!"); return -1; } MHVTL_DBG(3, "Rewinding over header just read in: " "curr_position: %" PRId64, raw_pos.curr_blk); return 0; } /* * Create & write a new block header * * Returns: * == 0, success * != 0, failure */ static int mkNewHeader(uint32_t type, int blk_size, int comp_size, const struct encryption *cp, uint8_t *sam_stat) { struct raw_header h; MHVTL_DBG(1, "mkNewHeader"); memset(&h, 0, sizeof(h)); h.hdr.blk_type = type; /* Header type */ h.hdr.blk_flags = 0; h.hdr.blk_number = raw_pos.hdr.blk_number; if (type != B_DATA) { h.hdr.blk_size = 0; h.hdr.disk_blk_size = 0; } else { h.hdr.blk_size = blk_size; /* Size of uncompressed data */ if (comp_size) { h.hdr.blk_flags |= BLKHDR_FLG_COMPRESSED; h.hdr.disk_blk_size = comp_size; } else { h.hdr.disk_blk_size = blk_size; } if (cp != NULL) { int i; h.hdr.blk_flags |= BLKHDR_FLG_ENCRYPTED; h.hdr.encryption.ukad_length = cp->ukad_length; for (i = 0; i < cp->ukad_length; ++i) h.hdr.encryption.ukad[i] = cp->ukad[i]; h.hdr.encryption.akad_length = cp->akad_length; for (i = 0; i < cp->akad_length; ++i) h.hdr.encryption.akad[i] = cp->akad[i]; h.hdr.encryption.key_length = cp->key_length; for (i = 0; i < cp->key_length; ++i) h.hdr.encryption.key[i] = cp->key[i]; } } /* Update current position */ h.curr_blk = lseek64(datafile, 0, SEEK_CUR); /* If we are writing a new EOD marker, * - then set next pointer to itself * else * - Set pointer to next header (header size + size of data) */ if (type == B_EOD) h.next_blk = h.curr_blk; else h.next_blk = h.curr_blk + h.hdr.disk_blk_size + sizeof(h); if (h.curr_blk == raw_pos.curr_blk) { /* If current pos == last header read in we are about to overwrite the * current header block */ h.prev_blk = raw_pos.prev_blk; h.hdr.blk_number = raw_pos.hdr.blk_number; } else if (h.curr_blk == raw_pos.next_blk) { /* New header block at end of data file.. */ h.prev_blk = raw_pos.curr_blk; h.hdr.blk_number = raw_pos.hdr.blk_number + 1; } else { MHVTL_DBG(1, "Position error blk No: %d, Pos: %" PRId64 ", Exp: %" PRId64, h.hdr.blk_number, h.curr_blk, raw_pos.curr_blk); sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); return -1; } if (write(datafile, &h, sizeof(h)) != sizeof(h)) { sam_medium_error(E_WRITE_ERROR, sam_stat); MHVTL_DBG(1, "Write failure, pos: %" PRId64 ": %s", h.curr_blk, strerror(errno)); return -1; } /* * Write was successful, update raw_pos with this header block. */ memcpy(&raw_pos, &h, sizeof(h)); /* Update where we think we are.. */ return 0; } /* * Returns: * == 0, success * != 0, failure */ static int mkEODHeader(uint8_t *sam_stat) { MHVTL_DBG(1, "mkEODHeader"); if (mkNewHeader(B_EOD, 0, 0, NULL, sam_stat)) return -1; if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 1; /* If we have just written a END OF DATA marker, * rewind to just before it. */ /* Position to start of header (rewind over header) */ if (raw_pos.curr_blk != position_to_curr_header(sam_stat)) { sam_medium_error(E_SEQUENTIAL_POSITION_ERR, sam_stat); MHVTL_DBG(1, "Failed to write EOD header"); return -1; } return 0; } /* * */ static int skip_prev_filemark(uint8_t *sam_stat) { MHVTL_DBG(1, "skip_prev_filemark"); if (raw_pos.hdr.blk_type == B_FILEMARK) raw_pos.hdr.blk_type = B_NOOP; while (raw_pos.hdr.blk_type != B_FILEMARK) { if (raw_pos.hdr.blk_type == B_BOT) { sam_no_sense(NO_SENSE, E_BOM, sam_stat); MHVTL_DBG(2, "Found Beginning of tape"); return -1; } if (skip_to_prev_header(sam_stat)) return -1; } return 0; } /* * */ static int skip_next_filemark(uint8_t *sam_stat) { MHVTL_DBG(1, "skip_next_filemark"); /* While blk header is NOT a filemark, keep skipping to next header */ while (raw_pos.hdr.blk_type != B_FILEMARK) { /* END-OF-DATA -> Treat this as an error - return.. */ if (raw_pos.hdr.blk_type == B_EOD) { sam_blank_check(E_END_OF_DATA, sam_stat); MHVTL_DBG(2, "%s", "Found end of media"); if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 1; return -1; } if (skip_to_next_header(sam_stat)) return -1; /* On error */ } /* Position to header AFTER the FILEMARK.. */ if (skip_to_next_header(sam_stat)) return -1; return 0; } static int tape_loaded(uint8_t *sam_stat) { MHVTL_DBG(1, "tape_loaded"); switch (datafile != -1) { return 1; } sam_not_ready(E_MEDIUM_NOT_PRESENT, sam_stat); return 0; } /* * Returns: * == 0, success * != 0, failure */ int position_to_eod(uint8_t *sam_stat) { MHVTL_DBG(1, "position_to_eod"); if (!tape_loaded(sam_stat)) return -1; while (raw_pos.hdr.blk_type != B_EOD) { if (skip_to_next_header(sam_stat)) return -1; } if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 1; return 0; } /* * Rewind 'tape'. */ static int rawRewind(uint8_t *sam_stat) { off64_t retval; MHVTL_DBG(1, "rawRewind"); /* Start at beginning of datafile.. */ retval = lseek64(datafile, 0L, SEEK_SET); if (retval < 0) { MHVTL_DBG(1, "Can't seek to beginning of file: %s", strerror(errno)); return -1; } /* * Read header.. * If this is not the BOT header we are in trouble */ if (read_header(&raw_pos, sam_stat)) return -1; return 0; } /* * Rewind to beginning of data file and the position to first data header. * * Return 0 -> Not loaded. * 1 -> Load OK * 2 -> format corrupt. */ int rewind_tape(uint8_t *sam_stat) { MHVTL_DBG(1, "rewind_tape"); if (rawRewind(sam_stat)) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return 2; } if (raw_pos.hdr.blk_type != B_BOT) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return 2; } if (skip_to_next_header(sam_stat)) return 2; switch (MediumType) { case MEDIA_TYPE_CLEAN: OK_to_write = 0; break; case MEDIA_TYPE_WORM: /* Special condition... * If we * - rewind, * - write filemark * - EOD * We set this as writable media as the tape is blank. */ if (raw_pos.hdr.blk_type != B_EOD) OK_to_write = 0; /* Check that this header is a filemark and the next header * is End of Data. If it is, we are OK to write */ if (raw_pos.hdr.blk_type == B_FILEMARK) { skip_to_next_header(sam_stat); if (raw_pos.hdr.blk_type == B_EOD) OK_to_write = 1; } /* Now we have to go thru thru the rewind again.. */ if (rawRewind(sam_stat)) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return 2; } /* No need to do all previous error checking... */ skip_to_next_header(sam_stat); break; case MEDIA_TYPE_DATA: OK_to_write = 1; /* Reset flag to OK. */ break; } MHVTL_DBG(1, "Media is %s", (OK_to_write) ? "writable" : "not writable"); return 1; } /* * Returns: * == 0, success * != 0, failure */ int position_to_block(uint32_t blk_no, uint8_t *sam_stat) { MHVTL_DBG(1, "position_to_block"); if (!tape_loaded(sam_stat)) return -1; if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; if (blk_no < raw_pos.hdr.blk_number && raw_pos.hdr.blk_number - blk_no > blk_no) { if (rewind_tape(sam_stat)) return -1; } while (raw_pos.hdr.blk_number != blk_no) { if (raw_pos.hdr.blk_number > blk_no) { if (skip_to_prev_header(sam_stat) == -1) return -1; } else { if (skip_to_next_header(sam_stat) == -1) return -1; } } return 0; } /* * 'count' is in the range 0xff000001 to 0x00ffffff * * Returns: * == 0, success * != 0, failure */ int position_blocks(int32_t count, uint8_t *sam_stat) { MHVTL_DBG(1, "position_blocks"); if (!tape_loaded(sam_stat)) return -1; if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; if (count < 0) { for (; count < 0; count++) { if (skip_to_prev_header(sam_stat)) return -1; if (raw_pos.hdr.blk_type == B_FILEMARK) { sam_no_sense(SD_FILEMARK, E_MARK, sam_stat); return -1; } } } else { for (; count > 0; count--) { if (skip_to_next_header(sam_stat)) return -1; if (raw_pos.hdr.blk_type == B_FILEMARK) { sam_no_sense(SD_FILEMARK, E_MARK, sam_stat); return -1; } } } return 0; } /* * Returns: * == 0, success * != 0, failure */ int position_filemarks(int32_t count, uint8_t *sam_stat) { MHVTL_DBG(1, "position_filemarks"); if (!tape_loaded(sam_stat)) return -1; if (MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; if (count < 0) { for (; count < 0; count++) if (skip_prev_filemark(sam_stat)) return -1; } else { for (; count > 0; count--) if (skip_next_filemark(sam_stat)) return -1; } return 0; } int position_blocks_forw(uint32_t count, uint8_t *sam_stat) { MHVTL_DBG(1, "position_blks_forw"); return position_blocks(count, sam_stat); } int position_blocks_back(uint32_t count, uint8_t *sam_stat) { MHVTL_DBG(1, "position_blks_back"); return position_blocks(count > 0 ? -count : count, sam_stat); } int position_filemarks_forw(uint32_t count, uint8_t *sam_stat) { MHVTL_DBG(1, "position_filemarks_forw"); return position_filemarks(count, sam_stat); } int position_filemarks_back(uint32_t count, uint8_t *sam_stat) { MHVTL_DBG(1, "position_filemarks_back"); return position_filemarks(count > 0 ? -count : count, sam_stat); } /* * Writes data in struct MAM back to beginning of datafile.. * Returns 0 if nothing written or -1 on error */ int rewriteMAM(uint8_t *sam_stat) { loff_t nwrite = 0; MHVTL_DBG(1, "rewriteMAM"); /* Rewrite MAM data */ nwrite = pwrite(datafile, &mam, sizeof(mam), sizeof(struct blk_header)); if (nwrite != sizeof(mam)) { sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); return -1; } MediumType = mam.MediumType; return 0; } /* * Returns: * == 0, the new PCL was successfully created. * == 2, the PCL (probably) already existed. * == 1, an error occurred. */ int create_tape(const char *pcl, const struct MAM *mamp, uint8_t *sam_stat) { char newMedia[1024]; struct raw_header h; MHVTL_DBG(1, "create_tape"); /* Attempt to create the new PCL. * It will fail if the PCL already exists. */ sprintf((char *)newMedia, "%s/%s", MHVTL_HOME_PATH, pcl); syslog(LOG_DAEMON | LOG_INFO, "%s being created", newMedia); datafile = open((char *)newMedia, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (datafile == -1) { perror("Failed creating file"); return 2; } /* Write a B_BOT record consisting of the B_BOT header plus the MAM. */ memset(&h, 0, sizeof(h)); h.next_blk = sizeof(*mamp) + sizeof(h); h.hdr.blk_type = B_BOT; h.hdr.blk_size = ntohl(mamp->max_capacity) / 1048576; if (write(datafile, &h, sizeof(h)) != sizeof(h)) { perror("Unable to write header"); unlink(newMedia); return 1; } if (write(datafile, mamp, sizeof(*mamp)) != sizeof(*mamp)) { perror("Unable to write MAM"); unlink(newMedia); return 1; } /* Write a B_EOD record. */ memset(&h, 0, sizeof(h)); h.curr_blk = lseek64(datafile, 0, SEEK_CUR); h.next_blk = h.curr_blk; h.hdr.blk_type = B_EOD; if (write(datafile, &h, sizeof(h)) != sizeof(h)) { perror("Unable to write header"); unlink(newMedia); return 1; } close(datafile); datafile = -1; return 0; } /* * Attempt to load PCL - i.e. Open datafile and read in BOT header & MAM * * Returns: * == 0 -> Load OK * == 1 -> Tape already loaded. * == 2 -> format corrupt. * == 3 -> cartridge does not exist or cannot be opened. */ int load_tape(const char *pcl, uint8_t *sam_stat) { loff_t nread; uint32_t version = 0; MHVTL_DBG(1, "load_tape"); /* KFRDEBUG - sam_stat needs updates in lots of places here. */ #if NOTDEF if (datafile == -1) /* return 1; */ /* don't return 1 here */ return 0; #endif sprintf(currentMedia, "%s/%s", MHVTL_HOME_PATH, pcl); /* MHVTL_DBG(2, "Opening file/media %s", currentMedia); */ MHVTL_LOG("Opening file/media %s", currentMedia); datafile = open(currentMedia, O_RDWR | O_LARGEFILE); if (datafile == -1) { MHVTL_DBG(1, "%s: open file/media failed, %s", currentMedia, strerror(errno)); return 3; /* Unsuccessful load */ } /* Now read in header information from just opened datafile */ nread = read(datafile, &raw_pos, sizeof(raw_pos)); if (nread < 0) { MHVTL_LOG("%s: %s", "Error reading header in datafile, load failed", strerror(errno)); close(datafile); datafile = -1; return 4; /* Unsuccessful load */ } else if (nread < sizeof(raw_pos)) { /* Did not read anything... */ MHVTL_LOG("%s: %s", "Error: Not a tape format, load failed", strerror(errno)); close(datafile); datafile = -1; return 5; } if (raw_pos.hdr.blk_type != B_BOT) { MHVTL_LOG("Header type: %d not valid, load failed", raw_pos.hdr.blk_type); close(datafile); datafile = -1; return 6; } /* FIXME: Need better validation checking here !! */ if (raw_pos.next_blk != (sizeof(raw_pos) + sizeof(mam))) { MHVTL_LOG("MAM size incorrect, load failed" " - Expected size: %d, size found: %" PRId64, (int)(sizeof(raw_pos) + sizeof(mam)), raw_pos.next_blk); close(datafile); datafile = -1; return 7; /* Unsuccessful load */ } nread = read(datafile, &mam, sizeof(mam)); if (nread < 0) { MHVTL_LOG("Can not read MAM from mounted media, %s", strerror(errno)); close(datafile); datafile = -1; return 8; /* Unsuccessful load */ } if (nread != sizeof(mam)) { MHVTL_LOG("Can not read MAM from mounted media, " "insufficient data"); close(datafile); datafile = -1; return 9; /* Unsuccessful load */ } version = mam.tape_fmt_version; if (version != TAPE_FMT_VERSION) { MHVTL_LOG("Incorrect media format %lu", version); sam_medium_error(E_MEDIUM_FMT_CORRUPT, sam_stat); close(datafile); datafile = -1; return 10; } MediumType = mam.MediumType; c_pos = &raw_pos.hdr; return 0; } /* * Returns: * == 0, success * != 0, failure */ int write_filemarks(uint32_t count, uint8_t *sam_stat) { MHVTL_DBG(1, "write_filemarks"); if (!tape_loaded(sam_stat)) return -1; if (count > 0) { while (count > 0) { count--; if (mkNewHeader(B_FILEMARK, 0, 0, NULL, sam_stat)) return -1; } mkEODHeader(sam_stat); } return 0; } int write_tape_block(const uint8_t *buf, uint32_t blk_size, uint32_t comp_size, const struct encryption *cp, uint8_t *sam_stat) { loff_t nwrite; uint32_t iosize; MHVTL_DBG(1, "write_tape_block"); if (!tape_loaded(sam_stat)) return -1; /* If comp_size is non-zero then the data is compressed, so use comp_size for the I/O size. If comp_size is zero, the data is non-compressed, so use the blk_size as the I/O size. */ iosize = comp_size ? comp_size : blk_size; if (mkNewHeader(B_DATA, blk_size, comp_size, cp, sam_stat)) { MHVTL_DBG(1, "Failed to write header"); sam_medium_error(E_WRITE_ERROR, sam_stat); return -1; } /* now write the block of data.. */ nwrite = write(datafile, buf, iosize); if (nwrite <= 0) { MHVTL_DBG(1, "failed to write %d bytes, %s", iosize, strerror(errno)); sam_medium_error(E_WRITE_ERROR, sam_stat); return -1; } else if (nwrite != iosize) { MHVTL_DBG(1, "Did not write all data"); sam_medium_error(E_WRITE_ERROR, sam_stat); return -1; } /* Write END-OF-DATA marker */ if (mkEODHeader(sam_stat)) { MHVTL_DBG(1, "Did not write EOD"); sam_medium_error(E_WRITE_ERROR, sam_stat); return -1; } return 0; } void unload_tape(uint8_t *sam_stat) { MHVTL_DBG(1, "unload_tape"); if (datafile >= 0) { close(datafile); datafile = -1; } } uint32_t read_tape_block(uint8_t *buf, uint32_t buf_size, uint8_t *sam_stat) { loff_t nread; uint32_t size; MHVTL_DBG(1, "read_tape_block"); if (!tape_loaded(sam_stat)) return -1; size = raw_pos.hdr.disk_blk_size; if (size > buf_size) size = buf_size; nread = read(datafile, buf, size); /* Now read in subsequent header */ skip_to_next_header(sam_stat); return nread; } uint64_t current_tape_offset(void) { if (datafile != -1) return raw_pos.curr_blk; return 0; } void print_raw_header(void) { printf("Hdr:"); switch (raw_pos.hdr.blk_type) { case B_DATA: if ((raw_pos.hdr.blk_flags && (BLKHDR_FLG_COMPRESSED | BLKHDR_FLG_ENCRYPTED)) == (BLKHDR_FLG_COMPRESSED | BLKHDR_FLG_ENCRYPTED)) printf(" Encrypt/Comp data"); else if (raw_pos.hdr.blk_flags & BLKHDR_FLG_ENCRYPTED) printf(" Encrypted data"); else if (raw_pos.hdr.blk_flags & BLKHDR_FLG_COMPRESSED) printf(" Compressed data"); else printf(" data"); printf("(%02x), sz %6d/%-6d, Blk No.: %u, prev %" PRId64 ", cur %" PRId64 ", next %" PRId64 "\n", raw_pos.hdr.blk_type, raw_pos.hdr.disk_blk_size, raw_pos.hdr.blk_size, raw_pos.hdr.blk_number, raw_pos.prev_blk, raw_pos.curr_blk, raw_pos.next_blk); if (raw_pos.hdr.blk_flags & BLKHDR_FLG_ENCRYPTED) printf(" => Encr key length %d, ukad length %d, " "akad length %d\n", raw_pos.hdr.encryption.key_length, raw_pos.hdr.encryption.ukad_length, raw_pos.hdr.encryption.akad_length); break; case B_FILEMARK: printf(" Filemark"); printf("(%02x), sz %13d, Blk No.: %u, prev %" PRId64 ", cur %" PRId64 ", next %" PRId64 "\n", raw_pos.hdr.blk_type, raw_pos.hdr.blk_size, raw_pos.hdr.blk_number, raw_pos.prev_blk, raw_pos.curr_blk, raw_pos.next_blk); break; case B_BOT: printf(" Beginning of Tape"); printf("(%02x), Capacity %6d Mbytes" ", prev %" PRId64 ", cur %" PRId64 ", next %" PRId64 "\n", raw_pos.hdr.blk_type, raw_pos.hdr.blk_size, raw_pos.prev_blk, raw_pos.curr_blk, raw_pos.next_blk); return; break; case B_EOD: printf(" End of Data"); printf("(%02x), sz %13d, Blk No.: %u, prev %" PRId64 ", cur %" PRId64 ", next %" PRId64 "\n", raw_pos.hdr.blk_type, raw_pos.hdr.blk_size, raw_pos.hdr.blk_number, raw_pos.prev_blk, raw_pos.curr_blk, raw_pos.next_blk); break; case B_NOOP: printf(" No Operation"); break; default: printf(" Unknown type"); printf("(%02x), %6d/%-6d, Blk No.: %u, prev %" PRId64 ", cur %" PRId64 ", next %" PRId64 "\n", raw_pos.hdr.blk_type, raw_pos.hdr.disk_blk_size, raw_pos.hdr.blk_size, raw_pos.hdr.blk_number, raw_pos.prev_blk, raw_pos.curr_blk, raw_pos.next_blk); break; } } static int check_for_overwrite(uint8_t *sam_stat) { MHVTL_DBG(1, "check_for_overwrite"); #if NOTDEF uint32_t blk_number; uint64_t data_offset; int i; if (raw_pos.hdr.blk_type == B_EOD) return 0; MHVTL_DBG(2, "At block %ld", (unsigned long)raw_pos.hdr.blk_number); /* We aren't at EOD so we are performing a rewrite. Truncate * the data and index files back to the current length. */ blk_number = raw_pos.hdr.blk_number; data_offset = raw_pos.data_offset; if (ftruncate(indxfile, blk_number * sizeof(raw_pos))) { sam_medium_error(E_WRITE_ERROR, sam_stat); MHVTL_LOG("Index file ftruncate failure, pos: " "%" PRId64 ": %s", (uint64_t)blk_number * sizeof(raw_pos), strerror(errno)); return -1; } if (ftruncate(datafile, data_offset)) { sam_medium_error(E_WRITE_ERROR, sam_stat); MHVTL_LOG("Data file ftruncate failure, pos: " "%" PRId64 ": %s", data_offset, strerror(errno)); return -1; } /* Update the filemark map removing any filemarks which will be * overwritten. Rewrite the filemark map so that the on-disk image * of the map is consistent with the new sizes of the other two files. */ for (i = 0; i < meta.filemark_count; i++) { MHVTL_DBG(2, "filemarks[%d] %d", i, filemarks[i]); if (filemarks[i] >= blk_number) { MHVTL_DBG(2, "Setting filemark_count from %d to %d", meta.filemark_count, i); meta.filemark_count = i; return rewrite_meta_file(); } } #endif return 0; } void zero_filemark_count(void) { MHVTL_DBG(1, "zero_filemark_count"); /* free(filemarks); filemark_alloc = 0; filemarks = NULL; meta.filemark_count = 0; rewrite_meta_file(); */ } int format_tape(uint8_t *sam_stat) { MHVTL_DBG(1, "format_tape"); if (!tape_loaded(sam_stat)) return -1; if (check_for_overwrite(sam_stat)) return -1; zero_filemark_count(); return mkEODHeader(raw_pos.hdr.blk_number); } void print_filemark_count(void) { /* printf("Total num of filemarks: %d\n", meta.filemark_count); */ } void print_metadata(void) { /* int a; for (a = 0; a < meta.filemark_count; a++) printf("Filemark: %d\n", filemarks[a]); */ } ================================================ FILE: usr/vtllib.c ================================================ /* * Shared routines between vtltape & vtllibrary * * Copyright (C) 2005 - 2025 Mark Harvey markh794 at gmail dot com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #ifndef Solaris #include #endif #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "be_byteshift.h" #include "mhvtl_list.h" #include "mhvtl_scsi.h" #include "vtl_common.h" #include "vtlcart.h" #include "logging.h" #include "vtllib.h" #include "smc.h" #include "mode.h" #include "q.h" #include "ssc.h" #include "mhvtl_log.h" static int reset = 0; static int inquiry_data_changed = 0; /* Global variables */ struct MAM mam; struct priv_lu_ssc lu_ssc; struct lu_phy_attr lunit; struct encryption app_encryption_state; int current_state; int lbp_rscrc_be = 1; int OK_to_write = 0; uint8_t sense[SENSE_BUF_SIZE]; uint8_t modeBlockDescriptor[8] = {0, 0, 0, 0, 0, 0, 0, 0}; char home_directory[HOME_DIR_PATH_SZ + 1]; uint8_t debug = 0; uint8_t verbose = 0; long my_id = 0; #define INIT_MAM_ATTR(attr_id, len, ro, fmt, field, enum_id) \ do { \ _Static_assert(sizeof(field) == (len), \ "INIT_ATTR: field size does not match length"); \ mamp->attributes[(enum_id)] = (struct MAM_attr){ \ .attribute_id = (attr_id), \ .length = (len), \ .read_only = (ro), \ .format = (fmt), \ .value = &(field)}; \ } while (0) #define INIT_VTL_ATTR(attr_id, len, field, enum_id) \ do { \ _Static_assert(sizeof(field) == (len), \ "INIT_ATTR: field size does not match length"); \ mamp->mhvtl_attr[(enum_id)] = (struct MHVTL_attr){ \ .attribute_id = (attr_id), \ .length = (len), \ .value = &(field)}; \ } while (0) void init_mam(struct MAM *mamp) { /* Device (0x0000 - 0x03ff) */ INIT_MAM_ATTR(0x000, 8, 1, 0, mamp->remaining_capacity, MAM_REMAINING_CAPACITY); INIT_MAM_ATTR(0x001, 8, 1, 0, mamp->max_capacity, MAM_MAX_CAPACITY); INIT_MAM_ATTR(0x002, 8, 1, 0, mamp->TapeAlert, MAM_TAPE_ALERT); INIT_MAM_ATTR(0x003, 8, 1, 0, mamp->LoadCount, MAM_LOAD_COUNT); INIT_MAM_ATTR(0x004, 8, 1, 0, mamp->MAMSpaceRemaining, MAM_MAM_SPACE_REMAINING); INIT_MAM_ATTR(0x005, 8, 1, 1, mamp->AssigningOrganization_1, MAM_ASSIGNING_ORG_1); INIT_MAM_ATTR(0x006, 1, 1, 0, mamp->FormattedDensityCode, MAM_FORMATTED_DENSITY_CODE); INIT_MAM_ATTR(0x007, 2, 1, 0, mamp->InitializationCount, MAM_INITIALIZATION_COUNT); INIT_MAM_ATTR(0x009, 4, 1, 0, mamp->VolumeChangeReference, MAM_VOLUME_CHANGE_REFERENCE); INIT_MAM_ATTR(0x20a, 40, 1, 1, mamp->DevMakeSerialLastLoad, MAM_DEV_MAKE_SERIAL_LAST_LOAD); INIT_MAM_ATTR(0x20b, 40, 1, 1, mamp->DevMakeSerialLastLoad1, MAM_DEV_MAKE_SERIAL_LAST_LOAD1); INIT_MAM_ATTR(0x20c, 40, 1, 1, mamp->DevMakeSerialLastLoad2, MAM_DEV_MAKE_SERIAL_LAST_LOAD2); INIT_MAM_ATTR(0x20d, 40, 1, 1, mamp->DevMakeSerialLastLoad3, MAM_DEV_MAKE_SERIAL_LAST_LOAD3); INIT_MAM_ATTR(0x220, 8, 1, 0, mamp->WrittenInMediumLife, MAM_WRITTEN_IN_MEDIUM_LIFE); INIT_MAM_ATTR(0x221, 8, 1, 0, mamp->ReadInMediumLife, MAM_READ_IN_MEDIUM_LIFE); INIT_MAM_ATTR(0x222, 8, 1, 0, mamp->WrittenInLastLoad, MAM_WRITTEN_IN_LAST_LOAD); INIT_MAM_ATTR(0x223, 8, 1, 0, mamp->ReadInLastLoad, MAM_READ_IN_LAST_LOAD); /* Medium (0x0400 - 0x07ff) */ INIT_MAM_ATTR(0x400, 8, 1, 1, mamp->MediumManufacturer, MAM_MEDIUM_MANUFACTURER); INIT_MAM_ATTR(0x401, 32, 1, 1, mamp->MediumSerialNumber, MAM_MEDIUM_SERIAL_NUMBER); INIT_MAM_ATTR(0x402, 4, 1, 0, mamp->MediumLength, MAM_MEDIUM_LENGTH); INIT_MAM_ATTR(0x403, 4, 1, 0, mamp->MediumWidth, MAM_MEDIUM_WIDTH); INIT_MAM_ATTR(0x404, 8, 1, 1, mamp->AssigningOrganization_2, MAM_ASSIGNING_ORG_2); INIT_MAM_ATTR(0x405, 1, 1, 0, mamp->MediumDensityCode, MAM_MEDIUM_DENSITY_CODE); INIT_MAM_ATTR(0x406, 12, 1, 1, mamp->MediumManufactureDate, MAM_MEDIUM_MANUFACTURE_DATE); INIT_MAM_ATTR(0x407, 8, 1, 0, mamp->MAMCapacity, MAM_MAM_CAPACITY); INIT_MAM_ATTR(0x408, 1, 0, 0, mamp->MediumType, MAM_MEDIUM_TYPE); INIT_MAM_ATTR(0x409, 2, 1, 0, mamp->MediumTypeInformation, MAM_MEDIUM_TYPE_INFORMATION); /* Host (0x0800 - 0x0bff) */ INIT_MAM_ATTR(0x800, 8, 0, 1, mamp->ApplicationVendor, MAM_APPLICATION_VENDOR); INIT_MAM_ATTR(0x801, 32, 0, 1, mamp->ApplicationName, MAM_APPLICATION_NAME); INIT_MAM_ATTR(0x802, 8, 0, 1, mamp->ApplicationVersion, MAM_APPLICATION_VERSION); INIT_MAM_ATTR(0x803, 160, 0, 2, mamp->UserMediumTextLabel, MAM_USER_MEDIUM_TEXT_LABEL); INIT_MAM_ATTR(0x804, 12, 0, 1, mamp->DateTimeLastWritten, MAM_DATE_TIME_LAST_WRITTEN); INIT_MAM_ATTR(0x805, 1, 0, 0, mamp->LocalizationIdentifier, MAM_LOCALIZATION_IDENTIFIER); INIT_MAM_ATTR(0x806, 32, 0, 1, mamp->Barcode, MAM_BARCODE); INIT_MAM_ATTR(0x807, 80, 0, 2, mamp->OwningHostTextualName, MAM_OWNING_HOST_TEXTUAL_NAME); INIT_MAM_ATTR(0x808, 160, 0, 2, mamp->MediaPool, MAM_MEDIA_POOL); INIT_MAM_ATTR(0x80b, 16, 0, 1, mamp->ApplicationFormatVersion, MAM_APPLICATION_FORMAT_VERSION); INIT_MAM_ATTR(0x80c, 46, 0, 0, mamp->VolumeCoherencyInformation, MAM_VOLUME_COHERENCY_INFORMATION); /* 0x0c00 - 0x0fff - Device - Vendor Specific */ /* 0x1000 - 0x13ff - Medium - Vendor Specific */ /* 0x1400 - 0x17ff - Host - Vendor Specific */ INIT_MAM_ATTR(0x1623, 1, 1, 0, mamp->VolumeLock, MAM_VOLUME_LOCK); /* 0x1800 : end of implemented attributes */ mamp->attributes[MAM_ATTRIBUTE_END] = (struct MAM_attr){0x1800, 0, 1, 0, NULL}; /* mhvtl attributes */ INIT_VTL_ATTR(0x01, 1, mamp->record_dirty, MAM_MHVTL_RECORD_DIRTY); INIT_VTL_ATTR(0x02, 2, mamp->Flags, MAM_MHVTL_FLAGS); INIT_VTL_ATTR(0x03, 1, mamp->max_partitions, MAM_MHVTL_MAX_PARTITIONS); INIT_VTL_ATTR(0x04, 1, mamp->num_partitions, MAM_MHVTL_NUM_PARTITIONS); INIT_VTL_ATTR(0x05, 1, mamp->MediaType, MAM_MHVTL_NUM_PARTITIONS); INIT_VTL_ATTR(0x06, 4, mamp->media_info.bits_per_mm, MAM_MHVTL_MEDIAINFO_BITS_PER_MM); INIT_VTL_ATTR(0x07, 2, mamp->media_info.tracks, MAM_MHVTL_MEDIAINFO_TRACKS); INIT_VTL_ATTR(0x08, 8, mamp->media_info.density_name, MAM_MHVTL_MEDIAINFO_DENSITY_NAME); INIT_VTL_ATTR(0x09, 32, mamp->media_info.description, MAM_MHVTL_MEDIAINFO_DESCRIPTION); } int get_config(char *buf, conf_file conf, long id) { char format[128]; char config_path[CONF_DIR_PATH_SZ] = {0}; FILE *fp = fopen(MHVTL_CONFIG_PATH "/mhvtl.conf", "r"); snprintf(format, sizeof(format), "%%255[^= \t\r] = %%%u[^\n]", CONF_DIR_PATH_SZ - 1); if (fp) { char *line = NULL; size_t len = 0; while (getline(&line, &len, fp) != -1) { if (line[0] == '#' || line[0] == '\n') continue; char key[256]; char value[CONF_DIR_PATH_SZ - 1]; if (sscanf(line, format, key, value) == 2) { if (strcmp(key, "MHVTL_CONFIG_PATH") == 0) { strncpy(config_path, value, sizeof(config_path) - 1); break; } } else { MHVTL_ERR("Error of syntax in configuration file %s :\n%s", MHVTL_CONFIG_PATH "/mhvtl.conf", line); continue; } } free(line); fclose(fp); } switch (conf) { case DEVICE_CONF: snprintf(buf, CONF_FILE_SZ, "%s/device.conf", (config_path[0] != '\0') ? config_path : MHVTL_CONFIG_PATH); break; case LIBCONTENTS: snprintf(buf, CONF_FILE_SZ, "%s/library_contents.%ld", (config_path[0] != '\0') ? config_path : MHVTL_CONFIG_PATH, id); break; case LIBCONTENTS_PERSIST: snprintf(buf, CONF_FILE_SZ, "%s/library_contents.%ld.persist", (config_path[0] != '\0') ? config_path : MHVTL_CONFIG_PATH, id); break; default: MHVTL_ERR("Wrong config file requested"); return -1; } return 0; } static struct state_description { char *state_desc; } state_desc[] = { { "Initialising v2", }, { "Idle", }, { "Unloading", }, { "Loading", }, { "Loading Cleaning Tape", }, { "Loading WORM media", }, { "Loading NULL media", }, { "Loaded", }, { "Loaded - Idle", }, { "Load failed", }, { "Rewinding", }, { "Positioning", }, { "Locate", }, { "Reading", }, { "Writing", }, { "Unloading", }, { "Erasing", }, { "Moving media from drive to slot", }, { "Moving media from slot to drive", }, { "Moving media from drive to MAP", }, { "Moving media from MAP to drive", }, { "Moving media from slot to MAP", }, { "Moving media from MAP to slot", }, { "Moving media from drive to drive", }, { "Moving media from slot to slot", }, { "Opening MAP", }, { "Closing MAP", }, { "Robot Inventory", }, { "Initialise Elements", }, { "Online", }, { "Offline", }, { "System Uninitialised", }, }; static char *slot_type_string[] = { "ANY", "Picker", "Storage", "MAP", "Drive", }; void mhvtl_prt_cdb(int lvl, struct scsi_cmd *cmd) { int groupCode; uint8_t *cdb = cmd->scb; #ifdef MHVTL_DEBUG uint64_t sn = cmd->dbuf_p->serialNo; uint64_t delay = (uint64_t)cmd->pollInterval; #endif groupCode = (cdb[0] & 0xe0) >> 5; switch (groupCode) { case 0: /* 6 byte commands */ MHVTL_DBG_NO_FUNC(lvl, "CDB (%" PRId64 ") (delay %" PRId64 "): " "%02x %02x %02x %02x %02x %02x", sn, delay, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5]); break; case 1: /* 10 byte commands */ case 2: /* 10 byte commands */ MHVTL_DBG_NO_FUNC(lvl, "CDB (%" PRId64 ") (delay %" PRId64 "): " "%02x %02x %02x %02x %02x %02x" " %02x %02x %02x %02x", sn, delay, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]); break; case 3: /* Reserved - There is always one exception ;) */ MHVTL_DBG_NO_FUNC(lvl, "CDB (%" PRId64 ") (delay %" PRId64 "): " "%02x %02x %02x %02x %02x %02x" " %02x %02x %02x %02x %02x %02x", sn, delay, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11]); break; case 4: /* 16 byte commands */ MHVTL_DBG_NO_FUNC(lvl, "CDB (%" PRId64 ") (delay %" PRId64 "): " "%02x %02x %02x %02x %02x %02x" " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", sn, delay, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11], cdb[12], cdb[13], cdb[14], cdb[15]); break; case 5: /* 12 byte commands */ MHVTL_DBG_NO_FUNC(lvl, "CDB (%" PRId64 ") (delay %" PRId64 "): " "%02x %02x %02x %02x %02x %02x %02x" " %02x %02x %02x %02x %02x", sn, delay, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11]); break; case 6: /* Vendor Specific */ case 7: /* Vendor Specific */ MHVTL_DBG_NO_FUNC(lvl, "CDB (%" PRId64 ") (delay %" PRId64 "), " "VENDOR SPECIFIC !! " " %02x %02x %02x %02x %02x %02x", sn, delay, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5]); break; } } /* * zalloc(int size) * * Wrapper to first call malloc() and zero out any allocated space. */ void *zalloc(int sz) { #ifdef __arm__ void *p = malloc(max(32, sz)); MHVTL_DBG(2, "arm: malloc(%d)%s", sz, (sz < 32) ? " : rounding up to 32" : ""); #else void *p = malloc(sz); #endif if (p) memset(p, 0, sz); return p; } /* * Fills in a global array with current sense data * Sets 'sam status' to SAM_STAT_CHECK_CONDITION. */ static void return_sense(uint8_t key, uint32_t asc_ascq, struct s_sd *sd, uint8_t *sam_stat) { char extended[32]; /* Clear Sense key status */ memset(sense, 0, SENSE_BUF_SIZE); *sam_stat = SAM_STAT_CHECK_CONDITION; sense[0] = SD_CURRENT_INFORMATION_FIXED; /* SPC4 (Revision 30) Ch: 4.5.1 states: * The RESPONSE CODE field shall be set to 70h in all unit attention * condition sense data in which: * - The ADDITIONAL SENSE CODE field is set to 29h * - The ADDITIONAL SENSE CODE is set to MODE PARAMETERS CHANGED */ switch (key) { case UNIT_ATTENTION: if ((asc_ascq >> 8) == 0x29) break; if (asc_ascq == E_MODE_PARAMETERS_CHANGED) break; /* Fall thru to default handling */ default: sense[0] |= SD_VALID; break; } sense[2] = key; sense[7] = SENSE_BUF_SIZE - 8; put_unaligned_be16(asc_ascq, &sense[12]); if (sd) { sense[15] = sd->byte0; put_unaligned_be16(sd->field_pointer, &sense[16]); sprintf(extended, " 0x%02x %04x", sd->byte0, sd->field_pointer); } MHVTL_DBG(1, "[Key/ASC/ASCQ] [%02x %02x %02x]%s", sense[2], sense[12], sense[13], (sd) ? extended : ""); } void sam_unit_attention(uint16_t ascq, uint8_t *sam_stat) { return_sense(UNIT_ATTENTION, ascq, NULL, sam_stat); MHVTL_DBG(1, ""); } void sam_not_ready(uint16_t ascq, uint8_t *sam_stat) { return_sense(NOT_READY, ascq, NULL, sam_stat); MHVTL_DBG(1, ""); } void sam_illegal_request(uint16_t ascq, struct s_sd *sd, uint8_t *sam_stat) { return_sense(ILLEGAL_REQUEST, ascq, sd, sam_stat); MHVTL_DBG(1, ""); } void sam_medium_error(uint16_t ascq, uint8_t *sam_stat) { return_sense(MEDIUM_ERROR, ascq, NULL, sam_stat); MHVTL_DBG(1, ""); } void sam_blank_check(uint16_t ascq, uint8_t *sam_stat) { return_sense(BLANK_CHECK, ascq, NULL, sam_stat); MHVTL_DBG(1, ""); } void sam_data_protect(uint16_t ascq, uint8_t *sam_stat) { return_sense(DATA_PROTECT, ascq, NULL, sam_stat); MHVTL_DBG(1, ""); } void sam_hardware_error(uint16_t ascq, uint8_t *sam_stat) { return_sense(HARDWARE_ERROR, ascq, NULL, sam_stat); MHVTL_DBG(1, ""); } void sam_no_sense(uint8_t key, uint16_t ascq, uint8_t *sam_stat) { return_sense(NO_SENSE | key, ascq, NULL, sam_stat); MHVTL_DBG(1, ""); } int check_reset(uint8_t *sam_stat) { int retval = reset; if (reset) { sam_unit_attention(E_POWERON_RESET, sam_stat); reset = 0; } return retval; } int check_inquiry_data_has_changed(uint8_t *sam_stat) { int retval = inquiry_data_changed; if (inquiry_data_changed) { MHVTL_DBG(1, "Returning INQUIRY_DATA_HAS_CHANGED"); sam_unit_attention(E_INQUIRY_DATA_HAS_CHANGED, sam_stat); inquiry_data_changed = 0; } return retval; } void reset_device(void) { reset = 1; } /* Force flag to indicate inquiry data has changed */ void set_inquiry_data_changed(void) { inquiry_data_changed = 1; } #define READBLOCKLIMITS_ARR_SZ 6 int resp_read_block_limits(struct mhvtl_ds *dbuf_p, int sz) { uint8_t *arr = (uint8_t *)dbuf_p->data; MHVTL_DBG(2, "Min/Max sz: %d/%d", 1, sz); memset(arr, 0, READBLOCKLIMITS_ARR_SZ); put_unaligned_be24(sz, &arr[1]); arr[5] = 0x1; /* Minimum block size */ return READBLOCKLIMITS_ARR_SZ; } /* * Respond with S/No. of media currently mounted */ uint32_t resp_read_media_serial(uint8_t *sno, uint8_t *buf, uint8_t *sam_stat) { uint32_t size = 38; snprintf((char *)&buf[4], size - 3, "%-34.34s", sno); put_unaligned_be16(size, &buf[2]); return size; } void setTapeAlert(struct TapeAlert_pg *ta, uint64_t flg) { int a; MHVTL_DBG(2, "Setting TapeAlert flags 0x%.8x %.8x", (uint32_t)(flg >> 32) & 0xffffffff, (uint32_t)flg & 0xffffffff); for (a = 0; a < 64; a++) ta->TapeAlert[a].value = (flg & (1ull << a)) ? 1 : 0; } /* * Simple function to read 'count' bytes from the chardev into 'buf'. */ int retrieve_CDB_data(int cdev, struct mhvtl_ds *ds) { int ioctl_err; MHVTL_DBG(3, "retrieving %d bytes from kernel", ds->sz); ioctl_err = ioctl(cdev, VTL_GET_DATA, ds); if (ioctl_err < 0) { MHVTL_ERR("Failed retrieving data via ioctl(): %s", strerror(errno)); return 0; } return ds->sz; } /* * Passes struct mhvtl_ds to kernel module. * struct contains amount of data, status and pointer to data struct. * * Returns nothing. */ void completeSCSICommand(int cdev, struct mhvtl_ds *ds) { uint8_t *s; ioctl(cdev, VTL_PUT_DATA, ds); s = (uint8_t *)ds->sense_buf; if (ds->sam_stat == SAM_STAT_CHECK_CONDITION) { MHVTL_DBG(2, "s/n: (%ld), sz: %d, sam_status: %d" " [%02x %02x %02x]", (unsigned long)ds->serialNo, ds->sz, ds->sam_stat, s[2], s[12], s[13]); } else { MHVTL_DBG(2, "OP s/n: (%ld), sz: %d, sam_status: %d", (unsigned long)ds->serialNo, ds->sz, ds->sam_stat); } ds->sam_stat = 0; } /* Hex dump 'count' bytes at p */ void hex_dump(uint8_t *p, int count) { int j; int lvl = 2; /* Log at level 'lvl' */ int n; for (j = 0; j < count; j += 16) { n = count - j; switch (n) { case 0: break; case 1: MHVTL_DBG_NO_FUNC(lvl, "%02x %45s : %c", p[j + 0], " ", isprint(p[j + 0]) ? p[j + 0] : ' '); break; case 2: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %42s : %c%c", p[j + 0], p[j + 1], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.'); break; case 3: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %39s : %c%c%c", p[j + 0], p[j + 1], p[j + 2], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.'); break; case 4: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %36s : %c%c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.'); break; case 5: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %33s : %c%c%c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.'); break; case 6: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %30s : %c%c%c%c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.'); break; case 7: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %02x %27s : %c%c%c%c%c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], p[j + 6], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.', isprint(p[j + 6]) ? p[j + 6] : '.'); break; case 8: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %02x %02x %24s : %c%c%c%c%c%c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], p[j + 6], p[j + 7], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.', isprint(p[j + 6]) ? p[j + 6] : '.', isprint(p[j + 7]) ? p[j + 7] : '.'); break; case 9: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %19s : %c%c%c%c%c%c%c%c %c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], p[j + 6], p[j + 7], p[j + 8], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.', isprint(p[j + 6]) ? p[j + 6] : '.', isprint(p[j + 7]) ? p[j + 7] : '.', isprint(p[j + 8]) ? p[j + 8] : '.'); break; case 10: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %16s : %c%c%c%c%c%c%c%c %c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], p[j + 6], p[j + 7], p[j + 8], p[j + 9], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.', isprint(p[j + 6]) ? p[j + 6] : '.', isprint(p[j + 7]) ? p[j + 7] : '.', isprint(p[j + 8]) ? p[j + 8] : '.', isprint(p[j + 9]) ? p[j + 9] : '.'); break; case 11: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %13s : %c%c%c%c%c%c%c%c %c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], p[j + 6], p[j + 7], p[j + 8], p[j + 9], p[j + 10], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.', isprint(p[j + 6]) ? p[j + 6] : '.', isprint(p[j + 7]) ? p[j + 7] : '.', isprint(p[j + 8]) ? p[j + 8] : '.', isprint(p[j + 9]) ? p[j + 9] : '.', isprint(p[j + 10]) ? p[j + 10] : '.'); break; case 12: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %10s : %c%c%c%c%c%c%c%c %c%c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], p[j + 6], p[j + 7], p[j + 8], p[j + 9], p[j + 10], p[j + 11], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.', isprint(p[j + 6]) ? p[j + 6] : '.', isprint(p[j + 7]) ? p[j + 7] : '.', isprint(p[j + 8]) ? p[j + 8] : '.', isprint(p[j + 9]) ? p[j + 9] : '.', isprint(p[j + 10]) ? p[j + 10] : '.', isprint(p[j + 11]) ? p[j + 11] : '.'); break; case 13: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %8s : %c%c%c%c%c%c%c%c %c%c%c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], p[j + 6], p[j + 7], p[j + 8], p[j + 9], p[j + 10], p[j + 11], p[j + 12], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.', isprint(p[j + 6]) ? p[j + 6] : '.', isprint(p[j + 7]) ? p[j + 7] : '.', isprint(p[j + 8]) ? p[j + 8] : '.', isprint(p[j + 9]) ? p[j + 9] : '.', isprint(p[j + 10]) ? p[j + 10] : '.', isprint(p[j + 11]) ? p[j + 11] : '.', isprint(p[j + 12]) ? p[j + 12] : '.'); break; case 14: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %5s : %c%c%c%c%c%c%c%c %c%c%c%c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], p[j + 6], p[j + 7], p[j + 8], p[j + 9], p[j + 10], p[j + 11], p[j + 12], p[j + 13], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.', isprint(p[j + 6]) ? p[j + 6] : '.', isprint(p[j + 7]) ? p[j + 7] : '.', isprint(p[j + 8]) ? p[j + 8] : '.', isprint(p[j + 9]) ? p[j + 9] : '.', isprint(p[j + 10]) ? p[j + 10] : '.', isprint(p[j + 11]) ? p[j + 11] : '.', isprint(p[j + 12]) ? p[j + 12] : '.', isprint(p[j + 13]) ? p[j + 13] : '.'); break; case 15: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %2s : %c%c%c%c%c%c%c%c %c%c%c%c%c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], p[j + 6], p[j + 7], p[j + 8], p[j + 9], p[j + 10], p[j + 11], p[j + 12], p[j + 13], p[j + 14], " ", isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.', isprint(p[j + 6]) ? p[j + 6] : '.', isprint(p[j + 7]) ? p[j + 7] : '.', isprint(p[j + 8]) ? p[j + 8] : '.', isprint(p[j + 9]) ? p[j + 9] : '.', isprint(p[j + 10]) ? p[j + 10] : '.', isprint(p[j + 11]) ? p[j + 11] : '.', isprint(p[j + 12]) ? p[j + 12] : '.', isprint(p[j + 13]) ? p[j + 13] : '.', isprint(p[j + 14]) ? p[j + 14] : '.'); break; default: MHVTL_DBG_NO_FUNC(lvl, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x : %c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c", p[j + 0], p[j + 1], p[j + 2], p[j + 3], p[j + 4], p[j + 5], p[j + 6], p[j + 7], p[j + 8], p[j + 9], p[j + 10], p[j + 11], p[j + 12], p[j + 13], p[j + 14], p[j + 15], isprint(p[j + 0]) ? p[j + 0] : '.', isprint(p[j + 1]) ? p[j + 1] : '.', isprint(p[j + 2]) ? p[j + 2] : '.', isprint(p[j + 3]) ? p[j + 3] : '.', isprint(p[j + 4]) ? p[j + 4] : '.', isprint(p[j + 5]) ? p[j + 5] : '.', isprint(p[j + 6]) ? p[j + 6] : '.', isprint(p[j + 7]) ? p[j + 7] : '.', isprint(p[j + 8]) ? p[j + 8] : '.', isprint(p[j + 9]) ? p[j + 9] : '.', isprint(p[j + 10]) ? p[j + 10] : '.', isprint(p[j + 11]) ? p[j + 11] : '.', isprint(p[j + 12]) ? p[j + 12] : '.', isprint(p[j + 13]) ? p[j + 13] : '.', isprint(p[j + 14]) ? p[j + 14] : '.', isprint(p[j + 15]) ? p[j + 15] : '.'); break; } } } static int mhvtl_access(char *p, int len, char *entry) { int fstat; struct stat km; char filename[256]; snprintf(filename, ARRAY_SIZE(filename), "/sys/bus/mhvtl/drivers/mhvtl/%s", entry); MHVTL_DBG(1, "Testing %s", filename); fstat = stat(filename, &km); if (fstat >= 0) { strncpy(p, filename, len); return 0; } snprintf(filename, ARRAY_SIZE(filename), "/sys/bus/pseudo9/drivers/mhvtl/%s", entry); MHVTL_DBG(1, "Testing %s", filename); fstat = stat(filename, &km); if (fstat >= 0) { strncpy(p, filename, len); return 0; } snprintf(filename, ARRAY_SIZE(filename), "/sys/bus/pseudo/drivers/mhvtl/%s", entry); MHVTL_DBG(1, "Testing %s", filename); fstat = stat(filename, &km); if (fstat >= 0) { strncpy(p, filename, len); return 0; } return -1; } /* Writing to the kernel module will block until the device is created. * Unfortunately, we need to be polling the device and process the * SCSI op code before the lu can be created. * Chicken & Egg. * So spawn child process and don't wait for return. * Let the child process write to the kernel module */ pid_t add_lu(unsigned minor, struct mhvtl_ctl *ctl) { char str[1024]; pid_t ppid, pid, mypid; ssize_t retval; FILE *pseudo; char pseudo_filename[256]; char errmsg[512]; sprintf(str, "add %u %d %d %d", minor, ctl->channel, ctl->id, ctl->lun); if (mhvtl_access(pseudo_filename, ARRAY_SIZE(pseudo_filename), "add_lu") < 0) { sprintf(str, "Could not find mhvtl kernel module"); MHVTL_ERR("%s: %s", mhvtl_driver_name, str); printf("%s: %s\n", mhvtl_driver_name, str); exit(EIO); } /* Parent PID */ ppid = getpid(); switch (pid = fork()) { case 0: /* Child */ mypid = getpid(); pseudo = fopen(pseudo_filename, "w"); if (!pseudo) { snprintf(errmsg, ARRAY_SIZE(errmsg), "Could not open %s: %s", pseudo_filename, strerror(errno)); MHVTL_ERR("Parent PID: %ld -> %s : %s", (long)ppid, errmsg, strerror(errno)); perror("Could not open 'add_lu'"); exit(-1); } retval = fprintf(pseudo, "%s\n", str); MHVTL_DBG(2, "Wrote '%s' (%d bytes) to %s", str, (int)retval, pseudo_filename); fclose(pseudo); MHVTL_DBG(1, "Parent PID: [%ld] -> Child [%ld] anounces 'lu [%d:%d:%d] created'.", (long)ppid, (long)mypid, ctl->channel, ctl->id, ctl->lun); exit(0); break; case -1: perror("Failed to fork()"); MHVTL_ERR("Parent PID: %ld -> Fail to fork() %s", (long)ppid, strerror(errno)); return 0; break; default: /* Parent */ MHVTL_DBG(2, "[%ld] Child PID [%ld] will start logical unit [%d:%d:%d]", (long)ppid, (long)pid, ctl->channel, ctl->id, ctl->lun); return pid; break; } return 0; } static int chrdev_get_major(void) { FILE *f; char filename[256]; const char str[] = "Could not locate mhvtl kernel module"; int rc = 0; int x; int majno; if (mhvtl_access(filename, ARRAY_SIZE(filename), "major") < 0) { MHVTL_ERR("%s: %s", mhvtl_driver_name, str); printf("%s: %s\n", mhvtl_driver_name, str); exit(EIO); } f = fopen(filename, "r"); if (!f) { MHVTL_DBG(1, "Can't open %s: %s", filename, strerror(errno)); return -ENOENT; } x = fscanf(f, "%d", &majno); if (!x) { MHVTL_DBG(1, "Cant identify major number for mhvtl"); rc = -1; } else rc = majno; fclose(f); return rc; } int chrdev_create(unsigned minor) { int majno; int x; int ret = 0; dev_t dev; char pathname[64]; snprintf(pathname, sizeof(pathname), "/dev/mhvtl%u", minor); majno = chrdev_get_major(); if (majno == -ENOENT) { MHVTL_DBG(1, "** Incorrect version of kernel module loaded **"); ret = -1; goto err; } dev = makedev(majno, minor); MHVTL_DBG(2, "Major number: %d, minor number: %u", major(dev), minor(dev)); MHVTL_DBG(3, "mknod(%s, %02o, major: %d minor: %d", pathname, S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, major(dev), minor(dev)); x = mknod(pathname, S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, dev); if (x < 0) { if (errno == EEXIST) /* Success if node already exists */ return 0; MHVTL_DBG(1, "Error creating device node for mhvtl: %s", strerror(errno)); ret = -1; } err: return ret; } int chrdev_open(const char *name, unsigned minor) { FILE *f; char devname[256]; char buf[256]; int devn; int ctlfd; f = fopen("/proc/devices", "r"); if (!f) { printf("Cannot open control path to the driver: %s\n", strerror(errno)); return -1; } devn = 0; while (!feof(f)) { if (!fgets(buf, sizeof(buf), f)) break; if (sscanf(buf, "%d %s", &devn, devname) != 2) continue; if (!strcmp(devname, name)) break; devn = 0; } fclose(f); if (!devn) { printf("Cannot find %s in /proc/devices - " "make sure the module is loaded\n", name); return -1; } snprintf(devname, sizeof(devname), "/dev/%s%u", name, minor); ctlfd = open(devname, O_RDWR | O_NONBLOCK | O_EXCL); if (ctlfd < 0) { printf("Cannot open %s %s\n", devname, strerror(errno)); fflush(NULL); printf("\n\n"); return -1; } return ctlfd; } /* Create the fifo and open it for writing (appending) * Return 0 on success, * Return errno on failure */ int open_fifo(FILE **fifo_fd, char *fifoname) { int ret; umask(0); ret = 0; ret = mknod(fifoname, S_IFIFO | 0644, 0); if ((ret < 0) && (errno != EEXIST)) { MHVTL_LOG("Sorry, cant create %s: %s, Disabling fifo feature", fifoname, strerror(errno)); ret = errno; } else { *fifo_fd = fopen(fifoname, "w+"); if (*fifo_fd) { MHVTL_DBG(2, "Successfully opened named pipe: %s", fifoname); } else { MHVTL_LOG("Sorry, cant open %s: %s, " "Disabling fifo feature", fifoname, strerror(errno)); ret = errno; } } return ret; } void status_change(FILE *fifo_fd, int current_status, int m_id, char **msg) { time_t t; char *timestamp; unsigned i; if (!fifo_fd) return; t = time(NULL); timestamp = ctime(&t); for (i = 14; i < strlen(timestamp); i++) if (timestamp[i] == '\n') timestamp[i] = '\0'; if (*msg) { fprintf(fifo_fd, "%s - %d: - %s\n", timestamp, m_id, *msg); free(*msg); *msg = NULL; } else fprintf(fifo_fd, "%s - %d: - %s\n", timestamp, m_id, state_desc[current_status].state_desc); fflush_unlocked(fifo_fd); return; } /* * Pinched straight from SCSI tgt code * Thanks guys.. * * Modified to always return success. Earlier kernels don't have oom_adjust * 'feature' so don't fail if we can't find it.. */ int oom_adjust(void) { int fd, ret; char path[64]; /* Avoid oom-killer */ sprintf(path, "/proc/%d/oom_score_adj", getpid()); fd = open(path, O_WRONLY); if (fd < 0) { MHVTL_DBG(3, "Can't open oom-killer's pardon %s, %s", path, strerror(errno)); return 0; } ret = write(fd, "-17\n", 4); if (ret < 0) { MHVTL_DBG(3, "Can't adjust oom-killer's pardon %s, %s", path, strerror(errno)); } close(fd); return 0; } /* fgets but replace '\n' with null */ char *readline(char *buf, int len, FILE *s) { int i; char *ret; ret = fgets(buf, len, s); if (!ret) return ret; /* Skip blank line */ for (i = 1; i < len; i++) if (buf[i] == '\n') buf[i] = 0; MHVTL_DBG(3, "%s", buf); return ret; } /* Copy bytes from 'src' to 'dest, blank-filling to length 'len'. There will * not be a NULL byte at the end. */ void blank_fill(uint8_t *dest, char *src, int len) { int i; for (i = 0; i < len; i++) { if (*src != '\0') *dest++ = *src++; else *dest++ = ' '; } } void truncate_spaces(char *s, int maxlen) { int x; for (x = 0; x < maxlen; x++) if ((s[x] == ' ') || (s[x] == '\0')) { s[x] = '\0'; return; } } /* MHVTL_VERSION looks like : 0.18.xx or 1.xx.xx * * Convert into a string after converting the 18.xx into "18xx" * * NOTE: Caller has to free string after use. */ char *get_version(void) { char b[64]; int x, y, z; char *c; c = (char *)zalloc(32); /* Way more than enough for a 4 byte string */ if (!c) return NULL; sprintf(b, "%s", MHVTL_VERSION); sscanf(b, "%d.%d.%d", &x, &y, &z); if (x) sprintf(c, "%02d%02d", x, y); else sprintf(c, "%02d%02d", y, z); return c; } void log_opcode(char *opcode, struct scsi_cmd *cmd) { struct s_sd sd; MHVTL_DBG(1, "*** Unsupported op code: %s ***", opcode); sd.byte0 = SKSV | CD; sd.field_pointer = 0; sam_illegal_request(E_INVALID_OP_CODE, &sd, &cmd->dbuf_p->sam_stat); MHVTL_DBG_PRT_CDB(1, cmd); } #define LOCK_PATH "/var/lock/mhvtl" #define MAX_WAIT 20 int check_for_running_daemons(unsigned minor) { char lck_file[128]; int lck; int a; long rand; /* Don't really care if this dir exists already */ if (mkdir(LOCK_PATH, S_IWUSR | S_IRUSR) < 0) { MHVTL_DBG(3, "Unable to create lock directory %s", LOCK_PATH); } sprintf(lck_file, "%s/mhvtl%d", LOCK_PATH, minor); MHVTL_DBG(1, "Checking lock file %s", lck_file); for (a = 0; a < MAX_WAIT; a++) { lck = open(lck_file, O_CREAT | O_EXCL | O_WRONLY, S_IRWXU); if (lck >= 0) { MHVTL_DBG(1, "Successfully created lock file %s", lck_file); close(lck); return 0; } else if (errno == EEXIST) { /* Lock file still exists */ rand = (0xffL & random()) << 12; MHVTL_DBG(3, "Lock file exists, sleeping for 0x%lxs", rand); usleep((useconds_t)rand); } else { /* Unexpected error */ MHVTL_DBG(1, "opening lock file %s failed %s", lck_file, strerror(errno)); return 2; } } MHVTL_DBG(1, "Unable to obtain lock file %s - returing error", lck_file); return 1; } int free_lock(unsigned minor) { char lck_file[128]; sprintf(lck_file, "%s/mhvtl%d", LOCK_PATH, minor); MHVTL_DBG(1, "Unlink %s", lck_file); unlink(lck_file); return 0; } /* Abort if string length > len */ void checkstrlen(char *s, unsigned int len, int lineno) { if (strlen(s) > len) { MHVTL_DBG(1, "Line #: %d, String %s is > %d... Aborting", lineno, s, len); printf("String %s longer than %d chars\n", s, len); printf("Please fix config file\n"); abort(); } } int device_type_register(struct lu_phy_attr *lu, struct device_type_template *t) { lu->scsi_ops = t; return 0; } uint8_t set_compression_mode_pg(struct list_head *l, int lvl) { struct mode *m; uint8_t *p; MHVTL_DBG(3, "*** Trace ***"); /* Find pointer to Data Compression mode Page */ m = lookup_mode_pg(l, MODE_DATA_COMPRESSION, 0); MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", l, m, m->pcodePointer); if (m) { p = m->pcodePointer; p[2] |= 0x80; /* Set data compression enable */ } /* Find pointer to Device Configuration mode Page */ m = lookup_mode_pg(l, MODE_DEVICE_CONFIGURATION, 0); MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", l, m, m->pcodePointer); if (m) { p = m->pcodePointer; p[14] = lvl; } return SAM_STAT_GOOD; } uint8_t clear_compression_mode_pg(struct list_head *l) { struct mode *m; uint8_t *p; MHVTL_DBG(3, "*** Trace ***"); /* Find pointer to Data Compression mode Page */ m = lookup_mode_pg(l, MODE_DATA_COMPRESSION, 0); MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", l, m, m->pcodePointer); if (m) { p = m->pcodePointer; p[2] &= 0x7f; /* clear data compression enable */ } /* Find pointer to Device Configuration mode Page */ m = lookup_mode_pg(l, MODE_DEVICE_CONFIGURATION, 0); MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", l, m, m->pcodePointer); if (m) { p = m->pcodePointer; p[14] = MHVTL_NO_COMPRESSION; } return SAM_STAT_GOOD; } uint8_t clear_WORM(struct list_head *l) { uint8_t *smp_dp; struct mode *m; m = lookup_mode_pg(l, MODE_MEDIUM_CONFIGURATION, 0); if (!m) { MHVTL_DBG(3, "Did not find MODE_MEDIUM_CONFIGURATION page"); } else { MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", l, m, m->pcodePointer); smp_dp = m->pcodePointer; if (!smp_dp) return SAM_STAT_GOOD; smp_dp[2] = 0x0; } return SAM_STAT_GOOD; } uint8_t set_WORM(struct list_head *l) { uint8_t *smp_dp; struct mode *m; MHVTL_DBG(3, "*** Trace ***"); m = lookup_mode_pg(l, MODE_MEDIUM_CONFIGURATION, 0); if (!m) { MHVTL_DBG(3, "Did not find MODE_MEDIUM_CONFIGURATION page"); } else { MHVTL_DBG(3, "l: %p, m: %p, m->pcodePointer: %p", l, m, m->pcodePointer); smp_dp = m->pcodePointer; if (!smp_dp) return SAM_STAT_GOOD; smp_dp[2] = 0x10; smp_dp[4] = 0x01; /* Indicate label overwrite */ } return SAM_STAT_GOOD; } /* Remove newline from string and fill rest of 'len' with char 'c' */ void rmnl(char *s, unsigned char c, int len) { int i; int found = 0; for (i = 0; i < len; i++) { if (s[i] == '\n') found = 1; if (found) s[i] = c; } } void update_vpd_86(struct lu_phy_attr *lu, void *p) { struct vpd *vpd_pg = lu->lu_vpd[PCODE_OFFSET(0x86)]; struct ssc_personality_template *spt; spt = p; MHVTL_DBG(1, "SPT is : 0x%02x", spt->drive_supports_LBP); vpd_pg->data[0] = (spt->drive_supports_LBP) ? 0x8 : 0; vpd_pg->data[1] = 0x01; /* SIMPSUP (Device supports simple queing) */ } void update_vpd_b0(struct lu_phy_attr *lu, void *p) { struct vpd *vpd_pg = lu->lu_vpd[PCODE_OFFSET(0xb0)]; uint8_t *worm; worm = (uint8_t *)p; *vpd_pg->data = (*worm) ? 1 : 0; /* Set WORM bit */ } void update_vpd_b1(struct lu_phy_attr *lu, void *p) { struct vpd *vpd_pg = lu->lu_vpd[PCODE_OFFSET(0xb1)]; memcpy(vpd_pg->data, p, vpd_pg->sz); } void update_vpd_b2(struct lu_phy_attr *lu, void *p) { struct vpd *vpd_pg = lu->lu_vpd[PCODE_OFFSET(0xb2)]; memcpy(vpd_pg->data, p, vpd_pg->sz); } /* VPD 0xB5 */ void update_vpd_lbp(struct lu_phy_attr *lu, void *p) { struct vpd *vpd_pg = lu->lu_vpd[PCODE_OFFSET(0xb2)]; memcpy(vpd_pg->data, p, vpd_pg->sz); } void update_vpd_c0(struct lu_phy_attr *lu, void *p) { struct vpd *vpd_pg = lu->lu_vpd[PCODE_OFFSET(0xc0)]; memcpy(&vpd_pg->data[20], p, strlen((const char *)p)); } void update_vpd_c1(struct lu_phy_attr *lu, void *p) { struct vpd *vpd_pg = lu->lu_vpd[PCODE_OFFSET(0xc1)]; memcpy(vpd_pg->data, p, vpd_pg->sz); } void cleanup_density_support(struct list_head *l) { struct supported_density_list *dp, *ndp; list_for_each_entry_safe(dp, ndp, l, siblings) { list_del(&dp->siblings); free(dp); } } int add_density_support(struct list_head *l, struct density_info *di, int rw) { struct supported_density_list *supported; supported = zalloc(sizeof(struct supported_density_list)); if (!supported) return -ENOMEM; supported->density_info = di; supported->rw = rw; list_add_tail(&supported->siblings, l); return 0; } void process_fifoname(struct lu_phy_attr *lu, char *s, int flag) { MHVTL_DBG(3, "entry: %s, flag: %d, existing name: %s", s, flag, lu->fifoname); if (lu->fifo_flag) /* fifo set via '-f ' switch */ return; checkstrlen(s, MALLOC_SZ - 1, 0); free(lu->fifoname); lu->fifoname = (char *)zalloc(strlen(s) + 2); if (!lu->fifoname) { printf("Unable to malloc fifo buffer"); exit(-ENOMEM); } lu->fifo_flag = flag; /* Already checked for sane length */ strcpy(lu->fifoname, s); } /* Remove message queue */ void cleanup_msg(void) { int msqid; int retval; struct msqid_ds ds; msqid = init_queue(); if (msqid < 0) { MHVTL_ERR("Failed to open msg queue: %s", strerror(errno)); return; } retval = msgctl(msqid, IPC_RMID, &ds); if (retval < 0) { MHVTL_ERR("Failed to remove msg queue: %s", strerror(errno)); } else { MHVTL_DBG(2, "Removed ipc resources"); } } #define QUERYSHM 0 #define INCSHM 1 #define DECSHM 2 static int mhvtl_shared_mem(int flag) { int mhvtl_shm; int retval = -1; int *base; key_t key; struct shmid_ds buf; key = 0x4d61726b; mhvtl_shm = shmget(key, 16, IPC_CREAT | 0666); if (mhvtl_shm < 0) { printf("Attempt to get Shared memory failed\n"); MHVTL_ERR("Attempt to get shared memory failed"); return -ENOMEM; } base = (int *)shmat(mhvtl_shm, NULL, 0); if (base == (void *)-1) { MHVTL_ERR("Failed to attach to shm: %s", strerror(errno)); return -1; } MHVTL_DBG(3, "[%d] shm count is: %d", (int)getpid(), *base); switch (flag) { case QUERYSHM: break; case INCSHM: (*base)++; break; case DECSHM: if (*base) (*base)--; /* No more consumers - mark as remove */ if (*base == 0) { shmctl(mhvtl_shm, IPC_STAT, &buf); shmctl(mhvtl_shm, IPC_RMID, &buf); MHVTL_DBG(3, "pid of creator: %d," " pid of last shmat(): %d, " " Number of current attach: %d", buf.shm_cpid, buf.shm_lpid, (int)buf.shm_nattch); /* Should be no more users of the message Q either */ cleanup_msg(); } break; } MHVTL_DBG(3, "[%d] shm count now: %d", (int)getpid(), *base); retval = *base; shmdt(base); return retval; } static int mhvtl_fifo_count(int direction) { sem_t *mhvtl_sem; int sval; int i; char errmsg[] = "mhvtl_sem"; int retval = -1; mhvtl_sem = sem_open("/mhVTL", O_CREAT, 0664, 1); if (SEM_FAILED == mhvtl_sem) { MHVTL_ERR("%s : %s", errmsg, strerror(errno)); return retval; } sem_getvalue(mhvtl_sem, &sval); for (i = 0; i < 10; i++) { if (sem_trywait(mhvtl_sem)) { MHVTL_LOG("Waiting for semaphore: %p", mhvtl_sem); sleep(1); if (i > 8) { /* Give up.. Clear the semaphore & do it */ MHVTL_ERR("waiting for semaphore: %p", mhvtl_sem); sem_post(mhvtl_sem); } } else { retval = mhvtl_shared_mem(direction); sem_post(mhvtl_sem); break; } } sem_close(mhvtl_sem); return retval; } int dec_fifo_count(void) { return mhvtl_fifo_count(DECSHM); } int inc_fifo_count(void) { return mhvtl_fifo_count(INCSHM); } int get_fifo_count(void) { return mhvtl_fifo_count(QUERYSHM); } /* * find the mhvtl home directory and the library ID supplied in the device.conf * file from our config directory. Use the default config directory unless one * is passed in */ void find_media_home_directory(char *config_directory, long lib_id) { char device_conf[CONF_FILE_SZ]; FILE *conf; char *b; /* Read from file into this buffer */ char *s; /* Somewhere for sscanf to store results */ long i; int found; found = 0; home_directory[0] = '\0'; if (config_directory) { snprintf(device_conf, CONF_FILE_SZ, "%s/device.conf", config_directory); } else if (get_config(device_conf, DEVICE_CONF, my_id) < 0) { exit(1); } conf = fopen(device_conf, "r"); if (!conf) { MHVTL_ERR("Can not open config file %s : %s", device_conf, strerror(errno)); perror("Can not open config file"); exit(1); } s = zalloc(MALLOC_SZ); if (!s) { perror("Could not allocate memory"); exit(1); } b = zalloc(MALLOC_SZ); if (!b) { perror("Could not allocate memory"); exit(1); } while (readline(b, MALLOC_SZ, conf) != NULL) { if (b[0] == '#') /* Ignore comments */ continue; if (strlen(b) < 3) /* Reset drive number of blank line */ i = 0xff; if (sscanf(b, "Library: %ld ", &i)) { MHVTL_DBG(2, "Found Library %ld, looking for %ld", i, lib_id); if (i == lib_id) found = 1; } if (found == 1) { int a; a = sscanf(b, " Home directory: %s", s); if (a > 0) { strncpy(home_directory, s, HOME_DIR_PATH_SZ); MHVTL_DBG(2, "Found home directory : %s", home_directory); goto finished; /* Found what we came for */ } } } /* Not found, then append the library id to default path */ snprintf(home_directory, HOME_DIR_PATH_SZ, "%s/%ld", MHVTL_HOME_PATH, lib_id); MHVTL_DBG(1, "Append library id %ld to default path %s: %s", lib_id, MHVTL_HOME_PATH, home_directory); finished: free(s); free(b); fclose(conf); } unsigned int set_media_params(struct MAM *mamp, char *density) { /* Invent some defaults */ mamp->MediaType = Media_undefined; put_unaligned_be32(2048, &mamp->media_info.bits_per_mm); put_unaligned_be16(1, &mamp->media_info.tracks); put_unaligned_be32(127, &mamp->MediumWidth); put_unaligned_be32(1024, &mamp->MediumLength); memcpy(&mamp->media_info.description, "mhvtl", 5); mamp->max_partitions = 1; mamp->num_partitions = 1; if (!(strncmp(density, "LTO1", 4))) { mamp->MediumDensityCode = medium_density_code_lto1; mamp->MediaType = Media_LTO1; put_unaligned_be32(384, &mamp->MediumLength); put_unaligned_be32(127, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "Ultrium 1/8T", 12); memcpy(&mamp->media_info.density_name, "U-18 ", 6); memcpy(&mamp->AssigningOrganization_1, "LTO-CVE", 7); put_unaligned_be32(4880, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "LTO2", 4))) { mamp->MediumDensityCode = medium_density_code_lto2; mamp->MediaType = Media_LTO2; put_unaligned_be32(512, &mamp->MediumLength); put_unaligned_be32(127, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "Ultrium 2/8T", 12); memcpy(&mamp->media_info.density_name, "U-28 ", 6); memcpy(&mamp->AssigningOrganization_1, "LTO-CVE", 7); put_unaligned_be32(7398, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "LTO3", 4))) { mamp->MediumDensityCode = medium_density_code_lto3; mamp->MediaType = Media_LTO3; put_unaligned_be32(704, &mamp->MediumLength); put_unaligned_be32(127, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "Ultrium 3/16T", 13); memcpy(&mamp->media_info.density_name, "U-316 ", 6); memcpy(&mamp->AssigningOrganization_1, "LTO-CVE", 7); put_unaligned_be32(9638, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "LTO4", 4))) { mamp->MediumDensityCode = medium_density_code_lto4; mamp->MediaType = Media_LTO4; put_unaligned_be32(896, &mamp->MediumLength); put_unaligned_be32(127, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "Ultrium 4/16T", 13); memcpy(&mamp->media_info.density_name, "U-416 ", 6); memcpy(&mamp->AssigningOrganization_1, "LTO-CVE", 7); put_unaligned_be32(12725, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "LTO5", 4))) { mamp->MediumDensityCode = medium_density_code_lto5; mamp->MediaType = Media_LTO5; put_unaligned_be32(1280, &mamp->MediumLength); put_unaligned_be32(127, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "Ultrium 5/16T", 13); memcpy(&mamp->media_info.density_name, "U-516 ", 6); memcpy(&mamp->AssigningOrganization_1, "LTO-CVE", 7); put_unaligned_be32(15142, &mamp->media_info.bits_per_mm); mamp->max_partitions = 2; mamp->num_partitions = 2; } else if (!(strncmp(density, "LTO6", 4))) { mamp->MediumDensityCode = medium_density_code_lto6; mamp->MediaType = Media_LTO6; put_unaligned_be32(2176, &mamp->MediumLength); put_unaligned_be32(127, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "Ultrium 6/16T", 13); memcpy(&mamp->media_info.density_name, "U-616 ", 6); memcpy(&mamp->AssigningOrganization_1, "LTO-CVE", 7); put_unaligned_be32(18441, &mamp->media_info.bits_per_mm); mamp->max_partitions = 2; mamp->num_partitions = 2; } else if (!(strncmp(density, "LTO7", 4))) { mamp->MediumDensityCode = medium_density_code_lto7; mamp->MediaType = Media_LTO7; put_unaligned_be32(960, &mamp->MediumLength); put_unaligned_be32(127, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "Ultrium 7/32T", 13); memcpy(&mamp->media_info.density_name, "U-732 ", 6); memcpy(&mamp->AssigningOrganization_1, "LTO-CVE", 7); put_unaligned_be32(19107, &mamp->media_info.bits_per_mm); mamp->max_partitions = 2; mamp->num_partitions = 2; } else if (!(strncmp(density, "LTO8", 4))) { mamp->MediumDensityCode = medium_density_code_lto8; mamp->MediaType = Media_LTO8; put_unaligned_be32(960, &mamp->MediumLength); put_unaligned_be32(127, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "Ultrium 8/32T", 13); memcpy(&mamp->media_info.density_name, "U-832 ", 6); memcpy(&mamp->AssigningOrganization_1, "LTO-CVE", 7); put_unaligned_be32(19107, &mamp->media_info.bits_per_mm); mamp->max_partitions = 2; mamp->num_partitions = 2; } else if (!(strncmp(density, "LTO9", 4))) { mamp->MediumDensityCode = medium_density_code_lto9; mamp->MediaType = Media_LTO9; put_unaligned_be32(960, &mamp->MediumLength); put_unaligned_be32(127, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "Ultrium 9/32T", 13); memcpy(&mamp->media_info.density_name, "U-932 ", 6); memcpy(&mamp->AssigningOrganization_1, "LTO-CVE", 7); put_unaligned_be32(19107, &mamp->media_info.bits_per_mm); mamp->max_partitions = 2; mamp->num_partitions = 2; } else if (!(strncmp(density, "AIT1", 4))) { /* Vaules for AIT taken from "Product Manual SDX-900V v1.0" */ mamp->MediumDensityCode = medium_density_code_ait1; mamp->MediaType = Media_AIT1; put_unaligned_be32(384, &mamp->MediumLength); put_unaligned_be32(0x50, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "AdvIntelligentTape1", 20); memcpy(&mamp->media_info.density_name, "AIT-1 ", 6); memcpy(&mamp->AssigningOrganization_1, "SONY", 4); put_unaligned_be32(0x11d7, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "AIT2", 4))) { mamp->MediumDensityCode = medium_density_code_ait2; mamp->MediaType = Media_AIT2; put_unaligned_be32(384, &mamp->MediumLength); put_unaligned_be32(0x50, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "AdvIntelligentTape2", 20); memcpy(&mamp->media_info.density_name, "AIT-2 ", 6); memcpy(&mamp->AssigningOrganization_1, "SONY", 4); put_unaligned_be32(0x17d6, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "AIT3", 4))) { mamp->MediumDensityCode = medium_density_code_ait3; mamp->MediaType = Media_AIT3; put_unaligned_be32(384, &mamp->MediumLength); put_unaligned_be32(0x50, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "AdvIntelligentTape3", 20); memcpy(&mamp->media_info.density_name, "AIT-3 ", 6); memcpy(&mamp->AssigningOrganization_1, "SONY", 4); put_unaligned_be32(0x17d6, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "AIT4", 4))) { mamp->MediumDensityCode = medium_density_code_ait4; mamp->MediaType = Media_AIT4; put_unaligned_be32(384, &mamp->MediumLength); put_unaligned_be32(0x50, &mamp->MediumWidth); memcpy(&mamp->media_info.description, "AdvIntelligentTape4", 20); memcpy(&mamp->media_info.density_name, "AIT-4 ", 6); memcpy(&mamp->AssigningOrganization_1, "SONY", 4); put_unaligned_be32(0x17d6, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "DLT3", 4))) { mamp->MediumDensityCode = medium_density_code_dlt3; mamp->MediaType = Media_DLT3; memcpy(&mamp->media_info.description, "DLTtape III", 11); memcpy(&mamp->media_info.density_name, "DLT-III", 7); memcpy(&mamp->AssigningOrganization_1, "QUANTUM", 7); } else if (!(strncmp(density, "DLT4", 4))) { mamp->MediumDensityCode = medium_density_code_dlt4; mamp->MediaType = Media_DLT4; memcpy(&mamp->media_info.description, "DLTtape IV", 10); memcpy(&mamp->media_info.density_name, "DLT-IV", 6); memcpy(&mamp->AssigningOrganization_1, "QUANTUM", 7); } else if (!(strncmp(density, "SDLT1", 5))) { mamp->MediumDensityCode = 0x48; mamp->MediaType = Media_SDLT; memcpy(&mamp->media_info.description, "SDLT I media", 12); memcpy(&mamp->media_info.density_name, "SDLT-1", 6); memcpy(&mamp->AssigningOrganization_1, "QUANTUM", 7); put_unaligned_be32(133000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "SDLT220", 7))) { mamp->MediumDensityCode = medium_density_code_220; mamp->MediaType = Media_SDLT220; memcpy(&mamp->media_info.description, "SDLT I media", 12); memcpy(&mamp->media_info.density_name, "SDLT220", 7); memcpy(&mamp->AssigningOrganization_1, "QUANTUM", 7); put_unaligned_be32(133000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "SDLT320", 7))) { mamp->MediumDensityCode = medium_density_code_320; mamp->MediaType = Media_SDLT320; memcpy(&mamp->media_info.description, "SDLT I media", 12); memcpy(&mamp->media_info.density_name, "SDLT320", 7); memcpy(&mamp->AssigningOrganization_1, "QUANTUM", 7); put_unaligned_be32(190000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "SDLT600", 7))) { mamp->MediumDensityCode = medium_density_code_600; mamp->MediaType = Media_SDLT600; memcpy(&mamp->media_info.description, "SDLT II media", 13); memcpy(&mamp->media_info.density_name, "SDLT600", 7); memcpy(&mamp->AssigningOrganization_1, "QUANTUM", 7); put_unaligned_be32(233000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "9840A", 5))) { mamp->MediumDensityCode = medium_density_code_9840A; mamp->MediaType = Media_9840A; memcpy(&mamp->media_info.description, "Raven 20 GB", 11); memcpy(&mamp->media_info.density_name, "R-20", 4); memcpy(&mamp->AssigningOrganization_1, "STK", 3); put_unaligned_be32(0, &mamp->media_info.bits_per_mm); put_unaligned_be16(288, &mamp->media_info.tracks); put_unaligned_be32(127, &mamp->MediumWidth); put_unaligned_be32(1024, &mamp->MediumLength); } else if (!(strncmp(density, "9840B", 5))) { mamp->MediumDensityCode = medium_density_code_9840B; mamp->MediaType = Media_9840B; memcpy(&mamp->media_info.description, "Raven 20 GB", 11); memcpy(&mamp->media_info.density_name, "R-20", 4); memcpy(&mamp->AssigningOrganization_1, "STK", 3); put_unaligned_be32(0, &mamp->media_info.bits_per_mm); put_unaligned_be16(288, &mamp->media_info.tracks); put_unaligned_be32(127, &mamp->MediumWidth); put_unaligned_be32(1024, &mamp->MediumLength); } else if (!(strncmp(density, "9840C", 5))) { mamp->MediumDensityCode = medium_density_code_9840C; mamp->MediaType = Media_9840C; memcpy(&mamp->media_info.description, "Raven 40 GB", 11); memcpy(&mamp->media_info.density_name, "R-40", 4); memcpy(&mamp->AssigningOrganization_1, "STK", 3); put_unaligned_be32(0, &mamp->media_info.bits_per_mm); put_unaligned_be16(288, &mamp->media_info.tracks); put_unaligned_be32(127, &mamp->MediumWidth); put_unaligned_be32(1024, &mamp->MediumLength); } else if (!(strncmp(density, "9840D", 5))) { mamp->MediumDensityCode = medium_density_code_9840D; mamp->MediaType = Media_9840D; memcpy(&mamp->media_info.description, "Raven 75 GB", 11); memcpy(&mamp->media_info.density_name, "R-75", 4); memcpy(&mamp->AssigningOrganization_1, "STK", 3); put_unaligned_be32(0, &mamp->media_info.bits_per_mm); put_unaligned_be16(576, &mamp->media_info.tracks); put_unaligned_be32(127, &mamp->MediumWidth); put_unaligned_be32(1024, &mamp->MediumLength); } else if (!(strncmp(density, "9940A", 5))) { mamp->MediumDensityCode = medium_density_code_9940A; mamp->MediaType = Media_9940A; memcpy(&mamp->media_info.description, "PeakCapacity 60 GB", 18); memcpy(&mamp->media_info.density_name, "P-60", 4); memcpy(&mamp->AssigningOrganization_1, "STK", 3); put_unaligned_be32(0, &mamp->media_info.bits_per_mm); put_unaligned_be16(288, &mamp->media_info.tracks); put_unaligned_be32(127, &mamp->MediumWidth); put_unaligned_be32(1024, &mamp->MediumLength); } else if (!(strncmp(density, "9940B", 5))) { mamp->MediumDensityCode = medium_density_code_9940B; mamp->MediaType = Media_9940B; memcpy(&mamp->media_info.description, "PeakCapacity 200 GB", 19); memcpy(&mamp->media_info.density_name, "P-200", 5); memcpy(&mamp->AssigningOrganization_1, "STK", 3); put_unaligned_be32(0, &mamp->media_info.bits_per_mm); put_unaligned_be16(576, &mamp->media_info.tracks); put_unaligned_be32(127, &mamp->MediumWidth); put_unaligned_be32(1024, &mamp->MediumLength); } else if (!(strncmp(density, "T10KA", 5))) { mamp->MediumDensityCode = medium_density_code_10kA; mamp->MediaType = Media_T10KA; memcpy(&mamp->media_info.description, "STK T10KA media", 15); memcpy(&mamp->media_info.density_name, "T10000A", 7); memcpy(&mamp->AssigningOrganization_1, "STK", 3); put_unaligned_be32(233000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "T10KB", 5))) { mamp->MediumDensityCode = medium_density_code_10kB; mamp->MediaType = Media_T10KB; memcpy(&mamp->media_info.description, "STK T10KB media", 15); memcpy(&mamp->media_info.density_name, "T10000B", 7); memcpy(&mamp->AssigningOrganization_1, "STK", 3); put_unaligned_be32(233000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "T10KC", 5))) { mamp->MediumDensityCode = medium_density_code_10kC; mamp->MediaType = Media_T10KC; memcpy(&mamp->media_info.description, "STK T10KC media", 15); memcpy(&mamp->media_info.density_name, "T10000C", 7); memcpy(&mamp->AssigningOrganization_1, "STK", 3); put_unaligned_be32(233000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "DDS1", 4))) { mamp->MediumDensityCode = medium_density_code_DDS1; mamp->MediaType = Media_DDS1; memcpy(&mamp->media_info.description, "4MM DDS-1 media", 15); memcpy(&mamp->media_info.density_name, "DDS1", 4); memcpy(&mamp->AssigningOrganization_1, "HP", 2); put_unaligned_be32(233000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "DDS2", 4))) { mamp->MediumDensityCode = medium_density_code_DDS2; mamp->MediaType = Media_DDS2; memcpy(&mamp->media_info.description, "4MM DDS-2 media", 15); memcpy(&mamp->media_info.density_name, "DDS2", 4); memcpy(&mamp->AssigningOrganization_1, "HP", 2); put_unaligned_be32(233000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "DDS3", 4))) { mamp->MediumDensityCode = medium_density_code_DDS3; mamp->MediaType = Media_DDS3; memcpy(&mamp->media_info.description, "4MM DDS-3 media", 15); memcpy(&mamp->media_info.density_name, "DDS3", 4); memcpy(&mamp->AssigningOrganization_1, "HP", 2); put_unaligned_be32(233000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "DDS4", 4))) { mamp->MediumDensityCode = medium_density_code_DDS4; mamp->MediaType = Media_DDS4; memcpy(&mamp->media_info.description, "4MM DDS-4 media", 15); memcpy(&mamp->media_info.density_name, "DDS4", 4); memcpy(&mamp->AssigningOrganization_1, "HP", 2); put_unaligned_be32(233000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "J1A", 3))) { mamp->MediumDensityCode = medium_density_code_j1a; mamp->MediaType = Media_3592_JA; memcpy(&mamp->media_info.description, "3592 J1A media", 14); memcpy(&mamp->media_info.density_name, "3592J1A", 7); memcpy(&mamp->AssigningOrganization_1, "IBM", 3); put_unaligned_be32(233000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "E05", 3))) { mamp->MediumDensityCode = medium_density_code_e05; mamp->MediaType = Media_3592_JB; memcpy(&mamp->media_info.description, "3592 E05 media", 14); memcpy(&mamp->media_info.density_name, "3592E05", 7); memcpy(&mamp->AssigningOrganization_1, "IBM", 3); put_unaligned_be32(233000, &mamp->media_info.bits_per_mm); } else if (!(strncmp(density, "E06", 3))) { mamp->MediumDensityCode = medium_density_code_e06; mamp->MediaType = Media_3592_JX; memcpy(&mamp->media_info.description, "3592 E06 media", 14); memcpy(&mamp->media_info.density_name, "3592E06", 7); memcpy(&mamp->AssigningOrganization_1, "IBM", 3); } else if (!(strncmp(density, "E07", 3))) { mamp->MediumDensityCode = medium_density_code_e07; mamp->MediaType = Media_3592_JK; memcpy(&mamp->media_info.description, "3592 E07 media", 14); memcpy(&mamp->media_info.density_name, "3592E07", 7); memcpy(&mamp->AssigningOrganization_1, "IBM", 3); } else printf("'%s' is an invalid density\n", density); if (mamp->MediaType == Media_undefined) { printf("Warning: mamp->MediaType is still undefined\n"); return 1; } mamp->FormattedDensityCode = mamp->MediumDensityCode; return 0; } void ymd(int *year, int *month, int *day, int *hh, int *min, int *sec) { sscanf(__TIME__, "%d:%d:%d", hh, min, sec); if (sscanf(__DATE__, "Jan %d %d", day, year) == 2) *month = 1; if (sscanf(__DATE__, "Feb %d %d", day, year) == 2) *month = 2; if (sscanf(__DATE__, "Mar %d %d", day, year) == 2) *month = 3; if (sscanf(__DATE__, "Apr %d %d", day, year) == 2) *month = 4; if (sscanf(__DATE__, "May %d %d", day, year) == 2) *month = 5; if (sscanf(__DATE__, "Jun %d %d", day, year) == 2) *month = 6; if (sscanf(__DATE__, "Jul %d %d", day, year) == 2) *month = 7; if (sscanf(__DATE__, "Aug %d %d", day, year) == 2) *month = 8; if (sscanf(__DATE__, "Sep %d %d", day, year) == 2) *month = 9; if (sscanf(__DATE__, "Oct %d %d", day, year) == 2) *month = 10; if (sscanf(__DATE__, "Nov %d %d", day, year) == 2) *month = 11; if (sscanf(__DATE__, "Dec %d %d", day, year) == 2) *month = 12; } void opcode_6_params(struct scsi_cmd *cmd, int *num, int *sz) { uint8_t *cdb = cmd->scb; if (cdb[1] & FIXED_BLOCK) { /* If Fixed block writes */ *num = get_unaligned_be24(&cdb[2]); *sz = get_unaligned_be24(&modeBlockDescriptor[5]); } else { /* else - Variable Block writes */ *num = 1; *sz = get_unaligned_be24(&cdb[2]); } } char *slot_type_str(int type) { return slot_type_string[type]; } void init_smc_log_pages(struct lu_phy_attr *lu) { add_log_temperature_page(lu); add_log_tape_alert(lu); } void init_smc_mode_pages(struct lu_phy_attr *lu) { add_mode_disconnect_reconnect(lu); add_mode_control_extension(lu); add_mode_power_condition(lu); add_mode_information_exception(lu); add_mode_element_address_assignment(lu); add_mode_transport_geometry(lu); add_mode_device_capabilities(lu); } void bubbleSort(int *array, int size) { int swapped; int i; int j; for (i = 1; i < size; i++) { swapped = 0; for (j = 0; j < size - i; j++) { if (array[j] > array[j + 1]) { int temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; swapped = 1; } } if (!swapped) break; /* if it is sorted then stop */ } } void sort_library_slot_type(struct lu_phy_attr *lu, struct smc_type_slot *type) { int i; struct smc_priv *smc_p = lu->lu_private; int arr[4]; arr[0] = smc_p->pm->start_drive; arr[1] = smc_p->pm->start_picker; arr[2] = smc_p->pm->start_map; arr[3] = smc_p->pm->start_storage; bubbleSort(arr, 4); for (i = 0; i < 4; i++) { if (smc_p->pm->start_drive == arr[i]) { type[i].type = DATA_TRANSFER; type[i].start = smc_p->pm->start_drive; } if (smc_p->pm->start_picker == arr[i]) { type[i].type = MEDIUM_TRANSPORT; type[i].start = smc_p->pm->start_picker; } if (smc_p->pm->start_map == arr[i]) { type[i].type = MAP_ELEMENT; type[i].start = smc_p->pm->start_map; } if (smc_p->pm->start_storage == arr[i]) { type[i].type = STORAGE_ELEMENT; type[i].start = smc_p->pm->start_storage; } } } /* Set VPD data with device serial number */ void update_vpd_80(struct lu_phy_attr *lu, void *p) { struct vpd *vpd_pg = lu->lu_vpd[PCODE_OFFSET(0x80)]; assert(vpd_pg); /* space should have been pre-allocated */ memcpy(vpd_pg->data, p, strlen((const char *)p)); } void update_vpd_83(struct lu_phy_attr *lu, void *p) { struct vpd *vpd_pg = lu->lu_vpd[PCODE_OFFSET(0x83)]; uint8_t *d; char *ptr; int num; int len, j; assert(vpd_pg); /* space should have been pre-allocated */ d = vpd_pg->data; d[0] = 2; d[1] = 1; d[2] = 0; num = VENDOR_ID_LEN + PRODUCT_ID_LEN + 10; d[3] = num; memcpy(&d[4], &lu->vendor_id, VENDOR_ID_LEN); memcpy(&d[12], &lu->product_id, PRODUCT_ID_LEN); memcpy(&d[28], &lu->lu_serial_no, 10); len = (int)strlen(lu->lu_serial_no); ptr = &lu->lu_serial_no[len]; num += 4; /* NAA IEEE registered identifier (faked) */ d[num] = 0x1; /* Binary */ d[num + 1] = 0x3; d[num + 2] = 0x0; d[num + 3] = 0x8; d[num + 4] = 0x51; d[num + 5] = 0x23; d[num + 6] = 0x45; d[num + 7] = 0x60; d[num + 8] = 0x3; d[num + 9] = 0x3; d[num + 10] = 0x3; d[num + 11] = 0x3; if (lu->naa) { /* If defined in config file */ sscanf((const char *)lu->naa, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &d[num + 4], &d[num + 5], &d[num + 6], &d[num + 7], &d[num + 8], &d[num + 9], &d[num + 10], &d[num + 11]); } else { /* Else munge the serial number */ ptr--; for (j = 11; j > 3; ptr--, j--) d[num + j] = *ptr; } /* Bug reported by Stefan Hauser. * [num +4] is always 0x5x */ d[num + 4] &= 0x0f; d[num + 4] |= 0x50; } ================================================ FILE: vagrant/README.MD ================================================ # Testing with Vagrant To test mhvtl on different distros, we use vagrant to spin up virtual boxes with different distros and install mhvtl from source on each box. ## Prerequisites To be able to test with Vagrant, you need: - Vagrant (tested with 2.2.4) - Virtualbox (tested with 5.2.26) ## Test in different Operating Systems Run any of the following commands to test the code on different GNU/Linux distributions. ``` Vagrant up ubuntu ``` or ``` Vagrant up centos ``` or ``` Vagrant up opensuse ``` **_NOTE:_** OpenSUSE in currently not working The test will build and install mhvtl from source in the virtual machine. If the output looks something like this, then the installation went fine: ``` ... ubuntu: Show your tape libraries now! ubuntu: [2:0:0:0] disk VBOX HARDDISK 1.0 /dev/sda /dev/sg0 ubuntu: [2:0:1:0] disk VBOX HARDDISK 1.0 /dev/sdb /dev/sg1 ubuntu: [3:0:0:0] mediumx STK L700 0106 - /dev/sg7 ubuntu: [3:0:1:0] tape IBM ULT3580-TD5 0106 - /dev/sg6 ubuntu: [3:0:2:0] tape IBM ULT3580-TD5 0106 - /dev/sg11 ubuntu: [3:0:3:0] tape IBM ULT3580-TD4 0106 - /dev/sg4 ubuntu: [3:0:4:0] tape IBM ULT3580-TD4 0106 - /dev/sg8 ubuntu: [3:0:8:0] mediumx STK L80 0106 - /dev/sg3 ubuntu: [3:0:9:0] tape STK T10000B 0106 - /dev/sg5 ubuntu: [3:0:10:0] tape STK T10000B 0106 - /dev/sg2 ubuntu: [3:0:11:0] tape STK T10000B 0106 - /dev/sg9 ubuntu: [3:0:12:0] tape STK T10000B 0106 - /dev/sg10 ubuntu: Found some virtual tapes, success! ``` ### Cleanup When you are done, and you want to remove the box, run the following command: ``` Vagrant destroy -f ``` ================================================ FILE: vagrant/Vagrantfile ================================================ # -*- mode: ruby -*- # vi: set ft=ruby : def gui_enabled? !ENV.fetch('GUI', '').empty? end Vagrant.configure("2") do |config| config.vm.box_check_update = false config.vm.define "ubuntu" do |instance| instance.vm.box = "generic/ubuntu1804" instance.vm.network "private_network", ip: "192.168.56.20" end config.vm.define "rocky" do |instance| instance.vm.hostname = "rocky" instance.vm.box = "rockylinux/8" instance.vm.network "private_network", ip: "192.168.56.21" end config.vm.define "centos8" do |instance| instance.vm.box = "centos/8" instance.vm.network "private_network", ip: "192.168.56.22" end config.vm.define "centos" do |instance| instance.vm.box = "geerlingguy/centos7" instance.vm.network "private_network", ip: "192.168.56.23" end config.vm.define "opensuse" do |instance| instance.vm.box = "opensuse/Leap-15.2.x86_64" instance.vm.network "private_network", ip: "192.168.56.24" end config.vm.define "alma" do |instance| instance.vm.hostname = "alma" instance.vm.box = "almalinux/9" instance.vm.network "private_network", ip: "192.168.56.25" config.vbguest.auto_update = false end config.vm.provider "virtualbox" do |vb| vb.memory = "2048" vb.gui = gui_enabled? end config.vm.synced_folder "../", "/vagrant_data" config.vm.provision "shell", inline: <<-SHELL cd /vagrant_data/vagrant sudo ./install.sh SHELL end ================================================ FILE: vagrant/install.sh ================================================ #!/bin/bash # This script assumes it is located in a subdirectory from 'mhvtl' source root echo "Script Begin" get_os_name() { if [ "$(hostnamectl | grep -i ubuntu | wc -l)" != "0" ]; then OS_NAME='ubuntu' elif [ "$(hostnamectl | grep -i sles | wc -l)" != "0" ]; then OS_NAME='sles' elif [ "$(hostnamectl | grep -i opensuse | wc -l)" != "0" ]; then OS_NAME='opensuse' elif [ "$(hostnamectl | grep -i centos | wc -l)" != "0" ]; then OS_NAME='centos' elif [ "$(hostnamectl | grep -i rocky | wc -l)" != "0" ]; then OS_NAME='rockylinux' elif [ "$(hostnamectl | grep -i alma | wc -l)" != "0" ]; then OS_NAME='almalinux' else echo 'This os is not supported!' exit 1 fi echo "OS_NAME is $OS_NAME" } # check our script has been started with root auth if [ "$(id -u)" != "0" ]; then echo "This script must be run with root privileges. Please run again as either root or using sudo." tput sgr0 exit 1 fi get_os_name # Lets break the script if there are any errors set -e install_ubuntu_pre_req() { echo "Ubuntu" sudo apt-get update && sudo apt-get install ntp sysstat mtx mt-st sg3-utils zlib1g-dev git lsscsi build-essential gawk alien fakeroot linux-headers-$(uname -r) linux-modules-extra-$(uname -r) targetcli-fb -y } install_alma_pre_req() { echo "alma" # sudo dnf install -y deltarpm sudo dnf update -y && sudo yum install -y git mc gcc gcc-c++ make kernel-devel-$(uname -r) zlib-devel sg3_utils lsscsi mt-st mtx targetcli vim chrony policycoreutils-python-utils policycoreutils sudo dnf upgrade -y # Rebuild VBox guest tools for any new kernel(s) installed # /sbin/rcvboxadd quicksetup all } install_rocky_pre_req() { echo "Rocky" # sudo yum install -y deltarpm sudo yum update -y && sudo yum install -y git mc gcc gcc-c++ make kernel-devel-$(uname -r) zlib-devel sg3_utils lsscsi mt-st mtx targetcli vim policycoreutils-python-utils policycoreutils sudo yum upgrade -y # Rebuild VBox guest tools for any new kernel(s) installed /sbin/rcvboxadd quicksetup all } install_centos_pre_req() { echo "CentOS" sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* sudo yum install -y deltarpm sudo yum update -y && sudo yum install -y git mc ntp gcc gcc-c++ make kernel-devel-$(uname -r) zlib-devel sg3_utils lsscsi mt-st mtx perl-Config-General targetcli policycoreutils-python-utils policycoreutils sudo yum upgrade -y # Rebuild VBox guest tools for any new kernel(s) installed /sbin/rcvboxadd quicksetup all } install_sles_pre_req() { echo "SLES/OpenSuse" # Workaround so that we install the same kernel-devel and kernel-syms version as the running kernel. UNAME_R=$(echo $(uname -r) | cut -d "-" -f-2) PATCHED_KERNEL_VERSION=$(sudo zypper se -s kernel-devel | grep ${UNAME_R} | cut -d "|" -f4 | tr -d " ") sudo zypper install -y --oldpackage kernel-devel-${PATCHED_KERNEL_VERSION} sudo zypper install -y --oldpackage kernel-syms-${PATCHED_KERNEL_VERSION} sudo zypper install -y git mc ntp gcc gcc-c++ make zlib-devel sg3_utils lsscsi mtx perl-Config-General targetcli-fb python-semanage } install_pre_req() { if [ ${OS_NAME} == 'ubuntu' ]; then SYSTEMD_GENERATOR_DIR="/lib/systemd/system-generators" install_ubuntu_pre_req elif [ ${OS_NAME} == 'rockylinux' ]; then SYSTEMD_GENERATOR_DIR="/lib/systemd/system-generators" install_rocky_pre_req elif [ ${OS_NAME} == 'centos' ]; then SYSTEMD_GENERATOR_DIR="/lib/systemd/system-generators" install_centos_pre_req elif [ ${OS_NAME} == 'almalinux' ]; then SYSTEMD_GENERATOR_DIR="/usr/lib/systemd/system-generators" install_alma_pre_req elif [ ${OS_NAME} == 'sles' ] || [ ${OS_NAME} == 'opensuse' ]; then SYSTEMD_GENERATOR_DIR="/usr/lib/systemd/system-generators" install_sles_pre_req else echo "Unable to handle install_pre_req for ${OS_NAME}" fi } install_mhvtl_kernel_module() { make distclean if [ ${OS_NAME} == 'ubuntu' ]; then make sudo make install elif [ ${OS_NAME} == 'centos' ] || [ ${OS_NAME} == 'rockylinux' ] || [ ${OS_NAME} == 'almalinux' ]; then for a in `rpm -qa | awk '/kernel-devel/ {print $1}' | sed -e "s/kernel-devel-//g"` do echo "Building mhVTL kernel module for ${a}" make V=${a} sudo make install V=${a} make distclean done elif [ ${OS_NAME} == 'sles' ] || [ ${OS_NAME} == 'opensuse' ]; then make sudo make install fi } setup_time() { echo "Setting timezone to $1" timedatectl set-timezone $1 if [ ${OS_NAME} == 'almalinux' ]; then echo "Alma linux - ntp (timesyncd) enabled by default" systemctl restart chronyd return fi if [ ${OS_NAME} == 'rockylinux' ]; then echo "Rocky linux - ntp (timesyncd) enabled by default" systemctl restart chronyd return fi # And enable ntp. if [ $(egrep "^server|^pool" /etc/ntp.conf | wc -l) -eq 0 ]; then rcntpd addserver 0.au.pool.ntp.org rcntpd addserver 1.au.pool.ntp.org fi if [ ${OS_NAME} == 'ubuntu' ]; then NTP="ntp" elif [ ${OS_NAME} == 'almalinux' ]; then NTP="chrony" elif [ ${OS_NAME} == 'rockylinux' ]; then NTP="chrony" elif [ ${OS_NAME} == 'centos' ]; then NTP="ntpd" elif [ ${OS_NAME} == 'sles' ] || [ ${OS_NAME} == 'opensuse' ]; then NTP="ntpd" else echo 'Could not determine os type' return fi systemctl enable ${NTP} systemctl start ${NTP} # Rocky Linux - ntp (chrony) is already running, so restart systemctl restart ${NTP} } # Install required packages install_pre_req # Use 'timedatectl list-timezones' to view valid timezone strings setup_time "Australia/Sydney" # Change to mhVTL folder cd ../ # Clean up any previous build make distclean # Build kernel module for all versions which have the development package installed cd kernel/ install_mhvtl_kernel_module # Now make user-space binaries and install cd .. make echo "placing SYSTEMD_GENERATOR_DIR : ${SYSTEMD_GENERATOR_DIR}" sudo make install SYSTEMD_GENERATOR_DIR=${SYSTEMD_GENERATOR_DIR} # Load it sudo depmod -a sudo systemctl daemon-reload sudo systemctl enable mhvtl.target sudo systemctl start mhvtl.target sleep 3 echo "Show your tape libraries now!" hba=`lsscsi -H | awk '/mhvtl/ {print $1}' | sed -e 's/\[//g' -e 's/\]//g'` lsscsi ${hba} -g echo "" if [ "$(lsscsi -g ${hba} | wc -l)" -gt 2 ]; then echo "Found some virtual tapes, success!" else echo "Could not find the virtual tapes, the installation failed!" exit 1 fi exit 0 ================================================ FILE: webgui/index.php ================================================ mhvtl configuration"; echo "
"; echo "
"; phpinfo(); ?>