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
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
35 #define NELEM(x) (sizeof(x)/sizeof((x)[0]))
37 typedef struct File File
;
38 typedef struct Fid Fid
;
56 File
* next
; /* siblings, protected by parent lock */
59 File
* dirents
; /* if directory */
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
,
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
);
108 pthread_mutex_lock(&f
->lock
);
110 pthread_mutex_unlock(&f
->lock
);
114 file_decrefimpl(File
*f
, int lock
)
122 pthread_mutex_lock(&f
->lock
);
126 assert(f
->dirents
== NULL
);
131 pthread_mutex_unlock(&f
->lock
);
133 pthread_mutex_destroy(&f
->lock
);
136 pthread_mutex_unlock(&f
->lock
);
142 file_decref0(File
*f
)
144 return file_decrefimpl(f
, 0);
150 return file_decrefimpl(f
, 1);
154 truncate_file(File
*f
, u64 size
)
166 n
= (size
/blksize
+ (size
%blksize
?1:0)) * blksize
;
167 buf
= realloc(f
->data
, n
);
177 find_file(File
*dir
, char *name
)
182 if (strcmp(name
, "..") == 0)
186 pthread_mutex_lock(&dir
->lock
);
187 for(f
= dir
->dirents
; f
!= NULL
; f
= f
->next
)
188 if (strcmp(name
, f
->name
) == 0) {
192 pthread_mutex_unlock(&dir
->lock
);
198 check_perm(File
*dir
, Npuser
*user
, int perm
)
209 if (dir
->uid
== user
) {
210 n
|= (dir
->perm
>> 6) & 7;
211 n
|= (dir
->perm
>> 3) & 7;
215 np_werror(Eperm
, EPERM
);
223 file_create(File
*parent
, char *name
, int perm
, Npuser
*user
)
227 file
= malloc(sizeof(*file
));
228 pthread_mutex_init(&file
->lock
, NULL
);
233 file
->atime
= time(NULL
);
234 file
->mtime
= time(NULL
);
236 file
->gid
= user
->dfltgroup
;
238 file
->extension
= NULL
;
241 file
->parent
= parent
;
244 file
->dirents
= NULL
;
245 file
->dirlast
= NULL
;
248 truncate_file(file
, 0);
249 file
->qid
.type
= perm
>> 24;
250 file
->qid
.version
= 0;
251 file
->qid
.path
= qidpath
++;
257 file2wstat(File
*f
, Npwstat
*wstat
)
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
;
281 f
= malloc(sizeof(*f
));
291 ramfs_connclose(Npconn
*conn
)
297 ramfs_fiddestroy(Npfid
*fid
)
305 file_decref(f
->file
);
306 file_decref(f
->dirent
);
311 ramfs_attach(Npfid
*nfid
, Npfid
*nafid
, Npstr
*uname
, Npstr
*aname
)
321 np_werror(Enoauth
, EIO
);
325 if (!check_perm(root
, user
, 4))
338 ret
= np_create_rattach(&fid
->file
->qid
);
344 ramfs_clone(Npfid
*fid
, Npfid
*newfid
)
351 file_incref(f
->file
);
358 ramfs_walk(Npfid
*fid
, Npstr
* wname
, Npqid
*wqid
)
367 if (!check_perm(file
, fid
->user
, 1))
370 name
= np_strdup(wname
);
371 nfile
= find_file(file
, name
);
374 np_werror(Enotfound
, ENOENT
);
387 ramfs_open(Npfid
*fid
, u8 mode
)
419 pthread_mutex_lock(&file
->lock
);
420 if (!check_perm(file
, fid
->user
, m
))
423 if (file
->perm
& Dmdir
) {
425 f
->dirent
= file
->dirents
;
426 file_incref(f
->dirent
);
429 truncate_file(file
, 0);
433 np_werror(Eopen
, EPERM
);
440 ret
= np_create_ropen(&file
->qid
, 0);
443 pthread_mutex_unlock(&file
->lock
);
448 ramfs_create(Npfid
*fid
, Npstr
*name
, u32 perm
, u8 mode
, Npstr
*extension
)
452 File
*dir
, *file
, *nf
;
459 np_werror(Eperm
, EPERM
);
465 sname
= np_strdup(name
);
466 nf
= find_file(dir
, sname
);
468 np_werror(Eexist
, EEXIST
);
472 if (!strcmp(sname
, ".") || !strcmp(sname
, "..")) {
473 np_werror(Eexist
, EEXIST
);
477 if (!check_perm(dir
, fid
->user
, 2))
481 perm
&= ~0777 | (dir
->perm
& 0777);
483 perm
&= ~0666 | (dir
->perm
& 0666);
485 pthread_mutex_lock(&dir
->lock
);
486 file
= file_create(dir
, sname
, perm
, dir
->uid
);
489 dir
->dirlast
->next
= file
;
490 file
->prev
= dir
->dirlast
;
495 dir
->muid
= fid
->user
;
496 dir
->mtime
= time(NULL
);
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 */
507 if (perm
&(Dmnamedpipe
|Dmsymlink
|Dmlink
|Dmdevice
|Dmsocket
)) {
508 if (!fid
->conn
->dotu
) {
509 np_werror(Eperm
, EPERM
);
513 file
->extension
= np_strdup(extension
);
537 if (!check_perm(file
, fid
->user
, m
)) {
545 if (file
->perm
& Dmdir
) {
547 f
->dirent
= file
->dirents
;
548 file_incref(f
->dirent
);
552 return np_create_rcreate(&file
->qid
, 0);
559 ramfs_read(Npfid
*fid
, u64 offset
, u32 count
, Npreq
*req
)
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
);
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,
595 file_decref(f
->dirent
);
597 pthread_mutex_unlock(&file
->lock
);
600 if (file
->length
< offset
+count
)
601 n
= file
->length
- offset
;
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
);
619 ramfs_write(Npfid
*fid
, u64 offset
, u32 count
, u8
*data
, Npreq
*req
)
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
);
637 if (offset
+count
> file
->datasize
) {
638 if (file
->datasize
- offset
> 0)
639 count
= file
->datasize
- offset
;
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
);
655 memmove(file
->data
+ offset
, data
, count
);
657 pthread_mutex_lock(&file
->lock
);
658 file
->mtime
= time(NULL
);
659 file
->atime
= time(NULL
);
661 file
->muid
= fid
->user
;
662 pthread_mutex_unlock(&file
->lock
);
663 return np_create_rwrite(count
);
667 ramfs_clunk(Npfid
*fid
)
670 return np_create_rclunk();
674 ramfs_remove(Npfid
*fid
)
683 pthread_mutex_lock(&file
->lock
);
684 if (file
->perm
&Dmdir
&& file
->dirents
) {
685 pthread_mutex_unlock(&file
->lock
);
686 np_werror(Enotempty
, EIO
);
689 pthread_mutex_unlock(&file
->lock
);
691 pthread_mutex_lock(&file
->parent
->lock
);
692 if (!check_perm(file
->parent
, fid
->user
, 2))
695 if (file
->parent
->dirents
== file
)
696 file
->parent
->dirents
= file
->next
;
698 file
->prev
->next
= file
->next
;
701 file
->next
->prev
= file
->prev
;
703 if (file
== file
->parent
->dirlast
)
704 file
->parent
->dirlast
= file
->prev
;
709 file
->parent
->muid
= fid
->user
;
710 file
->parent
->mtime
= time(NULL
);
711 file
->parent
->qid
.version
++;
714 file_decref0(file
->parent
);
715 ret
= np_create_rremove();
719 pthread_mutex_unlock(&file
->parent
->lock
);
725 ramfs_stat(Npfid
*fid
)
731 file2wstat(f
->file
, &wstat
);
732 return np_create_rstat(&wstat
, fid
->conn
->dotu
);
736 ramfs_wstat(Npfid
*fid
, Npstat
*stat
)
738 int lockparent
, lockfile
;
742 char *sname
, *oldname
;
743 u64 length
, oldlength
;
755 if (file
->perm
&(Dmnamedpipe
|Dmsymlink
|Dmlink
|Dmdevice
) && fid
->conn
->dotu
) {
756 np_werror(Eperm
, EPERM
);
760 pthread_mutex_lock(&file
->lock
);
763 lockparent
= stat
->name
.len
!= 0;
765 pthread_mutex_lock(&file
->parent
->lock
);
768 if (stat
->name
.len
!= 0) {
769 if (!check_perm(file
->parent
, fid
->user
, 2))
772 sname
= np_strdup(&stat
->name
);
773 nf
= find_file(file
->parent
, sname
);
777 np_werror(Eexist
, EEXIST
);
781 oldname
= file
->name
;
785 if (stat
->length
!= (u64
)~0) {
786 if (!check_perm(file
, fid
->user
, 2) || file
->perm
&Dmdir
)
789 oldlength
= file
->length
;
790 length
= stat
->length
;
791 if (truncate_file(file
, length
)) {
792 np_werror(Enospace
, ENOSPC
);
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
);
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
);
821 oldmtime
= file
->mtime
;
822 file
->mtime
= stat
->mtime
;
825 ret
= np_create_rwstat();
831 file
->name
= oldname
;
835 file
->perm
= oldperm
;
838 file
->mtime
= oldmtime
;
840 if (oldlength
!= ~0) {
841 file
->length
= oldlength
;
842 truncate_file(file
, oldlength
);
846 if (stat
->length
!= ~0) {
847 truncate_file(file
, file
->length
);
848 memset(file
->data
+ file
->length
, 0, file
->datasize
- file
->length
);
853 pthread_mutex_unlock(&file
->lock
);
856 pthread_mutex_unlock(&file
->parent
->lock
);
864 fprintf(stderr
, "ramfs: -d -u user -w nthreads -b blocksize "
865 "-o mount-options mount-point\n");
870 main(int argc
, char **argv
)
872 int c
, debuglevel
, nwthreads
, fd
;
875 char *opts
, *logfile
, *s
;
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) {
890 user
= np_default_users
->uname2user(np_default_users
, optarg
);
894 blksize
= strtol(optarg
, &s
, 10);
900 nwthreads
= strtol(optarg
, &s
, 10);
914 fprintf(stderr
, "invalid option\n");
919 fprintf(stderr
, "invalid user\n");
923 fd
= open(logfile
, O_WRONLY
| O_APPEND
| O_CREAT
, 0666);
925 fprintf(stderr
, "cannot open log file %s: %d\n", logfile
, errno
);
932 if (dup2(fd
, 2) < 0) {
933 fprintf(stderr
, "dup failed: %d\n", errno
);
946 root
= file_create(NULL
, strdup(""), ROOTPERM
| Dmdir
, user
);
950 srv
= np_pipesrv_create(nwthreads
);
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
;
973 if (np_pipesrv_mount(srv
, argv
[optind
], user
->uname
, 0, opts
))