1 /*
2  * Copyright (C) 2023 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 //! Provides AApexInfo (name, version) from the calling process
18 
19 use apex_manifest::apex_manifest::ApexManifest;
20 use protobuf::Message;
21 use std::env;
22 use std::ffi::CString;
23 use std::fs::File;
24 use std::path::{Path, PathBuf};
25 
26 #[derive(Debug)]
27 pub enum AApexInfoError {
28     /// The executable's path is not from an APEX
29     PathNotFromApex(PathBuf),
30     /// Fail to get the current executable's path
31     ExePathUnavailable(std::io::Error),
32     /// Fail to read apex_manifest.pb from an APEX
33     InvalidApex(String),
34 }
35 
36 /// AApexInfo is used as an opaque object from FFI clients. This just wraps
37 /// ApexManifest protobuf message.
38 ///
39 /// NOTE: that we don't want to provide too many details about APEX. It provides
40 /// minimal information (for now, name and version) only for the APEX where the
41 /// current process is loaded from.
42 pub struct AApexInfo {
43     pub name: CString,
44     pub version: i64,
45 }
46 
47 impl AApexInfo {
48     /// Returns AApexInfo object when called by an executable from an APEX.
create() -> Result<Self, AApexInfoError>49     pub fn create() -> Result<Self, AApexInfoError> {
50         let exe_path = env::current_exe().map_err(AApexInfoError::ExePathUnavailable)?;
51         let manifest_path = get_apex_manifest_path(exe_path)?;
52         let manifest = parse_apex_manifest(manifest_path)?;
53         Ok(AApexInfo {
54             name: CString::new(manifest.name)
55                 .map_err(|err| AApexInfoError::InvalidApex(format!("{err:?}")))?,
56             version: manifest.version,
57         })
58     }
59 }
60 
61 /// Returns the apex_manifest.pb path when a given path belongs to an apex.
get_apex_manifest_path<P: AsRef<Path>>(path: P) -> Result<PathBuf, AApexInfoError>62 fn get_apex_manifest_path<P: AsRef<Path>>(path: P) -> Result<PathBuf, AApexInfoError> {
63     let remain = path
64         .as_ref()
65         .strip_prefix("/apex")
66         .map_err(|_| AApexInfoError::PathNotFromApex(path.as_ref().to_owned()))?;
67     let apex_name = remain
68         .iter()
69         .next()
70         .ok_or_else(|| AApexInfoError::PathNotFromApex(path.as_ref().to_owned()))?;
71     Ok(Path::new("/apex").join(apex_name).join("apex_manifest.pb"))
72 }
73 
74 /// Parses the apex_manifest.pb protobuf message from a given path.
parse_apex_manifest<P: AsRef<Path>>(path: P) -> Result<ApexManifest, AApexInfoError>75 fn parse_apex_manifest<P: AsRef<Path>>(path: P) -> Result<ApexManifest, AApexInfoError> {
76     let mut f = File::open(path).map_err(|err| AApexInfoError::InvalidApex(format!("{err:?}")))?;
77     Message::parse_from_reader(&mut f).map_err(|err| AApexInfoError::InvalidApex(format!("{err:?}")))
78 }
79 
80 #[cfg(test)]
81 mod test {
82     use super::*;
83 
84     #[test]
test_get_apex_manifest_path()85     fn test_get_apex_manifest_path() {
86         assert_eq!(
87             get_apex_manifest_path("/apex/com.android.foo/bin/foo").unwrap(),
88             PathBuf::from("/apex/com.android.foo/apex_manifest.pb")
89         );
90         assert!(get_apex_manifest_path("/apex/").is_err());
91         assert!(get_apex_manifest_path("/apex").is_err());
92         assert!(get_apex_manifest_path("/com.android.foo/bin/foo").is_err());
93         assert!(get_apex_manifest_path("/system/apex/com.android.foo/bin/foo").is_err());
94     }
95 }
96