// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fs

import (
	
	
)

// A SubFS is a file system with a Sub method.
type SubFS interface {
	FS

	// Sub returns an FS corresponding to the subtree rooted at dir.
	Sub(dir string) (FS, error)
}

// Sub returns an [FS] corresponding to the subtree rooted at fsys's dir.
//
// If dir is ".", Sub returns fsys unchanged.
// Otherwise, if fs implements [SubFS], Sub returns fsys.Sub(dir).
// Otherwise, Sub returns a new [FS] implementation sub that,
// in effect, implements sub.Open(name) as fsys.Open(path.Join(dir, name)).
// The implementation also translates calls to ReadDir, ReadFile, and Glob appropriately.
//
// Note that Sub(os.DirFS("/"), "prefix") is equivalent to os.DirFS("/prefix")
// and that neither of them guarantees to avoid operating system
// accesses outside "/prefix", because the implementation of [os.DirFS]
// does not check for symbolic links inside "/prefix" that point to
// other directories. That is, [os.DirFS] is not a general substitute for a
// chroot-style security mechanism, and Sub does not change that fact.
func ( FS,  string) (FS, error) {
	if !ValidPath() {
		return nil, &PathError{Op: "sub", Path: , Err: errors.New("invalid name")}
	}
	if  == "." {
		return , nil
	}
	if ,  := .(SubFS);  {
		return .Sub()
	}
	return &subFS{, }, nil
}

type subFS struct {
	fsys FS
	dir  string
}

// fullName maps name to the fully-qualified name dir/name.
func ( *subFS) ( string,  string) (string, error) {
	if !ValidPath() {
		return "", &PathError{Op: , Path: , Err: errors.New("invalid name")}
	}
	return path.Join(.dir, ), nil
}

// shorten maps name, which should start with f.dir, back to the suffix after f.dir.
func ( *subFS) ( string) ( string,  bool) {
	if  == .dir {
		return ".", true
	}
	if len() >= len(.dir)+2 && [len(.dir)] == '/' && [:len(.dir)] == .dir {
		return [len(.dir)+1:], true
	}
	return "", false
}

// fixErr shortens any reported names in PathErrors by stripping f.dir.
func ( *subFS) ( error) error {
	if ,  := .(*PathError);  {
		if ,  := .shorten(.Path);  {
			.Path = 
		}
	}
	return 
}

func ( *subFS) ( string) (File, error) {
	,  := .fullName("open", )
	if  != nil {
		return nil, 
	}
	,  := .fsys.Open()
	return , .fixErr()
}

func ( *subFS) ( string) ([]DirEntry, error) {
	,  := .fullName("read", )
	if  != nil {
		return nil, 
	}
	,  := ReadDir(.fsys, )
	return , .fixErr()
}

func ( *subFS) ( string) ([]byte, error) {
	,  := .fullName("read", )
	if  != nil {
		return nil, 
	}
	,  := ReadFile(.fsys, )
	return , .fixErr()
}

func ( *subFS) ( string) ([]string, error) {
	// Check pattern is well-formed.
	if ,  := path.Match(, "");  != nil {
		return nil, 
	}
	if  == "." {
		return []string{"."}, nil
	}

	 := .dir + "/" + 
	,  := Glob(.fsys, )
	for ,  := range  {
		,  := .shorten()
		if ! {
			return nil, errors.New("invalid result from inner fsys Glob: " +  + " not in " + .dir) // can't use fmt in this package
		}
		[] = 
	}
	return , .fixErr()
}

func ( *subFS) ( string) (FS, error) {
	if  == "." {
		return , nil
	}
	,  := .fullName("sub", )
	if  != nil {
		return nil, 
	}
	return &subFS{.fsys, }, nil
}