files: ~Use RPC_FLAG_SENDTO
[meinos.git] / apps / lib / libcdi / fs.c
blob4c4cdbb6f9d815101376e2bfbcf11a22b637ca38
1 /*
2 meinOS - A unix-like x86 microkernel operating system
3 Copyright (C) 2008 Janosch Gräf <janosch.graef@gmx.net>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <cdi.h>
20 #include <cdi/lists.h>
21 #include <llist.h> // for llist_find()
22 #include <cdi/fs.h>
23 #include <errno.h>
24 #include <sys/shm.h>
25 #include <rpc.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <sys/cdefs.h>
29 #include <sys/stat.h>
30 #include <sys/statvfs.h>
31 #include <utime.h>
32 #include <unistd.h>
33 #include <path.h>
35 #define cdi_list_find(list,element) llist_find((llist_t)(list),(element))
37 #define CDI_FS_STREAM(filesystem,resource) { .fs = (filesystem), .res = (resource), .error = 0 }
39 #define CDI_FS_FIFOBUF_SIZE 4096
41 /// @todo use!
42 #define fs_is_reg(res) (res->file!=NULL)
43 #define fs_is_dir(res) (res->dir!=NULL)
44 #define fs_is_link(res) (res->link!=NULL)
45 #define fs_is_block(res) (res->special!=NULL && res->type==CDI_FS_BLOCK)
46 #define fs_is_char(res) (res->special!=NULL && res->type==CDI_FS_CHAR)
47 #define fs_is_fifo(res) (res->special!=NULL && res->type==CDI_FS_FIFO)
48 #define fs_is_sock(res) (res->special!=NULL && res->type==CDI_FS_SOCK)
50 struct cdi_fs_file {
51 struct cdi_fs_res *res;
52 int fh;
53 enum {
54 FILE_REG, // is also for devices
55 FILE_DIR,
56 FILE_FIFO,
57 FILE_LINK
58 } type;
59 uint64_t pos;
60 void *shmbuf;
61 cdi_list_t dirlist;
62 struct {
63 void *buffer;
64 size_t posr;
65 size_t posw;
66 cdi_list_t list;
67 } fifo;
70 /**
71 * Mounts FS driver
72 * @param driver FS driver
73 * @param fs_name Filesystem name
74 * @param mountpoint Mountpoint
75 * @param readonly If FS should be mounted readonly
76 * @return Success?
78 static int cdi_fs_mount(struct cdi_fs_driver *driver,const char *fs_name,const char *mountpoint,const char *dev,int readonly) {
79 CDI_DEBUG("fs_mount(0x%x,%s,%s,%s,%d)\n",driver,fs_name,mountpoint,dev,readonly);
80 int fsid = rpc_call("vfs_regfs",0,fs_name,mountpoint);
81 if (fsid!=-1) {
82 struct cdi_fs_filesystem *filesystem = malloc(sizeof(struct cdi_fs_filesystem));
83 filesystem->driver = driver;
84 filesystem->error = 0;
85 filesystem->read_only = readonly/*||(access(dev,W_OK)==-1)*/;
86 filesystem->fsid = fsid;
87 filesystem->last_fh = 0;
88 filesystem->files = cdi_list_create();
89 filesystem->mountpoint = strdup(mountpoint);
90 if (dev!=NULL) filesystem->data_fh = open(dev,O_RDWR);
91 else filesystem->data_fh = 0;
93 if (filesystem->data_fh!=-1) {
94 filesystem->data_dev = dev;
95 if (driver->fs_init(filesystem)) {
96 cdi_list_push(driver->filesystems,filesystem);
97 cdi_list_push(cdi_filesystems,filesystem);
98 return 0;
101 else {
102 cdi_list_destroy(filesystem->files);
103 close(filesystem->data_fh);
104 free((void*)(filesystem->mountpoint));
105 free(filesystem);
108 return -EIO;
111 static int cdi_fs_mount_rpc(char *fs_name,const char *mountpoint,const char *dev,int readonly) {
112 CDI_DEBUG("fs_mount_rpc(%s,%s,%s,%d)\n",fs_name,mountpoint,dev,readonly);
113 size_t i;
114 struct cdi_fs_driver *driver;
115 if (!*dev) dev = NULL;
116 for (i=0;(driver = cdi_list_get(cdi_drivers,i));i++) {
117 if (strcmp(driver->drv.name,fs_name)==0) return cdi_fs_mount(driver,fs_name,mountpoint,dev,readonly);
119 return -EINVAL;
122 static int cdi_fs_unmount(struct cdi_fs_filesystem *filesystem) {
123 CDI_DEBUG("fs_unmount(0x%x)\n",filesystem);
124 rpc_call("vfs_unregfs",0,filesystem->fsid);
125 return filesystem->driver->fs_destroy(filesystem)?0:-1;
128 static int cdi_fs_unmount_rpc(char *fs_name,char *mountpoint) {
129 CDI_DEBUG("fs_unmount_rpc(%s)\n",fs_name);
130 size_t i,j;
131 struct cdi_fs_driver *driver;
132 struct cdi_fs_filesystem *filesystem;
133 for (i=0;(driver = cdi_list_get(cdi_drivers,i));i++) {
134 if (strcmp(driver->drv.name,fs_name)==0) {
135 for (j=0;(filesystem = cdi_list_get(driver->filesystems,j));j++) {
136 if (strcmp(filesystem->mountpoint,mountpoint)==0) return cdi_fs_unmount(filesystem);
140 return -1;
144 * Find a filesystem
145 * @param fsid FSID
146 * @return Filesystem
148 static struct cdi_fs_filesystem *cdi_fs_find(int fsid) {
149 CDI_DEBUG("fs_find(%d)\n",fsid);
150 size_t i;
151 struct cdi_fs_filesystem *filesystem;
152 for (i=0;(filesystem = cdi_list_get(cdi_filesystems,i));i++) {
153 if (filesystem->fsid==fsid) return filesystem;
155 return NULL;
158 static struct cdi_fs_file *cdi_fs_file_find(struct cdi_fs_filesystem *filesystem,int fh) {
159 CDI_DEBUG("fs_file_find(0x%x,%d)\n",filesystem,fh);
160 size_t i;
161 struct cdi_fs_file *file;
162 for (i=0;(file = cdi_list_get(filesystem->files,i));i++) {
163 if (file->fh==fh) return file;
165 return NULL;
169 * Converts a CDI FS error number into a Clib errno
170 * @param error CDI FS error number
171 * @return Clib errno
173 static int cdi_fs_error2errno(cdi_fs_error_t error) {
174 if (error==CDI_FS_ERROR_NONE) return 0;
175 if (error==CDI_FS_ERROR_IO) return EIO;
176 if (error==CDI_FS_ERROR_ONS) return ENOSYS;
177 if (error==CDI_FS_ERROR_RNF) return ENOENT;
178 if (error==CDI_FS_ERROR_EOF) return EOF;
179 if (error==CDI_FS_ERROR_RO) return EROFS;
180 if (error==CDI_FS_ERROR_INTERNAL) return -1;
181 if (error==CDI_FS_ERROR_NOT_IMPLEMENTED) return ENOTSUP;
182 return -1;
186 * Loads a resource
187 * @param res Resource
188 * @param stream Stream
190 static inline int cdi_fs_loadres(struct cdi_fs_stream *stream) {
191 CDI_DEBUG("fs_loadres(0x%x)\n",stream);
192 return stream->res->loaded?0:(stream->res->res->load(stream)?0:-1);
196 * Unloads a resource
197 * @param res Resource
198 * @param stream Stream
200 static inline int cdi_fs_unloadres(struct cdi_fs_stream *stream) {
201 CDI_DEBUG("fs_unloadres(0x%x)\n",stream);
202 /// @todo Zum Verringern von IO, aber wieder normal machen!
203 return stream->res->loaded?(stream->res->res->load(stream)?0:-1):0;
207 * Gets parent CDI FS resource by path
208 * @param filesystem CDI filesystem
209 * @param path Path to resource
210 * @param filename Reference for filename (will point into path)
211 * @return Resource or NULL
213 static struct cdi_fs_res *cdi_fs_parentres(struct cdi_fs_filesystem *filesystem,char *path_str,char **filename) {
214 CDI_DEBUG("fs_parentres(0x%x,%s)\n",filesystem,path_str);
215 struct cdi_fs_res *res = filesystem->root_res;
216 struct cdi_fs_res *child = res;
217 size_t k;
218 ssize_t i,j;
219 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,res);
221 path_t *path = path_parse(path_str);
222 for (j=path->num_parts-1;j>=0 && path->parts[j][0]==0;j--);
223 for (i=0;i<j;i++) {
224 if (path->parts[i][0]==0) continue;
225 cdi_fs_loadres(&stream);
226 for (k=0;(child = cdi_list_get(res->children,k));k++) {
227 if (strcmp(child->name,path->parts[i])==0) {
228 res = child;
229 stream.res = child;
230 break;
233 if (child==NULL) break;
236 *filename = path_str;
237 if (j<0) strcpy(*filename,"/");
238 else strcpy(path_str,path->parts[j]);
240 return child;
244 * Gets CDI FS resource by path
245 * @param filesystem CDI filesystem
246 * @param path Path to resource
247 * @return Resource or NULL
249 static struct cdi_fs_res *cdi_fs_path2res(struct cdi_fs_filesystem *filesystem,char *path_str) {
250 CDI_DEBUG("fs_path2res(0x%x,%s)\n",filesystem,path_str);
251 struct cdi_fs_res *res = filesystem->root_res;
252 struct cdi_fs_res *child = res;
253 size_t i,k;
254 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,res);
256 path_t *path = path_parse(path_str);
257 for (i=0;i<path->num_parts;i++) {
258 if (path->parts[i][0]==0) continue;
259 cdi_fs_loadres(&stream);
260 for (k=0;(child = cdi_list_get(res->children,k));k++) {
261 if (strcmp(child->name,path->parts[i])==0) {
262 res = child;
263 stream.res = child;
264 break;
267 if (child==NULL) break;
270 return child;
274 * Converts POSIX mode to class (and special file type)
275 * @param mode POSIX mode
276 * @param special Reference for special file type
277 * @return Resource class
279 static cdi_fs_res_class_t cdi_fs_mode2class(mode_t mode,cdi_fs_res_type_t *special) {
280 if (S_ISREG(mode)) return CDI_FS_CLASS_FILE;
281 if (S_ISDIR(mode)) return CDI_FS_CLASS_DIR;
282 if (S_ISLNK(mode)) return CDI_FS_CLASS_LINK;
283 if (S_ISCHR(mode)) {
284 *special = CDI_FS_CHAR;
285 return CDI_FS_CLASS_SPECIAL;
287 if (S_ISBLK(mode)) {
288 *special = CDI_FS_BLOCK;
289 return CDI_FS_CLASS_SPECIAL;
291 if (S_ISFIFO(mode)) {
292 *special = CDI_FS_FIFO;
293 return CDI_FS_CLASS_SPECIAL;
295 if (S_ISSOCK(mode)) {
296 *special = CDI_FS_SOCKET;
297 return CDI_FS_CLASS_SPECIAL;
299 return CDI_FS_CLASS_FILE;
303 * Converts resource class (and special file type) to POSIX mode
304 * @param res Resource
305 * @return POSIX mode
307 static mode_t cdi_fs_class2mode(struct cdi_fs_res *res) {
308 mode_t mode = 0;
309 if (res->file!=NULL) mode |= S_IFREG;
310 if (res->dir!=NULL) mode |= S_IFDIR;
311 if (res->link!=NULL) mode |= S_IFLNK;
312 if (res->special!=NULL) {
313 if (res->type==CDI_FS_CHAR) mode |= S_IFCHR;
314 else if (res->type==CDI_FS_BLOCK) mode |= S_IFBLK;
315 else if (res->type==CDI_FS_FIFO) mode |= S_IFIFO;
316 else if (res->type==CDI_FS_SOCKET) mode |= S_IFSOCK;
318 return mode;
322 * Gets filesize from a resource
323 * @param filesystem CDI Filesystem
324 * @param res Resource
325 * @return Filesize
327 static size_t cdi_fs_filesize(struct cdi_fs_filesystem *filesystem,struct cdi_fs_res *res) {
328 if (res->res->meta_read!=NULL) {
329 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,res);
330 return res->res->meta_read(&stream,CDI_FS_META_SIZE);
332 else return 0;
335 // FIFO functions ///////
338 * Opens a FIFO
339 * @param fs CDI FS
340 * @param fifo FIFO
342 static void fs_fifo_open(struct cdi_fs_filesystem *fs,struct cdi_fs_file *fifo) {
343 size_t i;
345 fifo->fifo.buffer = malloc(CDI_FS_FIFOBUF_SIZE);
346 fifo->fifo.posr = 0;
347 fifo->fifo.posw = 0;
348 fifo->type = FILE_FIFO;
349 fifo->fifo.list = NULL;
351 // find other filehandles of this FIFO
352 struct cdi_fs_file *file;
353 for (i=0;(file = cdi_list_get(fs->files,i));i++) {
354 if (file->res==fifo->res) {
355 fifo->fifo.list = file->fifo.list; // same FIFO, get FIFO list
356 break;
360 if (fifo->fifo.list==NULL) fifo->fifo.list = cdi_list_create(); // first opener of FIFO, create FIFO list
362 // add yourself to list
363 cdi_list_push(fifo->fifo.list,fifo);
367 * Closes a FIFO
368 * @param fs CDI FS
369 * @param fifo FIFO
371 static void fs_fifo_close(struct cdi_fs_filesystem *fs,struct cdi_fs_file *fifo) {
372 free(fifo->fifo.buffer);
374 if (llist_size(fifo->fifo.list)==1) cdi_list_destroy(fifo->fifo.list); // I am the one and only, destroy list
375 else cdi_list_remove(fifo->fifo.list,cdi_list_find(fifo->fifo.list,fifo)); // Else just remove yourself from list
379 * Reads from a FIFO
380 * @param fs CDI FS
381 * @param fifo FIFO
382 * @param size How many bytes to read
383 * @return How many bytes read
385 static ssize_t fs_fifo_read(struct cdi_fs_filesystem *fs,struct cdi_fs_file *fifo,size_t size) {
386 if (size==0) return 0;
388 // calculate free space in buffer
389 size_t size_free = fifo->fifo.posw>=fifo->fifo.posr?fifo->fifo.posw-fifo->fifo.posr:fifo->fifo.posw+CDI_FS_FIFOBUF_SIZE-fifo->fifo.posr;
391 // check if buffer is big enough
392 if (size>size_free) size = size_free;
393 if (size==0) return 0;
395 // if isn't be wrapped around at end just copy
396 if (fifo->fifo.posr<(fifo->fifo.posr+size)%CDI_FS_FIFOBUF_SIZE) memcpy(fifo->shmbuf,fifo->fifo.buffer+fifo->fifo.posr,size);
397 // else you have to do 2 copies
398 else {
399 size_t part1 = CDI_FS_FIFOBUF_SIZE-fifo->fifo.posr;
400 size_t part2 = fifo->fifo.posw;
401 memcpy(fifo->shmbuf,fifo->fifo.buffer+fifo->fifo.posr,part1);
402 memcpy(fifo->shmbuf+part1,fifo->fifo.buffer,part2);
404 fifo->fifo.posr = (fifo->fifo.posr+size)%CDI_FS_FIFOBUF_SIZE;
406 return size;
410 * Writes in a FIFO
411 * @param fs CDI FS
412 * @param fifo FIFO
413 * @param size How many bytes to write
414 * @return How many bytes written
416 static ssize_t fs_fifo_write(struct cdi_fs_filesystem *fs,struct cdi_fs_file *fifo,size_t size) {
417 struct cdi_fs_file *dstfifo;
419 if (size==0 || cdi_list_size(fifo->fifo.list)==1) return 0;
421 // calculate minimum free space in buffers
422 size_t i;
423 size_t size_free = ~0;
424 for (i=0;(dstfifo = cdi_list_get(fifo->fifo.list,i));i++) {
425 if (dstfifo!=fifo) {
426 size_t free = fifo->fifo.posr>fifo->fifo.posw?fifo->fifo.posr-fifo->fifo.posw:fifo->fifo.posr+CDI_FS_FIFOBUF_SIZE-fifo->fifo.posw;
427 if (free<size_free) size_free = free;
431 // check if buffer is big enough
432 if (size>size_free) size = size_free;
433 if (size==0) return 0;
435 for (i=0;(dstfifo = cdi_list_get(fifo->fifo.list,i));i++) {
436 if (dstfifo!=fifo) {
437 // if isn't wrapped around at end just copy
438 if (dstfifo->fifo.posw<(dstfifo->fifo.posr+size)%CDI_FS_FIFOBUF_SIZE) memcpy(dstfifo->fifo.buffer+dstfifo->fifo.posw,fifo->shmbuf,size);
439 // else you have to do 2 copies
440 else {
441 size_t part1 = CDI_FS_FIFOBUF_SIZE-dstfifo->fifo.posw;
442 size_t part2 = dstfifo->fifo.posr;
443 memcpy(dstfifo->fifo.buffer+dstfifo->fifo.posw,fifo->shmbuf,part1);
444 memcpy(dstfifo->fifo.buffer,fifo->shmbuf+part1,part2);
446 dstfifo->fifo.posw = (dstfifo->fifo.posw+size)%CDI_FS_FIFOBUF_SIZE;
450 return size;
453 // Wrapper functions ////
455 /// @todo add other filetypes?
456 static int fs_open(int fsid,int oflag,int shmid) {
457 CDI_DEBUG("fs_open(%d,%d,%d)\n",fsid,oflag,shmid);
458 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
459 if (filesystem!=NULL) {
460 void *shmbuf = shmat(shmid,NULL,0);
461 if (shmbuf!=NULL) {
462 struct cdi_fs_res *res = cdi_fs_path2res(filesystem,shmbuf);
463 if (res!=NULL) {
464 if (fs_is_reg(res) || fs_is_char(res) || fs_is_block(res) || fs_is_fifo(res) || fs_is_link(res)) {
465 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,res);
466 if (cdi_fs_loadres(&stream)==0) {
467 struct cdi_fs_file *file = malloc(sizeof(struct cdi_fs_file));
468 file->res = res;
469 file->fh = filesystem->last_fh++;
470 file->shmbuf = shmbuf;
471 if (fs_is_reg(res) || fs_is_char(res) || fs_is_block(res)) {
472 file->type = FILE_REG;
473 file->pos = (oflag&O_APPEND)?cdi_fs_filesize(filesystem,res):0;
475 else if (fs_is_link(res)) file->type = FILE_LINK;
476 else if (fs_is_fifo(res)) fs_fifo_open(filesystem,file);
477 cdi_list_push(filesystem->files,file);
478 return file->fh;
480 else {
481 shmdt(shmbuf);
482 return -cdi_fs_error2errno(stream.error);
485 else {
486 shmdt(shmbuf);
487 return -ENOENT;
490 else {
491 shmdt(shmbuf);
492 return -ENOENT;
495 else return -errno;
497 else return -EINVAL;
500 static int fs_close(int fsid,int fh) {
501 CDI_DEBUG("fs_close(%d,%d)\n",fsid,fh);
502 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
503 if (filesystem!=NULL) {
504 struct cdi_fs_file *file = cdi_fs_file_find(filesystem,fh);
505 if (file!=NULL) {
506 if (file->type==FILE_REG || file->type==FILE_FIFO) {
507 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,file->res);
508 if (file->res->res->unload(&stream)) {
509 if (file->type==FILE_FIFO) fs_fifo_close(filesystem,file);
510 shmdt(file->shmbuf);
511 cdi_list_remove(filesystem->files,cdi_list_find(filesystem->files,file));
512 free(file);
513 return 0;
515 else return -ENOSYS;
517 else return -EBADF;
519 else return -EBADF;
521 else return -EINVAL;
524 static ssize_t fs_read(int fsid,int fh,size_t count) {
525 CDI_DEBUG("fs_read(%d,%d,%d)\n",fsid,fh,count);
526 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
527 if (filesystem!=NULL) {
528 struct cdi_fs_file *file = cdi_fs_file_find(filesystem,fh);
529 if (file!=NULL) {
530 if (file->type==FILE_REG) {
531 if (file->res->file->read!=NULL) {
532 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,file->res);
533 ssize_t ret = file->res->file->read(&stream,file->pos,count,file->shmbuf);
534 if (ret>0) file->pos += ret;
535 return ret>=0?ret:-cdi_fs_error2errno(stream.error);
537 else return -ENOSYS;
539 else if (file->type==FILE_FIFO) return fs_fifo_read(filesystem,file,count);
540 else return -EBADF;
542 else return -EBADF;
544 else return -EINVAL;
547 static ssize_t fs_write(int fsid,int fh,size_t count) {
548 CDI_DEBUG("fs_write(%d,%d,%d)\n",fsid,fh,count);
550 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
551 if (filesystem!=NULL) {
552 struct cdi_fs_file *file = cdi_fs_file_find(filesystem,fh);
553 if (file!=NULL) {
554 if (file->type==FILE_REG) {
555 if (file->res->file->write!=NULL) {
556 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,file->res);
557 ssize_t ret = file->res->file->write(&stream,file->pos,count,file->shmbuf);
558 if (ret>0) file->pos += ret;
559 return ret>=0?ret:-cdi_fs_error2errno(stream.error);
561 else return -ENOSYS;
563 else if (file->type==FILE_FIFO) return fs_fifo_write(filesystem,file,count);
564 else return -EBADF;
566 else return -EBADF;
568 else return -EINVAL;
571 static off_t fs_seek(int fsid,int fh,off_t off,int whence) {
572 CDI_DEBUG("fs_seek(%d,%d,%d,%d)\n",fsid,fh,off,whence);
573 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
574 if (filesystem!=NULL) {
575 struct cdi_fs_file *file = cdi_fs_file_find(filesystem,fh);
576 if (file!=NULL) {
577 if (file->type==FILE_REG) {
578 if (whence==SEEK_SET) file->pos = off;
579 else if (whence==SEEK_CUR) file->pos += off;
580 else if (whence==SEEK_END) file->pos = cdi_fs_filesize(filesystem,file->res)+off;
581 else return -EINVAL;
582 return (off_t)file->pos;
584 else return -EBADF;
586 else return -EBADF;
588 else return -EINVAL;
591 static int fs_fstat(int fsid,int fh) {
592 CDI_DEBUG("fs_fstat(%d,%d)\n",fsid,fh);
593 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
594 if (filesystem!=NULL) {
595 struct cdi_fs_file *file = cdi_fs_file_find(filesystem,fh);
596 if (file!=NULL) {
597 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,file->res);
598 struct stat *stbuf = file->shmbuf;
599 memset(stbuf,0,sizeof(struct stat));
600 //if (file->res->special!=NULL && file->res->special->dev_read!=NULL) file->res->special->dev_read(&stream,&(stbuf->st_rdev));
601 stbuf->st_size = file->res->res->meta_read(&stream,CDI_FS_META_SIZE);
602 stbuf->st_atime = file->res->res->meta_read(&stream,CDI_FS_META_ACCESSTIME);
603 stbuf->st_mtime = file->res->res->meta_read(&stream,CDI_FS_META_CHANGETIME);
604 stbuf->st_ctime = file->res->res->meta_read(&stream,CDI_FS_META_CREATETIME);
605 stbuf->st_blksize = file->res->res->meta_read(&stream,CDI_FS_META_BLOCKSZ);
606 stbuf->st_blocks = file->res->res->meta_read(&stream,CDI_FS_META_USEDBLOCKS);
607 /// @todo ACL
608 stbuf->st_mode = cdi_fs_class2mode(file->res)|0755;
609 return 0;
611 else return -EBADF;
613 else return -EINVAL;
616 static int fs_unlink(int fsid,char *path) {
617 CDI_DEBUG("fs_unlink(%d,%s)\n",fsid,path);
618 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
619 if (filesystem!=NULL) {
620 struct cdi_fs_res *res = cdi_fs_path2res(filesystem,path);
621 if (res!=NULL) {
622 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,res);
623 //if (!cdi_list_empty(res->children)) return -ENOTEMPTY;
624 if (!(res->file!=NULL?res->res->remove_class(&stream,CDI_FS_CLASS_FILE):1)) return -cdi_fs_error2errno(stream.error);
625 if (!(res->dir!=NULL?res->res->remove_class(&stream,CDI_FS_CLASS_DIR):1)) return -cdi_fs_error2errno(stream.error);
626 if (!(res->link!=NULL?res->res->remove_class(&stream,CDI_FS_CLASS_LINK):1)) return -cdi_fs_error2errno(stream.error);
627 if (!(res->special!=NULL?res->res->remove_class(&stream,CDI_FS_CLASS_SPECIAL):1)) return -cdi_fs_error2errno(stream.error);
628 return res->res->remove(&stream)?0:-cdi_fs_error2errno(stream.error);
630 else return -ENOENT;
632 else return -EINVAL;
635 static int fs_rmdir(int fsid,char *path) {
636 CDI_DEBUG("fs_rmdir(%d,%s)\n",fsid,path);
637 return fs_unlink(fsid,path);
640 static int fs_ftruncate(int fsid,int fh,off_t newsize) {
641 CDI_DEBUG("fs_ftruncate(%d,%d,%d)\n",fsid,fh,newsize);
642 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
643 if (filesystem!=NULL) {
644 struct cdi_fs_file *file = cdi_fs_file_find(filesystem,fh);
645 if (file!=NULL) {
646 if (file->res->file!=NULL && file->type==FILE_REG) {
647 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,file->res);
648 return file->res->file->truncate(&stream,newsize)?0:-cdi_fs_error2errno(stream.error);
650 else return -ENOENT;
652 else return -EBADF;
654 else return -EINVAL;
657 static int fs_opendir(int fsid,int shmid) {
658 CDI_DEBUG("fs_opendir(%d,%d)\n",fsid,shmid);
659 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
660 if (filesystem!=NULL) {
661 void *shmbuf = shmat(shmid,NULL,0);
662 if (shmbuf!=NULL) {
663 struct cdi_fs_res *res = cdi_fs_path2res(filesystem,shmbuf);
664 if (res!=NULL) {
665 if (fs_is_dir(res)) {
666 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,res);
667 if (cdi_fs_loadres(&stream)==0) {
668 struct cdi_fs_file *file = malloc(sizeof(struct cdi_fs_file));
669 file->res = res;
670 file->fh = filesystem->last_fh++;
671 file->type = FILE_DIR;
672 file->pos = 0;
673 file->shmbuf = shmbuf;
674 file->dirlist = NULL;
675 cdi_list_push(filesystem->files,file);
676 return file->fh;
678 else {
679 shmdt(shmbuf);
680 return -cdi_fs_error2errno(stream.error);
683 else {
684 shmdt(shmbuf);
685 return -ENOTDIR;
688 else {
689 shmdt(shmbuf);
690 return -ENOENT;
693 else return -errno;
695 else return -EINVAL;
698 static int fs_readdir(int fsid,int dh) {
699 CDI_DEBUG("fs_readdir(%d,%d)\n",fsid,dh);
700 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
701 if (filesystem!=NULL) {
702 struct cdi_fs_file *file = cdi_fs_file_find(filesystem,dh);
703 if (file!=NULL) {
704 if (file->type==FILE_DIR) {
705 if (file->pos<2) {
706 strcpy(file->shmbuf,file->pos++==0?".":"..");
707 return 0;
709 else {
710 if (file->dirlist==NULL) {
711 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,file->res);
712 cdi_fs_loadres(&stream);
713 if (file->res->dir!=NULL) file->dirlist = file->res->dir->list(&stream);
714 else return -ENOTDIR;
716 struct cdi_fs_res *child = cdi_list_get(file->dirlist,(file->pos++)-2);
717 if (child!=NULL) {
718 strcpy(file->shmbuf,child->name);
719 return 0;
721 else return -ENOENT;
724 else return -ENOTDIR;
726 else return -EBADF;
728 else return -EINVAL;
731 static int fs_closedir(int fsid,int dh) {
732 CDI_DEBUG("fs_closedir(%d,%d)\n",fsid,dh);
733 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
734 if (filesystem!=NULL) {
735 struct cdi_fs_file *file = cdi_fs_file_find(filesystem,dh);
736 if (file!=NULL) {
737 if (file->type==FILE_DIR) {
738 shmdt(file->shmbuf);
739 cdi_list_remove(filesystem->files,cdi_list_find(filesystem->files,file));
740 free(file);
741 return 0;
743 else return -ENOTDIR;
745 else return -EBADF;
747 else return -EINVAL;
750 static off_t fs_seekdir(int fsid,int dh,off_t off) {
751 CDI_DEBUG("fs_seekdir(%d,%d,%d)\n",fsid,dh,off);
752 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
753 if (filesystem!=NULL) {
754 struct cdi_fs_file *file = cdi_fs_file_find(filesystem,dh);
755 if (file!=NULL) {
756 if (file->type==FILE_DIR) {
757 if (off!=-1) file->pos = off;
758 return file->pos;
760 else return -ENOTDIR;
762 else return -EBADF;
764 else return -EINVAL;
767 static int fs_statvfs(int fsid,int shmid) {
768 CDI_DEBUG("fs_statvfs(%d,%d)\n",fsid,shmid);
769 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
770 if (filesystem!=NULL) {
771 struct statvfs *stbuf = shmat(shmid,NULL,0);
772 if (stbuf!=NULL) {
773 memset(stbuf,0,sizeof(struct statvfs));
774 /// @todo
775 shmdt(stbuf);
776 return 0;
778 else return -errno;
780 else return -EINVAL;
783 static int fs_mknod(int fsid,char *path,mode_t mode,dev_t dev) {
784 CDI_DEBUG("fs_mknod(%d,%s,%d,%d)\n",fsid,path,mode,dev);
785 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
786 if (filesystem!=NULL) {
787 if (cdi_fs_path2res(filesystem,path)==NULL) {
788 char *filename;
789 struct cdi_fs_res *res = cdi_fs_parentres(filesystem,path,&filename);
790 if (res!=NULL) {
791 if (res->dir!=NULL) {
792 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,NULL);
793 if (!res->dir->create_child(&stream,filename,res)) return -cdi_fs_error2errno(stream.error);
794 if (cdi_fs_loadres(&stream)==-1) return -cdi_fs_error2errno(stream.error);
795 stream.res->type = 0;
796 if (!stream.res->res->assign_class(&stream,cdi_fs_mode2class(mode,&(stream.res->type)))) return -cdi_fs_error2errno(stream.error);
797 /// @todo ACL
798 return cdi_fs_unloadres(&stream)==0?0:-cdi_fs_error2errno(stream.error);
800 else return -ENOTDIR;
802 else return -ENOENT;
804 else return -EEXIST;
806 else return -EINVAL;
809 static ssize_t fs_readlink(int fsid,int shmid,size_t bufsize) {
810 CDI_DEBUG("fs_readlink(%d,%d,%d)\n",fsid,shmid,bufsize);
811 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
812 if (filesystem!=NULL) {
813 void *shmbuf = shmat(shmid,NULL,0);
814 if (shmbuf!=NULL) {
815 struct cdi_fs_res *res = cdi_fs_path2res(filesystem,shmbuf);
816 if (res!=NULL) {
817 if (res->link!=NULL) {
818 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,res);
819 const char *link = res->link->read_link(&stream);
820 size_t len = 0;
821 if (link!=NULL) {
822 len = strlen(link);
823 strcpy(shmbuf,link);
825 shmdt(shmbuf);
826 return len;
828 else {
829 shmdt(shmbuf);
830 return -ENOLINK;
833 else {
834 shmdt(shmbuf);
835 return -ENOENT;
838 else return -errno;
840 else return -EINVAL;
843 static ssize_t fs_symlink(int fsid,char *src,char *dest) {
844 CDI_DEBUG("fs_symlink(%d,%s,%s)\n",fsid,src,dest);
845 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
846 if (filesystem!=NULL) {
847 char *filename;
848 struct cdi_fs_res *res = cdi_fs_parentres(filesystem,src,&filename);
849 if (res!=NULL) {
850 if (res->dir!=NULL) {
851 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,NULL);
852 if (!res->dir->create_child(&stream,filename,res)) return -cdi_fs_error2errno(stream.error);
853 if (cdi_fs_loadres(&stream)==-1) return -cdi_fs_error2errno(stream.error);
854 stream.res->type = 0;
855 if (!stream.res->res->assign_class(&stream,CDI_FS_CLASS_LINK)) return -cdi_fs_error2errno(stream.error);
856 /// @todo ACL
857 if (!stream.res->link->write_link(&stream,dest)) return -cdi_fs_error2errno(stream.error);
858 return cdi_fs_unloadres(&stream)==0?0:-cdi_fs_error2errno(stream.error);
860 else return -ENOTDIR;
862 else return -ENOENT;
864 else return -EINVAL;
867 static int fs_dup(int fsid,int fh,int shmid) {
868 CDI_DEBUG("fs_dup(%d,%d,%d)\n",fsid,fh,shmid);
869 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
870 if (filesystem!=NULL) {
871 struct cdi_fs_file *file = cdi_fs_file_find(filesystem,fh);
872 if (file!=NULL) {
873 void *shmbuf = shmat(shmid,NULL,0);
874 if (shmbuf!=NULL) {
875 struct cdi_fs_file *new = malloc(sizeof(struct cdi_fs_file));
876 new->fh = filesystem->last_fh++;
877 new->shmbuf = shmbuf;
878 return new->fh;
880 else return -errno;
882 else return -EBADF;
884 else return -EINVAL;
887 static int fs_utime(int fsid,int shmid) {
888 CDI_DEBUG("fs_utime(%d,%d)\n",fsid,shmid);
889 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
890 if (filesystem!=NULL) {
891 void *shmbuf = shmat(shmid,NULL,0);
892 if (shmbuf!=NULL) {
893 struct cdi_fs_res *res = cdi_fs_path2res(filesystem,shmbuf);
894 if (res!=NULL) {
895 struct utimbuf *times = shmbuf+strlen(shmbuf)+1;
896 struct cdi_fs_stream stream = CDI_FS_STREAM(filesystem,res);
897 res->res->meta_write(&stream,CDI_FS_META_ACCESSTIME,times->actime);
898 res->res->meta_write(&stream,CDI_FS_META_CHANGETIME,times->modtime);
899 shmdt(shmbuf);
900 return 0;
902 else {
903 shmdt(shmbuf);
904 return -ENOENT;
907 else return -errno;
909 else return -EINVAL;
912 static int fs_sync(int fsid) {
913 CDI_DEBUG("fs_sync(%d,%d)\n",fsid,shmid);
914 struct cdi_fs_filesystem *filesystem = cdi_fs_find(fsid);
915 if (filesystem!=NULL) {
916 if (filesystem->driver->fs_sync!=NULL) {
917 return filesystem->driver->fs_sync(filesystem)?0:-cdi_fs_error2errno(filesystem->error);
919 else return -ENOSYS;
921 else return -EINVAL;
925 * Initializes CDI FS
926 * @return Success?
928 int cdi_fs_init() {
929 rpc_func(fs_open,"iii",sizeof(int)*3);
930 rpc_func(fs_close,"ii",sizeof(int)*2);
931 rpc_func(fs_read,"iii",sizeof(int)*3);
932 rpc_func(fs_write,"iii",sizeof(int)*3);
933 rpc_func(fs_seek,"iiii",sizeof(int)*4);
934 rpc_func(fs_fstat,"ii",sizeof(int)*2);
935 rpc_func(fs_unlink,"i$",sizeof(int)+PATH_MAX);
936 rpc_func(fs_rmdir,"i$",sizeof(int)+PATH_MAX);
937 //rpc_func(fs_rename,"i$$",sizeof(int)+PATH_MAX*2);
938 rpc_func(fs_ftruncate,"iii",sizeof(int)*3);
939 rpc_func(fs_opendir,"ii",sizeof(int)*2);
940 rpc_func(fs_readdir,"ii",sizeof(int)*2);
941 rpc_func(fs_closedir,"ii",sizeof(int)*2);
942 rpc_func(fs_seekdir,"iii",sizeof(int)*3);
943 rpc_func(fs_statvfs,"ii",sizeof(int));
944 rpc_func(fs_readlink,"iii",sizeof(int)*3);
945 rpc_func(fs_symlink,"i$$",sizeof(int)+PATH_MAX*2);
946 //rpc_func(fs_link,"i$$",sizeof(int)+PATH_MAX*2);
947 rpc_func(fs_mknod,"i$ii",sizeof(int)*3+PATH_MAX);
948 rpc_func(fs_dup,"iii",sizeof(int)*3);
949 /*rpc_func(fs_chown,"i$ii",sizeof(int)*3+PATH_MAX);
950 rpc_func(fs_chmod,"i$i",sizeof(int)*2+PATH_MAX);
951 rpc_func(fs_access,"i$i",sizeof(int)*2+PATH_MAX);*/
952 rpc_func(fs_utime,"ii",sizeof(int)*2);
953 rpc_func(fs_sync,"i",sizeof(int));
954 return 0;
958 * Initializes FS driver
959 * @param driver FS driver
961 void cdi_fs_driver_init(struct cdi_fs_driver* driver) {
962 CDI_DEBUG("fs_driver_init(0x%x)\n",driver);
963 cdi_driver_init((struct cdi_driver*)driver);
964 driver->filesystems = cdi_list_create();
968 * Destroys FS driver
969 * @param driver FS driver
971 void cdi_fs_driver_destroy(struct cdi_fs_driver* driver) {
972 CDI_DEBUG("fs_driver_destroy(0x%x)\n",driver);
973 struct cdi_fs_filesystem *filesystem;
974 while ((filesystem = cdi_list_pop(driver->filesystems))) driver->fs_destroy(filesystem);
975 cdi_list_destroy(driver->filesystems);
976 cdi_driver_destroy((struct cdi_driver*)driver);
980 * Registers FS driver
981 * @param driver FS driver
983 void cdi_fs_driver_register(struct cdi_fs_driver* driver) {
984 CDI_DEBUG("fs_driver_register(0x%x)\n",driver);
985 cdi_driver_register((struct cdi_driver*)driver);
986 char *name;
987 asprintf(&name,"%s_mount",driver->drv.name);
988 rpc_func_create(name,cdi_fs_mount_rpc,"$$$i",NAME_MAX+PATH_MAX+sizeof(int));
989 free(name);
990 asprintf(&name,"%s_unmount",driver->drv.name);
991 rpc_func_create(name,cdi_fs_unmount_rpc,"$$",NAME_MAX);
992 free(name);
995 size_t cdi_fs_data_read(struct cdi_fs_filesystem* fs,uint64_t start,size_t size,void* buffer) {
996 CDI_DEBUG("fs_data_read(0x%x,0x%x,0x%x,0x%x)\n",fs,start,size,buffer);
997 if (fs->data_dev!=NULL) {
998 if (lseek(fs->data_fh,(off_t)start,SEEK_SET)!=-1) return read(fs->data_fh,buffer,size);
1000 return 0;
1003 size_t cdi_fs_data_write(struct cdi_fs_filesystem* fs,uint64_t start,size_t size,const void* buffer) {
1004 CDI_DEBUG("fs_data_write(0x%x,0x%x,0x%x,0x%x)\n",fs,start,size,buffer);
1005 if (fs->data_dev!=NULL) {
1006 if (lseek(fs->data_fh,(off_t)start,SEEK_SET)!=-1) return write(fs->data_fh,buffer,size);
1008 return 0;