@@ -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,28 +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
125
+ func (fs * Memory ) Lstat (filename string ) (billy.FileInfo , error ) {
126
+ fullpath := fs .fullpath (filename )
127
+ f , has := fs .s .Get (fullpath )
128
+ if ! has {
129
+ return nil , os .ErrNotExist
130
+ }
131
+
132
+ return f .Stat (), nil
133
+ }
134
+
117
135
// ReadDir returns a list of billy.FileInfo in the given directory.
118
136
func (fs * Memory ) ReadDir (path string ) ([]billy.FileInfo , error ) {
119
- 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
+ }
120
143
121
144
var entries []billy.FileInfo
122
- for _ , f := range fs .s .Children (path ) {
145
+ for _ , f := range fs .s .Children (fullpath ) {
123
146
entries = append (entries , f .Stat ())
124
147
}
125
148
@@ -161,49 +184,18 @@ func (fs *Memory) getTempFilename(dir, prefix string) string {
161
184
162
185
// Rename moves a the `from` file to the `to` file.
163
186
func (fs * Memory ) Rename (from , to string ) error {
164
- if fs .renameIfLink (from , to ) {
165
- return nil
166
- }
167
-
168
187
from = fs .Join (fs .base , from )
169
188
to = fs .Join (fs .base , to )
170
189
171
190
return fs .s .Rename (from , to )
172
191
}
173
192
174
- func (fs * Memory ) renameIfLink (from , to string ) bool {
175
- from = clean (fs .Join (fs .base , from ))
176
- to = clean (fs .Join (fs .base , to ))
177
-
178
- if _ , ok := fs .links [from ]; ! ok {
179
- return false
180
- }
181
-
182
- fs .links [to ] = fs .links [from ]
183
- delete (fs .links , from )
184
- return true
185
- }
186
-
187
193
// Remove deletes a given file from storage.
188
194
func (fs * Memory ) Remove (filename string ) error {
189
- if fs .removeIfLink (filename ) {
190
- return nil
191
- }
192
-
193
195
fullpath := fs .Join (fs .base , filename )
194
196
return fs .s .Remove (fullpath )
195
197
}
196
198
197
- func (fs * Memory ) removeIfLink (filename string ) bool {
198
- fullpath := clean (fs .Join (fs .base , filename ))
199
- if _ , ok := fs .links [fullpath ]; ! ok {
200
- return false
201
- }
202
-
203
- delete (fs .links , fullpath )
204
- return true
205
- }
206
-
207
199
// Join joins any number of path elements into a single path, adding a Separator if necessary.
208
200
func (fs * Memory ) Join (elem ... string ) string {
209
201
return filepath .Join (elem ... )
@@ -219,26 +211,37 @@ func (fs *Memory) Dir(path string) billy.Filesystem {
219
211
}
220
212
221
213
func (fs * Memory ) Symlink (target , link string ) error {
222
- 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 {
223
218
return os .ErrExist
224
219
}
225
220
226
- fullpath := clean (fs .Join (fs .base , link ))
227
- fs .links [fullpath ] = internalLink {
228
- Name : link ,
229
- Target : clean (target ),
221
+ if ! os .IsNotExist (err ) {
222
+ return err
230
223
}
231
224
232
- return nil
225
+ target = clean (target )
226
+ return billy .WriteFile (fs , fullpath , []byte (target ), 0777 | os .ModeSymlink )
233
227
}
234
228
235
229
func (fs * Memory ) Readlink (link string ) (string , error ) {
236
- fullpath := clean (fs .Join (fs .base , link ))
237
- if l , ok := fs .links [fullpath ]; ok {
238
- return l .Target , nil
230
+ fullpath := fs .fullpath (link )
231
+ f , has := fs .s .Get (fullpath )
232
+ if ! has {
233
+ return "" , os .ErrNotExist
239
234
}
240
235
241
- 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
242
245
}
243
246
244
247
// Base returns the base path for the filesystem.
@@ -409,3 +412,7 @@ func isReadOnly(flag int) bool {
409
412
func isWriteOnly (flag int ) bool {
410
413
return flag & os .O_WRONLY != 0
411
414
}
415
+
416
+ func isSymlink (m os.FileMode ) bool {
417
+ return m & os .ModeSymlink != 0
418
+ }
0 commit comments