• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 use crate::AconfigdError;
18 use openssl::hash::{Hasher, MessageDigest};
19 use std::fs::File;
20 use std::io::Read;
21 use std::os::unix::fs::PermissionsExt;
22 use std::path::Path;
23 
24 /// Set file permission
set_file_permission(file: &Path, mode: u32) -> Result<(), AconfigdError>25 pub(crate) fn set_file_permission(file: &Path, mode: u32) -> Result<(), AconfigdError> {
26     let perms = std::fs::Permissions::from_mode(mode);
27     std::fs::set_permissions(file, perms).map_err(|errmsg| {
28         AconfigdError::FailToUpdateFilePerm { file: file.display().to_string(), mode, errmsg }
29     })?;
30     Ok(())
31 }
32 
33 /// Copy file
copy_file(src: &Path, dst: &Path, mode: u32) -> Result<(), AconfigdError>34 pub(crate) fn copy_file(src: &Path, dst: &Path, mode: u32) -> Result<(), AconfigdError> {
35     if dst.exists() {
36         set_file_permission(dst, 0o644)?;
37     }
38 
39     let mut src_file = File::open(src).map_err(|errmsg| AconfigdError::FailToCopyFile {
40         src: src.display().to_string(),
41         dst: dst.display().to_string(),
42         errmsg,
43     })?;
44 
45     let mut dst_file = File::create(dst).map_err(|errmsg| AconfigdError::FailToCopyFile {
46         src: src.display().to_string(),
47         dst: dst.display().to_string(),
48         errmsg,
49     })?;
50 
51     std::io::copy(&mut src_file, &mut dst_file).map_err(|errmsg| {
52         AconfigdError::FailToCopyFile {
53             src: src.display().to_string(),
54             dst: dst.display().to_string(),
55             errmsg,
56         }
57     })?;
58 
59     // force kernel to flush file data in kernel buffer to filesystem
60     dst_file.sync_all().map_err(|errmsg| AconfigdError::FailToCopyFile {
61         src: src.display().to_string(),
62         dst: dst.display().to_string(),
63         errmsg,
64     })?;
65 
66     set_file_permission(dst, mode)
67 }
68 
69 /// Copy file without fsync
copy_file_without_fsync( src: &Path, dst: &Path, mode: u32, ) -> Result<(), AconfigdError>70 pub(crate) fn copy_file_without_fsync(
71     src: &Path,
72     dst: &Path,
73     mode: u32,
74 ) -> Result<(), AconfigdError> {
75     if dst.exists() {
76         set_file_permission(dst, 0o644)?;
77     }
78 
79     let mut src_file = File::open(src).map_err(|errmsg| AconfigdError::FailToCopyFile {
80         src: src.display().to_string(),
81         dst: dst.display().to_string(),
82         errmsg,
83     })?;
84 
85     let mut dst_file = File::create(dst).map_err(|errmsg| AconfigdError::FailToCopyFile {
86         src: src.display().to_string(),
87         dst: dst.display().to_string(),
88         errmsg,
89     })?;
90 
91     std::io::copy(&mut src_file, &mut dst_file).map_err(|errmsg| {
92         AconfigdError::FailToCopyFile {
93             src: src.display().to_string(),
94             dst: dst.display().to_string(),
95             errmsg,
96         }
97     })?;
98 
99     set_file_permission(dst, mode)
100 }
101 
102 /// Remove file
remove_file(src: &Path) -> Result<(), AconfigdError>103 pub(crate) fn remove_file(src: &Path) -> Result<(), AconfigdError> {
104     if let Ok(true) = src.try_exists() {
105         std::fs::remove_file(src).map_err(|errmsg| AconfigdError::FailToRemoveFile {
106             file: src.display().to_string(),
107             errmsg,
108         })?;
109     }
110     Ok(())
111 }
112 
113 /// Read pb from file
read_pb_from_file<T: protobuf::Message>(file: &Path) -> Result<T, AconfigdError>114 pub(crate) fn read_pb_from_file<T: protobuf::Message>(file: &Path) -> Result<T, AconfigdError> {
115     if !Path::new(file).exists() {
116         return Ok(T::new());
117     }
118 
119     let data = std::fs::read(file).map_err(|errmsg| AconfigdError::FailToReadFile {
120         file: file.display().to_string(),
121         errmsg,
122     })?;
123     protobuf::Message::parse_from_bytes(data.as_ref()).map_err(|errmsg| {
124         AconfigdError::FailToParsePbFromBytes { file: file.display().to_string(), errmsg }
125     })
126 }
127 
128 /// Write pb to file
write_pb_to_file<T: protobuf::Message>( pb: &T, file: &Path, ) -> Result<(), AconfigdError>129 pub(crate) fn write_pb_to_file<T: protobuf::Message>(
130     pb: &T,
131     file: &Path,
132 ) -> Result<(), AconfigdError> {
133     let bytes = protobuf::Message::write_to_bytes(pb).map_err(|errmsg| {
134         AconfigdError::FailToSerializePb { file: file.display().to_string(), errmsg }
135     })?;
136     std::fs::write(file, bytes).map_err(|errmsg| AconfigdError::FailToWriteFile {
137         file: file.display().to_string(),
138         errmsg,
139     })?;
140     Ok(())
141 }
142 
143 /// The digest is returned as a hexadecimal string.
get_files_digest(paths: &[&Path]) -> Result<String, AconfigdError>144 pub(crate) fn get_files_digest(paths: &[&Path]) -> Result<String, AconfigdError> {
145     let mut hasher = Hasher::new(MessageDigest::sha256())
146         .map_err(|errmsg| AconfigdError::FailToGetHasherForDigest { errmsg })?;
147     let mut buffer = [0; 1024];
148     for path in paths {
149         let mut f = File::open(path).map_err(|errmsg| AconfigdError::FailToOpenFile {
150             file: path.display().to_string(),
151             errmsg,
152         })?;
153         loop {
154             let n = f.read(&mut buffer[..]).map_err(|errmsg| AconfigdError::FailToReadFile {
155                 file: path.display().to_string(),
156                 errmsg,
157             })?;
158             if n == 0 {
159                 break;
160             }
161             hasher.update(&buffer).map_err(|errmsg| AconfigdError::FailToHashFile {
162                 file: path.display().to_string(),
163                 errmsg,
164             })?;
165         }
166     }
167     let digest: &[u8] =
168         &hasher.finish().map_err(|errmsg| AconfigdError::FailToGetDigest { errmsg })?;
169     let mut xdigest = String::new();
170     for x in digest {
171         xdigest.push_str(format!("{:02x}", x).as_str());
172     }
173     Ok(xdigest)
174 }
175 
176 #[cfg(test)]
177 mod tests {
178     use super::*;
179     use aconfigd_protos::ProtoLocalFlagOverrides;
180     use std::io::Write;
181     use tempfile::tempdir;
182 
get_file_perm_mode(file: &Path) -> u32183     fn get_file_perm_mode(file: &Path) -> u32 {
184         let f = std::fs::File::open(&file).unwrap();
185         let metadata = f.metadata().unwrap();
186         metadata.permissions().mode() & 0o777
187     }
188 
189     #[test]
test_copy_file()190     fn test_copy_file() {
191         let tmp_dir = tempdir().unwrap();
192 
193         let package_map = tmp_dir.path().join("package.map");
194         copy_file(&Path::new("./tests/data/package.map"), &package_map, 0o444).unwrap();
195         assert_eq!(get_file_perm_mode(&package_map), 0o444);
196 
197         let flag_map = tmp_dir.path().join("flag.map");
198         copy_file(&Path::new("./tests/data/flag.map"), &flag_map, 0o644).unwrap();
199         assert_eq!(get_file_perm_mode(&flag_map), 0o644);
200     }
201 
202     #[test]
test_remove_file()203     fn test_remove_file() {
204         let tmp_dir = tempdir().unwrap();
205         let package_map = tmp_dir.path().join("package.map");
206         copy_file(&Path::new("./tests/data/package.map"), &package_map, 0o444).unwrap();
207         assert!(remove_file(&package_map).is_ok());
208         assert!(!package_map.exists());
209     }
210 
211     #[test]
test_set_file_permission()212     fn test_set_file_permission() {
213         let tmp_dir = tempdir().unwrap();
214         let package_map = tmp_dir.path().join("package.map");
215         copy_file(&Path::new("./tests/data/package.map"), &package_map, 0o644).unwrap();
216         set_file_permission(&package_map, 0o444).unwrap();
217         assert_eq!(get_file_perm_mode(&package_map), 0o444);
218     }
219 
220     #[test]
test_write_pb_to_file()221     fn test_write_pb_to_file() {
222         let tmp_dir = tempdir().unwrap();
223         let test_pb = tmp_dir.path().join("test.pb");
224         let pb = ProtoLocalFlagOverrides::new();
225         write_pb_to_file(&pb, &test_pb).unwrap();
226         assert!(test_pb.exists());
227     }
228 
229     #[test]
test_read_pb_from_file()230     fn test_read_pb_from_file() {
231         let tmp_dir = tempdir().unwrap();
232         let test_pb = tmp_dir.path().join("test.pb");
233         let pb = ProtoLocalFlagOverrides::new();
234         write_pb_to_file(&pb, &test_pb).unwrap();
235         let new_pb: ProtoLocalFlagOverrides = read_pb_from_file(&test_pb).unwrap();
236         assert_eq!(new_pb.overrides.len(), 0);
237     }
238 
239     #[test]
test_get_files_digest()240     fn test_get_files_digest() {
241         let path1 = Path::new("/tmp/hi.txt");
242         let path2 = Path::new("/tmp/bye.txt");
243         let mut file1 = File::create(path1).unwrap();
244         let mut file2 = File::create(path2).unwrap();
245         file1.write_all(b"Hello, world!").expect("Writing to file");
246         file2.write_all(b"Goodbye, world!").expect("Writing to file");
247         let digest = get_files_digest(&[path1, path2]);
248         assert_eq!(
249             digest.expect("Calculating digest"),
250             "8352c31d9ff5f446b838139b7f4eb5fed821a1f80d6648ffa6ed7391ecf431f4"
251         );
252     }
253 }
254