• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2021 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 //! Pack profiles into reports.
18 
19 use anyhow::{anyhow, Result};
20 use lazy_static::lazy_static;
21 use macaddr::MacAddr6;
22 use std::fs::{self, File, Permissions};
23 use std::io::{Read, Write};
24 use std::os::unix::fs::PermissionsExt;
25 use std::path::{Path, PathBuf};
26 use std::time::{Duration, SystemTime};
27 use uuid::v1::{Context, Timestamp};
28 use uuid::Uuid;
29 use zip::write::FileOptions;
30 use zip::CompressionMethod::Deflated;
31 use zip::ZipWriter;
32 
33 use crate::config::Config;
34 
35 pub const NO_USAGE_SETTING: i32 = -1;
36 
37 lazy_static! {
38     pub static ref UUID_CONTEXT: Context = Context::new(0);
39 }
40 
pack_report( profile: &Path, report: &Path, config: &Config, usage_setting: i32, ) -> Result<String>41 pub fn pack_report(
42     profile: &Path,
43     report: &Path,
44     config: &Config,
45     usage_setting: i32,
46 ) -> Result<String> {
47     let mut report = PathBuf::from(report);
48     let report_filename = get_report_filename(&config.node_id)?;
49     report.push(&report_filename);
50     report.set_extension("zip");
51 
52     // Remove the current report file if exists.
53     fs::remove_file(&report).ok();
54 
55     let report_file = fs::OpenOptions::new().create_new(true).write(true).open(&report)?;
56 
57     // Set report file ACL bits to 644, so that this can be shared to uploaders.
58     // Who has permission to actually read the file is protected by SELinux policy.
59     fs::set_permissions(&report, Permissions::from_mode(0o644))?;
60 
61     let options = FileOptions::default().compression_method(Deflated);
62     let mut zip = ZipWriter::new(report_file);
63 
64     fs::read_dir(profile)?
65         .filter_map(|e| e.ok())
66         .map(|e| e.path())
67         .filter(|e| e.is_file())
68         .try_for_each(|e| -> Result<()> {
69             let filename = e
70                 .file_name()
71                 .and_then(|f| f.to_str())
72                 .ok_or_else(|| anyhow!("Malformed profile path: {}", e.display()))?;
73             zip.start_file(filename, options)?;
74             let mut f = File::open(e)?;
75             let mut buffer = Vec::new();
76             f.read_to_end(&mut buffer)?;
77             zip.write_all(&buffer)?;
78             Ok(())
79         })?;
80 
81     if usage_setting != NO_USAGE_SETTING {
82         zip.start_file("usage_setting", options)?;
83         zip.write_all(usage_setting.to_string().as_bytes())?;
84     }
85     zip.finish()?;
86 
87     Ok(report_filename)
88 }
89 
get_report_filename(node_id: &MacAddr6) -> Result<String>90 fn get_report_filename(node_id: &MacAddr6) -> Result<String> {
91     let since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
92     let ts =
93         Timestamp::from_unix(&*UUID_CONTEXT, since_epoch.as_secs(), since_epoch.subsec_nanos());
94     let uuid = Uuid::new_v1(
95         ts,
96         node_id.as_bytes().try_into().expect("Invalid number of bytes in V1 UUID"),
97     );
98     Ok(uuid.to_string())
99 }
100 
101 /// Get report creation timestamp through its filename (version 1 UUID).
get_report_ts(filename: &str) -> Result<SystemTime>102 pub fn get_report_ts(filename: &str) -> Result<SystemTime> {
103     let uuid_ts = Uuid::parse_str(filename)?
104         .get_timestamp()
105         .ok_or_else(|| anyhow!("filename is not a valid V1 UUID."))?
106         .to_unix();
107     Ok(SystemTime::UNIX_EPOCH + Duration::new(uuid_ts.0, uuid_ts.1))
108 }
109