@@ -21,19 +21,13 @@ type Memory struct {
21
21
s * storage
22
22
23
23
tempCount int
24
- links map [string ]internalLink
25
- }
26
-
27
- type internalLink struct {
28
- Name , Target string
29
24
}
30
25
31
26
//New returns a new Memory filesystem
32
27
func New () * Memory {
33
28
return & Memory {
34
- base : string (separator ),
35
- s : newStorage (),
36
- links : make (map [string ]internalLink ),
29
+ base : string (separator ),
30
+ s : newStorage (),
37
31
}
38
32
}
39
33
@@ -49,44 +43,51 @@ func (fs *Memory) Open(filename string) (billy.File, error) {
49
43
50
44
// OpenFile returns the file from a given name with given flag and permits.
51
45
func (fs * Memory ) OpenFile (filename string , flag int , perm os.FileMode ) (billy.File , error ) {
52
- path := fs .resolvePath (filename )
53
- f , has := fs .s .Get (path )
46
+ fullpath := fs .fullpath (filename )
47
+ f , has := fs .s .Get (fullpath )
54
48
if ! has {
55
49
if ! isCreate (flag ) {
56
50
return nil , os .ErrNotExist
57
51
}
58
52
59
53
var err error
60
- f , err = fs .s .New (path , perm , flag )
54
+ f , err = fs .s .New (fullpath , perm , flag )
61
55
if err != nil {
62
56
return nil , err
63
57
}
58
+ } else {
59
+ if target , isLink := fs .resolveIfLink (fullpath , f ); isLink {
60
+ return fs .OpenFile (target , flag , perm )
61
+ }
64
62
}
65
63
66
64
if f .mode .IsDir () {
67
65
return nil , fmt .Errorf ("cannot open directory: %s" , filename )
68
66
}
69
67
70
- filename , err := filepath .Rel (fs .base , path )
68
+ filename , err := filepath .Rel (fs .base , fullpath )
71
69
if err != nil {
72
70
return nil , err
73
71
}
74
72
75
73
return f .Duplicate (filename , perm , flag ), nil
76
74
}
77
75
78
- func (fs * Memory ) resolvePath (path string ) string {
79
- fullpath := clean (fs .Join (fs .base , path ))
80
- l , ok := fs .links [fullpath ]
81
- if ! ok {
82
- return fullpath
76
+ func (fs * Memory ) fullpath (path string ) string {
77
+ return clean (fs .Join (fs .base , path ))
78
+ }
79
+
80
+ func (fs * Memory ) resolveIfLink (fullpath string , f * file ) (target string , isLink bool ) {
81
+ if ! isSymlink (f .mode ) {
82
+ return fullpath , false
83
83
}
84
84
85
- if isAbs (l .Target ) {
86
- return l .Target
85
+ target = string (f .content .bytes )
86
+ if ! isAbs (target ) {
87
+ target = fs .Join (filepath .Dir (fullpath ), target )
87
88
}
88
89
89
- return fs . resolvePath ( fs . Join ( filepath . Dir ( fullpath ), l . Target ))
90
+ return clean ( target ), true
90
91
}
91
92
92
93
// On Windows OS, IsAbs validates if a path is valid based on if stars with a
@@ -98,41 +99,50 @@ func isAbs(path string) bool {
98
99
99
100
// Stat returns a billy.FileInfo with the information of the requested file.
100
101
func (fs * Memory ) Stat (filename string ) (billy.FileInfo , error ) {
101
- fullpath := fs .resolvePath (filename )
102
+ fullpath := fs .fullpath (filename )
102
103
f , has := fs .s .Get (fullpath )
103
104
if ! has {
104
105
return nil , os .ErrNotExist
105
106
}
106
107
107
- fi := f .Stat ().(* fileInfo )
108
+ fi := f .Stat ()
109
+
110
+ var err error
111
+ if target , isLink := fs .resolveIfLink (fullpath , f ); isLink {
112
+ fi , err = fs .Stat (target )
113
+ if err != nil {
114
+ return nil , err
115
+ }
116
+ }
108
117
109
118
// the name of the file should always the name of the stated file, so we
110
119
// overwrite the Stat returned from the storage with it, since the
111
120
// filename may belong to a link.
112
- fi .name = filepath .Base (filename )
113
-
121
+ fi .(* fileInfo ).name = filepath .Base (filename )
114
122
return fi , nil
115
123
}
116
124
117
125
func (fs * Memory ) Lstat (filename string ) (billy.FileInfo , error ) {
118
- fullpath := clean ( fs .Join ( fs . base , filename ) )
119
- l , ok := fs .links [ fullpath ]
120
- if ! ok {
121
- return fs . Stat ( filename )
126
+ fullpath := fs .fullpath ( filename )
127
+ f , has := fs .s . Get ( fullpath )
128
+ if ! has {
129
+ return nil , os . ErrNotExist
122
130
}
123
131
124
- return & fileInfo {
125
- name : filepath .Base (l .Name ),
126
- mode : 0777 | os .ModeSymlink ,
127
- }, nil
132
+ return f .Stat (), nil
128
133
}
129
134
130
135
// ReadDir returns a list of billy.FileInfo in the given directory.
131
136
func (fs * Memory ) ReadDir (path string ) ([]billy.FileInfo , error ) {
132
- path = fs .resolvePath (path )
137
+ fullpath := fs .fullpath (path )
138
+ if f , has := fs .s .Get (fullpath ); has {
139
+ if target , isLink := fs .resolveIfLink (fullpath , f ); isLink {
140
+ return fs .ReadDir (target )
141
+ }
142
+ }
133
143
134
144
var entries []billy.FileInfo
135
- for _ , f := range fs .s .Children (path ) {
145
+ for _ , f := range fs .s .Children (fullpath ) {
136
146
entries = append (entries , f .Stat ())
137
147
}
138
148
@@ -174,49 +184,18 @@ func (fs *Memory) getTempFilename(dir, prefix string) string {
174
184
175
185
// Rename moves a the `from` file to the `to` file.
176
186
func (fs * Memory ) Rename (from , to string ) error {
177
- if fs .renameIfLink (from , to ) {
178
- return nil
179
- }
180
-
181
187
from = fs .Join (fs .base , from )
182
188
to = fs .Join (fs .base , to )
183
189
184
190
return fs .s .Rename (from , to )
185
191
}
186
192
187
- func (fs * Memory ) renameIfLink (from , to string ) bool {
188
- from = clean (fs .Join (fs .base , from ))
189
- to = clean (fs .Join (fs .base , to ))
190
-
191
- if _ , ok := fs .links [from ]; ! ok {
192
- return false
193
- }
194
-
195
- fs .links [to ] = fs .links [from ]
196
- delete (fs .links , from )
197
- return true
198
- }
199
-
200
193
// Remove deletes a given file from storage.
201
194
func (fs * Memory ) Remove (filename string ) error {
202
- if fs .removeIfLink (filename ) {
203
- return nil
204
- }
205
-
206
195
fullpath := fs .Join (fs .base , filename )
207
196
return fs .s .Remove (fullpath )
208
197
}
209
198
210
- func (fs * Memory ) removeIfLink (filename string ) bool {
211
- fullpath := clean (fs .Join (fs .base , filename ))
212
- if _ , ok := fs .links [fullpath ]; ! ok {
213
- return false
214
- }
215
-
216
- delete (fs .links , fullpath )
217
- return true
218
- }
219
-
220
199
// Join joins any number of path elements into a single path, adding a Separator if necessary.
221
200
func (fs * Memory ) Join (elem ... string ) string {
222
201
return filepath .Join (elem ... )
@@ -232,26 +211,37 @@ func (fs *Memory) Dir(path string) billy.Filesystem {
232
211
}
233
212
234
213
func (fs * Memory ) Symlink (target , link string ) error {
235
- if _ , err := fs .Stat (link ); err == nil {
214
+ fullpath := clean (fs .Join (fs .base , link ))
215
+
216
+ _ , err := fs .Stat (fullpath )
217
+ if err == nil {
236
218
return os .ErrExist
237
219
}
238
220
239
- fullpath := clean (fs .Join (fs .base , link ))
240
- fs .links [fullpath ] = internalLink {
241
- Name : link ,
242
- Target : clean (target ),
221
+ if ! os .IsNotExist (err ) {
222
+ return err
243
223
}
244
224
245
- return nil
225
+ target = clean (target )
226
+ return billy .WriteFile (fs , fullpath , []byte (target ), 0777 | os .ModeSymlink )
246
227
}
247
228
248
229
func (fs * Memory ) Readlink (link string ) (string , error ) {
249
- fullpath := clean (fs .Join (fs .base , link ))
250
- if l , ok := fs .links [fullpath ]; ok {
251
- return l .Target , nil
230
+ fullpath := fs .fullpath (link )
231
+ f , has := fs .s .Get (fullpath )
232
+ if ! has {
233
+ return "" , os .ErrNotExist
252
234
}
253
235
254
- return "" , os .ErrNotExist
236
+ if ! isSymlink (f .mode ) {
237
+ return "" , & os.PathError {
238
+ Op : "readlink" ,
239
+ Path : fullpath ,
240
+ Err : fmt .Errorf ("not a symlink" ),
241
+ }
242
+ }
243
+
244
+ return string (f .content .bytes ), nil
255
245
}
256
246
257
247
// Base returns the base path for the filesystem.
@@ -422,3 +412,7 @@ func isReadOnly(flag int) bool {
422
412
func isWriteOnly (flag int ) bool {
423
413
return flag & os .O_WRONLY != 0
424
414
}
415
+
416
+ func isSymlink (m os.FileMode ) bool {
417
+ return m & os .ModeSymlink != 0
418
+ }
0 commit comments