@@ -10,6 +10,20 @@ use selinux::SecurityContext;
1010#[ derive( Debug ) ]
1111pub enum Error {
1212 SELinuxNotEnabled ,
13+ FileOpenFailure ,
14+ ContextRetrievalFailure ,
15+ ContextConversionFailure ,
16+ }
17+
18+ impl From < Error > for i32 {
19+ fn from ( error : Error ) -> i32 {
20+ match error {
21+ Error :: SELinuxNotEnabled => 1 ,
22+ Error :: FileOpenFailure => 2 ,
23+ Error :: ContextRetrievalFailure => 3 ,
24+ Error :: ContextConversionFailure => 4 ,
25+ }
26+ }
1327}
1428
1529/// Checks if SELinux is enabled on the system.
@@ -109,6 +123,73 @@ pub fn set_selinux_security_context(path: &Path, context: Option<&String>) -> Re
109123 }
110124}
111125
126+ /// Gets the SELinux security context for the given filesystem path.
127+ ///
128+ /// Retrieves the security context of the specified filesystem path if SELinux is enabled
129+ /// on the system.
130+ ///
131+ /// # Arguments
132+ ///
133+ /// * `path` - Filesystem path for which to retrieve the SELinux context.
134+ ///
135+ /// # Returns
136+ ///
137+ /// * `Ok(String)` - The SELinux context string if successfully retrieved. Returns an empty
138+ /// string if no context was found.
139+ /// * `Err(Error)` - An error variant indicating the type of failure:
140+ /// - `Error::SELinuxNotEnabled` - SELinux is not enabled on the system.
141+ /// - `Error::FileOpenFailure` - Failed to open the specified file.
142+ /// - `Error::ContextRetrievalFailure` - Failed to retrieve the security context.
143+ /// - `Error::ContextConversionFailure` - Failed to convert the security context to a string.
144+ ///
145+ /// # Examples
146+ ///
147+ /// ```
148+ /// use std::path::Path;
149+ /// use uucore::selinux::{get_selinux_security_context, Error};
150+ ///
151+ /// // Get the SELinux context for a file
152+ /// match get_selinux_security_context(Path::new("/path/to/file")) {
153+ /// Ok(context) => {
154+ /// if context.is_empty() {
155+ /// println!("No SELinux context found for the file");
156+ /// } else {
157+ /// println!("SELinux context: {}", context);
158+ /// }
159+ /// },
160+ /// Err(Error::SELinuxNotEnabled) => println!("SELinux is not enabled on this system"),
161+ /// Err(Error::FileOpenFailure) => println!("Failed to open the file"),
162+ /// Err(Error::ContextRetrievalFailure) => println!("Failed to retrieve the security context"),
163+ /// Err(Error::ContextConversionFailure) => println!("Failed to convert the security context to a string"),
164+ /// }
165+ /// ```
166+
167+ pub fn get_selinux_security_context ( path : & Path ) -> Result < String , Error > {
168+ if selinux:: kernel_support ( ) == selinux:: KernelSupport :: Unsupported {
169+ return Err ( Error :: SELinuxNotEnabled ) ;
170+ }
171+
172+ let f = std:: fs:: File :: open ( path) . map_err ( |_| Error :: FileOpenFailure ) ?;
173+
174+ // Get the security context of the file
175+ let context = match SecurityContext :: of_file ( & f, false ) {
176+ Ok ( Some ( ctx) ) => ctx,
177+ Ok ( None ) => return Ok ( String :: new ( ) ) , // No context found, return empty string
178+ Err ( _) => return Err ( Error :: ContextRetrievalFailure ) ,
179+ } ;
180+
181+ let context_c_string = context
182+ . to_c_string ( )
183+ . map_err ( |_| Error :: ContextConversionFailure ) ?;
184+
185+ if let Some ( c_str) = context_c_string {
186+ // Convert the C string to a Rust String
187+ Ok ( c_str. to_string_lossy ( ) . to_string ( ) )
188+ } else {
189+ Ok ( String :: new ( ) )
190+ }
191+ }
192+
112193#[ cfg( test) ]
113194mod tests {
114195 use super :: * ;
@@ -171,4 +252,43 @@ mod tests {
171252 }
172253 }
173254 }
255+
256+ #[ test]
257+ fn test_get_selinux_security_context ( ) {
258+ let tmpfile = NamedTempFile :: new ( ) . expect ( "Failed to create tempfile" ) ;
259+ let path = tmpfile. path ( ) ;
260+
261+ std:: fs:: write ( path, b"test content" ) . expect ( "Failed to write to tempfile" ) ;
262+
263+ let result = get_selinux_security_context ( path) ;
264+
265+ if result. is_ok ( ) {
266+ println ! ( "Retrieved SELinux context: {}" , result. unwrap( ) ) ;
267+ } else {
268+ let err = result. unwrap_err ( ) ;
269+
270+ // Valid error types
271+ match err {
272+ Error :: SELinuxNotEnabled => assert ! ( true , "SELinux not supported" ) ,
273+ Error :: ContextRetrievalFailure => assert ! ( true , "Context retrieval failure" ) ,
274+ Error :: ContextConversionFailure => assert ! ( true , "Context conversion failure" ) ,
275+ Error :: FileOpenFailure => {
276+ panic ! ( "File open failure occurred despite file being created" )
277+ }
278+ }
279+ }
280+ }
281+
282+ #[ test]
283+ fn test_get_selinux_context_nonexistent_file ( ) {
284+ let path = Path :: new ( "/nonexistent/file/that/does/not/exist" ) ;
285+
286+ let result = get_selinux_security_context ( path) ;
287+
288+ assert ! ( result. is_err( ) ) ;
289+ assert ! (
290+ matches!( result. unwrap_err( ) , Error :: FileOpenFailure ) ,
291+ "Expected file open error for nonexistent file"
292+ ) ;
293+ }
174294}
0 commit comments