33// For the full copyright and license information, please view the LICENSE
44// file that was distributed with this source code.
55
6+ use std:: error:: Error ;
67use std:: path:: Path ;
78
89use selinux:: SecurityContext ;
@@ -13,16 +14,16 @@ pub enum SeLinuxError {
1314 #[ error( "SELinux is not enabled on this system" ) ]
1415 SELinuxNotEnabled ,
1516
16- #[ error( "Failed to open the file: {0}" ) ]
17+ #[ error( "failed to open the file: {0}" ) ]
1718 FileOpenFailure ( String ) ,
1819
19- #[ error( "Failed to retrieve the security context: {0}" ) ]
20+ #[ error( "failed to retrieve the security context: {0}" ) ]
2021 ContextRetrievalFailure ( String ) ,
2122
22- #[ error( "Failed to set default file creation context to '{0}': {1}" ) ]
23+ #[ error( "failed to set default file creation context to '{0}': {1}" ) ]
2324 ContextSetFailure ( String , String ) ,
2425
25- #[ error( "Failed to set default file creation context to '{0}': {1}" ) ]
26+ #[ error( "failed to set default file creation context to '{0}': {1}" ) ]
2627 ContextConversionFailure ( String , String ) ,
2728}
2829
@@ -45,6 +46,22 @@ pub fn is_selinux_enabled() -> bool {
4546 selinux:: kernel_support ( ) != selinux:: KernelSupport :: Unsupported
4647}
4748
49+ /// Returns a string describing the error and its causes.
50+ fn selinux_error_description ( mut error : & dyn Error ) -> String {
51+ let mut description = String :: new ( ) ;
52+ while let Some ( source) = error. source ( ) {
53+ let error_text = source. to_string ( ) ;
54+ // Check if this is an OS error and trim it
55+ if let Some ( idx) = error_text. find ( " (os error " ) {
56+ description. push_str ( & error_text[ ..idx] ) ;
57+ } else {
58+ description. push_str ( & error_text) ;
59+ }
60+ error = source;
61+ }
62+ description
63+ }
64+
4865/// Sets the SELinux security context for the given filesystem path.
4966///
5067/// If a specific context is provided, it attempts to set this context explicitly.
@@ -99,28 +116,40 @@ pub fn set_selinux_security_context(
99116 if let Some ( ctx_str) = context {
100117 // Create a CString from the provided context string
101118 let c_context = std:: ffi:: CString :: new ( ctx_str. as_str ( ) ) . map_err ( |e| {
102- SeLinuxError :: ContextConversionFailure ( ctx_str. to_string ( ) , e. to_string ( ) )
119+ SeLinuxError :: ContextConversionFailure (
120+ ctx_str. to_string ( ) ,
121+ selinux_error_description ( & e) ,
122+ )
103123 } ) ?;
104124
105125 // Convert the CString into an SELinux security context
106126 let security_context =
107127 selinux:: OpaqueSecurityContext :: from_c_str ( & c_context) . map_err ( |e| {
108- SeLinuxError :: ContextConversionFailure ( ctx_str. to_string ( ) , e. to_string ( ) )
128+ SeLinuxError :: ContextConversionFailure (
129+ ctx_str. to_string ( ) ,
130+ selinux_error_description ( & e) ,
131+ )
109132 } ) ?;
110133
111134 // Set the provided security context on the specified path
112135 SecurityContext :: from_c_str (
113136 & security_context. to_c_string ( ) . map_err ( |e| {
114- SeLinuxError :: ContextConversionFailure ( ctx_str. to_string ( ) , e. to_string ( ) )
137+ SeLinuxError :: ContextConversionFailure (
138+ ctx_str. to_string ( ) ,
139+ selinux_error_description ( & e) ,
140+ )
115141 } ) ?,
116142 false ,
117143 )
118144 . set_for_path ( path, false , false )
119- . map_err ( |e| SeLinuxError :: ContextSetFailure ( ctx_str. to_string ( ) , e. to_string ( ) ) )
145+ . map_err ( |e| {
146+ SeLinuxError :: ContextSetFailure ( ctx_str. to_string ( ) , selinux_error_description ( & e) )
147+ } )
120148 } else {
121149 // If no context provided, set the default SELinux context for the path
122- SecurityContext :: set_default_for_path ( path)
123- . map_err ( |e| SeLinuxError :: ContextSetFailure ( String :: new ( ) , e. to_string ( ) ) )
150+ SecurityContext :: set_default_for_path ( path) . map_err ( |e| {
151+ SeLinuxError :: ContextSetFailure ( String :: new ( ) , selinux_error_description ( & e) )
152+ } )
124153 }
125154}
126155
@@ -171,18 +200,23 @@ pub fn get_selinux_security_context(path: &Path) -> Result<String, SeLinuxError>
171200 return Err ( SeLinuxError :: SELinuxNotEnabled ) ;
172201 }
173202
174- let f = std:: fs:: File :: open ( path) . map_err ( |e| SeLinuxError :: FileOpenFailure ( e. to_string ( ) ) ) ?;
203+ let f = std:: fs:: File :: open ( path)
204+ . map_err ( |e| SeLinuxError :: FileOpenFailure ( selinux_error_description ( & e) ) ) ?;
175205
176206 // Get the security context of the file
177207 let context = match SecurityContext :: of_file ( & f, false ) {
178208 Ok ( Some ( ctx) ) => ctx,
179209 Ok ( None ) => return Ok ( String :: new ( ) ) , // No context found, return empty string
180- Err ( e) => return Err ( SeLinuxError :: ContextRetrievalFailure ( e. to_string ( ) ) ) ,
210+ Err ( e) => {
211+ return Err ( SeLinuxError :: ContextRetrievalFailure (
212+ selinux_error_description ( & e) ,
213+ ) ) ;
214+ }
181215 } ;
182216
183- let context_c_string = context
184- . to_c_string ( )
185- . map_err ( |e| SeLinuxError :: ContextConversionFailure ( String :: new ( ) , e . to_string ( ) ) ) ?;
217+ let context_c_string = context. to_c_string ( ) . map_err ( |e| {
218+ SeLinuxError :: ContextConversionFailure ( String :: new ( ) , selinux_error_description ( & e ) )
219+ } ) ?;
186220
187221 if let Some ( c_str) = context_c_string {
188222 // Convert the C string to a Rust String
@@ -336,7 +370,8 @@ mod tests {
336370 println ! ( "Context conversion failure for '{}': {}" , ctx, e) ;
337371 }
338372 SeLinuxError :: ContextSetFailure ( ctx, e) => {
339- assert ! ( false ) ;
373+ assert ! ( !e. is_empty( ) , "Error message should not be empty" ) ;
374+ println ! ( "Context conversion failure for '{}': {}" , ctx, e) ;
340375 }
341376 SeLinuxError :: FileOpenFailure ( e) => {
342377 assert ! (
0 commit comments