dep/gps/filesystem.go

137 строки
2.9 KiB
Go

// Copyright 2017 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 gps
import (
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
)
// fsLink represents a symbolic link.
type fsLink struct {
path string
to string
// circular denotes if evaluating the symlink fails with "too many links" error.
// This errors means that it's very likely that the symlink has circual refernce.
circular bool
// broken denotes that attempting to resolve the link fails, most likely because
// the destaination doesn't exist.
broken bool
}
// filesystemState represents the state of a file system.
type filesystemState struct {
root string
dirs []string
files []string
links []fsLink
}
func (s filesystemState) setup() error {
for _, dir := range s.dirs {
p := filepath.Join(s.root, dir)
if err := os.MkdirAll(p, 0777); err != nil {
return errors.Errorf("os.MkdirAll(%q, 0777) err=%q", p, err)
}
}
for _, file := range s.files {
p := filepath.Join(s.root, file)
f, err := os.Create(p)
if err != nil {
return errors.Errorf("os.Create(%q) err=%q", p, err)
}
if err := f.Close(); err != nil {
return errors.Errorf("file %q Close() err=%q", p, err)
}
}
for _, link := range s.links {
p := filepath.Join(s.root, link.path)
// On Windows, relative symlinks confuse filepath.Walk. So, we'll just sigh
// and do absolute links, assuming they are relative to the directory of
// link.path.
//
// Reference: https://github.com/golang/go/issues/17540
//
// TODO(ibrasho): This was fixed in Go 1.9. Remove this when support for
// 1.8 is dropped.
dir := filepath.Dir(p)
to := ""
if link.to != "" {
to = filepath.Join(dir, link.to)
}
if err := os.Symlink(to, p); err != nil {
return errors.Errorf("os.Symlink(%q, %q) err=%q", to, p, err)
}
}
return nil
}
// deriveFilesystemState returns a filesystemState based on the state of
// the filesystem on root.
func deriveFilesystemState(root string) (filesystemState, error) {
fs := filesystemState{root: root}
err := filepath.Walk(fs.root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == fs.root {
return nil
}
relPath, err := filepath.Rel(fs.root, path)
if err != nil {
return err
}
if (info.Mode() & os.ModeSymlink) != 0 {
l := fsLink{path: relPath}
l.to, err = filepath.EvalSymlinks(path)
if err != nil && strings.HasSuffix(err.Error(), "too many links") {
l.circular = true
} else if err != nil && os.IsNotExist(err) {
l.broken = true
} else if err != nil {
return err
}
fs.links = append(fs.links, l)
return nil
}
if info.IsDir() {
fs.dirs = append(fs.dirs, relPath)
return nil
}
fs.files = append(fs.files, relPath)
return nil
})
if err != nil {
return filesystemState{}, err
}
return fs, nil
}