1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package mem
16
17 import (
18 "bytes"
19 "errors"
20 "io"
21 "io/fs"
22 "os"
23 "path/filepath"
24 "sync"
25 "sync/atomic"
26 "time"
27
28 "github.com/spf13/afero/internal/common"
29 )
30
31 const FilePathSeparator = string(filepath.Separator)
32
33 var _ fs.ReadDirFile = &File{}
34
35 type File struct {
36
37 at int64
38 readDirCount int64
39 closed bool
40 readOnly bool
41 fileData *FileData
42 }
43
44 func NewFileHandle(data *FileData) *File {
45 return &File{fileData: data}
46 }
47
48 func NewReadOnlyFileHandle(data *FileData) *File {
49 return &File{fileData: data, readOnly: true}
50 }
51
52 func (f File) Data() *FileData {
53 return f.fileData
54 }
55
56 type FileData struct {
57 sync.Mutex
58 name string
59 data []byte
60 memDir Dir
61 dir bool
62 mode os.FileMode
63 modtime time.Time
64 uid int
65 gid int
66 }
67
68 func (d *FileData) Name() string {
69 d.Lock()
70 defer d.Unlock()
71 return d.name
72 }
73
74 func CreateFile(name string) *FileData {
75 return &FileData{name: name, mode: os.ModeTemporary, modtime: time.Now()}
76 }
77
78 func CreateDir(name string) *FileData {
79 return &FileData{name: name, memDir: &DirMap{}, dir: true, modtime: time.Now()}
80 }
81
82 func ChangeFileName(f *FileData, newname string) {
83 f.Lock()
84 f.name = newname
85 f.Unlock()
86 }
87
88 func SetMode(f *FileData, mode os.FileMode) {
89 f.Lock()
90 f.mode = mode
91 f.Unlock()
92 }
93
94 func SetModTime(f *FileData, mtime time.Time) {
95 f.Lock()
96 setModTime(f, mtime)
97 f.Unlock()
98 }
99
100 func setModTime(f *FileData, mtime time.Time) {
101 f.modtime = mtime
102 }
103
104 func SetUID(f *FileData, uid int) {
105 f.Lock()
106 f.uid = uid
107 f.Unlock()
108 }
109
110 func SetGID(f *FileData, gid int) {
111 f.Lock()
112 f.gid = gid
113 f.Unlock()
114 }
115
116 func GetFileInfo(f *FileData) *FileInfo {
117 return &FileInfo{f}
118 }
119
120 func (f *File) Open() error {
121 atomic.StoreInt64(&f.at, 0)
122 atomic.StoreInt64(&f.readDirCount, 0)
123 f.fileData.Lock()
124 f.closed = false
125 f.fileData.Unlock()
126 return nil
127 }
128
129 func (f *File) Close() error {
130 f.fileData.Lock()
131 f.closed = true
132 if !f.readOnly {
133 setModTime(f.fileData, time.Now())
134 }
135 f.fileData.Unlock()
136 return nil
137 }
138
139 func (f *File) Name() string {
140 return f.fileData.Name()
141 }
142
143 func (f *File) Stat() (os.FileInfo, error) {
144 return &FileInfo{f.fileData}, nil
145 }
146
147 func (f *File) Sync() error {
148 return nil
149 }
150
151 func (f *File) Readdir(count int) (res []os.FileInfo, err error) {
152 if !f.fileData.dir {
153 return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")}
154 }
155 var outLength int64
156
157 f.fileData.Lock()
158 files := f.fileData.memDir.Files()[f.readDirCount:]
159 if count > 0 {
160 if len(files) < count {
161 outLength = int64(len(files))
162 } else {
163 outLength = int64(count)
164 }
165 if len(files) == 0 {
166 err = io.EOF
167 }
168 } else {
169 outLength = int64(len(files))
170 }
171 f.readDirCount += outLength
172 f.fileData.Unlock()
173
174 res = make([]os.FileInfo, outLength)
175 for i := range res {
176 res[i] = &FileInfo{files[i]}
177 }
178
179 return res, err
180 }
181
182 func (f *File) Readdirnames(n int) (names []string, err error) {
183 fi, err := f.Readdir(n)
184 names = make([]string, len(fi))
185 for i, f := range fi {
186 _, names[i] = filepath.Split(f.Name())
187 }
188 return names, err
189 }
190
191
192 func (f *File) ReadDir(n int) ([]fs.DirEntry, error) {
193 fi, err := f.Readdir(n)
194 if err != nil {
195 return nil, err
196 }
197 di := make([]fs.DirEntry, len(fi))
198 for i, f := range fi {
199 di[i] = common.FileInfoDirEntry{FileInfo: f}
200 }
201 return di, nil
202 }
203
204 func (f *File) Read(b []byte) (n int, err error) {
205 f.fileData.Lock()
206 defer f.fileData.Unlock()
207 if f.closed {
208 return 0, ErrFileClosed
209 }
210 if len(b) > 0 && int(f.at) == len(f.fileData.data) {
211 return 0, io.EOF
212 }
213 if int(f.at) > len(f.fileData.data) {
214 return 0, io.ErrUnexpectedEOF
215 }
216 if len(f.fileData.data)-int(f.at) >= len(b) {
217 n = len(b)
218 } else {
219 n = len(f.fileData.data) - int(f.at)
220 }
221 copy(b, f.fileData.data[f.at:f.at+int64(n)])
222 atomic.AddInt64(&f.at, int64(n))
223 return
224 }
225
226 func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
227 prev := atomic.LoadInt64(&f.at)
228 atomic.StoreInt64(&f.at, off)
229 n, err = f.Read(b)
230 atomic.StoreInt64(&f.at, prev)
231 return
232 }
233
234 func (f *File) Truncate(size int64) error {
235 if f.closed {
236 return ErrFileClosed
237 }
238 if f.readOnly {
239 return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")}
240 }
241 if size < 0 {
242 return ErrOutOfRange
243 }
244 f.fileData.Lock()
245 defer f.fileData.Unlock()
246 if size > int64(len(f.fileData.data)) {
247 diff := size - int64(len(f.fileData.data))
248 f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{0o0}, int(diff))...)
249 } else {
250 f.fileData.data = f.fileData.data[0:size]
251 }
252 setModTime(f.fileData, time.Now())
253 return nil
254 }
255
256 func (f *File) Seek(offset int64, whence int) (int64, error) {
257 if f.closed {
258 return 0, ErrFileClosed
259 }
260 switch whence {
261 case io.SeekStart:
262 atomic.StoreInt64(&f.at, offset)
263 case io.SeekCurrent:
264 atomic.AddInt64(&f.at, offset)
265 case io.SeekEnd:
266 atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset)
267 }
268 return f.at, nil
269 }
270
271 func (f *File) Write(b []byte) (n int, err error) {
272 if f.closed {
273 return 0, ErrFileClosed
274 }
275 if f.readOnly {
276 return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")}
277 }
278 n = len(b)
279 cur := atomic.LoadInt64(&f.at)
280 f.fileData.Lock()
281 defer f.fileData.Unlock()
282 diff := cur - int64(len(f.fileData.data))
283 var tail []byte
284 if n+int(cur) < len(f.fileData.data) {
285 tail = f.fileData.data[n+int(cur):]
286 }
287 if diff > 0 {
288 f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{0o0}, int(diff)), b...)...)
289 f.fileData.data = append(f.fileData.data, tail...)
290 } else {
291 f.fileData.data = append(f.fileData.data[:cur], b...)
292 f.fileData.data = append(f.fileData.data, tail...)
293 }
294 setModTime(f.fileData, time.Now())
295
296 atomic.AddInt64(&f.at, int64(n))
297 return
298 }
299
300 func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
301 atomic.StoreInt64(&f.at, off)
302 return f.Write(b)
303 }
304
305 func (f *File) WriteString(s string) (ret int, err error) {
306 return f.Write([]byte(s))
307 }
308
309 func (f *File) Info() *FileInfo {
310 return &FileInfo{f.fileData}
311 }
312
313 type FileInfo struct {
314 *FileData
315 }
316
317
318 func (s *FileInfo) Name() string {
319 s.Lock()
320 _, name := filepath.Split(s.name)
321 s.Unlock()
322 return name
323 }
324
325 func (s *FileInfo) Mode() os.FileMode {
326 s.Lock()
327 defer s.Unlock()
328 return s.mode
329 }
330
331 func (s *FileInfo) ModTime() time.Time {
332 s.Lock()
333 defer s.Unlock()
334 return s.modtime
335 }
336
337 func (s *FileInfo) IsDir() bool {
338 s.Lock()
339 defer s.Unlock()
340 return s.dir
341 }
342 func (s *FileInfo) Sys() interface{} { return nil }
343 func (s *FileInfo) Size() int64 {
344 if s.IsDir() {
345 return int64(42)
346 }
347 s.Lock()
348 defer s.Unlock()
349 return int64(len(s.data))
350 }
351
352 var (
353 ErrFileClosed = errors.New("File is closed")
354 ErrOutOfRange = errors.New("out of range")
355 ErrTooLarge = errors.New("too large")
356 ErrFileNotFound = os.ErrNotExist
357 ErrFileExists = os.ErrExist
358 ErrDestinationExists = os.ErrExist
359 )
360
View as plain text