- filled in group field, previously missing.
[npfs.git] / fs / ramfs2.c
blob05793f7aeda569b2fc9e55757e75a86db058703d
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;
39 struct File {
40 u8* data;
41 u64 datasize;
44 static void rfs_connclose(Npconn *conn);
45 static int rfs_read(Npfilefid* file, u64 offset, u32 count, u8* data, Npreq *);
46 static int rfs_write(Npfilefid* file, u64 offset, u32 count, u8* data, Npreq *);
47 static int rfs_wstat(Npfile*, Npstat*);
48 static void rfs_destroy(Npfile*);
49 static Npfile* rfs_create(Npfile *dir, char *name, u32 perm, Npuser *uid,
50 Npgroup *gid, char *extension);
51 static Npfile* rfs_first(Npfile *dir);
52 static Npfile* rfs_next(Npfile *dir, Npfile *prevchild);
53 static int rfs_wstat(Npfile*, Npstat*);
54 static int rfs_remove(Npfile *dir, Npfile *file);
56 static Npsrv *srv;
57 static Npfile *root;
58 static u64 qidpath;
59 static int blksize;
61 static char *Enospace = "no space left";
63 Npfileops fileops = {
64 .read = rfs_read,
65 .write = rfs_write,
66 .wstat = rfs_wstat,
67 .destroy = rfs_destroy,
70 Npdirops dirops = {
71 .create = rfs_create,
72 .first = rfs_first,
73 .next = rfs_next,
74 .wstat = rfs_wstat,
75 .remove = rfs_remove,
76 .destroy = rfs_destroy,
79 static File* file_alloc(void);
81 void
82 usage()
84 fprintf(stderr, "ramfs: -d -u user -w nthreads -b blocksize "
85 "-o mount-options mount-point\n");
86 exit(-1);
89 int
90 main(int argc, char **argv)
92 int c, debuglevel, nwthreads, fd;
93 pid_t pid;
94 Npuser *user;
95 char *opts, *logfile, *s;
97 debuglevel = 0;
98 blksize = 8192;
99 nwthreads = 1;
100 opts = "";
101 logfile = "/tmp/ramfs.log";
102 user = np_default_users->uid2user(np_default_users, getuid());
103 while ((c = getopt(argc, argv, "du:w:b:o:l:")) != -1) {
104 switch (c) {
105 case 'd':
106 debuglevel = 1;
107 break;
109 case 'u':
110 user = np_default_users->uname2user(np_default_users, optarg);
111 break;
113 case 'b':
114 blksize = strtol(optarg, &s, 10);
115 if (*s != '\0')
116 usage();
117 break;
119 case 'w':
120 nwthreads = strtol(optarg, &s, 10);
121 if (*s != '\0')
122 usage();
123 break;
125 case 'o':
126 opts = optarg;
127 break;
129 case 'l':
130 logfile = optarg;
131 break;
133 default:
134 fprintf(stderr, "invalid option\n");
138 if (!user) {
139 fprintf(stderr, "invalid user\n");
140 return -1;
143 fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0666);
144 if (fd < 0) {
145 fprintf(stderr, "cannot open log file %s: %d\n", logfile, errno);
146 return -1;
149 close(0);
150 close(1);
151 close(2);
152 if (dup2(fd, 2) < 0) {
153 fprintf(stderr, "dup failed: %d\n", errno);
154 return -1;
157 pid = fork();
158 if (pid < 0)
159 return -1;
160 else if (pid != 0)
161 return 0;
163 setsid();
164 chdir("/");
166 root = npfile_alloc(NULL, strdup(""), ROOTPERM|Dmdir, qidpath++,
167 &dirops, file_alloc());
168 npfile_incref(root);
169 root->parent = root;
170 root->atime = time(NULL);
171 root->mtime = root->atime;
172 root->uid = user;
173 root->gid = user->dfltgroup;
174 root->muid = user;
176 srv = np_pipesrv_create(nwthreads);
177 if (!srv)
178 return -1;
180 npfile_init_srv(srv, root);
181 if (optind >= argc)
182 usage();
184 srv->debuglevel = debuglevel;
185 srv->connclose = rfs_connclose;
186 np_pipesrv_mount(srv, argv[optind], user->uname, 0, opts);
188 while (1) {
189 sleep(100);
192 return 0;
195 static File*
196 file_alloc(void)
198 File *f;
200 f = malloc(sizeof(*f));
201 f->data = NULL;
202 f->datasize = 0;
204 return f;
207 static int
208 file_truncate(File *f, u64 size)
210 int n;
211 u8* buf;
213 if (size == 0) {
214 free(f->data);
215 f->data = NULL;
216 f->datasize = 0;
217 return 0;
220 n = (size/blksize + (size%blksize?1:0)) * blksize;
221 buf = realloc(f->data, n);
222 if (!buf)
223 return -1;
225 f->data = buf;
226 f->datasize = n;
227 return 0;
230 static void
231 rfs_connclose(Npconn *conn)
233 exit(0);
236 static int
237 rfs_read(Npfilefid* fid, u64 offset, u32 count, u8* data, Npreq *req)
239 int n;
240 Npfile *file;
241 File *f;
243 file = fid->file;
244 f = file->aux;
245 n = count;
246 if (file->length < offset+count)
247 n = file->length - offset;
249 if (n < 0)
250 n = 0;
252 memmove(data, f->data + offset, n);
253 return n;
256 static int
257 rfs_write(Npfilefid* fid, u64 offset, u32 count, u8* data, Npreq *req)
259 int n;
260 Npfile *file;
261 File *f;
263 file = fid->file;
264 f = file->aux;
265 if (fid->omode & Oappend)
266 offset = file->length;
268 n = count;
269 if (file->length < offset+count) {
270 pthread_mutex_lock(&file->lock);
271 if (file_truncate(f, offset+count)) {
272 np_werror(Enospace, ENOSPC);
273 pthread_mutex_unlock(&file->lock);
274 return 0;
277 if (offset+count > f->datasize) {
278 if (f->datasize - offset > 0)
279 n = f->datasize - offset;
280 else
281 n = 0;
284 if (n) {
285 if (file->length < offset)
286 memset(f->data + file->length, 0, offset -
287 file->length);
288 file->length = offset + count;
290 pthread_mutex_unlock(&file->lock);
293 if (n)
294 memmove(f->data + offset, data, n);
296 return n;
299 static int
300 rfs_wstat(Npfile *file, Npstat *stat)
302 File *f;
303 Npfile *nfile;
304 char *sname, *oldname;
305 int lockparent;
306 u64 length, oldlength;
307 u32 oldperm;
308 u32 oldmtime;
310 f = file->aux;
311 oldlength = ~0;
312 oldperm = ~0;
313 oldmtime = ~0;
314 oldname = NULL;
316 lockparent = stat->name.len != 0;
317 if (lockparent)
318 pthread_mutex_lock(&file->parent->lock);
320 if (stat->name.len != 0) {
321 sname = np_strdup(&stat->name);
322 nfile = npfile_find(file->parent, sname);
323 if (nfile) {
324 free(sname);
325 np_werror(Eexist, EEXIST);
326 goto error;
327 } else
328 npfile_decref(nfile);
330 oldname = file->name;
331 file->name = sname;
334 if (stat->length != (u64) ~0) {
335 oldlength = file->length;
336 length = stat->length;
337 if (file_truncate(f, length)) {
338 np_werror(Enospace, ENOSPC);
339 goto error;
342 if (length > f->datasize)
343 length = f->datasize;
345 if (file->length < length)
346 memset(f->data + file->length, 0, length - file->length);
348 file->length = length;
351 if (stat->mode != (u32) ~0) {
352 oldperm = file->mode;
353 file->mode = stat->mode;
356 if (stat->mtime != (u32)~0) {
357 oldmtime = file->mtime;
358 file->mtime = stat->mtime;
361 free(oldname);
362 if (lockparent)
363 pthread_mutex_unlock(&file->parent->lock);
365 return 1;
367 error:
368 if (oldname) {
369 free(file->name);
370 file->name = oldname;
373 if (oldperm != ~0)
374 file->mode = oldperm;
376 if (oldmtime != ~0)
377 file->mtime = oldmtime;
379 if (oldlength != ~0) {
380 file->length = oldlength;
381 file_truncate(f, oldlength);
384 if (lockparent)
385 pthread_mutex_unlock(&file->parent->lock);
387 return 0;
390 static void
391 rfs_destroy(Npfile* file)
393 File *f;
395 f = file->aux;
396 free(f->data);
397 free(f);
400 static Npfile*
401 rfs_create(Npfile *dir, char *name, u32 perm, Npuser *uid, Npgroup *gid,
402 char *extension)
404 Npfile *file;
405 File *d, *f;
406 void *ops;
408 if (perm & Dmlink) {
409 np_werror(Eperm, EPERM);
410 return NULL;
413 d = dir->aux;
414 f = file_alloc();
415 if (perm&Dmdir)
416 ops = &dirops;
417 else
418 ops = &fileops;
420 file = npfile_alloc(dir, name, perm, qidpath++, ops, f);
421 file->uid = uid;
422 file->gid = gid;
423 file->muid = uid;
424 npfile_incref(file);
425 npfile_incref(file);
427 if (dir->dirlast) {
428 dir->dirlast->next = file;
429 file->prev = dir->dirlast;
430 } else
431 dir->dirfirst = file;
433 dir->dirlast = file;
434 file->extension = strdup(extension);
436 return file;
439 static Npfile*
440 rfs_first(Npfile *dir)
442 npfile_incref(dir->dirfirst);
443 return dir->dirfirst;
446 static Npfile*
447 rfs_next(Npfile *dir, Npfile *prevchild)
449 npfile_incref(prevchild->next);
450 return prevchild->next;
453 static int
454 rfs_remove(Npfile *dir, Npfile *file)
456 if (dir->dirfirst == file)
457 dir->dirfirst = file->next;
458 else
459 file->prev->next = file->next;
461 if (file->next)
462 file->next->prev = file->prev;
464 if (file == dir->dirlast)
465 dir->dirlast = file->prev;
467 file->prev = NULL;
468 file->next = NULL;
469 file->parent = NULL;
471 return 1;