ukfs.c revision 1.1 1 /* $NetBSD: ukfs.c,v 1.1 2008/07/29 13:17:41 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by the
7 * Finnish Cultural Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 /*
32 * This library enables access to files systems directly without
33 * involving system calls.
34 */
35
36 #ifdef __linux__
37 #define _XOPEN_SOURCE 500
38 #define _BSD_SOURCE
39 #define _FILE_OFFSET_BITS 64
40 #endif
41
42 #include <sys/stat.h>
43
44 #include <assert.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <pthread.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <stdint.h>
53
54 #include <rump/ukfs.h>
55
56 #include <rump/rump.h>
57 #include <rump/rump_syscalls.h>
58
59 #define UKFS_MODE_DEFAULT 0555
60
61 struct ukfs {
62 struct mount *ukfs_mp;
63 struct vnode *ukfs_rvp;
64
65 pthread_spinlock_t ukfs_spin;
66 pid_t ukfs_nextpid;
67 struct vnode *ukfs_cdir;
68 };
69
70 struct mount *
71 ukfs_getmp(struct ukfs *ukfs)
72 {
73
74 return ukfs->ukfs_mp;
75 }
76
77 struct vnode *
78 ukfs_getrvp(struct ukfs *ukfs)
79 {
80 struct vnode *rvp;
81
82 rvp = ukfs->ukfs_rvp;
83 rump_vp_incref(rvp);
84
85 return rvp;
86 }
87
88 static pid_t
89 nextpid(struct ukfs *ukfs)
90 {
91 pid_t npid;
92
93 pthread_spin_lock(&ukfs->ukfs_spin);
94 npid = ukfs->ukfs_nextpid++;
95 pthread_spin_unlock(&ukfs->ukfs_spin);
96
97 return npid;
98 }
99
100 static void
101 precall(struct ukfs *ukfs)
102 {
103 struct vnode *rvp, *cvp;
104
105 rump_setup_curlwp(nextpid(ukfs), 1, 1);
106 rvp = ukfs_getrvp(ukfs);
107 pthread_spin_lock(&ukfs->ukfs_spin);
108 cvp = ukfs->ukfs_cdir;
109 pthread_spin_unlock(&ukfs->ukfs_spin);
110 rump_rcvp_set(rvp, cvp); /* takes refs */
111 rump_vp_rele(rvp);
112 }
113
114 static void
115 postcall(struct ukfs *ukfs)
116 {
117 struct vnode *rvp;
118
119 rvp = ukfs_getrvp(ukfs);
120 rump_rcvp_set(NULL, rvp);
121 rump_vp_rele(rvp);
122 rump_clear_curlwp();
123 }
124
125 int
126 ukfs_init()
127 {
128
129 rump_init();
130
131 return 0;
132 }
133
134 struct ukfs *
135 ukfs_mount(const char *vfsname, const char *devpath, const char *mountpath,
136 int mntflags, void *arg, size_t alen)
137 {
138 struct ukfs *fs = NULL;
139 struct vfsops *vfsops;
140 struct mount *mp;
141 int rv = 0;
142
143 vfsops = rump_vfs_getopsbyname(vfsname);
144 if (vfsops == NULL) {
145 rv = ENOENT;
146 goto out;
147 }
148
149 mp = rump_mnt_init(vfsops, mntflags);
150
151 fs = malloc(sizeof(struct ukfs));
152 if (fs == NULL) {
153 rv = ENOMEM;
154 goto out;
155 }
156 memset(fs, 0, sizeof(struct ukfs));
157 pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
158
159 rump_fakeblk_register(devpath);
160 rv = rump_mnt_mount(mp, mountpath, arg, &alen);
161 rump_fakeblk_deregister(devpath);
162 if (rv) {
163 warnx("VFS_MOUNT %d", rv);
164 goto out;
165 }
166 fs->ukfs_mp = mp;
167
168 rv = rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
169 fs->ukfs_cdir = ukfs_getrvp(fs);
170
171 out:
172 if (rv) {
173 if (fs && fs->ukfs_mp)
174 rump_mnt_destroy(fs->ukfs_mp);
175 if (fs)
176 free(fs);
177 errno = rv;
178 fs = NULL;
179 }
180
181 return fs;
182 }
183
184 void
185 ukfs_release(struct ukfs *fs, int flags)
186 {
187 int rv;
188
189 if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
190 rump_vp_rele(fs->ukfs_cdir);
191 rv = rump_vfs_sync(fs->ukfs_mp, 1, NULL);
192 rump_vp_recycle_nokidding(ukfs_getrvp(fs));
193 rv |= rump_vfs_unmount(fs->ukfs_mp, 0);
194 assert(rv == 0);
195 }
196
197 rump_vfs_syncwait(fs->ukfs_mp);
198 rump_mnt_destroy(fs->ukfs_mp);
199
200 pthread_spin_destroy(&fs->ukfs_spin);
201 free(fs);
202 }
203
204 /* don't need vn_lock(), since we don't have VXLOCK */
205 #define VLE(a) rump_vp_lock_exclusive(a)
206 #define VLS(a) rump_vp_lock_shared(a)
207 #define VUL(a) rump_vp_unlock(a)
208 #define AUL(a) assert(rump_vp_islocked(a) == 0)
209
210 #define STDCALL(ukfs, thecall) \
211 int rv = 0; \
212 \
213 precall(ukfs); \
214 thecall; \
215 postcall(ukfs); \
216 if (rv) { \
217 errno = rv; \
218 return -1; \
219 } \
220 return 0;
221
222 int
223 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
224 uint8_t *buf, size_t bufsize)
225 {
226 struct uio *uio;
227 struct vnode *vp;
228 size_t resid;
229 int rv, eofflag;
230
231 precall(ukfs);
232 rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
233 NULL, &vp, NULL);
234 postcall(ukfs);
235 if (rv)
236 goto out;
237
238 uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
239 rv = RUMP_VOP_READDIR(vp, uio, NULL, &eofflag, NULL, NULL);
240 VUL(vp);
241 *off = rump_uio_getoff(uio);
242 resid = rump_uio_free(uio);
243 rump_vp_rele(vp);
244
245 out:
246 if (rv) {
247 errno = rv;
248 return -1;
249 }
250
251 /* LINTED: not totally correct return type, but follows syscall */
252 return bufsize - resid;
253 }
254
255 ssize_t
256 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
257 uint8_t *buf, size_t bufsize)
258 {
259 int fd, rv = 0, dummy;
260 ssize_t xfer = -1; /* XXXgcc */
261
262 precall(ukfs);
263 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0, &rv);
264 if (rv)
265 goto out;
266
267 xfer = rump_sys_pread(fd, buf, bufsize, 0, off, &rv);
268 rump_sys_close(fd, &dummy);
269
270 out:
271 postcall(ukfs);
272 if (rv) {
273 errno = rv;
274 return -1;
275 }
276 return xfer;
277 }
278
279 ssize_t
280 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
281 uint8_t *buf, size_t bufsize)
282 {
283 int fd, rv = 0, dummy;
284 ssize_t xfer = -1; /* XXXgcc */
285
286 precall(ukfs);
287 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0, &rv);
288 if (rv)
289 goto out;
290
291 /* write and commit */
292 xfer = rump_sys_pwrite(fd, buf, bufsize, 0, off, &rv);
293 if (rv == 0)
294 rump_sys_fsync(fd, &dummy);
295
296 rump_sys_close(fd, &dummy);
297
298 out:
299 postcall(ukfs);
300 if (rv) {
301 errno = rv;
302 return -1;
303 }
304 return xfer;
305 }
306
307 int
308 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
309 {
310 int rv, fd, dummy;
311
312 precall(ukfs);
313 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode, &rv);
314 rump_sys_close(fd, &dummy);
315
316 postcall(ukfs);
317 if (rv) {
318 errno = rv;
319 return -1;
320 }
321 return 0;
322 }
323
324 int
325 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
326 {
327
328 STDCALL(ukfs, rump_sys_mknod(path, mode, dev, &rv));
329 }
330
331 int
332 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
333 {
334
335 STDCALL(ukfs, rump_sys_mkfifo(path, mode, &rv));
336 }
337
338 int
339 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
340 {
341
342 STDCALL(ukfs, rump_sys_mkdir(filename, mode, &rv));
343 }
344
345 int
346 ukfs_remove(struct ukfs *ukfs, const char *filename)
347 {
348
349 STDCALL(ukfs, rump_sys_unlink(filename, &rv));
350 }
351
352 int
353 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
354 {
355
356 STDCALL(ukfs, rump_sys_rmdir(filename, &rv));
357 }
358
359 int
360 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
361 {
362
363 STDCALL(ukfs, rump_sys_link(filename, f_create, &rv));
364 }
365
366 int
367 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
368 {
369
370 STDCALL(ukfs, rump_sys_symlink(filename, linkname, &rv));
371 }
372
373 ssize_t
374 ukfs_readlink(struct ukfs *ukfs, const char *filename,
375 char *linkbuf, size_t buflen)
376 {
377 ssize_t rv;
378 int myerr = 0;
379
380 precall(ukfs);
381 rv = rump_sys_readlink(filename, linkbuf, buflen, &myerr);
382 postcall(ukfs);
383 if (myerr) {
384 errno = rv;
385 return -1;
386 }
387 return rv;
388 }
389
390 int
391 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
392 {
393
394 STDCALL(ukfs, rump_sys_rename(from, to, &rv));
395 }
396
397 int
398 ukfs_chdir(struct ukfs *ukfs, const char *path)
399 {
400 struct vnode *newvp, *oldvp;
401 int rv;
402
403 precall(ukfs);
404 rump_sys_chdir(path, &rv);
405 if (rv)
406 goto out;
407
408 newvp = rump_cdir_get();
409 pthread_spin_lock(&ukfs->ukfs_spin);
410 oldvp = ukfs->ukfs_cdir;
411 ukfs->ukfs_cdir = newvp;
412 pthread_spin_unlock(&ukfs->ukfs_spin);
413 if (oldvp)
414 rump_vp_rele(oldvp);
415
416 out:
417 postcall(ukfs);
418 if (rv) {
419 errno = rv;
420 return -1;
421 }
422 return 0;
423 }
424
425 int
426 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
427 {
428
429 STDCALL(ukfs, rump_sys___stat30(filename, file_stat, &rv));
430 }
431
432 int
433 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
434 {
435
436 STDCALL(ukfs, rump_sys___lstat30(filename, file_stat, &rv));
437 }
438
439 int
440 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
441 {
442
443 STDCALL(ukfs, rump_sys_chmod(filename, mode, &rv));
444 }
445
446 int
447 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
448 {
449
450 STDCALL(ukfs, rump_sys_lchmod(filename, mode, &rv));
451 }
452
453 int
454 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
455 {
456
457 STDCALL(ukfs, rump_sys_chown(filename, uid, gid, &rv));
458 }
459
460 int
461 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
462 {
463
464 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid, &rv));
465 }
466
467 int
468 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
469 {
470
471 STDCALL(ukfs, rump_sys_chflags(filename, flags, &rv));
472 }
473
474 int
475 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
476 {
477
478 STDCALL(ukfs, rump_sys_lchflags(filename, flags, &rv));
479 }
480
481 int
482 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
483 {
484
485 STDCALL(ukfs, rump_sys_utimes(filename, tptr, &rv));
486 }
487
488 int
489 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
490 const struct timeval *tptr)
491 {
492
493 STDCALL(ukfs, rump_sys_lutimes(filename, tptr, &rv));
494 }
495
496 int
497 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
498 {
499 char *f1, *f2;
500 int rv;
501 mode_t mask;
502 bool end;
503
504 /*ukfs_umask((mask = ukfs_umask(0)));*/
505 umask((mask = umask(0)));
506
507 f1 = f2 = strdup(pathname);
508 if (f1 == NULL) {
509 errno = ENOMEM;
510 return -1;
511 }
512
513 end = false;
514 for (;;) {
515 /* find next component */
516 f2 += strspn(f2, "/");
517 f2 += strcspn(f2, "/");
518 if (*f2 == '\0')
519 end = true;
520 else
521 *f2 = '\0';
522
523 rv = ukfs_mkdir(ukfs, f1, mode & ~mask);
524 if (errno == EEXIST)
525 rv = 0;
526
527 if (rv == -1 || *f2 != '\0' || end)
528 break;
529
530 *f2 = '/';
531 }
532
533 free(f1);
534
535 return rv;
536 }
537