@@ -288,9 +288,9 @@ struct DiskFileSystemInner {
288288
289289 #[ turbo_tasks( debug_ignore, trace_ignore) ]
290290 watcher : DiskWatcher ,
291- /// A root path that we do not allow access to from this filesystem.
291+ /// Root paths that we do not allow access to from this filesystem.
292292 /// Useful for things like output directories to prevent accidental ouroboros situations.
293- denied_path : Option < RcStr > ,
293+ denied_paths : Vec < RcStr > ,
294294}
295295
296296impl DiskFileSystemInner {
@@ -303,24 +303,19 @@ impl DiskFileSystemInner {
303303 /// Checks if a path is within the denied path
304304 /// Returns true if the path should be treated as non-existent
305305 ///
306- /// Since denied_path is guaranteed to be:
306+ /// Since denied_paths are guaranteed to be:
307307 /// - normalized (no ../ traversals)
308308 /// - using unix separators (/)
309309 /// - relative to the fs root
310310 ///
311311 /// We can efficiently check using string operations
312312 fn is_path_denied ( & self , path : & FileSystemPath ) -> bool {
313- let Some ( denied_path) = & self . denied_path else {
314- return false ;
315- } ;
316- // If the path starts with the denied path then there are three cases
317- // * they are equal => denied
318- // * root relative path is a descendant which means the next character is a / => denied
319- // * anything else => not denied (covers denying `.next` but allowing `.next2`)
320313 let path = & path. path ;
321- path. starts_with ( denied_path. as_str ( ) )
322- && ( path. len ( ) == denied_path. len ( )
323- || path. as_bytes ( ) . get ( denied_path. len ( ) ) == Some ( & b'/' ) )
314+ self . denied_paths . iter ( ) . any ( |denied_path| {
315+ path. starts_with ( denied_path. as_str ( ) )
316+ && ( path. len ( ) == denied_path. len ( )
317+ || path. as_bytes ( ) . get ( denied_path. len ( ) ) == Some ( & b'/' ) )
318+ } )
324319 }
325320
326321 /// registers the path as an invalidator for the current task,
@@ -639,7 +634,7 @@ impl DiskFileSystem {
639634 /// * `root` - Path to the given filesystem's root. Should be
640635 /// [canonicalized][std::fs::canonicalize].
641636 pub fn new ( name : RcStr , root : RcStr ) -> Vc < Self > {
642- Self :: new_internal ( name, root, None )
637+ Self :: new_internal ( name, root, Vec :: new ( ) )
643638 }
644639
645640 /// Create a new instance of `DiskFileSystem`.
@@ -656,14 +651,25 @@ impl DiskFileSystem {
656651 normalize_path( & denied_path) . as_deref( ) == Some ( & * denied_path) ,
657652 "denied_path must be normalized: {denied_path:?}"
658653 ) ;
659- Self :: new_internal ( name, root, Some ( denied_path) )
654+ Self :: new_internal ( name, root, vec ! [ denied_path] )
655+ }
656+
657+ pub fn new_with_denied_paths ( name : RcStr , root : RcStr , denied_paths : Vec < RcStr > ) -> Vc < Self > {
658+ for denied_path in & denied_paths {
659+ debug_assert ! ( !denied_path. is_empty( ) , "denied_path must not be empty" ) ;
660+ debug_assert ! (
661+ normalize_path( denied_path) . as_deref( ) == Some ( & * * denied_path) ,
662+ "denied_path must be normalized: {denied_path:?}"
663+ ) ;
664+ }
665+ Self :: new_internal ( name, root, denied_paths)
660666 }
661667}
662668
663669#[ turbo_tasks:: value_impl]
664670impl DiskFileSystem {
665671 #[ turbo_tasks:: function]
666- fn new_internal ( name : RcStr , root : RcStr , denied_path : Option < RcStr > ) -> Vc < Self > {
672+ fn new_internal ( name : RcStr , root : RcStr , denied_paths : Vec < RcStr > ) -> Vc < Self > {
667673 mark_stateful ( ) ;
668674
669675 let instance = DiskFileSystem {
@@ -677,7 +683,7 @@ impl DiskFileSystem {
677683 read_semaphore : create_read_semaphore ( ) ,
678684 write_semaphore : create_write_semaphore ( ) ,
679685 watcher : DiskWatcher :: new ( ) ,
680- denied_path ,
686+ denied_paths ,
681687 } ) ,
682688 } ;
683689
@@ -785,15 +791,18 @@ impl FileSystem for DiskFileSystem {
785791 bail ! ( anyhow!( e) . context( format!( "reading dir {}" , full_path. display( ) ) ) )
786792 }
787793 } ;
788- let denied_entry = match self . inner . denied_path . as_ref ( ) {
789- Some ( denied_path) => {
794+ let dir_path = fs_path. path . as_str ( ) ;
795+ let denied_entries: FxHashSet < & str > = self
796+ . inner
797+ . denied_paths
798+ . iter ( )
799+ . filter_map ( |denied_path| {
790800 // If we have a denied path, we need to see if the current directory is a prefix of
791801 // the denied path meaning that it is possible that some directory entry needs to be
792802 // filtered. we do this first to avoid string manipulation on every
793803 // iteration of the directory entries. So while expanding `foo/bar`,
794804 // if `foo/bar/baz` is denied, we filter out `baz`.
795805 // But if foo/bar/baz/qux is denied we don't filter anything from this level.
796- let dir_path = fs_path. path . as_str ( ) ;
797806 if denied_path. starts_with ( dir_path) {
798807 let denied_path_suffix =
799808 if denied_path. as_bytes ( ) . get ( dir_path. len ( ) ) == Some ( & b'/' ) {
@@ -808,9 +817,8 @@ impl FileSystem for DiskFileSystem {
808817 } else {
809818 None
810819 }
811- }
812- None => None ,
813- } ;
820+ } )
821+ . collect ( ) ;
814822
815823 let entries = read_dir
816824 . filter_map ( |r| {
@@ -822,9 +830,7 @@ impl FileSystem for DiskFileSystem {
822830 // we filter out any non unicode names
823831 let file_name: RcStr = e. file_name ( ) . to_str ( ) ?. into ( ) ;
824832 // Filter out denied entries
825- if let Some ( denied_name) = denied_entry
826- && denied_name == file_name. as_str ( )
827- {
833+ if denied_entries. contains ( file_name. as_str ( ) ) {
828834 return None ;
829835 }
830836
0 commit comments