1/*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15//! decompress
16
17use std::{
18    fs,
19    io::{self, Read},
20    path::Path,
21};
22
23use crate::tar::{entry::Entry, header};
24#[allow(unused)]
25use crate::utils::hdc_log::*;
26
27/// Decomposes the tar package
28pub struct Decompress {
29    entrys: Vec<Entry>,
30}
31
32impl Decompress {
33    /// Decomposes tar
34    /// path: tar path
35    pub fn file(path: &str) -> Result<Decompress, io::Error> {
36        match fs::metadata(path) {
37            Ok(metadata) => {
38                let file_size = metadata.len();
39                if file_size == 0 || file_size % header::HEADER_LEN as u64 != 0 {
40                    return Err(io::Error::new(
41                        io::ErrorKind::InvalidInput,
42                        format!("{path} is not tar file"),
43                    ));
44                }
45            }
46            Err(e) => return Err(e),
47        };
48        let mut f = fs::File::open(path)?;
49        let mut buff = [0u8; header::HEADER_LEN as usize];
50
51        let mut decompress = Self { entrys: Vec::new() };
52
53        let mut entry = None;
54        loop {
55            match f.read(&mut buff)? {
56                512 => match entry {
57                    None => {
58                        if let Ok(p_entry) = Entry::create_from_raw_data(&buff) {
59                            if p_entry.is_finish() {
60                                decompress.entrys.push(p_entry);
61                            } else {
62                                entry = Some(p_entry);
63                            }
64                        }
65                        continue;
66                    }
67                    Some(ref mut p_entry) => {
68                        p_entry.add_data(&buff);
69                        if p_entry.is_finish() {
70                            decompress.entrys.push(entry.unwrap());
71                            entry = None;
72                        }
73                    }
74                },
75                0 => break,
76                n => {
77                    crate::error!("read error n {n}");
78                    break;
79                }
80            }
81        }
82
83        Ok(decompress)
84    }
85
86    /// 将文件解压到文件夹,传入路径为空则是当前文件夹
87    pub fn decompress(&self, prefix: &str) -> io::Result<()> {
88        let prefix = if !prefix.is_empty() { prefix } else { "./" };
89
90        let prefix_path = Path::new(prefix);
91
92        if prefix_path.exists() {
93            if prefix_path.is_file() {
94                return Err(io::Error::new(
95                    io::ErrorKind::InvalidInput,
96                    format!("{} is not a dir", prefix),
97                ));
98            }
99        } else {
100            crate::debug!("need create dir {}", prefix);
101            fs::create_dir_all(prefix)?;
102        }
103
104        for entry in &self.entrys {
105            if !entry.is_finish() {
106                crate::error!("file data is not load");
107                continue;
108            }
109            if let Err(e) = entry.write_to_file(prefix_path) {
110                crate::error!("entry.write_to_file failed: {}", e);
111            }
112        }
113
114        Ok(())
115    }
116}
117