]> git.proxmox.com Git - proxmox-backup.git/blob - src/tape/changer/sg_pt_changer.rs
tape: changer: sg_pt: query element types separately
[proxmox-backup.git] / src / tape / changer / sg_pt_changer.rs
1 //! SCSI changer implementation using libsgutil2
2
3 use std::os::unix::prelude::AsRawFd;
4 use std::io::Read;
5 use std::collections::HashMap;
6 use std::path::Path;
7 use std::fs::{OpenOptions, File};
8
9 use anyhow::{bail, format_err, Error};
10 use endian_trait::Endian;
11
12 use proxmox::tools::io::ReadExt;
13
14 use crate::{
15 tape::{
16 changer::{
17 DriveStatus,
18 ElementStatus,
19 StorageElementStatus,
20 TransportElementStatus,
21 MtxStatus,
22 },
23 },
24 tools::sgutils2::{
25 SgRaw,
26 SENSE_KEY_NO_SENSE,
27 SENSE_KEY_RECOVERED_ERROR,
28 SENSE_KEY_UNIT_ATTENTION,
29 SENSE_KEY_NOT_READY,
30 InquiryInfo,
31 ScsiError,
32 scsi_ascii_to_string,
33 scsi_inquiry,
34 },
35 api2::types::ScsiTapeChanger,
36 };
37
38 const SCSI_CHANGER_DEFAULT_TIMEOUT: usize = 60*5; // 5 minutes
39
40 /// Initialize element status (Inventory)
41 pub fn initialize_element_status<F: AsRawFd>(file: &mut F) -> Result<(), Error> {
42
43 let mut sg_raw = SgRaw::new(file, 64)?;
44
45 // like mtx(1), set a very long timeout (30 minutes)
46 sg_raw.set_timeout(30*60);
47
48 let mut cmd = Vec::new();
49 cmd.extend(&[0x07, 0, 0, 0, 0, 0]); // INITIALIZE ELEMENT STATUS (07h)
50
51 sg_raw.do_command(&cmd)
52 .map_err(|err| format_err!("initializte element status (07h) failed - {}", err))?;
53
54 Ok(())
55 }
56
57 #[repr(C, packed)]
58 #[derive(Endian)]
59 struct AddressAssignmentPage {
60 data_len: u8,
61 reserved1: u8,
62 reserved2: u8,
63 block_descriptor_len: u8,
64
65 page_code: u8,
66 additional_page_len: u8,
67 first_transport_element_address: u16,
68 transport_element_count: u16,
69 first_storage_element_address: u16,
70 storage_element_count: u16,
71 first_import_export_element_address: u16,
72 import_export_element_count: u16,
73 first_tranfer_element_address: u16,
74 transfer_element_count: u16,
75 reserved22: u8,
76 reserved23: u8,
77 }
78
79 /// Execute scsi commands, optionally repeat the command until
80 /// successful (sleep 1 second between invovations)
81 ///
82 /// Any Sense key other than NO_SENSE, RECOVERED_ERROR, NOT_READY and
83 /// UNIT_ATTENTION aborts the loop and returns an error. If the device
84 /// reports "Not Ready - becoming ready", we wait up to 5 minutes.
85 ///
86 /// Skipped errors are printed on stderr.
87 fn execute_scsi_command<F: AsRawFd>(
88 sg_raw: &mut SgRaw<F>,
89 cmd: &[u8],
90 error_prefix: &str,
91 retry: bool,
92 ) -> Result<Vec<u8>, Error> {
93
94 let start = std::time::SystemTime::now();
95
96 let mut last_msg: Option<String> = None;
97
98 let mut timeout = std::time::Duration::new(5, 0); // short timeout by default
99
100 loop {
101 match sg_raw.do_command(&cmd) {
102 Ok(data) => return Ok(data.to_vec()),
103 Err(err) => {
104 if !retry {
105 bail!("{} failed: {}", error_prefix, err);
106 }
107 if let ScsiError::Sense(ref sense) = err {
108
109 if sense.sense_key == SENSE_KEY_NO_SENSE ||
110 sense.sense_key == SENSE_KEY_RECOVERED_ERROR ||
111 sense.sense_key == SENSE_KEY_UNIT_ATTENTION ||
112 sense.sense_key == SENSE_KEY_NOT_READY
113 {
114 let msg = err.to_string();
115 if let Some(ref last) = last_msg {
116 if &msg != last {
117 eprintln!("{}", err);
118 last_msg = Some(msg);
119 }
120 } else {
121 eprintln!("{}", err);
122 last_msg = Some(msg);
123 }
124
125 // Not Ready - becoming ready
126 if sense.sense_key == SENSE_KEY_NOT_READY && sense.asc == 0x04 && sense.ascq == 1 {
127 // wait up to 5 minutes, long enough to finish inventorize
128 timeout = std::time::Duration::new(5*60, 0);
129 }
130
131 if start.elapsed()? > timeout {
132 bail!("{} failed: {}", error_prefix, err);
133 }
134
135 std::thread::sleep(std::time::Duration::new(1, 0));
136 continue; // try again
137 }
138 }
139 }
140 }
141 }
142 }
143
144
145 fn read_element_address_assignment<F: AsRawFd>(
146 file: &mut F,
147 ) -> Result<AddressAssignmentPage, Error> {
148
149 let allocation_len: u8 = u8::MAX;
150 let mut sg_raw = SgRaw::new(file, allocation_len as usize)?;
151 sg_raw.set_timeout(SCSI_CHANGER_DEFAULT_TIMEOUT);
152
153 let mut cmd = Vec::new();
154 cmd.push(0x1A); // MODE SENSE6 (1Ah)
155 cmd.push(0x08); // DBD=1 (The Disable Block Descriptors)
156 cmd.push(0x1D); // Element Address Assignment Page
157 cmd.push(0);
158 cmd.push(allocation_len); // allocation len
159 cmd.push(0); //control
160
161 let data = execute_scsi_command(&mut sg_raw, &cmd, "read element address assignment", true)?;
162
163 proxmox::try_block!({
164 let mut reader = &data[..];
165 let page: AddressAssignmentPage = unsafe { reader.read_be_value()? };
166
167 if page.data_len != 23 {
168 bail!("got unexpected page len ({} != 23)", page.data_len);
169 }
170
171 Ok(page)
172 }).map_err(|err: Error| format_err!("decode element address assignment page failed - {}", err))
173 }
174
175 fn scsi_move_medium_cdb(
176 medium_transport_address: u16,
177 source_element_address: u16,
178 destination_element_address: u16,
179 ) -> Vec<u8> {
180
181 let mut cmd = Vec::new();
182 cmd.push(0xA5); // MOVE MEDIUM (A5h)
183 cmd.push(0); // reserved
184 cmd.extend(&medium_transport_address.to_be_bytes());
185 cmd.extend(&source_element_address.to_be_bytes());
186 cmd.extend(&destination_element_address.to_be_bytes());
187 cmd.push(0); // reserved
188 cmd.push(0); // reserved
189 cmd.push(0); // Invert=0
190 cmd.push(0); // control
191
192 cmd
193 }
194
195 /// Load media from storage slot into drive
196 pub fn load_slot(
197 file: &mut File,
198 from_slot: u64,
199 drivenum: u64,
200 ) -> Result<(), Error> {
201 let status = read_element_status(file)?;
202
203 let transport_address = status.transport_address();
204 let source_element_address = status.slot_address(from_slot)?;
205 let drive_element_address = status.drive_address(drivenum)?;
206
207 let cmd = scsi_move_medium_cdb(
208 transport_address,
209 source_element_address,
210 drive_element_address,
211 );
212
213 let mut sg_raw = SgRaw::new(file, 64)?;
214 sg_raw.set_timeout(SCSI_CHANGER_DEFAULT_TIMEOUT);
215
216 sg_raw.do_command(&cmd)
217 .map_err(|err| format_err!("load drive failed - {}", err))?;
218
219 Ok(())
220 }
221
222 /// Unload media from drive into a storage slot
223 pub fn unload(
224 file: &mut File,
225 to_slot: u64,
226 drivenum: u64,
227 ) -> Result<(), Error> {
228
229 let status = read_element_status(file)?;
230
231 let transport_address = status.transport_address();
232 let target_element_address = status.slot_address(to_slot)?;
233 let drive_element_address = status.drive_address(drivenum)?;
234
235 let cmd = scsi_move_medium_cdb(
236 transport_address,
237 drive_element_address,
238 target_element_address,
239 );
240
241 let mut sg_raw = SgRaw::new(file, 64)?;
242 sg_raw.set_timeout(SCSI_CHANGER_DEFAULT_TIMEOUT);
243
244 sg_raw.do_command(&cmd)
245 .map_err(|err| format_err!("unload drive failed - {}", err))?;
246
247 Ok(())
248 }
249
250 /// Transfer medium from one storage slot to another
251 pub fn transfer_medium<F: AsRawFd>(
252 file: &mut F,
253 from_slot: u64,
254 to_slot: u64,
255 ) -> Result<(), Error> {
256
257 let status = read_element_status(file)?;
258
259 let transport_address = status.transport_address();
260 let source_element_address = status.slot_address(from_slot)?;
261 let target_element_address = status.slot_address(to_slot)?;
262
263 let cmd = scsi_move_medium_cdb(
264 transport_address,
265 source_element_address,
266 target_element_address,
267 );
268
269 let mut sg_raw = SgRaw::new(file, 64)?;
270 sg_raw.set_timeout(SCSI_CHANGER_DEFAULT_TIMEOUT);
271
272 sg_raw.do_command(&cmd)
273 .map_err(|err| {
274 format_err!("transfer medium from slot {} to slot {} failed - {}",
275 from_slot, to_slot, err)
276 })?;
277
278 Ok(())
279 }
280
281 #[repr(u8)]
282 #[derive(Clone, Copy)]
283 enum ElementType {
284 MediumTransport = 1,
285 Storage = 2,
286 ImportExport = 3,
287 DataTransfer = 4,
288 }
289
290 fn scsi_read_element_status_cdb(
291 start_element_address: u16,
292 number_of_elements: u16,
293 element_type: ElementType,
294 allocation_len: u32,
295 ) -> Vec<u8> {
296
297 let mut cmd = Vec::new();
298 cmd.push(0xB8); // READ ELEMENT STATUS (B8h)
299 cmd.push(1u8<<4 | (element_type as u8)); // volume tags and given type
300 cmd.extend(&start_element_address.to_be_bytes());
301
302 cmd.extend(&number_of_elements.to_be_bytes());
303 let byte6 = match element_type {
304 ElementType::DataTransfer => 0b001, // Mixed=0,CurData=0,DVCID=1
305 _ => 0b000, // Mixed=0,CurData=0,DVCID=0
306 };
307 cmd.push(byte6);
308 cmd.extend(&allocation_len.to_be_bytes()[1..4]);
309 cmd.push(0);
310 cmd.push(0);
311
312 cmd
313 }
314
315 // query a single element type from the changer
316 fn get_element<F: AsRawFd>(
317 inquiry: &InquiryInfo,
318 sg_raw: &mut SgRaw<F>,
319 element_type: ElementType,
320 allocation_len: u32,
321 mut retry: bool,
322 ) -> Result<DecodedStatusPage, Error> {
323
324 let mut start_element_address = 0;
325 let number_of_elements: u16 = 1000; // some changers limit the query
326
327 let mut result = DecodedStatusPage {
328 last_element_address: None,
329 transports: Vec::new(),
330 drives: Vec::new(),
331 storage_slots: Vec::new(),
332 import_export_slots: Vec::new(),
333 };
334
335 loop {
336 let cmd = scsi_read_element_status_cdb(start_element_address, number_of_elements, element_type, allocation_len);
337
338 let data = execute_scsi_command(sg_raw, &cmd, "read element status (B8h)", retry)?;
339
340 let page = decode_element_status_page(&inquiry, &data, start_element_address)?;
341
342 retry = false; // only retry the first command
343
344 let returned_number_of_elements = page.transports.len()
345 + page.drives.len()
346 + page.storage_slots.len()
347 + page.import_export_slots.len();
348
349 result.transports.extend(page.transports);
350 result.drives.extend(page.drives);
351 result.storage_slots.extend(page.storage_slots);
352 result.import_export_slots.extend(page.import_export_slots);
353 result.last_element_address = page.last_element_address;
354
355 if let Some(last_element_address) = page.last_element_address {
356 if last_element_address < start_element_address {
357 bail!("got strange element address");
358 }
359 if returned_number_of_elements >= (number_of_elements as usize) {
360 start_element_address = last_element_address + 1;
361 continue; // we possibly have to read additional elements
362 }
363 }
364 break;
365 }
366
367 Ok(result)
368 }
369
370 /// Read element status.
371 pub fn read_element_status<F: AsRawFd>(file: &mut F) -> Result<MtxStatus, Error> {
372
373 let inquiry = scsi_inquiry(file)?;
374
375 if inquiry.peripheral_type != 8 {
376 bail!("wrong device type (not a scsi changer device)");
377 }
378
379 // first, request address assignment (used for sanity checks)
380 let setup = read_element_address_assignment(file)?;
381
382 let allocation_len: u32 = 0x10000;
383
384 let mut sg_raw = SgRaw::new(file, allocation_len as usize)?;
385 sg_raw.set_timeout(SCSI_CHANGER_DEFAULT_TIMEOUT);
386
387 let mut drives = Vec::new();
388 let mut storage_slots = Vec::new();
389 let mut import_export_slots = Vec::new();
390 let mut transports = Vec::new();
391
392 let page = get_element(&inquiry, &mut sg_raw, ElementType::Storage, allocation_len, true)?;
393 storage_slots.extend(page.storage_slots);
394
395 let page = get_element(&inquiry, &mut sg_raw, ElementType::ImportExport, allocation_len, false)?;
396 import_export_slots.extend(page.import_export_slots);
397
398 let page = get_element(&inquiry, &mut sg_raw, ElementType::DataTransfer, allocation_len, false)?;
399 drives.extend(page.drives);
400
401 let page = get_element(&inquiry, &mut sg_raw, ElementType::MediumTransport, allocation_len, false)?;
402 transports.extend(page.transports);
403
404 if (setup.transport_element_count as usize) != transports.len() {
405 bail!("got wrong number of transport elements");
406 }
407 if (setup.storage_element_count as usize) != storage_slots.len() {
408 bail!("got wrong number of storage elements");
409 }
410 if (setup.import_export_element_count as usize) != import_export_slots.len() {
411 bail!("got wrong number of import/export elements");
412 }
413 if (setup.transfer_element_count as usize) != drives.len() {
414 bail!("got wrong number of transfer elements");
415 }
416
417 // create same virtual slot order as mtx(1)
418 // - storage slots first
419 // - import export slots at the end
420 let mut slots = storage_slots;
421 slots.extend(import_export_slots);
422
423 let mut status = MtxStatus { transports, drives, slots };
424
425 // sanity checks
426 if status.drives.is_empty() {
427 bail!("no data transfer elements reported");
428 }
429 if status.slots.is_empty() {
430 bail!("no storage elements reported");
431 }
432
433 // compute virtual storage slot to element_address map
434 let mut slot_map = HashMap::new();
435 for (i, slot) in status.slots.iter().enumerate() {
436 slot_map.insert(slot.element_address, (i + 1) as u64);
437 }
438
439 // translate element addresses in loaded_lot
440 for drive in status.drives.iter_mut() {
441 if let Some(source_address) = drive.loaded_slot {
442 let source_address = source_address as u16;
443 drive.loaded_slot = slot_map.get(&source_address).map(|v| *v);
444 }
445 }
446
447 Ok(status)
448 }
449
450 /// Read status and map import-export slots from config
451 pub fn status(config: &ScsiTapeChanger) -> Result<MtxStatus, Error> {
452 let path = &config.path;
453
454 let mut file = open(path)
455 .map_err(|err| format_err!("error opening '{}': {}", path, err))?;
456 let mut status = read_element_status(&mut file)
457 .map_err(|err| format_err!("error reading element status: {}", err))?;
458
459 status.mark_import_export_slots(&config)?;
460
461 Ok(status)
462 }
463
464
465 #[repr(C, packed)]
466 #[derive(Endian)]
467 struct ElementStatusHeader {
468 first_element_address_reported: u16,
469 number_of_elements_available: u16,
470 reserved: u8,
471 byte_count_of_report_available: [u8;3],
472 }
473
474 #[repr(C, packed)]
475 #[derive(Endian)]
476 struct SubHeader {
477 element_type_code: u8,
478 flags: u8,
479 descriptor_length: u16,
480 reserved: u8,
481 byte_count_of_descriptor_data_available: [u8;3],
482 }
483
484 impl SubHeader {
485
486 fn parse_optional_volume_tag<R: Read>(
487 &self,
488 reader: &mut R,
489 full: bool,
490 ) -> Result<Option<String>, Error> {
491
492 if (self.flags & 128) != 0 { // has PVolTag
493 let tmp = reader.read_exact_allocated(36)?;
494 if full {
495 let volume_tag = scsi_ascii_to_string(&tmp);
496 return Ok(Some(volume_tag));
497 }
498 }
499 Ok(None)
500 }
501
502 // AFAIK, tape changer do not use AlternateVolumeTag
503 // but parse anyways, just to be sure
504 fn skip_alternate_volume_tag<R: Read>(
505 &self,
506 reader: &mut R,
507 ) -> Result<Option<String>, Error> {
508
509 if (self.flags & 64) != 0 { // has AVolTag
510 let _tmp = reader.read_exact_allocated(36)?;
511 }
512
513 Ok(None)
514 }
515 }
516
517 #[repr(C, packed)]
518 #[derive(Endian)]
519 struct TrasnsportDescriptor { // Robot/Griper
520 element_address: u16,
521 flags1: u8,
522 reserved_3: u8,
523 additional_sense_code: u8,
524 additional_sense_code_qualifier: u8,
525 reserved_6: [u8;3],
526 flags2: u8,
527 source_storage_element_address: u16,
528 // volume tag and Mixed media descriptor follows (depends on flags)
529 }
530
531 #[repr(C, packed)]
532 #[derive(Endian)]
533 struct TransferDescriptor { // Tape drive
534 element_address: u16,
535 flags1: u8,
536 reserved_3: u8,
537 additional_sense_code: u8,
538 additional_sense_code_qualifier: u8,
539 id_valid: u8,
540 scsi_bus_address: u8,
541 reserved_8: u8,
542 flags2: u8,
543 source_storage_element_address: u16,
544 // volume tag, drive identifier and Mixed media descriptor follows
545 // (depends on flags)
546 }
547
548 #[repr(C, packed)]
549 #[derive(Endian)]
550 struct DvcidHead { // Drive Identifier Header
551 code_set: u8,
552 identifier_type: u8,
553 reserved: u8,
554 identifier_len: u8,
555 // Identifier follows
556 }
557
558 #[repr(C, packed)]
559 #[derive(Endian)]
560 struct StorageDescriptor { // Mail Slot
561 element_address: u16,
562 flags1: u8,
563 reserved_3: u8,
564 additional_sense_code: u8,
565 additional_sense_code_qualifier: u8,
566 reserved_6: [u8;3],
567 flags2: u8,
568 source_storage_element_address: u16,
569 // volume tag and Mixed media descriptor follows (depends on flags)
570 }
571
572 struct DecodedStatusPage {
573 last_element_address: Option<u16>,
574 transports: Vec<TransportElementStatus>,
575 drives: Vec<DriveStatus>,
576 storage_slots: Vec<StorageElementStatus>,
577 import_export_slots: Vec<StorageElementStatus>,
578 }
579
580 fn create_element_status(full: bool, volume_tag: Option<String>) -> ElementStatus {
581 if full {
582 if let Some(volume_tag) = volume_tag {
583 ElementStatus::VolumeTag(volume_tag)
584 } else {
585 ElementStatus::Full
586 }
587 } else {
588 ElementStatus::Empty
589 }
590 }
591
592 fn decode_element_status_page(
593 _info: &InquiryInfo,
594 data: &[u8],
595 start_element_address: u16,
596 ) -> Result<DecodedStatusPage, Error> {
597
598 proxmox::try_block!({
599
600 let mut result = DecodedStatusPage {
601 last_element_address: None,
602 transports: Vec::new(),
603 drives: Vec::new(),
604 storage_slots: Vec::new(),
605 import_export_slots: Vec::new(),
606 };
607
608 let mut reader = &data[..];
609
610 let head: ElementStatusHeader = unsafe { reader.read_be_value()? };
611
612 if head.number_of_elements_available == 0 {
613 return Ok(result);
614 }
615
616 if head.first_element_address_reported < start_element_address {
617 bail!("got wrong first_element_address_reported"); // sanity check
618 }
619
620 loop {
621 if reader.is_empty() {
622 break;
623 }
624
625 let subhead: SubHeader = unsafe { reader.read_be_value()? };
626
627 let len = subhead.byte_count_of_descriptor_data_available;
628 let mut len = ((len[0] as usize) << 16) + ((len[1] as usize) << 8) + (len[2] as usize);
629 if len > reader.len() {
630 len = reader.len();
631 }
632
633 let descr_data = reader.read_exact_allocated(len)?;
634 let mut reader = &descr_data[..];
635
636 loop {
637 if reader.is_empty() {
638 break;
639 }
640 if reader.len() < (subhead.descriptor_length as usize) {
641 break;
642 }
643
644 let len_before = reader.len();
645
646 match subhead.element_type_code {
647 1 => {
648 let desc: TrasnsportDescriptor = unsafe { reader.read_be_value()? };
649
650 let full = (desc.flags1 & 1) != 0;
651 let volume_tag = subhead.parse_optional_volume_tag(&mut reader, full)?;
652
653 subhead.skip_alternate_volume_tag(&mut reader)?;
654
655 let mut reserved = [0u8; 4];
656 reader.read_exact(&mut reserved)?;
657
658 result.last_element_address = Some(desc.element_address);
659
660 let status = TransportElementStatus {
661 status: create_element_status(full, volume_tag),
662 element_address: desc.element_address,
663 };
664 result.transports.push(status);
665 }
666 2 | 3 => {
667 let desc: StorageDescriptor = unsafe { reader.read_be_value()? };
668
669 let full = (desc.flags1 & 1) != 0;
670 let volume_tag = subhead.parse_optional_volume_tag(&mut reader, full)?;
671
672 subhead.skip_alternate_volume_tag(&mut reader)?;
673
674 let mut reserved = [0u8; 4];
675 reader.read_exact(&mut reserved)?;
676
677 result.last_element_address = Some(desc.element_address);
678
679 if subhead.element_type_code == 3 {
680 let status = StorageElementStatus {
681 import_export: true,
682 status: create_element_status(full, volume_tag),
683 element_address: desc.element_address,
684 };
685 result.import_export_slots.push(status);
686 } else {
687 let status = StorageElementStatus {
688 import_export: false,
689 status: create_element_status(full, volume_tag),
690 element_address: desc.element_address,
691 };
692 result.storage_slots.push(status);
693 }
694 }
695 4 => {
696 let desc: TransferDescriptor = unsafe { reader.read_be_value()? };
697
698 let loaded_slot = if (desc.flags2 & 128) != 0 { // SValid
699 Some(desc.source_storage_element_address as u64)
700 } else {
701 None
702 };
703
704 let full = (desc.flags1 & 1) != 0;
705 let volume_tag = subhead.parse_optional_volume_tag(&mut reader, full)?;
706
707 subhead.skip_alternate_volume_tag(&mut reader)?;
708
709 let dvcid: DvcidHead = unsafe { reader.read_be_value()? };
710
711 let (drive_serial_number, vendor, model) = match (dvcid.code_set, dvcid.identifier_type) {
712 (2, 0) => { // Serial number only (Quantum Superloader3 uses this)
713 let serial = reader.read_exact_allocated(dvcid.identifier_len as usize)?;
714 let serial = scsi_ascii_to_string(&serial);
715 (Some(serial), None, None)
716 }
717 (2, 1) => {
718 if dvcid.identifier_len != 34 {
719 bail!("got wrong DVCID length");
720 }
721 let vendor = reader.read_exact_allocated(8)?;
722 let vendor = scsi_ascii_to_string(&vendor);
723 let model = reader.read_exact_allocated(16)?;
724 let model = scsi_ascii_to_string(&model);
725 let serial = reader.read_exact_allocated(10)?;
726 let serial = scsi_ascii_to_string(&serial);
727 (Some(serial), Some(vendor), Some(model))
728 }
729 _ => (None, None, None),
730 };
731
732 result.last_element_address = Some(desc.element_address);
733
734 let drive = DriveStatus {
735 loaded_slot,
736 status: create_element_status(full, volume_tag),
737 drive_serial_number,
738 vendor,
739 model,
740 element_address: desc.element_address,
741 };
742 result.drives.push(drive);
743 }
744 code => bail!("got unknown element type code {}", code),
745 }
746
747 // we have to consume the whole descriptor size, else
748 // our position in the reader is not correct
749 let len_after = reader.len();
750 let have_read = len_before - len_after;
751 let desc_len = subhead.descriptor_length as usize;
752 if desc_len > have_read {
753 let mut left_to_read = desc_len - have_read;
754 if left_to_read > len_after {
755 left_to_read = len_after; // reader has not enough data?
756 }
757 let _ = reader.read_exact_allocated(left_to_read)?;
758 }
759 }
760 }
761
762 Ok(result)
763 }).map_err(|err: Error| format_err!("decode element status failed - {}", err))
764 }
765
766 /// Open the device for read/write, returns the file handle
767 pub fn open<P: AsRef<Path>>(path: P) -> Result<File, Error> {
768 let file = OpenOptions::new()
769 .read(true)
770 .write(true)
771 .open(path)?;
772
773 Ok(file)
774 }