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