@@ -226,6 +226,24 @@ pub trait CapStdExtDirExt {
226226 /// to determine, and `None` will be returned in those cases.
227227 fn is_mountpoint ( & self , path : impl AsRef < Path > ) -> Result < Option < bool > > ;
228228
229+ #[ cfg( not( windows) ) ]
230+ /// Get the value of an extended attribute. If the attribute is not present,
231+ /// this function will return `Ok(None)`.
232+ fn getxattr ( & self , path : impl AsRef < Path > , key : impl AsRef < OsStr > ) -> Result < Option < Vec < u8 > > > ;
233+
234+ #[ cfg( not( windows) ) ]
235+ /// List all extended attribute keys for this path.
236+ fn listxattrs ( & self , path : impl AsRef < Path > ) -> Result < crate :: XattrList > ;
237+
238+ #[ cfg( not( windows) ) ]
239+ /// Set the value of an extended attribute.
240+ fn setxattr (
241+ & self ,
242+ path : impl AsRef < Path > ,
243+ key : impl AsRef < OsStr > ,
244+ value : impl AsRef < [ u8 ] > ,
245+ ) -> Result < ( ) > ;
246+
229247 /// Recursively walk a directory. If the function returns [`std::ops::ControlFlow::Break`]
230248 /// while inspecting a directory, traversal of that directory is skipped. If
231249 /// [`std::ops::ControlFlow::Break`] is returned when inspecting a non-directory,
@@ -562,6 +580,20 @@ where
562580 Ok ( ( ) )
563581}
564582
583+ // Ensure that the target path isn't absolute, and doesn't
584+ // have any parent references.
585+ pub ( crate ) fn validate_relpath_no_uplinks ( path : & Path ) -> Result < & Path > {
586+ let is_absolute = path. is_absolute ( ) ;
587+ let contains_uplinks = path
588+ . components ( )
589+ . any ( |e| e == std:: path:: Component :: ParentDir ) ;
590+ if is_absolute || contains_uplinks {
591+ Err ( crate :: escape_attempt ( ) )
592+ } else {
593+ Ok ( path)
594+ }
595+ }
596+
565597impl CapStdExtDirExt for Dir {
566598 fn open_optional ( & self , path : impl AsRef < Path > ) -> Result < Option < File > > {
567599 map_optional ( self . open ( path. as_ref ( ) ) )
@@ -738,6 +770,26 @@ impl CapStdExtDirExt for Dir {
738770 is_mountpoint_impl_statx ( self , path. as_ref ( ) ) . map_err ( Into :: into)
739771 }
740772
773+ #[ cfg( not( windows) ) ]
774+ fn getxattr ( & self , path : impl AsRef < Path > , key : impl AsRef < OsStr > ) -> Result < Option < Vec < u8 > > > {
775+ crate :: xattrs:: impl_getxattr ( self , path. as_ref ( ) , key. as_ref ( ) )
776+ }
777+
778+ #[ cfg( not( windows) ) ]
779+ fn listxattrs ( & self , path : impl AsRef < Path > ) -> Result < crate :: XattrList > {
780+ crate :: xattrs:: impl_listxattrs ( self , path. as_ref ( ) )
781+ }
782+
783+ #[ cfg( not( windows) ) ]
784+ fn setxattr (
785+ & self ,
786+ path : impl AsRef < Path > ,
787+ key : impl AsRef < OsStr > ,
788+ value : impl AsRef < [ u8 ] > ,
789+ ) -> Result < ( ) > {
790+ crate :: xattrs:: impl_setxattr ( self , path. as_ref ( ) , key. as_ref ( ) , value. as_ref ( ) )
791+ }
792+
741793 fn walk < C , E > ( & self , config : & WalkConfiguration , mut callback : C ) -> std:: result:: Result < ( ) , E >
742794 where
743795 C : FnMut ( & WalkComponent ) -> WalkResult < E > ,
@@ -851,3 +903,23 @@ impl CapStdExtDirExtUtf8 for cap_std::fs_utf8::Dir {
851903 Ok ( r)
852904 }
853905}
906+
907+ #[ cfg( test) ]
908+ mod tests {
909+ use std:: path:: Path ;
910+
911+ use super :: * ;
912+
913+ #[ test]
914+ fn test_validate_relpath_no_uplinks ( ) {
915+ let ok_cases = [ "foo" , "foo/bar" , "foo/bar/" ] ;
916+ let err_cases = [ "/foo" , "/" , "../foo" , "foo/../bar" ] ;
917+
918+ for case in ok_cases {
919+ assert ! ( validate_relpath_no_uplinks( Path :: new( case) ) . is_ok( ) ) ;
920+ }
921+ for case in err_cases {
922+ assert ! ( validate_relpath_no_uplinks( Path :: new( case) ) . is_err( ) ) ;
923+ }
924+ }
925+ }
0 commit comments