(feature): init

This commit is contained in:
Maksym Sadovnychyy 2024-10-31 19:42:40 +01:00
commit 4e076e90cf
21 changed files with 8445 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

262
.gitignore vendored Normal file
View File

@ -0,0 +1,262 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
.directory
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

47
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,47 @@
# Contributing to MaksIT.LTO.Backup
Thank you for your interest in contributing to the MaksIT.LTO.Backup project! Whether you're submitting a bug report, suggesting a new feature, or improving the code, your efforts are greatly appreciated. Below are some guidelines to help make the process smooth and effective.
## Getting Started
1. **Fork the Repository**: Start by forking this repository to your GitHub account.
2. **Clone Your Fork**: Clone the fork to your local machine to begin making changes.
```bash
git clone https://github.com/your-username/MaksIT.LTO.Backup.git
```
3. **Install Dependencies**: Ensure .NET8 or higher is installed on your system.
4. **Prepare Configurations**: Check the `README.md` for details on setting up `configuration.json` in the application directory.
5. **Run and Test**: Before submitting changes, ensure the application builds and functions correctly. Run all relevant tests and add new ones if needed.
## Reporting Issues
When reporting an issue, please include:
- Detailed steps to reproduce the issue.
- Your operating system and .NET version.
- Any relevant logs or error messages.
If the issue is specific to a particular LTO version that you cannot test, please consider either:
- **Providing Funding**: Donations to cover hardware expenses can help expand compatibility. Contributions can be made via:
<a href="https://www.buymeacoffee.com/maksitcom" target="_blank">
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px; width: 217px;">
</a>
- **Sponsoring Hardware**: If you're facing compatibility issues with a specific LTO generation, you can sponsor by directly purchasing the relevant LTO drive and tapes for testing. Contact me for shipping details if you're interested in sponsoring hardware.
## Code Contribution
### Guidelines
1. **Pull Request**: Open a pull request (PR) with a clear description of your changes. If the PR is linked to an issue, reference it in the description.
2. **Code Style**: Follow existing code conventions and formatting. Keep your changes focused and avoid unnecessary code refactoring.
3. **Documentation**: Ensure any new methods, classes, or configurations are fully documented.
4. **Testing**: Test your changes thoroughly and add relevant tests where possible.
## Contact
If you have any questions or need further assistance, feel free to reach out:
- **Email**: [maksym.sadovnychyy@gmail.com](mailto:maksym.sadovnychyy@gmail.com)
Thank you for considering contributing, and feel free to reach out if you have questions! Your support helps make MaksIT.LTO.Backup better for everyone.

325
LICENSE.md Normal file
View File

@ -0,0 +1,325 @@
The GNU General Public License (GPL-2.0)
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
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.
MaksIT.LTO.Backup - Simple LTO backup cli utility for Windows
Copyright (C) 2024 Maksym Sadovnychyy (MAKS-IT)
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU 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.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when
it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author Gnomovision
comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is
free software, and you are welcome to redistribute it under certain
conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may be
called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
signature of Ty Coon, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

95
README.md Normal file
View File

@ -0,0 +1,95 @@
# MaksIT.LTO.Backup
A C# application designed to facilitate backup and restore operations to an LTO tape drive. This application enables seamless management of backups by handling file organization, descriptor creation, and efficient tape handling processes for loading, writing, and restoring data.
## Features
- **Load and Eject Tape**: Safely loads and unloads the tape using `TapeDeviceHandler`.
- **Backup Operation**: Allows users to create a descriptor file for organizing file metadata and backs up files to the LTO tape in structured blocks.
- **Restore Operation**: Reads data from the tape and restores files to a specified directory, reconstructing original file structure.
- **Customizable Block Sizes**: Supports multiple LTO generations, allowing customization of block sizes based on the tape generation (e.g., LTO-6).
- **File Descriptor Management**: Metadata is organized for each file, including file paths, sizes, creation, and modification times.
- **Zero-Filled End Blocks**: Marks the end of a backup with zero-filled blocks to assist with reading and integrity checks.
## Requirements
- .NET8 or higher
- JSON configuration files: `configuration.json` and `descriptor.json` (auto-generated)
- LTO Tape Drive and compatible drivers
## Setup
1. Clone the repository:
```bash
git clone https://github.com/your-username/MaksIT.LTO.Backup.git
```
2. Ensure `.NET Core` is installed on your system.
3. Prepare a `configuration.json` file in the application directory with the following structure:
```json
{
"TapePath": "YourTapePath",
"Backups": [
{
"Name": "BackupName",
"Barcode": "123456",
"Source": "path/to/source",
"Destination": "path/to/destination",
"LTOGen": "LTO6"
}
]
}
```
## Usage
### Running the Application
Execute the application by navigating to the project directory and running:
```bash
dotnet run
```
### Application Menu
Upon running, the following options will be presented:
1. **Load Tape**: Loads the tape into the drive.
2. **Backup**: Prompts the user to select a backup task from the configured list and initiates the backup process.
3. **Restore**: Restores a previously backed-up directory from the tape.
4. **Eject Tape**: Ejects the tape from the drive safely.
5. **Exit**: Exits the application.
### Code Overview
- **Application Class**: Main class handling backup and restore functionalities. Provides methods like `LoadTape`, `EjectTape`, `Backup`, `Restore`, `CreateDescriptor`, `WriteFilesToTape`, and `FindDescriptor`.
- **TapeDeviceHandler**: Controls tape device operations such as setting positions, writing data, and reading data.
- **BackupDescriptor**: Organizes metadata for backed-up files. Used to log details about each file block on tape.
### Sample Configuration
Below is an example configuration setup for an LTO-6 tape generation backup operation:
```json
{
"TapePath": "\\\\.\\Tape0",
"Backups": [
{
"Name": "Monthly Backup",
"Barcode": "MB12345",
"Source": "/path/to/source",
"Destination": "/path/to/restore",
"LTOGen": "LTO6"
}
]
}
```
### Error Handling
Errors during backup or restore are caught and logged to the console. Ensure that your `TapeDeviceHandler` is correctly configured and that the tape drive is accessible.
## License
This project is licensed under the terms of the GPLv2. See the [LICENSE](./LICENSE) file for full details.
© Maksym Sadovnychyy (MAKS-IT) 2024

4949
docs/ntddstor.h Normal file

File diff suppressed because it is too large Load Diff

368
docs/ntddtape.h Normal file
View File

@ -0,0 +1,368 @@
/*++ BUILD Version: 0001 // Increment this if a change has global effects
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
ntddtape.h
Abstract:
This is the include file that defines all constants and types for
accessing the Tape device.
--*/
#ifndef _NTDDTAPE_
#define _NTDDTAPE_
#if _MSC_VER > 1000
#pragma once
#endif
#include <winapifamily.h>
#if _MSC_VER >= 1200
#pragma warning(push)
#pragma warning(disable:4820) /* padding added after data member */
#endif
#pragma region Desktop Family or OneCore Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
#ifdef __cplusplus
extern "C" {
#endif
//
// Device Name - this string is the name of the device. It is the name
// that should be passed to NtOpenFile when accessing the device.
//
// Note: For devices that support multiple units, it should be suffixed
// with the Ascii representation of the unit number.
//
#define DD_TAPE_DEVICE_NAME "\\Device\\Tape"
//
// NtDeviceIoControlFile IoControlCode values for this device.
//
// Warning: Remember that the low two bits of the code specify how the
// buffers are passed to the driver!
//
#define IOCTL_TAPE_BASE FILE_DEVICE_TAPE
#define IOCTL_TAPE_ERASE CTL_CODE(IOCTL_TAPE_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_TAPE_PREPARE CTL_CODE(IOCTL_TAPE_BASE, 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_WRITE_MARKS CTL_CODE(IOCTL_TAPE_BASE, 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_TAPE_GET_POSITION CTL_CODE(IOCTL_TAPE_BASE, 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_SET_POSITION CTL_CODE(IOCTL_TAPE_BASE, 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_GET_DRIVE_PARAMS CTL_CODE(IOCTL_TAPE_BASE, 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_SET_DRIVE_PARAMS CTL_CODE(IOCTL_TAPE_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_TAPE_GET_MEDIA_PARAMS CTL_CODE(IOCTL_TAPE_BASE, 0x0007, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_SET_MEDIA_PARAMS CTL_CODE(IOCTL_TAPE_BASE, 0x0008, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_GET_STATUS CTL_CODE(IOCTL_TAPE_BASE, 0x0009, METHOD_BUFFERED, FILE_READ_ACCESS )
#define IOCTL_TAPE_CREATE_PARTITION CTL_CODE(IOCTL_TAPE_BASE, 0x000a, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
//
// The following device control codes are common for all class drivers. The
// functions codes defined here must match all of the other class drivers.
//
// Warning: these codes will be replaced in the future with the IOCTL_STORAGE
// codes included below
//
#define IOCTL_TAPE_MEDIA_REMOVAL CTL_CODE(IOCTL_TAPE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_EJECT_MEDIA CTL_CODE(IOCTL_TAPE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_LOAD_MEDIA CTL_CODE(IOCTL_TAPE_BASE, 0x0203, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_RESERVE CTL_CODE(IOCTL_TAPE_BASE, 0x0204, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_RELEASE CTL_CODE(IOCTL_TAPE_BASE, 0x0205, METHOD_BUFFERED, FILE_READ_ACCESS)
#if (NTDDI_VERSION < NTDDI_WS03)
#define IOCTL_TAPE_CHECK_VERIFY CTL_CODE(IOCTL_TAPE_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_TAPE_FIND_NEW_DEVICES CTL_CODE(IOCTL_DISK_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS)
#endif
//
// The following file contains the IOCTL_STORAGE class ioctls
//
#include <ntddstor.h>
// begin_winnt begin_ntminitape
#ifndef _NTDDTAPE_WINNT_
#define _NTDDTAPE_WINNT_
//
// IOCTL_TAPE_ERASE definitions
//
#define TAPE_ERASE_SHORT 0L
#define TAPE_ERASE_LONG 1L
typedef struct _TAPE_ERASE {
ULONG Type;
BOOLEAN Immediate;
} TAPE_ERASE, *PTAPE_ERASE;
//
// IOCTL_TAPE_PREPARE definitions
//
#define TAPE_LOAD 0L
#define TAPE_UNLOAD 1L
#define TAPE_TENSION 2L
#define TAPE_LOCK 3L
#define TAPE_UNLOCK 4L
#define TAPE_FORMAT 5L
typedef struct _TAPE_PREPARE {
ULONG Operation;
BOOLEAN Immediate;
} TAPE_PREPARE, *PTAPE_PREPARE;
//
// IOCTL_TAPE_WRITE_MARKS definitions
//
#define TAPE_SETMARKS 0L
#define TAPE_FILEMARKS 1L
#define TAPE_SHORT_FILEMARKS 2L
#define TAPE_LONG_FILEMARKS 3L
typedef struct _TAPE_WRITE_MARKS {
ULONG Type;
ULONG Count;
BOOLEAN Immediate;
} TAPE_WRITE_MARKS, *PTAPE_WRITE_MARKS;
//
// IOCTL_TAPE_GET_POSITION definitions
//
#define TAPE_ABSOLUTE_POSITION 0L
#define TAPE_LOGICAL_POSITION 1L
#define TAPE_PSEUDO_LOGICAL_POSITION 2L
typedef struct _TAPE_GET_POSITION {
ULONG Type;
ULONG Partition;
LARGE_INTEGER Offset;
} TAPE_GET_POSITION, *PTAPE_GET_POSITION;
//
// IOCTL_TAPE_SET_POSITION definitions
//
#define TAPE_REWIND 0L
#define TAPE_ABSOLUTE_BLOCK 1L
#define TAPE_LOGICAL_BLOCK 2L
#define TAPE_PSEUDO_LOGICAL_BLOCK 3L
#define TAPE_SPACE_END_OF_DATA 4L
#define TAPE_SPACE_RELATIVE_BLOCKS 5L
#define TAPE_SPACE_FILEMARKS 6L
#define TAPE_SPACE_SEQUENTIAL_FMKS 7L
#define TAPE_SPACE_SETMARKS 8L
#define TAPE_SPACE_SEQUENTIAL_SMKS 9L
typedef struct _TAPE_SET_POSITION {
ULONG Method;
ULONG Partition;
LARGE_INTEGER Offset;
BOOLEAN Immediate;
} TAPE_SET_POSITION, *PTAPE_SET_POSITION;
//
// IOCTL_TAPE_GET_DRIVE_PARAMS definitions
//
//
// Definitions for FeaturesLow parameter
//
#define TAPE_DRIVE_FIXED 0x00000001
#define TAPE_DRIVE_SELECT 0x00000002
#define TAPE_DRIVE_INITIATOR 0x00000004
#define TAPE_DRIVE_ERASE_SHORT 0x00000010
#define TAPE_DRIVE_ERASE_LONG 0x00000020
#define TAPE_DRIVE_ERASE_BOP_ONLY 0x00000040
#define TAPE_DRIVE_ERASE_IMMEDIATE 0x00000080
#define TAPE_DRIVE_TAPE_CAPACITY 0x00000100
#define TAPE_DRIVE_TAPE_REMAINING 0x00000200
#define TAPE_DRIVE_FIXED_BLOCK 0x00000400
#define TAPE_DRIVE_VARIABLE_BLOCK 0x00000800
#define TAPE_DRIVE_WRITE_PROTECT 0x00001000
#define TAPE_DRIVE_EOT_WZ_SIZE 0x00002000
#define TAPE_DRIVE_ECC 0x00010000
#define TAPE_DRIVE_COMPRESSION 0x00020000
#define TAPE_DRIVE_PADDING 0x00040000
#define TAPE_DRIVE_REPORT_SMKS 0x00080000
#define TAPE_DRIVE_GET_ABSOLUTE_BLK 0x00100000
#define TAPE_DRIVE_GET_LOGICAL_BLK 0x00200000
#define TAPE_DRIVE_SET_EOT_WZ_SIZE 0x00400000
#define TAPE_DRIVE_EJECT_MEDIA 0x01000000
#define TAPE_DRIVE_CLEAN_REQUESTS 0x02000000
#define TAPE_DRIVE_SET_CMP_BOP_ONLY 0x04000000
#define TAPE_DRIVE_RESERVED_BIT 0x80000000 //don't use this bit!
// //can't be a low features bit!
// //reserved; high features only
//
// Definitions for FeaturesHigh parameter
//
#define TAPE_DRIVE_LOAD_UNLOAD 0x80000001
#define TAPE_DRIVE_TENSION 0x80000002
#define TAPE_DRIVE_LOCK_UNLOCK 0x80000004
#define TAPE_DRIVE_REWIND_IMMEDIATE 0x80000008
#define TAPE_DRIVE_SET_BLOCK_SIZE 0x80000010
#define TAPE_DRIVE_LOAD_UNLD_IMMED 0x80000020
#define TAPE_DRIVE_TENSION_IMMED 0x80000040
#define TAPE_DRIVE_LOCK_UNLK_IMMED 0x80000080
#define TAPE_DRIVE_SET_ECC 0x80000100
#define TAPE_DRIVE_SET_COMPRESSION 0x80000200
#define TAPE_DRIVE_SET_PADDING 0x80000400
#define TAPE_DRIVE_SET_REPORT_SMKS 0x80000800
#define TAPE_DRIVE_ABSOLUTE_BLK 0x80001000
#define TAPE_DRIVE_ABS_BLK_IMMED 0x80002000
#define TAPE_DRIVE_LOGICAL_BLK 0x80004000
#define TAPE_DRIVE_LOG_BLK_IMMED 0x80008000
#define TAPE_DRIVE_END_OF_DATA 0x80010000
#define TAPE_DRIVE_RELATIVE_BLKS 0x80020000
#define TAPE_DRIVE_FILEMARKS 0x80040000
#define TAPE_DRIVE_SEQUENTIAL_FMKS 0x80080000
#define TAPE_DRIVE_SETMARKS 0x80100000
#define TAPE_DRIVE_SEQUENTIAL_SMKS 0x80200000
#define TAPE_DRIVE_REVERSE_POSITION 0x80400000
#define TAPE_DRIVE_SPACE_IMMEDIATE 0x80800000
#define TAPE_DRIVE_WRITE_SETMARKS 0x81000000
#define TAPE_DRIVE_WRITE_FILEMARKS 0x82000000
#define TAPE_DRIVE_WRITE_SHORT_FMKS 0x84000000
#define TAPE_DRIVE_WRITE_LONG_FMKS 0x88000000
#define TAPE_DRIVE_WRITE_MARK_IMMED 0x90000000
#define TAPE_DRIVE_FORMAT 0xA0000000
#define TAPE_DRIVE_FORMAT_IMMEDIATE 0xC0000000
#define TAPE_DRIVE_HIGH_FEATURES 0x80000000 //mask for high features flag
typedef struct _TAPE_GET_DRIVE_PARAMETERS {
BOOLEAN ECC;
BOOLEAN Compression;
BOOLEAN DataPadding;
BOOLEAN ReportSetmarks;
ULONG DefaultBlockSize;
ULONG MaximumBlockSize;
ULONG MinimumBlockSize;
ULONG MaximumPartitionCount;
ULONG FeaturesLow;
ULONG FeaturesHigh;
ULONG EOTWarningZoneSize;
} TAPE_GET_DRIVE_PARAMETERS, *PTAPE_GET_DRIVE_PARAMETERS;
//
// IOCTL_TAPE_SET_DRIVE_PARAMETERS definitions
//
typedef struct _TAPE_SET_DRIVE_PARAMETERS {
BOOLEAN ECC;
BOOLEAN Compression;
BOOLEAN DataPadding;
BOOLEAN ReportSetmarks;
ULONG EOTWarningZoneSize;
} TAPE_SET_DRIVE_PARAMETERS, *PTAPE_SET_DRIVE_PARAMETERS;
//
// IOCTL_TAPE_GET_MEDIA_PARAMETERS definitions
//
typedef struct _TAPE_GET_MEDIA_PARAMETERS {
LARGE_INTEGER Capacity;
LARGE_INTEGER Remaining;
ULONG BlockSize;
ULONG PartitionCount;
BOOLEAN WriteProtected;
} TAPE_GET_MEDIA_PARAMETERS, *PTAPE_GET_MEDIA_PARAMETERS;
//
// IOCTL_TAPE_SET_MEDIA_PARAMETERS definitions
//
typedef struct _TAPE_SET_MEDIA_PARAMETERS {
ULONG BlockSize;
} TAPE_SET_MEDIA_PARAMETERS, *PTAPE_SET_MEDIA_PARAMETERS;
//
// IOCTL_TAPE_CREATE_PARTITION definitions
//
#define TAPE_FIXED_PARTITIONS 0L
#define TAPE_SELECT_PARTITIONS 1L
#define TAPE_INITIATOR_PARTITIONS 2L
typedef struct _TAPE_CREATE_PARTITION {
ULONG Method;
ULONG Count;
ULONG Size;
} TAPE_CREATE_PARTITION, *PTAPE_CREATE_PARTITION;
//
// WMI Methods
//
#define TAPE_QUERY_DRIVE_PARAMETERS 0L
#define TAPE_QUERY_MEDIA_CAPACITY 1L
#define TAPE_CHECK_FOR_DRIVE_PROBLEM 2L
#define TAPE_QUERY_IO_ERROR_DATA 3L
#define TAPE_QUERY_DEVICE_ERROR_DATA 4L
typedef struct _TAPE_WMI_OPERATIONS {
ULONG Method;
ULONG DataBufferSize;
PVOID DataBuffer;
} TAPE_WMI_OPERATIONS, *PTAPE_WMI_OPERATIONS;
//
// Type of drive errors
//
typedef enum _TAPE_DRIVE_PROBLEM_TYPE {
TapeDriveProblemNone, TapeDriveReadWriteWarning,
TapeDriveReadWriteError, TapeDriveReadWarning,
TapeDriveWriteWarning, TapeDriveReadError,
TapeDriveWriteError, TapeDriveHardwareError,
TapeDriveUnsupportedMedia, TapeDriveScsiConnectionError,
TapeDriveTimetoClean, TapeDriveCleanDriveNow,
TapeDriveMediaLifeExpired, TapeDriveSnappedTape
} TAPE_DRIVE_PROBLEM_TYPE;
#endif
// end_winnt end_ntminitape
#ifdef __cplusplus
}
#endif
#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) */
#pragma endregion
#if _MSC_VER >= 1200
#pragma warning(pop)
#endif
#endif // _NTDDTAPE_

31
src/MaksIT.LTO.Backup.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MaksIT.LTO.Backup", "MaksIT.LTO.Backup\MaksIT.LTO.Backup.csproj", "{8E6B7295-1579-4FF7-914C-7CF6CD5C67C9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaksIT.LTO.Core", "MaksIT.LTO.Core\MaksIT.LTO.Core.csproj", "{EA1BAEAB-248C-47D6-85B7-C2C0CDC9FCAF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8E6B7295-1579-4FF7-914C-7CF6CD5C67C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8E6B7295-1579-4FF7-914C-7CF6CD5C67C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E6B7295-1579-4FF7-914C-7CF6CD5C67C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E6B7295-1579-4FF7-914C-7CF6CD5C67C9}.Release|Any CPU.Build.0 = Release|Any CPU
{EA1BAEAB-248C-47D6-85B7-C2C0CDC9FCAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA1BAEAB-248C-47D6-85B7-C2C0CDC9FCAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA1BAEAB-248C-47D6-85B7-C2C0CDC9FCAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA1BAEAB-248C-47D6-85B7-C2C0CDC9FCAF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D228D2F6-B38A-412B-870E-0B579D8203D6}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,432 @@
using System.Text;
using System.Text.Json;
using System.Diagnostics.CodeAnalysis;
using MaksIT.LTO.Core;
using MaksIT.LTO.Backup.Entities;
namespace MaksIT.LTO.Backup;
public class Application {
private const string _descriptoFileName = "descriptor.json";
private const string _configurationFileName = "configuration.json";
private readonly string appPath = AppDomain.CurrentDomain.BaseDirectory;
private readonly string _tapePath;
private readonly string _descriptorFilePath;
private Configuration _configuration;
public Application() {
_descriptorFilePath = Path.Combine(appPath, _descriptoFileName);
LoadConfiguration();
_tapePath = _configuration.TapePath;
}
[MemberNotNull(nameof(_configuration))]
public void LoadConfiguration() {
var configFilePath = Path.Combine(appPath, _configurationFileName);
var configuration = JsonSerializer.Deserialize<Configuration>(File.ReadAllText(configFilePath));
if (configuration == null)
throw new InvalidOperationException("Failed to deserialize configuration.");
_configuration = configuration;
}
public void LoadTape() {
using var handler = new TapeDeviceHandler(_tapePath);
LoadTape(handler);
}
public void LoadTape(TapeDeviceHandler handler) {
handler.Prepare(TapeDeviceHandler.TAPE_LOAD);
Thread.Sleep(2000);
Console.WriteLine("Tape loaded.");
}
public void EjectTape() {
using var handler = new TapeDeviceHandler(_tapePath);
EjectTape(handler);
}
public void EjectTape(TapeDeviceHandler handler) {
handler.Prepare(TapeDeviceHandler.TAPE_UNLOAD);
Thread.Sleep(2000);
Console.WriteLine("Tape ejected.");
}
public void CreateDescriptor(string directoryPath, string descriptorFilePath, uint blockSize) {
var files = Directory.GetFiles(directoryPath, "*.*", SearchOption.AllDirectories);
// Define list to hold file descriptors
var descriptor = new List<FileDescriptor>();
uint currentTapeBlock = 0;
foreach (var filePath in files) {
var fileInfo = new FileInfo(filePath);
var relativePath = Path.GetRelativePath(directoryPath, filePath);
var numberOfBlocks = (uint)((fileInfo.Length + blockSize - 1) / blockSize);
// Optional: Calculate a simple hash for file integrity (e.g., MD5)
using var md5 = System.Security.Cryptography.MD5.Create();
using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var bufferedStream = new BufferedStream(fileStream, (int)blockSize);
byte[] buffer = new byte[blockSize];
int bytesRead;
while ((bytesRead = bufferedStream.Read(buffer, 0, buffer.Length)) > 0) {
md5.TransformBlock(buffer, 0, bytesRead, null, 0);
}
md5.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
string fileHash = BitConverter.ToString(md5.Hash).Replace("-", "").ToLower();
descriptor.Add(new FileDescriptor {
StartBlock = currentTapeBlock, // Position of the file on the tape
NumberOfBlocks = numberOfBlocks, // Number of blocks used by the file
FilePath = relativePath,
FileSize = fileInfo.Length,
CreationTime = fileInfo.CreationTime,
LastModifiedTime = fileInfo.LastWriteTime,
FileHash = fileHash
});
currentTapeBlock += numberOfBlocks;
}
// Convert descriptor list to JSON and include BlockSize
string descriptorJson = JsonSerializer.Serialize(new BackupDescriptor {
BlockSize = blockSize,
Files = descriptor
});
File.WriteAllText(descriptorFilePath, descriptorJson);
}
private void ZeroFillBlocks(TapeDeviceHandler handler, int blocks, uint blockSize) {
Console.WriteLine($"Writing {blocks} zero-filled blocks to tape.");
Console.WriteLine($"Block Size: {blockSize}.");
for (int i = 0; i < blocks; i++) {
handler.WriteData(new byte[blockSize]);
Thread.Sleep(100);
}
}
public void WriteFilesToTape(string directoryPath, string descriptorFilePath, uint blockSize) {
Console.WriteLine($"Writing files to tape from: {directoryPath}.");
Console.WriteLine($"Block Size: {blockSize}.");
using var handler = new TapeDeviceHandler(_tapePath);
LoadTape(handler);
handler.SetMediaParams(blockSize);
handler.SetPosition(TapeDeviceHandler.TAPE_REWIND);
Thread.Sleep(2000);
handler.Prepare(TapeDeviceHandler.TAPE_TENSION);
Thread.Sleep(2000);
handler.Prepare(TapeDeviceHandler.TAPE_LOCK);
Thread.Sleep(2000);
handler.WaitForTapeReady();
// Read descriptor from file system
string descriptorJson = File.ReadAllText(descriptorFilePath);
var descriptor = JsonSerializer.Deserialize<BackupDescriptor>(descriptorJson);
if (descriptor == null) {
throw new InvalidOperationException("Failed to deserialize descriptor.");
}
var currentTapeBlock = (descriptorJson.Length + blockSize - 1) / blockSize;
foreach (var file in descriptor.Files) {
var filePath = Path.Combine(directoryPath, file.FilePath);
using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var bufferedStream = new BufferedStream(fileStream, (int)blockSize);
byte[] buffer = new byte[blockSize];
int bytesRead;
for (var i = 0; i < file.NumberOfBlocks; i++) {
bytesRead = bufferedStream.Read(buffer, 0, buffer.Length);
if (bytesRead < buffer.Length) {
// Zero-fill the remaining part of the buffer if the last block is smaller than blockSize
Array.Clear(buffer, bytesRead, buffer.Length - bytesRead);
}
handler.WriteData(buffer);
currentTapeBlock++;
Thread.Sleep(100); // Small delay between blocks
}
}
// write mark to indicate end of files
handler.WriteMarks(TapeDeviceHandler.TAPE_FILEMARKS, 1);
// write descriptor to tape
var descriptorData = Encoding.UTF8.GetBytes(descriptorJson);
var descriptorBlocks = (descriptorData.Length + blockSize - 1) / blockSize;
for (int i = 0; i < descriptorBlocks; i++) {
var startIndex = i * blockSize;
var length = Math.Min(blockSize, descriptorData.Length - startIndex);
byte[] block = new byte[blockSize]; // Initialized with zeros by default
Array.Copy(descriptorData, startIndex, block, 0, length);
handler.WriteData(block);
currentTapeBlock++;
Thread.Sleep(100); // Small delay between blocks
}
// write 3 0 filled blocks to indicate end of backup
ZeroFillBlocks(handler, 3, blockSize);
handler.Prepare(TapeDeviceHandler.TAPE_UNLOCK);
Thread.Sleep(2000);
handler.SetPosition(TapeDeviceHandler.TAPE_REWIND);
Thread.Sleep(2000);
}
public BackupDescriptor? FindDescriptor(uint blockSize) {
Console.WriteLine("Searching for descriptor on tape...");
Console.WriteLine($"Block Size: {blockSize}.");
using var handler = new TapeDeviceHandler(_tapePath);
LoadTape(handler);
handler.SetMediaParams(blockSize);
handler.SetPosition(TapeDeviceHandler.TAPE_REWIND);
Thread.Sleep(2000);
handler.SetPosition(TapeDeviceHandler.TAPE_SPACE_FILEMARKS, 0, 1);
Thread.Sleep(2000);
handler.WaitForTapeReady();
// Read data from tape until 3 zero-filled blocks are found
var buffer = new List<byte>();
byte[] data;
var zeroBlocks = 0;
do {
data = handler.ReadData(blockSize);
buffer.AddRange(data);
if (data.All(b => b == 0)) {
zeroBlocks++;
}
else {
zeroBlocks = 0;
}
} while (zeroBlocks < 3);
// Remove the last 3 zero-filled blocks from the buffer
var totalZeroBlocksSize = (int)(3 * blockSize);
if (buffer.Count >= totalZeroBlocksSize) {
buffer.RemoveRange(buffer.Count - totalZeroBlocksSize, totalZeroBlocksSize);
}
// Convert buffer to byte array
var byteArray = buffer.ToArray();
// Convert byte array to string and trim ending zeros
var json = Encoding.UTF8.GetString(byteArray).TrimEnd('\0');
try {
var descriptor = JsonSerializer.Deserialize<BackupDescriptor>(json);
if (descriptor != null) {
Console.WriteLine("Descriptor read successfully.");
return descriptor;
}
}
catch (JsonException ex) {
Console.WriteLine($"Failed to parse descriptor JSON: {ex.Message}");
}
handler.Prepare(TapeDeviceHandler.TAPE_UNLOCK);
Thread.Sleep(2000);
handler.SetPosition(TapeDeviceHandler.TAPE_REWIND);
Thread.Sleep(2000);
return null;
}
public void RestoreDirectory(BackupDescriptor descriptor, string restoreDirectoryPath) {
Console.WriteLine("Restoring files to directory: " + restoreDirectoryPath);
Console.WriteLine("Block Size: " + descriptor.BlockSize);
using var handler = new TapeDeviceHandler(_tapePath);
LoadTape(handler);
handler.SetMediaParams(descriptor.BlockSize);
handler.SetPosition(TapeDeviceHandler.TAPE_REWIND);
Thread.Sleep(2000);
handler.WaitForTapeReady();
foreach (var file in descriptor.Files) {
// Set position to the start block of the file
handler.SetPosition(TapeDeviceHandler.TAPE_ABSOLUTE_BLOCK, 0, file.StartBlock);
Thread.Sleep(2000);
var filePath = Path.Combine(restoreDirectoryPath, file.FilePath);
var directoryPath = Path.GetDirectoryName(filePath);
if (directoryPath != null) {
Directory.CreateDirectory(directoryPath);
}
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) {
var buffer = new byte[descriptor.BlockSize];
for (var i = 0; i < file.NumberOfBlocks; i++) {
var bytesRead = handler.ReadData(buffer, 0, buffer.Length);
if (bytesRead < buffer.Length) {
// Zero-fill the remaining part of the buffer if the last block is smaller than blockSize
Array.Clear(buffer, bytesRead, buffer.Length - bytesRead);
}
var bytesToWrite = (i == file.NumberOfBlocks - 1) ? (int)(file.FileSize % descriptor.BlockSize) : buffer.Length;
fileStream.Write(buffer, 0, bytesToWrite);
}
}
// check md5 checksum of restored file with the one in descriptor
using (var md5 = System.Security.Cryptography.MD5.Create()) {
using (var fileStreamRead = new FileStream(filePath, FileMode.Open, FileAccess.Read)) {
var fileHash = md5.ComputeHash(fileStreamRead);
var fileHashString = BitConverter.ToString(fileHash).Replace("-", "").ToLower();
if (fileHashString != file.FileHash) {
Console.WriteLine($"Checksum mismatch for file: {filePath}");
}
else {
Console.WriteLine($"Restored file: {filePath}");
}
}
}
}
handler.SetPosition(TapeDeviceHandler.TAPE_REWIND);
Thread.Sleep(2000);
}
public int CheckMediaSize(string ltoGen) {
var descriptor = JsonSerializer.Deserialize<BackupDescriptor>(File.ReadAllText(_descriptorFilePath));
if (descriptor == null) {
Console.WriteLine("Failed to read descriptor.");
return 1;
}
var totalBlocks = (ulong)descriptor.Files.Sum(f => f.NumberOfBlocks);
const ulong fileMarkBlocks = 1;
const ulong terminalBlocks = 3;
var descriptorSize = new FileInfo(_descriptoFileName).Length;
ulong descriptorSizeBlocks = (ulong)Math.Ceiling((double)descriptorSize / descriptor.BlockSize);
totalBlocks += fileMarkBlocks + descriptorSizeBlocks + terminalBlocks;
var maxBlocks = LTOBlockSizes.GetMaxBlocks(ltoGen);
if (totalBlocks > maxBlocks) {
Console.WriteLine("Backup will not fit on tape. Please use a larger tape.");
return 1;
}
else {
Console.WriteLine("Backup will fit on tape.");
}
return 0;
}
public void Backup() {
while (true) {
Console.WriteLine("\nSelect a backup to perform:");
for (int i = 0; i < _configuration.Backups.Count; i++) {
var backupInt = _configuration.Backups[i];
Console.WriteLine($"{i + 1}. Backup Name: {backupInt.Name}, Bar code {backupInt.Barcode}, Source: {backupInt.Source}, Destination: {backupInt.Destination}");
}
Console.Write("Enter your choice (or '0' to go back): ");
var choice = Console.ReadLine();
if (choice == "0") {
return; // Go back to the main menu
}
if (!int.TryParse(choice, out int index) || index < 1 || index > _configuration.Backups.Count) {
Console.WriteLine("Invalid choice. Please try again.");
continue;
}
var backup = _configuration.Backups[index - 1];
uint blockSize = LTOBlockSizes.GetBlockSize(backup.LTOGen);
// Step 1: Create Descriptor and Write to File System
CreateDescriptor(backup.Source, _descriptorFilePath, blockSize);
// Step 2: calculate if files in descriptor will fit on tape
var checkMediaSizeResult = CheckMediaSize(backup.LTOGen);
if (checkMediaSizeResult != 0)
return;
// Step 3: Write Files to Tape
WriteFilesToTape(backup.Source, _descriptorFilePath, blockSize);
File.Delete(_descriptorFilePath);
Console.WriteLine("Backup completed.");
return; // Go back to the main menu after completing the backup
}
}
public void Restore() {
while (true) {
Console.WriteLine("\nSelect a backup to restore:");
for (int i = 0; i < _configuration.Backups.Count; i++) {
var backupInt = _configuration.Backups[i];
Console.WriteLine($"{i + 1}. Backup Name: {backupInt.Name}, Bar code {backupInt.Barcode}, Source: {backupInt.Source}, Destination: {backupInt.Destination}");
}
Console.Write("Enter your choice (or '0' to go back): ");
var choice = Console.ReadLine();
if (choice == "0") {
return; // Go back to the main menu
}
if (!int.TryParse(choice, out int index) || index < 1 || index > _configuration.Backups.Count) {
Console.WriteLine("Invalid choice. Please try again.");
continue;
}
var backup = _configuration.Backups[index - 1];
uint blockSize = LTOBlockSizes.GetBlockSize(backup.LTOGen);
var descriptor = FindDescriptor(blockSize);
if (descriptor != null) {
var json = JsonSerializer.Serialize(descriptor, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);
}
if (descriptor == null) {
Console.WriteLine("Descriptor not found on tape.");
return;
}
// Step 3: Test restore from tape
RestoreDirectory(descriptor, backup.Destination);
Console.WriteLine("Restore completed.");
return; // Go back to the main menu after completing the restore
}
}
}

View File

@ -0,0 +1,14 @@
namespace MaksIT.LTO.Backup;
public class BackupItem {
public required string Name { get; set; }
public required string Barcode { get; set; }
public required string Source { get; set; }
public required string Destination { get; set; }
public required string LTOGen { get; set; }
}
public class Configuration {
public required string TapePath { get; set; }
public required List<BackupItem> Backups { get; set; }
}

View File

@ -0,0 +1,8 @@

namespace MaksIT.LTO.Backup.Entities;
public class BackupDescriptor {
public uint ReservedBlocks { get; set; }
public uint BlockSize { get; set; }
public List<FileDescriptor> Files { get; set; } = new List<FileDescriptor>();
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MaksIT.LTO.Backup.Entities;
public class FileDescriptor {
public required string FilePath { get; set; }
public long StartBlock { get; set; }
public uint NumberOfBlocks { get; set; }
public long FileSize { get; set; }
public DateTime CreationTime { get; set; }
public DateTime LastModifiedTime { get; set; }
public required string FileHash { get; set; }
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MaksIT.LTO.Core\MaksIT.LTO.Core.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="configuration.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,58 @@
namespace MaksIT.LTO.Backup;
class Program {
public static void Main() {
var app = new Application();
Console.OutputEncoding = System.Text.Encoding.UTF8;
while (true) {
// Console.Clear();
Console.WriteLine("MaksIT.LTO.Backup v0.0.1");
Console.WriteLine("© Maksym Sadovnychyy (MAKS-IT) 2024");
Console.WriteLine("\nSelect an action:");
Console.WriteLine("1. Load tape");
Console.WriteLine("2. Backup");
Console.WriteLine("3. Restore");
Console.WriteLine("4. Eject tape");
Console.WriteLine("5. Reload configurations");
Console.WriteLine("6. Exit");
Console.Write("Enter your choice: ");
var choice = Console.ReadLine();
try {
switch (choice) {
case "1":
app.LoadTape();
break;
case "2":
app.Backup();
break;
case "3":
app.Restore();
break;
case "4":
app.EjectTape();
break;
case "5":
app.LoadConfiguration();
break;
case "6":
Console.WriteLine("Exiting...");
return;
default:
Console.WriteLine("Invalid choice. Please try again.");
break;
}
}
catch (Exception ex) {
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"TapePath": "\\\\.\\Tape0",
"Backups": [
{
"Name": "Test",
"Barcode": "",
"Source": "F:\\LTO\\Backup",
"Destination": "F:\\LTO\\Restore",
"LTOGen": "LTO5"
},
{
"Name": "Drivers",
"Barcode": "",
"Source": "D:\\Drivers",
"Destination": "F:\\LTO\\Restore",
"LTOGen": "LTO5"
}
]
}

View File

@ -0,0 +1,60 @@
namespace MaksIT.LTO.Core;
public static class LTOBlockSizes {
public const uint LTO1 = 65536; // 64 KB
public const uint LTO2 = 65536; // 64 KB
public const uint LTO3 = 131072; // 128 KB
public const uint LTO4 = 131072; // 128 KB
public const uint LTO5 = 262144; // 256 KB
public const uint LTO6 = 262144; // 256 KB
public const uint LTO7 = 524288; // 512 KB
public const uint LTO8 = 524288; // 512 KB
public const uint LTO9 = 1048576; // 1 MB
// Dictionary to store the total capacity for each LTO generation (in bytes)
private static readonly Dictionary<string, ulong> TapeCapacities = new Dictionary<string, ulong>
{
{ "LTO1", 100UL * 1024 * 1024 * 1024 }, // 100 GB
{ "LTO2", 200UL * 1024 * 1024 * 1024 }, // 200 GB
{ "LTO3", 400UL * 1024 * 1024 * 1024 }, // 400 GB
{ "LTO4", 800UL * 1024 * 1024 * 1024 }, // 800 GB
{ "LTO5", 1500UL * 1024 * 1024 * 1024 }, // 1.5 TB
{ "LTO6", 2500UL * 1024 * 1024 * 1024 }, // 2.5 TB
{ "LTO7", 6000UL * 1024 * 1024 * 1024 }, // 6 TB
{ "LTO8", 12000UL * 1024 * 1024 * 1024 },// 12 TB
{ "LTO9", 18000UL * 1024 * 1024 * 1024 } // 18 TB
};
// Method to get the block size for a given LTO generation
// Method to get the block size for a given LTO generation
public static uint GetBlockSize(string ltoGen) {
return ltoGen switch {
"LTO1" => LTO1,
"LTO2" => LTO2,
"LTO3" => LTO3,
"LTO4" => LTO4,
"LTO5" => LTO5,
"LTO6" => LTO6,
"LTO7" => LTO7,
"LTO8" => LTO8,
"LTO9" => LTO9,
_ => throw new ArgumentException("Invalid LTO generation")
};
}
// Method to get the total capacity for a given LTO generation
public static ulong GetTapeCapacity(string ltoGen) {
if (TapeCapacities.TryGetValue(ltoGen, out var capacity)) {
return capacity;
}
throw new ArgumentException("Invalid LTO generation");
}
// Method to calculate the maximum number of blocks that can be written on the tape
public static ulong GetMaxBlocks(string ltoGen) {
var blockSize = GetBlockSize(ltoGen);
var tapeCapacity = GetTapeCapacity(ltoGen);
return tapeCapacity / blockSize;
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DefineConstants>NTDDI_VERSION_05010000;NTDDI_WINXP_05010000</DefineConstants>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,169 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
// https://github.com/tpn/winsdk-10
namespace MaksIT.LTO.Core;
public partial class TapeDeviceHandler : IDisposable {
private string _tapeDevicePath;
private SafeFileHandle _tapeHandle;
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint OPEN_EXISTING = 3;
// Define IOCTL base
private const uint FILE_DEVICE_TAPE = 0x0000001F;
private const uint FILE_DEVICE_MASS_STORAGE = 0x0000002D;
// Define access rights
private const uint FILE_ANY_ACCESS = 0x0000; // any access
private const uint FILE_READ_ACCESS = 0x0001; // file & pipe
private const uint FILE_WRITE_ACCESS = 0x0002; // file & pipe
// Define method
private const uint METHOD_BUFFERED = 0;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool WriteFile(
SafeFileHandle hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToWrite,
out uint lpNumberOfBytesWritten,
IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadFile(
SafeFileHandle hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToRead,
out uint lpNumberOfBytesRead,
IntPtr lpOverlapped);
public TapeDeviceHandler(string tapeDevicePath) {
_tapeDevicePath = tapeDevicePath;
OpenTapeDevice(GENERIC_READ | GENERIC_WRITE);
}
[MemberNotNull(nameof(_tapeHandle))]
private void OpenTapeDevice(uint desiredAccess) {
_tapeHandle?.Dispose();
_tapeHandle = CreateFile(_tapeDevicePath, desiredAccess, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (_tapeHandle.IsInvalid) {
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
}
public void WriteData(byte[] data) {
IntPtr unmanagedPointer = Marshal.AllocHGlobal(data.Length);
try {
Marshal.Copy(data, 0, unmanagedPointer, data.Length);
bool result = WriteFile(_tapeHandle, unmanagedPointer, (uint)data.Length, out uint bytesWritten, IntPtr.Zero);
if (result) {
Console.WriteLine("Write Data: Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Write Data: Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(unmanagedPointer);
}
}
public byte[] ReadData(uint length) {
byte[] data = new byte[length];
IntPtr unmanagedPointer = Marshal.AllocHGlobal((int)length);
try {
bool result = ReadFile(_tapeHandle, unmanagedPointer, length, out uint bytesRead, IntPtr.Zero);
if (result) {
Marshal.Copy(unmanagedPointer, data, 0, (int)length);
Console.WriteLine("Read Data: Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Read Data: Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(unmanagedPointer);
}
return data;
}
public int ReadData(byte[] buffer, int offset, int length) {
IntPtr unmanagedPointer = Marshal.AllocHGlobal(length);
try {
bool result = ReadFile(_tapeHandle, unmanagedPointer, (uint)length, out uint bytesRead, IntPtr.Zero);
if (result) {
Marshal.Copy(unmanagedPointer, buffer, offset, (int)bytesRead);
Console.WriteLine("Read Data: Success");
return (int)bytesRead;
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Read Data: Failed with error code {error}");
return 0;
}
}
finally {
Marshal.FreeHGlobal(unmanagedPointer);
}
}
public void WaitForTapeReady() {
bool isReady = false;
while (!isReady) {
Console.WriteLine("Checking if tape is ready...");
int errorCode = GetStatus();
if (errorCode == 0) // Assuming 0 means success/ready
{
isReady = true;
Console.WriteLine("Tape is ready.");
}
else {
Console.WriteLine($"Tape not ready, status code: {errorCode}. Retrying...");
Thread.Sleep(1000); // Wait 1 second before checking again
}
}
}
public void Dispose() {
_tapeHandle?.Dispose();
}
}

View File

@ -0,0 +1,811 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace MaksIT.LTO.Core;
public partial class TapeDeviceHandler : IDisposable
{
#region Storage IOCTL Commands from ntddstor.h
//
// The following device control codes are common for all class drivers. They
// should be used in place of the older IOCTL_DISK, IOCTL_CDROM and IOCTL_TAPE
// common codes
//
private const uint IOCTL_STORAGE_CHECK_VERIFY = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0200 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_CHECK_VERIFY2 = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0200 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_MEDIA_REMOVAL = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0201 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_EJECT_MEDIA = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0202 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_LOAD_MEDIA = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0203 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_LOAD_MEDIA2 = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0203 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_RESERVE = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0204 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_RELEASE = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0205 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_FIND_NEW_DEVICES = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0206 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_EJECTION_CONTROL = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0250 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_MCN_CONTROL = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0251 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_GET_MEDIA_TYPES = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0300 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_GET_MEDIA_TYPES_EX = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0301 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0304 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_GET_HOTPLUG_INFO = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0305 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_SET_HOTPLUG_INFO = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0306 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_RESET_BUS = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0400 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_RESET_DEVICE = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0401 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_BREAK_RESERVATION = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0405 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_PERSISTENT_RESERVE_IN = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0406 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_PERSISTENT_RESERVE_OUT = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0407 << 2) | METHOD_BUFFERED;
//
// This IOCTL includes the same information as IOCTL_STORAGE_GET_DEVICE_NUMBER, plus the device GUID.
//
private const uint IOCTL_STORAGE_GET_DEVICE_NUMBER = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0420 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_PREDICT_FAILURE = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0440 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_FAILURE_PREDICTION_CONFIG = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0441 << 2) | METHOD_BUFFERED;
//
// This IOCTL retrieves reliability counters for a device.
//
private const uint IOCTL_STORAGE_GET_COUNTERS = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x442 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_READ_CAPACITY = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0450 << 2) | METHOD_BUFFERED;
//
// IOCTLs 0x0463 to 0x0468 reserved for dependent disk support.
//
//
// IOCTLs 0x0470 to 0x047f reserved for device and stack telemetry interfaces
//
private const uint IOCTL_STORAGE_GET_DEVICE_TELEMETRY = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0470 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_DEVICE_TELEMETRY_NOTIFY = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0471 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_DEVICE_TELEMETRY_QUERY_CAPS = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0472 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_GET_DEVICE_TELEMETRY_RAW = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0473 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_SET_TEMPERATURE_THRESHOLD = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0480 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_PROTOCOL_COMMAND = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x04F0 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_QUERY_PROPERTY = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0500 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_WRITE_ACCESS << 14) | (0x0501 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_GET_LB_PROVISIONING_MAP_RESOURCES = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0502 << 2) | METHOD_BUFFERED;
//
// IOCTLs 0x0503 to 0x0580 reserved for Enhanced Storage devices.
//
//
// This IOCTL offloads the erasure process to the storage device. There is no guarantee as to the successful
// deletion or recoverability of the data on the storage device after command completion. This IOCTL is limited
// to data disks in regular Windows. In WinPE, this IOCTL is supported for both boot and data disks.
//
// Initial implementation requires no input and returns no output other than status. Callers should first
// call FSCTL_LOCK_VOLUME before calling this ioctl to flush out cached data in upper layers. No waiting of
// outstanding request completion is done before issuing the command to the device.
//
private const uint IOCTL_STORAGE_REINITIALIZE_MEDIA = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_WRITE_ACCESS << 14) | (0x0503 << 2) | METHOD_BUFFERED;
//
// IOCTLs for bandwidth contracts on storage devices
// (Move this to ntddsfio if we decide to use a new base)
//
private const uint IOCTL_STORAGE_GET_BC_PROPERTIES = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_READ_ACCESS << 14) | (0x0600 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_ALLOCATE_BC_STREAM = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0601 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_FREE_BC_STREAM = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0602 << 2) | METHOD_BUFFERED;
//
// IOCTL to check for priority support
//
private const uint IOCTL_STORAGE_CHECK_PRIORITY_HINT_SUPPORT = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0620 << 2) | METHOD_BUFFERED;
//
// IOCTL for data integrity check support
//
private const uint IOCTL_STORAGE_START_DATA_INTEGRITY_CHECK = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0621 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_STOP_DATA_INTEGRITY_CHECK = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0622 << 2) | METHOD_BUFFERED;
//
// These ioctl codes are obsolete. They are defined here to avoid resuing them
// and to allow class drivers to respond to them more easily.
//
private const uint OBSOLETE_IOCTL_STORAGE_RESET_BUS = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0400 << 2) | METHOD_BUFFERED;
private const uint OBSOLETE_IOCTL_STORAGE_RESET_DEVICE = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0401 << 2) | METHOD_BUFFERED;
//
// IOCTLs 0x0643 to 0x0655 reserved for VHD disk support.
//
//
// IOCTLs for firmware upgrade on storage devices
//
private const uint IOCTL_STORAGE_FIRMWARE_GET_INFO = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0700 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_FIRMWARE_DOWNLOAD = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0701 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_FIRMWARE_ACTIVATE = (FILE_DEVICE_MASS_STORAGE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0702 << 2) | METHOD_BUFFERED;
//
// IOCTL to support Idle Power Management, including Device Wake
//
private const uint IOCTL_STORAGE_ENABLE_IDLE_POWER = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0720 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_GET_IDLE_POWERUP_REASON = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0721 << 2) | METHOD_BUFFERED;
//
// IOCTLs to allow class drivers to acquire and release active references on
// a unit. These should only be used if the class driver previously sent a
// successful IOCTL_STORAGE_ENABLE_IDLE_POWER request to the port driver.
//
private const uint IOCTL_STORAGE_POWER_ACTIVE = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0722 << 2) | METHOD_BUFFERED;
private const uint IOCTL_STORAGE_POWER_IDLE = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0723 << 2) | METHOD_BUFFERED;
//
// This IOCTL indicates that the physical device has triggered some sort of event.
//
private const uint IOCTL_STORAGE_EVENT_NOTIFICATION = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0724 << 2) | METHOD_BUFFERED;
//
// IOCTL to specify a power cap for a storage device.
//
private const uint IOCTL_STORAGE_DEVICE_POWER_CAP = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0725 << 2) | METHOD_BUFFERED;
//
// IOCTL to send commands to the RPMB for a storage device.
//
private const uint IOCTL_STORAGE_RPMB_COMMAND = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0726 << 2) | METHOD_BUFFERED;
//
// IOCTL to manage attributes for storage devices
//
private const uint IOCTL_STORAGE_ATTRIBUTE_MANAGEMENT = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0727 << 2) | METHOD_BUFFERED;
//
// IOCTL_STORAGE_DIAGNOSTIC IOCTL to query diagnostic data from the storage driver stack
//
private const uint IOCTL_STORAGE_DIAGNOSTIC = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0728 << 2) | METHOD_BUFFERED;
//
// IOCTLs for storage device depopulation support.
//
//
// IOCTL_STORAGE_GET_PHYSICAL_ELEMENT_STATUS IOCTL to query physical element status from device.
//
private const uint IOCTL_STORAGE_GET_PHYSICAL_ELEMENT_STATUS = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0729 << 2) | METHOD_BUFFERED;
//
// IOCTL_STORAGE_SET_PHYSICAL_ELEMENT_STATUS IOCTL to set physical element status on device.
//
private const uint IOCTL_STORAGE_REMOVE_ELEMENT_AND_TRUNCATE = (FILE_DEVICE_MASS_STORAGE << 16) | (FILE_ANY_ACCESS << 14) | (0x0730 << 2) | METHOD_BUFFERED;
#endregion
#region Storage IOCTL Structures from ntddstor.h
//
// Note: Function code values of less than 0x800 are reserved for Microsoft. Values of 0x800 and higher can be used by vendors.
// So do not use function code of 0x800 and higher to define new IOCTLs in this file.
//
//
// IOCTL_STORAGE_GET_HOTPLUG_INFO
//
[StructLayout(LayoutKind.Sequential)]
public struct STORAGE_HOTPLUG_INFO
{
public uint Size; // version
public bool MediaRemovable; // ie. zip, jaz, cdrom, mo, etc. vs hdd
public bool MediaHotplug; // ie. does the device succeed a lock even though its not lockable media?
public bool DeviceHotplug; // ie. 1394, USB, etc.
public bool WriteCacheEnableOverride; // This field should not be relied upon because it is no longer used
}
//
// IOCTL_STORAGE_GET_DEVICE_NUMBER
//
// input - none
//
// output - STORAGE_DEVICE_NUMBER structure
// The values in the STORAGE_DEVICE_NUMBER structure are guaranteed
// to remain unchanged until the system is rebooted. They are not
// guaranteed to be persistant across boots.
[StructLayout(LayoutKind.Sequential)]
public struct STORAGE_DEVICE_NUMBER
{
//
// The FILE_DEVICE_XXX type for this device.
//
public uint DeviceType;
//
// The number of this device
//
public uint DeviceNumber;
//
// If the device is partitionable, the partition number of the device.
// Otherwise -1
//
public uint PartitionNumber;
}
[StructLayout(LayoutKind.Sequential)]
public struct _STORAGE_DEVICE_NUMBERS
{
public uint NumberOfDeviceNumbers;
public STORAGE_DEVICE_NUMBER DeviceNumber;
}
//
// IOCTL_STORAGE_GET_DEVICE_NUMBER_EX
//
// input - none
//
// output - STORAGE_DEVICE_NUMBER_EX structure
//
//
// Possible flags that can be set in Flags field of
// STORAGE_DEVICE_NUMBER_EX structure defined below
//
//
// This flag indicates that deviceguid is randomly created because a deviceguid conflict was observed
//
public const uint STORAGE_DEVICE_FLAGS_RANDOM_DEVICEGUID = 0x1;
//
// This flag indicates that deviceguid is randomly created because the HW ID was not available
//
public const uint STORAGE_DEVICE_FLAGS_DEFAULT_DEVICEGUID = 0x2;
//
// This flag indicates that deviceguid is created from the scsi page83 data.
// If this flag is not set this implies it's created from serial number or is randomly generated.
//
public const uint STORAGE_DEVICE_FLAGS_DEVICEGUID_FROM_PAGE83 = 0x4;
[StructLayout(LayoutKind.Sequential)]
public struct STORAGE_DEVICE_NUMBER_EX
{
//
// Sizeof(STORAGE_DEVICE_NUMBER_EX).
//
public uint Version;
//
// Total size of the structure, including any additional data. Currently
// this will always be the same as sizeof(STORAGE_DEVICE_NUMBER_EX).
//
public uint Size;
//
// Flags for the device
//
public uint Flags;
//
// The FILE_DEVICE_XXX type for this device.
//
public uint DeviceType;
//
// The number of this device
//
public uint DeviceNumber;
//
// A globally-unique identification number for this device.
// A GUID of {0} indicates that a GUID could not be generated. The GUID
// is based on hardware information that doesn't change with firmware updates
// (for instance, serial number can be used to form the GUID, but not the firmware
// revision). The device GUID remains the same across reboots.
//
// In general, if a device exposes a globally unique identifier, the storage driver
// will use that identifier to form the GUID. Otherwise, the storage driver will combine
// the device's vendor ID, product ID and serial number to create the GUID.
//
// If a storage driver detects two devices with the same hardware information (which is
// an indication of a problem with the device), the driver will generate a random GUID for
// one of the two devices. When handling IOCTL_STORAGE_GET_DEVICE_NUMBER_EX for the device
// with the random GUID, the driver will add STORAGE_DEVICE_FLAGS_RANDOM_DEVICEGUID_REASON_CONFLICT
// to the Flags member of this structure.
//
// If a storage device does not provide any identifying information, the driver will generate a random
// GUID and add STORAGE_DEVICE_FLAGS_RANDOM_DEVICEGUID_REASON_NOHWID to the Flags member of this structure.
//
// A random GUID is not persisted and will not be the same after a reboot.
//
public Guid DeviceGuid;
//
// If the device is partitionable, the partition number of the device.
// Otherwise -1
//
public uint PartitionNumber;
}
//
// Define the structures for scsi resets
//
[StructLayout(LayoutKind.Sequential)]
public struct STORAGE_BUS_RESET_REQUEST
{
public byte PathId;
}
//
// Break reservation is sent to the Adapter/FDO with the given lun information.
//
[StructLayout(LayoutKind.Sequential)]
public struct STORAGE_BREAK_RESERVATION_REQUEST
{
public uint Length;
public byte _unused;
public byte PathId;
public byte TargetId;
public byte Lun;
}
//
// IOCTL_STORAGE_MEDIA_REMOVAL disables the mechanism
// on a storage device that ejects media. This function
// may or may not be supported on storage devices that
// support removable media.
//
// TRUE means prevent media from being removed.
// FALSE means allow media removal.
//
[StructLayout(LayoutKind.Sequential)]
public struct PREVENT_MEDIA_REMOVAL
{
public bool PreventMediaRemoval;
}
//
// This is the format of TARGET_DEVICE_CUSTOM_NOTIFICATION.CustomDataBuffer
// passed to applications by the classpnp autorun code (via IoReportTargetDeviceChangeAsynchronous).
//
[StructLayout(LayoutKind.Sequential)]
public struct CLASS_MEDIA_CHANGE_CONTEXT {
public uint MediaChangeCount;
public uint NewState;
}
// begin_ntminitape
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_STATISTICS
{
public uint Version;
public uint Flags;
public ulong RecoveredWriteOperations;
public ulong UnrecoveredWriteOperations;
public ulong RecoveredReadOperations;
public ulong UnrecoveredReadOperations;
public byte CompressionRatioRead;
public byte CompressionRatioWrite;
}
public uint RECOVERED_WRITES_VALID = 0x00000001;
public uint UNRECOVERED_WRITES_VALID = 0x00000002;
public uint RECOVERED_READS_VALID = 0x00000004;
public uint UNRECOVERED_READS_VALID = 0x00000008;
public uint WRITE_COMPRESSION_INFO_VALID = 0x00000010;
public uint READ_COMPRESSION_INFO_VALID = 0x00000020;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_GET_STATISTICS
{
public uint Operation;
}
public uint TAPE_RETURN_STATISTICS = 0;
public uint TAPE_RETURN_ENV_INFO = 1;
public uint TAPE_RESET_STATISTICS = 2;
//
// IOCTL_STORAGE_GET_MEDIA_TYPES_EX will return an array of DEVICE_MEDIA_INFO
// structures, one per supported type, embedded in the GET_MEDIA_TYPES struct.
//
public enum STORAGE_MEDIA_TYPE
{
// Unknown, // Format is unknown
// F5_1Pt2_512, // 5.25", 1.2MB, 512 bytes/sector
// F3_1Pt44_512, // 3.5", 1.44MB, 512 bytes/sector
// F3_2Pt88_512, // 3.5", 2.88MB, 512 bytes/sector
// F3_20Pt8_512, // 3.5", 20.8MB, 512 bytes/sector
// F3_720_512, // 3.5", 720KB, 512 bytes/sector
// F5_360_512, // 5.25", 360KB, 512 bytes/sector
// F5_320_512, // 5.25", 320KB, 512 bytes/sector
// F5_320_1024, // 5.25", 320KB, 1024 bytes/sector
// F5_180_512, // 5.25", 180KB, 512 bytes/sector
// F5_160_512, // 5.25", 160KB, 512 bytes/sector
// RemovableMedia, // Removable media other than floppy
// FixedMedia, // Fixed hard disk media
// F3_120M_512, // 3.5", 120M Floppy
// F3_640_512, // 3.5" , 640KB, 512 bytes/sector
// F5_640_512, // 5.25", 640KB, 512 bytes/sector
// F5_720_512, // 5.25", 720KB, 512 bytes/sector
// F3_1Pt2_512, // 3.5" , 1.2Mb, 512 bytes/sector
// F3_1Pt23_1024, // 3.5" , 1.23Mb, 1024 bytes/sector
// F5_1Pt23_1024, // 5.25", 1.23MB, 1024 bytes/sector
// F3_128Mb_512, // 3.5" MO 128Mb 512 bytes/sector
// F3_230Mb_512, // 3.5" MO 230Mb 512 bytes/sector
// F8_256_128, // 8", 256KB, 128 bytes/sector
// F3_200Mb_512, // 3.5", 200M Floppy (HiFD)
DDS_4mm = 0x20, // Tape - DAT DDS1,2,... (all vendors)
MiniQic, // Tape - miniQIC Tape
Travan, // Tape - Travan TR-1,2,3,...
QIC, // Tape - QIC
MP_8mm, // Tape - 8mm Exabyte Metal Particle
AME_8mm, // Tape - 8mm Exabyte Advanced Metal Evap
AIT1_8mm, // Tape - 8mm Sony AIT
DLT, // Tape - DLT Compact IIIxt, IV
NCTP, // Tape - Philips NCTP
IBM_3480, // Tape - IBM 3480
IBM_3490E, // Tape - IBM 3490E
IBM_Magstar_3590, // Tape - IBM Magstar 3590
IBM_Magstar_MP, // Tape - IBM Magstar MP
STK_DATA_D3, // Tape - STK Data D3
SONY_DTF, // Tape - Sony DTF
DV_6mm, // Tape - 6mm Digital Video
DMI, // Tape - Exabyte DMI and compatibles
SONY_D2, // Tape - Sony D2S and D2L
CLEANER_CARTRIDGE, // Cleaner - All Drive types that support Drive Cleaners
CD_ROM, // Opt_Disk - CD
CD_R, // Opt_Disk - CD-Recordable (Write Once)
CD_RW, // Opt_Disk - CD-Rewriteable
DVD_ROM, // Opt_Disk - DVD-ROM
DVD_R, // Opt_Disk - DVD-Recordable (Write Once)
DVD_RW, // Opt_Disk - DVD-Rewriteable
MO_3_RW, // Opt_Disk - 3.5" Rewriteable MO Disk
MO_5_WO, // Opt_Disk - MO 5.25" Write Once
MO_5_RW, // Opt_Disk - MO 5.25" Rewriteable (not LIMDOW)
MO_5_LIMDOW, // Opt_Disk - MO 5.25" Rewriteable (LIMDOW)
PC_5_WO, // Opt_Disk - Phase Change 5.25" Write Once Optical
PC_5_RW, // Opt_Disk - Phase Change 5.25" Rewriteable
PD_5_RW, // Opt_Disk - PhaseChange Dual Rewriteable
ABL_5_WO, // Opt_Disk - Ablative 5.25" Write Once Optical
PINNACLE_APEX_5_RW, // Opt_Disk - Pinnacle Apex 4.6GB Rewriteable Optical
SONY_12_WO, // Opt_Disk - Sony 12" Write Once
PHILIPS_12_WO, // Opt_Disk - Philips/LMS 12" Write Once
HITACHI_12_WO, // Opt_Disk - Hitachi 12" Write Once
CYGNET_12_WO, // Opt_Disk - Cygnet/ATG 12" Write Once
KODAK_14_WO, // Opt_Disk - Kodak 14" Write Once
MO_NFR_525, // Opt_Disk - Near Field Recording (Terastor)
NIKON_12_RW, // Opt_Disk - Nikon 12" Rewriteable
IOMEGA_ZIP, // Mag_Disk - Iomega Zip
IOMEGA_JAZ, // Mag_Disk - Iomega Jaz
SYQUEST_EZ135, // Mag_Disk - Syquest EZ135
SYQUEST_EZFLYER, // Mag_Disk - Syquest EzFlyer
SYQUEST_SYJET, // Mag_Disk - Syquest SyJet
AVATAR_F2, // Mag_Disk - 2.5" Floppy
MP2_8mm, // Tape - 8mm Hitachi
DST_S, // Ampex DST Small Tapes
DST_M, // Ampex DST Medium Tapes
DST_L, // Ampex DST Large Tapes
VXATape_1, // Ecrix 8mm Tape
VXATape_2, // Ecrix 8mm Tape
#if NTDDI_VERSION_05010000
STK_EAGLE, // STK Eagle
#elif NTDDI_WINXP_05010000
STK_9840, // STK 9840
#endif
LTO_Ultrium, // IBM, HP, Seagate LTO Ultrium
LTO_Accelis, // IBM, HP, Seagate LTO Accelis
DVD_RAM, // Opt_Disk - DVD-RAM
AIT_8mm, // AIT2 or higher
ADR_1, // OnStream ADR Mediatypes
ADR_2,
STK_9940, // STK 9940
SAIT, // SAIT Tapes
VXATape // VXA (Ecrix 8mm) Tape
}
public const uint MEDIA_ERASEABLE = 0x00000001;
public const uint MEDIA_WRITE_ONCE = 0x00000002;
public const uint MEDIA_READ_ONLY = 0x00000004;
public const uint MEDIA_READ_WRITE = 0x00000008;
public const uint MEDIA_WRITE_PROTECTED = 0x00000100;
public const uint MEDIA_CURRENTLY_MOUNTED = 0x80000000;
//
// Define the different storage bus types
// Bus types below 128 (0x80) are reserved for Microsoft use
//
public enum STORAGE_BUS_TYPE
{
BusTypeUnknown = 0x00,
BusTypeScsi,
BusTypeAtapi,
BusTypeAta,
BusType1394,
BusTypeSsa,
BusTypeFibre,
BusTypeUsb,
BusTypeRAID,
BusTypeiSCSI,
BusTypeSas,
BusTypeSata,
BusTypeSd,
BusTypeMmc,
BusTypeVirtual,
BusTypeFileBackedVirtual,
BusTypeSpaces,
BusTypeNvme,
BusTypeSCM,
BusTypeUfs,
BusTypeMax,
BusTypeMaxReserved = 0x7F
}
//
// Macro to identify which bus types
// support shared storage
//
public static bool SupportsDeviceSharing(STORAGE_BUS_TYPE busType)
{
return busType == STORAGE_BUS_TYPE.BusTypeScsi ||
busType == STORAGE_BUS_TYPE.BusTypeFibre ||
busType == STORAGE_BUS_TYPE.BusTypeiSCSI ||
busType == STORAGE_BUS_TYPE.BusTypeSas ||
busType == STORAGE_BUS_TYPE.BusTypeSpaces;
}
[StructLayout(LayoutKind.Sequential)]
public struct DEVICE_MEDIA_INFO
{
[StructLayout(LayoutKind.Sequential)]
public struct DiskInfoStruct
{
public long Cylinders; // LARGE_INTEGER
public STORAGE_MEDIA_TYPE MediaType;
public uint TracksPerCylinder;
public uint SectorsPerTrack;
public uint BytesPerSector;
public uint NumberMediaSides;
public uint MediaCharacteristics; // Bitmask of MEDIA_XXX values.
}
[StructLayout(LayoutKind.Sequential)]
public struct RemovableDiskInfoStruct
{
public long Cylinders; // LARGE_INTEGER
public STORAGE_MEDIA_TYPE MediaType;
public uint TracksPerCylinder;
public uint SectorsPerTrack;
public uint BytesPerSector;
public uint NumberMediaSides;
public uint MediaCharacteristics; // Bitmask of MEDIA_XXX values.
}
[StructLayout(LayoutKind.Sequential)]
public struct TapeInfoStruct
{
public STORAGE_MEDIA_TYPE MediaType;
public uint MediaCharacteristics; // Bitmask of MEDIA_XXX values.
public uint CurrentBlockSize;
public STORAGE_BUS_TYPE BusType;
[StructLayout(LayoutKind.Sequential)]
public struct ScsiInformationStruct
{
public byte MediumType;
public byte DensityCode;
}
[StructLayout(LayoutKind.Explicit)]
public struct BusSpecificDataUnion
{
[FieldOffset(0)]
public ScsiInformationStruct ScsiInformation;
}
public BusSpecificDataUnion BusSpecificData;
}
[StructLayout(LayoutKind.Explicit)]
public struct DeviceSpecificUnion
{
[FieldOffset(0)]
public DiskInfoStruct DiskInfo;
[FieldOffset(0)]
public RemovableDiskInfoStruct RemovableDiskInfo;
[FieldOffset(0)]
public TapeInfoStruct TapeInfo;
}
public DeviceSpecificUnion DeviceSpecific;
}
[StructLayout(LayoutKind.Sequential)]
public struct GET_MEDIA_TYPES
{
public uint DeviceType; // FILE_DEVICE_XXX values
public uint MediaInfoCount;
public DEVICE_MEDIA_INFO MediaInfo;
}
//
// IOCTL_STORAGE_PREDICT_FAILURE
//
// input - none
//
// output - STORAGE_PREDICT_FAILURE structure
// PredictFailure returns zero if no failure predicted and non zero
// if a failure is predicted.
//
// VendorSpecific returns 512 bytes of vendor specific information
// if a failure is predicted
//
[StructLayout(LayoutKind.Sequential)]
public struct STORAGE_PREDICT_FAILURE
{
public uint PredictFailure;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
public byte[] VendorSpecific;
}
//
// IOCTL_STORAGE_FAILURE_PREDICTION_CONFIG
//
// Input - STORAGE_FAILURE_PREDICTION_CONFIG structure.
// If the sender wants to enable or disable failure prediction then
// the sender should set the "Set" field to TRUE.
// Output - STORAGE_FAILURE_PREDICTION_CONFIG structure.
// If successful, the "Enabled" field will indicate if failure
// prediction is currently enabled or not.
//
[StructLayout(LayoutKind.Sequential)]
public struct STORAGE_FAILURE_PREDICTION_CONFIG
{
public uint Version; // ULONG in C++ is equivalent to uint in C#
public uint Size; // ULONG in C++ is equivalent to uint in C#
public byte Set; // BOOLEAN in C++ is equivalent to byte in C#
public byte Enabled; // BOOLEAN in C++ is equivalent to byte in C#
public ushort Reserved; // USHORT in C++ is equivalent to ushort in C#
}
public const uint STORAGE_FAILURE_PREDICTION_CONFIG_V1 = 1;
// end_ntminitape
//
// Property Query Structures
//
//
// IOCTL_STORAGE_QUERY_PROPERTY
//
// Input Buffer:
// a STORAGE_PROPERTY_QUERY structure which describes what type of query
// is being done, what property is being queried for, and any additional
// parameters which a particular property query requires.
//
// Output Buffer:
// Contains a buffer to place the results of the query into. Since all
// property descriptors can be cast into a STORAGE_DESCRIPTOR_HEADER,
// the IOCTL can be called once with a small buffer then again using
// a buffer as large as the header reports is necessary.
//
//
// Types of queries
//
public enum STORAGE_QUERY_TYPE
{
PropertyStandardQuery = 0, // Retrieves the descriptor
PropertyExistsQuery, // Used to test whether the descriptor is supported
PropertyMaskQuery, // Used to retrieve a mask of writeable fields in the descriptor
PropertyQueryMaxDefined // use to validate the value
}
//
// define some initial property id's
//
public enum STORAGE_PROPERTY_ID
{
StorageDeviceProperty = 0,
StorageAdapterProperty,
StorageDeviceIdProperty,
StorageDeviceUniqueIdProperty, // See storduid.h for details
StorageDeviceWriteCacheProperty,
StorageMiniportProperty,
StorageAccessAlignmentProperty,
StorageDeviceSeekPenaltyProperty,
StorageDeviceTrimProperty,
StorageDeviceWriteAggregationProperty,
StorageDeviceDeviceTelemetryProperty,
StorageDeviceLBProvisioningProperty,
StorageDevicePowerProperty,
StorageDeviceCopyOffloadProperty,
StorageDeviceResiliencyProperty,
StorageDeviceMediumProductType,
StorageAdapterRpmbProperty,
StorageAdapterCryptoProperty,
// end_winioctl
StorageDeviceTieringProperty,
StorageDeviceFaultDomainProperty,
StorageDeviceClusportProperty,
// begin_winioctl
StorageDeviceIoCapabilityProperty = 48,
StorageAdapterProtocolSpecificProperty,
StorageDeviceProtocolSpecificProperty,
StorageAdapterTemperatureProperty,
StorageDeviceTemperatureProperty,
StorageAdapterPhysicalTopologyProperty,
StorageDevicePhysicalTopologyProperty,
StorageDeviceAttributesProperty,
StorageDeviceManagementStatus,
StorageAdapterSerialNumberProperty,
StorageDeviceLocationProperty,
StorageDeviceNumaProperty,
StorageDeviceZonedDeviceProperty,
StorageDeviceUnsafeShutdownCount
}
//
// Query structure - additional parameters for specific queries can follow
// the header
//
[StructLayout(LayoutKind.Sequential)]
public struct STORAGE_PROPERTY_QUERY
{
public STORAGE_PROPERTY_ID PropertyId;
public STORAGE_QUERY_TYPE QueryType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] AdditionalParameters;
}
#endregion
#region Storage IOCTL Methods from ntddstor.h
#endregion
}

View File

@ -0,0 +1,673 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace MaksIT.LTO.Core;
public partial class TapeDeviceHandler : IDisposable {
#region Tape IOCTL Commands from ntddtape.h
//
// NtDeviceIoControlFile IoControlCode values for this device.
//
// Warning: Remember that the low two bits of the code specify how the
// buffers are passed to the driver!
//
private const uint IOCTL_TAPE_ERASE = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0000 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_PREPARE = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS) << 14) | (0x0001 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_WRITE_MARKS = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0002 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_GET_POSITION = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0003 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_SET_POSITION = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0004 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_GET_DRIVE_PARAMS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0005 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_SET_DRIVE_PARAMS = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0006 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_GET_MEDIA_PARAMS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0007 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_SET_MEDIA_PARAMS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0008 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_GET_STATUS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0009 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_CREATE_PARTITION = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x000A << 2) | METHOD_BUFFERED;
//
// The following device control codes are common for all class drivers. The
// functions codes defined here must match all of the other class drivers.
//
// Warning: these codes will be replaced in the future with the IOCTL_STORAGE
// codes included below
//
private const uint IOCTL_TAPE_MEDIA_REMOVAL = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0201 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_EJECT_MEDIA = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0202 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_LOAD_MEDIA = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0203 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_RESERVE = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0204 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_RELEASE = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0205 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_CHECK_VERIFY = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0200 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_FIND_NEW_DEVICES = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0206 << 2) | METHOD_BUFFERED;
#endregion
#region Tape IOCTL Structures from ntddtape.h
//
// IOCTL_TAPE_ERASE definitions
//
public const uint TAPE_ERASE_SHORT = 0;
public const uint TAPE_ERASE_LONG = 1;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_ERASE {
public uint Type; // ULONG in C is equivalent to uint in C#
public byte Immediate; // BOOLEAN in C is typically a byte in C#
}
//
// IOCTL_TAPE_PREPARE definitions
//
public const uint TAPE_LOAD = 0;
public const uint TAPE_UNLOAD = 1;
public const uint TAPE_TENSION = 2;
public const uint TAPE_LOCK = 3;
public const uint TAPE_UNLOCK = 4;
public const uint TAPE_FORMAT = 5;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_PREPARE {
public uint Operation; // ULONG in C is equivalent to uint in C#
public byte Immediate; // BOOLEAN in C is typically a byte in C#
}
//
// IOCTL_TAPE_WRITE_MARKS definitions
//
public const uint TAPE_SETMARKS = 0;
public const uint TAPE_FILEMARKS = 1;
public const uint TAPE_SHORT_FILEMARKS = 2;
public const uint TAPE_LONG_FILEMARKS = 3;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_WRITE_MARKS {
public uint Type; // ULONG in C is equivalent to uint in C#
public uint Count; // ULONG in C is equivalent to uint in C#
public byte Immediate; // BOOLEAN in C is typically a byte in C#
}
//
// IOCTL_TAPE_SET_POSITION definitions
//
public const uint TAPE_ABSOLUTE_POSITION = 0;
public const uint TAPE_LOGICAL_POSITION = 1;
public const uint TAPE_PSEUDO_LOGICAL_POSITION = 2;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_GET_POSITION {
public uint Type;
public uint Partition;
public uint OffsetLow;
public uint OffsetHigh;
}
//
// IOCTL_TAPE_SET_POSITION definitions
//
public const uint TAPE_REWIND = 0;
public const uint TAPE_ABSOLUTE_BLOCK = 1;
public const uint TAPE_LOGICAL_BLOCK = 2;
public const uint TAPE_PSEUDO_LOGICAL_BLOCK = 3;
public const uint TAPE_SPACE_END_OF_DATA = 4;
public const uint TAPE_SPACE_RELATIVE_BLOCKS = 5;
public const uint TAPE_SPACE_FILEMARKS = 6;
public const uint TAPE_SPACE_SEQUENTIAL_FMKS = 7;
public const uint TAPE_SPACE_SETMARKS = 8;
public const uint TAPE_SPACE_SEQUENTIAL_SMKS = 9;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_SET_POSITION {
public uint Method;
public uint Partition;
public long Offset;
public byte Immediate;
}
//
// IOCTL_TAPE_GET_DRIVE_PARAMS definitions
//
//
// Definitions for FeaturesLow parameter
//
public const uint TAPE_DRIVE_FIXED = 0x00000001;
public const uint TAPE_DRIVE_SELECT = 0x00000002;
public const uint TAPE_DRIVE_INITIATOR = 0x00000004;
public const uint TAPE_DRIVE_ERASE_SHORT = 0x00000010;
public const uint TAPE_DRIVE_ERASE_LONG = 0x00000020;
public const uint TAPE_DRIVE_ERASE_BOP_ONLY = 0x00000040;
public const uint TAPE_DRIVE_ERASE_IMMEDIATE = 0x00000080;
public const uint TAPE_DRIVE_TAPE_CAPACITY = 0x00000100;
public const uint TAPE_DRIVE_TAPE_REMAINING = 0x00000200;
public const uint TAPE_DRIVE_FIXED_BLOCK = 0x00000400;
public const uint TAPE_DRIVE_VARIABLE_BLOCK = 0x00000800;
public const uint TAPE_DRIVE_WRITE_PROTECT = 0x00001000;
public const uint TAPE_DRIVE_EOT_WZ_SIZE = 0x00002000;
public const uint TAPE_DRIVE_ECC = 0x00010000;
public const uint TAPE_DRIVE_COMPRESSION = 0x00020000;
public const uint TAPE_DRIVE_PADDING = 0x00040000;
public const uint TAPE_DRIVE_REPORT_SMKS = 0x00080000;
public const uint TAPE_DRIVE_GET_ABSOLUTE_BLK = 0x00100000;
public const uint TAPE_DRIVE_GET_LOGICAL_BLK = 0x00200000;
public const uint TAPE_DRIVE_SET_EOT_WZ_SIZE = 0x00400000;
public const uint TAPE_DRIVE_EJECT_MEDIA = 0x01000000; //don't use this bit!
// //can't be a low features bit!
// //reserved; high features only
//
// Definitions for FeaturesHigh parameter
//
public const uint TAPE_DRIVE_LOAD_UNLOAD = 0x80000001;
public const uint TAPE_DRIVE_TENSION = 0x80000002;
public const uint TAPE_DRIVE_LOCK_UNLOCK = 0x80000004;
public const uint TAPE_DRIVE_REWIND_IMMEDIATE = 0x80000008;
public const uint TAPE_DRIVE_SET_BLOCK_SIZE = 0x80000010;
public const uint TAPE_DRIVE_LOAD_UNLD_IMMED = 0x80000040;
public const uint TAPE_DRIVE_TENSION_IMMED = 0x80000080;
public const uint TAPE_DRIVE_LOCK_UNLK_IMMED = 0x80000100;
public const uint TAPE_DRIVE_SET_ECC = 0x80000200;
public const uint TAPE_DRIVE_SET_COMPRESSION = 0x80000400;
public const uint TAPE_DRIVE_SET_PADDING = 0x80000800;
public const uint TAPE_DRIVE_SET_REPORT_SMKS = 0x80001000;
public const uint TAPE_DRIVE_ABSOLUTE_BLK = 0x80002000;
public const uint TAPE_DRIVE_ABS_BLK_IMMED = 0x80004000;
public const uint TAPE_DRIVE_LOGICAL_BLK = 0x80008000;
public const uint TAPE_DRIVE_LOG_BLK_IMMED = 0x80010000;
public const uint TAPE_DRIVE_END_OF_DATA = 0x80020000;
public const uint TAPE_DRIVE_RELATIVE_BLKS = 0x80040000;
public const uint TAPE_DRIVE_FILEMARKS = 0x80080000;
public const uint TAPE_DRIVE_SEQUENTIAL_FMKS = 0x80100000;
public const uint TAPE_DRIVE_SETMARKS = 0x80200000;
public const uint TAPE_DRIVE_SEQUENTIAL_SMKS = 0x80400000;
public const uint TAPE_DRIVE_REVERSE_POSITION = 0x80800000;
public const uint TAPE_DRIVE_SPACE_IMMEDIATE = 0x81000000;
public const uint TAPE_DRIVE_WRITE_SETMARKS = 0x82000000;
public const uint TAPE_DRIVE_WRITE_FILEMARKS = 0x84000000;
public const uint TAPE_DRIVE_WRITE_SHORT_FMKS = 0x88000000;
public const uint TAPE_DRIVE_WRITE_LONG_FMKS = 0x90000000;
public const uint TAPE_DRIVE_WRITE_MARK_IMMED = 0xA0000000;
public const uint TAPE_DRIVE_FORMAT = 0xC0000000;
public const uint TAPE_DRIVE_FORMAT_IMMEDIATE = 0x80000000;
public const uint TAPE_DRIVE_HIGH_FEATURES = 0x80000000; //mask for high features flag
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_GET_DRIVE_PARAMETERS {
public uint ECC;
public uint Compression;
public uint DataPadding;
public uint ReportSetmarks;
public uint DefaultBlockSize;
public uint MaximumBlockSize;
public uint MinimumBlockSize;
public uint MaximumPartitionCount;
public uint FeaturesLow;
public uint FeaturesHigh;
public uint EOTWarningZoneSize;
}
//
// IOCTL_TAPE_SET_DRIVE_PARAMETERS definitions
//
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_SET_DRIVE_PARAMETERS {
public uint ECC;
public uint Compression;
public uint DataPadding;
public uint ReportSetmarks;
public uint EOTWarningZoneSize;
}
//
// IOCTL_TAPE_GET_MEDIA_PARAMETERS definitions
//
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_GET_MEDIA_PARAMETERS {
public uint Capacity;
public uint Remaining;
public uint BlockSize;
public uint PartitionCount;
public byte WriteProtected;
}
//
// IOCTL_TAPE_SET_MEDIA_PARAMETERS definitions
//
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_SET_MEDIA_PARAMETERS {
public uint BlockSize;
}
//
// IOCTL_TAPE_CREATE_PARTITION definitions
//
public const uint TAPE_FIXED_PARTITIONS = 0;
public const uint TAPE_SELECT_PARTITIONS = 1;
public const uint TAPE_INITIATOR_PARTITIONS = 2;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_CREATE_PARTITION {
public uint Method;
public uint Count;
public uint Size;
}
//
// WMI Methods
//
public const uint TAPE_QUERY_DRIVE_PARAMETERS = 0;
public const uint TAPE_QUERY_MEDIA_CAPACITY = 1;
public const uint TAPE_CHECK_FOR_DRIVE_PROBLEM = 2;
public const uint TAPE_QUERY_IO_ERROR_DATA = 3;
public const uint TAPE_QUERY_DEVICE_ERROR_DATA = 4;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_WMI_OPERATIONS {
public uint Method;
public uint DataBufferSize;
public IntPtr DataBuffer;
}
//
// Type of drive errors
//
public enum TAPE_DRIVE_PROBLEM_TYPE {
TapeDriveProblemNone,
TapeDriveReadWriteWarning,
TapeDriveReadWriteError,
TapeDriveReadWarning,
TapeDriveWriteWarning,
TapeDriveReadError,
TapeDriveWriteError,
TapeDriveHardwareError,
TapeDriveUnsupportedMedia,
TapeDriveScsiConnectionError,
TapeDriveTimetoClean,
TapeDriveCleanDriveNow,
TapeDriveMediaLifeExpired,
TapeDriveSnappedTape
}
#endregion
#region Tape IOCTL Methods from ntddtape.h
/// <summary>
/// Erase the tape
/// </summary>
/// <param name="type">The type of erase operation. Valid values are <see cref="TAPE_ERASE_SHORT"/> and <see cref="TAPE_ERASE_LONG"/>.</param>
public void Erase(uint type) {
TAPE_ERASE erase = new TAPE_ERASE {
Type = type,
Immediate = 0
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(erase));
try {
Marshal.StructureToPtr(erase, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_ERASE, inBuffer, (uint)Marshal.SizeOf(erase), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine($"Erase Tape ({type}): Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Erase Tape: Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
/// <summary>
/// Prepare the tape for a specific operation
/// </summary>
/// <param name="operation">The type of prepare operation. Valid values are <see cref="TAPE_LOAD"/>, <see cref="TAPE_UNLOAD"/>, <see cref="TAPE_UNLOCK"/> and <see cref="TAPE_FORMAT"/>.</param>
public void Prepare(uint operation) {
TAPE_PREPARE prepare = new TAPE_PREPARE {
Operation = operation,
Immediate = 0
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(prepare));
try {
Marshal.StructureToPtr(prepare, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_PREPARE, inBuffer, (uint)Marshal.SizeOf(prepare), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine($"Prepare Tape ({operation}): Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Prepare Tape: Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
/// <summary>
/// Write tape marks
/// <param name="type">The type of marks to write. Valid values are <see cref="TAPE_SETMARKS"/>, <see cref="TAPE_FILEMARKS"/>, <see cref="TAPE_SHORT_FILEMARKS"/> and <see cref="TAPE_LONG_FILEMARKS"/>.</param>
/// <param name="count">The number of marks to write.</param>
public void WriteMarks(uint type, uint count) {
TAPE_WRITE_MARKS marks = new TAPE_WRITE_MARKS {
Type = type,
Count = count,
Immediate = 0
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(marks));
try {
Marshal.StructureToPtr(marks, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_WRITE_MARKS, inBuffer, (uint)Marshal.SizeOf(marks), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine("Write Marks: Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Write Marks: Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
public class TapePosition {
public uint? MethodType { get; set; }
public uint? Partition { get; set; }
public uint? OffsetLow { get; set; }
public uint? OffsetHigh { get; set; }
public int? Error { get; set; }
}
/// <summary>
/// Get the current tape position
/// </summary>
/// <param name="type">The type of position to get. Valid values are <see cref="TAPE_ABSOLUTE_POSITION"/>, <see cref="TAPE_LOGICAL_POSITION"/> and <see cref="TAPE_PSEUDO_LOGICAL_POSITION"/>.</param>
/// <param name="partition">The partition number.</param>
/// <param name="offsetLow">The low offset value.</param>
/// <param name="offsetHigh">The high offset value.</param>
/// <returns>The tape position <see cref="TapePosition"/>.</returns>
public TapePosition GetPosition(uint type, uint partition = 0, uint offsetLow = 0, uint offsetHigh = 0) {
TAPE_GET_POSITION position = new TAPE_GET_POSITION {
Type = 0,
Partition = partition,
OffsetLow = offsetLow,
OffsetHigh = offsetHigh
};
IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(position));
try {
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_POSITION, IntPtr.Zero, 0, outBuffer, (uint)Marshal.SizeOf(position), out uint bytesReturned, IntPtr.Zero);
if (result) {
position = Marshal.PtrToStructure<TAPE_GET_POSITION>(outBuffer);
Console.WriteLine("Get Position: Success");
Console.WriteLine($"Type: {position.Type}");
Console.WriteLine($"Partition: {position.Partition}");
Console.WriteLine($"OffsetLow: {position.OffsetLow}");
Console.WriteLine($"OffsetHigh: {position.OffsetHigh}");
return new TapePosition {
MethodType = position.Type,
Partition = position.Partition,
OffsetLow = position.OffsetLow,
OffsetHigh = position.OffsetHigh
};
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Get Position: Failed with error code {error}");
return new TapePosition {
Error = error
};
}
}
finally {
Marshal.FreeHGlobal(outBuffer);
}
}
/// <summary>
/// Set the tape position
/// </summary>
/// <param name="method">The method to use for setting the position. Valid values are <see cref="TAPE_REWIND"/>, <see cref="TAPE_ABSOLUTE_BLOCK"/>, <see cref="TAPE_LOGICAL_BLOCK"/>, <see cref="TAPE_PSEUDO_LOGICAL_BLOCK"/>, <see cref="TAPE_SPACE_END_OF_DATA"/>, <see cref="TAPE_SPACE_RELATIVE_BLOCKS"/>, <see cref="TAPE_SPACE_FILEMARKS"/>, <see cref="TAPE_SPACE_SEQUENTIAL_FMKS"/>, <see cref="TAPE_SPACE_SETMARKS"/> and <see cref="TAPE_SPACE_SEQUENTIAL_SMKS"/>.</param>
/// <param name="partition">The partition number.</param>
/// <param name="offset">The offset value.</param>
public void SetPosition(uint method, uint partition = 0, long offset = 0) {
TAPE_SET_POSITION position = new TAPE_SET_POSITION {
Method = method,
Partition = partition,
Offset = offset,
Immediate = 0
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(position));
try {
Marshal.StructureToPtr(position, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_SET_POSITION, inBuffer, (uint)Marshal.SizeOf(position), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine($"Set Position ({method}): Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Set Position ({method}): Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
/// <summary>
/// Get the drive parameters
/// </summary>
/// <param name="maxBlockSize">The maximum block size supported by the drive.</param>
/// <param name="minBlockSize">The minimum block size supported by the drive.</param>
public void GetDriveParameters(out uint minBlockSize, out uint maxBlockSize) {
TAPE_GET_DRIVE_PARAMETERS driveParams = new TAPE_GET_DRIVE_PARAMETERS {
ECC = 0,
Compression = 0,
DataPadding = 0,
ReportSetmarks = 0,
DefaultBlockSize = 0,
MaximumBlockSize = 0,
MinimumBlockSize = 0,
MaximumPartitionCount = 0,
FeaturesLow = 0,
FeaturesHigh = 0,
EOTWarningZoneSize = 0
};
IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(driveParams));
try {
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_DRIVE_PARAMS, IntPtr.Zero, 0, outBuffer, (uint)Marshal.SizeOf(driveParams), out uint bytesReturned, IntPtr.Zero);
if (result) {
driveParams = Marshal.PtrToStructure<TAPE_GET_DRIVE_PARAMETERS>(outBuffer);
minBlockSize = driveParams.MinimumBlockSize;
maxBlockSize = driveParams.MaximumBlockSize;
Console.WriteLine($"Drive Parameters: MinBlockSize = {minBlockSize}, MaxBlockSize = {maxBlockSize}");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Get Drive Parameters Failed with error code {error}");
minBlockSize = 0;
maxBlockSize = 0;
}
}
finally {
Marshal.FreeHGlobal(outBuffer);
}
}
/// <summary>
/// Set the drive parameters
/// </summary>
/// <param name="ecc">The error correction code (ECC) setting.</param>
/// <param name="compression">The compression setting.</param>
/// <param name="dataPadding">The data padding setting.</param>
/// <param name="reportSetmarks">The report setmarks setting.</param>
/// <param name="eotWarningZoneSize">The end-of-tape (EOT) warning zone size setting.</param>
public void SetDriveParams(uint ecc, uint compression = 0, uint dataPadding = 0, uint reportSetmarks = 0, uint eotWarningZoneSize = 0) {
TAPE_SET_DRIVE_PARAMETERS driveParams = new TAPE_SET_DRIVE_PARAMETERS {
ECC = ecc,
Compression = compression,
DataPadding = dataPadding,
ReportSetmarks = reportSetmarks,
EOTWarningZoneSize = eotWarningZoneSize
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(driveParams));
try {
Marshal.StructureToPtr(driveParams, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_SET_DRIVE_PARAMS, inBuffer, (uint)Marshal.SizeOf(driveParams), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine("Set Drive Params: Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Set Drive Params Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
public void GetMediaParams(uint capacity = 0, uint remaining = 0, uint blockSize = 0, uint partitionCount = 0, byte writeProtected = 0) {
TAPE_GET_MEDIA_PARAMETERS mediaParams = new TAPE_GET_MEDIA_PARAMETERS {
Capacity = 0,
Remaining = 0,
BlockSize = 0,
PartitionCount = 0,
WriteProtected = 0
};
IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(mediaParams));
try {
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_MEDIA_PARAMS, IntPtr.Zero, 0, outBuffer, (uint)Marshal.SizeOf(mediaParams), out uint bytesReturned, IntPtr.Zero);
if (result) {
mediaParams = Marshal.PtrToStructure<TAPE_GET_MEDIA_PARAMETERS>(outBuffer);
Console.WriteLine("Get Media Params: Success");
Console.WriteLine($"Capacity: {mediaParams.Capacity}");
Console.WriteLine($"Remaining: {mediaParams.Remaining}");
Console.WriteLine($"BlockSize: {mediaParams.BlockSize}");
Console.WriteLine($"PartitionCount: {mediaParams.PartitionCount}");
Console.WriteLine($"WriteProtected: {mediaParams.WriteProtected}");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Get Media Params Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(outBuffer);
}
}
public void SetMediaParams(uint blockSize) {
TAPE_SET_MEDIA_PARAMETERS mediaParams = new TAPE_SET_MEDIA_PARAMETERS {
BlockSize = blockSize
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(mediaParams));
try {
Marshal.StructureToPtr(mediaParams, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_SET_MEDIA_PARAMS, inBuffer, (uint)Marshal.SizeOf(mediaParams), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (!result) {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Set Block Size Failed with error code {error}");
}
else {
Console.WriteLine($"Set Block Size ({blockSize}): Success");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
public int GetStatus() {
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_STATUS, IntPtr.Zero, 0, IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine("Get Status: Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Get Status: Failed with error code {error}");
return error;
}
return 0;
}
public void CreatePartition(uint method = 0, uint count = 0, uint size = 0) {
TAPE_CREATE_PARTITION partition = new TAPE_CREATE_PARTITION {
Method = 0,
Count = 1,
Size = 0
};
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_CREATE_PARTITION, IntPtr.Zero, 0, IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (!result) {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Create Partition: Failed with error code {error}");
}
else {
Console.WriteLine("Create Partition: Success");
}
}
#endregion
}

View File

@ -0,0 +1,15 @@
namespace MaksIT.LTO.Core;
public static class WindowsVersions
{
public const uint NTDDI_WINXP = 0x05010000; // Windows XP
public const uint NTDDI_WINXPSP2 = 0x05010200; // Windows XP SP2
public const uint NTDDI_WINXPSP3 = 0x05010300; // Windows XP SP3
public const uint NTDDI_VISTA = 0x06000000; // Windows Vista
public const uint NTDDI_VISTASP1 = 0x06000100; // Windows Vista SP1
public const uint NTDDI_VISTASP2 = 0x06000200; // Windows Vista SP2
public const uint NTDDI_WIN7 = 0x06010000; // Windows 7
public const uint NTDDI_WIN8 = 0x06020000; // Windows 8
public const uint NTDDI_WINBLUE = 0x06030000; // Windows 8.1
public const uint NTDDI_WIN10 = 0x0A000000; // Windows 10
}