]> git.proxmox.com Git - proxmox-backup.git/blob - pbs-tape/src/sg_tape/volume_statistics.rs
update to first proxmox crate split
[proxmox-backup.git] / pbs-tape / src / sg_tape / volume_statistics.rs
1 use std::io::Read;
2 use std::os::unix::io::AsRawFd;
3
4 use anyhow::{bail, format_err, Error};
5 use endian_trait::Endian;
6
7 use proxmox_io::ReadExt;
8
9 use pbs_api_types::Lp17VolumeStatistics;
10
11 use crate::sgutils2::SgRaw;
12
13 /// SCSI command to query volume statistics
14 ///
15 /// CDB: LOG SENSE / LP17h Volume Statistics
16 ///
17 /// The Volume Statistics log page is included in Ultrium 5 and later
18 /// drives.
19 pub fn read_volume_statistics<F: AsRawFd>(file: &mut F) -> Result<Lp17VolumeStatistics, Error> {
20
21 let data = sg_read_volume_statistics(file)?;
22
23 decode_volume_statistics(&data)
24 }
25
26 fn sg_read_volume_statistics<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> {
27
28 let alloc_len: u16 = 8192;
29 let mut sg_raw = SgRaw::new(file, alloc_len as usize)?;
30
31 let mut cmd = Vec::new();
32 cmd.push(0x4D); // LOG SENSE
33 cmd.push(0);
34 cmd.push((1<<6) | 0x17); // Volume Statistics log page
35 cmd.push(0); // Subpage 0
36 cmd.push(0);
37 cmd.push(0);
38 cmd.push(0);
39 cmd.extend(&alloc_len.to_be_bytes()); // alloc len
40 cmd.push(0u8); // control byte
41
42 sg_raw.do_command(&cmd)
43 .map_err(|err| format_err!("read tape volume statistics failed - {}", err))
44 .map(|v| v.to_vec())
45 }
46
47 #[repr(C, packed)]
48 #[derive(Endian)]
49 struct LpParameterHeader {
50 parameter_code: u16,
51 control: u8,
52 parameter_len: u8,
53 }
54
55 fn decode_volume_statistics(data: &[u8]) -> Result<Lp17VolumeStatistics, Error> {
56
57
58 let read_be_counter = |reader: &mut &[u8], len: u8| {
59 let len = len as usize;
60 if len == 0 || len > 8 {
61 bail!("invalid conter size '{}'", len);
62 }
63 let mut buffer = [0u8; 8];
64 reader.read_exact(&mut buffer[..len])?;
65
66 let value = buffer
67 .iter()
68 .take(len)
69 .fold(0, |value, curr| (value << 8) | *curr as u64);
70
71 Ok(value)
72 };
73
74 proxmox_lang::try_block!({
75 if !((data[0] & 0x7f) == 0x17 && data[1] == 0) {
76 bail!("invalid response");
77 }
78
79 let mut reader = &data[2..];
80
81 let page_len: u16 = unsafe { reader.read_be_value()? };
82
83 let page_len = page_len as usize;
84
85 if (page_len + 4) > data.len() {
86 bail!("invalid page length");
87 } else {
88 // Note: Quantum hh7 returns the allocation_length instead of real data_len
89 reader = &data[4..page_len+4];
90 }
91
92 let mut stat = Lp17VolumeStatistics::default();
93 let mut page_valid = false;
94
95 loop {
96 if reader.is_empty() {
97 break;
98 }
99 let head: LpParameterHeader = unsafe { reader.read_be_value()? };
100
101 match head.parameter_code {
102 0x0000 => {
103 let value: u64 = read_be_counter(&mut reader, head.parameter_len)?;
104 if value == 0 {
105 bail!("page-valid flag not set");
106 }
107 page_valid = true;
108 }
109 0x0001 => {
110 stat.volume_mounts =
111 read_be_counter(&mut reader, head.parameter_len)?;
112 }
113 0x0002 => {
114 stat.volume_datasets_written =
115 read_be_counter(&mut reader, head.parameter_len)?;
116 }
117 0x0003 => {
118 stat.volume_recovered_write_data_errors =
119 read_be_counter(&mut reader, head.parameter_len)?;
120 }
121 0x0004 => {
122 stat.volume_unrecovered_write_data_errors =
123 read_be_counter(&mut reader, head.parameter_len)?;
124 }
125 0x0005 => {
126 stat.volume_write_servo_errors =
127 read_be_counter(&mut reader, head.parameter_len)?;
128 }
129 0x0006 => {
130 stat.volume_unrecovered_write_servo_errors =
131 read_be_counter(&mut reader, head.parameter_len)?;
132 }
133 0x0007 => {
134 stat.volume_datasets_read =
135 read_be_counter(&mut reader, head.parameter_len)?;
136 }
137 0x0008 => {
138 stat.volume_recovered_read_errors =
139 read_be_counter(&mut reader, head.parameter_len)?;
140 }
141 0x0009 => {
142 stat.volume_unrecovered_read_errors =
143 read_be_counter(&mut reader, head.parameter_len)?;
144 }
145 0x000C => {
146 stat.last_mount_unrecovered_write_errors =
147 read_be_counter(&mut reader, head.parameter_len)?;
148 }
149 0x000D => {
150 stat.last_mount_unrecovered_read_errors =
151 read_be_counter(&mut reader, head.parameter_len)?;
152 }
153 0x000E => {
154 stat.last_mount_bytes_written =
155 read_be_counter(&mut reader, head.parameter_len)? * 1_000_000;
156 }
157 0x000F => {
158 stat.last_mount_bytes_read =
159 read_be_counter(&mut reader, head.parameter_len)? * 1_000_000;
160 }
161 0x0010 => {
162 stat.lifetime_bytes_written =
163 read_be_counter(&mut reader, head.parameter_len)? * 1_000_000;
164 }
165 0x0011 => {
166 stat.lifetime_bytes_read =
167 read_be_counter(&mut reader, head.parameter_len)? * 1_000_000;
168 }
169 0x0012 => {
170 stat.last_load_write_compression_ratio =
171 read_be_counter(&mut reader, head.parameter_len)?;
172 }
173 0x0013 => {
174 stat.last_load_read_compression_ratio =
175 read_be_counter(&mut reader, head.parameter_len)?;
176 }
177 0x0014 => {
178 stat.medium_mount_time =
179 read_be_counter(&mut reader, head.parameter_len)?;
180 }
181 0x0015 => {
182 stat.medium_ready_time =
183 read_be_counter(&mut reader, head.parameter_len)?;
184 }
185 0x0016 => {
186 stat.total_native_capacity =
187 read_be_counter(&mut reader, head.parameter_len)? * 1_000_000;
188 }
189 0x0017 => {
190 stat.total_used_native_capacity =
191 read_be_counter(&mut reader, head.parameter_len)? * 1_000_000;
192 }
193 0x0040 => {
194 let data = reader.read_exact_allocated(head.parameter_len as usize)?;
195 stat.serial = String::from_utf8_lossy(&data).to_string();
196 }
197 0x0080 => {
198 let value = read_be_counter(&mut reader, head.parameter_len)?;
199 if value == 1 {
200 stat.write_protect = true;
201 }
202 }
203 0x0081 => {
204 let value = read_be_counter(&mut reader, head.parameter_len)?;
205 if value == 1 {
206 stat.worm = true;
207 }
208 }
209 0x0101 => {
210 stat.beginning_of_medium_passes =
211 read_be_counter(&mut reader, head.parameter_len)?;
212 }
213 0x0102 => {
214 stat.middle_of_tape_passes =
215 read_be_counter(&mut reader, head.parameter_len)?;
216 }
217 _ => {
218 reader.read_exact_allocated(head.parameter_len as usize)?;
219 }
220 }
221 }
222
223 if !page_valid {
224 bail!("missing page-valid parameter");
225 }
226
227 Ok(stat)
228
229 }).map_err(|err| format_err!("decode volume statistics failed - {}", err))
230 }