@@ -13,27 +13,27 @@ pub enum SeLinuxError {
1313 #[ error( "SELinux is not enabled on this system" ) ]
1414 SELinuxNotEnabled ,
1515
16- #[ error( "Failed to open the file" ) ]
17- FileOpenFailure ,
16+ #[ error( "Failed to open the file: {0} " ) ]
17+ FileOpenFailure ( String ) ,
1818
19- #[ error( "Failed to retrieve the security context" ) ]
20- ContextRetrievalFailure ,
19+ #[ error( "Failed to retrieve the security context: {0} " ) ]
20+ ContextRetrievalFailure ( String ) ,
2121
22- #[ error( "failed to set default file creation context to {0 }" ) ]
23- ContextSetFailure ( String ) ,
22+ #[ error( "Failed to set default file creation context to '{0}': {1 }" ) ]
23+ ContextSetFailure ( String , String ) ,
2424
25- #[ error( "Invalid context string or conversion failure " ) ]
26- ContextConversionFailure ,
25+ #[ error( "Failed to set default file creation context to '{0}': {1} " ) ]
26+ ContextConversionFailure ( String , String ) ,
2727}
2828
2929impl From < SeLinuxError > for i32 {
3030 fn from ( error : SeLinuxError ) -> i32 {
3131 match error {
3232 SeLinuxError :: SELinuxNotEnabled => 1 ,
33- SeLinuxError :: FileOpenFailure => 2 ,
34- SeLinuxError :: ContextRetrievalFailure => 3 ,
35- SeLinuxError :: ContextSetFailure ( _) => 4 ,
36- SeLinuxError :: ContextConversionFailure => 5 ,
33+ SeLinuxError :: FileOpenFailure ( _ ) => 2 ,
34+ SeLinuxError :: ContextRetrievalFailure ( _ ) => 3 ,
35+ SeLinuxError :: ContextSetFailure ( _, _ ) => 4 ,
36+ SeLinuxError :: ContextConversionFailure ( _ , _ ) => 5 ,
3737 }
3838 }
3939}
@@ -98,26 +98,29 @@ pub fn set_selinux_security_context(
9898
9999 if let Some ( ctx_str) = context {
100100 // Create a CString from the provided context string
101- let c_context = std:: ffi:: CString :: new ( ctx_str. as_str ( ) )
102- . map_err ( |_| SeLinuxError :: ContextConversionFailure ) ?;
101+ let c_context = std:: ffi:: CString :: new ( ctx_str. as_str ( ) ) . map_err ( |e| {
102+ SeLinuxError :: ContextConversionFailure ( ctx_str. to_string ( ) , e. to_string ( ) )
103+ } ) ?;
103104
104105 // Convert the CString into an SELinux security context
105- let security_context = selinux:: OpaqueSecurityContext :: from_c_str ( & c_context)
106- . map_err ( |_| SeLinuxError :: ContextConversionFailure ) ?;
106+ let security_context =
107+ selinux:: OpaqueSecurityContext :: from_c_str ( & c_context) . map_err ( |e| {
108+ SeLinuxError :: ContextConversionFailure ( ctx_str. to_string ( ) , e. to_string ( ) )
109+ } ) ?;
107110
108111 // Set the provided security context on the specified path
109112 SecurityContext :: from_c_str (
110- & security_context
111- . to_c_string ( )
112- . map_err ( |_| SeLinuxError :: ContextConversionFailure ) ?,
113+ & security_context. to_c_string ( ) . map_err ( |e| {
114+ SeLinuxError :: ContextConversionFailure ( ctx_str . to_string ( ) , e . to_string ( ) )
115+ } ) ?,
113116 false ,
114117 )
115118 . set_for_path ( path, false , false )
116- . map_err ( |_ | SeLinuxError :: ContextSetFailure ( ctx_str. to_string ( ) ) )
119+ . map_err ( |e | SeLinuxError :: ContextSetFailure ( ctx_str. to_string ( ) , e . to_string ( ) ) )
117120 } else {
118121 // If no context provided, set the default SELinux context for the path
119122 SecurityContext :: set_default_for_path ( path)
120- . map_err ( |_ | SeLinuxError :: ContextSetFailure ( "" . to_string ( ) ) )
123+ . map_err ( |e | SeLinuxError :: ContextSetFailure ( String :: new ( ) , e . to_string ( ) ) )
121124 }
122125}
123126
@@ -157,28 +160,29 @@ pub fn set_selinux_security_context(
157160/// }
158161/// },
159162/// Err(SeLinuxError::SELinuxNotEnabled) => println!("SELinux is not enabled on this system"),
160- /// Err(SeLinuxError::FileOpenFailure) => println!("Failed to open the file"),
161- /// Err(SeLinuxError::ContextRetrievalFailure) => println!("Failed to retrieve the security context"),
162- /// Err(SeLinuxError::ContextConversionFailure) => println!("Failed to convert the security context to a string"),
163+ /// Err(SeLinuxError::FileOpenFailure(e)) => println!("Failed to open the file: {}", e),
164+ /// Err(SeLinuxError::ContextRetrievalFailure(e)) => println!("Failed to retrieve the security context: {}", e),
165+ /// Err(SeLinuxError::ContextConversionFailure(ctx, e)) => println!("Failed to convert context '{}': {}", ctx, e),
166+ /// Err(SeLinuxError::ContextSetFailure(ctx, e)) => println!("Failed to set context '{}': {}", ctx, e),
163167/// }
164168/// ```
165169pub fn get_selinux_security_context ( path : & Path ) -> Result < String , SeLinuxError > {
166170 if !is_selinux_enabled ( ) {
167171 return Err ( SeLinuxError :: SELinuxNotEnabled ) ;
168172 }
169173
170- let f = std:: fs:: File :: open ( path) . map_err ( |_ | SeLinuxError :: FileOpenFailure ) ?;
174+ let f = std:: fs:: File :: open ( path) . map_err ( |e | SeLinuxError :: FileOpenFailure ( e . to_string ( ) ) ) ?;
171175
172176 // Get the security context of the file
173177 let context = match SecurityContext :: of_file ( & f, false ) {
174178 Ok ( Some ( ctx) ) => ctx,
175179 Ok ( None ) => return Ok ( String :: new ( ) ) , // No context found, return empty string
176- Err ( _ ) => return Err ( SeLinuxError :: ContextRetrievalFailure ) ,
180+ Err ( e ) => return Err ( SeLinuxError :: ContextRetrievalFailure ( e . to_string ( ) ) ) ,
177181 } ;
178182
179183 let context_c_string = context
180184 . to_c_string ( )
181- . map_err ( |_ | SeLinuxError :: ContextConversionFailure ) ?;
185+ . map_err ( |e | SeLinuxError :: ContextConversionFailure ( String :: new ( ) , e . to_string ( ) ) ) ?;
182186
183187 if let Some ( c_str) = context_c_string {
184188 // Convert the C string to a Rust String
@@ -198,29 +202,50 @@ mod tests {
198202 let tmpfile = NamedTempFile :: new ( ) . expect ( "Failed to create tempfile" ) ;
199203 let path = tmpfile. path ( ) ;
200204
201- let result = set_selinux_security_context ( path, None ) ;
205+ if !is_selinux_enabled ( ) {
206+ let result = set_selinux_security_context ( path, None ) ;
207+ assert ! ( result. is_err( ) , "Expected error when SELinux is disabled" ) ;
208+ match result. unwrap_err ( ) {
209+ SeLinuxError :: SELinuxNotEnabled => {
210+ // This is the expected error when SELinux is not enabled
211+ }
212+ err => panic ! ( "Expected SELinuxNotEnabled error but got: {}" , err) ,
213+ }
214+ return ;
215+ }
202216
203- if result. is_ok ( ) {
204- // SELinux enabled and successfully set default context
205- assert ! ( true , "Successfully set SELinux context" ) ;
206- } else {
207- let err = result. unwrap_err ( ) ;
208- let valid_errors = [
209- "SELinux is not enabled on this system" ,
210- & format ! (
211- "Failed to set default context: selinux_lsetfilecon_default() failed on path '{}'" ,
212- path. display( )
213- ) ,
214- ] ;
217+ let default_result = set_selinux_security_context ( path, None ) ;
218+ assert ! (
219+ default_result. is_ok( ) ,
220+ "Failed to set default context: {:?}" ,
221+ default_result. err( )
222+ ) ;
223+
224+ let context = get_selinux_security_context ( path) . expect ( "Failed to get context" ) ;
225+ assert ! (
226+ !context. is_empty( ) ,
227+ "Expected non-empty context after setting default context"
228+ ) ;
229+
230+ let test_context = String :: from ( "system_u:object_r:tmp_t:s0" ) ;
231+ let explicit_result = set_selinux_security_context ( path, Some ( & test_context) ) ;
232+
233+ if explicit_result. is_ok ( ) {
234+ let new_context = get_selinux_security_context ( path)
235+ . expect ( "Failed to get context after setting explicit context" ) ;
215236
216237 assert ! (
217- valid_errors. contains( & err. as_str( ) ) ,
218- "Unexpected error message: {}" ,
219- err
238+ new_context. contains( "tmp_t" ) ,
239+ "Expected context to contain 'tmp_t', but got: {}" ,
240+ new_context
241+ ) ;
242+ } else {
243+ println ! (
244+ "Note: Could not set explicit context {:?}" ,
245+ explicit_result. err( )
220246 ) ;
221247 }
222248 }
223-
224249 #[ test]
225250 fn test_invalid_context_string_error ( ) {
226251 let tmpfile = NamedTempFile :: new ( ) . expect ( "Failed to create tempfile" ) ;
@@ -231,10 +256,18 @@ mod tests {
231256 let result = set_selinux_security_context ( path, Some ( & invalid_context) ) ;
232257
233258 assert ! ( result. is_err( ) ) ;
234- assert_eq ! (
235- result. unwrap_err( ) ,
236- "Invalid context string (contains null bytes)"
237- ) ;
259+ if let Err ( err) = result {
260+ match err {
261+ SeLinuxError :: ContextConversionFailure ( ctx, msg) => {
262+ assert_eq ! ( ctx, "invalid\0 context" ) ;
263+ assert ! (
264+ msg. contains( "nul byte" ) ,
265+ "Error message should mention nul byte"
266+ ) ;
267+ }
268+ _ => panic ! ( "Expected ContextConversionFailure error but got: {}" , err) ,
269+ }
270+ }
238271 }
239272
240273 #[ test]
@@ -261,19 +294,56 @@ mod tests {
261294 let result = get_selinux_security_context ( path) ;
262295
263296 if result. is_ok ( ) {
264- println ! ( "Retrieved SELinux context: {}" , result. unwrap( ) ) ;
297+ let context = result. unwrap ( ) ;
298+ println ! ( "Retrieved SELinux context: {}" , context) ;
299+
300+ assert ! (
301+ is_selinux_enabled( ) ,
302+ "Got a successful context result but SELinux is not enabled"
303+ ) ;
304+
305+ if !context. is_empty ( ) {
306+ assert ! (
307+ context. contains( ':' ) ,
308+ "SELinux context '{}' doesn't match expected format" ,
309+ context
310+ ) ;
311+ }
265312 } else {
266313 let err = result. unwrap_err ( ) ;
267314
268- // Valid error types
269315 match err {
270- SeLinuxError :: SELinuxNotEnabled => assert ! ( true , "SELinux not supported" ) ,
271- SeLinuxError :: ContextRetrievalFailure => assert ! ( true , "Context retrieval failure" ) ,
272- SeLinuxError :: ContextConversionFailure => {
273- assert ! ( true , "Context conversion failure" )
316+ SeLinuxError :: SELinuxNotEnabled => {
317+ assert ! (
318+ !is_selinux_enabled( ) ,
319+ "Got SELinuxNotEnabled error, but is_selinux_enabled() returned true"
320+ ) ;
321+ }
322+ SeLinuxError :: ContextRetrievalFailure ( e) => {
323+ assert ! (
324+ is_selinux_enabled( ) ,
325+ "Got ContextRetrievalFailure when SELinux is not enabled"
326+ ) ;
327+ assert ! ( !e. is_empty( ) , "Error message should not be empty" ) ;
328+ println ! ( "Context retrieval failure: {}" , e) ;
274329 }
275- SeLinuxError :: FileOpenFailure => {
276- panic ! ( "File open failure occurred despite file being created" )
330+ SeLinuxError :: ContextConversionFailure ( ctx, e) => {
331+ assert ! (
332+ is_selinux_enabled( ) ,
333+ "Got ContextConversionFailure when SELinux is not enabled"
334+ ) ;
335+ assert ! ( !e. is_empty( ) , "Error message should not be empty" ) ;
336+ println ! ( "Context conversion failure for '{}': {}" , ctx, e) ;
337+ }
338+ SeLinuxError :: ContextSetFailure ( ctx, e) => {
339+ assert ! ( false ) ;
340+ }
341+ SeLinuxError :: FileOpenFailure ( e) => {
342+ assert ! (
343+ Path :: new( path) . exists( ) ,
344+ "File open failure occurred despite file being created: {}" ,
345+ e
346+ ) ;
277347 }
278348 }
279349 }
@@ -286,9 +356,16 @@ mod tests {
286356 let result = get_selinux_security_context ( path) ;
287357
288358 assert ! ( result. is_err( ) ) ;
289- assert ! (
290- matches!( result. unwrap_err( ) , SeLinuxError :: FileOpenFailure ) ,
291- "Expected file open error for nonexistent file"
292- ) ;
359+ if let Err ( err) = result {
360+ match err {
361+ SeLinuxError :: FileOpenFailure ( e) => {
362+ assert ! (
363+ e. contains( "No such file" ) ,
364+ "Error should mention file not found"
365+ ) ;
366+ }
367+ _ => panic ! ( "Expected FileOpenFailure error but got: {}" , err) ,
368+ }
369+ }
293370 }
294371}
0 commit comments