|
1 // Copyright © 2015 Steve Francia <spf@spf13.com>. |
|
2 // Copyright 2013 tsuru authors. All rights reserved. |
|
3 // |
|
4 // Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 // you may not use this file except in compliance with the License. |
|
6 // You may obtain a copy of the License at |
|
7 // http://www.apache.org/licenses/LICENSE-2.0 |
|
8 // |
|
9 // Unless required by applicable law or agreed to in writing, software |
|
10 // distributed under the License is distributed on an "AS IS" BASIS, |
|
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
12 // See the License for the specific language governing permissions and |
|
13 // limitations under the License. |
|
14 |
|
15 package mem |
|
16 |
|
17 import ( |
|
18 "bytes" |
|
19 "errors" |
|
20 "io" |
|
21 "os" |
|
22 "path/filepath" |
|
23 "sync" |
|
24 "sync/atomic" |
|
25 ) |
|
26 |
|
27 import "time" |
|
28 |
|
29 const FilePathSeparator = string(filepath.Separator) |
|
30 |
|
31 type File struct { |
|
32 // atomic requires 64-bit alignment for struct field access |
|
33 at int64 |
|
34 readDirCount int64 |
|
35 closed bool |
|
36 readOnly bool |
|
37 fileData *FileData |
|
38 } |
|
39 |
|
40 func NewFileHandle(data *FileData) *File { |
|
41 return &File{fileData: data} |
|
42 } |
|
43 |
|
44 func NewReadOnlyFileHandle(data *FileData) *File { |
|
45 return &File{fileData: data, readOnly: true} |
|
46 } |
|
47 |
|
48 func (f File) Data() *FileData { |
|
49 return f.fileData |
|
50 } |
|
51 |
|
52 type FileData struct { |
|
53 sync.Mutex |
|
54 name string |
|
55 data []byte |
|
56 memDir Dir |
|
57 dir bool |
|
58 mode os.FileMode |
|
59 modtime time.Time |
|
60 } |
|
61 |
|
62 func (d *FileData) Name() string { |
|
63 d.Lock() |
|
64 defer d.Unlock() |
|
65 return d.name |
|
66 } |
|
67 |
|
68 func CreateFile(name string) *FileData { |
|
69 return &FileData{name: name, mode: os.ModeTemporary, modtime: time.Now()} |
|
70 } |
|
71 |
|
72 func CreateDir(name string) *FileData { |
|
73 return &FileData{name: name, memDir: &DirMap{}, dir: true} |
|
74 } |
|
75 |
|
76 func ChangeFileName(f *FileData, newname string) { |
|
77 f.Lock() |
|
78 f.name = newname |
|
79 f.Unlock() |
|
80 } |
|
81 |
|
82 func SetMode(f *FileData, mode os.FileMode) { |
|
83 f.Lock() |
|
84 f.mode = mode |
|
85 f.Unlock() |
|
86 } |
|
87 |
|
88 func SetModTime(f *FileData, mtime time.Time) { |
|
89 f.Lock() |
|
90 setModTime(f, mtime) |
|
91 f.Unlock() |
|
92 } |
|
93 |
|
94 func setModTime(f *FileData, mtime time.Time) { |
|
95 f.modtime = mtime |
|
96 } |
|
97 |
|
98 func GetFileInfo(f *FileData) *FileInfo { |
|
99 return &FileInfo{f} |
|
100 } |
|
101 |
|
102 func (f *File) Open() error { |
|
103 atomic.StoreInt64(&f.at, 0) |
|
104 atomic.StoreInt64(&f.readDirCount, 0) |
|
105 f.fileData.Lock() |
|
106 f.closed = false |
|
107 f.fileData.Unlock() |
|
108 return nil |
|
109 } |
|
110 |
|
111 func (f *File) Close() error { |
|
112 f.fileData.Lock() |
|
113 f.closed = true |
|
114 if !f.readOnly { |
|
115 setModTime(f.fileData, time.Now()) |
|
116 } |
|
117 f.fileData.Unlock() |
|
118 return nil |
|
119 } |
|
120 |
|
121 func (f *File) Name() string { |
|
122 return f.fileData.Name() |
|
123 } |
|
124 |
|
125 func (f *File) Stat() (os.FileInfo, error) { |
|
126 return &FileInfo{f.fileData}, nil |
|
127 } |
|
128 |
|
129 func (f *File) Sync() error { |
|
130 return nil |
|
131 } |
|
132 |
|
133 func (f *File) Readdir(count int) (res []os.FileInfo, err error) { |
|
134 if !f.fileData.dir { |
|
135 return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")} |
|
136 } |
|
137 var outLength int64 |
|
138 |
|
139 f.fileData.Lock() |
|
140 files := f.fileData.memDir.Files()[f.readDirCount:] |
|
141 if count > 0 { |
|
142 if len(files) < count { |
|
143 outLength = int64(len(files)) |
|
144 } else { |
|
145 outLength = int64(count) |
|
146 } |
|
147 if len(files) == 0 { |
|
148 err = io.EOF |
|
149 } |
|
150 } else { |
|
151 outLength = int64(len(files)) |
|
152 } |
|
153 f.readDirCount += outLength |
|
154 f.fileData.Unlock() |
|
155 |
|
156 res = make([]os.FileInfo, outLength) |
|
157 for i := range res { |
|
158 res[i] = &FileInfo{files[i]} |
|
159 } |
|
160 |
|
161 return res, err |
|
162 } |
|
163 |
|
164 func (f *File) Readdirnames(n int) (names []string, err error) { |
|
165 fi, err := f.Readdir(n) |
|
166 names = make([]string, len(fi)) |
|
167 for i, f := range fi { |
|
168 _, names[i] = filepath.Split(f.Name()) |
|
169 } |
|
170 return names, err |
|
171 } |
|
172 |
|
173 func (f *File) Read(b []byte) (n int, err error) { |
|
174 f.fileData.Lock() |
|
175 defer f.fileData.Unlock() |
|
176 if f.closed == true { |
|
177 return 0, ErrFileClosed |
|
178 } |
|
179 if len(b) > 0 && int(f.at) == len(f.fileData.data) { |
|
180 return 0, io.EOF |
|
181 } |
|
182 if int(f.at) > len(f.fileData.data) { |
|
183 return 0, io.ErrUnexpectedEOF |
|
184 } |
|
185 if len(f.fileData.data)-int(f.at) >= len(b) { |
|
186 n = len(b) |
|
187 } else { |
|
188 n = len(f.fileData.data) - int(f.at) |
|
189 } |
|
190 copy(b, f.fileData.data[f.at:f.at+int64(n)]) |
|
191 atomic.AddInt64(&f.at, int64(n)) |
|
192 return |
|
193 } |
|
194 |
|
195 func (f *File) ReadAt(b []byte, off int64) (n int, err error) { |
|
196 atomic.StoreInt64(&f.at, off) |
|
197 return f.Read(b) |
|
198 } |
|
199 |
|
200 func (f *File) Truncate(size int64) error { |
|
201 if f.closed == true { |
|
202 return ErrFileClosed |
|
203 } |
|
204 if f.readOnly { |
|
205 return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")} |
|
206 } |
|
207 if size < 0 { |
|
208 return ErrOutOfRange |
|
209 } |
|
210 if size > int64(len(f.fileData.data)) { |
|
211 diff := size - int64(len(f.fileData.data)) |
|
212 f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...) |
|
213 } else { |
|
214 f.fileData.data = f.fileData.data[0:size] |
|
215 } |
|
216 setModTime(f.fileData, time.Now()) |
|
217 return nil |
|
218 } |
|
219 |
|
220 func (f *File) Seek(offset int64, whence int) (int64, error) { |
|
221 if f.closed == true { |
|
222 return 0, ErrFileClosed |
|
223 } |
|
224 switch whence { |
|
225 case 0: |
|
226 atomic.StoreInt64(&f.at, offset) |
|
227 case 1: |
|
228 atomic.AddInt64(&f.at, int64(offset)) |
|
229 case 2: |
|
230 atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset) |
|
231 } |
|
232 return f.at, nil |
|
233 } |
|
234 |
|
235 func (f *File) Write(b []byte) (n int, err error) { |
|
236 if f.readOnly { |
|
237 return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")} |
|
238 } |
|
239 n = len(b) |
|
240 cur := atomic.LoadInt64(&f.at) |
|
241 f.fileData.Lock() |
|
242 defer f.fileData.Unlock() |
|
243 diff := cur - int64(len(f.fileData.data)) |
|
244 var tail []byte |
|
245 if n+int(cur) < len(f.fileData.data) { |
|
246 tail = f.fileData.data[n+int(cur):] |
|
247 } |
|
248 if diff > 0 { |
|
249 f.fileData.data = append(bytes.Repeat([]byte{00}, int(diff)), b...) |
|
250 f.fileData.data = append(f.fileData.data, tail...) |
|
251 } else { |
|
252 f.fileData.data = append(f.fileData.data[:cur], b...) |
|
253 f.fileData.data = append(f.fileData.data, tail...) |
|
254 } |
|
255 setModTime(f.fileData, time.Now()) |
|
256 |
|
257 atomic.StoreInt64(&f.at, int64(len(f.fileData.data))) |
|
258 return |
|
259 } |
|
260 |
|
261 func (f *File) WriteAt(b []byte, off int64) (n int, err error) { |
|
262 atomic.StoreInt64(&f.at, off) |
|
263 return f.Write(b) |
|
264 } |
|
265 |
|
266 func (f *File) WriteString(s string) (ret int, err error) { |
|
267 return f.Write([]byte(s)) |
|
268 } |
|
269 |
|
270 func (f *File) Info() *FileInfo { |
|
271 return &FileInfo{f.fileData} |
|
272 } |
|
273 |
|
274 type FileInfo struct { |
|
275 *FileData |
|
276 } |
|
277 |
|
278 // Implements os.FileInfo |
|
279 func (s *FileInfo) Name() string { |
|
280 s.Lock() |
|
281 _, name := filepath.Split(s.name) |
|
282 s.Unlock() |
|
283 return name |
|
284 } |
|
285 func (s *FileInfo) Mode() os.FileMode { |
|
286 s.Lock() |
|
287 defer s.Unlock() |
|
288 return s.mode |
|
289 } |
|
290 func (s *FileInfo) ModTime() time.Time { |
|
291 s.Lock() |
|
292 defer s.Unlock() |
|
293 return s.modtime |
|
294 } |
|
295 func (s *FileInfo) IsDir() bool { |
|
296 s.Lock() |
|
297 defer s.Unlock() |
|
298 return s.dir |
|
299 } |
|
300 func (s *FileInfo) Sys() interface{} { return nil } |
|
301 func (s *FileInfo) Size() int64 { |
|
302 if s.IsDir() { |
|
303 return int64(42) |
|
304 } |
|
305 s.Lock() |
|
306 defer s.Unlock() |
|
307 return int64(len(s.data)) |
|
308 } |
|
309 |
|
310 var ( |
|
311 ErrFileClosed = errors.New("File is closed") |
|
312 ErrOutOfRange = errors.New("Out of range") |
|
313 ErrTooLarge = errors.New("Too large") |
|
314 ErrFileNotFound = os.ErrNotExist |
|
315 ErrFileExists = os.ErrExist |
|
316 ErrDestinationExists = os.ErrExist |
|
317 ) |