tape: add command to read cartridge memory (MAM)
Thsi add an additional dependency to sg3-utils (small).
This commit is contained in:
		
							
								
								
									
										1
									
								
								debian/control.in
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/control.in
									
									
									
									
										vendored
									
									
								
							@ -14,6 +14,7 @@ Depends: fonts-font-awesome,
 | 
				
			|||||||
         smartmontools,
 | 
					         smartmontools,
 | 
				
			||||||
         mtx,
 | 
					         mtx,
 | 
				
			||||||
         mt-st,
 | 
					         mt-st,
 | 
				
			||||||
 | 
						 sg3-utils,
 | 
				
			||||||
         ${misc:Depends},
 | 
					         ${misc:Depends},
 | 
				
			||||||
         ${shlibs:Depends},
 | 
					         ${shlibs:Depends},
 | 
				
			||||||
Recommends: zfsutils-linux,
 | 
					Recommends: zfsutils-linux,
 | 
				
			||||||
 | 
				
			|||||||
@ -34,6 +34,7 @@ use crate::{
 | 
				
			|||||||
        TapeDeviceInfo,
 | 
					        TapeDeviceInfo,
 | 
				
			||||||
        MediaIdFlat,
 | 
					        MediaIdFlat,
 | 
				
			||||||
        LabelUuidMap,
 | 
					        LabelUuidMap,
 | 
				
			||||||
 | 
					        MamAttribute,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    server::WorkerTask,
 | 
					    server::WorkerTask,
 | 
				
			||||||
    tape::{
 | 
					    tape::{
 | 
				
			||||||
@ -46,6 +47,7 @@ use crate::{
 | 
				
			|||||||
        mtx_load,
 | 
					        mtx_load,
 | 
				
			||||||
        mtx_unload,
 | 
					        mtx_unload,
 | 
				
			||||||
        linux_tape_device_list,
 | 
					        linux_tape_device_list,
 | 
				
			||||||
 | 
					        read_mam_attributes,
 | 
				
			||||||
        open_drive,
 | 
					        open_drive,
 | 
				
			||||||
        media_changer,
 | 
					        media_changer,
 | 
				
			||||||
        update_changer_online_status,
 | 
					        update_changer_online_status,
 | 
				
			||||||
@ -768,6 +770,32 @@ fn barcode_label_media_worker(
 | 
				
			|||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[api(
 | 
				
			||||||
 | 
					    input: {
 | 
				
			||||||
 | 
					        properties: {
 | 
				
			||||||
 | 
					            drive: {
 | 
				
			||||||
 | 
					                schema: DRIVE_NAME_SCHEMA,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    returns: {
 | 
				
			||||||
 | 
					        description: "A List of medium auxiliary memory attributes.",
 | 
				
			||||||
 | 
					        type: Array,
 | 
				
			||||||
 | 
					        items: {
 | 
				
			||||||
 | 
					            type: MamAttribute,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					)]
 | 
				
			||||||
 | 
					/// Read Medium auxiliary memory attributes (Cartridge Memory)
 | 
				
			||||||
 | 
					pub fn mam_attributes(drive: String) -> Result<Vec<MamAttribute>, Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let (config, _digest) = config::drive::config()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    read_mam_attributes(&drive_config.path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[sortable]
 | 
					#[sortable]
 | 
				
			||||||
pub const SUBDIRS: SubdirMap = &sorted!([
 | 
					pub const SUBDIRS: SubdirMap = &sorted!([
 | 
				
			||||||
    (
 | 
					    (
 | 
				
			||||||
@ -801,6 +829,11 @@ pub const SUBDIRS: SubdirMap = &sorted!([
 | 
				
			|||||||
        &Router::new()
 | 
					        &Router::new()
 | 
				
			||||||
            .put(&API_METHOD_LOAD_SLOT)
 | 
					            .put(&API_METHOD_LOAD_SLOT)
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					        "mam-attributes",
 | 
				
			||||||
 | 
					        &Router::new()
 | 
				
			||||||
 | 
					            .put(&API_METHOD_MAM_ATTRIBUTES)
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
    (
 | 
					    (
 | 
				
			||||||
        "read-label",
 | 
					        "read-label",
 | 
				
			||||||
        &Router::new()
 | 
					        &Router::new()
 | 
				
			||||||
 | 
				
			|||||||
@ -99,3 +99,15 @@ pub struct DriveListEntry {
 | 
				
			|||||||
    #[serde(skip_serializing_if="Option::is_none")]
 | 
					    #[serde(skip_serializing_if="Option::is_none")]
 | 
				
			||||||
    pub serial: Option<String>,
 | 
					    pub serial: Option<String>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[api()]
 | 
				
			||||||
 | 
					#[derive(Serialize,Deserialize)]
 | 
				
			||||||
 | 
					/// Medium auxiliary memory attributes (MAM)
 | 
				
			||||||
 | 
					pub struct MamAttribute {
 | 
				
			||||||
 | 
					    /// Attribute id
 | 
				
			||||||
 | 
					    pub id: u16,
 | 
				
			||||||
 | 
					    /// Attribute name
 | 
				
			||||||
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    /// Attribute value
 | 
				
			||||||
 | 
					    pub value: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -512,6 +512,48 @@ fn debug_scan(param: Value) -> Result<(), Error> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[api(
 | 
				
			||||||
 | 
					    input: {
 | 
				
			||||||
 | 
					        properties: {
 | 
				
			||||||
 | 
					            drive: {
 | 
				
			||||||
 | 
					                schema: DRIVE_NAME_SCHEMA,
 | 
				
			||||||
 | 
					                optional: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					             "output-format": {
 | 
				
			||||||
 | 
					                schema: OUTPUT_FORMAT,
 | 
				
			||||||
 | 
					                optional: true,
 | 
				
			||||||
 | 
					             },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					)]
 | 
				
			||||||
 | 
					/// Read Medium auxiliary memory attributes (Cartridge Memory)
 | 
				
			||||||
 | 
					fn mam_attributes(
 | 
				
			||||||
 | 
					    mut param: Value,
 | 
				
			||||||
 | 
					    rpcenv: &mut dyn RpcEnvironment,
 | 
				
			||||||
 | 
					) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let (config, _digest) = config::drive::config()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    param["drive"] = lookup_drive_name(¶m, &config)?.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let output_format = get_output_format(¶m);
 | 
				
			||||||
 | 
					    let info = &api2::tape::drive::API_METHOD_MAM_ATTRIBUTES;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut data = match info.handler {
 | 
				
			||||||
 | 
					        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
 | 
				
			||||||
 | 
					        _ => unreachable!(),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let options = default_table_format_options()
 | 
				
			||||||
 | 
					        .column(ColumnConfig::new("id"))
 | 
				
			||||||
 | 
					        .column(ColumnConfig::new("name"))
 | 
				
			||||||
 | 
					        .column(ColumnConfig::new("value"))
 | 
				
			||||||
 | 
					        ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    format_and_print_result_full(&mut data, info.returns, &output_format, &options);
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[api(
 | 
					#[api(
 | 
				
			||||||
   input: {
 | 
					   input: {
 | 
				
			||||||
        properties: {
 | 
					        properties: {
 | 
				
			||||||
@ -593,6 +635,11 @@ fn main() {
 | 
				
			|||||||
            CliCommand::new(&API_METHOD_READ_LABEL)
 | 
					            CliCommand::new(&API_METHOD_READ_LABEL)
 | 
				
			||||||
                .completion_cb("drive", complete_drive_name)
 | 
					                .completion_cb("drive", complete_drive_name)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        .insert(
 | 
				
			||||||
 | 
					            "mam",
 | 
				
			||||||
 | 
					            CliCommand::new(&API_METHOD_MAM_ATTRIBUTES)
 | 
				
			||||||
 | 
					                .completion_cb("drive", complete_drive_name)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        .insert(
 | 
					        .insert(
 | 
				
			||||||
            "label",
 | 
					            "label",
 | 
				
			||||||
            CliCommand::new(&API_METHOD_LABEL_MEDIA)
 | 
					            CliCommand::new(&API_METHOD_LABEL_MEDIA)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										175
									
								
								src/tape/drive/mam.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								src/tape/drive/mam.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,175 @@
 | 
				
			|||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use std::convert::TryInto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use anyhow::{bail, format_err, Error};
 | 
				
			||||||
 | 
					use endian_trait::Endian;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use proxmox::tools::io::ReadExt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::api2::types::MamAttribute;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Read Medium auxiliary memory attributes (MAM)
 | 
				
			||||||
 | 
					// see IBM SCSI reference: https://www-01.ibm.com/support/docview.wss?uid=ssg1S7003556&aid=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Endian)]
 | 
				
			||||||
 | 
					#[repr(C,packed)]
 | 
				
			||||||
 | 
					struct MamAttributeHeader {
 | 
				
			||||||
 | 
					    id: u16,
 | 
				
			||||||
 | 
					    flags: u8,
 | 
				
			||||||
 | 
					    len: u16,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum MamFormat {
 | 
				
			||||||
 | 
					    BINARY,
 | 
				
			||||||
 | 
					    ASCII,
 | 
				
			||||||
 | 
					    DEC,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static MAM_ATTRIBUTES: &'static [ (u16, u16, MamFormat, &'static str) ] = &[
 | 
				
			||||||
 | 
					    (0x00_00, 8, MamFormat::DEC, "Remaining Capacity In Partition"),
 | 
				
			||||||
 | 
					    (0x00_01, 8, MamFormat::DEC, "Maximum Capacity In Partition"),
 | 
				
			||||||
 | 
					    (0x00_02, 8, MamFormat::BINARY, "Tapealert Flags"),
 | 
				
			||||||
 | 
					    (0x00_03, 8, MamFormat::DEC, "Load Count"),
 | 
				
			||||||
 | 
					    (0x00_04, 8, MamFormat::DEC, "MAM Space Remaining"),
 | 
				
			||||||
 | 
					    (0x00_05, 8, MamFormat::ASCII, "Assigning Organization"),
 | 
				
			||||||
 | 
					    (0x00_06, 1, MamFormat::BINARY, "Formatted Density Code"),
 | 
				
			||||||
 | 
					    (0x00_07, 2, MamFormat::DEC, "Initialization Count"),
 | 
				
			||||||
 | 
					    (0x00_09, 4, MamFormat::BINARY, "Volume Change Reference"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (0x02_0A, 40, MamFormat::ASCII, "Device Vendor/Serial Number at Last Load"),
 | 
				
			||||||
 | 
					    (0x02_0B, 40, MamFormat::ASCII, "Device Vendor/Serial Number at Load-1"),
 | 
				
			||||||
 | 
					    (0x02_0C, 40, MamFormat::ASCII, "Device Vendor/Serial Number at Load-2"),
 | 
				
			||||||
 | 
					    (0x02_0D, 40, MamFormat::ASCII, "Device Vendor/Serial Number at Load-3"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (0x02_20, 8, MamFormat::DEC, "Total MBytes Written in Medium Life"),
 | 
				
			||||||
 | 
					    (0x02_21, 8, MamFormat::DEC, "Total MBytes Read In Medium Life"),
 | 
				
			||||||
 | 
					    (0x02_22, 8, MamFormat::DEC, "Total MBytes Written in Current Load"),
 | 
				
			||||||
 | 
					    (0x02_23, 8, MamFormat::DEC, "Total MBytes Read in Current/Last Load"),
 | 
				
			||||||
 | 
					    (0x02_24, 8, MamFormat::BINARY, "Logical Position of First Encrypted Block"),
 | 
				
			||||||
 | 
					    (0x02_25, 8, MamFormat::BINARY, "Logical Position of First Unencrypted Block After the First Encrypted Block"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (0x04_00, 8, MamFormat::ASCII, "Medium Manufacturer"),
 | 
				
			||||||
 | 
					    (0x04_01, 32, MamFormat::ASCII, "Medium Serial Number"),
 | 
				
			||||||
 | 
					    (0x04_02, 4, MamFormat::DEC, "Medium Length"),
 | 
				
			||||||
 | 
					    (0x04_03, 4, MamFormat::DEC, "Medium Width"),
 | 
				
			||||||
 | 
					    (0x04_04, 8, MamFormat::ASCII, "Assigning Organization"),
 | 
				
			||||||
 | 
					    (0x04_05, 1, MamFormat::BINARY, "Medium Density Code"),
 | 
				
			||||||
 | 
					    (0x04_06, 8, MamFormat::ASCII, "Medium Manufacture Date"),
 | 
				
			||||||
 | 
					    (0x04_07, 8, MamFormat::DEC, "MAM Capacity"),
 | 
				
			||||||
 | 
					    (0x04_08, 1, MamFormat::BINARY, "Medium Type"),
 | 
				
			||||||
 | 
					    (0x04_09, 2, MamFormat::BINARY, "Medium Type Information"),
 | 
				
			||||||
 | 
					    (0x04_0B, 10, MamFormat::BINARY, "Supported Density Codes"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (0x08_00, 8, MamFormat::ASCII, "Application Vendor"),
 | 
				
			||||||
 | 
					    (0x08_01, 32, MamFormat::ASCII, "Application Name"),
 | 
				
			||||||
 | 
					    (0x08_02, 8, MamFormat::ASCII, "Application Version"),
 | 
				
			||||||
 | 
					    (0x08_03, 160, MamFormat::ASCII, "User Medium Text Label"),
 | 
				
			||||||
 | 
					    (0x08_04, 12, MamFormat::ASCII, "Date And Time Last Written"),
 | 
				
			||||||
 | 
					    (0x08_05, 1, MamFormat::BINARY, "Text Localization Identifer"),
 | 
				
			||||||
 | 
					    (0x08_06, 32, MamFormat::ASCII, "Barcode"),
 | 
				
			||||||
 | 
					    (0x08_07, 80, MamFormat::ASCII, "Owning Host Textual Name"),
 | 
				
			||||||
 | 
					    (0x08_08, 160, MamFormat::ASCII, "Media Pool"),
 | 
				
			||||||
 | 
					    (0x08_0B, 16, MamFormat::ASCII, "Application Format Version"),
 | 
				
			||||||
 | 
					    (0x08_0C, 50, MamFormat::ASCII, "Volume Coherency Information"),
 | 
				
			||||||
 | 
					    (0x08_20, 36, MamFormat::ASCII, "Medium Globally Unique Identifer"),
 | 
				
			||||||
 | 
					    (0x08_21, 36, MamFormat::ASCII, "Media Pool Globally Unique Identifer"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (0x10_00, 28,  MamFormat::BINARY, "Unique Cartridge Identify (UCI)"),
 | 
				
			||||||
 | 
					    (0x10_01, 24,  MamFormat::BINARY, "Alternate Unique Cartridge Identify (Alt-UCI)"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lazy_static::lazy_static!{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static ref MAM_ATTRIBUTE_NAMES: HashMap<u16, &'static (u16, u16, MamFormat, &'static str)> = {
 | 
				
			||||||
 | 
					        let mut map = HashMap::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for entry in MAM_ATTRIBUTES {
 | 
				
			||||||
 | 
					            map.insert(entry.0, entry);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        map
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn read_tape_mam(path: &str) -> Result<Vec<u8>, Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut command = std::process::Command::new("sg_raw");
 | 
				
			||||||
 | 
					    command.args(&[
 | 
				
			||||||
 | 
					        "-r", "32k",
 | 
				
			||||||
 | 
					        "-o", "-",
 | 
				
			||||||
 | 
					        path,
 | 
				
			||||||
 | 
					        "8c", "00", "00", "00", "00", "00", "00", "00",
 | 
				
			||||||
 | 
					        "00", "00", // first attribute
 | 
				
			||||||
 | 
					        "00", "00", "8f", "ff", // alloc len
 | 
				
			||||||
 | 
					        "00", "00",
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let output = command.output()
 | 
				
			||||||
 | 
					        .map_err(|err| format_err!("failed to execute {:?} - {}", command, err))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(output.stdout)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn read_mam_attributes(path: &str) -> Result<Vec<MamAttribute>, Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let data = read_tape_mam(path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut reader = &data[..];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let data_len: u32 = unsafe { reader.read_be_value()? };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let expected_len = data_len as usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if reader.len() != expected_len {
 | 
				
			||||||
 | 
					        bail!("read_mam_attributes: got unexpected data len ({} != {})", reader.len(), expected_len);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut list = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loop {
 | 
				
			||||||
 | 
					        if reader.is_empty() {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let head: MamAttributeHeader =  unsafe { reader.read_be_value()? };
 | 
				
			||||||
 | 
					        //println!("GOT ID {:04X} {:08b} {}", head.id, head.flags, head.len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let head_id = head.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let data = if head.len > 0 {
 | 
				
			||||||
 | 
					            reader.read_exact_allocated(head.len as usize)?
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Vec::new()
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(info) = MAM_ATTRIBUTE_NAMES.get(&head_id) {
 | 
				
			||||||
 | 
					            if info.1 == head.len {
 | 
				
			||||||
 | 
					                let value = match info.2 {
 | 
				
			||||||
 | 
					                    MamFormat::ASCII => String::from_utf8_lossy(&data).to_string(),
 | 
				
			||||||
 | 
					                    MamFormat::DEC => {
 | 
				
			||||||
 | 
					                        if info.1 == 2 {
 | 
				
			||||||
 | 
					                            format!("{}", u16::from_be_bytes(data[0..2].try_into()?))
 | 
				
			||||||
 | 
					                        } else if info.1 == 4 {
 | 
				
			||||||
 | 
					                            format!("{}", u32::from_be_bytes(data[0..4].try_into()?))
 | 
				
			||||||
 | 
					                        } else if info.1 == 8 {
 | 
				
			||||||
 | 
					                            format!("{}", u64::from_be_bytes(data[0..8].try_into()?))
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            unreachable!();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    MamFormat::BINARY => proxmox::tools::digest_to_hex(&data),
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                list.push(MamAttribute {
 | 
				
			||||||
 | 
					                    id: head_id,
 | 
				
			||||||
 | 
					                    name: info.3.to_string(),
 | 
				
			||||||
 | 
					                    value,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                eprintln!("read_mam_attributes: got starnge data len for id {:04X}", head_id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // skip unknown IDs
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Ok(list)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -2,6 +2,9 @@ mod virtual_tape;
 | 
				
			|||||||
mod linux_mtio;
 | 
					mod linux_mtio;
 | 
				
			||||||
mod linux_tape;
 | 
					mod linux_tape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod mam;
 | 
				
			||||||
 | 
					pub use mam::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod linux_list_drives;
 | 
					mod linux_list_drives;
 | 
				
			||||||
pub use linux_list_drives::*;
 | 
					pub use linux_list_drives::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user