forked from golang/dep
-
Notifications
You must be signed in to change notification settings - Fork 0
/
filesystem.go
136 lines (106 loc) · 2.9 KB
/
filesystem.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// 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 error means that it's very likely that the symlink has a circular reference.
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
}