Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/blueprint/pathtools/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 16 kB image not shown  

Quelle  fs.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pathtools

import (
 "bytes"
 "fmt"
 "io"
 "io/fs"
 "io/ioutil"
 "os"
 "path/filepath"
 "sort"
 "strings"
 "sync"
 "syscall"
 "time"
)

// Based on Andrew Gerrand's "10 things you (probably) dont' know about Go"

type ShouldFollowSymlinks bool

const (
 FollowSymlinks     = ShouldFollowSymlinks(true)
 DontFollowSymlinks = ShouldFollowSymlinks(false)
)

var OsFs FileSystem = &osFs{}

func MockFs(files map[string][]byte) FileSystem {
 fs := &mockFs{
  files:    make(map[string][]byte, len(files)),
  dirs:     make(map[string]bool),
  symlinks: make(map[string]string),
  all:      []string(nil),
 }

 for f, b := range files {
  if tokens := strings.SplitN(f, "->", 2); len(tokens) == 2 {
   fs.symlinks[strings.TrimSpace(tokens[0])] = strings.TrimSpace(tokens[1])
   continue
  }

  fs.files[filepath.Clean(f)] = b
  dir := filepath.Dir(f)
  for dir != "." && dir != "/" {
   fs.dirs[dir] = true
   dir = filepath.Dir(dir)
  }
  fs.dirs[dir] = true
 }

 fs.dirs["."] = true
 fs.dirs["/"] = true

 for f := range fs.files {
  fs.all = append(fs.all, f)
 }

 for d := range fs.dirs {
  fs.all = append(fs.all, d)
 }

 for s := range fs.symlinks {
  fs.all = append(fs.all, s)
 }

 sort.Strings(fs.all)

 return fs
}

type ReaderAtSeekerCloser interface {
 io.Reader
 io.ReaderAt
 io.Seeker
 io.Closer
}

type WriteTruncateCloser interface {
 io.WriteCloser
 Truncate(size int64) error
}

type FileSystem interface {
 // Open opens a file for reading. Follows symlinks.
 Open(name string) (ReaderAtSeekerCloser, error)

 // OpenFile opens a file for read/write, if the file does not exist, and the
 // O_CREATE flag is passed, it is created with mode perm (before umask).
 OpenFile(name string, flag int, perm fs.FileMode) (WriteTruncateCloser, error)

 // Remove removes a file or a directory.
 Remove(path string) error

 // Exists returns whether the file exists and whether it is a directory.  Follows symlinks.
 Exists(name string) (bool, bool, error)

 Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error)
 glob(pattern string) (matches []string, err error)

 // IsDir returns true if the path points to a directory, false it it points to a file.  Follows symlinks.
 // Returns os.ErrNotExist if the path does not exist or is a symlink to a path that does not exist.
 IsDir(name string) (bool, error)

 // IsSymlink returns true if the path points to a symlink, even if that symlink points to a path that does
 // not exist.  Returns os.ErrNotExist if the path does not exist.
 IsSymlink(name string) (bool, error)

 // Lstat returns info on a file without following symlinks.
 Lstat(name string) (os.FileInfo, error)

 // Stat returns info on a file.
 Stat(name string) (os.FileInfo, error)

 // ListDirsRecursive returns a list of all the directories in a path, following symlinks if requested.
 ListDirsRecursive(name string, follow ShouldFollowSymlinks) (dirs []string, err error)

 // ReadDirNames returns a list of everything in a directory.
 ReadDirNames(name string) ([]string, error)

 // Readlink returns the destination of the named symbolic link.
 Readlink(name string) (string, error)
}

// osFs implements FileSystem using the local disk.
type osFs struct {
 srcDir        string
 openFilesChan chan bool
}

func NewOsFs(path string) FileSystem {
 // Darwin has a default limit of 256 open files, rate limit open files to 200
 limit := 200
 return &osFs{
  srcDir:        path,
  openFilesChan: make(chan bool, limit),
 }
}

func (fs *osFs) acquire() {
 if fs.openFilesChan != nil {
  fs.openFilesChan <- true
 }
}

func (fs *osFs) release() {
 if fs.openFilesChan != nil {
  <-fs.openFilesChan
 }
}

func (fs *osFs) toAbs(path string) string {
 if filepath.IsAbs(path) {
  return path
 }
 return filepath.Join(fs.srcDir, path)
}

func (fs *osFs) removeSrcDirPrefix(path string) string {
 if fs.srcDir == "" {
  return path
 }
 rel, err := filepath.Rel(fs.srcDir, path)
 if err != nil {
  panic(fmt.Errorf("unexpected failure in removeSrcDirPrefix filepath.Rel(%s, %s): %s",
   fs.srcDir, path, err))
 }
 if strings.HasPrefix(rel, "../") {
  panic(fmt.Errorf("unexpected relative path outside directory in removeSrcDirPrefix filepath.Rel(%s, %s): %s",
   fs.srcDir, path, rel))
 }
 return rel
}

func (fs *osFs) removeSrcDirPrefixes(paths []string) []string {
 if fs.srcDir != "" {
  for i, path := range paths {
   paths[i] = fs.removeSrcDirPrefix(path)
  }
 }
 return paths
}

// OsFile wraps an os.File to also release open file descriptors semaphore on close
type OsFile struct {
 *os.File
 fs *osFs
}

// Close closes file and releases the open file descriptor semaphore
func (f *OsFile) Close() error {
 if f.File != nil {
  err := f.File.Close()
  f.fs.release()
  f.File = nil
  return err
 }
 return nil
}

func (fs *osFs) Open(name string) (ReaderAtSeekerCloser, error) {
 fs.acquire()
 f, err := os.Open(fs.toAbs(name))
 if err != nil {
  return nil, err
 }
 return &OsFile{f, fs}, nil
}

func (fs *osFs) OpenFile(name string, flag int, perm fs.FileMode) (WriteTruncateCloser, error) {
 fs.acquire()
 f, err := os.OpenFile(fs.toAbs(name), flag, perm)
 if err != nil {
  return nil, err
 }
 return &OsFile{f, fs}, nil
}

func (fs *osFs) Remove(path string) error {
 _, err := os.Stat(fs.toAbs(path))
 if err != nil {
  if os.IsNotExist(err) {
   return nil // Directory doesn't exist, so nothing to remove.
  }
  // Some other error occurred while checking path status
  return fmt.Errorf("failed to check status of directory '%s': %w", path, err)
 }

 return os.RemoveAll(fs.toAbs(path))
}

func (fs *osFs) Exists(name string) (bool, bool, error) {
 stat, err := os.Stat(fs.toAbs(name))
 if err == nil {
  return true, stat.IsDir(), nil
 } else if os.IsNotExist(err) {
  return false, false, nil
 } else {
  return false, false, err
 }
}

func (fs *osFs) IsDir(name string) (bool, error) {
 info, err := os.Stat(fs.toAbs(name))
 if err != nil {
  return false, err
 }
 return info.IsDir(), nil
}

func (fs *osFs) IsSymlink(name string) (bool, error) {
 if info, err := os.Lstat(fs.toAbs(name)); err != nil {
  return false, err
 } else {
  return info.Mode()&os.ModeSymlink != 0, nil
 }
}

func (fs *osFs) Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error) {
 return glob(fs, pattern, excludes, follow)
}

func (fs *osFs) glob(pattern string) ([]string, error) {
 fs.acquire()
 defer fs.release()
 paths, err := filepath.Glob(fs.toAbs(pattern))
 fs.removeSrcDirPrefixes(paths)
 return paths, err
}

func (fs *osFs) Lstat(path string) (stats os.FileInfo, err error) {
 return os.Lstat(fs.toAbs(path))
}

func (fs *osFs) Stat(path string) (stats os.FileInfo, err error) {
 return os.Stat(fs.toAbs(path))
}

// Returns a list of all directories under dir
func (fs *osFs) ListDirsRecursive(name string, follow ShouldFollowSymlinks) (dirs []string, err error) {
 return listDirsRecursive(fs, name, follow)
}

func (fs *osFs) ReadDirNames(name string) ([]string, error) {
 fs.acquire()
 defer fs.release()
 dir, err := os.Open(fs.toAbs(name))
 if err != nil {
  return nil, err
 }
 defer dir.Close()

 contents, err := dir.Readdirnames(-1)
 if err != nil {
  return nil, err
 }

 sort.Strings(contents)
 return contents, nil
}

func (fs *osFs) Readlink(name string) (string, error) {
 return os.Readlink(fs.toAbs(name))
}

type mockFs struct {
 files    map[string][]byte
 dirs     map[string]bool
 symlinks map[string]string
 all      []string
 lock     sync.RWMutex
}

func (m *mockFs) followSymlinks(name string) string {
 dir, file := quickSplit(name)
 if dir != "." && dir != "/" {
  dir = m.followSymlinks(dir)
 }
 name = filepath.Join(dir, file)

 for i := 0; i < 255; i++ {
  i++
  if i > 255 {
   panic("symlink loop")
  }
  to, exists := m.symlinks[name]
  if !exists {
   break
  }
  if filepath.IsAbs(to) {
   name = to
  } else {
   name = filepath.Join(dir, to)
  }
 }
 return name
}

func (m *mockFs) Open(name string) (ReaderAtSeekerCloser, error) {
 m.lock.RLock()
 defer m.lock.RUnlock()
 name = filepath.Clean(name)
 name = m.followSymlinks(name)
 if f, ok := m.files[name]; ok {
  return struct {
   io.Closer
   *bytes.Reader
  }{
   ioutil.NopCloser(nil),
   bytes.NewReader(f),
  }, nil
 }

 return nil, &os.PathError{
  Op:   "open",
  Path: name,
  Err:  os.ErrNotExist,
 }
}

type MockFileWriter struct {
 name string
 fs   *mockFs
}

func (b *MockFileWriter) Write(p []byte) (n int, err error) {
 b.fs.lock.Lock()
 defer b.fs.lock.Unlock()
 b.fs.files[b.name] = append(b.fs.files[b.name], p...)
 return len(p), nil
}

func (b *MockFileWriter) Truncate(size int64) error {
 if size < 0 {
  return os.ErrInvalid
 }
 b.fs.lock.Lock()
 defer b.fs.lock.Unlock()
 f := b.fs.files[b.name]
 if int(size) < len(f) {
  f = f[:size]
 } else {
  f = append(f, make([]byte, int(size)-len(f))...)
 }
 b.fs.files[b.name] = f
 return nil
}

func (b *MockFileWriter) Close() error {
 return nil
}

func (m *mockFs) OpenFile(name string, flag int, perm fs.FileMode) (WriteTruncateCloser, error) {
 // For mockFs we simplify the logic here by just either creating a new file or
 // truncating an existing one.
 m.lock.Lock()
 defer m.lock.Unlock()
 name = filepath.Clean(name)
 name = m.followSymlinks(name)
 m.files[name] = []byte{}
 return &MockFileWriter{
  name: name,
  fs:   m,
 }, nil
}

func (m *mockFs) Remove(name string) error {
 m.lock.RLock()
 defer m.lock.RUnlock()
 name = filepath.Clean(name)
 if _, ok := m.files[name]; ok {
  delete(m.files, name)
 }
 if _, ok := m.symlinks[name]; ok {
  delete(m.symlinks, name)
 }
 // We need to remove all its contents first, and then the directory itself.
 if _, ok := m.dirs[name]; ok {
  for entryPath := range m.files {
   if strings.HasPrefix(entryPath, name+"/") {
    delete(m.files, entryPath)
   }
  }
  for entryPath := range m.symlinks {
   if strings.HasPrefix(entryPath, name+"/") {
    delete(m.symlinks, entryPath)
   }
  }
  for entryPath := range m.dirs {
   if strings.HasPrefix(entryPath, name+"/") {
    delete(m.dirs, entryPath)
   }
  }

  // Finally, remove the directory itself.
  delete(m.dirs, name)
  m.updateAllPaths()
 }
 return nil
}

// updateAllPaths is a helper method to rebuild the 'all' slice.
func (m *mockFs) updateAllPaths() {
 m.all = m.all[:0]
 for f := range m.files {
  m.all = append(m.all, f)
 }
 for d := range m.dirs {
  m.all = append(m.all, d)
 }
 for s := range m.symlinks {
  m.all = append(m.all, s)
 }
 sort.Strings(m.all)
}

func (m *mockFs) Exists(name string) (bool, bool, error) {
 m.lock.RLock()
 defer m.lock.RUnlock()
 name = filepath.Clean(name)
 name = m.followSymlinks(name)
 if _, ok := m.files[name]; ok {
  return ok, false, nil
 }
 if _, ok := m.dirs[name]; ok {
  return ok, true, nil
 }
 return false, false, nil
}

func (m *mockFs) IsDir(name string) (bool, error) {
 m.lock.RLock()
 defer m.lock.RUnlock()
 dir := filepath.Dir(name)
 if dir != "." && dir != "/" {
  isDir, err := m.IsDir(dir)

  if serr, ok := err.(*os.SyscallError); ok && serr.Err == syscall.ENOTDIR {
   isDir = false
  } else if err != nil {
   return false, err
  }

  if !isDir {
   return false, os.NewSyscallError("stat "+name, syscall.ENOTDIR)
  }
 }

 name = filepath.Clean(name)
 name = m.followSymlinks(name)

 if _, ok := m.dirs[name]; ok {
  return true, nil
 }
 if _, ok := m.files[name]; ok {
  return false, nil
 }
 return false, os.ErrNotExist
}

func (m *mockFs) IsSymlink(name string) (bool, error) {
 m.lock.RLock()
 defer m.lock.RUnlock()
 dir, file := quickSplit(name)
 dir = m.followSymlinks(dir)
 name = filepath.Join(dir, file)

 if _, isSymlink := m.symlinks[name]; isSymlink {
  return true, nil
 }
 if _, isDir := m.dirs[name]; isDir {
  return false, nil
 }
 if _, isFile := m.files[name]; isFile {
  return false, nil
 }
 return false, os.ErrNotExist
}

func (m *mockFs) Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error) {
 return glob(m, pattern, excludes, follow)
}

func unescapeGlob(s string) string {
 i := 0
 for i < len(s) {
  if s[i] == '\\' {
   s = s[:i] + s[i+1:]
  } else {
   i++
  }
 }
 return s
}

func (m *mockFs) glob(pattern string) ([]string, error) {
 dir, file := quickSplit(pattern)

 dir = unescapeGlob(dir)
 toDir := m.followSymlinks(dir)

 var matches []string
 for _, f := range m.all {
  fDir, fFile := quickSplit(f)
  if toDir == fDir {
   match, err := filepath.Match(file, fFile)
   if err != nil {
    return nil, err
   }
   if (f == "." || f == "/") && f != pattern {
    // filepath.Glob won't return "." or "/" unless the pattern was "." or "/"
    match = false
   }
   if match {
    matches = append(matches, filepath.Join(dir, fFile))
   }
  }
 }
 return matches, nil
}

type mockStat struct {
 name string
 size int64
 mode os.FileMode
}

func (ms *mockStat) Name() string       { return ms.name }
func (ms *mockStat) IsDir() bool        { return ms.Mode().IsDir() }
func (ms *mockStat) Size() int64        { return ms.size }
func (ms *mockStat) Mode() os.FileMode  { return ms.mode }
func (ms *mockStat) ModTime() time.Time { return time.Time{} }
func (ms *mockStat) Sys() interface{}   { return nil }

func (m *mockFs) Lstat(name string) (os.FileInfo, error) {
 m.lock.RLock()
 defer m.lock.RUnlock()
 dir, file := quickSplit(name)
 dir = m.followSymlinks(dir)
 name = filepath.Join(dir, file)

 ms := mockStat{
  name: file,
 }

 if symlink, isSymlink := m.symlinks[name]; isSymlink {
  ms.mode = os.ModeSymlink
  ms.size = int64(len(symlink))
 } else if _, isDir := m.dirs[name]; isDir {
  ms.mode = os.ModeDir
 } else if _, isFile := m.files[name]; isFile {
  ms.mode = 0
  ms.size = int64(len(m.files[name]))
 } else {
  return nil, os.ErrNotExist
 }

 return &ms, nil
}

func (m *mockFs) Stat(name string) (os.FileInfo, error) {
 m.lock.RLock()
 defer m.lock.RUnlock()
 name = filepath.Clean(name)
 origName := name
 name = m.followSymlinks(name)

 ms := mockStat{
  name: filepath.Base(origName),
  size: int64(len(m.files[name])),
 }

 if _, isDir := m.dirs[name]; isDir {
  ms.mode = os.ModeDir
 } else if _, isFile := m.files[name]; isFile {
  ms.mode = 0
  ms.size = int64(len(m.files[name]))
 } else {
  return nil, os.ErrNotExist
 }

 return &ms, nil
}

func (m *mockFs) ReadDirNames(name string) ([]string, error) {
 name = filepath.Clean(name)
 name = m.followSymlinks(name)

 exists, isDir, err := m.Exists(name)
 if err != nil {
  return nil, err
 }
 if !exists {
  return nil, os.ErrNotExist
 }
 if !isDir {
  return nil, os.NewSyscallError("readdir", syscall.ENOTDIR)
 }

 var ret []string
 for _, f := range m.all {
  dir, file := quickSplit(f)
  if dir == name && len(file) > 0 && file[0] != '.' {
   ret = append(ret, file)
  }
 }
 return ret, nil
}

func (m *mockFs) ListDirsRecursive(name string, follow ShouldFollowSymlinks) ([]string, error) {
 return listDirsRecursive(m, name, follow)
}

func (m *mockFs) Readlink(name string) (string, error) {
 dir, file := quickSplit(name)
 dir = m.followSymlinks(dir)

 origName := name
 name = filepath.Join(dir, file)

 if dest, isSymlink := m.symlinks[name]; isSymlink {
  return dest, nil
 }

 if exists, _, err := m.Exists(name); err != nil {
  return "", err
 } else if !exists {
  return "", os.ErrNotExist
 } else {
  return "", os.NewSyscallError("readlink: "+origName, syscall.EINVAL)
 }
}

func listDirsRecursive(fs FileSystem, name string, follow ShouldFollowSymlinks) ([]string, error) {
 name = filepath.Clean(name)

 isDir, err := fs.IsDir(name)
 if err != nil {
  return nil, err
 }

 if !isDir {
  return nil, nil
 }

 dirs := []string{name}

 subDirs, err := listDirsRecursiveRelative(fs, name, follow, 0)
 if err != nil {
  return nil, err
 }

 for _, d := range subDirs {
  dirs = append(dirs, filepath.Join(name, d))
 }

 return dirs, nil
}

func listDirsRecursiveRelative(fs FileSystem, name string, follow ShouldFollowSymlinks, depth int) ([]string, error) {
 depth++
 if depth > 255 {
  return nil, fmt.Errorf("too many symlinks")
 }
 contents, err := fs.ReadDirNames(name)
 if err != nil {
  return nil, err
 }

 var dirs []string
 for _, f := range contents {
  if f[0] == '.' {
   continue
  }
  f = filepath.Join(name, f)
  var info os.FileInfo
  if follow == DontFollowSymlinks {
   info, err = fs.Lstat(f)
   if err != nil {
    continue
   }
   if info.Mode()&os.ModeSymlink != 0 {
    continue
   }
  } else {
   info, err = fs.Stat(f)
   if err != nil {
    continue
   }
  }
  if info.IsDir() {
   dirs = append(dirs, f)
   subDirs, err := listDirsRecursiveRelative(fs, f, follow, depth)
   if err != nil {
    return nil, err
   }
   for _, s := range subDirs {
    dirs = append(dirs, filepath.Join(f, s))
   }
  }
 }

 for i, d := range dirs {
  rel, err := filepath.Rel(name, d)
  if err != nil {
   return nil, err
  }
  dirs[i] = rel
 }

 return dirs, nil
}

[Dauer der Verarbeitung: 0.25 Sekunden, vorverarbeitet 2026-06-28]