blob: 6d5ad12623dfd952c2dd9fd4a1d5a7eb920bb3fc [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#include <string.h>
25#include <stdlib.h>
26#include <debug.h>
27#include <err.h>
28#include <trace.h>
29#include <list.h>
30#include <lk/init.h>
31#include <lib/fs.h>
32#include <kernel/mutex.h>
33
34#define LOCAL_TRACE 0
35
36typedef struct {
37 struct list_node files;
38 struct list_node dcookies;
39
40 mutex_t lock;
41} memfs_t;
42
43typedef struct {
44 struct list_node node;
45 memfs_t *fs;
46
47 // name
48 char *name;
49
50 // main data area
51 uint8_t *ptr;
52 size_t len;
53} memfs_file_t;
54
55struct dircookie {
56 struct list_node node;
57 memfs_t *fs;
58
59 // next entry that will be returned
60 memfs_file_t *next_file;
61};
62
63static memfs_file_t *find_file(memfs_t *mem, const char *name)
64{
65 memfs_file_t *file;
66 list_for_every_entry(&mem->files, file, memfs_file_t, node) {
67 if (!strcmp(name, file->name))
68 return file;
69 }
70
71 return NULL;
72}
73
74static status_t memfs_mount(struct bdev *dev, fscookie **cookie)
75{
76 LTRACEF("dev %p, cookie %p\n", dev, cookie);
77
78 memfs_t *mem = malloc(sizeof(*mem));
79 if (!mem)
80 return ERR_NO_MEMORY;
81
82 list_initialize(&mem->files);
83 list_initialize(&mem->dcookies);
84 mutex_init(&mem->lock);
85
86 *cookie = (fscookie *)mem;
87
88 return NO_ERROR;
89}
90
91static void free_file(memfs_file_t *file)
92{
93 free(file->ptr);
94 free(file->name);
95 free(file);
96}
97
98static status_t memfs_unmount(fscookie *cookie)
99{
100 LTRACEF("cookie %p\n", cookie);
101
102 memfs_t *mem = (memfs_t *)cookie;
103
104 mutex_acquire(&mem->lock);
105
106 // free all the files
107 memfs_file_t *file;
108 while ((file = list_remove_head_type(&mem->files, memfs_file_t, node))) {
109 free_file(file);
110 }
111
112 mutex_release(&mem->lock);
113
114 free(mem);
115
116 return NO_ERROR;
117}
118
119static status_t memfs_create(fscookie *cookie, const char *name, filecookie **fcookie, uint64_t len)
120{
121 status_t err;
122
123 LTRACEF("cookie %p name '%s' filecookie %p len %llu\n", cookie, name, fcookie, len);
124
125 memfs_t *mem = (memfs_t *)cookie;
126
127 if (len >= ULONG_MAX)
128 return ERR_NO_MEMORY;
129
130 // make sure we strip out any leading /
131 name = trim_name(name);
132
133 // we can't handle directories right now, so fail if the file has a / in its name
134 if (strchr(name, '/'))
135 return ERR_NOT_SUPPORTED;
136
137 mutex_acquire(&mem->lock);
138
139 // see if the file already exists
140 if (find_file(mem, name)) {
141 err = ERR_ALREADY_EXISTS;
142 goto out;
143 }
144
145 // allocate a new file
146 memfs_file_t *file = malloc(sizeof(*file));
147 if (!file) {
148 err = ERR_NO_MEMORY;
149 goto out;
150 }
151
152 // allocate the space for it
153 file->ptr = calloc(1, len);
154 if (!file->ptr) {
155 free(file);
156 err = ERR_NO_MEMORY;
157 goto out;
158 }
159 file->len = len;
160
161 // fill in some metadata and stuff it in the file list
162 file->name = strdup(name);
163 file->fs = mem;
164
165 list_add_tail(&mem->files, &file->node);
166
167 *fcookie = (filecookie *)file;
168
169 err = NO_ERROR;
170
171out:
172 mutex_release(&mem->lock);
173
174 return err;
175}
176
177static status_t memfs_open(fscookie *cookie, const char *name, filecookie **fcookie)
178{
179 LTRACEF("cookie %p name '%s' filecookie %p\n", cookie, name, fcookie);
180
181 memfs_t *mem = (memfs_t *)cookie;
182
183 // make sure we strip out any leading /
184 name = trim_name(name);
185
186 mutex_acquire(&mem->lock);
187 memfs_file_t *file = find_file(mem, name);
188 mutex_release(&mem->lock);
189
190 if (!file)
191 return ERR_NOT_FOUND;
192
193 *fcookie = (filecookie *)file;
194
195 return NO_ERROR;
196}
197
198static status_t memfs_remove(fscookie *cookie, const char *name)
199{
200 LTRACEF("cookie %p name '%s'\n", cookie, name);
201
202 memfs_t *mem = (memfs_t *)cookie;
203
204 // make sure we strip out any leading /
205 name = trim_name(name);
206
207 mutex_acquire(&mem->lock);
208 memfs_file_t *file = find_file(mem, name);
209 if (file)
210 list_delete(&file->node);
211 mutex_release(&mem->lock);
212
213 if (!file)
214 return ERR_NOT_FOUND;
215
216 // XXX make sure there are no open file handles
217 free_file(file);
218
219 return NO_ERROR;
220}
221
222static status_t memfs_close(filecookie *fcookie)
223{
224 memfs_file_t *file = (memfs_file_t *)fcookie;
225
226 LTRACEF("cookie %p name '%s'\n", fcookie, file->name);
227
228 return NO_ERROR;
229}
230
231static ssize_t memfs_read(filecookie *fcookie, void *buf, off_t off, size_t len)
232{
233 LTRACEF("filecookie %p buf %p offset %lld len %zu\n", fcookie, buf, off, len);
234
235 memfs_file_t *file = (memfs_file_t *)fcookie;
236
237 if (off < 0)
238 return ERR_INVALID_ARGS;
239
240 mutex_acquire(&file->fs->lock);
241
242 if (off >= file->len) {
243 len = 0;
244 } else if (off + len > file->len) {
245 len = file->len - off;
246 }
247
248 // copy that floppy
249 memcpy(buf, file->ptr + off, len);
250
251 mutex_release(&file->fs->lock);
252
253 return len;
254}
255
256static ssize_t memfs_write(filecookie *fcookie, const void *buf, off_t off, size_t len)
257{
258 LTRACEF("filecookie %p buf %p offset %lld len %zu\n", fcookie, buf, off, len);
259
260 memfs_file_t *file = (memfs_file_t *)fcookie;
261
262 if (off < 0)
263 return ERR_INVALID_ARGS;
264
265 mutex_acquire(&file->fs->lock);
266
267 // see if this write will extend the file
268 if (off + len > file->len) {
269 void *ptr = realloc(file->ptr, off + len);
270 if (!ptr) {
271 mutex_release(&file->fs->lock);
272 return ERR_NO_MEMORY;
273 }
274
275 file->ptr = ptr;
276 file->len = off + len;
277 }
278
279 memcpy(file->ptr + off, buf, len);
280
281 mutex_release(&file->fs->lock);
282
283 return len;
284}
285
286static status_t memfs_stat(filecookie *fcookie, struct file_stat *stat)
287{
288 LTRACEF("filecookie %p stat %p\n", fcookie, stat);
289
290 memfs_file_t *file = (memfs_file_t *)fcookie;
291
292 mutex_acquire(&file->fs->lock);
293
294 if (stat) {
295 stat->is_dir = false;
296 stat->size = file->len;
297 }
298
299 mutex_release(&file->fs->lock);
300
301 return NO_ERROR;
302}
303
304static status_t memfs_opendir(fscookie *cookie, const char *name, dircookie **dcookie)
305{
306 LTRACEF("cookie %p name '%s' dircookie %p\n", cookie, name, dcookie);
307
308 memfs_t *mem = (memfs_t *)cookie;
309
310 // make sure we strip out any leading /
311 name = trim_name(name);
312
313 // at the moment, we only support opening "" (with / stripped)
314 if (strcmp("", name))
315 return ERR_NOT_FOUND;
316
317 // allocate a dir cookie, point it at the first file, and stuff it in the dircookie jar
318 dircookie *dir = malloc(sizeof(*dir));
319 if (!dir)
320 return ERR_NO_MEMORY;
321
322 dir->fs = mem;
323
324 mutex_acquire(&mem->lock);
325 dir->next_file = list_peek_head_type(&mem->files, memfs_file_t, node);
326 list_add_head(&mem->dcookies, &dir->node);
327 mutex_release(&mem->lock);
328
329 *dcookie = dir;
330
331 return NO_ERROR;
332}
333
334static status_t memfs_readdir(dircookie *dcookie, struct dirent *ent)
335{
336 status_t err;
337
338 LTRACEF("dircookie %p ent %p\n", dcookie, ent);
339
340 if (!ent)
341 return ERR_INVALID_ARGS;
342
343 mutex_acquire(&dcookie->fs->lock);
344
345 // return the next file in the list and bump the cursor
346 if (dcookie->next_file) {
347 strlcpy(ent->name, dcookie->next_file->name, sizeof(ent->name));
348 dcookie->next_file = list_next_type(&dcookie->fs->files, &dcookie->next_file->node, memfs_file_t, node);
349 err = NO_ERROR;
350 } else {
351 err = ERR_NOT_FOUND;
352 }
353
354 mutex_release(&dcookie->fs->lock);
355
356 return err;
357}
358
359static status_t memfs_closedir(dircookie *dcookie)
360{
361 LTRACEF("dircookie %p\n", dcookie);
362
363 // free the dircookie
364 mutex_acquire(&dcookie->fs->lock);
365 list_delete(&dcookie->node);
366 mutex_release(&dcookie->fs->lock);
367
368 free(dcookie);
369
370 return NO_ERROR;
371}
372
373static const struct fs_api memfs_api = {
374 .mount = memfs_mount,
375 .unmount = memfs_unmount,
376
377 .create = memfs_create,
378 .open = memfs_open,
379 .remove = memfs_remove,
380 .close = memfs_close,
381
382 .read = memfs_read,
383 .write = memfs_write,
384
385 .stat = memfs_stat,
386
387#if 0
388 status_t (*mkdir)(fscookie *, const char *);
389#endif
390 .opendir = memfs_opendir,
391 .readdir = memfs_readdir,
392 .closedir = memfs_closedir,
393
394};
395
396STATIC_FS_IMPL(memfs, &memfs_api);