- filled in group field, previously missing.
[npfs.git] / fs / ramfs.c
blob0fb84b91c811fb168e7e5624b321c5b085018a8f
1 /*
2 * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net>
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * LATCHESAR IONKOV AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
23 #define _XOPEN_SOURCE 600
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <pthread.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <assert.h>
32 #include "npfs.h"
34 #define ROOTPERM 0755
35 #define NELEM(x) (sizeof(x)/sizeof((x)[0]))
37 typedef struct File File;
38 typedef struct Fid Fid;
40 struct File {
41 pthread_mutex_t lock;
42 int refcount;
43 char* name;
44 u32 perm;
45 u64 length;
46 u32 atime;
47 u32 mtime;
48 Npuser* uid;
49 Npgroup* gid;
50 Npuser* muid;
51 char* extension;
52 Npqid qid;
53 int excl;
55 File* parent;
56 File* next; /* siblings, protected by parent lock */
57 File* prev;
59 File* dirents; /* if directory */
60 File* dirlast;
62 u8* data;
63 u64 datasize;
66 struct Fid {
67 File* file;
68 int omode;
69 u64 diroffset;
70 File* dirent;
73 static Npsrv *srv;
74 static File *root;
75 static u64 qidpath;
76 static int blksize;
78 static char *Enoextension = "empty extension while creating special file";
79 static char *Enospace = "no space left";
80 //static char *E = "";
82 static Npfcall* ramfs_attach(Npfid *fid, Npfid *afid, Npstr *uname, Npstr *aname);
83 static int ramfs_clone(Npfid *fid, Npfid *newfid);
84 static int ramfs_walk(Npfid *fid, Npstr* wname, Npqid *wqid);
85 static Npfcall* ramfs_open(Npfid *fid, u8 mode);
86 static Npfcall* ramfs_create(Npfid *fid, Npstr *name, u32 perm, u8 mode,
87 Npstr *extension);
88 static Npfcall* ramfs_read(Npfid *fid, u64 offset, u32 count, Npreq *);
89 static Npfcall* ramfs_write(Npfid *fid, u64 offset, u32 count, u8 *data, Npreq *);
90 static Npfcall* ramfs_clunk(Npfid *fid);
91 static Npfcall* ramfs_remove(Npfid *fid);
92 static Npfcall* ramfs_stat(Npfid *fid);
93 static Npfcall* ramfs_wstat(Npfid *fid, Npstat *stat);
94 static void ramfs_fiddestroy(Npfid *fid);
96 static void
97 file_incref0(File *f)
99 f->refcount++;
102 static void
103 file_incref(File *f)
105 if (!f)
106 return;
108 pthread_mutex_lock(&f->lock);
109 file_incref0(f);
110 pthread_mutex_unlock(&f->lock);
113 static int
114 file_decrefimpl(File *f, int lock)
116 int ret;
118 if (!f)
119 return 0;
121 if (lock)
122 pthread_mutex_lock(&f->lock);
124 ret = --f->refcount;
125 if (!ret) {
126 assert(f->dirents == NULL);
127 free(f->name);
128 free(f->extension);
129 free(f->data);
130 if (lock)
131 pthread_mutex_unlock(&f->lock);
133 pthread_mutex_destroy(&f->lock);
134 free(f);
135 } else if (lock)
136 pthread_mutex_unlock(&f->lock);
138 return ret;
141 static int
142 file_decref0(File *f)
144 return file_decrefimpl(f, 0);
147 static int
148 file_decref(File *f)
150 return file_decrefimpl(f, 1);
153 static int
154 truncate_file(File *f, u64 size)
156 int n;
157 u8* buf;
159 if (size == 0) {
160 free(f->data);
161 f->data = NULL;
162 f->datasize = 0;
163 return 0;
166 n = (size/blksize + (size%blksize?1:0)) * blksize;
167 buf = realloc(f->data, n);
168 if (!buf)
169 return -1;
171 f->data = buf;
172 f->datasize = n;
173 return 0;
176 static File *
177 find_file(File *dir, char *name)
179 File *ret;
180 File *f;
182 if (strcmp(name, "..") == 0)
183 return dir->parent;
185 ret = NULL;
186 pthread_mutex_lock(&dir->lock);
187 for(f = dir->dirents; f != NULL; f = f->next)
188 if (strcmp(name, f->name) == 0) {
189 ret = f;
190 break;
192 pthread_mutex_unlock(&dir->lock);
194 return ret;
197 static int
198 check_perm(File *dir, Npuser *user, int perm)
200 int n;
202 if (!user)
203 return 0;
205 if (!perm)
206 return 1;
208 n = dir->perm & 7;
209 if (dir->uid == user) {
210 n |= (dir->perm >> 6) & 7;
211 n |= (dir->perm >> 3) & 7;
214 if (!(n & perm)) {
215 np_werror(Eperm, EPERM);
216 return 0;
219 return 1;
222 static File*
223 file_create(File *parent, char *name, int perm, Npuser *user)
225 File *file;
227 file = malloc(sizeof(*file));
228 pthread_mutex_init(&file->lock, NULL);
229 file->refcount = 0;
230 file->name = name;
231 file->perm = perm;
232 file->length = 0;
233 file->atime = time(NULL);
234 file->mtime = time(NULL);
235 file->uid = user;
236 file->gid = user->dfltgroup;
237 file->muid = user;
238 file->extension = NULL;
239 file->excl = 0;
241 file->parent = parent;
242 file->next = NULL;
243 file->prev = NULL;
244 file->dirents = NULL;
245 file->dirlast = NULL;
246 file->data = NULL;
247 file->datasize = 0;
248 truncate_file(file, 0);
249 file->qid.type = perm >> 24;
250 file->qid.version = 0;
251 file->qid.path = qidpath++;
253 return file;
256 static void
257 file2wstat(File *f, Npwstat *wstat)
259 wstat->size = 0;
260 wstat->type = 0;
261 wstat->dev = 0;
262 wstat->qid = f->qid;
263 wstat->mode = f->perm;
264 wstat->atime = f->atime;
265 wstat->mtime = f->mtime;
266 wstat->length = f->length;
267 wstat->name = f->name;
268 wstat->uid = f->uid->uname;
269 wstat->gid = f->gid->gname;
270 wstat->muid = f->muid->uname;
271 wstat->extension = f->extension;
272 wstat->n_uid = f->uid->uid;
273 wstat->n_gid = f->gid->gid;
274 wstat->n_muid = f->muid->uid;
277 static Fid*
278 fidalloc() {
279 Fid *f;
281 f = malloc(sizeof(*f));
282 f->file = NULL;
283 f->omode = -1;
284 f->diroffset = 0;
285 f->dirent = NULL;
287 return f;
290 static void
291 ramfs_connclose(Npconn *conn)
293 exit(0);
296 static void
297 ramfs_fiddestroy(Npfid *fid)
299 Fid *f;
301 f = fid->aux;
302 if (!f)
303 return;
305 file_decref(f->file);
306 file_decref(f->dirent);
307 free(f);
310 static Npfcall*
311 ramfs_attach(Npfid *nfid, Npfid *nafid, Npstr *uname, Npstr *aname)
313 Npfcall* ret;
314 Fid *fid;
315 Npuser *user;
317 user = NULL;
318 ret = NULL;
320 if (nafid != NULL) {
321 np_werror(Enoauth, EIO);
322 return NULL;
325 if (!check_perm(root, user, 4))
326 goto done;
328 nfid->user = user;
330 fid = fidalloc();
331 fid->omode = -1;
332 fid->omode = -1;
333 fid->file = root;
334 file_incref(root);
335 nfid->aux = fid;
336 np_fid_incref(nfid);
338 ret = np_create_rattach(&fid->file->qid);
339 done:
340 return ret;
343 static int
344 ramfs_clone(Npfid *fid, Npfid *newfid)
346 Fid *f, *nf;
348 f = fid->aux;
349 nf = fidalloc();
350 nf->file = f->file;
351 file_incref(f->file);
352 newfid->aux = nf;
354 return 1;
357 static int
358 ramfs_walk(Npfid *fid, Npstr* wname, Npqid *wqid)
360 char *name;
361 Fid *f;
362 File *file, *nfile;
364 f = fid->aux;
365 file = f->file;
367 if (!check_perm(file, fid->user, 1))
368 return 0;
370 name = np_strdup(wname);
371 nfile = find_file(file, name);
372 free(name);
373 if (!nfile) {
374 np_werror(Enotfound, ENOENT);
375 return 0;
378 file_incref(nfile);
379 file_decref(file);
380 f->file = nfile;
382 *wqid = nfile->qid;
383 return 1;
386 static Npfcall*
387 ramfs_open(Npfid *fid, u8 mode)
389 int m;
390 Fid *f;
391 File *file;
392 Npfcall *ret;
394 ret = NULL;
395 f = fid->aux;
396 m = 0;
397 switch (mode & 3) {
398 case Oread:
399 m = 4;
400 break;
402 case Owrite:
403 m = 2;
404 break;
406 case Ordwr:
407 m = 6;
408 break;
410 case Oexec:
411 m = 1;
412 break;
415 if (mode & Otrunc)
416 m |= 2;
418 file = f->file;
419 pthread_mutex_lock(&file->lock);
420 if (!check_perm(file, fid->user, m))
421 goto done;
423 if (file->perm & Dmdir) {
424 f->diroffset = 0;
425 f->dirent = file->dirents;
426 file_incref(f->dirent);
427 } else {
428 if (mode & Otrunc)
429 truncate_file(file, 0);
431 if (mode & Oexcl) {
432 if (file->excl) {
433 np_werror(Eopen, EPERM);
434 goto done;
436 file->excl = 1;
439 f->omode = mode;
440 ret = np_create_ropen(&file->qid, 0);
442 done:
443 pthread_mutex_unlock(&file->lock);
444 return ret;
447 static Npfcall*
448 ramfs_create(Npfid *fid, Npstr *name, u32 perm, u8 mode, Npstr *extension)
450 int m;
451 Fid *f;
452 File *dir, *file, *nf;
453 char *sname;
455 sname = NULL;
456 file = NULL;
458 if (perm & Dmlink) {
459 np_werror(Eperm, EPERM);
460 goto error;
463 f = fid->aux;
464 dir = f->file;
465 sname = np_strdup(name);
466 nf = find_file(dir, sname);
467 if (nf) {
468 np_werror(Eexist, EEXIST);
469 goto error;
472 if (!strcmp(sname, ".") || !strcmp(sname, "..")) {
473 np_werror(Eexist, EEXIST);
474 goto error;
477 if (!check_perm(dir, fid->user, 2))
478 goto error;
480 if (perm & Dmdir)
481 perm &= ~0777 | (dir->perm & 0777);
482 else
483 perm &= ~0666 | (dir->perm & 0666);
485 pthread_mutex_lock(&dir->lock);
486 file = file_create(dir, sname, perm, dir->uid);
487 file_incref(file);
488 if (dir->dirlast) {
489 dir->dirlast->next = file;
490 file->prev = dir->dirlast;
491 } else
492 dir->dirents = file;
494 dir->dirlast = file;
495 dir->muid = fid->user;
496 dir->mtime = time(NULL);
497 dir->qid.version++;
498 pthread_mutex_unlock(&dir->lock);
500 /* we have to decref the dir because we remove it from the fid,
501 then we have to incref it because it has a new child file,
502 let's just skip playing with the ref */
503 f->file = file;
504 f->omode = mode;
505 file_incref(file);
507 if (perm&(Dmnamedpipe|Dmsymlink|Dmlink|Dmdevice|Dmsocket)) {
508 if (!fid->conn->dotu) {
509 np_werror(Eperm, EPERM);
510 goto error;
513 file->extension = np_strdup(extension);
514 } else {
515 m = 0;
516 switch (mode & 3) {
517 case Oread:
518 m = 4;
519 break;
521 case Owrite:
522 m = 2;
523 break;
525 case Ordwr:
526 m = 6;
527 break;
529 case Oexec:
530 m = 1;
531 break;
534 if (mode & Otrunc)
535 m |= 2;
537 if (!check_perm(file, fid->user, m)) {
538 file_decref(file);
539 goto error;
542 if (mode & Oexcl)
543 file->excl = 1;
545 if (file->perm & Dmdir) {
546 f->diroffset = 0;
547 f->dirent = file->dirents;
548 file_incref(f->dirent);
552 return np_create_rcreate(&file->qid, 0);
554 error:
555 return NULL;
558 static Npfcall*
559 ramfs_read(Npfid *fid, u64 offset, u32 count, Npreq *req)
561 int i, n;
562 Fid *f;
563 File *file, *cf;
564 Npfcall *ret;
565 u8* buf;
566 Npwstat wstat;
568 f = fid->aux;
569 buf = malloc(count);
570 file = f->file;
571 if (file->perm & Dmdir) {
572 pthread_mutex_lock(&file->lock);
573 if (offset == 0 && f->diroffset != 0) {
574 file_decref(f->dirent);
575 f->dirent = file->dirents;
576 file_incref(f->dirent);
577 f->diroffset = 0;
580 n = 0;
581 cf = f->dirent;
582 for(n = 0, cf = f->dirent; n<count && cf != NULL; cf = cf->next) {
583 file2wstat(cf, &wstat);
584 i = np_serialize_stat(&wstat, buf + n, count - n - 1,
585 fid->conn->dotu);
587 if (i==0)
588 break;
590 n += i;
593 f->diroffset += n;
594 file_incref(cf);
595 file_decref(f->dirent);
596 f->dirent = cf;
597 pthread_mutex_unlock(&file->lock);
598 } else {
599 n = count;
600 if (file->length < offset+count)
601 n = file->length - offset;
603 if (n < 0)
604 n = 0;
606 memmove(buf, file->data + offset, n);
609 pthread_mutex_lock(&file->lock);
610 file->atime = time(NULL);
611 pthread_mutex_unlock(&file->lock);
613 ret = np_create_rread(n, buf);
614 free(buf);
615 return ret;
618 static Npfcall*
619 ramfs_write(Npfid *fid, u64 offset, u32 count, u8 *data, Npreq *req)
621 Fid *f;
622 File *file;
624 f = fid->aux;
625 file = f->file;
626 if (f->omode & Oappend)
627 offset = file->length;
629 if (file->length < offset+count) {
630 pthread_mutex_lock(&file->lock);
631 if (truncate_file(file, offset+count)) {
632 pthread_mutex_unlock(&file->lock);
633 np_werror(Enospace, ENOSPC);
634 return NULL;
637 if (offset+count > file->datasize) {
638 if (file->datasize - offset > 0)
639 count = file->datasize - offset;
640 else
641 count = 0;
644 if (count) {
645 if (file->length < offset)
646 memset(file->data+file->length, 0,
647 offset - file->length);
649 file->length = offset+count;
651 pthread_mutex_unlock(&file->lock);
654 if (count)
655 memmove(file->data + offset, data, count);
657 pthread_mutex_lock(&file->lock);
658 file->mtime = time(NULL);
659 file->atime = time(NULL);
660 file->qid.version++;
661 file->muid = fid->user;
662 pthread_mutex_unlock(&file->lock);
663 return np_create_rwrite(count);
666 static Npfcall*
667 ramfs_clunk(Npfid *fid)
669 np_fid_decref(fid);
670 return np_create_rclunk();
673 static Npfcall*
674 ramfs_remove(Npfid *fid)
676 Fid *f;
677 File *file;
678 Npfcall *ret;
680 ret = NULL;
681 f = fid->aux;
682 file = f->file;
683 pthread_mutex_lock(&file->lock);
684 if (file->perm&Dmdir && file->dirents) {
685 pthread_mutex_unlock(&file->lock);
686 np_werror(Enotempty, EIO);
687 return NULL;
689 pthread_mutex_unlock(&file->lock);
691 pthread_mutex_lock(&file->parent->lock);
692 if (!check_perm(file->parent, fid->user, 2))
693 goto done;
695 if (file->parent->dirents == file)
696 file->parent->dirents = file->next;
697 else
698 file->prev->next = file->next;
700 if (file->next)
701 file->next->prev = file->prev;
703 if (file == file->parent->dirlast)
704 file->parent->dirlast = file->prev;
706 file->prev = NULL;
707 file->next = NULL;
709 file->parent->muid = fid->user;
710 file->parent->mtime = time(NULL);
711 file->parent->qid.version++;
713 file_decref(file);
714 file_decref0(file->parent);
715 ret = np_create_rremove();
716 np_fid_decref(fid);
718 done:
719 pthread_mutex_unlock(&file->parent->lock);
720 return ret;
724 static Npfcall*
725 ramfs_stat(Npfid *fid)
727 Fid *f;
728 Npwstat wstat;
730 f = fid->aux;
731 file2wstat(f->file, &wstat);
732 return np_create_rstat(&wstat, fid->conn->dotu);
735 static Npfcall*
736 ramfs_wstat(Npfid *fid, Npstat *stat)
738 int lockparent, lockfile;
739 Fid *f;
740 File *file, *nf;
741 Npfcall *ret;
742 char *sname, *oldname;
743 u64 length, oldlength;
744 u32 oldperm;
745 u32 oldmtime;
747 ret = NULL;
748 oldlength = ~0;
749 oldperm = ~0;
750 oldmtime = ~0;
751 oldname = NULL;
753 f = fid->aux;
754 file = f->file;
755 if (file->perm&(Dmnamedpipe|Dmsymlink|Dmlink|Dmdevice) && fid->conn->dotu) {
756 np_werror(Eperm, EPERM);
757 goto out;
760 pthread_mutex_lock(&file->lock);
761 lockfile = 1;
763 lockparent = stat->name.len != 0;
764 if (lockparent)
765 pthread_mutex_lock(&file->parent->lock);
767 oldname = NULL;
768 if (stat->name.len != 0) {
769 if (!check_perm(file->parent, fid->user, 2))
770 goto out;
772 sname = np_strdup(&stat->name);
773 nf = find_file(file->parent, sname);
775 if (nf) {
776 free(sname);
777 np_werror(Eexist, EEXIST);
778 goto out;
781 oldname = file->name;
782 file->name = sname;
785 if (stat->length != (u64)~0) {
786 if (!check_perm(file, fid->user, 2) || file->perm&Dmdir)
787 goto out;
789 oldlength = file->length;
790 length = stat->length;
791 if (truncate_file(file, length)) {
792 np_werror(Enospace, ENOSPC);
793 goto out;
796 if (length > file->datasize)
797 length = file->datasize;
799 if (file->length < length)
800 memset(file->data+file->length, 0, length-file->length);
802 file->length = length;
805 if (stat->mode != (u32)~0) {
806 if (file->uid != fid->user) {
807 np_werror(Eperm, EPERM);
808 goto out;
811 oldperm = file->perm;
812 file->perm = stat->mode;
815 if (stat->mtime != (u32)~0) {
816 if (file->uid != fid->user) {
817 np_werror(Eperm, EPERM);
818 goto out;
821 oldmtime = file->mtime;
822 file->mtime = stat->mtime;
825 ret = np_create_rwstat();
827 out:
828 if (np_haserror()) {
829 if (oldname) {
830 free(file->name);
831 file->name = oldname;
834 if (oldperm != ~0)
835 file->perm = oldperm;
837 if (oldmtime != ~0)
838 file->mtime = oldmtime;
840 if (oldlength != ~0) {
841 file->length = oldlength;
842 truncate_file(file, oldlength);
844 } else {
845 free(oldname);
846 if (stat->length != ~0) {
847 truncate_file(file, file->length);
848 memset(file->data + file->length, 0, file->datasize - file->length);
852 if (lockfile)
853 pthread_mutex_unlock(&file->lock);
855 if (lockparent)
856 pthread_mutex_unlock(&file->parent->lock);
858 return ret;
861 void
862 usage()
864 fprintf(stderr, "ramfs: -d -u user -w nthreads -b blocksize "
865 "-o mount-options mount-point\n");
866 exit(-1);
870 main(int argc, char **argv)
872 int c, debuglevel, nwthreads, fd;
873 pid_t pid;
874 Npuser *user;
875 char *opts, *logfile, *s;
877 debuglevel = 0;
878 blksize = 8192;
879 nwthreads = 4;
880 opts = "";
881 logfile = "/tmp/ramfs.log";
882 user = np_default_users->uid2user(np_default_users, getuid());
883 while ((c = getopt(argc, argv, "du:w:b:o:l:")) != -1) {
884 switch (c) {
885 case 'd':
886 debuglevel = 1;
887 break;
889 case 'u':
890 user = np_default_users->uname2user(np_default_users, optarg);
891 break;
893 case 'b':
894 blksize = strtol(optarg, &s, 10);
895 if (*s != '\0')
896 usage();
897 break;
899 case 'w':
900 nwthreads = strtol(optarg, &s, 10);
901 if (*s != '\0')
902 usage();
903 break;
905 case 'o':
906 opts = optarg;
907 break;
909 case 'l':
910 logfile = optarg;
911 break;
913 default:
914 fprintf(stderr, "invalid option\n");
918 if (!user) {
919 fprintf(stderr, "invalid user\n");
920 return -1;
923 fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0666);
924 if (fd < 0) {
925 fprintf(stderr, "cannot open log file %s: %d\n", logfile, errno);
926 return -1;
929 close(0);
930 close(1);
931 close(2);
932 if (dup2(fd, 2) < 0) {
933 fprintf(stderr, "dup failed: %d\n", errno);
934 return -1;
937 pid = fork();
938 if (pid < 0)
939 return -1;
940 else if (pid != 0)
941 return 0;
943 setsid();
944 chdir("/");
946 root = file_create(NULL, strdup(""), ROOTPERM | Dmdir, user);
947 file_incref(root);
948 root->parent = root;
950 srv = np_pipesrv_create(nwthreads);
951 if (!srv)
952 return -1;
954 srv->dotu = 1;
955 srv->connclose = ramfs_connclose;
956 srv->attach = ramfs_attach;
957 srv->clone = ramfs_clone;
958 srv->walk = ramfs_walk;
959 srv->open = ramfs_open;
960 srv->create = ramfs_create;
961 srv->read = ramfs_read;
962 srv->write = ramfs_write;
963 srv->clunk = ramfs_clunk;
964 srv->remove = ramfs_remove;
965 srv->stat = ramfs_stat;
966 srv->wstat = ramfs_wstat;
967 srv->fiddestroy = ramfs_fiddestroy;
968 srv->debuglevel = debuglevel;
970 if (optind >= argc)
971 usage();
973 if (np_pipesrv_mount(srv, argv[optind], user->uname, 0, opts))
974 return -1;
976 while (1) {
977 sleep(100);
980 return 0;