diff --git a/commands/mount/mount.c b/commands/mount/mount.c index 40015a4ce..3f80e9adb 100644 --- a/commands/mount/mount.c +++ b/commands/mount/mount.c @@ -28,7 +28,7 @@ char *argv[]; { int i, n, v = 0, mountflags, write_mtab; char **ap, *vs, *opt, *err, *type, *args, *device; - char special[PATH_MAX+1], mounted_on[PATH_MAX+1], version[10], rw_flag[10]; + char special[PATH_MAX], mounted_on[PATH_MAX], version[10], rw_flag[10]; if (argc == 1) list(); /* just list /etc/mtab */ mountflags = 0; @@ -104,9 +104,9 @@ char *argv[]; /* For MFS, use a version number. Otherwise, use the FS type name. */ if (!strcmp(type, MINIX_FS_TYPE)) { switch (v) { - case FSVERSION_MFS1: vs = "1"; break; - case FSVERSION_MFS2: vs = "2"; break; - case FSVERSION_MFS3: vs = "3"; break; + case FSVERSION_MFS1: vs = "MFSv1"; break; + case FSVERSION_MFS2: vs = "MFSv2"; break; + case FSVERSION_MFS3: vs = "MFSv3"; break; default: vs = "0"; break; } } else { @@ -131,7 +131,7 @@ char *argv[]; void list() { int n; - char special[PATH_MAX+1], mounted_on[PATH_MAX+1], version[10], rw_flag[10]; + char special[PATH_MAX], mounted_on[PATH_MAX], version[10], rw_flag[10]; /* Read and print /etc/mtab. */ n = load_mtab("mount"); diff --git a/include/unistd.h b/include/unistd.h index 3edcdbf83..87902b148 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -202,6 +202,9 @@ _PROTOTYPE( pid_t getnpid, (endpoint_t proc_ep) ); _PROTOTYPE( uid_t getnuid, (endpoint_t proc_ep) ); _PROTOTYPE( gid_t getngid, (endpoint_t proc_ep) ); _PROTOTYPE( int getnucred, (endpoint_t proc_ep, struct ucred *ucred) ); +_PROTOTYPE( ssize_t pread64, (int fd, void *buf, size_t count, u64_t where)); +_PROTOTYPE( ssize_t pwrite64, (int fd, const void *buf, size_t count, + u64_t where)); #endif diff --git a/lib/Makefile b/lib/Makefile index 16cdf1382..1e9879eb4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -21,7 +21,8 @@ SUBDIR= csu ${LIBCOMPAT_DIR} ${LIBC_DIR} libdriver libnetdriver \ libddekit libminixfs libbdev .if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no") -SUBDIR+= libelf libminc libcrypt libterminfo libcurses libvassert libutil +SUBDIR+= libelf libminc libcrypt libterminfo libcurses libvassert libutil \ + libpuffs librefuse .endif .if ${COMPILER_TYPE} == "ack" diff --git a/lib/libc/other/_mount.c b/lib/libc/other/_mount.c index 51c10fc2e..4567b0004 100644 --- a/lib/libc/other/_mount.c +++ b/lib/libc/other/_mount.c @@ -10,11 +10,13 @@ #include #include #include +#include #define OK 0 -#define FSPATH "/sbin/" #define FSDEFAULT "mfs" +static char fspath[] = "/sbin/:/usr/pkg/bin/"; /* Must include trailing '/' */ + PRIVATE int rs_down(char *label) { char cmd[200]; @@ -32,7 +34,7 @@ int mountflags; message m; struct stat statbuf; char label[16]; - char path[60]; + char path[PATH_MAX]; char cmd[200]; char *p; int reuse = 0; @@ -42,6 +44,7 @@ int mountflags; if (type == NULL) type = FSDEFAULT; if (args == NULL) args = ""; reuse = 0; + memset(path, '\0', sizeof(path)); /* Check mount flags */ if(mountflags & MS_REUSE) { @@ -87,8 +90,6 @@ int mountflags; /* Tell VFS that we are passing in a 16-byte label. */ mountflags |= MS_LABEL16; - - /* Sanity check on user input. */ if(strchr(args, '\'')) { errno = EINVAL; @@ -97,28 +98,40 @@ int mountflags; /* start the fs-server if not using existing one */ if (!use_existing) { /* See if the given type is even remotely valid. */ - if(strlen(FSPATH)+strlen(type) >= sizeof(path)) { - errno = E2BIG; - return -1; - } - strcpy(path, FSPATH); - strcat(path, type); - - if(stat(path, &statbuf) != 0) { + + char *testpath; + testpath = strtok(fspath, ":"); + + do { + if (strlen(testpath) + strlen(type) >= sizeof(path)) { + errno = E2BIG; + return(-1); + } + + strcpy(path, testpath); + strcat(path, type); + + if (access(path, F_OK) == 0) break; + + } while ((testpath = strtok(NULL, ":")) != NULL); + + if (testpath == NULL) { + /* We were not able to find type somewhere in "fspath" */ errno = EINVAL; - return -1; + return(-1); } - - if(strlen(_PATH_SERVICE)+strlen(path)+strlen(label)+ - strlen(args)+50 >= sizeof(cmd)) { + + if (strlen(_PATH_SERVICE) + strlen(path) + strlen(label) + + strlen(args) + 50 >= sizeof(cmd)) { errno = E2BIG; return -1; } - sprintf(cmd, _PATH_SERVICE " %sup %s -label '%s' -args '%s%s'", - reuse ? "-r ": "", path, label, args[0] ? "-o " : "", args); + sprintf(cmd, _PATH_SERVICE " %sup %s -label '%s' -args '%s %s %s%s'", + reuse ? "-r ": "", path, label, special, name, + args[0] ? "-o " : "", args); - if((r = system(cmd)) != 0) { + if ((r = system(cmd)) != 0) { fprintf(stderr, "mount: couldn't run %s\n", cmd); errno = r; return -1; @@ -134,8 +147,8 @@ int mountflags; m.m1_p3 = label; r = _syscall(VFS_PROC_NR, MOUNT, &m); - if(r != OK && !use_existing) { - /* If mount() failed, tell RS to shutdown MFS process. + if (r != OK && !use_existing) { + /* If mount() failed, tell RS to shutdown FS process. * No error check - won't do anything with this error anyway. */ rs_down(label); diff --git a/lib/libc/posix/pread.c b/lib/libc/posix/pread.c index a1af0223e..4ef6b5703 100644 --- a/lib/libc/posix/pread.c +++ b/lib/libc/posix/pread.c @@ -1,5 +1,25 @@ #include #include +#include + +ssize_t pread64(int fd, void *buffer, size_t nbytes, u64_t where) +{ + u64_t here; + ssize_t r; + + if (lseek64(fd, make64(0,0), SEEK_CUR, &here) < 0) return(-1); + if (lseek64(fd, where, SEEK_SET, NULL) < 0) return(-1); + if ((r = read(fd, buffer, nbytes)) < 0) { + int e ; errno; + lseek64(fd, here, SEEK_SET, NULL); + errno = e; + return(-1); + } + + if (lseek64(fd, here, SEEK_SET, NULL) < 0) return(-1); + + return(r); +} ssize_t pread(int fd, void *buffer, size_t nbytes, off_t where) { diff --git a/lib/libc/posix/pwrite.c b/lib/libc/posix/pwrite.c index 7cb3f52c4..94b1d4ad3 100644 --- a/lib/libc/posix/pwrite.c +++ b/lib/libc/posix/pwrite.c @@ -1,5 +1,25 @@ #include #include +#include + +ssize_t pwrite64(int fd, const void *buffer, size_t nbytes, u64_t where) +{ + u64_t here; + ssize_t w; + + if (lseek64(fd, make64(0,0), SEEK_CUR, &here) < 0) return(-1); + if (lseek64(fd, where, SEEK_SET, NULL) < 0) return(-1); + if ((w = write(fd, buffer, nbytes)) < 0) { + int e = errno; + lseek64(fd, here, SEEK_SET, NULL); + errno = e; + return(-1); + } + + if (lseek64(fd, here, SEEK_SET, NULL) < 0) return(-1); + + return(w); +} ssize_t pwrite(int fd, const void *buffer, size_t nbytes, off_t where) { diff --git a/lib/libpuffs/Makefile b/lib/libpuffs/Makefile new file mode 100644 index 000000000..6d18c7122 --- /dev/null +++ b/lib/libpuffs/Makefile @@ -0,0 +1,17 @@ +# Makefile for libpuffs + +LIB= puffs +CC= clang +SRCS= callcontext.c creds.c null.c pnode.c puffs.c subr.c\ + table.c link.c misc.c open.c path.c path_puffs.c protect.c\ + read.c stadir.c time.c utility.c mount.c device.c inode.c +INCS= puffs.h puffs_msgif.h +INCSDIR= /usr/include +MAN= puffs.3 puffs_cc.3 puffs_cred.3 puffs_node.3 \ + puffs_ops.3 puffs_path.3 + +DEFAULT_NR_BUFS= 1024 + +CPPFLAGS+= -D_MINIX -D_POSIX_SOURCE -D_POSIX_C_SOURCE -D_NETBSD_SOURCE -DDEFAULT_NR_BUFS=${DEFAULT_NR_BUFS} + +.include diff --git a/lib/libpuffs/callcontext.c b/lib/libpuffs/callcontext.c new file mode 100644 index 000000000..5ecaf049a --- /dev/null +++ b/lib/libpuffs/callcontext.c @@ -0,0 +1,350 @@ +/* $NetBSD: callcontext.c,v 1.23 2008/08/11 16:23:37 pooka Exp $ */ + +/* + * Copyright (c) 2006, 2007, 2008 Antti Kantee. All Rights Reserved. + * + * Development of this software was supported by the + * Research Foundation of Helsinki University of Technology + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#if !defined(lint) +__RCSID("$NetBSD: callcontext.c,v 1.23 2008/08/11 16:23:37 pooka Exp $"); +#endif /* !lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + +#if 0 +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +/* + * Set the following to 1 to not handle each request on a separate + * stack. This is highly volatile kludge, therefore no external + * interface. + */ +int puffs_fakecc; + +/* + * user stuff + */ + +/* + * So, we need to get back to where we came from. This can happen in two + * different ways: + * 1) PCC_MLCONT is set, in which case we need to go to the mainloop + * 2) It is not set, and we simply jump to pcc_uc_ret. + */ +void +puffs_cc_yield(struct puffs_cc *pcc) +{ + struct puffs_cc *jumpcc; + int rv; + + assert(puffs_fakecc == 0); + + pcc->pcc_flags &= ~PCC_BORROWED; + + /* romanes eunt domus */ + DPRINTF(("puffs_cc_yield: ")); + if ((pcc->pcc_flags & PCC_MLCONT) == 0) { + DPRINTF(("no mlcont, pcc %p\n", pcc)); + swapcontext(&pcc->pcc_uc, &pcc->pcc_uc_ret); + } else { + DPRINTF(("mlcont, pcc %p\n", pcc)); + pcc->pcc_flags &= ~PCC_MLCONT; + rv = puffs__cc_create(pcc->pcc_pu, puffs__theloop, &jumpcc); + if (rv) + abort(); /* p-p-p-pa-pa-panic (XXX: fixme) */ + swapcontext(&pcc->pcc_uc, &jumpcc->pcc_uc); + DPRINTF(("puffs_cc_yield: post swap pcc %p\n", pcc)); + } +} + +/* + * Internal continue routine. This has slightly different semantics. + * We simply make our cc available in the freelist and jump to the + * indicated pcc. + */ +void +puffs__cc_cont(struct puffs_cc *pcc) +{ + struct puffs_cc *mycc; + + mycc = puffs_cc_getcc(pcc->pcc_pu); + DPRINTF(("puffs__cc_cont: pcc %p, mycc %p\n", pcc, mycc)); + + /* + * XXX: race between setcontenxt() and recycle if + * we go multithreaded + */ + puffs__cc_destroy(mycc, 1); + pcc->pcc_flags |= PCC_MLCONT; + setcontext(&pcc->pcc_uc); +} + +void +puffs_cc_continue(struct puffs_cc *pcc) +{ + + /* ramble on */ + DPRINTF(("puffs_cc_continue: pcc %p\n", pcc)); + if (puffs_fakecc) { + pcc->pcc_func(pcc->pcc_farg); + } else { + swapcontext(&pcc->pcc_uc_ret, &pcc->pcc_uc); + } +} + +/* + * "Borrows" pcc, *NOT* called from pcc owner. Acts like continue. + * So the idea is to use this, give something the context back to + * run to completion and then jump back to where ever this was called + * from after the op dispatching is complete (or if the pcc decides to + * yield again). + */ +void +puffs__goto(struct puffs_cc *loanpcc) +{ + + loanpcc->pcc_flags |= PCC_BORROWED; + + swapcontext(&loanpcc->pcc_uc_ret, &loanpcc->pcc_uc); +} + +void +puffs_cc_schedule(struct puffs_cc *pcc) +{ + struct puffs_usermount *pu = pcc->pcc_pu; + + assert(pu->pu_state & PU_INLOOP); + TAILQ_INSERT_TAIL(&pu->pu_sched, pcc, pcc_schedent); +} + +int +puffs_cc_getcaller(struct puffs_cc *pcc, pid_t *pid, lwpid_t *lid) +{ + + if ((pcc->pcc_flags & PCC_HASCALLER) == 0) { + errno = ESRCH; + return -1; + } + + if (pid) + *pid = pcc->pcc_pid; + if (lid) + *lid = pcc->pcc_lid; + return 0; +} + +static struct puffs_cc fakecc; + +static struct puffs_cc * +slowccalloc(struct puffs_usermount *pu) +{ + struct puffs_cc *volatile pcc; + void *sp; + size_t stacksize = 1<pu_cc_stackshift; + + if (puffs_fakecc) + return &fakecc; + + sp = minix_mmap(NULL, stacksize, PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, 0); + if (sp == MAP_FAILED) + return NULL; + + pcc = sp; + memset(pcc, 0, sizeof(struct puffs_cc)); + + /* initialize both ucontext's */ + if (getcontext(&pcc->pcc_uc) == -1) { + minix_munmap(pcc, stacksize); + return NULL; + } + if (getcontext(&pcc->pcc_uc_ret) == -1) { + minix_munmap(pcc, stacksize); + return NULL; + } + + return pcc; +} + +int +puffs__cc_create(struct puffs_usermount *pu, puffs_ccfunc func, + struct puffs_cc **pccp) +{ + struct puffs_cc *pcc; + size_t stacksize = 1<pu_cc_stackshift; + stack_t *st; + + /* Do we have a cached copy? */ + if (pu->pu_cc_nstored == 0) { + pcc = slowccalloc(pu); + if (pcc == NULL) + return -1; + pcc->pcc_pu = pu; + DPRINTF(("puffs__cc_create: allocated pcc %p\n", pcc)); + } else { + pcc = LIST_FIRST(&pu->pu_ccmagazin); + assert(pcc != NULL); + + LIST_REMOVE(pcc, pcc_rope); + pu->pu_cc_nstored--; + DPRINTF(("puffs__cc_create: magazin pcc %p\n", pcc)); + } + assert(pcc->pcc_pu == pu); + + if (puffs_fakecc) { + pcc->pcc_func = func; + pcc->pcc_farg = pcc; + } else { + /* link context */ + pcc->pcc_uc.uc_link = &pcc->pcc_uc_ret; + + /* setup stack + * + * XXX: I guess this should theoretically be preserved by + * swapcontext(). However, it gets lost. So reinit it. + */ + st = &pcc->pcc_uc.uc_stack; + st->ss_sp = pcc; + st->ss_size = stacksize; + st->ss_flags = 0; + + /* + * Give us an initial context to jump to. + * + * Our manual page says that portable code shouldn't + * rely on being able to pass pointers through makecontext(). + * kjk says that NetBSD code doesn't need to worry about this. + * uwe says it would be like putting a "keep away from + * children" sign on a box of toys. + */ + makecontext(&pcc->pcc_uc, (void *)func, 1, (uintptr_t)pcc); + } + + *pccp = pcc; + return 0; +} + +void +puffs__cc_setcaller(struct puffs_cc *pcc, pid_t pid, lwpid_t lid) +{ + + pcc->pcc_pid = pid; + pcc->pcc_lid = lid; + pcc->pcc_flags |= PCC_HASCALLER; +} + +static void +cc_free(struct puffs_cc *pcc) +{ + struct puffs_usermount *pu = pcc->pcc_pu; + size_t stacksize = 1<pu_cc_stackshift; + + DPRINTF(("invalidating pcc %p\n", pcc)); + assert(!puffs_fakecc); + minix_munmap(pcc, stacksize); +} + +void +puffs__cc_destroy(struct puffs_cc *pcc, int nonuke) +{ + struct puffs_usermount *pu = pcc->pcc_pu; + + assert(pcc->pcc_flags == 0); + assert(!puffs_fakecc); + + /* not over limit? stuff away in the store, otherwise nuke */ + if (nonuke || pu->pu_cc_nstored < PUFFS_CCMAXSTORE) { + pcc->pcc_pb = NULL; + DPRINTF(("puffs__cc_destroy: storing pcc %p\n", pcc)); + LIST_INSERT_HEAD(&pu->pu_ccmagazin, pcc, pcc_rope); + pu->pu_cc_nstored++; + } else { + cc_free(pcc); + } +} + +void +puffs__cc_exit(struct puffs_usermount *pu) +{ + struct puffs_cc *pcc; + + while ((pcc = LIST_FIRST(&pu->pu_ccmagazin)) != NULL) { + LIST_REMOVE(pcc, pcc_rope); + cc_free(pcc); + } +} + +struct puffs_cc * +puffs_cc_getcc(struct puffs_usermount *pu) +{ + size_t stacksize = 1<pu_cc_stackshift; + uintptr_t bottom; + + if (puffs_fakecc) + return &fakecc; + + bottom = ((uintptr_t)&bottom) & ~(stacksize-1); + return (struct puffs_cc *)bottom; +} + +int +puffs__cc_savemain(struct puffs_usermount *pu) +{ + + if (puffs_fakecc) + return 0; + + PU_CLRSFLAG(pu, PU_MAINRESTORE); + return getcontext(&pu->pu_mainctx); +} + +int +puffs__cc_restoremain(struct puffs_usermount *pu) +{ + + if (puffs_fakecc) + return 0; + + puffs__cc_destroy(puffs_cc_getcc(pu), 1); + PU_SETSFLAG(pu, PU_MAINRESTORE); + return setcontext(&pu->pu_mainctx); +} diff --git a/lib/libpuffs/creds.c b/lib/libpuffs/creds.c new file mode 100644 index 000000000..01b15e526 --- /dev/null +++ b/lib/libpuffs/creds.c @@ -0,0 +1,259 @@ +/* $NetBSD: creds.c,v 1.14.12.1 2009/11/28 16:01:03 bouyer Exp $ */ + +/* + * Copyright (c) 2006 Antti Kantee. All Rights Reserved. + * + * Development of this software was supported by the Ulla Tuominen Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#if !defined(lint) +__RCSID("$NetBSD: creds.c,v 1.14.12.1 2009/11/28 16:01:03 bouyer Exp $"); +#endif /* !lint */ + +/* + * Interface for dealing with credits. + */ + +#include +#include + +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + +#define UUCCRED(a) (a->pkcr_type == PUFFCRED_TYPE_UUC) +#define INTCRED(a) (a->pkcr_type == PUFFCRED_TYPE_INTERNAL) + +int +puffs_cred_getuid(const struct puffs_cred *pcr, uid_t *ruid) +{ + PUFFS_MAKEKCRED(pkcr, pcr); + + if (!UUCCRED(pkcr)) { + errno = EOPNOTSUPP; + return -1; + } + *ruid = pkcr->pkcr_uuc.cr_uid; + + return 0; +} + +int +puffs_cred_getgid(const struct puffs_cred *pcr, gid_t *rgid) +{ + PUFFS_MAKEKCRED(pkcr, pcr); + + if (!UUCCRED(pkcr)) { + errno = EOPNOTSUPP; + return -1; + } + *rgid = pkcr->pkcr_uuc.cr_gid; + + return 0; +} + +int +puffs_cred_getgroups(const struct puffs_cred *pcr, gid_t *rgids, short *ngids) +{ + PUFFS_MAKEKCRED(pkcr, pcr); + size_t ncopy; + + if (!UUCCRED(pkcr)) { + errno = EOPNOTSUPP; + *ngids = 0; + return -1; + } + + ncopy = MIN(*ngids, pkcr->pkcr_uuc.cr_ngroups); + (void)memcpy(rgids, pkcr->pkcr_uuc.cr_groups, sizeof(gid_t) * ncopy); + *ngids = (short)ncopy; + + return 0; +} + +bool +puffs_cred_isuid(const struct puffs_cred *pcr, uid_t uid) +{ + PUFFS_MAKEKCRED(pkcr, pcr); + + return UUCCRED(pkcr) && pkcr->pkcr_uuc.cr_uid == uid; +} + +bool +puffs_cred_hasgroup(const struct puffs_cred *pcr, gid_t gid) +{ + PUFFS_MAKEKCRED(pkcr, pcr); + short i; + + if (!UUCCRED(pkcr)) + return false; + + if (pkcr->pkcr_uuc.cr_gid == gid) + return true; + for (i = 0; i < pkcr->pkcr_uuc.cr_ngroups; i++) + if (pkcr->pkcr_uuc.cr_groups[i] == gid) + return true; + + return false; +} + +bool +puffs_cred_isregular(const struct puffs_cred *pcr) +{ + PUFFS_MAKEKCRED(pkcr, pcr); + + return UUCCRED(pkcr); +} + +bool +puffs_cred_iskernel(const struct puffs_cred *pcr) +{ + PUFFS_MAKEKCRED(pkcr, pcr); + + return INTCRED(pkcr) && pkcr->pkcr_internal == PUFFCRED_CRED_NOCRED; +} + +bool +puffs_cred_isfs(const struct puffs_cred *pcr) +{ + PUFFS_MAKEKCRED(pkcr, pcr); + + return INTCRED(pkcr) && pkcr->pkcr_internal == PUFFCRED_CRED_FSCRED; +} + +bool +puffs_cred_isjuggernaut(const struct puffs_cred *pcr) +{ + + return puffs_cred_isuid(pcr, 0) || puffs_cred_iskernel(pcr) + || puffs_cred_isfs(pcr); +} + +/* + * Generic routine for checking file access rights. Modeled after + * vaccess() in the kernel. + */ +int +puffs_access(enum vtype type, mode_t file_mode, uid_t uid, gid_t gid, + mode_t acc_mode, const struct puffs_cred *pcr) +{ + mode_t mask; + + /* megapower */ + if (puffs_cred_iskernel(pcr) || puffs_cred_isfs(pcr)) + return 0; + + /* superuser, allow all except exec if *ALL* exec bits are unset */ + if (puffs_cred_isuid(pcr, 0)) { + if ((acc_mode & PUFFS_VEXEC) && type != VDIR && + (file_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0) + return EACCES; + return 0; + } + + mask = 0; + /* owner */ + if (puffs_cred_isuid(pcr, uid)) { + if (acc_mode & PUFFS_VEXEC) + mask |= S_IXUSR; + if (acc_mode & PUFFS_VREAD) + mask |= S_IRUSR; + if (acc_mode & PUFFS_VWRITE) + mask |= S_IWUSR; + /* group */ + } else if (puffs_cred_hasgroup(pcr, gid)) { + if (acc_mode & PUFFS_VEXEC) + mask |= S_IXGRP; + if (acc_mode & PUFFS_VREAD) + mask |= S_IRGRP; + if (acc_mode & PUFFS_VWRITE) + mask |= S_IWGRP; + /* other */ + } else { + if (acc_mode & PUFFS_VEXEC) + mask |= S_IXOTH; + if (acc_mode & PUFFS_VREAD) + mask |= S_IROTH; + if (acc_mode & PUFFS_VWRITE) + mask |= S_IWOTH; + } + + if ((file_mode & mask) == mask) + return 0; + else + return EACCES; +} + +int +puffs_access_chown(uid_t owner, gid_t group, uid_t newowner, gid_t newgroup, + const struct puffs_cred *pcr) +{ + + if (newowner == (uid_t)PUFFS_VNOVAL) + newowner = owner; + if (newgroup == (gid_t)PUFFS_VNOVAL) + newgroup = group; + + if ((!puffs_cred_isuid(pcr, owner) || newowner != owner || + ((newgroup != group && !puffs_cred_hasgroup(pcr, newgroup)))) + && !puffs_cred_isjuggernaut(pcr)) + return EPERM; + + return 0; +} + +int +puffs_access_chmod(uid_t owner, gid_t group, enum vtype type, mode_t mode, + const struct puffs_cred *pcr) +{ + + if (!puffs_cred_isuid(pcr, owner) && !puffs_cred_isjuggernaut(pcr)) + return EPERM; + + if (!puffs_cred_isjuggernaut(pcr)) { + if (type != VDIR && (mode & S_ISTXT)) + return EFTYPE; + if (!puffs_cred_hasgroup(pcr, group) && (mode & S_ISGID)) + return EPERM; + } + + return 0; +} + +int +puffs_access_times(uid_t uid, gid_t gid, mode_t mode, int va_utimes_null, + const struct puffs_cred *pcr) +{ + + if (!puffs_cred_isuid(pcr, uid) && !puffs_cred_isjuggernaut(pcr) + && (va_utimes_null == 0 + || puffs_access(VNON, mode, uid, gid, PUFFS_VWRITE, pcr) != 0)) + return EPERM; + + return 0; +} diff --git a/lib/libpuffs/device.c b/lib/libpuffs/device.c new file mode 100644 index 000000000..92720ede3 --- /dev/null +++ b/lib/libpuffs/device.c @@ -0,0 +1,356 @@ +#include "fs.h" +#include +#include +#include +#include +#include +#include "drivers.h" + +#include + +FORWARD _PROTOTYPE( int safe_io_conversion, (endpoint_t driver, + cp_grant_id_t *gid, int *op, cp_grant_id_t *gids, endpoint_t *io_ept, + void **buffer, int *vec_grants, size_t bytes)); +FORWARD _PROTOTYPE( void safe_io_cleanup, (cp_grant_id_t, cp_grant_id_t *, + int)); +FORWARD _PROTOTYPE( int gen_opcl, (endpoint_t driver_e, int op, + dev_t dev, endpoint_t proc_e, int flags) ); +FORWARD _PROTOTYPE( int gen_io, (endpoint_t task_nr, message *mess_ptr) ); + + +/*===========================================================================* + * fs_new_driver * + *===========================================================================*/ +PUBLIC int fs_new_driver(void) +{ + /* New driver endpoint for this device */ + dev_t dev; + dev = (dev_t) fs_m_in.REQ_DEV; + driver_endpoints[major(dev)].driver_e = (endpoint_t) fs_m_in.REQ_DRIVER_E; + return(OK); +} + + +/*===========================================================================* + * safe_io_conversion * + *===========================================================================*/ +PRIVATE int safe_io_conversion(driver, gid, op, gids, io_ept, buffer, + vec_grants, bytes) +endpoint_t driver; +cp_grant_id_t *gid; +int *op; +cp_grant_id_t *gids; +endpoint_t *io_ept; +void **buffer; +int *vec_grants; +size_t bytes; +{ + unsigned int j; + int access; + iovec_t *v; + static iovec_t *new_iovec; + + STATICINIT(new_iovec, NR_IOREQS); + + /* Number of grants allocated in vector I/O. */ + *vec_grants = 0; + + /* Driver can handle it - change request to a safe one. */ + + *gid = GRANT_INVALID; + + switch(*op) { + case MFS_DEV_READ: + case MFS_DEV_WRITE: + /* Change to safe op. */ + *op = *op == MFS_DEV_READ ? DEV_READ_S : DEV_WRITE_S; + *gid = cpf_grant_direct(driver, (vir_bytes) *buffer, bytes, + *op == DEV_READ_S ? CPF_WRITE : CPF_READ); + if(*gid == GRANT_INVALID) { + panic("cpf_grant_magic of buffer failed"); + } + + break; + case MFS_DEV_GATHER: + case MFS_DEV_SCATTER: + /* Change to safe op. */ + *op = *op == MFS_DEV_GATHER ? DEV_GATHER_S : DEV_SCATTER_S; + + /* Grant access to my new i/o vector. */ + *gid = cpf_grant_direct(driver, (vir_bytes) new_iovec, + bytes * sizeof(iovec_t), CPF_READ|CPF_WRITE); + if(*gid == GRANT_INVALID) { + panic("cpf_grant_direct of vector failed"); + } + + v = (iovec_t *) *buffer; + + /* Grant access to i/o buffers. */ + for(j = 0; j < bytes; j++) { + if(j >= NR_IOREQS) + panic("vec too big: %u", bytes); + access = (*op == DEV_GATHER_S) ? CPF_WRITE : CPF_READ; + new_iovec[j].iov_addr = gids[j] = + cpf_grant_direct(driver, (vir_bytes) v[j].iov_addr, + (size_t) v[j].iov_size, access); + + if(!GRANT_VALID(gids[j])) { + panic("ext2: grant to iovec buf failed"); + } + new_iovec[j].iov_size = v[j].iov_size; + (*vec_grants)++; + } + + /* Set user's vector to the new one. */ + *buffer = new_iovec; + break; + default: + panic("Illegal operation %d\n", *op); + break; + } + + /* If we have converted to a safe operation, I/O + * endpoint becomes FS if it wasn't already. + */ + if(GRANT_VALID(*gid)) { + *io_ept = SELF_E; + return 1; + } + + /* Not converted to a safe operation (because there is no + * copying involved in this operation). + */ + return 0; +} + +/*===========================================================================* + * safe_io_cleanup * + *===========================================================================*/ +PRIVATE void safe_io_cleanup(gid, gids, gids_size) +cp_grant_id_t gid; +cp_grant_id_t *gids; +int gids_size; +{ +/* Free resources (specifically, grants) allocated by safe_io_conversion(). */ + int j; + + (void) cpf_revoke(gid); + + for(j = 0; j < gids_size; j++) + (void) cpf_revoke(gids[j]); + + return; +} + +/*===========================================================================* + * block_dev_io * + *===========================================================================*/ +PUBLIC int block_dev_io( + int op, /* MFS_DEV_READ, MFS_DEV_WRITE, etc. */ + dev_t dev, /* major-minor device number */ + endpoint_t proc_e, /* in whose address space is buf? */ + void *buffer, /* virtual address of the buffer */ + u64_t pos, /* byte position */ + size_t bytes /* how many bytes to transfer */ +) +{ +/* Read or write from a device. The parameter 'dev' tells which one. */ + int r, safe; + message m; + cp_grant_id_t gid = GRANT_INVALID; + int vec_grants; + int op_used; + void *buf_used; + static cp_grant_id_t *gids; + endpoint_t driver_e; + + STATICINIT(gids, NR_IOREQS); + + /* Determine driver endpoint for this device */ + driver_e = driver_endpoints[major(dev)].driver_e; + + /* See if driver is roughly valid. */ + if (driver_e == NONE) { + lpuffs_debug("%d block_dev_io: no driver for dev %x\n", SELF_E, dev); + return(EDEADEPT); + } + + /* The io vector copying relies on this I/O being for FS itself. */ + if(proc_e != SELF_E) { + lpuffs_debug("%d doing block_dev_io for non-self %d\n", SELF_E, proc_e); + panic("doing block_dev_io for non-self: %d", proc_e); + } + + /* By default, these are right. */ + m.USER_ENDPT = proc_e; + m.ADDRESS = buffer; + buf_used = buffer; + + /* Convert parameters to 'safe mode'. */ + op_used = op; + safe = safe_io_conversion(driver_e, &gid, &op_used, gids, &m.USER_ENDPT, + &buf_used, &vec_grants, bytes); + + /* Set up rest of the message. */ + if (safe) m.IO_GRANT = (char *) gid; + + m.m_type = op_used; + m.DEVICE = minor(dev); + m.POSITION = ex64lo(pos); + m.COUNT = bytes; + m.HIGHPOS = ex64hi(pos); + + /* Call the task. */ + r = sendrec(driver_e, &m); + if(r == OK && m.REP_STATUS == ERESTART) r = EDEADEPT; + + /* As block I/O never SUSPENDs, safe cleanup must be done whether + * the I/O succeeded or not. */ + if (safe) safe_io_cleanup(gid, gids, vec_grants); + + /* RECOVERY: + * - send back dead driver number + * - VFS unmaps it, waits for new driver + * - VFS sends the new driver endp for the FS proc and the request again + */ + if (r != OK) { + if (r == EDEADSRCDST || r == EDEADEPT) { + lpuffs_debug("%d dead driver %d\n", SELF_E, driver_e); + driver_endpoints[major(dev)].driver_e = NONE; + return(r); + } else if (r == ELOCKED) { + lpuffs_debug("%d ELOCKED talking to %d\n", SELF_E, driver_e); + return(r); + } else + panic("call_task: can't send/receive: %d", r); + } else { + /* Did the process we did the sendrec() for get a result? */ + if (m.REP_ENDPT != proc_e) { + lpuffs_debug("%d strange device reply from %d, type = %d, proc " + "= %d (not %d) (2) ignored\n", SELF_E, m.m_source, + m.m_type, proc_e, m.REP_ENDPT); + r = EIO; + } + } + + /* Task has completed. See if call completed. */ + if (m.REP_STATUS == SUSPEND) { + panic("ext2 block_dev_io: driver returned SUSPEND"); + } + + if(buffer != buf_used && r == OK) { + memcpy(buffer, buf_used, bytes * sizeof(iovec_t)); + } + + return(m.REP_STATUS); +} + +/*===========================================================================* + * dev_open * + *===========================================================================*/ +PUBLIC int dev_open( + endpoint_t driver_e, + dev_t dev, /* device to open */ + endpoint_t proc_e, /* process to open for */ + int flags /* mode bits and flags */ +) +{ + int major, r; + + /* Determine the major device number call the device class specific + * open/close routine. (This is the only routine that must check the + * device number for being in range. All others can trust this check.) + */ + major = major(dev); + if (major >= NR_DEVICES) { + lpuffs_debug("Major device number %d not in range\n", major(dev)); + return(EIO); + } + r = gen_opcl(driver_e, DEV_OPEN, dev, proc_e, flags); + if (r == SUSPEND) panic("suspend on open from"); + return(r); +} + + +/*===========================================================================* + * dev_close * + *===========================================================================*/ +PUBLIC void dev_close( + endpoint_t driver_e, + dev_t dev /* device to close */ +) +{ + (void) gen_opcl(driver_e, DEV_CLOSE, dev, 0, 0); +} + + +/*===========================================================================* + * gen_opcl * + *===========================================================================*/ +PRIVATE int gen_opcl( + endpoint_t driver_e, + int op, /* operation, DEV_OPEN or DEV_CLOSE */ + dev_t dev, /* device to open or close */ + endpoint_t proc_e, /* process to open/close for */ + int flags /* mode bits and flags */ +) +{ +/* Called from the dmap struct in table.c on opens & closes of special files.*/ + message dev_mess; + + dev_mess.m_type = op; + dev_mess.DEVICE = minor(dev); + dev_mess.USER_ENDPT = proc_e; + dev_mess.COUNT = flags; + + /* Call the task. */ + (void) gen_io(driver_e, &dev_mess); + + return(dev_mess.REP_STATUS); +} + + +/*===========================================================================* + * gen_io * + *===========================================================================*/ +PRIVATE int gen_io( + endpoint_t task_nr, /* which task to call */ + message *mess_ptr /* pointer to message for task */ +) +{ +/* All file system I/O ultimately comes down to I/O on major/minor device + * pairs. These lead to calls on the following routines via the dmap table. + */ + + int r, proc_e; + + proc_e = mess_ptr->USER_ENDPT; + + r = sendrec(task_nr, mess_ptr); + if(r == OK && mess_ptr->REP_STATUS == ERESTART) + r = EDEADEPT; + + if (r != OK) { + if (r == EDEADSRCDST || r == EDEADEPT) { + lpuffs_debug("fs: dead driver %d\n", task_nr); + panic("should handle crashed drivers"); + return(r); + } + if (r == ELOCKED) { + lpuffs_debug("fs: ELOCKED talking to %d\n", task_nr); + return(r); + } + panic("call_task: can't send/receive: %d", r); + } + + /* Did the process we did the sendrec() for get a result? */ + if (mess_ptr->REP_ENDPT != proc_e) { + lpuffs_debug("fs: strange device reply from %d, type = %d, proc = %d (not " + "%d) (2) ignored\n", mess_ptr->m_source, mess_ptr->m_type, + proc_e, + mess_ptr->REP_ENDPT); + return(EIO); + } + + return(OK); +} diff --git a/lib/libpuffs/drivers.h b/lib/libpuffs/drivers.h new file mode 100644 index 000000000..cbe8032ac --- /dev/null +++ b/lib/libpuffs/drivers.h @@ -0,0 +1,17 @@ +#ifndef LIBPUFFS_DRIVERS_H +#define LIBPUFFS_DRIVERS_H + +/* Args to dev_bio/dev_io */ +#define MFS_DEV_READ 10001 +#define MFS_DEV_WRITE 10002 +#define MFS_DEV_SCATTER 10003 +#define MFS_DEV_GATHER 10004 + +/* Driver endpoints for major devices. Only the block devices + * are mapped here, it's a subset of the mapping in the VFS */ + +EXTERN struct driver_endpoints { + endpoint_t driver_e; +} driver_endpoints[NR_DEVICES]; + +#endif /* LIBPUFFS_DRIVERS_H */ diff --git a/lib/libpuffs/fs.h b/lib/libpuffs/fs.h new file mode 100644 index 000000000..691b0dd2e --- /dev/null +++ b/lib/libpuffs/fs.h @@ -0,0 +1,31 @@ +/* This is the master header for fs. It includes some other files + * and defines the principal constants. + */ + +#ifndef LIBPUFFS_FS_H +#define LIBPUFFS_FS_H + +#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */ +#define _MINIX 1 /* tell headers to include MINIX stuff */ +#define _SYSTEM 1 /* tell headers that this is the kernel */ + +#define VERBOSE 0 /* show messages during initialization? */ + +/* The following are so basic, all the *.c files get them automatically. */ +#include /* MUST be first */ +#include /* MUST be second */ +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "proto.h" +#include "glo.h" + +#endif /* LIBPUFFS_FS_H */ diff --git a/lib/libpuffs/glo.h b/lib/libpuffs/glo.h new file mode 100644 index 000000000..db34d56a6 --- /dev/null +++ b/lib/libpuffs/glo.h @@ -0,0 +1,61 @@ +/* EXTERN should be extern except for the table file */ + +#ifndef LIBPUFFS_GLO_H +#define LIBPUFFS_GLO_H + +#ifdef _TABLE +#undef EXTERN +#define EXTERN +#endif + +#include + +#include "puffs_msgif.h" +#include "drivers.h" + +EXTERN struct puffs_usermount *global_pu; + +EXTERN int is_readonly_fs; +EXTERN int is_root_fs; +EXTERN int buildpath; + +/* Sometimes user can call exit. If we received a message, + * report a failure to VFS before exiting. Especially on mount + * and unmount. + * + * Either transid of last request or 0. + */ +EXTERN int last_request_transid; + +/* The following variables are used for returning results to the caller. */ +EXTERN int err_code; /* temporary storage for error number */ + +/* TODO: it duplicates caller_uid and caller_gid */ +EXTERN struct puffs_kcred global_kcred; + +extern _PROTOTYPE (int (*fs_call_vec[]), (void) ); /* fs call table */ + +EXTERN message fs_m_in; +EXTERN message fs_m_out; +EXTERN vfs_ucred_t credentials; + +EXTERN uid_t caller_uid; +EXTERN gid_t caller_gid; + +EXTERN int req_nr; + +EXTERN endpoint_t SELF_E; + +EXTERN char user_path[PATH_MAX+1]; /* pathname to be processed */ + +EXTERN dev_t fs_dev; /* The device that is handled by this FS proc + */ +EXTERN char fs_dev_label[16]; /* Name of the device driver that is handled + * by this FS proc. + */ +EXTERN char fs_name[PATH_MAX+1]; + +EXTERN int unmountdone; +EXTERN int exitsignaled; + +#endif /* LIBPUFFS_GLO_H */ diff --git a/lib/libpuffs/inode.c b/lib/libpuffs/inode.c new file mode 100644 index 000000000..510260be7 --- /dev/null +++ b/lib/libpuffs/inode.c @@ -0,0 +1,103 @@ +/* + * Created (MFS based): + * June 2011 (Evgeniy Ivanov) + */ + +#include "fs.h" +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + + +void release_node(struct puffs_usermount *pu, struct puffs_node *pn) +{ + assert(pn->pn_count == 0); + + /* Required if puffs_node_reclaim() decides to leave node in the list */ + pn->pn_mountpoint = FALSE; + + if (pu->pu_ops.puffs_node_reclaim) { + if (global_pu->pu_ops.puffs_node_reclaim(global_pu, pn) != 0) + lpuffs_debug("Warning: reclaim failed\n"); + } else { + puffs_pn_put(pn); + } +} + + +/*===========================================================================* + * fs_putnode * + *===========================================================================*/ +PUBLIC int fs_putnode(void) +{ +/* Find the pnode specified by the request message and decrease its counter. + * Release unused pnode. + */ + struct puffs_node *pn; + int count = fs_m_in.REQ_COUNT; + ino_t inum = fs_m_in.REQ_INODE_NR; + + if ((pn = puffs_pn_nodewalk(global_pu, 0, &inum)) == NULL) { + /* XXX Probably removed from the list, see puffs_pn_remove() */ + struct puffs_node *pn_cur, *pn_next; + pn_cur = LIST_FIRST(&global_pu->pu_pnode_removed_lst); + while (pn_cur) { + pn_next = LIST_NEXT(pn_cur, pn_entries); + if (pn_cur->pn_va.va_fileid == inum) { + pn = pn_cur; + break; + } + pn_cur = pn_next; + } + } + + if (pn == NULL) { + lpuffs_debug("%s:%d putnode: pnode #%ld dev: %d not found\n", __FILE__, + __LINE__, inum, fs_dev); + panic("fs_putnode failed"); + } + + if (count <= 0) { + lpuffs_debug("%s:%d putnode: bad value for count: %d\n", __FILE__, + __LINE__, count); + panic("fs_putnode failed"); + } else if (pn->pn_count == 0) { + /* FUSE fs might store in the list pnodes, which we hasn't + * open, this means we got put request for file, + * which wasn't opened by VFS. + */ + lpuffs_debug("%s:%d putnode: pn_count already zero\n", __FILE__, + __LINE__); + panic("fs_putnode failed"); + } else if (count > pn->pn_count) { + struct puffs_node *pn_cur, *pn_next; + struct puffs_usermount *pu = global_pu; + ino_t ino = pn->pn_va.va_fileid; + + pn_cur = LIST_FIRST(&pu->pu_pnodelst); + lpuffs_debug("inum count path polen hash\n"); + while (pn_cur) { + pn_next = LIST_NEXT(pn_cur, pn_entries); + if (pn_cur->pn_va.va_fileid == ino) { + lpuffs_debug("%ld: %d %s %u %u\n", ino, pn_cur->pn_count, + pn_cur->pn_po.po_path, + pn_cur->pn_po.po_len, + pn_cur->pn_po.po_hash); + } + pn_cur = pn_next; + } + lpuffs_debug("%s:%d putnode: count too high: %d > %d\n", __FILE__, + __LINE__, count, pn->pn_count); + panic("fs_putnode failed"); + } + + pn->pn_count -= count; + + if (pn->pn_count == 0) + release_node(global_pu, pn); + + return(OK); +} diff --git a/lib/libpuffs/link.c b/lib/libpuffs/link.c new file mode 100644 index 000000000..e85222450 --- /dev/null +++ b/lib/libpuffs/link.c @@ -0,0 +1,560 @@ +#include "fs.h" + +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + +#define SAME 1000 + + +/*===========================================================================* + * fs_ftrunc * + *===========================================================================*/ +PUBLIC int fs_ftrunc(void) +{ + int r; + struct puffs_node *pn; + off_t start, end; + PUFFS_MAKECRED(pcr, &global_kcred); + + if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(EINVAL); + + start = fs_m_in.REQ_TRC_START_LO; + end = fs_m_in.REQ_TRC_END_LO; + + if (end == 0) { + struct vattr va; + + if (pn->pn_va.va_size == start) + return(OK); + + if (global_pu->pu_ops.puffs_node_setattr == NULL) + return(EINVAL); + + puffs_vattr_null(&va); + va.va_size = start; + + r = global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr); + if (r) return(EINVAL); + } else { + /* XXX zerofill the given region. Can we make a hole? */ + off_t bytes_left = end - start; + char* rw_buf; + + if (global_pu->pu_ops.puffs_node_write == NULL) + return(EINVAL); + + /* XXX split into chunks? */ + rw_buf = malloc(bytes_left); + if (!rw_buf) + panic("fs_ftrunc: failed to allocated memory\n"); + memset(rw_buf, 0, bytes_left); + + r = global_pu->pu_ops.puffs_node_write(global_pu, pn, (uint8_t *)rw_buf, + start, (size_t *) &bytes_left, pcr, 0); + free(rw_buf); + if (r) return(EINVAL); + } + + update_times(pn, CTIME | MTIME, 0); + + return(r); +} + + +/*===========================================================================* + * fs_link * + *===========================================================================*/ +PUBLIC int fs_link() +{ +/* Perform the link(name1, name2) system call. */ + + register int r; + char string[NAME_MAX + 1]; + phys_bytes len; + struct puffs_node *pn, *pn_dir, *new_pn; + time_t cur_time; + struct puffs_kcn pkcnp; + PUFFS_MAKECRED(pcr, &global_kcred); + struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *)pcr, {0}}; + + if (global_pu->pu_ops.puffs_node_link == NULL) + return(OK); + + /* Copy the link name's last component */ + len = fs_m_in.REQ_PATH_LEN; + if (len > NAME_MAX + 1) + return(ENAMETOOLONG); + + r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT, 0, + (vir_bytes) string, (size_t) len, D); + if (r != OK) return(r); + NUL(string, len, sizeof(string)); + + if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(EINVAL); + + /* Check to see if the file has maximum number of links already. */ + if (pn->pn_va.va_nlink >= LINK_MAX) + return(EMLINK); + + /* Only super_user may link to directories. */ + if ((pn->pn_va.va_mode & I_TYPE) == I_DIRECTORY && caller_uid != SU_UID) + return(EPERM); + + if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_DIR_INO)) == NULL) + return(EINVAL); + + if (pn_dir->pn_va.va_nlink == NO_LINK) { + /* Dir does not actually exist */ + return(ENOENT); + } + + /* If 'name2' exists in full (even if no space) set 'r' to error. */ + if ((new_pn = advance(pn_dir, string, IGN_PERM)) == NULL) { + r = err_code; + if (r == ENOENT) r = OK; + } else { + r = EEXIST; + } + + if (r != OK) return(r); + + /* Try to link. */ + pcn.pcn_namelen = strlen(string); + assert(pcn.pcn_namelen <= MAXPATHLEN); + strcpy(pcn.pcn_name, string); + + if (buildpath) { + if (puffs_path_pcnbuild(global_pu, &pcn, pn_dir) != 0) + return(EINVAL); + } + + if (global_pu->pu_ops.puffs_node_link(global_pu, pn_dir, pn, &pcn) != 0) + r = EINVAL; + + if (buildpath) + global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full); + + if (r != OK) return(EINVAL); + + cur_time = clock_time(); + update_times(pn, CTIME, cur_time); + update_times(pn_dir, MTIME | CTIME, cur_time); + + return(OK); +} + + +/*===========================================================================* + * fs_rdlink * + *===========================================================================*/ +PUBLIC int fs_rdlink() +{ + register int r; /* return value */ + size_t copylen; + struct puffs_node *pn; + char user_path[PATH_MAX]; + PUFFS_MAKECRED(pcr, &global_kcred); + + copylen = fs_m_in.REQ_MEM_SIZE < UMAX_FILE_POS ? + fs_m_in.REQ_MEM_SIZE : UMAX_FILE_POS; + + assert(copylen <= PATH_MAX); + + if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(EINVAL); + + if (!S_ISLNK(pn->pn_va.va_mode)) + return(EACCES); + + if (global_pu->pu_ops.puffs_node_readlink == NULL) + return(EINVAL); + + r = global_pu->pu_ops.puffs_node_readlink(global_pu, pn, pcr, user_path, + ©len); + if (r != OK) { + if (r > 0) r = -r; + return(r); + } + + r = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT, + (vir_bytes) 0, (vir_bytes) user_path, (size_t) copylen, D); + if (r == OK) + fs_m_out.RES_NBYTES = copylen; + + return(r); +} + + +FORWARD _PROTOTYPE( void release_node, (struct puffs_usermount *pu, + struct puffs_node *pn )); + +/*===========================================================================* + * fs_rename * + *===========================================================================*/ +PUBLIC int fs_rename() +{ +/* Perform the rename(name1, name2) system call. */ + struct puffs_node *old_dirp, *old_ip; /* ptrs to old dir, file pnodes */ + struct puffs_node *new_dirp, *new_ip; /* ptrs to new dir, file pnodes */ + struct puffs_kcn pkcnp_src; + PUFFS_MAKECRED(pcr_src, &global_kcred); + struct puffs_cn pcn_src = {&pkcnp_src, (struct puffs_cred *) pcr_src, {0}}; + struct puffs_kcn pkcnp_dest; + PUFFS_MAKECRED(pcr_dest, &global_kcred); + struct puffs_cn pcn_targ = {&pkcnp_dest, (struct puffs_cred *) pcr_dest, {0}}; + int r = OK; /* error flag; initially no error */ + int odir, ndir; /* TRUE iff {old|new} file is dir */ + int same_pdir; /* TRUE iff parent dirs are the same */ + phys_bytes len; + time_t cur_time; + + if (global_pu->pu_ops.puffs_node_rename == NULL) + return(EINVAL); + + /* Copy the last component of the old name */ + len = fs_m_in.REQ_REN_LEN_OLD; /* including trailing '\0' */ + if (len > NAME_MAX + 1) + return(ENAMETOOLONG); + + r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_REN_GRANT_OLD, + (vir_bytes) 0, (vir_bytes) pcn_src.pcn_name, (size_t) len, D); + if (r != OK) return(r); + NUL(pcn_src.pcn_name, len, sizeof(pcn_src.pcn_name)); + pcn_src.pcn_namelen = len - 1; + + /* Copy the last component of the new name */ + len = fs_m_in.REQ_REN_LEN_NEW; /* including trailing '\0' */ + if (len > NAME_MAX + 1) + return(ENAMETOOLONG); + + r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_REN_GRANT_NEW, + (vir_bytes) 0, (vir_bytes) pcn_targ.pcn_name, (size_t) len, D); + if (r != OK) return(r); + NUL(pcn_targ.pcn_name, len, sizeof(pcn_targ.pcn_name)); + pcn_targ.pcn_namelen = len - 1; + + /* Get old dir pnode */ + if ((old_dirp = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_REN_OLD_DIR)) + == NULL) + return(ENOENT); + + old_ip = advance(old_dirp, pcn_src.pcn_name, IGN_PERM); + if (!old_ip) { + return(ENOENT); + } + r = err_code; + + if (r == EENTERMOUNT || r == ELEAVEMOUNT) { + old_ip = NULL; + if (r == EENTERMOUNT) r = EXDEV; /* should this fail at all? */ + else if (r == ELEAVEMOUNT) r = EINVAL; /* rename on dot-dot */ + } + + /* Get new dir pnode */ + if ((new_dirp = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_REN_NEW_DIR)) + == NULL) { + r = ENOENT; + } else { + if (new_dirp->pn_va.va_nlink == NO_LINK) { + /* Dir does not actually exist */ + return(ENOENT); + } + } + + /* not required to exist */ + new_ip = advance(new_dirp, pcn_targ.pcn_name, IGN_PERM); + + /* However, if the check failed because the file does exist, don't continue. + * Note that ELEAVEMOUNT is covered by the dot-dot check later. */ + if (err_code == EENTERMOUNT) { + new_ip = NULL; + r = EBUSY; + } + + if (old_ip != NULL) { + /* TRUE iff dir */ + odir = ((old_ip->pn_va.va_mode & I_TYPE) == I_DIRECTORY); + } else { + odir = FALSE; + } + + if (r != OK) return(r); + + /* Check for a variety of possible errors. */ + same_pdir = (old_dirp == new_dirp); + + /* The old or new name must not be . or .. */ + if (strcmp(pcn_src.pcn_name, ".") == 0 || + strcmp(pcn_src.pcn_name, "..") == 0 || + strcmp(pcn_targ.pcn_name, ".") == 0 || + strcmp(pcn_targ.pcn_name, "..") == 0) { + r = EINVAL; + } + + /* Some tests apply only if the new path exists. */ + if (new_ip == NULL) { + if (odir && (new_dirp->pn_va.va_nlink >= SHRT_MAX || + new_dirp->pn_va.va_nlink >= LINK_MAX) && + !same_pdir && r == OK) { + r = EMLINK; + } + } else { + if (old_ip == new_ip) r = SAME; /* old=new */ + + /* dir ? */ + ndir = ((new_ip->pn_va.va_mode & I_TYPE) == I_DIRECTORY); + if (odir == TRUE && ndir == FALSE) r = ENOTDIR; + if (odir == FALSE && ndir == TRUE) r = EISDIR; + } + + if (r == SAME) { + r = OK; + goto rename_out; + } + + if (r != OK) return(r); + + /* If a process has another root directory than the system root, we might + * "accidently" be moving it's working directory to a place where it's + * root directory isn't a super directory of it anymore. This can make + * the function chroot useless. If chroot will be used often we should + * probably check for it here. */ + + /* The rename will probably work. Only two things can go wrong now: + * 1. being unable to remove the new file. (when new file already exists) + * 2. being unable to make the new directory entry. (new file doesn't exists) + * [directory has to grow by one block and cannot because the disk + * is completely full]. + * 3. Something (doubtfully) else depending on the FS. + */ + + if (buildpath) { + pcn_src.pcn_po_full = old_ip->pn_po; + + if (puffs_path_pcnbuild(global_pu, &pcn_targ, new_dirp) != 0) + return(EINVAL); + } + + r = global_pu->pu_ops.puffs_node_rename(global_pu, old_dirp, old_ip, &pcn_src, + new_dirp, new_ip, &pcn_targ); + if (r > 0) r = -r; + + if (buildpath) { + if (r) { + global_pu->pu_pathfree(global_pu, &pcn_targ.pcn_po_full); + } else { + struct puffs_pathinfo pi; + struct puffs_pathobj po_old; + + /* handle this node */ + po_old = old_ip->pn_po; + old_ip->pn_po = pcn_targ.pcn_po_full; + + if (old_ip->pn_va.va_type != VDIR) { + global_pu->pu_pathfree(global_pu, &po_old); + return(OK); + } + + /* handle all child nodes for DIRs */ + pi.pi_old = &pcn_src.pcn_po_full; + pi.pi_new = &pcn_targ.pcn_po_full; + + PU_LOCK(); + if (puffs_pn_nodewalk(global_pu, puffs_path_prefixadj, &pi) + != NULL) { + /* Actually nomem */ + return(EINVAL); + } + PU_UNLOCK(); + global_pu->pu_pathfree(global_pu, &po_old); + } + } + +rename_out: + cur_time = clock_time(); + update_times(old_dirp, MTIME | CTIME, cur_time); + update_times(new_dirp, MTIME | CTIME, cur_time); + + /* XXX see release_node comment in fs_unlink */ + if (new_ip && new_ip->pn_count == 0) { + release_node(global_pu, new_ip); + } + + return(r); +} + +FORWARD _PROTOTYPE( int remove_dir, (struct puffs_node *pn_dir, + struct puffs_node *pn, struct puffs_cn *pcn)); +FORWARD _PROTOTYPE( int unlink_file, (struct puffs_node *dirp, + struct puffs_node *pn, struct puffs_cn *pcn)); + +/*===========================================================================* + * fs_unlink * + *===========================================================================*/ +PUBLIC int fs_unlink() +{ +/* Perform the unlink(name) or rmdir(name) system call. The code for these two + * is almost the same. They differ only in some condition testing. Unlink() + * may be used by the superuser to do dangerous things; rmdir() may not. + */ + int r; + struct puffs_node *pn, *pn_dir; + time_t cur_time; + struct puffs_kcn pkcnp; + struct puffs_cn pcn = {&pkcnp, 0, {0}}; + PUFFS_KCREDTOCRED(pcn.pcn_cred, &global_kcred); + int len; + + /* Copy the last component */ + len = fs_m_in.REQ_PATH_LEN; + pcn.pcn_namelen = len - 1; + if (pcn.pcn_namelen > NAME_MAX) + return(ENAMETOOLONG); + + r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT, + (vir_bytes) 0, (vir_bytes) pcn.pcn_name, + (size_t) len, D); + if (r != OK) return (r); + NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name)); + + if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(EINVAL); + + /* The last directory exists. Does the file also exist? */ + pn = advance(pn_dir, pcn.pcn_name, IGN_PERM); + r = err_code; + + /* If error, return pnode. */ + if (r != OK) { + /* Mount point? */ + if (r == EENTERMOUNT || r == ELEAVEMOUNT) { + r = EBUSY; + } + return(r); + } + + /* Now test if the call is allowed, separately for unlink() and rmdir(). */ + if (fs_m_in.m_type == REQ_UNLINK) { + /* Only the su may unlink directories, but the su can unlink any dir */ + if ((pn->pn_va.va_mode & I_TYPE) == I_DIRECTORY) + r = EPERM; + if (r == OK) + r = unlink_file(pn_dir, pn, &pcn); + } else { + r = remove_dir(pn_dir, pn, &pcn); /* call is RMDIR */ + } + + if (pn->pn_va.va_nlink != 0) { + cur_time = clock_time(); + update_times(pn, CTIME, cur_time); + update_times(pn_dir, MTIME | CTIME, cur_time); + } + + /* XXX Ideally, we should check pn->pn_flags & PUFFS_NODE_REMOVED, but + * librefuse doesn't set it (neither manually or via puffs_pn_remove() ). + * Thus we just check that "pn_count == 0". Otherwise release_node() + * will be called in fs_put(). + */ + if (pn->pn_count == 0) + release_node(global_pu, pn); + + return(r); +} + + +/*===========================================================================* + * remove_dir * + *===========================================================================*/ +PRIVATE int remove_dir(pn_dir, pn, pcn) +struct puffs_node *pn_dir; /* parent directory */ +struct puffs_node *pn; /* directory to be removed */ +struct puffs_cn *pcn; /* Name, creads of directory */ +{ + /* A directory file has to be removed. Five conditions have to met: + * - The file must be a directory + * - The directory must be empty (except for . and ..) + * - The final component of the path must not be . or .. + * - The directory must not be the root of a mounted file system (VFS) + * - The directory must not be anybody's root/working directory (VFS) + */ + + /* "." and ".." dentries can be stored in 28 bytes */ + #define EMPTY_DIR_DENTRIES_SIZE 28 + int r; + char remove_dir_buf[EMPTY_DIR_DENTRIES_SIZE]; + struct dirent *dent = (struct dirent*) remove_dir_buf; + int buf_left = EMPTY_DIR_DENTRIES_SIZE; + off_t pos = 0; + int eofflag = 0; + + if (global_pu->pu_ops.puffs_node_rmdir == NULL) + return(EINVAL); + + if (!S_ISDIR(pn->pn_va.va_mode)) + return(ENOTDIR); + + /* Check if directory is empty */ + r = global_pu->pu_ops.puffs_node_readdir(global_pu, pn, dent, &pos, + (size_t *)&buf_left, pcn->pcn_cred, &eofflag, 0, 0); + if (r) return(EINVAL); + if (!eofflag) return(ENOTEMPTY); + + if (strcmp(pcn->pcn_name, ".") == 0 || strcmp(pcn->pcn_name, "..") == 0) + return(EINVAL); + + if (pn->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid) + return(EBUSY); /* can't remove 'root' */ + + if (buildpath) { + r = puffs_path_pcnbuild(global_pu, pcn, pn_dir); + if (r) return(EINVAL); + } + + r = global_pu->pu_ops.puffs_node_rmdir(global_pu, pn_dir, pn, pcn); + + global_pu->pu_pathfree(global_pu, &pcn->pcn_po_full); + + if (r) return(EINVAL); + + return(OK); +} + + +/*===========================================================================* + * unlink_file * + *===========================================================================*/ +PRIVATE int unlink_file(dirp, pn, pcn) +struct puffs_node *dirp; /* parent directory of file */ +struct puffs_node *pn; /* pnode of file, may be NULL too. */ +struct puffs_cn *pcn; /* Name, creads of file */ +{ +/* Unlink 'file_name'; pn must be the pnode of 'file_name' */ + int r; + + assert(pn != NULL); + + if (global_pu->pu_ops.puffs_node_remove == NULL) + return(EINVAL); + + if (S_ISDIR(pn->pn_va.va_mode)) + return(EINVAL); + + if (buildpath) { + r = puffs_path_pcnbuild(global_pu, pcn, dirp); + if (r) + return(EINVAL); + } + + r = global_pu->pu_ops.puffs_node_remove(global_pu, dirp, pn, pcn); + + global_pu->pu_pathfree(global_pu, &pcn->pcn_po_full); + + if (r) return(EINVAL); + + return(OK); +} diff --git a/lib/libpuffs/misc.c b/lib/libpuffs/misc.c new file mode 100644 index 000000000..d9694386f --- /dev/null +++ b/lib/libpuffs/misc.c @@ -0,0 +1,53 @@ +/* Created (MFS based): + * June 2011 (Evgeniy Ivanov) + */ + +#include "fs.h" +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + +/*===========================================================================* + * fs_sync * + *===========================================================================*/ +PUBLIC int fs_sync() +{ +/* Perform the sync() system call. Flush all the tables. + * The order in which the various tables are flushed is critical. + */ + int r; + PUFFS_MAKECRED(pcr, &global_kcred); + + if (is_readonly_fs) + return(OK); /* nothing to sync */ + + r = global_pu->pu_ops.puffs_fs_sync(global_pu, MNT_WAIT, pcr); + if (r) { + lpuffs_debug("Warning: sync failed!\n"); + } + + return(OK); /* sync() can't fail */ +} + + +/*===========================================================================* + * fs_flush * + *===========================================================================*/ +PUBLIC int fs_flush() +{ +/* Flush the blocks of a device from the cache after writing any dirty blocks + * to disk. + */ +#if 0 + dev_t dev = (dev_t) fs_m_in.REQ_DEV; + + if(dev == fs_dev) return(EBUSY); + + flushall(dev); + invalidate(dev); +#endif + + return(OK); +} diff --git a/lib/libpuffs/mntopts.h b/lib/libpuffs/mntopts.h new file mode 100644 index 000000000..1db0d2bc6 --- /dev/null +++ b/lib/libpuffs/mntopts.h @@ -0,0 +1,13 @@ +#ifndef _MNTOPTS_H_ +#define _MNTOPTS_H_ + +struct mntopt { + const char *m_option; /* option name */ + int m_inverse; /* if a negative option, eg "dev" */ + int m_flag; /* bit to set, eg. MNT_RDONLY */ + int m_altloc; /* 1 => set bit in altflags */ +}; + + + +#endif diff --git a/lib/libpuffs/mount.c b/lib/libpuffs/mount.c new file mode 100644 index 000000000..eef46a49c --- /dev/null +++ b/lib/libpuffs/mount.c @@ -0,0 +1,141 @@ +/* Created (MFS based): + * June 2011 (Evgeniy Ivanov) + */ + +#include "fs.h" +#include +#include +#include +#include +#include +#include + +#include "puffs_priv.h" + +/*===========================================================================* + * fs_readsuper * + *===========================================================================*/ +PUBLIC int fs_readsuper() +{ + int r = OK; + cp_grant_id_t label_gid; + size_t label_len; + endpoint_t driver_e; + struct vattr *root_va; + + fs_dev = fs_m_in.REQ_DEV; + label_gid = fs_m_in.REQ_GRANT; + label_len = fs_m_in.REQ_PATH_LEN; + is_readonly_fs = (fs_m_in.REQ_FLAGS & REQ_RDONLY) ? 1 : 0; + is_root_fs = (fs_m_in.REQ_FLAGS & REQ_ISROOT) ? 1 : 0; + + if (label_len > sizeof(fs_dev_label)) + return(EINVAL); + + r = sys_safecopyfrom(fs_m_in.m_source, label_gid, 0, + (vir_bytes)fs_dev_label, label_len, D); + if (r != OK) { + lpuffs_debug("%s:%d fs_readsuper: safecopyfrom failed: %d\n", + __FILE__, __LINE__, r); + return(EINVAL); + } + + fs_m_out.RES_DEV = NO_DEV; + + if (strlen(fs_dev_label)) { + /* Map the driver endpoint for this major */ + r= ds_retrieve_label_endpt(fs_dev_label, &driver_e); + if (r != OK) + { + lpuffs_debug("fs_readsuper: ds_retrieve_label_endpt failed for '%s': %d\n", + fs_dev_label, r); + return EINVAL; + } + + driver_endpoints[(fs_dev >> MAJOR) & BYTE].driver_e = driver_e; + + /* Open the device the file system lives on. */ + if (dev_open(driver_e, fs_dev, driver_e, + is_readonly_fs ? R_BIT : (R_BIT|W_BIT)) != OK) { + return(EINVAL); + } + fs_m_out.RES_DEV = fs_dev; + } + + /* Open root pnode */ + global_pu->pu_pn_root->pn_count = 1; + + /* Root pnode properties */ + root_va = &global_pu->pu_pn_root->pn_va; + fs_m_out.RES_INODE_NR = root_va->va_fileid; + fs_m_out.RES_MODE = root_va->va_mode; + fs_m_out.RES_FILE_SIZE_LO = root_va->va_size; + fs_m_out.RES_UID = root_va->va_uid; + fs_m_out.RES_GID = root_va->va_gid; + + return(r); +} + + +/*===========================================================================* + * fs_mountpoint * + *===========================================================================*/ +PUBLIC int fs_mountpoint() +{ +/* This function looks up the mount point, it checks the condition whether + * the partition can be mounted on the pnode or not. + */ + int r = OK; + struct puffs_node *pn; + mode_t bits; + + /* + * XXX: we assume that lookup was done first, so pnode can be found with + * puffs_pn_nodewalk. + */ + if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(EINVAL); + + + if (pn->pn_mountpoint) r = EBUSY; + + /* It may not be special. */ + bits = pn->pn_va.va_mode & I_TYPE; + if(bits == I_BLOCK_SPECIAL || bits == I_CHAR_SPECIAL) r = ENOTDIR; + + if (r == OK) + pn->pn_mountpoint = TRUE; + + return(r); +} + + +/*===========================================================================* + * fs_unmount * + *===========================================================================*/ +PUBLIC int fs_unmount() +{ + int error; + + /* XXX there is no information about flags, 0 should be safe enough */ + error = global_pu->pu_ops.puffs_fs_unmount(global_pu, 0); + if (error) { + /* XXX we can't return any error to VFS */ + lpuffs_debug("user handler failed to unmount filesystem!\ + Force unmount!\n"); + } + + fs_sync(); + + if (strlen(fs_dev_label)) { + /* Close the device the file system lives on. */ + dev_close(driver_endpoints[(fs_dev >> MAJOR) & BYTE].driver_e, fs_dev); + } + + /* Finish off the unmount. */ + PU_SETSTATE(global_pu, PUFFS_STATE_UNMOUNTED); + unmountdone = TRUE; + global_pu->pu_pn_root->pn_count--; + + return(OK); +} diff --git a/lib/libpuffs/null.c b/lib/libpuffs/null.c new file mode 100644 index 000000000..d1aad767c --- /dev/null +++ b/lib/libpuffs/null.c @@ -0,0 +1,589 @@ +/* $NetBSD: null.c,v 1.25 2008/08/12 19:44:39 pooka Exp $ */ + +/* + * Copyright (c) 2007 Antti Kantee. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#if !defined(lint) +__RCSID("$NetBSD: null.c,v 1.25 2008/08/12 19:44:39 pooka Exp $"); +#endif /* !lint */ + +/* + * A "nullfs" using puffs, i.e. maps one location in the hierarchy + * to another using standard system calls. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "puffs.h" + + +PUFFSOP_PROTOS(puffs_null) + +/* + * set attributes to what is specified. XXX: no rollback in case of failure + */ +static int +processvattr(const char *path, const struct vattr *va, int regular) +{ + struct utimbuf tbuf; + + /* XXX: -1 == PUFFS_VNOVAL, but shouldn't trust that */ + if (va->va_uid != (unsigned)-1 || va->va_gid != (unsigned)-1) + /* FIXME: lchown */ + if (chown(path, va->va_uid, va->va_gid) == -1) + return errno; + + if (va->va_mode != (unsigned)PUFFS_VNOVAL) + /* FIXME: lchmod */ + if (chmod(path, va->va_mode) == -1) + return errno; + + /* sloppy */ + if (va->va_atime.tv_sec != (unsigned)PUFFS_VNOVAL + || va->va_mtime.tv_sec != (unsigned)PUFFS_VNOVAL) { + /* FIXME: nsec too */ + tbuf.actime = va->va_atime.tv_sec; + tbuf.modtime = va->va_mtime.tv_sec; + + /* FIXME: lutimes */ + if (utime(path, &tbuf) == -1) + return errno; + } + + if (regular && va->va_size != (u_quad_t)PUFFS_VNOVAL) + if (truncate(path, (off_t)va->va_size) == -1) + return errno; + + return 0; +} + +/* + * Kludge to open files which aren't writable *any longer*. This kinda + * works because the vfs layer does validation checks based on the file's + * permissions to allow writable opening before opening them. However, + * the problem arises if we want to create a file, write to it (cache), + * adjust permissions and then flush the file. + */ +static int +writeableopen(const char *path) +{ + struct stat sb; + mode_t origmode; + int sverr = 0; + int fd; + + fd = open(path, O_WRONLY); + if (fd == -1) { + if (errno == EACCES) { + if (stat(path, &sb) == -1) + return -1; + origmode = sb.st_mode & ALLPERMS; + + if (chmod(path, 0200) == -1) + return -1; + + fd = open(path, O_WRONLY); + if (fd == -1) + sverr = errno; + + chmod(path, origmode); + if (sverr) + errno = sverr; + } else + return -1; + } + + return fd; +} + +/*ARGSUSED*/ +static void * +inodecmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg) +{ + ino_t *cmpino = arg; + + if (pn->pn_va.va_fileid == *cmpino) + return pn; + return NULL; +} + +static int +makenode(struct puffs_usermount *pu, struct puffs_newinfo *pni, + const struct puffs_cn *pcn, const struct vattr *va, int regular) +{ + struct puffs_node *pn; + struct stat sb; + int rv; + + if ((rv = processvattr(PCNPATH(pcn), va, regular)) != 0) + return rv; + + pn = puffs_pn_new(pu, NULL); + if (!pn) + return ENOMEM; + puffs_setvattr(&pn->pn_va, va); + + if (lstat(PCNPATH(pcn), &sb) == -1) + return errno; + puffs_stat2vattr(&pn->pn_va, &sb); + + puffs_newinfo_setcookie(pni, pn); + return 0; +} + +/* This should be called first and overriden from the file system */ +void +puffs_null_setops(struct puffs_ops *pops) +{ + + PUFFSOP_SET(pops, puffs_null, fs, statvfs); + PUFFSOP_SETFSNOP(pops, unmount); + PUFFSOP_SETFSNOP(pops, sync); + + PUFFSOP_SET(pops, puffs_null, node, lookup); + PUFFSOP_SET(pops, puffs_null, node, create); + PUFFSOP_SET(pops, puffs_null, node, mknod); + PUFFSOP_SET(pops, puffs_null, node, getattr); + PUFFSOP_SET(pops, puffs_null, node, setattr); + PUFFSOP_SET(pops, puffs_null, node, fsync); + PUFFSOP_SET(pops, puffs_null, node, remove); + PUFFSOP_SET(pops, puffs_null, node, link); + PUFFSOP_SET(pops, puffs_null, node, rename); + PUFFSOP_SET(pops, puffs_null, node, mkdir); + PUFFSOP_SET(pops, puffs_null, node, rmdir); + PUFFSOP_SET(pops, puffs_null, node, symlink); + PUFFSOP_SET(pops, puffs_null, node, readlink); + PUFFSOP_SET(pops, puffs_null, node, readdir); + PUFFSOP_SET(pops, puffs_null, node, read); + PUFFSOP_SET(pops, puffs_null, node, write); + PUFFSOP_SET(pops, puffs_genfs, node, reclaim); +} + +/*ARGSUSED*/ +int +puffs_null_fs_statvfs(struct puffs_usermount *pu, struct statvfs *svfsb) +{ + + if (statvfs(PNPATH(puffs_getroot(pu)), svfsb) == -1) + return errno; + + return 0; +} + +int +puffs_null_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc, + struct puffs_newinfo *pni, const struct puffs_cn *pcn) +{ + struct puffs_node *pn = opc, *pn_res; + struct stat sb; + int rv; + + assert(pn->pn_va.va_type == VDIR); + + /* + * Note to whoever is copypasting this: you must first check + * if the node is there and only then do nodewalk. Alternatively + * you could make sure that you don't return unlinked/rmdir'd + * nodes in some other fashion + */ + rv = lstat(PCNPATH(pcn), &sb); + if (rv) + return errno; + + /* XXX2: nodewalk is a bit too slow here */ + pn_res = puffs_pn_nodewalk(pu, inodecmp, &sb.st_ino); + + if (pn_res == NULL) { + pn_res = puffs_pn_new(pu, NULL); + if (pn_res == NULL) + return ENOMEM; + puffs_stat2vattr(&pn_res->pn_va, &sb); + } + + puffs_newinfo_setcookie(pni, pn_res); + puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type); + puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size); + puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev); + + return 0; +} + +/*ARGSUSED*/ +int +puffs_null_node_create(struct puffs_usermount *pu, puffs_cookie_t opc, + struct puffs_newinfo *pni, const struct puffs_cn *pcn, + const struct vattr *va) +{ + int fd, rv; + + fd = open(PCNPATH(pcn), O_RDWR | O_CREAT | O_TRUNC); + if (fd == -1) + return errno; + close(fd); + + rv = makenode(pu, pni, pcn, va, 1); + if (rv) + unlink(PCNPATH(pcn)); + return rv; +} + +/*ARGSUSED*/ +int +puffs_null_node_mknod(struct puffs_usermount *pu, puffs_cookie_t opc, + struct puffs_newinfo *pni, const struct puffs_cn *pcn, + const struct vattr *va) +{ + mode_t mode; + int rv; + + mode = puffs_addvtype2mode(va->va_mode, va->va_type); + if (mknod(PCNPATH(pcn), mode, va->va_rdev) == -1) + return errno; + + rv = makenode(pu, pni, pcn, va, 0); + if (rv) + unlink(PCNPATH(pcn)); + return rv; +} + +/*ARGSUSED*/ +int +puffs_null_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc, + struct vattr *va, const struct puffs_cred *pcred) +{ + struct puffs_node *pn = opc; + struct stat sb; + + if (lstat(PNPATH(pn), &sb) == -1) + return errno; + puffs_stat2vattr(va, &sb); + + return 0; +} + +/*ARGSUSED*/ +int +puffs_null_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc, + const struct vattr *va, const struct puffs_cred *pcred) +{ + struct puffs_node *pn = opc; + int rv; + + rv = processvattr(PNPATH(pn), va, pn->pn_va.va_type == VREG); + if (rv) + return rv; + + puffs_setvattr(&pn->pn_va, va); + + return 0; +} + +/*ARGSUSED*/ +int +puffs_null_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc, + const struct puffs_cred *pcred, int how, + off_t offlo, off_t offhi) +{ +/* FIXME: implement me. */ +#if 0 + struct puffs_node *pn = opc; + int fd, rv; + int fflags; + + rv = 0; + fd = writeableopen(PNPATH(pn)); + if (fd == -1) + return errno; + + if (how & PUFFS_FSYNC_DATAONLY) + fflags = FDATASYNC; + else + fflags = FFILESYNC; + if (how & PUFFS_FSYNC_CACHE) + fflags |= FDISKSYNC; + + if (fsync_range(fd, fflags, offlo, offhi - offlo) == -1) + rv = errno; + + close(fd); + + return rv; +#endif + return 0; +} + +/*ARGSUSED*/ +int +puffs_null_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, + puffs_cookie_t targ, const struct puffs_cn *pcn) +{ + struct puffs_node *pn_targ = targ; + + if (unlink(PCNPATH(pcn)) == -1) + return errno; + puffs_pn_remove(pn_targ); + + return 0; +} + +/*ARGSUSED*/ +int +puffs_null_node_link(struct puffs_usermount *pu, puffs_cookie_t opc, + puffs_cookie_t targ, const struct puffs_cn *pcn) +{ + struct puffs_node *pn_targ = targ; + + if (link(PNPATH(pn_targ), PCNPATH(pcn)) == -1) + return errno; + + return 0; +} + +/*ARGSUSED*/ +int +puffs_null_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc, + puffs_cookie_t src, const struct puffs_cn *pcn_src, + puffs_cookie_t targ_dir, puffs_cookie_t targ, + const struct puffs_cn *pcn_targ) +{ + struct puffs_node *pn_targ = targ; + + if (rename(PCNPATH(pcn_src), PCNPATH(pcn_targ)) == -1) + return errno; + + if (pn_targ) + puffs_pn_remove(pn_targ); + + return 0; +} + +/*ARGSUSED*/ +int +puffs_null_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, + struct puffs_newinfo *pni, const struct puffs_cn *pcn, + const struct vattr *va) +{ + int rv; + + if (mkdir(PCNPATH(pcn), va->va_mode) == -1) + return errno; + + rv = makenode(pu, pni, pcn, va, 0); + if (rv) + rmdir(PCNPATH(pcn)); + return rv; +} + +/*ARGSUSED*/ +int +puffs_null_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, + puffs_cookie_t targ, const struct puffs_cn *pcn) +{ + struct puffs_node *pn_targ = targ; + + if (rmdir(PNPATH(pn_targ)) == -1) + return errno; + puffs_pn_remove(pn_targ); + + return 0; +} + +/*ARGSUSED*/ +int +puffs_null_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, + struct puffs_newinfo *pni, const struct puffs_cn *pcn, + const struct vattr *va, const char *linkname) +{ + int rv; + + if (symlink(linkname, PCNPATH(pcn)) == -1) + return errno; + + rv = makenode(pu, pni, pcn, va, 0); + if (rv) + unlink(PCNPATH(pcn)); + return rv; +} + +/*ARGSUSED*/ +int +puffs_null_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, + const struct puffs_cred *pcred, char *linkname, size_t *linklen) +{ + struct puffs_node *pn = opc; + ssize_t rv; + + rv = readlink(PNPATH(pn), linkname, *linklen); + if (rv == -1) + return errno; + + *linklen = rv; + return 0; +} + +/*ARGSUSED*/ +int +puffs_null_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, + struct dirent *de, off_t *off, size_t *reslen, + const struct puffs_cred *pcred, int *eofflag, off_t *cookies, + size_t *ncookies) +{ + /* TODO: use original code since we have libc from NetBSD */ + struct puffs_node *pn = opc; + struct dirent *entry; + DIR *dp; + off_t i; + int rv; + + dp = opendir(PNPATH(pn)); + if (dp == NULL) + return errno; + + rv = 0; + i = *off; + + /* + * XXX: need to do trickery here, telldir/seekdir would be nice, but + * then we'd need to keep state, which I'm too lazy to keep + */ + while (i--) { + entry = readdir(dp); + if (!entry) { + *eofflag = 1; + goto out; + } + } + + for (;;) { + /* FIXME: DIRENT_SIZE macro? For now do calculations here */ + int namelen; + char* cp; + int dirent_size; + + entry = readdir(dp); + + if (!entry) { + *eofflag = 1; + goto out; + } + + cp = memchr(entry->d_name, '\0', NAME_MAX); + if (cp == NULL) + namelen = NAME_MAX; + else + namelen = cp - (entry->d_name); + dirent_size = _DIRENT_RECLEN(entry, namelen); + + if (dirent_size > *reslen) + goto out; + + *de = *entry; + strncpy(de->d_name, entry->d_name, namelen); + *reslen -= dirent_size; + + de = _DIRENT_NEXT(de); + (*off)++; + } + + out: + closedir(dp); + return 0; +} + +/*ARGSUSED*/ +int +puffs_null_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, + uint8_t *buf, off_t offset, size_t *buflen, + const struct puffs_cred *pcred, int ioflag) +{ + struct puffs_node *pn = opc; + ssize_t n; + off_t off; + int fd, rv; + + rv = 0; + fd = open(PNPATH(pn), O_RDONLY); + if (fd == -1) + return errno; + off = lseek(fd, offset, SEEK_SET); + if (off == -1) { + rv = errno; + goto out; + } + + n = read(fd, buf, *buflen); + if (n == -1) + rv = errno; + else + *buflen -= n; + + out: + close(fd); + return rv; +} + +/*ARGSUSED*/ +int +puffs_null_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, + uint8_t *buf, off_t offset, size_t *buflen, + const struct puffs_cred *pcred, int ioflag) +{ + struct puffs_node *pn = opc; + ssize_t n; + off_t off; + int fd, rv; + + rv = 0; + fd = writeableopen(PNPATH(pn)); + if (fd == -1) + return errno; + + off = lseek(fd, offset, SEEK_SET); + if (off == -1) { + rv = errno; + goto out; + } + + n = write(fd, buf, *buflen); + if (n == -1) + rv = errno; + else + *buflen -= n; + + out: + close(fd); + return rv; +} diff --git a/lib/libpuffs/open.c b/lib/libpuffs/open.c new file mode 100644 index 000000000..da3e1f059 --- /dev/null +++ b/lib/libpuffs/open.c @@ -0,0 +1,378 @@ +/* Created (MFS based): + * June 2011 (Evgeniy Ivanov) + */ + +#include "fs.h" +#include +#include +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + + +/*===========================================================================* + * fs_create * + *===========================================================================*/ +PUBLIC int fs_create() +{ + int r; + struct puffs_node *pn_dir; + struct puffs_node *pn; + mode_t omode; + struct puffs_newinfo pni; + struct puffs_kcn pkcnp; + PUFFS_MAKECRED(pcr, &global_kcred); + struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) pcr, {0}}; + struct vattr va; + time_t cur_time; + int len; + + if (global_pu->pu_ops.puffs_node_create == NULL) { + lpuffs_debug("No puffs_node_create"); + return(ENFILE); + } + + /* Read request message */ + omode = (mode_t) fs_m_in.REQ_MODE; + caller_uid = (uid_t) fs_m_in.REQ_UID; + caller_gid = (gid_t) fs_m_in.REQ_GID; + + /* Copy the last component (i.e., file name) */ + len = fs_m_in.REQ_PATH_LEN; + pcn.pcn_namelen = len - 1; + if (pcn.pcn_namelen > NAME_MAX) + return(ENAMETOOLONG); + + err_code = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT, + (vir_bytes) 0, (vir_bytes) pcn.pcn_name, + (size_t) len, D); + if (err_code != OK) return(err_code); + NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name)); + + /* Get last directory pnode (i.e., directory that will hold the new pnode) */ + if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(ENOENT); + + memset(&pni, 0, sizeof(pni)); + pni.pni_cookie = (void** )&pn; + + cur_time = clock_time(); + + memset(&va, 0, sizeof(va)); + va.va_type = VREG; + va.va_mode = omode; + va.va_uid = caller_uid; + va.va_gid = caller_gid; + va.va_atime.tv_sec = va.va_mtime.tv_sec = va.va_ctime.tv_sec = cur_time; + + if (buildpath) { + r = puffs_path_pcnbuild(global_pu, &pcn, pn_dir); + if (r) { + lpuffs_debug("pathbuild error\n"); + return(ENOENT); + } + } + + r = global_pu->pu_ops.puffs_node_create(global_pu, pn_dir, &pni, &pcn, &va); + if (buildpath) { + if (r) { + global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full); + } else { + struct puffs_node *_pn; + + _pn = PU_CMAP(global_pu, pn); + _pn->pn_po = pcn.pcn_po_full; + } + } + + if (r != OK) { + if (r > 0) r = -r; + return(r); + } + + /* Open pnode */ + pn->pn_count++; + + update_times(pn_dir, MTIME | CTIME, cur_time); + + /* Reply message */ + fs_m_out.RES_INODE_NR = pn->pn_va.va_fileid; + fs_m_out.RES_MODE = pn->pn_va.va_mode; + fs_m_out.RES_FILE_SIZE_LO = pn->pn_va.va_size; + + /* This values are needed for the execution */ + fs_m_out.RES_UID = pn->pn_va.va_uid; + fs_m_out.RES_GID = pn->pn_va.va_gid; + + return(OK); +} + + +/*===========================================================================* + * fs_mknod * + *===========================================================================*/ +PUBLIC int fs_mknod() +{ + int r; + struct puffs_node *pn_dir; + struct puffs_node *pn; + struct puffs_newinfo pni; + struct puffs_kcn pkcnp; + PUFFS_MAKECRED(pcr, &global_kcred); + struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) pcr, {0}}; + struct vattr va; + time_t cur_time; + int len; + + if (global_pu->pu_ops.puffs_node_mknod == NULL) { + lpuffs_debug("No puffs_node_mknod"); + return(ENFILE); + } + + /* Copy the last component and set up caller's user and group id */ + len = fs_m_in.REQ_PATH_LEN; + pcn.pcn_namelen = len - 1; + if (pcn.pcn_namelen > NAME_MAX) + return(ENAMETOOLONG); + + err_code = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT, + (vir_bytes) 0, (vir_bytes) pcn.pcn_name, + (size_t) len, D); + if (err_code != OK) return(err_code); + NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name)); + + caller_uid = (uid_t) fs_m_in.REQ_UID; + caller_gid = (gid_t) fs_m_in.REQ_GID; + + /* Get last directory pnode */ + if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(ENOENT); + + memset(&pni, 0, sizeof(pni)); + pni.pni_cookie = (void** )&pn; + + cur_time = clock_time(); + + memset(&va, 0, sizeof(va)); + va.va_type = VDIR; + va.va_mode = (mode_t) fs_m_in.REQ_MODE; + va.va_uid = caller_uid; + va.va_gid = caller_gid; + va.va_rdev = (dev_t) fs_m_in.REQ_DEV; + va.va_atime.tv_sec = va.va_mtime.tv_sec = va.va_ctime.tv_sec = cur_time; + + if (buildpath) { + if (puffs_path_pcnbuild(global_pu, &pcn, pn_dir) != 0) { + lpuffs_debug("pathbuild error\n"); + return(ENOENT); + } + } + + r = global_pu->pu_ops.puffs_node_mknod(global_pu, pn_dir, &pni, &pcn, &va); + if (buildpath) { + if (r) { + global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full); + } else { + struct puffs_node *_pn; + + _pn = PU_CMAP(global_pu, pn); + _pn->pn_po = pcn.pcn_po_full; + } + } + + if (r != OK) { + if (r > 0) r = -r; + return(r); + } + + update_times(pn_dir, MTIME | CTIME, cur_time); + + return(OK); +} + + +/*===========================================================================* + * fs_mkdir * + *===========================================================================*/ +PUBLIC int fs_mkdir() +{ + int r; + struct puffs_node *pn_dir; + struct puffs_node *pn; + struct puffs_newinfo pni; + struct puffs_kcn pkcnp; + PUFFS_MAKECRED(pcr, &global_kcred); + struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) pcr, {0}}; + struct vattr va; + time_t cur_time; + int len; + + if (global_pu->pu_ops.puffs_node_mkdir == NULL) { + lpuffs_debug("No puffs_node_mkdir"); + return(ENFILE); + } + + /* Copy the last component and set up caller's user and group id */ + len = fs_m_in.REQ_PATH_LEN; + pcn.pcn_namelen = len - 1; + if (pcn.pcn_namelen > NAME_MAX) + return(ENAMETOOLONG); + + err_code = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT, + (vir_bytes) 0, (vir_bytes) pcn.pcn_name, + (phys_bytes) len, D); + if (err_code != OK) return(err_code); + NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name)); + + caller_uid = (uid_t) fs_m_in.REQ_UID; + caller_gid = (gid_t) fs_m_in.REQ_GID; + + /* Get last directory pnode */ + if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(ENOENT); + + cur_time = clock_time(); + + memset(&pni, 0, sizeof(pni)); + pni.pni_cookie = (void** )&pn; + + memset(&va, 0, sizeof(va)); + va.va_type = VDIR; + va.va_mode = (mode_t) fs_m_in.REQ_MODE; + va.va_uid = caller_uid; + va.va_gid = caller_gid; + va.va_atime.tv_sec = va.va_mtime.tv_sec = va.va_ctime.tv_sec = cur_time; + + if (buildpath) { + r = puffs_path_pcnbuild(global_pu, &pcn, pn_dir); + if (r) { + lpuffs_debug("pathbuild error\n"); + return(ENOENT); + } + } + + r = global_pu->pu_ops.puffs_node_mkdir(global_pu, pn_dir, &pni, &pcn, &va); + if (buildpath) { + if (r) { + global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full); + } else { + struct puffs_node *_pn; + + _pn = PU_CMAP(global_pu, pn); + _pn->pn_po = pcn.pcn_po_full; + } + } + + if (r != OK) { + if (r > 0) r = -r; + return(r); + } + + update_times(pn_dir, MTIME | CTIME, cur_time); + + return(OK); +} + + +/*===========================================================================* + * fs_slink * + *===========================================================================*/ +PUBLIC int fs_slink() +{ + int r; + struct pnode *pn; /* pnode containing symbolic link */ + struct pnode *pn_dir; /* directory containing link */ + char target[PATH_MAX + 1]; /* target path */ + struct puffs_newinfo pni; + struct puffs_kcn pkcnp; + PUFFS_MAKECRED(pcr, &global_kcred); + struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) pcr, {0}}; + struct vattr va; + int len; + + caller_uid = (uid_t) fs_m_in.REQ_UID; + caller_gid = (gid_t) fs_m_in.REQ_GID; + + /* Copy the link name's last component */ + len = fs_m_in.REQ_PATH_LEN; + pcn.pcn_namelen = len - 1; + if (pcn.pcn_namelen > NAME_MAX) + return(ENAMETOOLONG); + + if (fs_m_in.REQ_MEM_SIZE >= PATH_MAX) + return(ENAMETOOLONG); + + r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT, + (vir_bytes) 0, (vir_bytes) pcn.pcn_name, + (size_t) len, D); + if (r != OK) return(r); + NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name)); + + /* Copy the target path (note that it's not null terminated) */ + r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT3, + (vir_bytes) 0, (vir_bytes) target, + (size_t) fs_m_in.REQ_MEM_SIZE, D); + if (r != OK) return(r); + target[fs_m_in.REQ_MEM_SIZE] = '\0'; + + if (strlen(target) != fs_m_in.REQ_MEM_SIZE) { + /* This can happen if the user provides a buffer + * with a \0 in it. This can cause a lot of trouble + * when the symlink is used later. We could just use + * the strlen() value, but we want to let the user + * know he did something wrong. ENAMETOOLONG doesn't + * exactly describe the error, but there is no + * ENAMETOOWRONG. + */ + return(ENAMETOOLONG); + } + + if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(EINVAL); + + memset(&pni, 0, sizeof(pni)); + pni.pni_cookie = (void** )&pn; + + memset(&va, 0, sizeof(va)); + va.va_type = VLNK; + va.va_mode = (mode_t) (I_SYMBOLIC_LINK | RWX_MODES); + va.va_uid = caller_uid; + va.va_gid = caller_gid; + va.va_atime.tv_sec = va.va_mtime.tv_sec = va.va_ctime.tv_sec = clock_time(); + + if (buildpath) { + r = puffs_path_pcnbuild(global_pu, &pcn, pn_dir); + if (r) { + lpuffs_debug("pathbuild error\n"); + return(ENOENT); + } + } + + r = global_pu->pu_ops.puffs_node_symlink(global_pu, pn_dir, &pni, &pcn, &va, target); + if (buildpath) { + if (r) { + global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full); + } else { + struct puffs_node *_pn; + + _pn = PU_CMAP(global_pu, pn); + _pn->pn_po = pcn.pcn_po_full; + } + } + + if (r > 0) r = -r; + + return(r); +} + + +/*===========================================================================* + * fs_inhibread * + *===========================================================================*/ +PUBLIC int fs_inhibread() +{ + return(OK); +} diff --git a/lib/libpuffs/path.c b/lib/libpuffs/path.c new file mode 100644 index 000000000..211afbfad --- /dev/null +++ b/lib/libpuffs/path.c @@ -0,0 +1,550 @@ +/* + * This file contains the procedures that look up path names in the directory + * system and determine the pnode number that goes with a given path name. + * + * Created (based on MFS): + * June 2011 (Evgeniy Ivanov) + */ + +#include "fs.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + +PUBLIC char dot2[3] = ".."; /* permissions for . and .. */ + +FORWARD _PROTOTYPE( char *get_name, (char *name, char string[NAME_MAX+1]) ); +FORWARD _PROTOTYPE( int ltraverse, (struct puffs_node *pn, char *suffix) ); +FORWARD _PROTOTYPE( int parse_path, (ino_t dir_ino, ino_t root_ino, + int flags, struct puffs_node **res_inop, + size_t *offsetp, int *symlinkp) ); + +/*===========================================================================* + * fs_lookup * + *===========================================================================*/ +PUBLIC int fs_lookup() +{ + cp_grant_id_t grant; + int r, r1, flags, symlinks; + unsigned int len; + size_t offset = 0, path_size; + ino_t dir_ino, root_ino; + struct puffs_node *pn; + + grant = (cp_grant_id_t) fs_m_in.REQ_GRANT; + path_size = (size_t) fs_m_in.REQ_PATH_SIZE; /* Size of the buffer */ + len = (int) fs_m_in.REQ_PATH_LEN; /* including terminating nul */ + dir_ino = (ino_t) fs_m_in.REQ_DIR_INO; + root_ino = (ino_t) fs_m_in.REQ_ROOT_INO; + flags = (int) fs_m_in.REQ_FLAGS; + + /* Check length. */ + if (len > sizeof(user_path)) return(E2BIG); /* too big for buffer */ + if (len == 0) return(EINVAL); /* too small */ + + /* Copy the pathname and set up caller's user and group id */ + r = sys_safecopyfrom(VFS_PROC_NR, grant, /*offset*/ 0, + (vir_bytes) user_path, (size_t) len, D); + if (r != OK) return(r); + + /* Verify this is a null-terminated path. */ + if (user_path[len - 1] != '\0') return(EINVAL); + + memset(&credentials, 0, sizeof(credentials)); + if(!(flags & PATH_GET_UCRED)) { /* Do we have to copy uid/gid credentials? */ + caller_uid = (uid_t) fs_m_in.REQ_UID; + caller_gid = (gid_t) fs_m_in.REQ_GID; + } else { + if((r=fs_lookup_credentials(&credentials, + &caller_uid, &caller_gid, + (cp_grant_id_t) fs_m_in.REQ_GRANT2, + (size_t) fs_m_in.REQ_UCRED_SIZE)) != OK) + return r; + } + + + /* Lookup pnode */ + pn = NULL; + r = parse_path(dir_ino, root_ino, flags, &pn, &offset, &symlinks); + + if (symlinks != 0 && (r == ELEAVEMOUNT || r == EENTERMOUNT || r == ESYMLINK)){ + len = strlen(user_path)+1; + if (len > path_size) return(ENAMETOOLONG); + + r1 = sys_safecopyto(VFS_PROC_NR, grant, (vir_bytes) 0, + (vir_bytes) user_path, (size_t) len, D); + if (r1 != OK) return(r1); + } + + if (r == ELEAVEMOUNT || r == ESYMLINK) { + /* Report offset and the error */ + fs_m_out.RES_OFFSET = offset; + fs_m_out.RES_SYMLOOP = symlinks; + + return(r); + } + + if (r != OK && r != EENTERMOUNT) { + return(r); + } + + if (r == OK) { + /* Open pnode */ + pn->pn_count++; + } + + fs_m_out.RES_INODE_NR = pn->pn_va.va_fileid; + fs_m_out.RES_MODE = pn->pn_va.va_mode; + fs_m_out.RES_FILE_SIZE_LO = pn->pn_va.va_size; + fs_m_out.RES_SYMLOOP = symlinks; + fs_m_out.RES_UID = pn->pn_va.va_uid; + fs_m_out.RES_GID = pn->pn_va.va_gid; + + /* This is only valid for block and character specials. But it doesn't + * cause any harm to set RES_DEV always. */ + fs_m_out.RES_DEV = pn->pn_va.va_rdev; + + if (r == EENTERMOUNT) { + fs_m_out.RES_OFFSET = offset; + } + + return(r); +} + + +/*===========================================================================* + * parse_path * + *===========================================================================*/ +PRIVATE int parse_path(dir_ino, root_ino, flags, res_inop, offsetp, symlinkp) +ino_t dir_ino; +ino_t root_ino; +int flags; +struct puffs_node **res_inop; +size_t *offsetp; +int *symlinkp; +{ + /* Parse the path in user_path, starting at dir_ino. If the path is the empty + * string, just return dir_ino. It is upto the caller to treat an empty + * path in a special way. Otherwise, if the path consists of just one or + * more slash ('/') characters, the path is replaced with ".". Otherwise, + * just look up the first (or only) component in path after skipping any + * leading slashes. + */ + int r, leaving_mount; + struct puffs_node *pn, *pn_dir; + char *cp, *next_cp; /* component and next component */ + char component[NAME_MAX+1]; + + /* Start parsing path at the first component in user_path */ + cp = user_path; + + /* No symlinks encountered yet */ + *symlinkp = 0; + + /* Find starting pnode according to the request message */ + /* XXX it's deffinitely OK to use nodewalk here */ + if ((pn = puffs_pn_nodewalk(global_pu, 0, &dir_ino)) == NULL) { + lpuffs_debug("nodewalk failed\n"); + return(ENOENT); + } + + /* If dir has been removed return ENOENT. */ + if (pn->pn_va.va_nlink == NO_LINK) return(ENOENT); + + /* If the given start pnode is a mountpoint, we must be here because the file + * system mounted on top returned an ELEAVEMOUNT error. In this case, we must + * only accept ".." as the first path component. + */ + leaving_mount = pn->pn_mountpoint; /* True iff pn is a mountpoint */ + + /* Scan the path component by component. */ + while (TRUE) { + if (cp[0] == '\0') { + /* We're done; either the path was empty or we've parsed all + components of the path */ + + *res_inop = pn; + *offsetp += cp - user_path; + + /* Return EENTERMOUNT if we are at a mount point */ + if (pn->pn_mountpoint) return(EENTERMOUNT); + + return(OK); + } + + while(cp[0] == '/') cp++; + next_cp = get_name(cp, component); + if (next_cp == NULL) { + return(err_code); + } + + /* Special code for '..'. A process is not allowed to leave a chrooted + * environment. A lookup of '..' at the root of a mounted filesystem + * has to return ELEAVEMOUNT. In both cases, the caller needs search + * permission for the current pnode, as it is used as directory. + */ + if (strcmp(component, "..") == 0) { + /* 'pn' is now accessed as directory */ + if ((r = forbidden(pn, X_BIT)) != OK) { + return(r); + } + + if (pn->pn_va.va_fileid == root_ino) { + cp = next_cp; + continue; /* Ignore the '..' at a process' root + and move on to the next component */ + } + + if (pn->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid + && !is_root_fs) { + /* Climbing up to parent FS */ + *offsetp += cp - user_path; + return(ELEAVEMOUNT); + } + } + + /* Only check for a mount point if we are not coming from one. */ + if (!leaving_mount && pn->pn_mountpoint) { + /* Going to enter a child FS */ + + *res_inop = pn; + *offsetp += cp - user_path; + return(EENTERMOUNT); + } + + /* There is more path. Keep parsing. + * If we're leaving a mountpoint, skip directory permission checks. + */ + pn_dir = pn; + if ((pn_dir->pn_va.va_mode & I_TYPE) != I_DIRECTORY) { + return(ENOTDIR); + } + pn = advance(pn_dir, leaving_mount ? dot2 : component, CHK_PERM); + if (err_code == ELEAVEMOUNT || err_code == EENTERMOUNT) + err_code = OK; + + if (err_code != OK) { + return(err_code); + } + + assert(pn != NULL); + + leaving_mount = 0; + + /* The call to advance() succeeded. Fetch next component. */ + if (S_ISLNK(pn->pn_va.va_mode)) { + if (next_cp[0] == '\0' && (flags & PATH_RET_SYMLINK)) { + *res_inop = pn; + *offsetp += next_cp - user_path; + return(OK); + } + + /* Extract path name from the symlink file */ + r = ltraverse(pn, next_cp); + next_cp = user_path; + *offsetp = 0; + + /* Symloop limit reached? */ + if (++(*symlinkp) > SYMLOOP_MAX) + r = ELOOP; + + if (r != OK) + return(r); + + if (next_cp[0] == '/') + return(ESYMLINK); + + pn = pn_dir; + } + + cp = next_cp; /* Process subsequent component in next round */ + } + +} + + +/*===========================================================================* + * ltraverse * + *===========================================================================*/ +PRIVATE int ltraverse(pn, suffix) +register struct puffs_node *pn;/* symbolic link */ +char *suffix; /* current remaining path. Has to point in the + * user_path buffer + */ +{ +/* Traverse a symbolic link. Copy the link text from the pnode and insert + * the text into the path. Return error code or report success. Base + * directory has to be determined according to the first character of the + * new pathname. + */ + int r; + char sp[PATH_MAX]; + size_t llen = PATH_MAX;/* length of link */ + size_t slen; /* length of suffix */ + PUFFS_MAKECRED(pcr, &global_kcred); + + + if (!S_ISLNK(pn->pn_va.va_mode)) + r = EACCES; + + if (global_pu->pu_ops.puffs_node_readlink == NULL) + return(EINVAL); + + if (global_pu->pu_ops.puffs_node_readlink(global_pu, pn, pcr, sp, &llen) != 0) + return(EINVAL); + + slen = strlen(suffix); + + /* The path we're parsing looks like this: + * /already/processed/path/ or + * /already/processed/path//not/yet/processed/path + * After expanding the , the path will look like + * or + * /not/yet/processed + * In both cases user_path must have enough room to hold . + * However, in the latter case we have to move /not/yet/processed to the + * right place first, before we expand . When strlen() is + * smaller than strlen(/already/processes/path), we move the suffix to the + * left. Is strlen() greater then we move it to the right. Else + * we do nothing. + */ + + if (slen > 0) { /* Do we have path after the link? */ + /* For simplicity we require that suffix starts with a slash */ + if (suffix[0] != '/') { + panic("ltraverse: suffix does not start with a slash"); + } + + /* To be able to expand the , we have to move the 'suffix' + * to the right place. + */ + if (slen + llen + 1 > sizeof(user_path)) + return(ENAMETOOLONG);/* +suffix+\0 does not fit*/ + if ((unsigned)(suffix - user_path) != llen) { + /* Move suffix left or right if needed */ + memmove(&user_path[llen], suffix, slen+1); + } + } else { + if (llen + 1 > sizeof(user_path)) + return(ENAMETOOLONG); /* + \0 does not fit */ + + /* Set terminating nul */ + user_path[llen]= '\0'; + } + + /* Everything is set, now copy the expanded link to user_path */ + memmove(user_path, sp, llen); + + return(OK); +} + + +/*===========================================================================* + * advance * + *===========================================================================*/ +PUBLIC struct puffs_node *advance(pn_dir, string, chk_perm) +struct puffs_node *pn_dir; /* pnode for directory to be searched */ +char string[NAME_MAX + 1]; /* component name to look for */ +int chk_perm; /* check permissions when string is looked up*/ +{ +/* Given a directory and a component of a path, look up the component in + * the directory, find the pnode, open it, and return a pointer to its pnode + * slot. + * TODO: instead of string, should get pcn. + */ + struct puffs_node *pn; + + struct puffs_newinfo pni; + + struct puffs_kcn pkcnp; + PUFFS_MAKECRED(pcr, &global_kcred); + struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) pcr, {0}}; + + enum vtype node_vtype; + voff_t size; + dev_t rdev; + int error; + + err_code = OK; + + /* If 'string' is empty, return an error. */ + if (string[0] == '\0') { + err_code = ENOENT; + return(NULL); + } + + /* Check for NULL. */ + if (pn_dir == NULL) + return(NULL); + + if (chk_perm) { + /* Just search permission is checked */ + if (forbidden(pn_dir, X_BIT)) { + err_code = EACCES; + return(NULL); + } + } + + if (strcmp(string, ".") == 0) { + /* Otherwise we will fall into trouble: path for pnode to be looked up + * will be parent path (same pnode as the one to be looked up) + + * requested path. E.g. after several lookups we might get advance + * for "." with parent path "/././././././././.". + * + * Another problem is that after lookup pnode will be added + * to the pu_pnodelst, which already contains pnode instance for this + * pnode. It will cause lot of troubles. + */ + return pn_dir; + } + + pni.pni_cookie = (void** )&pn; + pni.pni_vtype = &node_vtype; + pni.pni_size = &size; + pni.pni_rdev = &rdev; + + pcn.pcn_namelen = strlen(string); + assert(pcn.pcn_namelen <= MAXPATHLEN); + strcpy(pcn.pcn_name, string); + + if (buildpath) { + if (puffs_path_pcnbuild(global_pu, &pcn, pn_dir) != 0) { + lpuffs_debug("pathbuild error\n"); + err_code = ENOENT; + return(NULL); + } + } + + /* lookup *must* be present */ + error = global_pu->pu_ops.puffs_node_lookup(global_pu, pn_dir, + &pni, &pcn); + + if (buildpath) { + if (error) { + global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full); + err_code = ENOENT; + return(NULL); + } else { + struct puffs_node *_pn; + + /* + * did we get a new node or a + * recycled node? + */ + _pn = PU_CMAP(global_pu, pn); + if (_pn->pn_po.po_path == NULL) + _pn->pn_po = pcn.pcn_po_full; + else + global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full); + } + } + + if (error) { + err_code = error < 0 ? error : -error; + return(NULL); + } else { + /* In MFS/ext2 it's set by search_dir, puffs_node_lookup error codes are unclear, + * so we use error variable there + */ + err_code = OK; + } + + assert(pn != NULL); + + /* The following test is for "mountpoint/.." where mountpoint is a + * mountpoint. ".." will refer to the root of the mounted filesystem, + * but has to become a reference to the parent of the 'mountpoint' + * directory. + * + * This case is recognized by the looked up name pointing to a + * root pnode, and the directory in which it is held being a + * root pnode, _and_ the name[1] being '.'. (This is a test for '..' + * and excludes '.'.) + */ + if (pn->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid) { + if (pn_dir->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid) { + if (string[1] == '.') { + if (!is_root_fs) { + /* Climbing up mountpoint */ + err_code = ELEAVEMOUNT; + } + } + } + } + + /* See if the pnode is mounted on. If so, switch to root directory of the + * mounted file system. The super_block provides the linkage between the + * pnode mounted on and the root directory of the mounted file system. + */ + if (pn->pn_mountpoint) { + /* Mountpoint encountered, report it */ + err_code = EENTERMOUNT; + } + + return(pn); +} + + +/*===========================================================================* + * get_name * + *===========================================================================*/ +PRIVATE char *get_name(path_name, string) +char *path_name; /* path name to parse */ +char string[NAME_MAX+1]; /* component extracted from 'old_name' */ +{ +/* Given a pointer to a path name in fs space, 'path_name', copy the first + * component to 'string' (truncated if necessary, always nul terminated). + * A pointer to the string after the first component of the name as yet + * unparsed is returned. Roughly speaking, + * 'get_name' = 'path_name' - 'string'. + * + * This routine follows the standard convention that /usr/ast, /usr//ast, + * //usr///ast and /usr/ast/ are all equivalent. + * + * If len of component is greater, than allowed, then return 0. + */ + size_t len; + char *cp, *ep; + + cp = path_name; + + /* Skip leading slashes */ + while (cp[0] == '/') cp++; + + /* Find the end of the first component */ + ep = cp; + while (ep[0] != '\0' && ep[0] != '/') + ep++; + + len = (size_t) (ep - cp); + + /* XXX we don't check name_max of fileserver (probably we can't) */ + if (len > NAME_MAX) { + err_code = ENAMETOOLONG; + return(NULL); + } + + /* Special case of the string at cp is empty */ + if (len == 0) + strcpy(string, "."); /* Return "." */ + else { + memcpy(string, cp, len); + string[len]= '\0'; + } + + return(ep); +} diff --git a/lib/libpuffs/path_puffs.c b/lib/libpuffs/path_puffs.c new file mode 100644 index 000000000..027f1f815 --- /dev/null +++ b/lib/libpuffs/path_puffs.c @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2007 Antti Kantee. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + + +/* + * Generic routines for pathbuilding code + */ + +int +puffs_path_pcnbuild(struct puffs_usermount *pu, struct puffs_cn *pcn, + puffs_cookie_t parent) +{ + struct puffs_node *pn_parent = PU_CMAP(pu, parent); + struct puffs_cn pcn_orig; + struct puffs_pathobj po; + int rv; + + assert(pn_parent->pn_po.po_path != NULL); + assert(pu->pu_flags & PUFFS_FLAG_BUILDPATH); + + if (pu->pu_pathtransform) { + rv = pu->pu_pathtransform(pu, &pn_parent->pn_po, pcn, &po); + if (rv) + return rv; + } else { + po.po_path = pcn->pcn_name; + po.po_len = pcn->pcn_namelen; + } + + if (pu->pu_namemod) { + /* XXX: gcc complains if I do assignment */ + memcpy(&pcn_orig, pcn, sizeof(pcn_orig)); + rv = pu->pu_namemod(pu, &pn_parent->pn_po, pcn); + if (rv) + return rv; + } + + rv = pu->pu_pathbuild(pu, &pn_parent->pn_po, &po, 0, + &pcn->pcn_po_full); + puffs_path_buildhash(pu, &pcn->pcn_po_full); + + if (pu->pu_pathtransform) + pu->pu_pathfree(pu, &po); + + if (pu->pu_namemod && rv) + *pcn = pcn_orig; + + return rv; +} + +/* + * substitute all (child) patch prefixes. called from nodewalk, which + * in turn is called from rename + */ +void * +puffs_path_prefixadj(struct puffs_usermount *pu, struct puffs_node *pn, + void *arg) +{ + struct puffs_pathinfo *pi = arg; + struct puffs_pathobj localpo; + struct puffs_pathobj oldpo; + int rv; + + /* can't be a path prefix */ + if (pn->pn_po.po_len < pi->pi_old->po_len) + return NULL; + + if (pu->pu_pathcmp(pu, &pn->pn_po, pi->pi_old, pi->pi_old->po_len, 1)) + return NULL; + + /* otherwise we'd have two nodes with an equal path */ + assert(pn->pn_po.po_len > pi->pi_old->po_len); + + /* found a matching prefix */ + rv = pu->pu_pathbuild(pu, pi->pi_new, &pn->pn_po, + pi->pi_old->po_len, &localpo); + /* + * XXX: technically we shouldn't fail, but this is the only + * sensible thing to do here. If the buildpath routine fails, + * we will have paths in an inconsistent state. Should fix this, + * either by having two separate passes or by doing other tricks + * to make an invalid path with BUILDPATHS acceptable. + */ + if (rv != 0) + abort(); + + /* adjust hash sum */ + puffs_path_buildhash(pu, &localpo); + + /* out with the old and in with the new */ + oldpo = pn->pn_po; + pn->pn_po = localpo; + pu->pu_pathfree(pu, &oldpo); + + /* continue the walk */ + return NULL; +} + +/* + * called from nodewalk, checks for exact match + */ +void * +puffs_path_walkcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg) +{ + struct puffs_pathobj *po = arg; + struct puffs_pathobj po2; + + if (po->po_len != PNPLEN(pn)) + return NULL; + + /* + * If hashing and the hash doesn't match, we know this is + * definitely not a match. Otherwise check for collisions. + */ + if (pu->pu_flags & PUFFS_FLAG_HASHPATH) + if (pn->pn_po.po_hash != po->po_hash) + return NULL; + + po2.po_path = PNPATH(pn); + po2.po_len = PNPLEN(pn); + + if (pu->pu_pathcmp(pu, po, &po2, PNPLEN(pn), 0) == 0) + return pn; + return NULL; +} + +/* + * Hash sum building routine. Use string hash if the buildpath routine + * is the standard one, otherwise use binary hashes. A bit whimsical + * way to choose the routine, but the binary works for strings also, + * so don't sweat it. + */ +void +puffs_path_buildhash(struct puffs_usermount *pu, struct puffs_pathobj *po) +{ + + if ((pu->pu_flags & PUFFS_FLAG_HASHPATH) == 0) + return; + + if (pu->pu_pathbuild == puffs_stdpath_buildpath) + po->po_hash = hash32_strn(po->po_path, po->po_len, + HASH32_STR_INIT); + else + po->po_hash = hash32_buf(po->po_path, po->po_len, + HASH32_BUF_INIT); +} + +/* + * Routines provided to file systems which consider a path a tuple of + * strings and / the component separator. + */ + +/*ARGSUSED*/ +int +puffs_stdpath_cmppath(struct puffs_usermount *pu, struct puffs_pathobj *c1, + struct puffs_pathobj *c2, size_t clen, int checkprefix) +{ + char *p; + int rv; + + rv = strncmp(c1->po_path, c2->po_path, clen); + if (rv) + return 1; + + if (checkprefix == 0) + return 0; + + /* sanity for next step */ + if (!(c1->po_len > c2->po_len)) + return 1; + + /* check if it's really a complete path prefix */ + p = c1->po_path; + if ((*(p + clen)) != '/') + return 1; + + return 0; +} + +/*ARGSUSED*/ +int +puffs_stdpath_buildpath(struct puffs_usermount *pu, + const struct puffs_pathobj *po_pre, const struct puffs_pathobj *po_comp, + size_t offset, struct puffs_pathobj *newpath) +{ + char *path, *pcomp; + size_t plen, complen; + size_t prelen; + int isdotdot; + + complen = po_comp->po_len - offset; + + /* seek to correct place & remove all leading '/' from component */ + pcomp = po_comp->po_path; + pcomp += offset; + while (*pcomp == '/') { + pcomp++; + complen--; + } + + /* todotdot or nottodotdot */ + if (complen == 2 && strcmp(pcomp, "..") == 0) + isdotdot = 1; + else + isdotdot = 0; + + /* + * Strip trailing components from the preceending component. + * This is an issue only for the root node, which we might want + * to be at path "/" for some file systems. + */ + prelen = po_pre->po_len; + while (prelen > 0 && *((char *)po_pre->po_path + (prelen-1)) == '/') { + assert(isdotdot == 0); + prelen--; + } + + if (isdotdot) { + char *slash; /* sweet char of mine */ + + slash = strrchr(po_pre->po_path, '/'); + assert(slash != NULL); + + plen = slash - (char *)po_pre->po_path; + + /* + * As the converse to not stripping the initial "/" above, + * don't nuke it here either. + */ + if (plen == 0) + plen++; + + path = malloc(plen + 1); + if (path == NULL) + return errno; + + strlcpy(path, po_pre->po_path, plen+1); + } else { + /* + '/' + '\0' */ + plen = prelen + 1 + complen; + path = malloc(plen + 1); + if (path == NULL) + return errno; + + strlcpy(path, po_pre->po_path, prelen+1); + strcat(path, "/"); + strncat(path, pcomp, complen); + } + + newpath->po_path = path; + newpath->po_len = plen; + + return 0; +} + +/*ARGSUSED*/ +void +puffs_stdpath_freepath(struct puffs_usermount *pu, struct puffs_pathobj *po) +{ + + free(po->po_path); +} diff --git a/lib/libpuffs/pnode.c b/lib/libpuffs/pnode.c new file mode 100644 index 000000000..b0e665f9c --- /dev/null +++ b/lib/libpuffs/pnode.c @@ -0,0 +1,221 @@ +/* $NetBSD: pnode.c,v 1.10 2008/08/12 19:44:39 pooka Exp $ */ + +/* + * Copyright (c) 2006 Antti Kantee. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#if !defined(lint) +__RCSID("$NetBSD: pnode.c,v 1.10 2008/08/12 19:44:39 pooka Exp $"); +#endif /* !lint */ + +#include + +#include +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + +/* + * Well, you're probably wondering why this isn't optimized. + * The reason is simple: my available time is not optimized for + * size ... so please be patient ;) + */ +struct puffs_node * +puffs_pn_new(struct puffs_usermount *pu, void *privdata) +{ + struct puffs_node *pn; + + pn = calloc(1, sizeof(struct puffs_node)); + if (pn == NULL) + return NULL; + + pn->pn_data = privdata; + pn->pn_mnt = pu; + puffs_vattr_null(&pn->pn_va); + + LIST_INSERT_HEAD(&pu->pu_pnodelst, pn, pn_entries); + + return pn; +} + +void +puffs_pn_remove(struct puffs_node *pn) +{ + struct puffs_usermount *pu = pn->pn_mnt; + assert(pu != NULL); + + LIST_REMOVE(pn, pn_entries); + pn->pn_flags |= PUFFS_NODE_REMOVED; + if (pn->pn_count != 0) { + /* XXX FS removes this pn from the list to prevent further + * lookups from finding node after remove/rm/rename op. + * But VFS still uses it, i.e. pnode is still open, and + * will put it later. Keep it in separate list to do reclaim + * in fs_put(). + */ + LIST_INSERT_HEAD(&pu->pu_pnode_removed_lst, pn, pn_entries); + } +} + +void +puffs_pn_put(struct puffs_node *pn) +{ + struct puffs_usermount *pu = pn->pn_mnt; + + pu->pu_pathfree(pu, &pn->pn_po); + /* Removes either from pu_pnodelst or pu_pnode_removed_lst */ + LIST_REMOVE(pn, pn_entries); + free(pn); +} + +/* walk list, rv can be used either to halt or to return a value + * XXX (MINIX note): if fn is 0, then arg is ino_t and we search + * node with ino_t. TODO: modify docs. + */ +void * +puffs_pn_nodewalk(struct puffs_usermount *pu, puffs_nodewalk_fn fn, void *arg) +{ + struct puffs_node *pn_cur, *pn_next; + void *rv; + + pn_cur = LIST_FIRST(&pu->pu_pnodelst); + while (pn_cur) { + pn_next = LIST_NEXT(pn_cur, pn_entries); + if (fn) { + rv = fn(pu, pn_cur, arg); + if (rv) + return rv; + } else { + if (pn_cur->pn_va.va_fileid == *((ino_t*) arg)) + return pn_cur; + } + pn_cur = pn_next; + } + + return NULL; +} + +void* +puffs_pn_nodeprint(struct puffs_usermount *pu, struct puffs_node *pn, void *arg) +{ + /* If arg is specified, print only pnodes with inum (should be only one, + * otherwise - all. + */ + if (arg != NULL) { + ino_t inum = *(ino_t*)arg; + if (pn->pn_va.va_fileid != inum) { + return NULL; + } + } + lpuffs_debug(" ino %ld used %d %s\n", pn->pn_va.va_fileid, pn->pn_count, + pn->pn_po.po_path); + /* If arg specified, it should be the only one pnode to be printed, + * but we walk through the rest of list for debugging purposes. + */ + return NULL; +} + +void +puffs_pn_nodeprintall(struct puffs_usermount *pu) +{ + puffs_pn_nodewalk(pu, puffs_pn_nodeprint, NULL); +} + +struct vattr * +puffs_pn_getvap(struct puffs_node *pn) +{ + + return &pn->pn_va; +} + +void * +puffs_pn_getpriv(struct puffs_node *pn) +{ + + return pn->pn_data; +} + +void +puffs_pn_setpriv(struct puffs_node *pn, void *priv) +{ + + pn->pn_data = priv; +} + +struct puffs_pathobj * +puffs_pn_getpo(struct puffs_node *pn) +{ + + return &pn->pn_po; +} + +struct puffs_usermount * +puffs_pn_getmnt(struct puffs_node *pn) +{ + + return pn->pn_mnt; +} + +/* convenience / shortcut */ +void * +puffs_pn_getmntspecific(struct puffs_node *pn) +{ + + return pn->pn_mnt->pu_privdata; +} + +/* + * newnode parameters + */ +void +puffs_newinfo_setcookie(struct puffs_newinfo *pni, puffs_cookie_t cookie) +{ + + *pni->pni_cookie = cookie; +} + +void +puffs_newinfo_setvtype(struct puffs_newinfo *pni, enum vtype vt) +{ + + *pni->pni_vtype = vt; +} + +void +puffs_newinfo_setsize(struct puffs_newinfo *pni, voff_t size) +{ + + *pni->pni_size = size; +} + +void +puffs_newinfo_setrdev(struct puffs_newinfo *pni, dev_t rdev) +{ + + *pni->pni_rdev = rdev; +} diff --git a/lib/libpuffs/protect.c b/lib/libpuffs/protect.c new file mode 100644 index 000000000..48043ff3d --- /dev/null +++ b/lib/libpuffs/protect.c @@ -0,0 +1,145 @@ +/* Created (MFS based): + * June 2011 (Evgeniy Ivanov) + */ + +#include "fs.h" +#include + +#include "puffs.h" +#include "puffs_priv.h" + +FORWARD _PROTOTYPE( int in_group, (gid_t grp) ); + + +/*===========================================================================* + * fs_chmod * + *===========================================================================*/ +PUBLIC int fs_chmod() +{ +/* Perform the chmod(name, mode) system call. */ + int r; + struct puffs_node *pn; + mode_t mode; + struct vattr va; + PUFFS_MAKECRED(pcr, &global_kcred); + + if (global_pu->pu_ops.puffs_node_setattr == NULL) + return(EINVAL); + + mode = (mode_t) fs_m_in.REQ_MODE; + + if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(EINVAL); + + puffs_vattr_null(&va); + /* Clear setgid bit if file is not in caller's grp */ + va.va_mode = (pn->pn_va.va_mode & ~ALL_MODES) | (mode & ALL_MODES); + va.va_ctime.tv_nsec = 0; + va.va_ctime.tv_sec = clock_time(); + + if (global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr) != 0) + return(EINVAL); + + /* Return full new mode to caller. */ + fs_m_out.RES_MODE = pn->pn_va.va_mode; + + return(OK); +} + + +/*===========================================================================* + * fs_chown * + *===========================================================================*/ +PUBLIC int fs_chown() +{ + int r; + struct puffs_node *pn; + struct vattr va; + PUFFS_MAKECRED(pcr, &global_kcred); + + if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(EINVAL); + + /* Not permitted to change the owner of a file on a read-only file sys. */ + if (!is_readonly_fs) { + puffs_vattr_null(&va); + va.va_uid = fs_m_in.REQ_UID; + va.va_gid = fs_m_in.REQ_GID; + va.va_mode = pn->pn_va.va_mode & ~(I_SET_UID_BIT | I_SET_GID_BIT); + va.va_ctime.tv_nsec = 0; + va.va_ctime.tv_sec = clock_time(); + + if (global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr) != 0) + return(EINVAL); + } + + /* Update caller on current mode, as it may have changed. */ + fs_m_out.RES_MODE = pn->pn_va.va_mode; + + return(OK); +} + + +/*===========================================================================* + * forbidden * + *===========================================================================*/ +PUBLIC int forbidden(register struct puffs_node *pn, mode_t access_desired) +{ +/* Given a pointer to an pnode, 'pn', and the access desired, determine + * if the access is allowed, and if not why not. The routine looks up the + * caller's uid in the 'fproc' table. If access is allowed, OK is returned + * if it is forbidden, EACCES is returned. + */ + + register mode_t bits, perm_bits; + int r, shift; + + /* Isolate the relevant rwx bits from the mode. */ + bits = pn->pn_va.va_mode; + if (caller_uid == SU_UID) { + /* Grant read and write permission. Grant search permission for + * directories. Grant execute permission (for non-directories) if + * and only if one of the 'X' bits is set. + */ + if ( (bits & I_TYPE) == I_DIRECTORY || + bits & ((X_BIT << 6) | (X_BIT << 3) | X_BIT)) + perm_bits = R_BIT | W_BIT | X_BIT; + else + perm_bits = R_BIT | W_BIT; + } else { + if (caller_uid == pn->pn_va.va_uid) shift = 6; /* owner */ + else if (caller_gid == pn->pn_va.va_gid) shift = 3; /* group */ + else if (in_group(pn->pn_va.va_gid) == OK) shift = 3; /* other groups */ + else shift = 0; /* other */ + perm_bits = (bits >> shift) & (R_BIT | W_BIT | X_BIT); + } + + /* If access desired is not a subset of what is allowed, it is refused. */ + r = OK; + if ((perm_bits | access_desired) != perm_bits) r = EACCES; + + /* Check to see if someone is trying to write on a file system that is + * mounted read-only. + */ + if (r == OK) { + if (access_desired & W_BIT) { + r = is_readonly_fs ? EROFS : OK; + } + } + + return(r); +} + + +/*===========================================================================* + * in_group * + *===========================================================================*/ +PRIVATE int in_group(gid_t grp) +{ + int i; + for(i = 0; i < credentials.vu_ngroups; i++) + if (credentials.vu_sgroups[i] == grp) + return(OK); + + return(EINVAL); +} diff --git a/lib/libpuffs/proto.h b/lib/libpuffs/proto.h new file mode 100644 index 000000000..da1c95d8b --- /dev/null +++ b/lib/libpuffs/proto.h @@ -0,0 +1,71 @@ +#ifndef PUFFS_PROTO_H +#define PUFFS_PROTO_H + +/* Function prototypes. */ + +_PROTOTYPE( int fs_new_driver, (void) ); + +/* inode.c */ +_PROTOTYPE( int fs_putnode, (void) ); + +/* device.c */ +_PROTOTYPE( int dev_open, (endpoint_t driver_e, dev_t dev, + endpoint_t proc_e, int flags) ); +_PROTOTYPE( void dev_close, (endpoint_t driver_e, dev_t dev) ); + +/* link.c */ +_PROTOTYPE( int fs_ftrunc, (void) ); +_PROTOTYPE( int fs_link, (void) ); +_PROTOTYPE( int fs_rdlink, (void) ); +_PROTOTYPE( int fs_rename, (void) ); +_PROTOTYPE( int fs_unlink, (void) ); + +/* misc.c */ +_PROTOTYPE( int fs_flush, (void) ); +_PROTOTYPE( int fs_sync, (void) ); + +/* mount.c */ +_PROTOTYPE( int fs_mountpoint, (void) ); +_PROTOTYPE( int fs_readsuper, (void) ); +_PROTOTYPE( int fs_unmount, (void) ); + +/* open.c */ +_PROTOTYPE( int fs_create, (void) ); +_PROTOTYPE( int fs_inhibread, (void) ); +_PROTOTYPE( int fs_mkdir, (void) ); +_PROTOTYPE( int fs_mknod, (void) ); +_PROTOTYPE( int fs_slink, (void) ); + +/* path.c */ +_PROTOTYPE( int fs_lookup, (void) ); +_PROTOTYPE( struct puffs_node *advance, (struct puffs_node *dirp, + char string[NAME_MAX + 1], int chk_perm)); + +/* protect.c */ +_PROTOTYPE( int fs_chmod, (void) ); +_PROTOTYPE( int fs_chown, (void) ); +_PROTOTYPE( int fs_getdents, (void) ); +_PROTOTYPE( int forbidden, (struct puffs_node *rip, + mode_t access_desired) ); + +/* read.c */ +_PROTOTYPE( int fs_breadwrite, (void) ); +_PROTOTYPE( int fs_readwrite, (void) ); + +/* stadir.c */ +_PROTOTYPE( int fs_fstatfs, (void) ); +_PROTOTYPE( int fs_stat, (void) ); +_PROTOTYPE( int fs_statvfs, (void) ); + +/* time.c */ +_PROTOTYPE( int fs_utime, (void) ); + +/* utility.c */ +_PROTOTYPE( int no_sys, (void) ); +_PROTOTYPE( void mfs_nul_f, (char *file, int line, char *str, + unsigned int len, unsigned int maxlen) ); +_PROTOTYPE( time_t clock_time, (void) ); +_PROTOTYPE( int update_times, (struct puffs_node *pn, int fl, time_t t) ); +_PROTOTYPE( void lpuffs_debug, (char *format, ...) ); + +#endif /* PUFFS_PROTO_H */ diff --git a/lib/libpuffs/puffs.3 b/lib/libpuffs/puffs.3 new file mode 100644 index 000000000..abc276d87 --- /dev/null +++ b/lib/libpuffs/puffs.3 @@ -0,0 +1,542 @@ +.\" $NetBSD: puffs.3,v 1.42.4.2 2009/10/16 12:07:23 sborrill Exp $ +.\" +.\" Copyright (c) 2006, 2007, 2008 Antti Kantee. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd September 6, 2008 +.Dt PUFFS 3 +.Os +.Sh NAME +.Nm puffs +.Nd Pass-to-Userspace Framework File System development interface +.Sh LIBRARY +.Lb libpuffs +.Sh SYNOPSIS +.In puffs.h +.Ft struct puffs_usermount * +.Fo puffs_init +.Fa "struct puffs_ops *pops" "const char *mntfromname" "const char *puffsname" +.Fa "void *private" "uint32_t flags" +.Fc +.Ft int +.Fo puffs_mount +.Fa "struct puffs_usermount *pu" "const char *dir" "int mntflags" +.Fa "puffs_cookie_t root_cookie" +.Fc +.Ft int +.Fn puffs_getselectable "struct puffs_usermount *pu" +.Ft int +.Fn puffs_setblockingmode "struct puffs_usermount *pu" "int mode" +.Ft int +.Fn puffs_getstate "struct puffs_usermount *pu" +.Ft int +.Fn puffs_setstacksize "struct puffs_usermount *pu" "size_t stacksize" +.Ft void +.Fn puffs_setroot "struct puffs_usermount *pu" "struct puffs_node *node" +.Ft void +.Fo puffs_setrootinfo +.Fa "struct puffs_usermount *pu" "enum vtype vt" "vsize_t vsize" "dev_t rdev" +.Fc +.Ft struct puffs_node * +.Fn puffs_getroot "struct puffs_usermount *pu" +.Ft void * +.Fn puffs_getspecific "struct puffs_usermount *pu" +.Ft void +.Fn puffs_setspecific "struct puffs_usermount *pu" "void *private" +.Ft void +.Fn puffs_setmaxreqlen "struct puffs_usermount *pu" "size_t maxreqlen" +.Ft size_t +.Fn puffs_getmaxreqlen "struct puffs_usermount *pu" +.Ft void +.Fn puffs_setfhsize "struct puffs_usermount *pu" "size_t fhsize" "int flags" +.Ft void +.Fn puffs_setncookiehash "struct puffs_usermount *pu" "int nhashes" +.Ft void +.Fn puffs_ml_loop_fn "struct puffs_usermount *pu" +.Ft void +.Fn puffs_ml_setloopfn "struct puffs_usermount *pu" "puffs_ml_loop_fn lfn" +.Ft void +.Fn puffs_ml_settimeout "struct puffs_usermount *pu" "struct timespec *ts" +.Ft int +.Fn puffs_daemon "struct puffs_usermount *pu" "int nochdir" "int noclose" +.Ft int +.Fn puffs_mainloop "struct puffs_usermount *pu" +.Ft int +.Fo puffs_dispatch_create +.Fa "struct puffs_usermount *pu" "struct puffs_framebuf *pb" +.Fa "struct puffs_cc **pccp" +.Fc +.Ft int +.Fn puffs_dispatch_exec .Fa "struct puffs_cc *pcc" "struct puffs_framebuf **pbp" +.Sh DESCRIPTION +.Nm +provides a framework for creating file systems as userspace servers. +Operations are transported from the kernel virtual file system layer +to the concrete implementation behind +.Nm , +where they are processed and results are sent back to the kernel. +.Pp +It is possible to use +.Nm +in two different ways. +Calling +.Fn puffs_mainloop +takes execution context away from the caller and automatically handles +all requests by using the callbacks. +By using +.Xr puffs_framebuf 3 +in conjuction with +.Fn puffs_mainloop , +it is possible to handle I/O to and from file descriptors. +This is suited e.g. for distributed file servers. +.Ss Library operation +Operations on the library always require a pointer to the opaque context +identifier, +.Va struct puffs_usermount . +It is obtained by calling +.Fn puffs_init . +.Pp +.Nm +operates using operation callbacks. +They can be initialized using the macro +.Fn PUFFSOP_SET pops fsname type opname , +which will initialize the operation +.Fn puffs_type_opname +in +.Fa pops +to +.Fn fsname_type_opname . +All operations are initialized to a default state with the call +.Fn PUFFSOP_INIT pops . +All of the VFS routines are mandatory, but all of the node operations +with the exception of +.Fn puffs_node_lookup +are optional. +However, leaving operations blank will naturally have an effect on the +features available from the file system implementation. +.Bl -tag -width xxxx +.It Fn puffs_init pops mntfromname puffsname private flags +Initializes the library context. +.Ar pops +specifies the callback operations vector. +.Ar mntfromname +is device the file system is mounted from. +This can be for example a block device such as +.Pa /dev/wd0a +or, if the file system is pseudo file system, the +.Nm +device name can be given by +.Dv _PATH_PUFFS . +This value is used for example in the first column of the output of +.Xr mount 8 +and +.Xr df 1 . +.Ar puffsname +is the file system type. +It will always be prepended with the string "puffs|". +If possible, file server binaries should be named using the format +"mount_myfsnamehere" and this value should equal "myfsnamehere". +A file system specific context pointer can optionally be given in +.Ar private . +This can be retrieved by +.Fn puffs_getspecific . +Flags for +.Nm +can be given via +.Fa pflags . +Currently the following flags are supported: +.Bl -tag -width "XPUFFS_KFLAG_LOOKUP_FULLPNBUF" +.It Dv PUFFS_KFLAG_NOCACHE_NAME +Do not enter pathname components into the name cache. +This means that every time the kernel does a lookup for a +componentname, the file server will be consulted. +.It Dv PUFFS_KFLAG_NOCACHE_PAGE +Do not use the page cache. +This means that all reads and writes to regular file are +propagated to the file server for handling. +This option makes a difference only for regular files. +.It Dv PUFFS_KFLAG_NOCACHE +An alias for both +.Dv PUFFS_KFLAG_NOCACHE_NAME +and +.Dv PUFFS_KFLAG_NOCACHE_PAGE . +.It Dv PUFFS_KFLAG_ALLOPS +This flag requests that all operations are sent to userspace. +Normally the kernel shortcircuits unimplemented operations. +This flag is mostly useful for debugging purposes. +.It Dv PUFFS_KFLAG_WTCACHE +Set the file system cache behavior as write-through. +This means that all writes are immediately issued to the file server +instead of being flushed in file system sync. +This is useful especially for distributed file systems. +.It Dv PUFFS_KFLAG_IAONDEMAND +Issue inactive only on demand. +If a file server defines the inactive method, call it only if the file +server has explicitly requested that inactive be called for the +node in question. +Once inactive has been called for a node, it will not be called +again unless the request to call inactive is reissued by the file server. +See +.Fn puffs_setback +in +.Xr puffs_ops 3 +for more information. +.It Dv PUFFS_KFLAG_LOOKUP_FULLPNBUF +This flag affects only the parameter +.Ar pcn to +.Fn puffs_node_lookup . +If this flag is not given, only the next pathname component under +lookup is found from +.Ar pcn-\*[Gt]pcn_name . +If this flag is given, the full path the kernel was +asked to resolve can be found from there. +.It Dv PUFFS_FLAG_BUILDPATH +The framework will build a complete path name, which is supplied +with each operation and can be found from the +.Va pcn_po_full.po_path +field in a +.Vt struct puffs_cn . +The option assumes that the framework can map a cookie to a +.Vt struct puffs_node . +See +.Sx Cookies +for more information on cookie mapping. +See +.Xr puffs_path 3 +for more information on library calls involving paths. +.It Dv PUFFS_FLAG_HASHPATH +Calculate a hash of the path into the path object field +.Va po_hash . +This hash value is used by +.Fn puffs_path_walkcmp +to avoid doing a full comparison for every path equal in length to +the one searched for. +Especially if the file system uses the abovementioned function, it +is a good idea to define this flag. +.It Dv PUFFS_FLAG_OPDUMP +This option makes the framework dump a textual representation of +each operation before executing it. +It is useful for debugging purposes. +.El +.El +.Pp +The following functions can be used to query or modify the global +state of the file system. +Note, that all calls are not available at all times. +.Bl -tag -width xxxx +.It Fn puffs_getselectable "pu" +Returns a handle to do I/O multiplexing with: +.Xr select 2 , +.Xr poll 2 , +and +.Xr kqueue 2 +are all examples of acceptable operations. +.It Fn puffs_setblockingmode "pu" "mode" +Sets the file system upstream access to blocking or non-blocking mode. +Acceptable values for the argument are +.Dv PUFFSDEV_BLOCK +and +.Dv PUFFSDEV_NONBLOCK . +.Pp +This routine can be called only after calling +.Fn puffs_mount . +.It Fn puffs_getstate "pu" +Returns the state of the file system. +It is maintained by the framework and is mostly useful for the framework +itself. +Possible values are +.Dv PUFFS_STATE_BEFOREMOUNT , +.Dv PUFFS_STATE_RUNNING , +.Dv PUFFS_STATE_UNMOUNTING +and +.Dv PUFFS_STATE_UNMOUNTED . +.It Fn puffs_setstacksize "pu" "stacksize" +Sets the stack size used when running callbacks. +The default is +.Dv PUFFS_STACKSIZE_DEFAULT +bytes of stack space per request. +The minimum stacksize is architecture-dependent and can be specified +by using the opaque constant +.Dv PUFFS_STACKSIZE_MIN . +.It Fn puffs_setroot "pu" "node" +Sets the root node of mount +.Fa pu +to +.Fa "node" . +Setting the root node is currently required only if the path +framework is used, see +.Xr puffs_path 3 . +.It Fn puffs_setrootinfo pu vt vsize rdev +The default root node is a directory. +In case the file system wants something different, it can call this +function and set the type, size and possible device type to whatever +it wants. +This routine is independent of +.Fn puffs_setroot . +.It Fn puffs_getroot "pu" +Returns the root node set earlier. +.It Fn puffs_getspecific "pu" +Returns the +.Fa private +argument of +.Fn puffs_init . +.It Fn puffs_setspecific "pu" "private" +Can be used to set the specific data after the call to +.Fn puffs_init . +.It Fn puffs_setmaxreqlen "pu" "maxreqlen" +In case the file system desires a maximum buffer length different from +the default, the amount +.Fa maxreqlen +will be requested from the kernel when the file system is mounted. +.Pp +It is legal to call this function only between +.Fn puffs_init +and +.Fn puffs_mount . +.Pp +.Em NOTE +This does not currently work. +.It Fn puffs_getmaxreqlen "pu" +Returns the maximum request length the kernel will need for a single +request. +.Pp +.Em NOTE +This does not currently work. +.It Fn puffs_setfhsize "pu" "fhsize" "flags" +Sets the desired file handle size. +This must be called if the file system wishes to support NFS exporting +file systems of the +.Fn fh* +family of function calls. +.Pp +In case all nodes in the file system produce the same length file handle, +it must be supplied as +.Fa fhsize . +In this case, the file system may ignore the length parameters in the +file handle callback routines, as the kernel will always pass the +correct length buffer. +However, if the file handle size varies according to file, the argument +.Fa fhsize +defines the maximum size of a file handle for the file system. +In this case the file system must take care of the handle lengths by +itself in the file handle callbacks, see +.Xr puffs_ops 3 +for more information. +Also, the flag +.Dv PUFFS_FHFLAG_DYNAMIC +must be provided in the argument +.Fa flags . +.Pp +In case the file system wants to sanity check its file handle lengths +for the limits of NFS, it can supply +.Dv PUFFS_FHFLAG_NFSV2 +and +.Dv PUFFS_FHFLAG_NFSV3 +in the +.Fa flags +parameter. +It is especially important to note that these are not directly the +limits specified by the protocols, as the kernel uses some bytes from +the buffer space. +In case the file handles are too large, mount will return an error. +.Pp +It is legal to call this function only between +.Fn puffs_init +and +.Fn puffs_mount . +.It Fn puffs_setncookiehash "pu" "ncookiehash" +The parameter +.Fa ncookiehash +controls the amount of hash buckets the kernel has for reverse lookups +from cookie to vnode. +Technically the default is enough, but a memory/time tradeoff can be +made by increasing this for file systems which know they will have +very many active files. +.Pp +It is legal to call this function only between +.Fn puffs_init +and +.Fn puffs_mount . +.El +.Pp +After the correct setup for the library has been established and the +backend has been initialized the file system is made operational by calling +.Fn puffs_mount . +After this function returns the file system should start processing requests. +.Bl -tag -width xxxx +.It Fn puffs_mount pu dir mntflags root_cookie +.Ar pu +is the library context pointer from +.Fn puffs_init . +The argument +.Fa dir +signifies the mount point and +.Fa mntflags +is the flagset given to +.Xr mount 2 . +The value +.Ar root_cookie +will be used as the cookie for the file system root node. +.El +.Ss Using the built-in eventloop +.Bl -tag -width xxxx +.It Fn puffs_ml_loop_fn pu +Loop function signature. +.It Fn puffs_ml_setloopfn pu lfn +Set loop function to +.Ar lfn . +This function is called once each time the event loop loops. +It is not a well-defined interval, but it can be made fairly regular +by setting the loop timeout by +.Fn puffs_ml_settimeout . +.It Fn puffs_ml_settimeout pu ts +Sets the loop timeout to +.Ar ts +or disables it if +.Ar ts +is +.Dv NULL . +This can be used to roughly control how often the loop callback +.Fn lfn +is called +.It Fn puffs_daemon pu nochdir noclose +Detach from the console like +.Fn daemon 3 . +This call synchronizes with +.Fn puffs_mount +and the foreground process does not exit before the file system mount +call has returned from the kernel. +.It Fn puffs_mainloop pu flags +Handle all requests automatically until the file system is unmounted. +It returns 0 if the file system was successfully unmounted or \-1 if it +was killed in action. +.Pp +In case +.Xr puffs_framebuf 3 +has been initialized, I/O from the relevant descriptors is processed +automatically by the eventloop. +.It Fn puffs_dispatch_create pu pb pccp +.It Fn puffs_dispatch_exec pcc pbp +In case the use of +.Fn puffs_mainloop +is not possible, requests may be dispatched manually. +However, as this is less efficient than using the mainloop, +it should never be the first preference. +.Pp +Calling +.Fn puffs_dispatch_create +creates a dispatch request. +The argument +.Ar pb +should contains a valid request and upon success +.Ar pccp +will contain a valid request context. +This context is passed to +.Fn puffs_dispatch_exec +to execute the request. +If the request yielded before completing, the routine returns 0, +otherwise 1. +When the routine completes, +.Ar pcc +is made invalid and a pointer to the processed buffer is placed in +.Ar pbp . +It is the responsibility of the caller to send the response (if +necessary) and destroy the buffer. +.Pp +See +.Xr puffs_cc 3 +and +.Xr puffs_framebuf 3 +for further information. +.El +.Ss Cookies +Every file (regular file, directory, device node, ...) instance is +attached to the kernel using a cookie. +A cookie should uniquely map to a file during its lifetime. +If file instances are kept in memory, a simple strategy is to use +the virtual address of the structure describing the file. +The cookie can be recycled when +.Fn puffs_node_reclaim +is called for a node. +.Pp +For some operations (such as building paths) the framework needs to map +the cookie to the framework-level structure describing a file, +.Vt struct puffs_node . +It is advisable to simply use the +.Vt struct puffs_node +address as a cookie and store file system specific data in the private +portion of +.Vt struct puffs_node . +The library assumes this by default. +If it is not desirable, the file system implementation can call +.Fn puffs_set_cookiemap +to provide an alternative cookie-to-node mapping function. +.Sh SEE ALSO +.Xr mount 2 , +.Xr puffs_cc 3 , +.Xr puffs_cred 3 , +.Xr puffs_flush 3 , +.Xr puffs_framebuf 3 , +.Xr puffs_node 3 , +.Xr puffs_ops 3 , +.Xr puffs_path 3 , +.Xr puffs_suspend 3 , +.Xr refuse 3 , +.Xr puffs 4 +.Rs +.%A Antti Kantee +.%D March 2007 +.%J Proceedings of AsiaBSDCon 2007 +.%P pp. 29-42 +.%T puffs - Pass-to-Userspace Framework File System +.Re +.Rs +.%A Antti Kantee +.%D September 2007 +.%I Helsinki University of Technology +.%R Tech Report TKK-TKO-B157 +.%T Using puffs for Implementing Client-Server Distributed File Systems +.Re +.Rs +.%A Antti Kantee +.%A Alistair Crooks +.%D September 2007 +.%J EuroBSDCon 2007 +.%T ReFUSE: Userspace FUSE Reimplementation Using puffs +.Re +.Rs +.%A Antti Kantee +.%D March 2008 +.%J Proceedings of AsiaBSDCon 2008 +.%P pp. 55-70 +.%T Send and Receive of File System Protocols: Userspace Approach With puffs +.Re +.Sh HISTORY +An unsupported experimental version of +.Nm +first appeared in +.Nx 4.0 . +A stable version appeared in +.Nx 5.0 . +.Sh AUTHORS +.An Antti Kantee Aq pooka@iki.fi diff --git a/lib/libpuffs/puffs.c b/lib/libpuffs/puffs.c new file mode 100644 index 000000000..dcedaf3b2 --- /dev/null +++ b/lib/libpuffs/puffs.c @@ -0,0 +1,774 @@ +/* $NetBSD: puffs.c,v 1.92.4.4 2009/10/27 20:37:38 bouyer Exp $ */ + +/* + * Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved. + * + * Development of this software was supported by the + * Google Summer of Code program and the Ulla Tuominen Foundation. + * The Google SoC project was mentored by Bill Studenmund. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* TODO: We don't support PUFFS_COMFD used in original puffs_mount, + * add it to the docs if any. + * + */ + +#include "fs.h" +#include +#if !defined(lint) +__RCSID("$NetBSD: puffs.c,v 1.92.4.4 2009/10/27 20:37:38 bouyer Exp $"); +#endif /* !lint */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + +#ifdef PUFFS_WITH_THREADS +#include +pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + + +/* Declare some local functions. */ +FORWARD _PROTOTYPE(void get_work, (message *m_in) ); +FORWARD _PROTOTYPE( void reply, (endpoint_t who, message *m_out) ); + +/* SEF functions and variables. */ +FORWARD _PROTOTYPE( void sef_local_startup, (void) ); +FORWARD _PROTOTYPE( int sef_cb_init_fresh, (int type, sef_init_info_t *info) ); +FORWARD _PROTOTYPE( void sef_cb_signal_handler, (int signo) ); + +EXTERN int env_argc; +EXTERN char **env_argv; + + +#define PUFFS_MAX_ARGS 20 + +int __real_main(int argc, char* argv[]); + +int __wrap_main(int argc, char *argv[]) +{ + int i; + int new_argc = 0; + static char* new_argv[PUFFS_MAX_ARGS]; + char *name; + + /* SEF local startup. */ + env_setargs(argc, argv); + sef_local_startup(); + + global_kcred.pkcr_type = PUFFCRED_TYPE_INTERNAL; + + if (argc < 3) { + panic("Unexpected arguments, use:\ + mount -t fs /dev/ /dir [-o option1,option2]\n"); + } + + name = argv[0] + strlen(argv[0]); + while (*name != '/' && name != argv[0]) + name--; + if (name != argv[0]) + name++; + strcpy(fs_name, name); + + new_argv[new_argc] = argv[0]; + new_argc++; + + for (i = 1; i < argc; i++) { + if (new_argc >= PUFFS_MAX_ARGS) { + panic("Too many arguments, change PUFFS_MAX_ARGS"); + } + new_argv[new_argc] = argv[i]; + new_argc++; + } + + assert(new_argc > 0); + + get_work(&fs_m_in); + + return __real_main(new_argc, new_argv); +} + + +#define FILLOP(lower, upper) \ +do { \ + if (pops->puffs_node_##lower) \ + opmask[PUFFS_VN_##upper] = 1; \ +} while (/*CONSTCOND*/0) +static void +fillvnopmask(struct puffs_ops *pops, uint8_t *opmask) +{ + + memset(opmask, 0, PUFFS_VN_MAX); + + FILLOP(create, CREATE); + FILLOP(mknod, MKNOD); + FILLOP(open, OPEN); + FILLOP(close, CLOSE); + FILLOP(access, ACCESS); + FILLOP(getattr, GETATTR); + FILLOP(setattr, SETATTR); + FILLOP(poll, POLL); /* XXX: not ready in kernel */ + FILLOP(mmap, MMAP); + FILLOP(fsync, FSYNC); + FILLOP(seek, SEEK); + FILLOP(remove, REMOVE); + FILLOP(link, LINK); + FILLOP(rename, RENAME); + FILLOP(mkdir, MKDIR); + FILLOP(rmdir, RMDIR); + FILLOP(symlink, SYMLINK); + FILLOP(readdir, READDIR); + FILLOP(readlink, READLINK); + FILLOP(reclaim, RECLAIM); + FILLOP(inactive, INACTIVE); + FILLOP(print, PRINT); + FILLOP(read, READ); + FILLOP(write, WRITE); + FILLOP(abortop, ABORTOP); +} +#undef FILLOP + + +/*ARGSUSED*/ +static void +puffs_defaulterror(struct puffs_usermount *pu, uint8_t type, + int error, const char *str, puffs_cookie_t cookie) +{ + + lpuffs_debug("abort: type %d, error %d, cookie %p (%s)\n", + type, error, cookie, str); + abort(); +} + + +int +puffs_getstate(struct puffs_usermount *pu) +{ + + return pu->pu_state & PU_STATEMASK; +} + +void +puffs_setstacksize(struct puffs_usermount *pu, size_t ss) +{ + long psize, minsize; + int stackshift; + int bonus; + + assert(puffs_getstate(pu) == PUFFS_STATE_BEFOREMOUNT); + + psize = sysconf(_SC_PAGESIZE); + minsize = 4*psize; + if (ss < minsize || ss == PUFFS_STACKSIZE_MIN) { + if (ss != PUFFS_STACKSIZE_MIN) + lpuffs_debug("puffs_setstacksize: adjusting " + "stacksize to minimum %ld\n", minsize); + ss = 4*psize; + } + + stackshift = -1; + bonus = 0; + while (ss) { + if (ss & 0x1) + bonus++; + ss >>= 1; + stackshift++; + } + if (bonus > 1) { + stackshift++; + lpuffs_debug("puffs_setstacksize: using next power of two: " + "%d\n", 1<pu_cc_stackshift = stackshift; +} + +struct puffs_pathobj * +puffs_getrootpathobj(struct puffs_usermount *pu) +{ + struct puffs_node *pnr; + + pnr = pu->pu_pn_root; + if (pnr == NULL) { + errno = ENOENT; + return NULL; + } + + return &pnr->pn_po; +} + +void +puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn) +{ + + pu->pu_pn_root = pn; +} + +struct puffs_node * +puffs_getroot(struct puffs_usermount *pu) +{ + + return pu->pu_pn_root; +} + +void +puffs_setrootinfo(struct puffs_usermount *pu, enum vtype vt, + vsize_t vsize, dev_t rdev) +{ + struct puffs_kargs *pargs = pu->pu_kargp; + + if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) { + warnx("puffs_setrootinfo: call has effect only " + "before mount\n"); + return; + } + + pargs->pa_root_vtype = vt; + pargs->pa_root_vsize = vsize; + pargs->pa_root_rdev = rdev; +} + +void * +puffs_getspecific(struct puffs_usermount *pu) +{ + + return pu->pu_privdata; +} + +void +puffs_setspecific(struct puffs_usermount *pu, void *privdata) +{ + + pu->pu_privdata = privdata; +} + +void +puffs_setmntinfo(struct puffs_usermount *pu, + const char *mntfromname, const char *puffsname) +{ + struct puffs_kargs *pargs = pu->pu_kargp; + + (void)strlcpy(pargs->pa_mntfromname, mntfromname, + sizeof(pargs->pa_mntfromname)); + (void)strlcpy(pargs->pa_typename, puffsname, + sizeof(pargs->pa_typename)); +} + +size_t +puffs_getmaxreqlen(struct puffs_usermount *pu) +{ + + return pu->pu_maxreqlen; +} + +void +puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen) +{ + + if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) + warnx("puffs_setmaxreqlen: call has effect only " + "before mount\n"); + + pu->pu_kargp->pa_maxmsglen = reqlen; +} + +void +puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags) +{ + + if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) + warnx("puffs_setfhsize: call has effect only before mount\n"); + + pu->pu_kargp->pa_fhsize = fhsize; + pu->pu_kargp->pa_fhflags = flags; +} + +void +puffs_setncookiehash(struct puffs_usermount *pu, int nhash) +{ + + if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) + warnx("puffs_setfhsize: call has effect only before mount\n"); + + pu->pu_kargp->pa_nhashbuckets = nhash; +} + +void +puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn) +{ + + pu->pu_pathbuild = fn; +} + +void +puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn) +{ + + pu->pu_pathtransform = fn; +} + +void +puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn) +{ + + pu->pu_pathcmp = fn; +} + +void +puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn) +{ + + pu->pu_pathfree = fn; +} + +void +puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn) +{ + + pu->pu_namemod = fn; +} + +void +puffs_set_errnotify(struct puffs_usermount *pu, pu_errnotify_fn fn) +{ + + pu->pu_errnotify = fn; +} + +void +puffs_set_cmap(struct puffs_usermount *pu, pu_cmap_fn fn) +{ + + pu->pu_cmap = fn; +} + +void +puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn) +{ + + pu->pu_ml_lfn = lfn; +} + +void +puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts) +{ + + if (ts == NULL) { + pu->pu_ml_timep = NULL; + } else { + pu->pu_ml_timeout = *ts; + pu->pu_ml_timep = &pu->pu_ml_timeout; + } +} + +void +puffs_set_prepost(struct puffs_usermount *pu, + pu_prepost_fn pre, pu_prepost_fn pst) +{ + + pu->pu_oppre = pre; + pu->pu_oppost = pst; +} + +int +puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags, + puffs_cookie_t cookie) +{ + endpoint_t src; + int error, ind; + + pu->pu_kargp->pa_root_cookie = cookie; + + src = fs_m_in.m_source; + error = OK; + caller_uid = INVAL_UID; /* To trap errors */ + caller_gid = INVAL_GID; + req_nr = fs_m_in.m_type; + + if (req_nr < VFS_BASE) { + fs_m_in.m_type += VFS_BASE; + req_nr = fs_m_in.m_type; + } + ind = req_nr - VFS_BASE; + + assert(ind == REQ_READ_SUPER); + + if (ind < 0 || ind >= NREQS) { + error = EINVAL; + } else { + error = (*fs_call_vec[ind])(); + } + + fs_m_out.m_type = error; + if (IS_VFS_FS_TRANSID(last_request_transid)) { + /* If a transaction ID was set, reset it */ + fs_m_out.m_type = TRNS_ADD_ID(fs_m_out.m_type, + last_request_transid); + } + reply(src, &fs_m_out); + + if (error) { + free(pu->pu_kargp); + pu->pu_kargp = NULL; + errno = error; + return -1; + } + + PU_SETSTATE(pu, PUFFS_STATE_RUNNING); + return 0; +} + +/*ARGSUSED*/ +struct puffs_usermount * +_puffs_init(int dummy, struct puffs_ops *pops, const char *mntfromname, + const char *puffsname, void *priv, uint32_t pflags) +{ + struct puffs_usermount *pu; + struct puffs_kargs *pargs; + int sverrno; + + if (puffsname == PUFFS_DEFER) + puffsname = "n/a"; + if (mntfromname == PUFFS_DEFER) + mntfromname = "n/a"; + if (priv == PUFFS_DEFER) + priv = NULL; + + pu = malloc(sizeof(struct puffs_usermount)); + if (pu == NULL) + goto failfree; + memset(pu, 0, sizeof(struct puffs_usermount)); + + pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs)); + if (pargs == NULL) + goto failfree; + memset(pargs, 0, sizeof(struct puffs_kargs)); + + pargs->pa_vers = PUFFSDEVELVERS | PUFFSVERSION; + pargs->pa_flags = PUFFS_FLAG_KERN(pflags); + fillvnopmask(pops, pargs->pa_vnopmask); + puffs_setmntinfo(pu, mntfromname, puffsname); + + puffs_zerostatvfs(&pargs->pa_svfsb); + pargs->pa_root_cookie = NULL; + pargs->pa_root_vtype = VDIR; + pargs->pa_root_vsize = 0; + pargs->pa_root_rdev = 0; + pargs->pa_maxmsglen = 0; + + pu->pu_flags = pflags; + buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH; /* XXX */ + pu->pu_ops = *pops; + free(pops); /* XXX */ + + pu->pu_privdata = priv; + pu->pu_cc_stackshift = PUFFS_CC_STACKSHIFT_DEFAULT; + LIST_INIT(&pu->pu_pnodelst); + LIST_INIT(&pu->pu_pnode_removed_lst); + LIST_INIT(&pu->pu_ios); + LIST_INIT(&pu->pu_ios_rmlist); + LIST_INIT(&pu->pu_ccmagazin); + TAILQ_INIT(&pu->pu_sched); + + /* defaults for some user-settable translation functions */ + pu->pu_cmap = NULL; /* identity translation */ + + pu->pu_pathbuild = puffs_stdpath_buildpath; + pu->pu_pathfree = puffs_stdpath_freepath; + pu->pu_pathcmp = puffs_stdpath_cmppath; + pu->pu_pathtransform = NULL; + pu->pu_namemod = NULL; + + pu->pu_errnotify = puffs_defaulterror; + + PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT); + + global_pu = pu; + + return pu; + + failfree: + /* can't unmount() from here for obvious reasons */ + sverrno = errno; + free(pu); + errno = sverrno; + return NULL; +} + +void +puffs_cancel(struct puffs_usermount *pu, int error) +{ + assert(puffs_getstate(pu) < PUFFS_STATE_RUNNING); + free(pu); +} + +/*ARGSUSED1*/ +int +puffs_exit(struct puffs_usermount *pu, int force) +{ + struct puffs_node *pn; + + lpuffs_debug("puffs_exit\n"); + + while ((pn = LIST_FIRST(&pu->pu_pnodelst)) != NULL) + puffs_pn_put(pn); + + while ((pn = LIST_FIRST(&pu->pu_pnode_removed_lst)) != NULL) + puffs_pn_put(pn); + + puffs__cc_exit(pu); + if (pu->pu_state & PU_HASKQ) + close(pu->pu_kq); + free(pu); + + return 0; /* always succesful for now, WILL CHANGE */ +} + +/* + * Actual mainloop. This is called from a context which can block. + * It is called either from puffs_mainloop (indirectly, via + * puffs_cc_continue() or from puffs_cc_yield()). + */ +void +puffs__theloop(struct puffs_cc *pcc) +{ + struct puffs_usermount *pu = pcc->pcc_pu; + int error, ind; + + while (!unmountdone || !exitsignaled) { + endpoint_t src; + + /* + * Schedule existing requests. + */ + while ((pcc = TAILQ_FIRST(&pu->pu_sched)) != NULL) { + lpuffs_debug("scheduling existing tasks\n"); + TAILQ_REMOVE(&pu->pu_sched, pcc, pcc_schedent); + puffs__goto(pcc); + } + + if (pu->pu_ml_lfn) { + lpuffs_debug("Calling user mainloop handler\n"); + pu->pu_ml_lfn(pu); + } + + /* Wait for request message. */ + get_work(&fs_m_in); + + src = fs_m_in.m_source; + error = OK; + caller_uid = INVAL_UID; /* To trap errors */ + caller_gid = INVAL_GID; + req_nr = fs_m_in.m_type; + + if (req_nr < VFS_BASE) { + fs_m_in.m_type += VFS_BASE; + req_nr = fs_m_in.m_type; + } + ind = req_nr - VFS_BASE; + + if (ind < 0 || ind >= NREQS) { + error = EINVAL; + } else { + error = (*fs_call_vec[ind])(); + } + + fs_m_out.m_type = error; + if (IS_VFS_FS_TRANSID(last_request_transid)) { + /* If a transaction ID was set, reset it */ + fs_m_out.m_type = TRNS_ADD_ID(fs_m_out.m_type, last_request_transid); + } + reply(src, &fs_m_out); + } + + if (puffs__cc_restoremain(pu) == -1) + warn("cannot restore main context. impending doom"); + + /* May get here, if puffs_fakecc is set to 1. Currently librefuse sets it. + * Now we just return to the caller. + */ +} + +int +puffs_mainloop(struct puffs_usermount *pu) +{ + struct puffs_cc *pcc; + int sverrno; + + assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING); + + pu->pu_state |= PU_HASKQ | PU_INLOOP; + + /* + * Create alternate execution context and jump to it. Note + * that we come "out" of savemain twice. Where we come out + * of it depends on the architecture. If the return address is + * stored on the stack, we jump out from puffs_cc_continue(), + * for a register return address from puffs__cc_savemain(). + * PU_MAINRESTORE makes sure we DTRT in both cases. + */ + if (puffs__cc_create(pu, puffs__theloop, &pcc) == -1) { + goto out; + } + if (puffs__cc_savemain(pu) == -1) { + goto out; + } + if ((pu->pu_state & PU_MAINRESTORE) == 0) + puffs_cc_continue(pcc); + + errno = 0; + + out: + /* store the real error for a while */ + sverrno = errno; + + errno = sverrno; + if (errno) + return -1; + else + return 0; +} + + +/*===========================================================================* + * sef_local_startup * + *===========================================================================*/ +PRIVATE void sef_local_startup() +{ + /* Register init callbacks. */ + sef_setcb_init_fresh(sef_cb_init_fresh); + sef_setcb_init_restart(sef_cb_init_fail); + + /* No live update support for now. */ + + /* Register signal callbacks. */ + sef_setcb_signal_handler(sef_cb_signal_handler); + + /* Let SEF perform startup. */ + sef_startup(); +} + +/*===========================================================================* + * sef_cb_init_fresh * + *===========================================================================*/ +PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *info) +{ +/* Initialize the Minix file server. */ + SELF_E = getprocnr(); + return(OK); +} + +/*===========================================================================* + * sef_cb_signal_handler * + *===========================================================================*/ +PRIVATE void sef_cb_signal_handler(int signo) +{ + /* Only check for termination signal, ignore anything else. */ + if (signo != SIGTERM) return; + + exitsignaled = 1; + fs_sync(); + + /* If unmounting has already been performed, exit immediately. + * We might not get another message. + */ + if (unmountdone) { + if (puffs__cc_restoremain(global_pu) == -1) + warn("cannot restore main context. impending doom"); + /* May happen if puffs_fakecc is set to 1. Currently librefuse sets it. + * There is a chance, that main loop hangs in receive() and we will + * never get any new message, so we have to exit() here. + */ + exit(0); + } +} + +/*===========================================================================* + * get_work * + *===========================================================================*/ +PRIVATE void get_work(m_in) +message *m_in; /* pointer to message */ +{ + int r, srcok = 0; + endpoint_t src; + + do { + if ((r = sef_receive(ANY, m_in)) != OK) /* wait for message */ + panic("sef_receive failed: %d", r); + src = m_in->m_source; + + if(src == VFS_PROC_NR) { + if(unmountdone) + lpuffs_debug("libpuffs: unmounted: unexpected message from FS\n"); + else + srcok = 1; /* Normal FS request. */ + + } else + lpuffs_debug("libpuffs: unexpected source %d\n", src); + } while(!srcok); + + assert((src == VFS_PROC_NR && !unmountdone)); + + last_request_transid = TRNS_GET_ID(fs_m_in.m_type); + fs_m_in.m_type = TRNS_DEL_ID(fs_m_in.m_type); + if (fs_m_in.m_type == 0) { + assert(!IS_VFS_FS_TRANSID(last_request_transid)); + fs_m_in.m_type = last_request_transid; /* Backwards compat. */ + last_request_transid = 0; + } else + assert(IS_VFS_FS_TRANSID(last_request_transid)); +} + + +/*===========================================================================* + * reply * + *===========================================================================*/ +PRIVATE void reply( + endpoint_t who, + message *m_out /* report result */ +) +{ + if (OK != send(who, m_out)) /* send the message */ + lpuffs_debug("libpuffs(%d) was unable to send reply\n", SELF_E); + + last_request_transid = 0; +} + diff --git a/lib/libpuffs/puffs.h b/lib/libpuffs/puffs.h new file mode 100644 index 000000000..09d177b2d --- /dev/null +++ b/lib/libpuffs/puffs.h @@ -0,0 +1,747 @@ +/* $NetBSD: puffs.h,v 1.108.4.4 2009/10/27 20:37:38 bouyer Exp $ */ + +/* + * Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved. + * + * Development of this software was supported by the + * Google Summer of Code program and the Ulla Tuominen Foundation. + * The Google SoC project was mentored by Bill Studenmund. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _PUFFS_H_ +#define _PUFFS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "puffs_msgif.h" + +#include +#include + +#include +#include +#include + +#include + + +/* MINIX forwards */ +struct flock; +struct dirent; + +/* XXX: from sys/fstypes.h + * Flags for various system call interfaces. + * + * waitfor flags to vfs_sync() and getvfsstat() + */ +#define MNT_WAIT 1 /* synchronously wait for I/O to complete */ +#define MNT_NOWAIT 2 /* start all I/O, but do not wait for it */ +#define MNT_LAZY 3 /* push data not written by filesystem syncer */ + +#define MNT_RDONLY 0x00000001 /* read only filesystem */ +#define MNT_SYNCHRONOUS 0x00000002 /* file system written synchronously */ +#define MNT_NOEXEC 0x00000004 /* can't exec from filesystem */ +#define MNT_NOSUID 0x00000008 /* don't honor setuid bits on fs */ +#define MNT_NODEV 0x00000010 /* don't interpret special files */ +#define MNT_UNION 0x00000020 /* union with underlying filesystem */ +#define MNT_ASYNC 0x00000040 /* file system written asynchronously */ +#define MNT_NOCOREDUMP 0x00008000 /* don't write core dumps to this FS */ +#define MNT_IGNORE 0x00100000 /* don't show entry in df */ +#define MNT_LOG 0x02000000 /* Use logging */ +#define MNT_NOATIME 0x04000000 /* Never update access times in fs */ +#define MNT_SYMPERM 0x20000000 /* recognize symlink permission */ +#define MNT_NODEVMTIME 0x40000000 /* Never update mod times for devs */ +#define MNT_SOFTDEP 0x80000000 /* Use soft dependencies */ + +/* XXX from machine/param.h */ +#define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ +#define DEV_BSIZE (1 << DEV_BSHIFT) + + +/* FIXME: move? + * some stuff from sys/dirent.h. + */ +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 +/* + * The _DIRENT_ALIGN macro returns the alignment of struct dirent. + * struct direct and struct dirent12 used 4 byte alignment but + * struct dirent uses 8. + */ +/* FIXME: in mfs code sizeof(long) and not d_ino */ +#undef _DIRENT_ALIGN +#define _DIRENT_ALIGN(dp) (sizeof((dp)->d_ino) - 1) +/* + * The _DIRENT_NAMEOFF macro returns the offset of the d_name field in + * struct dirent + */ +#define _DIRENT_NAMEOFF(dp) \ + ((char *)(void *)&(dp)->d_name - (char *)(void *)dp) +/* + * The _DIRENT_RECLEN macro gives the minimum record length which will hold + * a name of size "namlen". This requires the amount of space in struct dirent + * without the d_name field, plus enough space for the name with a terminating + * null byte (namlen+1), rounded up to a the appropriate byte boundary. + */ +/* FIXME */ +#undef _DIRENT_RECLEN +#define _DIRENT_RECLEN(dp, namlen) \ + ((_DIRENT_NAMEOFF(dp) + (namlen) + 1 + _DIRENT_ALIGN(dp)) & \ + ~_DIRENT_ALIGN(dp)) +/* + * The _DIRENT_NEXT macro advances to the next dirent record. + */ +#define _DIRENT_NEXT(dp) ((void *)((char *)(void *)(dp) + (dp)->d_reclen)) + + + + +/* forwards */ +struct puffs_cc; + +struct puffs_getreq; +struct puffs_cred; +struct puffs_newinfo; + +/* paths */ +struct puffs_pathobj { + void *po_path; + size_t po_len; + uint32_t po_hash; +}; + +/* for prefix rename */ +struct puffs_pathinfo { + struct puffs_pathobj *pi_old; + struct puffs_pathobj *pi_new; +}; + +/* describes one segment cached in the kernel */ +struct puffs_kcache { + off_t pkc_start; + off_t pkc_end; + + LIST_ENTRY(puffs_kcache) pkc_entries; +}; + +/* XXX: might disappear from here into a private header */ +struct puffs_node { + off_t pn_size; + int pn_flags; + struct vattr pn_va; + + void *pn_data; /* private data */ + + struct puffs_pathobj pn_po; /* PUFFS_FLAG_BUILDPATH */ + + struct puffs_usermount *pn_mnt; + LIST_ENTRY(puffs_node) pn_entries; + + LIST_HEAD(,puffs_kcache)pn_cacheinfo; /* PUFFS_KFLAG_CACHE */ + + /* MINIX fields */ + char pn_mountpoint; /* true if mounted on */ + int pn_count; /* # times inode used */ + +}; +#define PUFFS_NODE_REMOVED 0x01 /* not on entry list */ + + +struct puffs_usermount; + +/* + * megaXXX: these are values from inside _KERNEL + * need to work on the translation for ALL the necessary values. + */ +#define PUFFS_VNOVAL (-1) + +#define PUFFS_IO_APPEND 0x020 +#define PUFFS_IO_NDELAY 0x100 + +#define PUFFS_VEXEC 01 +#define PUFFS_VWRITE 02 +#define PUFFS_VREAD 04 + +#define PUFFS_FSYNC_DATAONLY 0x0002 +#define PUFFS_FSYNC_CACHE 0x0100 + +/* + * Magic constants + */ +#define PUFFS_CC_STACKSHIFT_DEFAULT 18 + +struct puffs_cn { + struct puffs_kcn *pcn_pkcnp; /* kernel input */ + struct puffs_cred *pcn_cred; /* cred used for lookup */ + + struct puffs_pathobj pcn_po_full; /* PUFFS_FLAG_BUILDPATH */ +}; +#define pcn_nameiop pcn_pkcnp->pkcn_nameiop +#define pcn_flags pcn_pkcnp->pkcn_flags +#define pcn_name pcn_pkcnp->pkcn_name +#define pcn_namelen pcn_pkcnp->pkcn_namelen +#define pcn_consume pcn_pkcnp->pkcn_consume + +/* + * Puffs options to mount + */ +/* kernel */ +#define PUFFSMOPT_NAMECACHE { "namecache", 1, PUFFS_KFLAG_NOCACHE_NAME, 1 } +#define PUFFSMOPT_PAGECACHE { "pagecache", 1, PUFFS_KFLAG_NOCACHE_PAGE, 1 } +#define PUFFSMOPT_CACHE { "cache", 1, PUFFS_KFLAG_NOCACHE, 1 } +#define PUFFSMOPT_ALLOPS { "allops", 0, PUFFS_KFLAG_ALLOPS, 1 } + +/* libpuffs */ +#define PUFFSMOPT_DUMP { "dump", 0, PUFFS_FLAG_OPDUMP, 1 } + +#define PUFFSMOPT_STD \ + PUFFSMOPT_NAMECACHE, \ + PUFFSMOPT_PAGECACHE, \ + PUFFSMOPT_CACHE, \ + PUFFSMOPT_ALLOPS, \ + PUFFSMOPT_DUMP + +extern const struct mntopt puffsmopts[]; /* puffs.c */ + +/* callbacks for operations */ +struct puffs_ops { + int (*puffs_fs_unmount)(struct puffs_usermount *, int); + int (*puffs_fs_statvfs)(struct puffs_usermount *, struct statvfs *); + int (*puffs_fs_sync)(struct puffs_usermount *, int, + const struct puffs_cred *); + int (*puffs_fs_fhtonode)(struct puffs_usermount *, void *, size_t, + struct puffs_newinfo *); + int (*puffs_fs_nodetofh)(struct puffs_usermount *, puffs_cookie_t, + void *, size_t *); + void (*puffs_fs_suspend)(struct puffs_usermount *, int); + + int (*puffs_node_lookup)(struct puffs_usermount *, + puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *); + int (*puffs_node_create)(struct puffs_usermount *, + puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *, + const struct vattr *); + int (*puffs_node_mknod)(struct puffs_usermount *, + puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *, + const struct vattr *); + int (*puffs_node_open)(struct puffs_usermount *, + puffs_cookie_t, int, const struct puffs_cred *); + int (*puffs_node_close)(struct puffs_usermount *, + puffs_cookie_t, int, const struct puffs_cred *); + int (*puffs_node_access)(struct puffs_usermount *, + puffs_cookie_t, int, const struct puffs_cred *); + int (*puffs_node_getattr)(struct puffs_usermount *, + puffs_cookie_t, struct vattr *, const struct puffs_cred *); + int (*puffs_node_setattr)(struct puffs_usermount *, + puffs_cookie_t, const struct vattr *, const struct puffs_cred *); + int (*puffs_node_poll)(struct puffs_usermount *, puffs_cookie_t, int *); + int (*puffs_node_mmap)(struct puffs_usermount *, + puffs_cookie_t, vm_prot_t, const struct puffs_cred *); + int (*puffs_node_fsync)(struct puffs_usermount *, + puffs_cookie_t, const struct puffs_cred *, int, off_t, off_t); + int (*puffs_node_seek)(struct puffs_usermount *, + puffs_cookie_t, off_t, off_t, const struct puffs_cred *); + int (*puffs_node_remove)(struct puffs_usermount *, + puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *); + int (*puffs_node_link)(struct puffs_usermount *, + puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *); + int (*puffs_node_rename)(struct puffs_usermount *, + puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *, + puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *); + int (*puffs_node_mkdir)(struct puffs_usermount *, + puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *, + const struct vattr *); + int (*puffs_node_rmdir)(struct puffs_usermount *, + puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *); + int (*puffs_node_symlink)(struct puffs_usermount *, + puffs_cookie_t, struct puffs_newinfo *, const struct puffs_cn *, + const struct vattr *, + const char *); + int (*puffs_node_readdir)(struct puffs_usermount *, + puffs_cookie_t, struct dirent *, off_t *, size_t *, + const struct puffs_cred *, int *, off_t *, size_t *); + int (*puffs_node_readlink)(struct puffs_usermount *, + puffs_cookie_t, const struct puffs_cred *, char *, size_t *); + int (*puffs_node_reclaim)(struct puffs_usermount *, puffs_cookie_t); + int (*puffs_node_inactive)(struct puffs_usermount *, puffs_cookie_t); + int (*puffs_node_print)(struct puffs_usermount *, puffs_cookie_t); + int (*puffs_node_pathconf)(struct puffs_usermount *, + puffs_cookie_t, int, int *); + int (*puffs_node_advlock)(struct puffs_usermount *, + puffs_cookie_t, void *, int, struct flock *, int); + int (*puffs_node_read)(struct puffs_usermount *, puffs_cookie_t, + uint8_t *, off_t, size_t *, const struct puffs_cred *, int); + int (*puffs_node_write)(struct puffs_usermount *, puffs_cookie_t, + uint8_t *, off_t, size_t *, const struct puffs_cred *, int); + int (*puffs_node_abortop)(struct puffs_usermount *, puffs_cookie_t, + const struct puffs_cn *); + +#if 0 + /* enable next time this structure is changed */ + void *puffs_ops_spare[32]; +#endif +}; + +typedef int (*pu_pathbuild_fn)(struct puffs_usermount *, + const struct puffs_pathobj *, + const struct puffs_pathobj *, size_t, + struct puffs_pathobj *); +typedef int (*pu_pathtransform_fn)(struct puffs_usermount *, + const struct puffs_pathobj *, + const struct puffs_cn *, + struct puffs_pathobj *); +typedef int (*pu_pathcmp_fn)(struct puffs_usermount *, struct puffs_pathobj *, + struct puffs_pathobj *, size_t, int); +typedef void (*pu_pathfree_fn)(struct puffs_usermount *, + struct puffs_pathobj *); +typedef int (*pu_namemod_fn)(struct puffs_usermount *, + struct puffs_pathobj *, struct puffs_cn *); + +typedef void (*pu_errnotify_fn)(struct puffs_usermount *, + uint8_t, int, const char *, puffs_cookie_t); + +typedef void (*pu_prepost_fn)(struct puffs_usermount *); + +typedef struct puffs_node *(*pu_cmap_fn)(struct puffs_usermount *, + puffs_cookie_t); + +enum { + PUFFS_STATE_BEFOREMOUNT, PUFFS_STATE_RUNNING, + PUFFS_STATE_UNMOUNTING, PUFFS_STATE_UNMOUNTED +}; + +#define PUFFS_FLAG_BUILDPATH 0x80000000 /* node paths in pnode */ +#define PUFFS_FLAG_OPDUMP 0x40000000 /* dump all operations */ +#define PUFFS_FLAG_HASHPATH 0x20000000 /* speedup: hash paths */ +#define PUFFS_FLAG_MASK 0xe0000000 + +#define PUFFS_FLAG_KERN(a) ((a) & PUFFS_KFLAG_MASK) +#define PUFFS_FLAG_LIB(a) ((a) & PUFFS_FLAG_MASK) + +/* blocking mode argument */ +#define PUFFSDEV_BLOCK 0 +#define PUFFSDEV_NONBLOCK 1 + +#define PUFFS_STACKSIZE_DEFAULT (1<puffs_##fsornode##_##opname = fsname##_##fsornode##_##opname +#define PUFFSOP_SETFSNOP(ops, opname) \ + (ops)->puffs_fs_##opname = puffs_fsnop_##opname + +PUFFSOP_PROTOS(puffs_null) /* XXX */ + +#define PUFFS_DEVEL_LIBVERSION 34 +#define puffs_init(a,b,c,d,e) \ + _puffs_init(PUFFS_DEVEL_LIBVERSION,a,b,c,d,e) + + +#define PNPATH(pnode) ((pnode)->pn_po.po_path) +#define PNPLEN(pnode) ((pnode)->pn_po.po_len) +#define PCNPATH(pcnode) ((pcnode)->pcn_po_full.po_path) +#define PCNPLEN(pcnode) ((pcnode)->pcn_po_full.po_len) +#define PCNISDOTDOT(pcnode) \ + ((pcnode)->pcn_namelen == 2 && strcmp((pcnode)->pcn_name, "..") == 0) + +#define PUFFS_STORE_DCOOKIE(cp, ncp, off) \ +if (cp) { \ + *((cp)++) = off; \ + (*(ncp))++; \ +} + +/* mainloop */ +typedef void (*puffs_ml_loop_fn)(struct puffs_usermount *); + +/* framebuf stuff */ +struct puffs_framebuf; +typedef int (*puffs_framev_readframe_fn)(struct puffs_usermount *, + struct puffs_framebuf *, + int, int *); +typedef int (*puffs_framev_writeframe_fn)(struct puffs_usermount *, + struct puffs_framebuf *, + int, int *); +typedef int (*puffs_framev_cmpframe_fn)(struct puffs_usermount *, + struct puffs_framebuf *, + struct puffs_framebuf *, + int *); +typedef void (*puffs_framev_fdnotify_fn)(struct puffs_usermount *, int, int); +typedef void (*puffs_framev_gotframe_fn)(struct puffs_usermount *, + struct puffs_framebuf *); +typedef void (*puffs_framev_cb)(struct puffs_usermount *, + struct puffs_framebuf *, + void *, int); +#define PUFFS_FBIO_READ 0x01 +#define PUFFS_FBIO_WRITE 0x02 +#define PUFFS_FBIO_ERROR 0x04 + +#define PUFFS_FBQUEUE_URGENT 0x01 + + +__BEGIN_DECLS + +#define PUFFS_DEFER ((void *)-1) +struct puffs_usermount *_puffs_init(int, struct puffs_ops *, const char *, + const char *, void *, uint32_t); +int puffs_mount(struct puffs_usermount *, const char *, int, void*); +int puffs_exit(struct puffs_usermount *, int); +void puffs_cancel(struct puffs_usermount *, int); +int puffs_mainloop(struct puffs_usermount *); + + +int puffs_getstate(struct puffs_usermount *); +void puffs_setstacksize(struct puffs_usermount *, size_t); + +void puffs_ml_setloopfn(struct puffs_usermount *, puffs_ml_loop_fn); +void puffs_ml_settimeout(struct puffs_usermount *, struct timespec *); + +void puffs_setroot(struct puffs_usermount *, + struct puffs_node *); +struct puffs_node *puffs_getroot(struct puffs_usermount *); +void puffs_setrootinfo(struct puffs_usermount *, + enum vtype, vsize_t, dev_t); + +void *puffs_getspecific(struct puffs_usermount *); +void puffs_setspecific(struct puffs_usermount *, void *); +void puffs_setmaxreqlen(struct puffs_usermount *, size_t); +size_t puffs_getmaxreqlen(struct puffs_usermount *); +void puffs_setfhsize(struct puffs_usermount *, size_t, int); +void puffs_setmntinfo(struct puffs_usermount *, + const char *, const char *); + +void puffs_setncookiehash(struct puffs_usermount *, int); + +struct puffs_pathobj *puffs_getrootpathobj(struct puffs_usermount *); + +struct puffs_node *puffs_pn_new(struct puffs_usermount *, void *); +void puffs_pn_remove(struct puffs_node *); +void puffs_pn_put(struct puffs_node *); +struct vattr *puffs_pn_getvap(struct puffs_node *); +void * puffs_pn_getpriv(struct puffs_node *); +void puffs_pn_setpriv(struct puffs_node *, void *); +struct puffs_pathobj *puffs_pn_getpo(struct puffs_node *); +struct puffs_usermount *puffs_pn_getmnt(struct puffs_node *); + +void puffs_newinfo_setcookie(struct puffs_newinfo *, puffs_cookie_t); +void puffs_newinfo_setvtype(struct puffs_newinfo *, enum vtype); +void puffs_newinfo_setsize(struct puffs_newinfo *, voff_t); +void puffs_newinfo_setrdev(struct puffs_newinfo *, dev_t); + +void *puffs_pn_getmntspecific(struct puffs_node *); + +typedef void * (*puffs_nodewalk_fn)(struct puffs_usermount *, + struct puffs_node *, void *); +void *puffs_pn_nodewalk(struct puffs_usermount *, + puffs_nodewalk_fn, void *); +void *puffs_pn_nodeprint(struct puffs_usermount *pu, + struct puffs_node *pn, void *arg); +void puffs_pn_nodeprintall(struct puffs_usermount *pu); + +void puffs_setvattr(struct vattr *, const struct vattr *); +void puffs_vattr_null(struct vattr *); + +void puffs_null_setops(struct puffs_ops *); + +int puffs_dispatch_create(struct puffs_usermount *, + struct puffs_framebuf *, + struct puffs_cc **); +int puffs_dispatch_exec(struct puffs_cc *, + struct puffs_framebuf **); + +/* + * generic/dummy routines applicable for some file systems + */ +int puffs_fsnop_unmount(struct puffs_usermount *, int); +int puffs_fsnop_statvfs(struct puffs_usermount *, struct statvfs *); +void puffs_zerostatvfs(struct statvfs *); +int puffs_fsnop_sync(struct puffs_usermount *, int waitfor, + const struct puffs_cred *); + +int puffs_genfs_node_getattr(struct puffs_usermount *, puffs_cookie_t, + struct vattr *, const struct puffs_cred *); +int puffs_genfs_node_reclaim(struct puffs_usermount *, puffs_cookie_t); + +/* + * Subroutine stuff + */ + +int puffs_gendotdent(struct dirent **, ino_t, int, size_t *); +int puffs_nextdent(struct dirent **, const char *, ino_t, + uint8_t, size_t *); +int puffs_vtype2dt(enum vtype); +enum vtype puffs_mode2vt(mode_t); +void puffs_stat2vattr(struct vattr *va, const struct stat *); +mode_t puffs_addvtype2mode(mode_t, enum vtype); + + +/* + * credentials & permissions + */ + +/* Credential fetch */ +int puffs_cred_getuid(const struct puffs_cred *, uid_t *); +int puffs_cred_getgid(const struct puffs_cred *, gid_t *); +int puffs_cred_getgroups(const struct puffs_cred *, gid_t *, short *); + +/* Credential check */ +bool puffs_cred_isuid(const struct puffs_cred *, uid_t); +bool puffs_cred_hasgroup(const struct puffs_cred *, gid_t); +bool puffs_cred_isregular(const struct puffs_cred *); +bool puffs_cred_iskernel(const struct puffs_cred *); +bool puffs_cred_isfs(const struct puffs_cred *); +bool puffs_cred_isjuggernaut(const struct puffs_cred *); + +/* misc */ +int puffs_access(enum vtype, mode_t, uid_t, gid_t, mode_t, + const struct puffs_cred *); +int puffs_access_chown(uid_t, gid_t, uid_t, gid_t, + const struct puffs_cred *); +int puffs_access_chmod(uid_t, gid_t, enum vtype, mode_t, + const struct puffs_cred *); +int puffs_access_times(uid_t, gid_t, mode_t, int, + const struct puffs_cred *); + + +/* + * Call Context interfaces relevant for user. + */ + +void puffs_cc_yield(struct puffs_cc *); +void puffs_cc_continue(struct puffs_cc *); +void puffs_cc_schedule(struct puffs_cc *); +int puffs_cc_getcaller(struct puffs_cc *,pid_t *,lwpid_t *); +struct puffs_cc *puffs_cc_getcc(struct puffs_usermount *); + +/* + * Flushing / invalidation routines + */ + +int puffs_inval_namecache_dir(struct puffs_usermount *, puffs_cookie_t); +int puffs_inval_namecache_all(struct puffs_usermount *); + +int puffs_inval_pagecache_node(struct puffs_usermount *, puffs_cookie_t); +int puffs_inval_pagecache_node_range(struct puffs_usermount *, + puffs_cookie_t, off_t, off_t); +int puffs_flush_pagecache_node(struct puffs_usermount *, puffs_cookie_t); +int puffs_flush_pagecache_node_range(struct puffs_usermount *, + puffs_cookie_t, off_t, off_t); + +/* + * Path constructicons + */ + +int puffs_stdpath_buildpath(struct puffs_usermount *, + const struct puffs_pathobj *, + const struct puffs_pathobj *, size_t, + struct puffs_pathobj *); +int puffs_stdpath_cmppath(struct puffs_usermount *, struct puffs_pathobj *, + struct puffs_pathobj *, size_t, int); +void puffs_stdpath_freepath(struct puffs_usermount *,struct puffs_pathobj *); + +void *puffs_path_walkcmp(struct puffs_usermount *, + struct puffs_node *, void *); +void *puffs_path_prefixadj(struct puffs_usermount *, + struct puffs_node *, void *); +int puffs_path_pcnbuild(struct puffs_usermount *, + struct puffs_cn *, void *); +void puffs_path_buildhash(struct puffs_usermount *, struct puffs_pathobj *); +void puffs_set_pathbuild(struct puffs_usermount *, pu_pathbuild_fn); void puffs_set_pathtransform(struct puffs_usermount *, pu_pathtransform_fn); +void puffs_set_pathcmp(struct puffs_usermount *, pu_pathcmp_fn); +void puffs_set_pathfree(struct puffs_usermount *, pu_pathfree_fn); +void puffs_set_namemod(struct puffs_usermount *, pu_namemod_fn); + +void puffs_set_errnotify(struct puffs_usermount *, pu_errnotify_fn); +void puffs_set_prepost(struct puffs_usermount *, + pu_prepost_fn, pu_prepost_fn); +void puffs_set_cmap(struct puffs_usermount *, pu_cmap_fn); + +/* + * Suspension + */ + +int puffs_fs_suspend(struct puffs_usermount *); + +/* + * Frame buffering + */ + +void puffs_framev_init(struct puffs_usermount *, + puffs_framev_readframe_fn, + puffs_framev_writeframe_fn, + puffs_framev_cmpframe_fn, + puffs_framev_gotframe_fn, + puffs_framev_fdnotify_fn); + +struct puffs_framebuf *puffs_framebuf_make(void); +void puffs_framebuf_destroy(struct puffs_framebuf *); +int puffs_framebuf_dup(struct puffs_framebuf *, + struct puffs_framebuf **); +void puffs_framebuf_recycle(struct puffs_framebuf *); +int puffs_framebuf_reserve_space(struct puffs_framebuf *, + size_t); + +int puffs_framebuf_putdata(struct puffs_framebuf *, const void *, size_t); +int puffs_framebuf_putdata_atoff(struct puffs_framebuf *, size_t, + const void *, size_t); +int puffs_framebuf_getdata(struct puffs_framebuf *, void *, size_t); +int puffs_framebuf_getdata_atoff(struct puffs_framebuf *, size_t, + void *, size_t); + +size_t puffs_framebuf_telloff(struct puffs_framebuf *); +size_t puffs_framebuf_tellsize(struct puffs_framebuf *); +size_t puffs_framebuf_remaining(struct puffs_framebuf *); +int puffs_framebuf_seekset(struct puffs_framebuf *, size_t); +int puffs_framebuf_getwindow(struct puffs_framebuf *, size_t, + void **, size_t *); + +int puffs_framev_enqueue_cc(struct puffs_cc *, int, + struct puffs_framebuf *, int); +int puffs_framev_enqueue_cb(struct puffs_usermount *, int, + struct puffs_framebuf *, + puffs_framev_cb, void *, int); +int puffs_framev_enqueue_justsend(struct puffs_usermount *, int, + struct puffs_framebuf *, int, int); +int puffs_framev_enqueue_directreceive(struct puffs_cc *, int, + struct puffs_framebuf *, int); +int puffs_framev_enqueue_directsend(struct puffs_cc *, int, + struct puffs_framebuf *, int); +int puffs_framev_enqueue_waitevent(struct puffs_cc *, int, int *); +int puffs_framev_framebuf_ccpromote(struct puffs_framebuf *, + struct puffs_cc *); + +int puffs_framev_addfd(struct puffs_usermount *, int, int); +int puffs_framev_enablefd(struct puffs_usermount *, int, int); +int puffs_framev_disablefd(struct puffs_usermount *, int, int); +int puffs_framev_removefd(struct puffs_usermount *, int, int); +void puffs_framev_removeonclose(struct puffs_usermount *, int, int); +void puffs_framev_unmountonclose(struct puffs_usermount *, int, int); + +__END_DECLS + +#endif /* _PUFFS_H_ */ diff --git a/lib/libpuffs/puffs_cc.3 b/lib/libpuffs/puffs_cc.3 new file mode 100644 index 000000000..dd3d7ff5b --- /dev/null +++ b/lib/libpuffs/puffs_cc.3 @@ -0,0 +1,93 @@ +.\" $NetBSD: puffs_cc.3,v 1.12.4.1 2009/02/24 03:45:56 snj Exp $ +.\" +.\" Copyright (c) 2007, 2008 Antti Kantee. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd January 28, 2008 +.Dt PUFFS_CC 3 +.Os +.Sh NAME +.Nm puffs_cc +.Nd puffs continuation routines +.Sh LIBRARY +.Lb libpuffs +.Sh SYNOPSIS +.In puffs.h +.Ft void +.Fn puffs_cc_yield "struct puffs_cc *pcc" +.Ft void +.Fn puffs_cc_continue "struct puffs_cc *pcc" +.Ft void +.Fn puffs_cc_schedule "struct puffs_cc *pcc" +.Ft struct puffs_cc * +.Fn puffs_cc_getcc "struct puffs_usermount *pu" +.Sh DESCRIPTION +These routines are used for the cooperative multitasking suite present +in puffs. +.Pp +.Bl -tag -width xxxx +.It Fn puffs_cc_yield "pcc" +Suspend and save the current execution context and return control +to the previous point. +In practice, from the file system author perspective, control returns +back to where either the mainloop or where +.Fn puffs_dispatch_exec +was called from. +.It Fn puffs_cc_continue pcc +Will suspend current execution and return control to where it was +before before calling +.Fn puffs_cc_yield . +This is rarely called directly but rather through +.Fn puffs_dispatch_exec . +.It Fn puffs_cc_schedule "pcc" +Schedule a continuation. +As opposed to +.Fn puffs_cc_continue +this call returns immediately. +.Fa pcc +will be scheduled sometime in the future. +.It Fn puffs_cc_getcc "pu" +Returns the current pcc or +.Dv NULL +if this is the main thread. +.Em NOTE : +The argument +.Ar pu +will most likely disappear at some point. +.El +.Pp +Before calling +.Fn puffs_cc_yield +a file system will typically want to record some cookie value into its +own internal bookkeeping. +This cookie should be hooked to the +.Va pcc +so that the correct continuation can be continued when the event it +was waiting for triggers. Alternatively, the +.Xr puffs_framebuf 3 +framework and +.Fn puffs_mainloop +can be used for handling this automatically. +.Sh SEE ALSO +.Xr puffs 3 , +.Xr puffs_framebuf 3 diff --git a/lib/libpuffs/puffs_cred.3 b/lib/libpuffs/puffs_cred.3 new file mode 100644 index 000000000..e4264e45c --- /dev/null +++ b/lib/libpuffs/puffs_cred.3 @@ -0,0 +1,167 @@ +.\" $NetBSD: puffs_cred.3,v 1.3.12.1 2009/02/24 03:45:56 snj Exp $ +.\" +.\" Copyright (c) 2007 Antti Kantee. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd October 18, 2007 +.Dt PUFFS_CRED 3 +.Os +.Sh NAME +.Nm puffs_cred +.Nd puffs credential and access control routines +.Sh LIBRARY +.Lb libpuffs +.Sh SYNOPSIS +.In puffs.h +.Ft int +.Fn puffs_cred_getuid "const struct puffs_cred *pcr" "uid_t *uid +.Ft int +.Fn puffs_cred_getgid "const struct puffs_cred *pcr" "gid_t *gid" +.Ft int +.Fo puffs_cred_getgroups +.Fa "const struct puffs_cred *pcr" "gid_t *gids" "short *ngids" +.Fc +.Pp +.Ft bool +.Fn puffs_cred_isuid "const struct puffs_cred *pcr" "uid_t uid" +.Ft bool +.Fn puffs_cred_hasgroup "const struct puffs_cred *pcr" "gid_t gid" +.Ft bool +.Fn puffs_cred_iskernel "const struct puffs_cred *pcr" +.Ft bool +.Fn puffs_cred_isfs "const struct puffs_cred *pcr" +.Ft bool +.Fn puffs_cred_isjuggernaut "const struct puffs_cred *pcr" +.Pp +.Ft int +.Fo puffs_access +.Fa "enum vtype type" "mode_t file_mode" "uid_t owner" "gid_t group" +.Fa "mode_t access_mode" "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_access_chown +.Fa "uid_t owner" "gid_t group" "uid_t newowner" "gid_t newgroup" +.Fa "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_access_chmod +.Fa "uid_t owner" "gid_t group" "enum vtype type" "mode_t newmode" +.Fa "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_access_times +.Fa "uid_t owner" "gid_t group" "mode_t file_mode" "int va_utimes_null" +.Fa "const struct puffs_cred *pcr" +.Fc +.Sh DESCRIPTION +These functions can be used to check operation credentials and perform +access control. +The structure +.Vt struct puffs_cred +can contain two types of credentials: ones belonging to users and +ones belonging to the kernel. +The latter is further divided into generic kernel credentials and +file system credentials. +The general rule is that these should be treated as more powerful +than superuser credentials, but the file system is free to treat +them as it sees fit. +.Ss Credentials +The +.Fn puffs_cred_get +family of functions fetch the uid or gid(s) from the given credential +cookie. +They return 0 for success or \-1 for an error and set +.Va errno . +An error happens when the credentials represent kernel or file system +credentials and do not contain an uid or gid(s). +.Pp +For +.Fn puffs_cred_getgroups , +the argument +.Fa gids +should point to an array with room for +.Fa *ngids +elements. +.Pp +The +.Fn puffs_cred_is +family of functions return 1 if the truth value of the function for +.Fa pcr +is true and 0 if it is false. +The function +.Fn puffs_cred_isjuggernaut +is true if +.Fa pcr +describes superuser, kernel or file system credentials. +.Ss Access Control +To help the programmers task of emulating normal kernel file system +access control semantics, several helper functions are provided to +check if access is allowed. +They return 0 if access should be permitted or an errno value to +indicate that access should be denied with the returned value. +.Pp +.Fn puffs_access +is equivalent to the kernel +.Fn vaccess +function. +The arguments specify current information of the file to be +tested with the exception of +.Fa access_mode , +which is a combination of +.Dv PUFFS_VREAD , +.Dv PUFFS_VWRITE +and +.Dv PUFFS_VEXEC +indicating the desired file access mode. +.Pp +The rest of the functions provide UFS semantics for operations. +.Fn puffs_access_chown +checks if it is permissible to chown a file with the current uid +and gid to the new uid and gid with the credentials of +.Fa pcr . +.Pp +.Fn puffs_access_chmod +checks against permission to chmod a file of type +.Fa type +to the mode +.Fa newmode . +.Pp +Finally, +.Fn puffs_access_times +checks if it is allowable to update the timestamps of a file. +The argument +.Fa va_utimes_null +signals if the flags +.Dv VA_UTIMES_NULL +was set in +.Fa va_vaflags +of +.Va struct vattr . +If coming from a path where this information is unavailable, passing +0 as this argument restricts the permission of modification to the +owner and superuser. +Otherwise the function checks for write permissions to the node and +returns success if the caller has write permissions. +.Sh SEE ALSO +.Xr puffs 3 , +.Xr vnode 9 diff --git a/lib/libpuffs/puffs_msgif.h b/lib/libpuffs/puffs_msgif.h new file mode 100644 index 000000000..092e1dd27 --- /dev/null +++ b/lib/libpuffs/puffs_msgif.h @@ -0,0 +1,261 @@ +/* $NetBSD: puffs_msgif.h,v 1.65.20.2 2009/12/14 19:36:57 sborrill Exp $ */ + +/* + * Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved. + * + * Development of this software was supported by the + * Google Summer of Code program and the Ulla Tuominen Foundation. + * The Google SoC project was mentored by Bill Studenmund. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FS_PUFFS_PUFFS_MSGIF_H_ +#define _FS_PUFFS_PUFFS_MSGIF_H_ + +#include +#include +#include +#include +#include + +#include + + +#define PUFFSOP_VFS 0x01 /* kernel-> */ +#define PUFFSOP_VN 0x02 /* kernel-> */ +#define PUFFSOP_CACHE 0x03 /* only kernel-> */ +#define PUFFSOP_ERROR 0x04 /* only kernel-> */ +#define PUFFSOP_FLUSH 0x05 /* ->kernel */ +#define PUFFSOP_SUSPEND 0x06 /* ->kernel */ + +#define PUFFSOPFLAG_FAF 0x10 /* fire-and-forget */ +#define PUFFSOPFLAG_ISRESPONSE 0x20 /* req is actually a resp */ + +#define PUFFSOP_OPCMASK 0x07 +#define PUFFSOP_OPCLASS(a) ((a) & PUFFSOP_OPCMASK) +#define PUFFSOP_WANTREPLY(a) (((a) & PUFFSOPFLAG_FAF) == 0) + +/* XXX: we don't need everything */ +enum { + PUFFS_VFS_MOUNT, PUFFS_VFS_START, PUFFS_VFS_UNMOUNT, + PUFFS_VFS_ROOT, PUFFS_VFS_STATVFS, PUFFS_VFS_SYNC, + PUFFS_VFS_VGET, PUFFS_VFS_FHTOVP, PUFFS_VFS_VPTOFH, + PUFFS_VFS_INIT, PUFFS_VFS_DONE, PUFFS_VFS_SNAPSHOT, + PUFFS_VFS_EXTATTCTL, PUFFS_VFS_SUSPEND +}; +#define PUFFS_VFS_MAX PUFFS_VFS_EXTATTCTL + +/* moreXXX: we don't need everything here either */ +enum { + PUFFS_VN_LOOKUP, PUFFS_VN_CREATE, PUFFS_VN_MKNOD, + PUFFS_VN_OPEN, PUFFS_VN_CLOSE, PUFFS_VN_ACCESS, + PUFFS_VN_GETATTR, PUFFS_VN_SETATTR, PUFFS_VN_READ, + PUFFS_VN_WRITE, PUFFS_VN_IOCTL, PUFFS_VN_FCNTL, + PUFFS_VN_POLL, PUFFS_VN_KQFILTER, PUFFS_VN_REVOKE, + PUFFS_VN_MMAP, PUFFS_VN_FSYNC, PUFFS_VN_SEEK, + PUFFS_VN_REMOVE, PUFFS_VN_LINK, PUFFS_VN_RENAME, + PUFFS_VN_MKDIR, PUFFS_VN_RMDIR, PUFFS_VN_SYMLINK, + PUFFS_VN_READDIR, PUFFS_VN_READLINK, PUFFS_VN_ABORTOP, + PUFFS_VN_INACTIVE, PUFFS_VN_RECLAIM, PUFFS_VN_LOCK, + PUFFS_VN_UNLOCK, PUFFS_VN_BMAP, PUFFS_VN_STRATEGY, + PUFFS_VN_PRINT, PUFFS_VN_ISLOCKED, PUFFS_VN_PATHCONF, + PUFFS_VN_ADVLOCK, PUFFS_VN_LEASE, PUFFS_VN_WHITEOUT, + PUFFS_VN_GETPAGES, PUFFS_VN_PUTPAGES, PUFFS_VN_GETEXTATTR, + PUFFS_VN_LISTEXTATTR, PUFFS_VN_OPENEXTATTR, PUFFS_VN_DELETEEXTATTR, + PUFFS_VN_SETEXTATTR +}; +#define PUFFS_VN_MAX PUFFS_VN_SETEXTATTR + +/* + * These signal invalid parameters the file system returned. + */ +enum { + PUFFS_ERR_MAKENODE, PUFFS_ERR_LOOKUP, PUFFS_ERR_READDIR, + PUFFS_ERR_READLINK, PUFFS_ERR_READ, PUFFS_ERR_WRITE, + PUFFS_ERR_VPTOFH, PUFFS_ERR_ERROR +}; +#define PUFFS_ERR_MAX PUFFS_ERR_VPTOFH + +#define PUFFSDEVELVERS 0x80000000 +#define PUFFSVERSION 26 +#define PUFFSNAMESIZE 32 + +#define PUFFS_TYPEPREFIX "puffs|" + +#if 0 +#define PUFFS_TYPELEN (_VFS_NAMELEN - (sizeof(PUFFS_TYPEPREFIX)+1)) +#define PUFFS_NAMELEN (_VFS_MNAMELEN-1) +#endif + +/* + * Just a weak typedef for code clarity. Additionally, we have a + * more appropriate vanity type for puffs: + * it should be croissant, not cookie. + */ +typedef void *puffs_cookie_t; +typedef puffs_cookie_t puffs_croissant_t; + +/* FIXME: move? */ +typedef off_t voff_t; + +/* other netbsd stuff + * FIXME: move to some other place? + */ +typedef int32_t lwpid_t; /* LWP id */ +typedef unsigned long vsize_t; +typedef int vm_prot_t; +typedef uint64_t u_quad_t; /* quads */ + + +/* FIXME: from sys/vnode.h, which is commented. */ +/* + * Vnode attributes. A field value of VNOVAL represents a field whose value + * is unavailable (getattr) or which is not to be changed (setattr). + */ +enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD }; +struct vattr { + enum vtype va_type; /* vnode type (for create) */ + mode_t va_mode; /* files access mode and type */ + nlink_t va_nlink; /* number of references to file */ + uid_t va_uid; /* owner user id */ + gid_t va_gid; /* owner group id */ + long va_fsid; /* file system id (dev for now) */ + ino_t va_fileid; /* file id */ + u_quad_t va_size; /* file size in bytes */ + long va_blocksize; /* blocksize preferred for i/o */ + struct timespec va_atime; /* time of last access */ + struct timespec va_mtime; /* time of last modification */ + struct timespec va_ctime; /* time file changed */ + struct timespec va_birthtime; /* time file created */ + u_long va_gen; /* generation number of file */ + u_long va_flags; /* flags defined for file */ + dev_t va_rdev; /* device the special file represents */ + u_quad_t va_bytes; /* bytes of disk space held by file */ + u_quad_t va_filerev; /* file modification number */ + u_int va_vaflags; /* operations flags, see below */ + long va_spare; /* remain quad aligned */ +}; + +struct puffs_kargs { + unsigned int pa_vers; + int pa_fd; + + uint32_t pa_flags; + + size_t pa_maxmsglen; + int pa_nhashbuckets; + + size_t pa_fhsize; + int pa_fhflags; + + puffs_cookie_t pa_root_cookie; + enum vtype pa_root_vtype; + voff_t pa_root_vsize; + dev_t pa_root_rdev; + + struct statvfs pa_svfsb; + + char pa_typename[NAME_MAX + 1]; + char pa_mntfromname[NAME_MAX + 1]; + + uint8_t pa_vnopmask[PUFFS_VN_MAX]; +}; + + + +#define PUFFS_KFLAG_NOCACHE_NAME 0x01 /* don't use name cache */ +#define PUFFS_KFLAG_NOCACHE_PAGE 0x02 /* don't use page cache */ +#define PUFFS_KFLAG_NOCACHE 0x03 /* no cache whatsoever */ +#define PUFFS_KFLAG_ALLOPS 0x04 /* ignore pa_vnopmask */ +#define PUFFS_KFLAG_WTCACHE 0x08 /* write-through page cache */ +#define PUFFS_KFLAG_IAONDEMAND 0x10 /* inactive only on demand */ +#define PUFFS_KFLAG_LOOKUP_FULLPNBUF 0x20 /* full pnbuf in lookup */ +#define PUFFS_KFLAG_MASK 0x3f + +#define PUFFS_FHFLAG_DYNAMIC 0x01 +#define PUFFS_FHFLAG_NFSV2 0x02 +#define PUFFS_FHFLAG_NFSV3 0x04 +#define PUFFS_FHFLAG_PROTOMASK 0x06 +#define PUFFS_FHFLAG_PASSTHROUGH 0x08 +#define PUFFS_FHFLAG_MASK 0x0f + +#define PUFFS_FHSIZE_MAX 1020 /* XXX: FHANDLE_SIZE_MAX - 4 */ + +#define PUFFS_SETBACK_INACT_N1 0x01 /* set VOP_INACTIVE for node 1 */ +#define PUFFS_SETBACK_INACT_N2 0x02 /* set VOP_INACTIVE for node 2 */ +#define PUFFS_SETBACK_NOREF_N1 0x04 /* set pn PN_NOREFS for node 1 */ +#define PUFFS_SETBACK_NOREF_N2 0x08 /* set pn PN_NOREFS for node 2 */ +#define PUFFS_SETBACK_MASK 0x0f + +#define PUFFS_INVAL_NAMECACHE_NODE 0 +#define PUFFS_INVAL_NAMECACHE_DIR 1 +#define PUFFS_INVAL_NAMECACHE_ALL 2 +#define PUFFS_INVAL_PAGECACHE_NODE_RANGE 3 +#define PUFFS_FLUSH_PAGECACHE_NODE_RANGE 4 + +/* keep this for now */ +#define PUFFSREQSIZEOP _IOR ('p', 1, size_t) + +#define PUFFCRED_TYPE_UUC 1 +#define PUFFCRED_TYPE_INTERNAL 2 +#define PUFFCRED_CRED_NOCRED 1 +#define PUFFCRED_CRED_FSCRED 2 + +/* + * 2*MAXPHYS is the max size the system will attempt to copy, + * else treated as garbage + */ +#define PUFFS_MSG_MAXSIZE 2*MAXPHYS +#define PUFFS_MSGSTRUCT_MAX 4096 /* XXX: approxkludge */ + +#define PUFFS_TOMOVE(a,b) (MIN((a), b->pmp_msg_maxsize - PUFFS_MSGSTRUCT_MAX)) + +/* puffs struct componentname built by kernel */ +struct puffs_kcn { + /* args */ + uint32_t pkcn_nameiop; /* namei operation */ + uint32_t pkcn_flags; /* flags */ + + char pkcn_name[MAXPATHLEN]; /* nulterminated path component */ + size_t pkcn_namelen; /* current component length */ + size_t pkcn_consume; /* IN: extra chars server ate */ +}; + +/* + * Credentials for an operation. Can be either struct uucred for + * ops called from a credential context or NOCRED/FSCRED for ops + * called from within the kernel. It is up to the implementation + * if it makes a difference between these two and the super-user. + */ +struct puffs_kcred { + struct uucred pkcr_uuc; + uint8_t pkcr_type; + uint8_t pkcr_internal; +}; +#define PUFFCRED_TYPE_UUC 1 +#define PUFFCRED_TYPE_INTERNAL 2 +#define PUFFCRED_CRED_NOCRED 1 +#define PUFFCRED_CRED_FSCRED 2 + + +#endif /* _FS_PUFFS_PUFFS_MSGIF_H_ */ diff --git a/lib/libpuffs/puffs_node.3 b/lib/libpuffs/puffs_node.3 new file mode 100644 index 000000000..783e82f62 --- /dev/null +++ b/lib/libpuffs/puffs_node.3 @@ -0,0 +1,102 @@ +.\" $NetBSD: puffs_node.3,v 1.3.12.1 2009/02/24 03:45:56 snj Exp $ +.\" +.\" Copyright (c) 2007 Antti Kantee. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd June 24, 2007 +.Dt PUFFS_NODE 3 +.Os +.Sh NAME +.Nm puffs_node +.Nd puffs node routines +.Sh LIBRARY +.Lb libpuffs +.Sh SYNOPSIS +.In puffs.h +.Ft struct puffs_node * +.Fn puffs_pn_new "struct puffs_usermount *pu" "void *priv" +.Ft void * +.Fo puffs_nodewalk_fn +.Fa "struct puffs_usermount *pu" "struct puffs_node *pn" "void *arg" +.Fc +.Ft void * +.Fo puffs_pn_nodewalk +.Fa "struct puffs_usermount *pu" "puffs_nodewalk_fn nwfn" "void *arg" +.Fc +.Ft void +.Fn puffs_pn_remove "struct puffs_node *pn" +.Ft void +.Fn puffs_pn_put "struct puffs_node *pn" +.Sh DESCRIPTION +.Bl -tag -width xxxx +.It Fn puffs_pn_new pu priv +Create a new node and attach it to the mountpoint +.Ar pu . +The argument +.Ar priv +can be used for associating file system specific data with the new +node and will not be accessed by puffs. +.It Fn puffs_nodewalk_fn pu pn arg +A callback for +.Fn puffs_nodewalk . +The list of nodes is iterated in the argument +.Ar pn +and the argument +.Ar arg +is the argument given to +.Fn puffs_nodewalk . +.It Fn puffs_nodewalk pu nwfn arg +Walk all nodes associted with the mountpoint +.Ar pu +and call +.Fn nwfn +for them. +The walk is aborted if +.Fn puffs_nodewalk_fn +returns a value which is not +.Dv NULL . +This value is also returned this function. +In case the whole set of nodes is traversed, +.Dv NULL +is returned. +This function is useful for example in handling the +.Fn puffs_fs_sync +callback, when cached data for every node should be flushed to stable +storage. +.It Fn puffs_pn_remove pn +Signal that a node has been removed from the file system, but do not +yet release resources associated with the node. +This will prevent the nodewalk functions from accessing the node. +If necessary, this is usually called from +.Fn puffs_node_remove +and +.Fn puffs_node_rmdir . +.It Fn puffs_pn_put pn +Free all resources associated with node +.Ar pn . +This is typically called from +.Fn puffs_node_reclaim . +.Pp +.El +.Sh SEE ALSO +.Xr puffs 3 diff --git a/lib/libpuffs/puffs_ops.3 b/lib/libpuffs/puffs_ops.3 new file mode 100644 index 000000000..43ceeccae --- /dev/null +++ b/lib/libpuffs/puffs_ops.3 @@ -0,0 +1,763 @@ +.\" $NetBSD: puffs_ops.3,v 1.21.4.2 2009/04/12 02:24:16 snj Exp $ +.\" +.\" Copyright (c) 2007 Antti Kantee. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd December 16, 2007 +.Dt PUFFS_OPS 3 +.Os +.Sh NAME +.Nm puffs_ops +.Nd puffs callback operations +.Sh LIBRARY +.Lb libpuffs +.Sh SYNOPSIS +.In puffs.h +.Ft int +.Fo puffs_fs_statvfs +.Fa "struct puffs_usermount *pu" "struct statvfs *sbp" +.Fc +.Ft int +.Fo puffs_fs_sync +.Fa "struct puffs_usermount *pu" "int waitfor" "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_fs_fhtonode +.Fa "struct puffs_usermount *pu" "void *fid" "size_t fidsize" +.Fa "struct puffs_newinfo *pni" +.Fc +.Ft int +.Fo puffs_fs_nodetofh +.Fa "struct puffs_usermount *pu" "puffs_cooie_t cookie" "void *fid" +.Fa "size_t *fidsize" +.Fc +.Ft void +.Fn puffs_fs_suspend "struct puffs_usermount *pu" "int status" +.Ft int +.Fo puffs_fs_unmount +.Fa "struct puffs_usermount *pu" "int flags" +.Fc +.Ft int +.Fo puffs_node_lookup +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" +.Fa "struct puffs_newinfo *pni" "const struct puffs_cn *pcn" +.Fc +.Ft int +.Fo puffs_node_create +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" +.Fa "struct puffs_newinfo *pni" "const struct puffs_cn *pcn" +.Fa "const struct vattr *vap" +.Fc +.Ft int +.Fo puffs_node_mknod +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" +.Fa "struct puffs_newinfo *pni" "const struct puffs_cn *pcn" +.Fa "const struct vattr *vap" +.Fc +.Ft int +.Fo puffs_node_open +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int mode" +.Fa "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_node_close +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int flags" +.Fa "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_node_access +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int mode" +.Fa "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_node_getattr +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "struct vattr *vap" +.Fa "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_node_setattr +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "const struct vattr *vap" +.Fa "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_node_poll +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int *events" +.Fc +.Ft int +.Fo puffs_node_mmap +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "int flags" +.Fa "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_node_fsync +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" +.Fa "const struct puffs_cred *pcr" "int flags" "off_t offlo" "off_t offhi" +.Fc +.Ft int +.Fo puffs_node_seek +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "off_t oldoff" +.Fa "off_t newoff" "const struct puffs_cred *pcr" +.Fc +.Ft int +.Fo puffs_node_remove +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "puffs_cookie_t targ" +.Fa "const struct puffs_cn *pcn" +.Fc +.Ft int +.Fo puffs_node_link +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "puffs_cookie_t targ" +.Fa "const struct puffs_cn *pcn" +.Fc +.Ft int +.Fo puffs_node_rename +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "puffs_cookie_t src" +.Fa "const struct puffs_cn *pcn_src" "puffs_cookie_t targ_dir" +.Fa "puffs_cookie_t targ" "const struct puffs_cn *pcn_targ" +.Fc +.Ft int +.Fo puffs_node_mkdir +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" +.Fa "struct puffs_newinfo *pni" "const struct puffs_cn *pcn" +.Fa "const struct vattr *vap" +.Fc +.Ft int +.Fo puffs_node_rmdir +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "puffs_cookie_t targ" +.Fa "const struct puffs_cn *pcn" +.Fc +.Ft int +.Fo puffs_node_readdir +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "struct dirent *dent" +.Fa "off_t *readoff" "size_t *reslen" "const struct puffs_cred *pcr" +.Fa "int *eofflag" "off_t *cookies" "size_t *ncookies" +.Fc +.Ft int +.Fo puffs_node_symlink +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" +.Fa "struct puffs_newinfo *pni" +.Fa "const struct puffs_cn *pcn_src" "const struct vattr *vap" +.Fa "const char *link_target" +.Fc +.Ft int +.Fo puffs_node_readlink +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" +.Fa "const struct puffs_cred *pcr" "char *link" "size_t *linklen" +.Fc +.Ft int +.Fo puffs_node_read +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "uint8_t *buf" +.Fa "off_t offset" "size_t *resid" "const struct puffs_cred *pcr" "int ioflag" +.Fc +.Ft int +.Fo puffs_node_write +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" "uint8_t *buf" +.Fa "off_t offset" "size_t *resid" "const struct puffs_cred *pcr" "int ioflag" +.Fc +.Ft int +.Fn puffs_node_print "struct puffs_usermount *pu" "puffs_cookie_t opc" +.Ft int +.Fo puffs_node_reclaim +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" +.Fc +.Ft int +.Fo puffs_node_inactive +.Fa "struct puffs_usermount *pu" "puffs_cookie_t opc" +.Fc +.Ft void +.Fn puffs_setback "struct puffs_cc *pcc" "int op" +.Ft void +.Fn puffs_newinfo_setcookie "struct puffs_newinfo *pni" "puffs_cookie_t cookie" +.Ft void +.Fn puffs_newinfo_setvtype "struct puffs_newinfo *pni" "enum vtype vtype" +.Ft void +.Fn puffs_newinfo_setsize "struct puffs_newinfo *pni" "voff_t size" +.Ft void +.Fn puffs_newinfo_setrdev "struct puffs_newinfo *pni" "dev_t rdev" +.Sh DESCRIPTION +The operations +.Nm puffs +requires to function can be divided into two categories: file system +callbacks and node callbacks. +The former affect the entire file system while the latter are targeted +at a file or a directory and a file. +They are roughly equivalent to the vfs and vnode operations in the +kernel. +.Pp +All callbacks can be prototyped with the file system name and operation +name using the macro +.Fn PUFFSOP_PROTOS fsname . +.Ss File system callbacks (puffs_fs) +.Bl -tag -width xxxx +.It Fn puffs_fs_statvfs "pu" "sbp" +The following fields of the argument +.Fa sbp +need to be filled: +.Bd -literal + * unsigned long f_bsize; file system block size + * unsigned long f_frsize; fundamental file system block size + * fsblkcnt_t f_blocks; number of blocks in file system, + * (in units of f_frsize) + * + * fsblkcnt_t f_bfree; free blocks avail in file system + * fsblkcnt_t f_bavail; free blocks avail to non-root + * fsblkcnt_t f_bresvd; blocks reserved for root + + * fsfilcnt_t f_files; total file nodes in file system + * fsfilcnt_t f_ffree; free file nodes in file system + * fsfilcnt_t f_favail; free file nodes avail to non-root + * fsfilcnt_t f_fresvd; file nodes reserved for root + +.Ed +.It Fn puffs_fs_sync "pu" "waitfor" "pcr" +All the dirty buffers that have been cached at the file server +level including metadata should be committed to stable storage. +The +.Fa waitfor +parameter affects the operation. +Possible values are: +.Bl -tag -width XMNT_NOWAITX +.It Dv MNT_WAIT +Wait for all I/O for complete until returning. +.It Dv MNT_NOWAIT +Initiate I/O, but do not wait for completion. +.It Dv MNT_LAZY +Synchorize data not synchoronized by the file system syncer, +i.e. data not written when +.Fn node_fsync +is called with +.Dv FSYNC_LAZY . +.El +.Pp +The credentials for the initiator of the sync operation are present in +.Fa pcr +and will usually be either file system or kernel credentials, but might +also be user credentials. +However, most of the time it is advisable to sync regardless of the +credentials of the caller. +.It Fn puffs_fs_fhtonode "pu" "fid" "fidsize" "pni" +Translates a file handle +.Fa fid +to a node. +The parameter +.Fa fidsize +indicates how large the file handle is. +In case the file system's handles are static length, this parameter can +be ignored as the kernel guarantees all file handles passed to the file +server are of correct length. +For dynamic length handles the field should be examined and +.Er EINVAL +returned in case the file handle length is not correct. +.Pp +This function provides essentially the same information to the kernel as +.Fn puffs_node_lookup . +The information is necessary for creating a new vnode corresponding to +the file handle. +.It Fn puffs_fs_nodetofh "pu" "cookie" "fid" "fidsize" +Create a file handle from the node described by +.Fa cookie . +The file handle should contain enough information to reliably identify +the node even after reboots and the pathname/inode being replaced by +another file. +If this is not possible, it is up to the author of the file system to +act responsibly and decide if the file system can support file handles +at all. +.Pp +For file systems which want dynamic length file handles, this function +must check if the file handle space indicated by +.Fa fidsize +is large enough to accommodate the file handle for the node. +If not, it must fill in the correct size and return +.Er E2BIG . +In either case, the handle length should be supplied to the kernel in +.Fa fidsize . +File systems with static length handles can ignore the size parameter as +the kernel always supplies the correct size buffer. +.It Fn puffs_fs_suspend "pu" "status" +Called when file system suspension reaches various phases. +See +.Xr puffs_suspend 3 +for more information. +.It Fn puffs_fs_unmount "pu" "flags" +Unmount the file system. +The kernel has assumedly flushed all cached data when this callback +is executed. +If the file system cannot currently be safely be unmounted, for whatever +reason, the kernel will honor an error value and not forcibly unmount. +However, if the flag +.Dv MNT_FORCE +is not honored by the file server, the kernel will forcibly unmount +the file system. +.El +.Ss Node callbacks +These operations operate in the level of individual files. +The file cookie is always provided as the second argument +.Fa opc . +If the operation is for a file, it will be the cookie of the file. +The case the operation involves a directory (such as +.Dq create file in directory ) , +the cookie will be for the directory. +Some operations take additional cookies to describe the rest of +the operands. +The return value 0 signals success, else an appropriate errno value +should be returned. +Please note that neither this list nor the descriptions are complete. +.Bl -tag -width xxxx +.It Fn puffs_node_lookup "pu" "opc" "pni" "pcn" +This function is used to locate nodes, or in other words translate +pathname components to file system data structures. +The implementation should match the name in +.Fa pcn +against the existing entries in the directory provided by the cookie +.Fa opc . +If found, the cookie for the located node should be set in +.Fa pni +using +.Fn puffs_newinfo_setcookie . +Additionally, the vnode type and size (latter applicable to regular files only) +should be set using +.Fn puffs_newinfo_setvtype +and +.Fn puffs_newinfo_setsize , +respectively. +If the located entry is a block device or character device file, +the dev_t for the entry should be set using +.Fn puffs_newinfo_setrdev . +.Pp +The type of operation is found from +.Va pcn-\*[Gt]pcn_nameiop : +.Bl -tag -width XPUFFSLOOKUP_LOOKUPX +.It Dv PUFFSLOOKUP_LOOKUP +Normal lookup operation. +.It Dv PUFFSLOOKUP_CREATE +Lookup to create a node. +.It Dv PUFFSLOOKUP_DELETE +Lookup for node deletion. +.It Dv PUFFSLOOKUP_RENAME +Lookup for the target of a rename operation (source will be looked +up using +.Dv PUFFSLOOKUP_DELETE ) . +.El +.Pp +The final component from a pathname lookup usually requires special +treatment. +It can be identified by looking at the +.Va pcn-\*[Gt]pcn_flags +fields for the flag +.Dv PUFFSLOOKUP_ISLASTCN . +For example, in most cases the lookup operation will want to check if +a delete, rename or create operation has enough credentials to perform +the operation. +.Pp +The return value 0 signals a found node and a nonzero value signals +an errno. +As a special case, +.Er ENOENT +signals "success" for cases where the lookup operation is +.Dv PUFFSLOOKUP_CREATE +or +.Dv PUFFSLOOKUP_RENAME . +Failure in these cases can be signalled by returning another appropriate +error code, for example +.Er EACCESS . +.Pp +Usually a null-terminated string for the next pathname component is +provided in +.Ar pcn-\*[Gt]pcn_name . +In case the file system is using the option +.Dv PUFFS_KFLAG_LOOKUP_FULLPNBUF , +the remainder of the complete pathname under lookup is found in +the same location. +.Ar pcn-\*[Gt]pcn_namelen +always specifies the length of the next component. +If operating with a full path, the file system is allowed to consume +more than the next component's length in node lookup. +This is done by setting +.Ar pcn-\*[Gt]pcn_consume +to indicate the amount of +.Em extra +characters in addition to +.Ar pcn-\*[Gt]pcn_namelen +processed. +.It Fn puffs_node_create "pu" "opc" "pni" "pcn" "va" +.It Fn puffs_node_mkdir "pu" "opc" "pni" "pcn" "va" +.It Fn puffs_node_mknod "pu" "opc" "pni" "pcn" "va" +A file node is created in the directory denoted by the cookie +.Fa opc +by any of the above callbacks. +The name of the new file can be found from +.Fa pcn +and the attributes are specified by +.Fa va +and the cookie for the newly created node should be set in +.Fa pni . +The only difference between these three is that they create a regular +file, directory and device special file, respectively. +.Pp +In case of mknod, the device identifier can be found in +.Fa va-\*[Gt]va_rdev . +.It Fn puffs_node_open "pu" "opc" "mode" "pcr" +Open the node denoted by the cookie +.Fa opc . +The parameter +.Fa mode +specifies the flags that +.Xr open 2 +was called with, e.g. +.Dv O_APPEND +and +.Dv O_NONBLOCK . +.It Fn puffs_node_close "pu" "opc" "flags" "pcr" +Close a node. +The parameter +.Fa flags +parameter describes the flags that the file was opened with. +.It Fn puffs_node_access "pu" "opc" "mode" "pcr" +Check if the credentials of +.Fa pcr +have the right to perform the operation specified by +.Fa mode +onto the node +.Fa opc . +The argument +.Fa mode +can specify read, write or execute by +.Dv PUFFS_VREAD , +.Dv PUFFS_VWRITE , +and +.Dv PUFFS_VEXEC , +respectively. +.It Fn puffs_node_getattr "pu" "opc" "va" "pcr" +The attributes of the node specified by +.Fa opc +must be copied to the space pointed by +.Fa va . +.It Fn puffs_node_setattr "pu" "opc" "va" "pcr" +The attributes for the node specified by +.Fa opc +must be set to those contained in +.Fa va . +Only fields of +.Fa va +which contain a value different from +.Dv PUFFS_VNOVAL +(typecast to the field's type!) contain a valid value. +.It Fn puffs_node_poll "pu" "opc" "events" +Poll for events on node +.Ar opc . +If +.Xr poll 2 +events specified in +.Ar events +are available, the function should set the bitmask to match available +events and return immediately. +Otherwise, the function should block (yield) until some events in +.Ar events +become available and only then set the +.Ar events +bitmask and return. +.Pp +In case this function returns an error, +.Dv POLLERR +(or it's +.Xr select 2 +equivalent) will be delivered to the calling process. +.Pp +.Em NOTE! +The system call interface for +.Fn poll +contains a timeout parameter. +At this level, however, the timeout is not supplied. +In case input does not arrive, the file system should periodically +unblock and return 0 new events to avoid hanging forever. +This will hopefully be better supported by libpuffs in the future. +.It Fn puffs_node_mmap "pu" "opc" "flags" "pcr" +Called when a regular file is being memory mapped by +.Xr mmap 2 . +.Fa flags +is currently always 0. +.It Fn puffs_node_fsync "pu" "opc" "pcr" "flags" "offlo" "offhi" +Sychronize a node's contents onto stable storage. +This is necessary only if the file server caches some information +before committing it. +The parameter +.Fa flags +specifies the minimum level of sychronization required (XXX: they are +not yet available). +The parameters +.Fa offlo +and +.Fa offhi +specify the data offsets requiring to be synced. +A high offset of 0 means sync from +.Fa offlo +to the end of the file. +.It Fn puffs_node_seek "pu" "opc" "oldoff" "newoff" "pcr" +Test if the node +.Ar opc +is seekable to the location +.Ar newoff . +The argument +.Ar oldoff +specifies the offset we are starting the seek from. +Most file systems dealing only with regular will choose to not +implement this. +However, it is useful for example in cases where files are +unseekable streams. +.It Fn puffs_node_remove "pu" "opc" "targ" "pcn" +.It Fn puffs_node_rmdir "pu" "opc" "targ" "pcn" +Remove the node +.Fa targ +from the directory indicated by +.Fa opc . +The directory entry name to be removed is provided by +.Fa pcn . +The rmdir operation removes only directories, while the remove +operation removes all other types except directories. +.Pp +It is paramount to note that the file system may not remove the +node data structures at this point, only the directory entry and prevent +lookups from finding the node again. +This is to retain the +.Ux +open file semantics. +The data may be removed only when +.Fn puffs_node_reclaim +is called for the node, as this assures there are no further users. +.It Fn puffs_node_link "pu" "opc" "targ" "pcn" +Create a hard link for the node +.Fa targ +into the directory +.Fa opc . +The argument +.Fa pcn +provides the directory entry name for the new link. +.It Fn puffs_node_rename "pu" "src_dir" "src" "pcn_src" "targ_dir" "targ" "pcn_targ" +Rename the node +.Fa src +with the name specified by +.Fa pcn_src +from the directory +.Fa src_dir . +The target directory and target name are given by +.Fa targ_dir +and +.Fa pcn_targ , +respectively. +.Em If +the target node already exists, it is specified by +.Fa targ +and must be replaced atomically. +Otherwise +.Fa targ +is gives as +.Dv NULL . +.Pp +It is legal to replace a directory node by another directory node with +the means of rename if the target directory is empty, otherwise +.Er ENOTEMPTY +should be returned. +All other types can replace all other types. +In case a rename between incompatible types is attempted, the errors +.Er ENOTDIR +or +.Er EISDIR +should be returned, depending on the target type. +.It Fn puffs_node_readdir "pu" "opc" "dent" "readoff" "reslen" "pcr" "eofflag" "cookies" "ncookies" +To read directory entries, +.Fn puffs_node_readdir +is called. +It should store directories as +.Va struct dirent +in the space pointed to by +.Fa dent . +The amount of space available is given by +.Fa reslen +and before returning it should be set to the amount of space +.Em remaining +in the buffer. +The argument +.Fa offset +is used to specify the offset to the directory. +Its intepretation is up to the file system and it should be set to +signal the continuation point when there is no more room for the next +entry in +.Fa dent . +It is most performant to return the maximal amount of directory +entries each call. +It is easiest to generate directory entries by using +.Fn puffs_nextdent , +which also automatically advances the necessary pointers. +.Pp +In case end-of-directory is reached, +.Fa eofflag +should be set to one. +Note that even a new call to readdir may start where +.Fa readoff +points to end-of-directory. +.Pp +If the file system supports file handles, the arguments +.Fa cookies +and +.Fa ncookies +must be filled out. +.Fa cookies +is a vector for offsets corresponding to read offsets. +One cookie should be filled out for each directory entry. +The value of the cookie should equal the offset of the +.Em next +directory entry, i.e. which offset should be passed to readdir for +the first entry read to be the entry following the current one. +.Fa ncookies +is the number of slots for cookies in the cookie vector upon entry to +the function and must be set to the amount of cookies stored in the +vector (i.e. amount of directory entries read) upon exit. +There is always enough space in the cookie vector for the maximal number +of entries that will fit into the directory entry buffer. +For filling out the vector, the helper function +.Fn PUFFS_STORE_DCOOKIE cookies ncookies offset +can be used. +This properly checks against +.Fa cookies +being +.Dv NULL . +Note that +.Fa ncookies +must be initialized to zero before the first call to +.Fn PUFFS_STORE_DCOOKIE . +.It Fn puffs_node_symlink "pu" "opc" "pni" "pcn_src" "va" "link_target" +Create a symbolic link into the directory +.Fa opc +with the name in +.Fa pcn_src +and the initial attributes in +.Fa va . +The argument +.Ar link_target +contains a null-terminated string for the link target. +The created node cookie should be set in +.Fa pni . +.It Fn puffs_node_readlink "pu" "opc" "pcr" "link" "linklen" +Read the target of a symbolic link +.Fa opc . +The result is placed in the buffer pointed to by +.Fa link . +This buffer's length is given in +.Fa linklen +and it must be updated to reflect the real link length. +A terminating nul character should not be put into the buffer and +.Em "must not" +be included in the link length. +.It Fn puffs_node_read "pu" "opc" "buf" "offset" "resid" "pcr" "ioflag" +Read the contents of a file +.Fa opc . +It will gather the data from +.Fa offset +in the file and read the number +.Fa resid +octets. +The buffer is guaranteed to have this much space. +The amount of data requested by +.Fa resid +should be read, except in the case of eof-of-file or an error. +The parameter +.Fa resid +should be set to indicate the amount of request NOT completed. +In the normal case this should be 0. +.It Fn puffs_node_write "pu" "opc" "buf" "offset" "resid" "pcr" "ioflag" +.Fn puffs_node_write +Write data to a file +.Fa opc +at +.Fa offset +and extend the file if necessary. +The number of octets written is indicated by +.Fa resid ; +everything must be written or an error will be generated. +The parameter must be set to indicate the amount of data NOT written. +In case the flag +.Dv PUFFS_IO_APPEND +is specified, the data should be appended to the end of the file. +.It Fn puffs_node_print "pu" "opc" +Print information about node. This is used only for kernel-initiated +diagnostic purposes. +.It Fn puffs_node_reclaim "pu" "opc" +The kernel will no longer reference the cookie and resources associated +with it may be freed. +In case the file +.Fa opc +has a link count of zero, it may be safely removed now. +.It Fn puffs_node_inactive "pu" "opc" +The node +.Fa opc +has lost its last reference in the kernel. +However, the cookie must still remain valid until +.Fn puffs_node_reclaim +is called. +.It Fn puffs_setback "pcc" "op" +Issue a "setback" operation which will be handled when the request response +is returned to the kernel. +Currently this can be only called from mmap, open, remove and rmdir. +The valid parameters for +.Ar op +are +.Dv PUFFS_SETBACK_INACT_N1 +and +.Dv PUFFS_SETBACK_INACT_N2 . +These signal that a file system mounted with +.Dv PUFFS_KFLAG_IAONDEMAND +should call the file system inactive method for the specified node. +The node number 1 always means the operation cookie +.Ar opc , +while the node number 2 can be used to specify the second node argument +present in some methods, e.g. remove. +.It Fn puffs_newinfo_setcookie pni cookie +Set cookie for node provided by this method to +.Ar cookie . +.It Fn puffs_newinfo_setvtype pni vtype +Set the type of the newly located node to +.Ar vtype . +This call is valid only for +.Fn lookup +and +.Fn fhtonode . +.It Fn puffs_newinfo_setsize pni size +Set the size of the newly located node to +.Ar size . +If left unset, the value defaults to 0. +This call is valid only for +.Fn lookup +and +.Fn fhtovp . +.It Fn puffs_newinfo_setrdev pni rdev +Set the type of the newly located node to +.Ar vtype . +This call is valid only for +.Fn lookup +and +.Fn fhtovp +producing device type nodes. +.El +.Sh SEE ALSO +.Xr puffs 3 , +.Xr vfsops 9 , +.Xr vnodeops 9 diff --git a/lib/libpuffs/puffs_path.3 b/lib/libpuffs/puffs_path.3 new file mode 100644 index 000000000..04c19813e --- /dev/null +++ b/lib/libpuffs/puffs_path.3 @@ -0,0 +1,125 @@ +.\" $NetBSD: puffs_path.3,v 1.3.10.1 2009/02/24 03:45:56 snj Exp $ +.\" +.\" Copyright (c) 2007 Antti Kantee. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd December 27, 2007 +.Dt PUFFS_PATH 3 +.Os +.Sh NAME +.Nm puffs_path +.Nd puffs pathbuilding routines +.Sh LIBRARY +.Lb libpuffs +.Sh SYNOPSIS +.In puffs.h +.Ft int +.Fo pu_pathbuild_fn +.Fa "struct puffs_usermount *pu" "const struct puffs_pathobj *po_dir" +.Fa "const struct puffs_pathobj *po_comp" "size_t offset" +.Fa "struct puffs_pathobj *po_new" +.Fc +.Ft int +.Fo pu_pathtransform_fn +.Fa "struct puffs_usermount *pu" "const struct puffs_pathobj *po_base" +.Fa "const struct puffs_cn *pcn" "struct puffs_pathobj *po_new" +.Fc +.Ft int +.Fo pu_pathcmp_fn +.Fa "struct puffs_usermount *pu" "struct puffs_pathobj *po1" +.Fa "struct puffs_pathobj *po2" "size_t checklen" "int checkprefix" +.Fc +.Ft void +.Fn pu_pathfree_fn "struct puffs_usermount *pu" "struct puffs_pathobj *po" +.Ft int +.Fo pu_namemod_fn +.Fa "struct puffs_usermount *pu" "struct puffs_pathobj *po_dir" +.Fa "struct puffs_cn *pcn" +.Fc +.Ft struct puffs_pathobj * +.Fn puffs_getrootpathobj "struct puffs_usermount *pu" +.Sh DESCRIPTION +The puffs library has the ability to provide full pathnames for backends +which require them. +Normal file systems should be constructed without the file system +node tied to a file name and should not used routines described herein. +An example of a file system where the backend requires filenames is +.Xr mount_psshfs 8 . +.Pp +The features described here are enabled by passing +.Dv PUFFS_FLAG_BUILDPATH +to +.Fn puffs_init . +This facility requires to use puffs nodes to store the contents of the +pathname. +Either the address of the operation cookie must directly be that of the +puffs node, or +.Fn puffs_set_cmap +must be used to set a mapping function from the cookie to the puffs +node associated with the cookie. +Finally, the root node for the file system must be set using +.Fn puffs_setroot +and the root path object retrieved using +.Fn puffs_getrootpathobj +and initialized. +.Pp +There are two different places a filename can be retrieved from. +It is available for each puffs node after the node has been registered +with the framework, i.e. +.Em after +the routine creating the node returns. +In other words, there is a window between the node is created and +when the pathname is available and multithreaded file systems must +take this into account. +The second place where a pathname is available is from the componentname +.Vt struct puffs_pcn +in operations which are passed one. +These can be retrieved using the convenience macros +.Fn PNPATH +and +.Fn PCNPATH +for node and componentname, respectively. +The type of object they return is +.Vt void * . +.Pp +By default the framework manages "regular" filenames, which consist +of directory names separated by "/" and a final component. +If the file system wishes to use pathnames of this format, all it +has to do it enable the feature. +Everything else, including bookkeeping for node and directory renames, +is done by the library. +The callback routines described next provide the ability to build +non-standard pathnames. +A +.Fn pu_foo_fn +callback is set using the +.Fn puffs_set_foo +routine. +.Pp +This manual page is still unfinished. +Please take a number and wait in line. +.Sh SEE ALSO +.Xr puffs 3 , +.Xr puffs_node 3 , +.Xr mount_psshfs 8 , +.Xr mount_sysctlfs 8 diff --git a/lib/libpuffs/puffs_priv.h b/lib/libpuffs/puffs_priv.h new file mode 100644 index 000000000..b168bf8dc --- /dev/null +++ b/lib/libpuffs/puffs_priv.h @@ -0,0 +1,276 @@ +/* $NetBSD: puffs_priv.h,v 1.41 2008/08/11 16:23:37 pooka Exp $ */ + +/* + * Copyright (c) 2006, 2007, 2008 Antti Kantee. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _PUFFS_PRIVATE_H_ +#define _PUFFS_PRIVATE_H_ + +#include +#include + +/* FIXME: fs/puffs/puffs_msgif.h? */ +#include "puffs_msgif.h" +#include "puffs.h" + +/* XXX: MINIX */ +#define IGN_PERM 0 +#define CHK_PERM 1 +#define SU_UID ((uid_t) 0) /* super_user's uid_t */ + +/* XXX: MINIX */ +#define ATIME 002 /* set if atime field needs updating */ +#define CTIME 004 /* set if ctime field needs updating */ +#define MTIME 010 /* set if mtime field needs updating */ + +#define REQ_READ_SUPER 28 + +#ifdef PUFFS_WITH_THREADS +#include +extern pthread_mutex_t pu_lock; +#define PU_LOCK() pthread_mutex_lock(&pu_lock) +#define PU_UNLOCK() pthread_mutex_unlock(&pu_lock) +#else +#define PU_LOCK() +#define PU_UNLOCK() +#endif + +#define PU_CMAP(pu, c) (pu->pu_cmap ? pu->pu_cmap(pu,c) : (struct puffs_node*)c) + +struct puffs_framectrl { + puffs_framev_readframe_fn rfb; + puffs_framev_writeframe_fn wfb; + puffs_framev_cmpframe_fn cmpfb; + puffs_framev_gotframe_fn gotfb; + puffs_framev_fdnotify_fn fdnotfn; +}; + +struct puffs_fctrl_io { + struct puffs_framectrl *fctrl; + + int io_fd; + int stat; + + int rwait; + int wwait; + + struct puffs_framebuf *cur_in; + + TAILQ_HEAD(, puffs_framebuf) snd_qing; /* queueing to be sent */ + TAILQ_HEAD(, puffs_framebuf) res_qing; /* q'ing for rescue */ + LIST_HEAD(, puffs_fbevent) ev_qing; /* q'ing for events */ + + LIST_ENTRY(puffs_fctrl_io) fio_entries; +}; +#define FIO_WR 0x01 +#define FIO_WRGONE 0x02 +#define FIO_RDGONE 0x04 +#define FIO_DEAD 0x08 +#define FIO_ENABLE_R 0x10 +#define FIO_ENABLE_W 0x20 + +#define FIO_EN_WRITE(fio) \ + (!(fio->stat & FIO_WR) \ + && ((!TAILQ_EMPTY(&fio->snd_qing) \ + && (fio->stat & FIO_ENABLE_W)) \ + || fio->wwait)) + +#define FIO_RM_WRITE(fio) \ + ((fio->stat & FIO_WR) \ + && (((TAILQ_EMPTY(&fio->snd_qing) \ + || (fio->stat & FIO_ENABLE_W) == 0)) \ + && (fio->wwait == 0))) + + +/* + * usermount: describes one file system instance + */ +struct puffs_usermount { + struct puffs_ops pu_ops; + + int pu_fd; + size_t pu_maxreqlen; + + uint32_t pu_flags; + int pu_cc_stackshift; + + ucontext_t pu_mainctx; +#define PUFFS_CCMAXSTORE 32 + int pu_cc_nstored; + + int pu_kq; + int pu_state; +#define PU_STATEMASK 0x00ff +#define PU_INLOOP 0x0100 +#define PU_ASYNCFD 0x0200 +#define PU_HASKQ 0x0400 +#define PU_PUFFSDAEMON 0x0800 +#define PU_MAINRESTORE 0x1000 +#define PU_SETSTATE(pu, s) (pu->pu_state = (s) | (pu->pu_state & ~PU_STATEMASK)) +#define PU_SETSFLAG(pu, s) (pu->pu_state |= (s)) +#define PU_CLRSFLAG(pu, s) \ + (pu->pu_state = ((pu->pu_state &= ~(s)) | (pu->pu_state & PU_STATEMASK))) + int pu_dpipe[2]; + + struct puffs_node *pu_pn_root; + + LIST_HEAD(, puffs_node) pu_pnodelst; + LIST_HEAD(, puffs_node) pu_pnode_removed_lst; + + LIST_HEAD(, puffs_cc) pu_ccmagazin; + TAILQ_HEAD(, puffs_cc) pu_lazyctx; + TAILQ_HEAD(, puffs_cc) pu_sched; + + pu_cmap_fn pu_cmap; + + pu_pathbuild_fn pu_pathbuild; + pu_pathtransform_fn pu_pathtransform; + pu_pathcmp_fn pu_pathcmp; + pu_pathfree_fn pu_pathfree; + pu_namemod_fn pu_namemod; + + pu_errnotify_fn pu_errnotify; + + pu_prepost_fn pu_oppre; + pu_prepost_fn pu_oppost; + + struct puffs_framectrl pu_framectrl[2]; +#define PU_FRAMECTRL_FS 0 +#define PU_FRAMECTRL_USER 1 + LIST_HEAD(, puffs_fctrl_io) pu_ios; + LIST_HEAD(, puffs_fctrl_io) pu_ios_rmlist; + struct kevent *pu_evs; + size_t pu_nfds; + + puffs_ml_loop_fn pu_ml_lfn; + struct timespec pu_ml_timeout; + struct timespec *pu_ml_timep; + + struct puffs_kargs *pu_kargp; + + uint64_t pu_nextreq; + void *pu_privdata; +}; + +/* call context */ + +struct puffs_cc; +typedef void (*puffs_ccfunc)(struct puffs_cc *); + +struct puffs_cc { + struct puffs_usermount *pcc_pu; + struct puffs_framebuf *pcc_pb; + + /* real cc */ + union { + struct { + ucontext_t uc; /* "continue" */ + ucontext_t uc_ret; /* "yield" */ + } real; + struct { + puffs_ccfunc func; + void *farg; + } fake; + } pcc_u; + + pid_t pcc_pid; + lwpid_t pcc_lid; + + int pcc_flags; + + TAILQ_ENTRY(puffs_cc) pcc_schedent; + LIST_ENTRY(puffs_cc) pcc_rope; +}; +#define pcc_uc pcc_u.real.uc +#define pcc_uc_ret pcc_u.real.uc_ret +#define pcc_func pcc_u.fake.func +#define pcc_farg pcc_u.fake.farg +#define PCC_DONE 0x01 +#define PCC_BORROWED 0x02 +#define PCC_HASCALLER 0x04 +#define PCC_MLCONT 0x08 + +struct puffs_newinfo { + void **pni_cookie; + enum vtype *pni_vtype; + voff_t *pni_size; + dev_t *pni_rdev; +}; + +#define PUFFS_MAKEKCRED(to, from) \ + /*LINTED: tnilxnaht, the cast is ok */ \ + const struct puffs_kcred *to = (const void *)from +#define PUFFS_MAKECRED(to, from) \ + /*LINTED: tnilxnaht, the cast is ok */ \ + const struct puffs_cred *to = (const void *)from +#define PUFFS_KCREDTOCRED(to, from) \ + /*LINTED: tnilxnaht, the cast is ok */ \ + to = (void *)from + +__BEGIN_DECLS + +void puffs__framev_input(struct puffs_usermount *, struct puffs_framectrl *, + struct puffs_fctrl_io *); +int puffs__framev_output(struct puffs_usermount *, struct puffs_framectrl*, + struct puffs_fctrl_io *); +void puffs__framev_exit(struct puffs_usermount *); +void puffs__framev_readclose(struct puffs_usermount *, + struct puffs_fctrl_io *, int); +void puffs__framev_writeclose(struct puffs_usermount *, + struct puffs_fctrl_io *, int); +void puffs__framev_notify(struct puffs_fctrl_io *, int); +void *puffs__framebuf_getdataptr(struct puffs_framebuf *); +int puffs__framev_addfd_ctrl(struct puffs_usermount *, int, int, + struct puffs_framectrl *); +void puffs__framebuf_moveinfo(struct puffs_framebuf *, + struct puffs_framebuf *); + +void puffs__theloop(struct puffs_cc *); +void puffs__ml_dispatch(struct puffs_usermount *, struct puffs_framebuf *); + +int puffs__cc_create(struct puffs_usermount *, puffs_ccfunc, + struct puffs_cc **); +void puffs__cc_cont(struct puffs_cc *); +void puffs__cc_destroy(struct puffs_cc *, int); +void puffs__cc_setcaller(struct puffs_cc *, pid_t, lwpid_t); +void puffs__goto(struct puffs_cc *); +int puffs__cc_savemain(struct puffs_usermount *); +int puffs__cc_restoremain(struct puffs_usermount *); +void puffs__cc_exit(struct puffs_usermount *); + +int puffs__fsframe_read(struct puffs_usermount *, struct puffs_framebuf *, + int, int *); +int puffs__fsframe_write(struct puffs_usermount *, struct puffs_framebuf *, + int, int *); +int puffs__fsframe_cmp(struct puffs_usermount *, struct puffs_framebuf *, + struct puffs_framebuf *, int *); +void puffs__fsframe_gotframe(struct puffs_usermount *, + struct puffs_framebuf *); + +__END_DECLS + +#define NUL(str,l,m) mfs_nul_f(__FILE__,__LINE__,(str), (l), (m)) + +#endif /* _PUFFS_PRIVATE_H_ */ diff --git a/lib/libpuffs/read.c b/lib/libpuffs/read.c new file mode 100644 index 000000000..3e260b10b --- /dev/null +++ b/lib/libpuffs/read.c @@ -0,0 +1,178 @@ +/* Created (MFS based): + * February 2010 (Evgeniy Ivanov) + */ + +#include "fs.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + + +#define GETDENTS_BUFSIZ 4096 +PRIVATE char getdents_buf[GETDENTS_BUFSIZ]; + +#define RW_BUFSIZ (128 << 10) +PRIVATE char rw_buf[RW_BUFSIZ]; + + +/*===========================================================================* + * fs_readwrite * + *===========================================================================*/ +PUBLIC int fs_readwrite(void) +{ + int r = OK, rw_flag; + cp_grant_id_t gid; + off_t pos; + size_t nrbytes, bytes_left, bytes_done; + struct puffs_node *pn; + struct vattr va; + PUFFS_MAKECRED(pcr, &global_kcred); + + if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) { + lpuffs_debug("walk failed...\n"); + return(EINVAL); + } + + /* Get the values from the request message */ + rw_flag = (fs_m_in.m_type == REQ_READ ? READING : WRITING); + gid = (cp_grant_id_t) fs_m_in.REQ_GRANT; + pos = (off_t) fs_m_in.REQ_SEEK_POS_LO; + nrbytes = bytes_left = (size_t) fs_m_in.REQ_NBYTES; + + if (nrbytes > RW_BUFSIZ) + nrbytes = bytes_left = RW_BUFSIZ; + + memset(getdents_buf, '\0', RW_BUFSIZ); /* Avoid leaking any data */ + + if (rw_flag == READING) { + if (global_pu->pu_ops.puffs_node_read == NULL) + return(EINVAL); + + r = global_pu->pu_ops.puffs_node_read(global_pu, pn, (uint8_t *)rw_buf, + pos, &bytes_left, pcr, 0); + if (r) { + lpuffs_debug("puffs_node_read failed\n"); + return(EINVAL); + } + + bytes_done = nrbytes - bytes_left; + if (bytes_done) { + r = sys_safecopyto(VFS_PROC_NR, gid, (vir_bytes) 0, + (vir_bytes) rw_buf, bytes_done, D); + update_times(pn, ATIME, 0); + } + } else if (rw_flag == WRITING) { + /* At first try to change vattr */ + if (global_pu->pu_ops.puffs_node_setattr == NULL) + return(EINVAL); + + puffs_vattr_null(&va); + if ( (pos + bytes_left) > pn->pn_va.va_size) + va.va_size = bytes_left + pos; + va.va_ctime.tv_sec = va.va_mtime.tv_sec = clock_time(); + va.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec; + + r = global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr); + if (r) return(EINVAL); + + r = sys_safecopyfrom(VFS_PROC_NR, gid, (vir_bytes) 0, + (vir_bytes) rw_buf, nrbytes, D); + if (r != OK) return(EINVAL); + + if (global_pu->pu_ops.puffs_node_write == NULL) + return(EINVAL); + + r = global_pu->pu_ops.puffs_node_write(global_pu, pn, (uint8_t *)rw_buf, + pos, &bytes_left, pcr, 0); + bytes_done = nrbytes - bytes_left; + } + + if (r != OK) return(EINVAL); + + fs_m_out.RES_SEEK_POS_LO = pos + bytes_done; + fs_m_out.RES_NBYTES = bytes_done; + + return(r); +} + + +/*===========================================================================* + * fs_breadwrite * + *===========================================================================*/ +PUBLIC int fs_breadwrite(void) +{ + /* We do not support breads/writes */ + panic("bread write requested, but FS doesn't support it!\n"); + return(OK); +} + + +/*===========================================================================* + * fs_getdents * + *===========================================================================*/ +PUBLIC int fs_getdents(void) +{ + int r; + register struct puffs_node *pn; + ino_t ino; + cp_grant_id_t gid; + size_t size, buf_left; + off_t pos; + struct dirent *dent; + int eofflag = 0; + size_t written; + PUFFS_MAKECRED(pcr, &global_kcred); + + ino = (ino_t) fs_m_in.REQ_INODE_NR; + gid = (gid_t) fs_m_in.REQ_GRANT; + size = buf_left = (size_t) fs_m_in.REQ_MEM_SIZE; + pos = (off_t) fs_m_in.REQ_SEEK_POS_LO; + + if ((pn = puffs_pn_nodewalk(global_pu, 0, &ino)) == NULL) { + lpuffs_debug("walk failed...\n"); + return(EINVAL); + } + + if (GETDENTS_BUFSIZ < size) + size = buf_left = GETDENTS_BUFSIZ; + memset(getdents_buf, '\0', GETDENTS_BUFSIZ); /* Avoid leaking any data */ + + dent = (struct dirent*) getdents_buf; + + r = global_pu->pu_ops.puffs_node_readdir(global_pu, pn, dent, &pos, + &buf_left, pcr, &eofflag, 0, 0); + if (r) { + lpuffs_debug("puffs_node_readdir returned error\n"); + return(EINVAL); + } + + assert(buf_left <= size); + written = size - buf_left; + + if (written == 0 && !eofflag) { + lpuffs_debug("The user's buffer is too small\n"); + return(EINVAL); + } + + if (written) { + r = sys_safecopyto(VFS_PROC_NR, gid, (vir_bytes) 0, + (vir_bytes) getdents_buf, written, D); + if (r != OK) return(r); + } + + update_times(pn, ATIME, 0); + + fs_m_out.RES_NBYTES = written; + fs_m_out.RES_SEEK_POS_LO = pos; + + return(OK); +} diff --git a/lib/libpuffs/stadir.c b/lib/libpuffs/stadir.c new file mode 100644 index 000000000..ecea24c34 --- /dev/null +++ b/lib/libpuffs/stadir.c @@ -0,0 +1,121 @@ +/* Created (MFS based): + * February 2010 (Evgeniy Ivanov) + */ + +#include "fs.h" +#include +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + + +/*===========================================================================* + * fs_fstatfs * + *===========================================================================*/ +PUBLIC int fs_fstatfs() +{ + int r; + struct statvfs st_vfs; + struct statfs st; + + if (global_pu->pu_ops.puffs_fs_statvfs(global_pu, &st_vfs) != 0) { + lpuffs_debug("statfs failed\n"); + return(EINVAL); + } + + st.f_bsize = st_vfs.f_bsize; + + /* Copy the struct to user space. */ + r = sys_safecopyto(fs_m_in.m_source, (cp_grant_id_t) fs_m_in.REQ_GRANT, + (vir_bytes) 0, (vir_bytes) &st, (size_t) sizeof(st), D); + + return(r); +} + + +/*===========================================================================* + * fs_stat * + *===========================================================================*/ +PUBLIC int fs_stat() +{ + register int r; /* return value */ + register struct puffs_node *pn; /* target pnode */ + struct vattr va; + struct stat statbuf; + mode_t mo; + int s; + PUFFS_MAKECRED(pcr, &global_kcred); + + if (global_pu->pu_ops.puffs_node_getattr == NULL) { + lpuffs_debug("fs_stat: puffs_node_getattr is missing\n"); + return(EINVAL); + } + + if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) { + lpuffs_debug("walk failed...\n"); + return(EINVAL); + } + + if (global_pu->pu_ops.puffs_node_getattr(global_pu, pn, &va, pcr) != 0) { + if (errno) { + if (errno > 0) errno = -errno; + return(errno); + } + return(EINVAL); + } + + /* Fill in the statbuf struct. */ + mo = va.va_mode & I_TYPE; + + /* true iff special */ + s = (mo == I_CHAR_SPECIAL || mo == I_BLOCK_SPECIAL); + + statbuf.st_dev = fs_dev; + statbuf.st_ino = va.va_fileid; + statbuf.st_mode = va.va_mode; + statbuf.st_nlink = va.va_nlink; + statbuf.st_uid = va.va_uid; + statbuf.st_gid = va.va_gid; + statbuf.st_rdev = (s ? va.va_rdev : NO_DEV); + statbuf.st_size = va.va_size; + statbuf.st_atime = va.va_atime.tv_sec; + statbuf.st_mtime = va.va_mtime.tv_sec; + statbuf.st_ctime = va.va_ctime.tv_sec; + + /* Copy the struct to user space. */ + r = sys_safecopyto(fs_m_in.m_source, (cp_grant_id_t) fs_m_in.REQ_GRANT, + (vir_bytes) 0, (vir_bytes) &statbuf, + (size_t) sizeof(statbuf), D); + + return(r); +} + + +/*===========================================================================* + * fs_statvfs * + *===========================================================================*/ +PUBLIC int fs_statvfs() +{ + int r; + struct statvfs st; + + if (global_pu->pu_ops.puffs_fs_statvfs(global_pu, &st) != 0) { + lpuffs_debug("statvfs failed\n"); + return(EINVAL); + } + + /* XXX libpuffs doesn't truncate filenames and returns ENAMETOOLONG, + * though some servers would like to behave differently. + * See subtest 2.18-19 of test23 and test/common.c:does_fs_truncate(). + */ + st.f_flag |= ST_NOTRUNC; + + /* Copy the struct to user space. */ + r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0, (vir_bytes) &st, + (phys_bytes) sizeof(st), D); + + return(r); +} diff --git a/lib/libpuffs/subr.c b/lib/libpuffs/subr.c new file mode 100644 index 000000000..c48b2fe0f --- /dev/null +++ b/lib/libpuffs/subr.c @@ -0,0 +1,325 @@ +/* $NetBSD: subr.c,v 1.23 2008/08/12 19:44:39 pooka Exp $ */ + +/* + * Copyright (c) 2006 Antti Kantee. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#if !defined(lint) +__RCSID("$NetBSD: subr.c,v 1.23 2008/08/12 19:44:39 pooka Exp $"); +#endif /* !lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + + +int +puffs_gendotdent(struct dirent **dent, ino_t id, int dotdot, size_t *reslen) +{ + const char *name; + + assert(dotdot == 0 || dotdot == 1); + name = dotdot == 0 ? "." : ".."; + + return puffs_nextdent(dent, name, id, DT_DIR, reslen); +} + +int +puffs_nextdent(struct dirent **dent, const char *name, ino_t id, uint8_t dtype, + size_t *reslen) +{ + struct dirent *d = *dent; + unsigned int len, reclen; + int o; + char *cp; + + /* check if we have enough room for our dent-aligned dirent */ + if (_DIRENT_RECLEN(d, strlen(name)) > *reslen) + return 0; + + d->d_ino = id; + + /* Compute the length of the name */ + cp = memchr(name, '\0', NAME_MAX); + if (cp == NULL) + len = NAME_MAX; + else + len = cp - (name); + + /* Compute record length */ + reclen = offsetof(struct dirent, d_name) + len + 1; + o = (reclen % sizeof(long)); + if (o != 0) + reclen += sizeof(long) - o; + + /* FIXME: Set d_off? + * dep->d_off = + */ + d->d_reclen = (unsigned short) reclen; + (void)memcpy(d->d_name, name, (size_t)len); + d->d_name[len] = '\0'; + + *reslen -= d->d_reclen; + *dent = _DIRENT_NEXT(d); + + return 1; +} + +/*ARGSUSED*/ +int +puffs_fsnop_unmount(struct puffs_usermount *dontuse1, int dontuse2) +{ + + /* would you like to see puffs rule again, my friend? */ + return 0; +} + +/*ARGSUSED*/ +int +puffs_fsnop_sync(struct puffs_usermount *dontuse1, int dontuse2, + const struct puffs_cred *dontuse3) +{ + + return 0; +} + +/*ARGSUSED*/ +int +puffs_fsnop_statvfs(struct puffs_usermount *dontuse1, struct statvfs *sbp) +{ + + /* FIXME: implement me + sbp->f_bsize = sbp->f_frsize = sbp->f_iosize = 512; + + sbp->f_bfree=sbp->f_bavail=sbp->f_bresvd=sbp->f_blocks = (fsblkcnt_t)0; + sbp->f_ffree=sbp->f_favail=sbp->f_fresvd=sbp->f_files = (fsfilcnt_t)0; + */ + return 0; +} + +/*ARGSUSED3*/ +int +puffs_genfs_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc, + struct vattr *va, const struct puffs_cred *pcr) +{ + struct puffs_node *pn = PU_CMAP(pu, opc); + + memcpy(va, &pn->pn_va, sizeof(struct vattr)); + return 0; +} + +/* + * Just put the node, don't do anything else. Don't use this if + * your fs needs more cleanup. + */ +/*ARGSUSED2*/ +int +puffs_genfs_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc) +{ + + puffs_pn_put(PU_CMAP(pu, opc)); + + return 0; +} + +/* + * Just a wrapper to make calling the above nicer without having to pass + * NULLs from application code + */ +void +puffs_zerostatvfs(struct statvfs *sbp) +{ + + puffs_fsnop_statvfs(NULL, sbp); +} + +/* + * Set vattr values for those applicable (i.e. not PUFFS_VNOVAL). + */ +void +puffs_setvattr(struct vattr *vap, const struct vattr *sva) +{ + +#define SETIFVAL(a, t) if (sva->a != (t)PUFFS_VNOVAL) vap->a = sva->a + if (sva->va_type != VNON) + vap->va_type = sva->va_type; + SETIFVAL(va_mode, mode_t); + SETIFVAL(va_nlink, nlink_t); + SETIFVAL(va_uid, uid_t); + SETIFVAL(va_gid, gid_t); + SETIFVAL(va_fsid, long); + SETIFVAL(va_size, u_quad_t); + SETIFVAL(va_fileid, ino_t); + SETIFVAL(va_blocksize, long); + SETIFVAL(va_atime.tv_sec, time_t); + SETIFVAL(va_ctime.tv_sec, time_t); + SETIFVAL(va_mtime.tv_sec, time_t); + SETIFVAL(va_birthtime.tv_sec, time_t); + SETIFVAL(va_atime.tv_nsec, long); + SETIFVAL(va_ctime.tv_nsec, long); + SETIFVAL(va_mtime.tv_nsec, long); + SETIFVAL(va_birthtime.tv_nsec, long); + SETIFVAL(va_gen, u_long); + SETIFVAL(va_flags, u_long); + SETIFVAL(va_rdev, dev_t); + SETIFVAL(va_bytes, u_quad_t); +#undef SETIFVAL + /* ignore va->va_vaflags */ +} + +void +puffs_vattr_null(struct vattr *vap) +{ + + vap->va_type = VNON; + + /* + * Assign individually so that it is safe even if size and + * sign of each member are varied. + */ + vap->va_mode = (mode_t)PUFFS_VNOVAL; + vap->va_nlink = (nlink_t)PUFFS_VNOVAL; + vap->va_uid = (uid_t)PUFFS_VNOVAL; + vap->va_gid = (gid_t)PUFFS_VNOVAL; + vap->va_fsid = PUFFS_VNOVAL; + vap->va_fileid = (ino_t)PUFFS_VNOVAL; + vap->va_size = (u_quad_t)PUFFS_VNOVAL; + vap->va_blocksize = sysconf(_SC_PAGESIZE); + vap->va_atime.tv_sec = + vap->va_mtime.tv_sec = + vap->va_ctime.tv_sec = + vap->va_birthtime.tv_sec = PUFFS_VNOVAL; + vap->va_atime.tv_nsec = + vap->va_mtime.tv_nsec = + vap->va_ctime.tv_nsec = + vap->va_birthtime.tv_nsec = PUFFS_VNOVAL; + vap->va_rdev = (dev_t)PUFFS_VNOVAL; + vap->va_bytes = (u_quad_t)PUFFS_VNOVAL; + + vap->va_flags = 0; + vap->va_gen = 0; + vap->va_vaflags = 0; +} + +static int vdmap[] = { + DT_UNKNOWN, /* VNON */ + DT_REG, /* VREG */ + DT_DIR, /* VDIR */ + DT_BLK, /* VBLK */ + DT_CHR, /* VCHR */ + DT_LNK, /* VLNK */ + DT_SOCK, /* VSUCK*/ + DT_FIFO, /* VFIFO*/ + DT_UNKNOWN /* VBAD */ +}; +/* XXX: DT_WHT ? */ +int +puffs_vtype2dt(enum vtype vt) +{ + + if ((int)vt >= VNON && vt < (sizeof(vdmap)/sizeof(vdmap[0]))) + return vdmap[vt]; + + return DT_UNKNOWN; +} + +enum vtype +puffs_mode2vt(mode_t mode) +{ + + switch (mode & S_IFMT) { + case S_IFIFO: + return VFIFO; + case S_IFCHR: + return VCHR; + case S_IFDIR: + return VDIR; + case S_IFBLK: + return VBLK; + case S_IFREG: + return VREG; + case S_IFLNK: + return VLNK; + case S_IFSOCK: + return VSOCK; + default: + return VBAD; /* XXX: not really true, but ... */ + } +} + +void +puffs_stat2vattr(struct vattr *va, const struct stat *sb) +{ + + va->va_type = puffs_mode2vt(sb->st_mode); + va->va_mode = sb->st_mode; + va->va_nlink = sb->st_nlink; + va->va_uid = sb->st_uid; + va->va_gid = sb->st_gid; + va->va_fsid = sb->st_dev; + va->va_fileid = sb->st_ino; + va->va_size = sb->st_size; + va->va_atime.tv_nsec = va->va_mtime.tv_nsec = va->va_ctime.tv_nsec = 0; + va->va_atime.tv_sec = sb->st_atime; + va->va_ctime.tv_sec = sb->st_ctime; + va->va_mtime.tv_sec = sb->st_mtime; + va->va_blocksize = sb->st_blksize; + va->va_birthtime = sb->st_birthtimespec; + va->va_gen = sb->st_gen; + va->va_flags = sb->st_flags; + va->va_rdev = sb->st_rdev; + va->va_bytes = sb->st_blocks * sb->st_blksize; + va->va_filerev = 0; + va->va_vaflags = 0; +} + +mode_t +puffs_addvtype2mode(mode_t mode, enum vtype type) +{ + + switch (type) { + case VCHR: + mode |= S_IFCHR; + break; + case VBLK: + mode |= S_IFBLK; + break; + default: + break; + } + + return mode; +} diff --git a/lib/libpuffs/table.c b/lib/libpuffs/table.c new file mode 100644 index 000000000..815fa83e7 --- /dev/null +++ b/lib/libpuffs/table.c @@ -0,0 +1,46 @@ +/* This file contains the table used to map system call numbers onto the + * routines that perform them. + * + * Created (MFS based): + * February 2010 (Evgeniy Ivanov) + */ + +#define _TABLE + +#include "fs.h" + +PUBLIC _PROTOTYPE (int (*fs_call_vec[]), (void) ) = { + no_sys, /* 0 not used */ + no_sys, /* 1 */ /* Was: fs_getnode */ + fs_putnode, /* 2 */ + fs_slink, /* 3 */ + fs_ftrunc, /* 4 */ + fs_chown, /* 5 */ + fs_chmod, /* 6 */ + fs_inhibread, /* 7 */ + fs_stat, /* 8 */ + fs_utime, /* 9 */ + fs_fstatfs, /* 10 */ + fs_breadwrite, /* 11 */ + fs_breadwrite, /* 12 */ + fs_unlink, /* 13 */ + fs_unlink, /* 14 */ + fs_unmount, /* 15 */ + fs_sync, /* 16 */ + fs_new_driver, /* 17 */ + fs_flush, /* 18 */ + fs_readwrite, /* 19 */ + fs_readwrite, /* 20 */ + fs_mknod, /* 21 */ + fs_mkdir, /* 22 */ + fs_create, /* 23 */ + fs_link, /* 24 */ + fs_rename, /* 25 */ + fs_lookup, /* 26 */ + fs_mountpoint, /* 27 */ + fs_readsuper, /* 28 */ + no_sys, /* 29 */ /* Was: fs_newnode */ + fs_rdlink, /* 30 */ + fs_getdents, /* 31 */ + fs_statvfs, /* 32 */ +}; diff --git a/lib/libpuffs/time.c b/lib/libpuffs/time.c new file mode 100644 index 000000000..09eec3de7 --- /dev/null +++ b/lib/libpuffs/time.c @@ -0,0 +1,43 @@ +/* Created (MFS based): + * February 2010 (Evgeniy Ivanov) + */ + +#include "fs.h" +#include +#include +#include + +#include "puffs.h" +#include "puffs_priv.h" + + +/*===========================================================================* + * fs_utime * + *===========================================================================*/ +PUBLIC int fs_utime() +{ + int r; + struct puffs_node *pn; + struct vattr va; + PUFFS_MAKECRED(pcr, &global_kcred); + + if (is_readonly_fs) + return(EROFS); + + if (global_pu->pu_ops.puffs_node_setattr == NULL) + return(EINVAL); + + if( (pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_INODE_NR)) == NULL) + return(EINVAL); + + puffs_vattr_null(&va); + va.va_atime.tv_nsec = va.va_mtime.tv_nsec = va.va_ctime.tv_nsec = 0; + va.va_atime.tv_sec = fs_m_in.REQ_ACTIME; + va.va_mtime.tv_sec = fs_m_in.REQ_MODTIME; + va.va_ctime.tv_sec = clock_time(); + + if (global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr) != 0) + return(EINVAL); + + return(OK); +} diff --git a/lib/libpuffs/utility.c b/lib/libpuffs/utility.c new file mode 100644 index 000000000..a1e262d87 --- /dev/null +++ b/lib/libpuffs/utility.c @@ -0,0 +1,114 @@ +/* Created (MFS based): + * February 2010 (Evgeniy Ivanov) + */ + +#include "fs.h" + +#include + +#include "puffs.h" +#include "puffs_priv.h" + + +/*===========================================================================* + * no_sys * + *===========================================================================*/ +PUBLIC int no_sys() +{ +/* Somebody has used an illegal system call number */ + lpuffs_debug("no_sys: invalid call %d\n", req_nr); + return(EINVAL); +} + + +/*===========================================================================* + * mfs_nul * + *===========================================================================*/ +PUBLIC void mfs_nul_f(char *file, int line, char *str, unsigned int len, + unsigned int maxlen) +{ + if (len < maxlen && str[len-1] != '\0') { + lpuffs_debug("%s:%d string (length %d,maxlen %d) not null-terminated\n", + file, line, len, maxlen); + } +} + + +/*===========================================================================* + * clock_time * + *===========================================================================*/ +PUBLIC time_t clock_time() +{ +/* This routine returns the time in seconds since 1.1.1970. MINIX is an + * astrophysically naive system that assumes the earth rotates at a constant + * rate and that such things as leap seconds do not exist. + */ + + register int k; + clock_t uptime; + time_t boottime; + + if ((k=getuptime2(&uptime, &boottime)) != OK) + panic("clock_time: getuptme2 failed: %d", k); + + return( (time_t) (boottime + (uptime/sys_hz()))); +} + + +/*===========================================================================* + * update_times * + *===========================================================================*/ +PUBLIC int update_times(struct puffs_node *pn, int flags, time_t t) +{ + int r; + struct vattr va; + time_t new_time; + PUFFS_MAKECRED(pcr, &global_kcred); + + if (!flags) + return 0; + + if (global_pu->pu_ops.puffs_node_setattr == NULL) + return EINVAL; + + new_time = t != 0 ? t : clock_time(); + + puffs_vattr_null(&va); + /* librefuse modifies atime and mtime together, + * so set old values to avoid setting either one + * to PUFFS_VNOVAL (set by puffs_vattr_null). + */ + va.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec; + va.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec; + + if (flags & ATIME) { + va.va_atime.tv_sec = new_time; + va.va_atime.tv_nsec = 0; + } + if (flags & MTIME) { + va.va_mtime.tv_sec = new_time; + va.va_mtime.tv_nsec = 0; + } + if (flags & CTIME) { + va.va_ctime.tv_sec = new_time; + va.va_ctime.tv_nsec = 0; + } + + r = global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr); + + return(r); +} + + +/*===========================================================================* + * lpuffs_debug * + *===========================================================================*/ +PUBLIC void lpuffs_debug(char *format, ...) +{ + char buffer[256]; + va_list args; + va_start (args, format); + vsprintf (buffer,format, args); + printf("%s: %s", fs_name, buffer); + va_end (args); +} diff --git a/lib/librefuse/Makefile b/lib/librefuse/Makefile new file mode 100644 index 000000000..d01b198bb --- /dev/null +++ b/lib/librefuse/Makefile @@ -0,0 +1,19 @@ +# $NetBSD: Makefile,v 1.8 2007/11/05 13:41:52 pooka Exp $ + +USE_FORT?= yes # data driven bugs? + +LIB= refuse +LIBDPLIBS+= puffs ${.CURDIR}/../libpuffs + +.ifdef DEBUG +FUSE_OPT_DEBUG_FLAGS= -g -DFUSE_OPT_DEBUG +.endif + +CFLAGS+= ${FUSE_OPT_DEBUG_FLAGS} +SRCS= refuse.c refuse_opt.c +MAN= refuse.3 +WARNS= 4 +INCS= fuse.h fuse_opt.h +INCSDIR= /usr/include + +.include diff --git a/lib/librefuse/TODO b/lib/librefuse/TODO new file mode 100644 index 000000000..918d38b1f --- /dev/null +++ b/lib/librefuse/TODO @@ -0,0 +1,25 @@ + $NetBSD: TODO,v 1.3 2007/05/03 21:02:54 agc Exp $ + +To Do +===== +address all XXX +implement all fuse_opt +implement proper lookup (pending some libpuffs stuff) +support fuse_mt (i.e. worker threads, but that'll probably be smarter + to do inside of libpuffs) +support fuse_ll (i.e. "raw" vfs/vnode export) +implement all sorts of compat tweaks to appease various file systems +do proper implementations of dirfillers +statfs - some fuse file systems want struct statfs and we only have + statvfs available natively + +Done +==== +statvfs +sync +WARNS=4 +address lint +special directory handling in open() +Finish off manual page +fuse_setup +fuse_teardown diff --git a/lib/librefuse/fuse.h b/lib/librefuse/fuse.h new file mode 100644 index 000000000..483fea3b0 --- /dev/null +++ b/lib/librefuse/fuse.h @@ -0,0 +1,193 @@ +/* $NetBSD: fuse.h,v 1.21 2008/08/01 15:54:09 dillo Exp $ */ + +/* + * Copyright © 2007 Alistair Crooks. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef FUSE_H_ +#define FUSE_H_ 20070123 + +/* set the default version to use for the fuse interface */ +/* this value determines the API to be used */ +#ifndef FUSE_USE_VERSION +#define FUSE_USE_VERSION 26 +#endif + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct fuse; +struct fuse_args; /* XXXsupportme */ + +struct fuse_file_info { + int32_t flags; + uint32_t fh_old; + int32_t writepage; + uint32_t direct_io:1; + uint32_t keep_cache:1; + uint32_t flush:1; + uint32_t padding:29; + uint64_t fh; + uint64_t lock_owner; +}; + +struct fuse_conn_info { + uint32_t proto_major; + uint32_t proto_minor; + uint32_t async_read; + uint32_t max_write; + uint32_t max_readahead; + uint32_t reserved[27]; +}; + +/* equivalent'ish of puffs_cc */ +struct fuse_context { + struct fuse *fuse; + uid_t uid; + gid_t gid; + pid_t pid; + void *private_data; +}; + +/** + * Argument list + */ +struct fuse_args { + int argc; + char **argv; + int allocated; +}; + +/** + * Initializer for 'struct fuse_args' + */ +#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } + +typedef struct puffs_fuse_dirh *fuse_dirh_t; + +typedef int (*fuse_fill_dir_t)(void *, const char *, const struct stat *, off_t); +typedef int (*fuse_dirfil_t)(fuse_dirh_t, const char *, int, ino_t); + +#define FUSE_VERSION 26 +#define FUSE_MAJOR_VERSION 2 +#define FUSE_MINOR_VERSION 6 + +/* + * These operations shadow those in puffs_usermount, and are used + * as a table of callbacks to make when file system requests come + * in. + * + * NOTE: keep same order as fuse + */ +struct fuse_operations { + int (*getattr)(const char *, struct stat *); + int (*readlink)(const char *, char *, size_t); + int (*getdir)(const char *, fuse_dirh_t, fuse_dirfil_t); + int (*mknod)(const char *, mode_t, dev_t); + int (*mkdir)(const char *, mode_t); + int (*unlink)(const char *); + int (*rmdir)(const char *); + int (*symlink)(const char *, const char *); + int (*rename)(const char *, const char *); + int (*link)(const char *, const char *); + int (*chmod)(const char *, mode_t); + int (*chown)(const char *, uid_t, gid_t); + int (*truncate)(const char *, off_t); + int (*utime)(const char *, struct utimbuf *); + int (*open)(const char *, struct fuse_file_info *); + int (*read)(const char *, char *, size_t, off_t, struct fuse_file_info *); + int (*write)(const char *, const char *, size_t, off_t, struct fuse_file_info *); + int (*statfs)(const char *, struct statvfs *); + int (*flush)(const char *, struct fuse_file_info *); + int (*release)(const char *, struct fuse_file_info *); + int (*fsync)(const char *, int, struct fuse_file_info *); + int (*setxattr)(const char *, const char *, const char *, size_t, int); + int (*getxattr)(const char *, const char *, char *, size_t); + int (*listxattr)(const char *, char *, size_t); + int (*removexattr)(const char *, const char *); + int (*opendir)(const char *, struct fuse_file_info *); + int (*readdir)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *); + int (*releasedir)(const char *, struct fuse_file_info *); + int (*fsyncdir)(const char *, int, struct fuse_file_info *); + void *(*init)(struct fuse_conn_info *); + void (*destroy)(void *); + int (*access)(const char *, int); + int (*create)(const char *, mode_t, struct fuse_file_info *); + int (*ftruncate)(const char *, off_t, struct fuse_file_info *); + int (*fgetattr)(const char *, struct stat *, struct fuse_file_info *); + int (*lock)(const char *, struct fuse_file_info *, int, struct flock *); + int (*utimens)(const char *, const struct timespec *); + int (*bmap)(const char *, size_t , uint64_t *); +}; + + +struct fuse_chan *fuse_mount(const char *, struct fuse_args *); +struct fuse *fuse_new(struct fuse_chan *, struct fuse_args *, + const struct fuse_operations *, size_t, void *); + +int fuse_main_real(int, char **, const struct fuse_operations *, size_t, void *); +int fuse_loop(struct fuse *); +struct fuse_context *fuse_get_context(void); +void fuse_exit(struct fuse *); +void fuse_destroy(struct fuse *); + +void fuse_unmount(const char *, struct fuse_chan *); + +struct fuse *fuse_setup(int, char **, const struct fuse_operations *, + size_t, char **, int *, int *); +void fuse_teardown(struct fuse *, char *); + +#if FUSE_USE_VERSION == 22 +#define fuse_unmount fuse_unmount_compat22 +#endif + +void fuse_unmount_compat22(const char *); + +#if FUSE_USE_VERSION >= 26 +#define fuse_main(argc, argv, op, arg) \ + fuse_main_real(argc, argv, op, sizeof(*(op)), arg) +#define fuse_setup fuse_setup26 +#else +#define fuse_main(argc, argv, op) \ + fuse_main_real(argc, argv, op, sizeof(*(op)), NULL) +#endif + +struct fuse *fuse_setup26(int, char **, const struct fuse_operations *, + size_t, char **, int *, void *); + +#ifdef __cplusplus +} +#endif + +#include + +#endif diff --git a/lib/librefuse/fuse_opt.h b/lib/librefuse/fuse_opt.h new file mode 100644 index 000000000..b4d958bb9 --- /dev/null +++ b/lib/librefuse/fuse_opt.h @@ -0,0 +1,70 @@ +/* $NetBSD: fuse_opt.h,v 1.5 2009/04/19 22:25:29 christos Exp $ */ + +/* + * Copyright (c) 2007 Alistair Crooks. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FUSE_OPT_H_ +#define _FUSE_OPT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + FUSE_OPT_KEY_OPT = -1, + FUSE_OPT_KEY_NONOPT = -2, + FUSE_OPT_KEY_KEEP = -3, + FUSE_OPT_KEY_DISCARD = -4 +}; + +struct fuse_opt { + const char *templ; + int32_t offset; + int32_t value; +}; + +#define FUSE_OPT_KEY(templ, key) { templ, -1U, key } +#define FUSE_OPT_END { .templ = NULL } + +typedef int (*fuse_opt_proc_t)(void *, const char *, int, struct fuse_args *); + + +int fuse_opt_add_arg(struct fuse_args *, const char *); +struct fuse_args *fuse_opt_deep_copy_args(int, char **); +void fuse_opt_free_args(struct fuse_args *); +int fuse_opt_insert_arg(struct fuse_args *, int, const char *); +int fuse_opt_add_opt(char **, const char *); +int fuse_opt_parse(struct fuse_args *, void *, + const struct fuse_opt *, fuse_opt_proc_t); +int fuse_opt_match(const struct fuse_opt *, const char *); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_OPT_H_ */ diff --git a/lib/librefuse/refuse.3 b/lib/librefuse/refuse.3 new file mode 100644 index 000000000..f2686f609 --- /dev/null +++ b/lib/librefuse/refuse.3 @@ -0,0 +1,243 @@ +.\" $NetBSD: refuse.3,v 1.7 2007/11/08 17:08:46 pooka Exp $ +.\" +.\" Copyright © 2007 Alistair Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd April 30, 2007 +.Dt REFUSE 3 +.Os +.Sh NAME +.Nm refuse +.Nd Re-implementation of a file system in userspace system +.Sh LIBRARY +.Lb librefuse +.Sh SYNOPSIS +.In fuse.h +.Ft int +.Fo fuse_main +.Fa "int argc" "char **argv" "const struct fuse_operations *" +.Fc +.Ft int +.Fo fuse_opt_add_arg +.Fa "struct fuse_args *args" "const char *arg" +.Fc +.Ft int +.Fo fuse_opt_parse +.Fa "struct fuse_args *args" "void *userdata" +.Fa "const struct fuse_opt *descriptions" "fuse_opt_proc_t processingfunc" +.Fc +.Ft int +.Fo fuse_teardown +.Fa "struct fuse *fuse" "char *mountpoint" +.Fc +.Ft struct fuse * +.Fo fuse_setup +.Fa "int argc" "char **argv" "const struct fuse_operations *ops" +.Fa "size_t opssize" "char **mountpoint" "int *multithreaded" "int *fd" +.Fc +.Ft int +.Fo puffs_fuse_node_getattr +.Fa "const char *path" "struct stat *attrs" +.Fc +.Ft int +.Fo puffs_fuse_node_readlink +.Fa "const char *path" "char *output" "size_t outlen" +.Fc +.Ft int +.Fo puffs_fuse_node_mknod +.Fa "const char *path" "mode_t permissions" "dev_t devicenumber" +.Fc +.Ft int +.Fo puffs_fuse_node_mkdir +.Fa "const char *path" "mode_t permissions" +.Fc +.Ft int +.Fo puffs_fuse_unlink +.Fa "const char *path" +.Fc +.Ft int +.Fo puffs_fuse_node_rmdir +.Fa "const char *path" +.Fc +.Ft int +.Fo puffs_fuse_node_symlink +.Fa "const char *path" "const char *target" +.Fc +.Ft int +.Fo puffs_fuse_node_rename +.Fa "const char *from" "const char *to" +.Fc +.Ft int +.Fo puffs_fuse_node_link +.Fa "const char *from" "const char *to" +.Fc +.Ft int +.Fo puffs_fuse_node_chmod +.Fa "const char *path" "mode_t permissions" +.Fc +.Ft int +.Fo puffs_fuse_node_own +.Fa "const char *path" "uid_t owner" "gid_t group" +.Fc +.Ft int +.Fo puffs_fuse_node_truncate +.Fa "const char *path" "off_t newsize" +.Fc +.Ft int +.Fo puffs_fuse_node_utime +.Fa "const char *path" "struct utimbuf *newtimes" +.Fc +.Ft int +.Fo puffs_fuse_node_open +.Fa "const char *path" "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_read +.Fa "const char *path" "char *buffer" "size_t bufferlen" "off_t startoffset" +.Fa "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_write +.Fa "const char *path" "char *buffer" "size_t bufferlen" "off_t startoffset" +.Fa "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_fs_statfs +.Fa "const char *path" "struct statvfs *vfsinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_flush +.Fa "const char *path" "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_fsync +.Fa "const char *path" "int flags" "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_setxattr +.Fa "const char *path" "const char *attrname" "const char *attrvalue" +.Fa "size_t attrsize" "int flags" +.Fc +.Ft int +.Fo puffs_fuse_node_getxattr +.Fa "const char *path" "const char *attrname" "const char *attrvalue" +.Fa "size_t attrvaluesize" +.Fc +.Ft int +.Fo puffs_fuse_node_listxattr +.Fa "const char *path" "const char *attrname" +.Fa "size_t attrvaluesize" +.Fc +.Ft int +.Fo puffs_fuse_node_removexattr +.Fa "const char *path" "const char *attrname" +.Fc +.Ft int +.Fo puffs_fuse_node_opendir +.Fa "const char *path" "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_readdir +.Fa "const char *path" "void *data" "fuse_fill_dir_t fillinfo" +.Fa "off_t offset" "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_releasedir +.Fa "const char *path" "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_fsyncdir +.Fa "const char *path" "int flags" "struct fuse_file_info *fileinfo" +.Fc +.Ft void * +.Fo puffs_fuse_fs_init +.Fa "struct fuse_conn_info *connectioninfo" +.Fc +.Ft void +.Fo puffs_fuse_node_destroy +.Fa "void *connection" +.Fc +.Ft int +.Fo puffs_fuse_node_access +.Fa "const char *path" "int accesstype" +.Fc +.Ft int +.Fo puffs_fuse_node_create +.Fa "const char *path" "mode_t permissions" "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_ftruncate +.Fa "const char *path" "off_t offset" "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_fgetattr +.Fa "const char *path" "struct stat *attrs" "struct fuse_file_info *fileinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_lock +.Fa "const char *path" "struct fuse_file_info *fileinfo" +.Fa "int locktype" "struct flock *lockinfo" +.Fc +.Ft int +.Fo puffs_fuse_node_utimens +.Fa "const char *path" "const struct timespec *newtimes" +.Fc +.Ft int +.Fo puffs_fuse_node_bmap +.Fa "const char *path" "size_t mapsize" "uint64_t offset" +.Fc +.Sh DESCRIPTION +.Nm +is a reimplementation of the file system in user space subsystem. +Operations are transported from the kernel virtual file system layer +to the concrete implementation behind +.Nm , +where they are processed and results are sent back to the kernel. +.Pp +It uses the framework provided by the +.Xr puffs 3 +subsystem, and, through that, the kernel interface provided by +.Xr puffs 4 . +.Sh SEE ALSO +.Xr puffs 3 , +.Xr puffs 4 +.Rs +.%A Antti Kantee +.%A Alistair Crooks +.%D September 2007 +.%J EuroBSDCon 2007 +.%T ReFUSE: Userspace FUSE Reimplementation Using puffs +.Re +.Sh HISTORY +An unsupported experimental version of +.Nm +first appeared in +.Nx 5.0 . +.Sh AUTHORS +.An Alistair Crooks Aq agc@NetBSD.org , +.An Antti Kantee Aq pooka@NetBSD.org +.Sh BUGS +Many, legion, but well-loved. diff --git a/lib/librefuse/refuse.c b/lib/librefuse/refuse.c new file mode 100644 index 000000000..9c3755006 --- /dev/null +++ b/lib/librefuse/refuse.c @@ -0,0 +1,1446 @@ +/* $NetBSD: refuse.c,v 1.92 2009/03/05 01:21:57 msaitoh Exp $ */ + +/* + * Copyright © 2007 Alistair Crooks. All rights reserved. + * Copyright © 2007 Antti Kantee. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#if !defined(lint) +__RCSID("$NetBSD: refuse.c,v 1.92 2009/03/05 01:21:57 msaitoh Exp $"); +#endif /* !lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef MULTITHREADED_REFUSE +#include +#endif + +typedef uint64_t fuse_ino_t; + +struct fuse_config { + uid_t uid; + gid_t gid; + mode_t umask; + double entry_timeout; + double negative_timeout; + double attr_timeout; + double ac_attr_timeout; + int ac_attr_timeout_set; + int debug; + int hard_remove; + int use_ino; + int readdir_ino; + int set_mode; + int set_uid; + int set_gid; + int direct_io; + int kernel_cache; + int auto_cache; + int intr; + int intr_signal; +}; + +struct fuse_chan { + const char *dir; + struct fuse_args *args; + struct puffs_usermount *pu; + int dead; +}; + +/* this is the private fuse structure */ +struct fuse { + struct fuse_chan *fc; /* fuse channel pointer */ + struct fuse_operations op; /* switch table of operations */ + int compat; /* compat level - + * not used in puffs_fuse */ + struct node **name_table; + size_t name_table_size; + struct node **id_table; + size_t id_table_size; + fuse_ino_t ctr; + unsigned int generation; + unsigned int hidectr; +#ifdef MULTITHREADED_REFUSE + pthread_mutex_t lock; + pthread_rwlock_t tree_lock; +#endif + void *user_data; + struct fuse_config conf; + int intr_installed; +}; + +struct puffs_fuse_dirh { + void *dbuf; + struct dirent *d; + + size_t reslen; + size_t bufsize; +}; + +struct refusenode { + struct fuse_file_info file_info; + struct puffs_fuse_dirh dirh; + int opencount; + int flags; +}; +#define RN_ROOT 0x01 +#define RN_OPEN 0x02 /* XXX: could just use opencount */ + +static int fuse_setattr(struct fuse *, struct puffs_node *, + const char *, const struct vattr *); + +static struct puffs_node * +newrn(struct puffs_usermount *pu) +{ + struct puffs_node *pn; + struct refusenode *rn; + + if ((rn = calloc(1, sizeof(*rn))) == NULL) { + err(EXIT_FAILURE, "newrn"); + } + pn = puffs_pn_new(pu, rn); + + return pn; +} + +static void +nukern(struct puffs_node *pn) +{ + struct refusenode *rn = pn->pn_data; + + free(rn->dirh.dbuf); + free(rn); + puffs_pn_put(pn); +} + +/* XXX - not threadsafe */ +static ino_t fakeino = 3; + +/***************** start of pthread context routines ************************/ + +/* + * Notes on fuse_context: + * we follow fuse's lead and use the pthread specific information to hold + * a reference to the fuse_context structure for this thread. + */ +#ifdef MULTITHREADED_REFUSE +static pthread_mutex_t context_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_key_t context_key; +static unsigned long context_refc; +#endif + +/* return the fuse_context struct related to this thread */ +struct fuse_context * +fuse_get_context(void) +{ +#ifdef MULTITHREADED_REFUSE + struct fuse_context *ctxt; + + if ((ctxt = pthread_getspecific(context_key)) == NULL) { + if ((ctxt = calloc(1, sizeof(struct fuse_context))) == NULL) { + abort(); + } + pthread_setspecific(context_key, ctxt); + } + return ctxt; +#else + static struct fuse_context fcon; + + return &fcon; +#endif +} + +/* used as a callback function */ +#ifdef MULTITHREADED_REFUSE +static void +free_context(void *ctxt) +{ + free(ctxt); +} +#endif + +/* + * Create the pthread key. The reason for the complexity is to + * enable use of multiple fuse instances within a single process. + */ +static int +create_context_key(void) +{ +#ifdef MULTITHREADED_REFUSE + int rv; + + rv = pthread_mutex_lock(&context_mutex); + assert(rv == 0); + + if (context_refc == 0) { + if (pthread_key_create(&context_key, free_context) != 0) { + warnx("create_context_key: pthread_key_create failed"); + pthread_mutex_unlock(&context_mutex); + return 0; + } + } + context_refc += 1; + pthread_mutex_unlock(&context_mutex); + return 1; +#else + return 1; +#endif +} + +static void +delete_context_key(void) +{ +#ifdef MULTITHREADED_REFUSE + pthread_mutex_lock(&context_mutex); + /* If we are the last fuse instances using the key, delete it */ + if (--context_refc == 0) { + free(pthread_getspecific(context_key)); + pthread_key_delete(context_key); + } + pthread_mutex_unlock(&context_mutex); +#endif +} + +/* set the uid and gid of the calling process in the current fuse context */ +static void +set_fuse_context_uid_gid(const struct puffs_cred *cred) +{ + struct fuse_context *fusectx; + uid_t uid; + gid_t gid; + + fusectx = fuse_get_context(); + if (puffs_cred_getuid(cred, &uid) == 0) { + fusectx->uid = uid; + } + if (puffs_cred_getgid(cred, &gid) == 0) { + fusectx->gid = gid; + } +} + +/* set the pid of the calling process in the current fuse context */ +static void +set_fuse_context_pid(struct puffs_usermount *pu) +{ + struct puffs_cc *pcc = puffs_cc_getcc(pu); + struct fuse_context *fusectx; + + fusectx = fuse_get_context(); + puffs_cc_getcaller(pcc, &fusectx->pid, NULL); +} + +/***************** end of pthread context routines ************************/ + +#define DIR_CHUNKSIZE 4096 +static int +fill_dirbuf(struct puffs_fuse_dirh *dh, const char *name, ino_t dino, + uint8_t dtype) +{ + + /* initial? */ + if (dh->bufsize == 0) { + if ((dh->dbuf = calloc(1, DIR_CHUNKSIZE)) == NULL) { + abort(); + } + dh->d = dh->dbuf; + dh->reslen = dh->bufsize = DIR_CHUNKSIZE; + } + + if (puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen)) { + return 0; + } + + /* try to increase buffer space */ + dh->dbuf = realloc(dh->dbuf, dh->bufsize + DIR_CHUNKSIZE); + if (dh->dbuf == NULL) { + abort(); + } + dh->d = (void *)((uint8_t *)dh->dbuf + (dh->bufsize - dh->reslen)); + dh->reslen += DIR_CHUNKSIZE; + dh->bufsize += DIR_CHUNKSIZE; + + return !puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen); +} + +/* ARGSUSED3 */ +/* XXX: I have no idea how "off" is supposed to be used */ +static int +puffs_fuse_fill_dir(void *buf, const char *name, + const struct stat *stbuf, off_t off) +{ + struct puffs_fuse_dirh *deh = buf; + ino_t dino; + uint8_t dtype; + + if (stbuf == NULL) { + dtype = DT_UNKNOWN; + dino = fakeino++; + } else { + dtype = puffs_vtype2dt(puffs_mode2vt(stbuf->st_mode)); + dino = stbuf->st_ino; + + /* + * Some FUSE file systems like to always use 0 as the + * inode number. Our readdir() doesn't like to show + * directory entries with inode number 0 ==> workaround. + */ + if (dino == 0) { + dino = fakeino++; + } + } + + return fill_dirbuf(deh, name, dino, dtype); +} + +static int +puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino) +{ + ino_t dino; + int dtype; + + if ((dtype = type) == 0) { + dtype = DT_UNKNOWN; + } + + dino = (ino) ? ino : fakeino++; + + return fill_dirbuf(h, name, dino, dtype); +} + +/* place the refuse file system name into `name' */ +static void +set_refuse_mount_name(char **argv, char *name, size_t size) +{ + char *slash; + + if (argv == NULL || *argv == NULL) { + (void) strlcpy(name, "refuse", size); + } else { + if ((slash = strrchr(*argv, '/')) == NULL) { + slash = *argv; + } else { + slash += 1; + } + if (strncmp(*argv, "refuse:", 7) == 0) { + /* we've already done this */ + (void) strlcpy(name, *argv, size); + } else { + (void) snprintf(name, size, "refuse:%s", slash); + } + } +} + + +/* this function exposes struct fuse to userland */ +static struct fuse * +fuse_setup_real(int argc, char **argv, const struct fuse_operations *ops, + size_t size, char **mountpoint, int *multithreaded, int *fd, + void *user_data) +{ + struct fuse_chan *fc; + struct fuse_args *args; + struct fuse *fuse; + char name[64]; + int i; + + /* set up the proper name */ + set_refuse_mount_name(argv, name, sizeof(name)); + + /* grab the pthread context key */ + if (!create_context_key()) { + return NULL; + } + + /* stuff name into fuse_args */ + args = fuse_opt_deep_copy_args(argc, argv); + if (args->argc > 0) { + free(args->argv[0]); + } + if ((args->argv[0] = strdup(name)) == NULL) { + fuse_opt_free_args(args); + return NULL; + } + + /* count back from the end over arguments starting with '-' */ + for (i = argc - 1 ; i > 0 && *argv[i] == '-' ; --i) { + } + + fc = fuse_mount(*mountpoint = argv[i], args); + fuse = fuse_new(fc, args, ops, size, user_data); + + fuse_opt_free_args(args); + free(args); + + /* XXX - wait for puffs to become multi-threaded */ + if (multithreaded) { + *multithreaded = 0; + } + + /* XXX - this is unused */ + if (fd) { + *fd = 0; + } + + return fuse; +} + +#ifdef fuse_setup +#undef fuse_setup +#endif + +struct fuse * +fuse_setup(int argc, char **argv, const struct fuse_operations *ops, + size_t size, char **mountpoint, int *multithreaded, int *fd) +{ + return fuse_setup_real(argc, argv, ops, size, mountpoint, + multithreaded, fd, NULL); +} + +struct fuse * +fuse_setup26(int argc, char **argv, const struct fuse_operations *ops, + size_t size, char **mountpoint, int *multithreaded, void *user_data) +{ + return fuse_setup_real(argc, argv, ops, size, mountpoint, + multithreaded, NULL, user_data); +} + +#define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file) +#define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir) + +/* ARGSUSED1 */ +static int +fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path, + struct vattr *va) +{ + struct stat st; + int ret; + + if (fuse->op.getattr == NULL) { + return ENOSYS; + } + + /* wrap up return code */ + memset(&st, 0, sizeof(st)); + ret = (*fuse->op.getattr)(path, &st); + + if (ret == 0) { + if (st.st_blksize == 0) + st.st_blksize = DEV_BSIZE; + puffs_stat2vattr(va, &st); + } + + return -ret; +} + +/* utility function to set various elements of the attribute */ +static int +fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path, + const struct vattr *va) +{ + struct refusenode *rn = pn->pn_data; + mode_t mode; + uid_t uid; + gid_t gid; + int error, ret; + + error = 0; + + mode = va->va_mode; + uid = va->va_uid; + gid = va->va_gid; + + if (mode != (mode_t)PUFFS_VNOVAL) { + ret = 0; + + if (fuse->op.chmod == NULL) { + error = -ENOSYS; + } else { + ret = fuse->op.chmod(path, mode); + if (ret) + error = ret; + } + } + if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) { + ret = 0; + + if (fuse->op.chown == NULL) { + error = -ENOSYS; + } else { + ret = fuse->op.chown(path, uid, gid); + if (ret) + error = ret; + } + } + if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL + || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) { + ret = 0; + + if (fuse->op.utimens) { + struct timespec tv[2]; + + tv[0].tv_sec = va->va_atime.tv_sec; + tv[0].tv_nsec = va->va_atime.tv_nsec; + tv[1].tv_sec = va->va_mtime.tv_sec; + tv[1].tv_nsec = va->va_mtime.tv_nsec; + + ret = fuse->op.utimens(path, tv); + } else if (fuse->op.utime) { + struct utimbuf timbuf; + + timbuf.actime = va->va_atime.tv_sec; + timbuf.modtime = va->va_mtime.tv_sec; + + ret = fuse->op.utime(path, &timbuf); + } else { + error = -ENOSYS; + } + + if (ret) + error = ret; + } + if (va->va_size != (u_quad_t)PUFFS_VNOVAL) { + ret = 0; + + if (fuse->op.truncate) { + ret = fuse->op.truncate(path, (off_t)va->va_size); + } else if (fuse->op.ftruncate) { + ret = fuse->op.ftruncate(path, (off_t)va->va_size, + &rn->file_info); + } else { + error = -ENOSYS; + } + + if (ret) + error = ret; + } + /* XXX: no reflection with reality */ + puffs_setvattr(&pn->pn_va, va); + + return -error; + +} + +static int +fuse_newnode(struct puffs_usermount *pu, const char *path, + const struct vattr *va, struct fuse_file_info *fi, + struct puffs_newinfo *pni, struct puffs_node **pn_new) +{ + struct puffs_node *pn; + struct refusenode *rn; + struct vattr newva; + struct fuse *fuse; + + fuse = puffs_getspecific(pu); + + /* fix up nodes */ + pn = newrn(pu); + if (pn == NULL) { + if (va->va_type == VDIR) { + FUSE_ERR_RMDIR(fuse, path); + } else { + FUSE_ERR_UNLINK(fuse, path); + } + return ENOMEM; + } + fuse_setattr(fuse, pn, path, va); + if (fuse_getattr(fuse, pn, path, &newva) == 0) + puffs_setvattr(&pn->pn_va, &newva); + + rn = pn->pn_data; + if (fi) + memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info)); + + puffs_newinfo_setcookie(pni, pn); + if (pn_new) + *pn_new = pn; + + return 0; +} + + +/* operation wrappers start here */ + +/* lookup the path */ +/* ARGSUSED1 */ +static int +puffs_fuse_node_lookup(struct puffs_usermount *pu, void *opc, + struct puffs_newinfo *pni, const struct puffs_cn *pcn) +{ + struct puffs_node *pn_res; + struct stat st; + struct fuse *fuse; + const char *path = PCNPATH(pcn); + int ret; + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(pcn->pcn_cred); + + ret = fuse->op.getattr(path, &st); + + if (ret != 0) { + return -ret; + } + + /* XXX: fiXXXme unconst */ + pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp, + __UNCONST(&pcn->pcn_po_full)); + if (pn_res == NULL) { + pn_res = newrn(pu); + if (pn_res == NULL) + return errno; + puffs_stat2vattr(&pn_res->pn_va, &st); + } + + puffs_newinfo_setcookie(pni, pn_res); + puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type); + puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size); + puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev); + + return 0; +} + +/* get attributes for the path name */ +/* ARGSUSED3 */ +static int +puffs_fuse_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va, + const struct puffs_cred *pcr) +{ + struct puffs_node *pn = opc; + struct fuse *fuse; + const char *path = PNPATH(pn); + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(pcr); + + return fuse_getattr(fuse, pn, path, va); +} + +/* read the contents of the symbolic link */ +/* ARGSUSED2 */ +static int +puffs_fuse_node_readlink(struct puffs_usermount *pu, void *opc, + const struct puffs_cred *cred, char *linkname, size_t *linklen) +{ + struct puffs_node *pn = opc; + struct fuse *fuse; + const char *path = PNPATH(pn), *p; + int ret; + + fuse = puffs_getspecific(pu); + if (fuse->op.readlink == NULL) { + return ENOSYS; + } + + set_fuse_context_uid_gid(cred); + + /* wrap up return code */ + ret = (*fuse->op.readlink)(path, linkname, *linklen); + + if (ret == 0) { + p = memchr(linkname, '\0', *linklen); + if (!p) + return EINVAL; + + *linklen = p - linkname; + } + + return -ret; +} + +/* make the special node */ +/* ARGSUSED1 */ +static int +puffs_fuse_node_mknod(struct puffs_usermount *pu, void *opc, + struct puffs_newinfo *pni, const struct puffs_cn *pcn, + const struct vattr *va) +{ + struct fuse *fuse; + mode_t mode; + const char *path = PCNPATH(pcn); + int ret; + + fuse = puffs_getspecific(pu); + if (fuse->op.mknod == NULL) { + return ENOSYS; + } + + set_fuse_context_uid_gid(pcn->pcn_cred); + + /* wrap up return code */ + mode = puffs_addvtype2mode(va->va_mode, va->va_type); + ret = (*fuse->op.mknod)(path, mode, va->va_rdev); + + if (ret == 0) { + ret = fuse_newnode(pu, path, va, NULL, pni, NULL); + } + + return -ret; +} + +/* make a directory */ +/* ARGSUSED1 */ +static int +puffs_fuse_node_mkdir(struct puffs_usermount *pu, void *opc, + struct puffs_newinfo *pni, const struct puffs_cn *pcn, + const struct vattr *va) +{ + struct fuse *fuse; + mode_t mode = va->va_mode; + const char *path = PCNPATH(pcn); + int ret; + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(pcn->pcn_cred); + + if (fuse->op.mkdir == NULL) { + return ENOSYS; + } + + /* wrap up return code */ + ret = (*fuse->op.mkdir)(path, mode); + + if (ret == 0) { + ret = fuse_newnode(pu, path, va, NULL, pni, NULL); + } + + return -ret; +} + +/* + * create a regular file + * + * since linux/fuse sports using mknod for creating regular files + * instead of having a separate call for it in some versions, if + * we don't have create, just jump to op->mknod. + */ +/*ARGSUSED1*/ +static int +puffs_fuse_node_create(struct puffs_usermount *pu, void *opc, + struct puffs_newinfo *pni, const struct puffs_cn *pcn, + const struct vattr *va) +{ + struct fuse *fuse; + struct fuse_file_info fi; + struct puffs_node *pn; + mode_t mode = va->va_mode; + const char *path = PCNPATH(pcn); + int ret, created; + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(pcn->pcn_cred); + + created = 0; + if (fuse->op.create) { + ret = fuse->op.create(path, mode | S_IFREG, &fi); + if (ret == 0) + created = 1; + + } else if (fuse->op.mknod) { + ret = fuse->op.mknod(path, mode | S_IFREG, 0); + + } else { + ret = -ENOSYS; + } + + if (ret == 0) { + ret = fuse_newnode(pu, path, va, &fi, pni, &pn); + + /* sweet.. create also open the file */ + if (created) { + struct refusenode *rn; + + rn = pn->pn_data; + rn->flags |= RN_OPEN; + rn->opencount++; + } + } + + return -ret; +} + +/* remove the directory entry */ +/* ARGSUSED1 */ +static int +puffs_fuse_node_remove(struct puffs_usermount *pu, void *opc, void *targ, + const struct puffs_cn *pcn) +{ + struct puffs_node *pn_targ = targ; + struct fuse *fuse; + const char *path = PNPATH(pn_targ); + int ret; + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(pcn->pcn_cred); + + if (fuse->op.unlink == NULL) { + return ENOSYS; + } + + /* wrap up return code */ + ret = (*fuse->op.unlink)(path); + + return -ret; +} + +/* remove the directory */ +/* ARGSUSED1 */ +static int +puffs_fuse_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ, + const struct puffs_cn *pcn) +{ + struct puffs_node *pn_targ = targ; + struct fuse *fuse; + const char *path = PNPATH(pn_targ); + int ret; + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(pcn->pcn_cred); + + if (fuse->op.rmdir == NULL) { + return ENOSYS; + } + + /* wrap up return code */ + ret = (*fuse->op.rmdir)(path); + + return -ret; +} + +/* create a symbolic link */ +/* ARGSUSED1 */ +static int +puffs_fuse_node_symlink(struct puffs_usermount *pu, void *opc, + struct puffs_newinfo *pni, const struct puffs_cn *pcn_src, + const struct vattr *va, const char *link_target) +{ + struct fuse *fuse; + const char *path = PCNPATH(pcn_src); + int ret; + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(pcn_src->pcn_cred); + + if (fuse->op.symlink == NULL) { + return ENOSYS; + } + + /* wrap up return code */ + ret = fuse->op.symlink(link_target, path); + + if (ret == 0) { + ret = fuse_newnode(pu, path, va, NULL, pni, NULL); + } + + return -ret; +} + +/* rename a directory entry */ +/* ARGSUSED1 */ +static int +puffs_fuse_node_rename(struct puffs_usermount *pu, void *opc, void *src, + const struct puffs_cn *pcn_src, void *targ_dir, void *targ, + const struct puffs_cn *pcn_targ) +{ + struct fuse *fuse; + const char *path_src = PCNPATH(pcn_src); + const char *path_dest = PCNPATH(pcn_targ); + int ret; + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(pcn_targ->pcn_cred); + + if (fuse->op.rename == NULL) { + return ENOSYS; + } + + ret = fuse->op.rename(path_src, path_dest); + + if (ret == 0) { + } + + return -ret; +} + +/* create a link in the file system */ +/* ARGSUSED1 */ +static int +puffs_fuse_node_link(struct puffs_usermount *pu, void *opc, void *targ, + const struct puffs_cn *pcn) +{ + struct puffs_node *pn = targ; + struct fuse *fuse; + int ret; + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(pcn->pcn_cred); + + if (fuse->op.link == NULL) { + return ENOSYS; + } + + /* wrap up return code */ + ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn)); + + return -ret; +} + +/* + * fuse's regular interface provides chmod(), chown(), utimes() + * and truncate() + some variations, so try to fit the square block + * in the circle hole and the circle block .... something like that + */ +/* ARGSUSED3 */ +static int +puffs_fuse_node_setattr(struct puffs_usermount *pu, void *opc, + const struct vattr *va, const struct puffs_cred *pcr) +{ + struct puffs_node *pn = opc; + struct fuse *fuse; + const char *path = PNPATH(pn); + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(pcr); + + return fuse_setattr(fuse, pn, path, va); +} + +/* ARGSUSED2 */ +static int +puffs_fuse_node_open(struct puffs_usermount *pu, void *opc, int mode, + const struct puffs_cred *cred) +{ + struct puffs_node *pn = opc; + struct refusenode *rn = pn->pn_data; + struct fuse_file_info *fi = &rn->file_info; + struct fuse *fuse; + const char *path = PNPATH(pn); + + fuse = puffs_getspecific(pu); + + set_fuse_context_uid_gid(cred); + + /* if open, don't open again, lest risk nuking file private info */ + if (rn->flags & RN_OPEN) { + rn->opencount++; + return 0; + } + + /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */ + fi->flags = (mode & ~(O_CREAT | O_EXCL | O_TRUNC)) - 1; + + if (pn->pn_va.va_type == VDIR) { + if (fuse->op.opendir) + fuse->op.opendir(path, fi); + } else { + if (fuse->op.open) + fuse->op.open(path, fi); + } + + rn->flags |= RN_OPEN; + rn->opencount++; + + return 0; +} + +/* ARGSUSED2 */ +static int +puffs_fuse_node_close(struct puffs_usermount *pu, void *opc, int fflag, + const struct puffs_cred *pcr) +{ + struct puffs_node *pn = opc; + struct refusenode *rn = pn->pn_data; + struct fuse *fuse; + struct fuse_file_info *fi; + const char *path = PNPATH(pn); + int ret; + + fuse = puffs_getspecific(pu); + fi = &rn->file_info; + ret = 0; + + set_fuse_context_uid_gid(pcr); + + if (rn->flags & RN_OPEN) { + if (pn->pn_va.va_type == VDIR) { + if (fuse->op.releasedir) + ret = fuse->op.releasedir(path, fi); + } else { + if (fuse->op.release) + ret = fuse->op.release(path, fi); + } + } + rn->flags &= ~RN_OPEN; + rn->opencount--; + + return ret; +} + +/* read some more from the file */ +/* ARGSUSED5 */ +static int +puffs_fuse_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf, + off_t offset, size_t *resid, const struct puffs_cred *pcr, + int ioflag) +{ + struct puffs_node *pn = opc; + struct refusenode *rn = pn->pn_data; + struct fuse *fuse; + const char *path = PNPATH(pn); + size_t maxread; + int ret; + + fuse = puffs_getspecific(pu); + if (fuse->op.read == NULL) { + return ENOSYS; + } + + set_fuse_context_uid_gid(pcr); + + maxread = *resid; + if (maxread > pn->pn_va.va_size - offset) { + /*LINTED*/ + maxread = pn->pn_va.va_size - offset; + } + if (maxread == 0) + return 0; + + ret = (*fuse->op.read)(path, (char *)buf, maxread, offset, + &rn->file_info); + + if (ret > 0) { + *resid -= ret; + ret = 0; + } + + return -ret; +} + +/* write to the file */ +/* ARGSUSED0 */ +static int +puffs_fuse_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, + off_t offset, size_t *resid, const struct puffs_cred *pcr, + int ioflag) +{ + struct puffs_node *pn = opc; + struct refusenode *rn = pn->pn_data; + struct fuse *fuse; + const char *path = PNPATH(pn); + int ret; + + fuse = puffs_getspecific(pu); + if (fuse->op.write == NULL) { + return ENOSYS; + } + + set_fuse_context_uid_gid(pcr); + + if (ioflag & PUFFS_IO_APPEND) + offset = pn->pn_va.va_size; + + ret = (*fuse->op.write)(path, (char *)buf, *resid, offset, + &rn->file_info); + + if (ret > 0) { + if ((uint64_t)(offset + ret) > pn->pn_va.va_size) + pn->pn_va.va_size = offset + ret; + *resid -= ret; + ret = 0; + } + + return -ret; +} + + +/* ARGSUSED3 */ +static int +puffs_fuse_node_readdir(struct puffs_usermount *pu, void *opc, + struct dirent *dent, off_t *readoff, size_t *reslen, + const struct puffs_cred *pcr, int *eofflag, + off_t *cookies, size_t *ncookies) +{ + struct puffs_node *pn = opc; + struct refusenode *rn = pn->pn_data; + struct puffs_fuse_dirh *dirh; + struct fuse *fuse; + struct dirent *fromdent; + const char *path = PNPATH(pn); + int ret; + + fuse = puffs_getspecific(pu); + if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) { + return ENOSYS; + } + + set_fuse_context_uid_gid(pcr); + + if (pn->pn_va.va_type != VDIR) + return ENOTDIR; + + dirh = &rn->dirh; + + /* + * if we are starting from the beginning, slurp entire directory + * into our buffers + */ + if (*readoff == 0) { + /* free old buffers */ + free(dirh->dbuf); + memset(dirh, 0, sizeof(struct puffs_fuse_dirh)); + + if (fuse->op.readdir) + ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir, + 0, &rn->file_info); + else + ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil); + if (ret) + return -ret; + } + + /* Both op.readdir and op.getdir read full directory */ + *eofflag = 1; + + /* now, stuff results into the kernel buffers */ + while (*readoff < (off_t)(dirh->bufsize - dirh->reslen)) { + /*LINTED*/ + fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff); + + if (*reslen < _DIRENT_SIZE(fromdent)) + break; + + memcpy(dent, fromdent, _DIRENT_SIZE(fromdent)); + *readoff += _DIRENT_SIZE(fromdent); + *reslen -= _DIRENT_SIZE(fromdent); + + dent = _DIRENT_NEXT(dent); + } + + return 0; +} + +/* ARGSUSED */ +static int +puffs_fuse_node_reclaim(struct puffs_usermount *pu, void *opc) +{ + struct puffs_node *pn = opc; + + nukern(pn); + return 0; +} + +/* ARGSUSED1 */ +static int +puffs_fuse_fs_unmount(struct puffs_usermount *pu, int flags) +{ + struct fuse *fuse; + + fuse = puffs_getspecific(pu); + if (fuse->op.destroy == NULL) { + return 0; + } + (*fuse->op.destroy)(fuse); + return 0; +} + +/* ARGSUSED0 */ +static int +puffs_fuse_fs_sync(struct puffs_usermount *pu, int flags, + const struct puffs_cred *cr) +{ + set_fuse_context_uid_gid(cr); + return 0; +} + +/* ARGSUSED2 */ +static int +puffs_fuse_fs_statvfs(struct puffs_usermount *pu, struct statvfs *svfsb) +{ + struct fuse *fuse; + int ret; + + fuse = puffs_getspecific(pu); + if (fuse->op.statfs == NULL) { + if ((ret = statvfs(PNPATH(puffs_getroot(pu)), svfsb)) == -1) { + return errno; + } + } else { + ret = fuse->op.statfs(PNPATH(puffs_getroot(pu)), svfsb); + } + + return -ret; +} + + +/* End of puffs_fuse operations */ +/* ARGSUSED3 */ +int +fuse_main_real(int argc, char **argv, const struct fuse_operations *ops, + size_t size, void *userdata) +{ + struct fuse *fuse; + char *mountpoint; + int multithreaded; + int fd; + + fuse = fuse_setup(argc, argv, ops, size, &mountpoint, &multithreaded, + &fd); + + return fuse_loop(fuse); +} + +/* + * XXX: just defer the operation until fuse_new() when we have more + * info on our hands. The real beef is why's this separate in fuse in + * the first place? + */ +/* ARGSUSED1 */ +struct fuse_chan * +fuse_mount(const char *dir, struct fuse_args *args) +{ + struct fuse_chan *fc; + char name[64]; + + if ((fc = calloc(1, sizeof(*fc))) == NULL) { + err(EXIT_FAILURE, "fuse_mount"); + } + fc->dead = 0; + + if ((fc->dir = strdup(dir)) == NULL) { + err(EXIT_FAILURE, "fuse_mount"); + } + + /* + * we need to deep copy the args struct - some fuse file + * systems "clean up" the argument vector for "security + * reasons" + */ + fc->args = fuse_opt_deep_copy_args(args->argc, args->argv); + + if (args->argc > 0) { + set_refuse_mount_name(args->argv, name, sizeof(name)); + if ((args->argv[0] = strdup(name)) == NULL) + err(1, "fuse_mount"); + } + + return fc; +} + +/* ARGSUSED1 */ +struct fuse * +fuse_new(struct fuse_chan *fc, struct fuse_args *args, + const struct fuse_operations *ops, size_t size, void *userdata) +{ + struct puffs_usermount *pu; + struct fuse_context *fusectx; + struct puffs_pathobj *po_root; + struct puffs_node *pn_root; + struct puffs_ops *pops; + struct refusenode *rn_root; + struct statvfs svfsb; + struct stat st; + struct fuse *fuse; + extern int puffs_fakecc; + char name[64]; + char *argv0; + + if ((fuse = calloc(1, sizeof(*fuse))) == NULL) { + err(EXIT_FAILURE, "fuse_new"); + } + + /* copy fuse ops to their own structure */ + (void) memcpy(&fuse->op, ops, sizeof(fuse->op)); + + fusectx = fuse_get_context(); + fusectx->fuse = fuse; + fusectx->uid = 0; + fusectx->gid = 0; + fusectx->pid = 0; + fusectx->private_data = userdata; + + fuse->fc = fc; + + /* initialise the puffs operations structure */ + PUFFSOP_INIT(pops); + + PUFFSOP_SET(pops, puffs_fuse, fs, sync); + PUFFSOP_SET(pops, puffs_fuse, fs, statvfs); + PUFFSOP_SET(pops, puffs_fuse, fs, unmount); + + /* + * XXX: all of these don't possibly need to be + * unconditionally set + */ + PUFFSOP_SET(pops, puffs_fuse, node, lookup); + PUFFSOP_SET(pops, puffs_fuse, node, getattr); + PUFFSOP_SET(pops, puffs_fuse, node, setattr); + PUFFSOP_SET(pops, puffs_fuse, node, readdir); + PUFFSOP_SET(pops, puffs_fuse, node, readlink); + PUFFSOP_SET(pops, puffs_fuse, node, mknod); + PUFFSOP_SET(pops, puffs_fuse, node, create); + PUFFSOP_SET(pops, puffs_fuse, node, remove); + PUFFSOP_SET(pops, puffs_fuse, node, mkdir); + PUFFSOP_SET(pops, puffs_fuse, node, rmdir); + PUFFSOP_SET(pops, puffs_fuse, node, symlink); + PUFFSOP_SET(pops, puffs_fuse, node, rename); + PUFFSOP_SET(pops, puffs_fuse, node, link); + PUFFSOP_SET(pops, puffs_fuse, node, open); + PUFFSOP_SET(pops, puffs_fuse, node, close); + PUFFSOP_SET(pops, puffs_fuse, node, read); + PUFFSOP_SET(pops, puffs_fuse, node, write); + PUFFSOP_SET(pops, puffs_fuse, node, reclaim); + + argv0 = (*args->argv[0] == 0x0) ? fc->args->argv[0] : args->argv[0]; + set_refuse_mount_name(&argv0, name, sizeof(name)); + + puffs_fakecc = 1; /* XXX */ + pu = puffs_init(pops, _PATH_PUFFS, name, fuse, + PUFFS_FLAG_BUILDPATH + | PUFFS_FLAG_HASHPATH + | PUFFS_KFLAG_NOCACHE); + if (pu == NULL) { + err(EXIT_FAILURE, "puffs_init"); + } + fc->pu = pu; + + pn_root = newrn(pu); + puffs_setroot(pu, pn_root); + rn_root = pn_root->pn_data; + rn_root->flags |= RN_ROOT; + + po_root = puffs_getrootpathobj(pu); + if ((po_root->po_path = strdup("/")) == NULL) + err(1, "fuse_new"); + po_root->po_len = 1; + puffs_path_buildhash(pu, po_root); + + /* sane defaults */ + puffs_vattr_null(&pn_root->pn_va); + pn_root->pn_va.va_type = VDIR; + pn_root->pn_va.va_mode = 0755; + if (fuse->op.getattr) + if (fuse->op.getattr(po_root->po_path, &st) == 0) + puffs_stat2vattr(&pn_root->pn_va, &st); + assert(pn_root->pn_va.va_type == VDIR); + + if (fuse->op.init) + fusectx->private_data = fuse->op.init(NULL); /* XXX */ + + puffs_set_prepost(pu, set_fuse_context_pid, NULL); + + puffs_zerostatvfs(&svfsb); + if (puffs_mount(pu, fc->dir, MNT_NODEV | MNT_NOSUID, pn_root) == -1) { + err(EXIT_FAILURE, "puffs_mount: directory \"%s\"", fc->dir); + } + + return fuse; +} + +int +fuse_loop(struct fuse *fuse) +{ + + return puffs_mainloop(fuse->fc->pu); +} + +void +fuse_destroy(struct fuse *fuse) +{ + + /* + * TODO: needs to assert the fs is quiescent, i.e. no other + * threads exist + */ + + delete_context_key(); + /* XXXXXX: missing stuff */ + free(fuse); +} + +void +fuse_exit(struct fuse *fuse) +{ + + /* XXX: puffs_exit() is WRONG */ + if (fuse->fc->dead == 0) + puffs_exit(fuse->fc->pu, 1); + fuse->fc->dead = 1; +} + +/* + * XXX: obviously not the most perfect of functions, but needs some + * puffs tweaking for a better tomorrow + */ +/*ARGSUSED*/ +void +fuse_unmount(const char *mp, struct fuse_chan *fc) +{ + + /* XXX: puffs_exit() is WRONG */ + if (fc->dead == 0) + puffs_exit(fc->pu, 1); + fc->dead = 1; +} + +/*ARGSUSED*/ +void +fuse_unmount_compat22(const char *mp) +{ + + return; +} + +/* The next function "exposes" struct fuse to userland. Not much +* that we can do about this, as we're conforming to a defined +* interface. */ + +void +fuse_teardown(struct fuse *fuse, char *mountpoint) +{ + fuse_unmount(mountpoint, fuse->fc); + fuse_destroy(fuse); +} diff --git a/lib/librefuse/refuse_opt.c b/lib/librefuse/refuse_opt.c new file mode 100644 index 000000000..f44bdc236 --- /dev/null +++ b/lib/librefuse/refuse_opt.c @@ -0,0 +1,368 @@ +/* $NetBSD: refuse_opt.c,v 1.14 2009/01/19 09:56:06 lukem Exp $ */ + +/*- + * Copyright (c) 2007 Juan Romero Pardines. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * TODO: + * * -oblah,foo... works, but the options are not enabled. + * * -ofoo=%s (accepts a string) or -ofoo=%u (int) is not + * supported for now. + * * void *data: how is it used? I think it's used to enable + * options or pass values for the matching options. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef FUSE_OPT_DEBUG +#define DPRINTF(x) do { printf x; } while ( /* CONSTCOND */ 0) +#else +#define DPRINTF(x) +#endif + +enum { + KEY_HELP, + KEY_VERBOSE, + KEY_VERSION +}; + +struct fuse_opt_option { + const struct fuse_opt *fop; + char *option; + int key; + void *data; +}; + +static int fuse_opt_popt(struct fuse_opt_option *, const struct fuse_opt *); + +/* + * Public API. + * + * The following functions always return 0: + * + * int fuse_opt_add_opt(char **, const char *); + * + * We implement the next ones: + * + * int fuse_opt_add_arg(struct fuse_args *, const char *); + * void fuse_opt_free_args(struct fuse_args *); + * int fuse_opt_insert_arg(struct fuse_args *, const char *); + * int fuse_opt_match(const struct fuse_opt *, const char *); + * int fuse_opt_parse(struct fuse_args *, void *, + * const struct fuse_opt *, fuse_opt_proc_t); + * + */ + +/* ARGSUSED */ +int +fuse_opt_add_arg(struct fuse_args *args, const char *arg) +{ + struct fuse_args *ap; + + if (args->allocated == 0) { + ap = fuse_opt_deep_copy_args(args->argc, args->argv); + args->argv = ap->argv; + args->argc = ap->argc; + args->allocated = ap->allocated; + (void) free(ap); + } else if (args->allocated == args->argc) { + void *a; + int na = args->allocated + 10; + + if ((a = realloc(args->argv, na * sizeof(*args->argv))) == NULL) + return -1; + + args->argv = a; + args->allocated = na; + } + DPRINTF(("%s: arguments passed: [arg:%s]\n", __func__, arg)); + if ((args->argv[args->argc++] = strdup(arg)) == NULL) + err(1, "fuse_opt_add_arg"); + args->argv[args->argc] = NULL; + return 0; +} + +struct fuse_args * +fuse_opt_deep_copy_args(int argc, char **argv) +{ + struct fuse_args *ap; + int i; + + if ((ap = malloc(sizeof(*ap))) == NULL) + err(1, "_fuse_deep_copy_args"); + /* deep copy args structure into channel args */ + ap->allocated = ((argc / 10) + 1) * 10; + + if ((ap->argv = calloc((size_t)ap->allocated, + sizeof(*ap->argv))) == NULL) + err(1, "_fuse_deep_copy_args"); + + for (i = 0; i < argc; i++) { + if ((ap->argv[i] = strdup(argv[i])) == NULL) + err(1, "_fuse_deep_copy_args"); + } + ap->argv[ap->argc = i] = NULL; + return ap; +} + +void +fuse_opt_free_args(struct fuse_args *ap) +{ + int i; + + for (i = 0; i < ap->argc; i++) { + free(ap->argv[i]); + } + free(ap->argv); + ap->argv = NULL; + ap->allocated = ap->argc = 0; +} + +/* ARGSUSED */ +int +fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) +{ + int i; + int na; + void *a; + + DPRINTF(("%s: arguments passed: [pos=%d] [arg=%s]\n", + __func__, pos, arg)); + if (args->argv == NULL) { + na = 10; + a = malloc(na * sizeof(*args->argv)); + } else { + na = args->allocated + 10; + a = realloc(args->argv, na * sizeof(*args->argv)); + } + if (a == NULL) { + warn("fuse_opt_insert_arg"); + return -1; + } + args->argv = a; + args->allocated = na; + + for (i = args->argc++; i > pos; --i) { + args->argv[i] = args->argv[i - 1]; + } + if ((args->argv[pos] = strdup(arg)) == NULL) + err(1, "fuse_opt_insert_arg"); + args->argv[args->argc] = NULL; + return 0; +} + +/* ARGSUSED */ +int fuse_opt_add_opt(char **opts, const char *opt) +{ + DPRINTF(("%s: arguments passed: [opts=%s] [opt=%s]\n", + __func__, *opts, opt)); + return 0; +} + +/* + * Returns 0 if opt was matched with any option from opts, + * otherwise returns 1. + */ +int +fuse_opt_match(const struct fuse_opt *opts, const char *opt) +{ + while (opts++) { + if (strcmp(opt, opts->templ) == 0) + return 0; + } + + return 1; +} + +/* + * Returns 0 if foo->option was matched with any option from opts, + * and sets the following on match: + * + * * foo->key is set to the foo->fop->value if offset == -1. + * * foo->fop points to the matched struct opts. + * + * otherwise returns 1. + */ +static int +fuse_opt_popt(struct fuse_opt_option *foo, const struct fuse_opt *opts) +{ + int i, found = 0; + char *match; + + if (!foo->option) { + (void)fprintf(stderr, "fuse: missing argument after -o\n"); + return 1; + } + /* + * iterate over argv and opts to see + * if there's a match with any template. + */ + for (match = strtok(foo->option, ","); + match; match = strtok(NULL, ",")) { + + DPRINTF(("%s: specified option='%s'\n", __func__, match)); + found = 0; + + for (i = 0; opts && opts->templ; opts++, i++) { + + DPRINTF(("%s: opts->templ='%s' opts->offset=%d " + "opts->value=%d\n", __func__, opts->templ, + opts->offset, opts->value)); + + /* option is ok */ + if (strcmp(match, opts->templ) == 0) { + DPRINTF(("%s: option matched='%s'\n", + __func__, match)); + found++; + /* + * our fop pointer now points + * to the matched struct opts. + */ + foo->fop = opts; + /* + * assign default key value, necessary for + * KEY_HELP, KEY_VERSION and KEY_VERBOSE. + */ + if (foo->fop->offset == -1) + foo->key = foo->fop->value; + /* reset counter */ + opts -= i; + break; + } + } + /* invalid option */ + if (!found) { + (void)fprintf(stderr, "fuse: '%s' is not a " + "valid option\n", match); + return 1; + } + } + + return 0; +} + +/* ARGSUSED1 */ +int +fuse_opt_parse(struct fuse_args *args, void *data, + const struct fuse_opt *opts, fuse_opt_proc_t proc) +{ + struct fuse_opt_option foo; + char *buf; + int i, rv = 0; + + if (!args || !args->argv || !args->argc || !proc) + return 0; + + if (args->argc == 1) + return proc(foo.data, *args->argv, FUSE_OPT_KEY_OPT, args); + + /* the real loop to process the arguments */ + for (i = 1; i < args->argc; i++) { + + /* assign current argv string */ + foo.option = buf = args->argv[i]; + + /* argvn != -foo... */ + if (buf[0] != '-') { + + foo.key = FUSE_OPT_KEY_NONOPT; + rv = proc(foo.data, foo.option, foo.key, args); + if (rv != 0) + break; + + /* -o was specified... */ + } else if (buf[0] == '-' && buf[1] == 'o') { + + /* -oblah,foo... */ + if (buf[2]) { + /* skip -o */ + foo.option = args->argv[i] + 2; + /* -o blah,foo... */ + } else { + /* + * skip current argv and pass to the + * next one to parse the options. + */ + ++i; + foo.option = args->argv[i]; + } + + rv = fuse_opt_popt(&foo, opts); + if (rv != 0) + break; + + /* help/version/verbose argument */ + } else if (buf[0] == '-' && buf[1] != 'o') { + /* + * check if the argument matches + * with any template in opts. + */ + rv = fuse_opt_popt(&foo, opts); + if (rv != 0) { + break; + } else { + DPRINTF(("%s: foo.fop->templ='%s' " + "foo.fop->offset: %d " + "foo.fop->value: %d\n", + __func__, foo.fop->templ, + foo.fop->offset, foo.fop->value)); + + /* argument needs to be discarded */ + if (foo.key == FUSE_OPT_KEY_DISCARD) { + rv = 1; + break; + } + + /* process help/version argument */ + if (foo.key != KEY_VERBOSE && + foo.key != FUSE_OPT_KEY_KEEP) { + rv = proc(foo.data, foo.option, + foo.key, args); + break; + } else { + /* process verbose argument */ + rv = proc(foo.data, foo.option, + foo.key, args); + if (rv != 0) + break; + } + } + /* unknown option, how could that happen? */ + } else { + DPRINTF(("%s: unknown option\n", __func__)); + rv = 1; + break; + } + } + + return rv; +} diff --git a/lib/librefuse/shlib_version b/lib/librefuse/shlib_version new file mode 100644 index 000000000..b82cf9365 --- /dev/null +++ b/lib/librefuse/shlib_version @@ -0,0 +1,4 @@ +# $NetBSD: shlib_version,v 1.3 2010/05/21 10:53:41 pooka Exp $ +# +major=2 +minor=0 diff --git a/lib/nbsd_libc/sys-minix/mount.c b/lib/nbsd_libc/sys-minix/mount.c index 8a24160af..46a8bca85 100644 --- a/lib/nbsd_libc/sys-minix/mount.c +++ b/lib/nbsd_libc/sys-minix/mount.c @@ -10,6 +10,7 @@ #include #include #include +#include #define OK 0 #ifdef __weak_alias @@ -18,10 +19,10 @@ __weak_alias(umount, _umount) __weak_alias(umount2, _umount2) #endif - -#define FSPATH "/sbin/" #define FSDEFAULT "mfs" +static char fspath[] = "/sbin/:/usr/pkg/bin/"; /* Must include trailing '/' */ + PRIVATE int rs_down(char *label) { char cmd[200]; @@ -39,7 +40,7 @@ int mountflags; message m; struct stat statbuf; char label[16]; - char path[60]; + char path[PATH_MAX]; char cmd[200]; char *p; int reuse = 0; @@ -49,6 +50,7 @@ int mountflags; if (type == NULL) type = __UNCONST(FSDEFAULT); if (args == NULL) args = __UNCONST(""); reuse = 0; + memset(path, '\0', sizeof(path)); /* Check mount flags */ if(mountflags & MS_REUSE) { @@ -94,19 +96,6 @@ int mountflags; /* Tell VFS that we are passing in a 16-byte label. */ mountflags |= MS_LABEL16; - /* See if the given type is even remotely valid. */ - if(strlen(FSPATH)+strlen(type) >= sizeof(path)) { - errno = E2BIG; - return -1; - } - strcpy(path, FSPATH); - strcat(path, type); - - if(stat(path, &statbuf) != 0) { - errno = EINVAL; - return -1; - } - /* Sanity check on user input. */ if(strchr(args, '\'')) { errno = EINVAL; @@ -114,16 +103,41 @@ int mountflags; } /* start the fs-server if not using existing one */ if (!use_existing) { - if(strlen(_PATH_SERVICE)+strlen(path)+strlen(label)+ - strlen(args)+50 >= sizeof(cmd)) { + /* See if the given type is even remotely valid. */ + + char *testpath; + testpath = strtok(fspath, ":"); + + do { + if (strlen(testpath) + strlen(type) >= sizeof(path)) { + errno = E2BIG; + return(-1); + } + + strcpy(path, testpath); + strcat(path, type); + + if (access(path, F_OK) == 0) break; + + } while ((testpath = strtok(NULL, ":")) != NULL); + + if (testpath == NULL) { + /* We were not able to find type somewhere in "fspath" */ + errno = EINVAL; + return(-1); + } + + if (strlen(_PATH_SERVICE) + strlen(path) + strlen(label) + + strlen(args) + 50 >= sizeof(cmd)) { errno = E2BIG; return -1; } - sprintf(cmd, _PATH_SERVICE " %sup %s -label '%s' -args '%s%s'", - reuse ? "-r ": "", path, label, args[0] ? "-o " : "", args); + sprintf(cmd, _PATH_SERVICE " %sup %s -label '%s' -args '%s %s %s%s'", + reuse ? "-r ": "", path, label, special, name, + args[0] ? "-o " : "", args); - if((r = system(cmd)) != 0) { + if ((r = system(cmd)) != 0) { fprintf(stderr, "mount: couldn't run %s\n", cmd); errno = r; return -1; @@ -139,8 +153,8 @@ int mountflags; m.m1_p3 = label; r = _syscall(VFS_PROC_NR, MOUNT, &m); - if(r != OK) { - /* If mount() failed, tell RS to shutdown MFS process. + if (r != OK && !use_existing) { + /* If mount() failed, tell RS to shutdown FS process. * No error check - won't do anything with this error anyway. */ rs_down(label); diff --git a/lib/nbsd_libc/sys-minix/pread.c b/lib/nbsd_libc/sys-minix/pread.c index 566b071bc..5da762536 100644 --- a/lib/nbsd_libc/sys-minix/pread.c +++ b/lib/nbsd_libc/sys-minix/pread.c @@ -10,6 +10,26 @@ __weak_alias(pread, _pread) #include #include +#include + +ssize_t pread64(int fd, void *buffer, size_t nbytes, u64_t where) +{ + u64_t here; + ssize_t r; + + if (lseek64(fd, make64(0,0), SEEK_CUR, &here) < 0) return(-1); + if (lseek64(fd, where, SEEK_SET, NULL) < 0) return(-1); + if ((r = read(fd, buffer, nbytes)) < 0) { + int e ; errno; + lseek64(fd, here, SEEK_SET, NULL); + errno = e; + return(-1); + } + + if (lseek64(fd, here, SEEK_SET, NULL) < 0) return(-1); + + return(r); +} ssize_t pread(int fd, void *buffer, size_t nbytes, off_t where) { diff --git a/lib/nbsd_libc/sys-minix/pwrite.c b/lib/nbsd_libc/sys-minix/pwrite.c index 6783804c8..f2c07b3a4 100644 --- a/lib/nbsd_libc/sys-minix/pwrite.c +++ b/lib/nbsd_libc/sys-minix/pwrite.c @@ -8,6 +8,27 @@ __weak_alias(pwrite, _pwrite) #endif +#include + +ssize_t pwrite64(int fd, const void *buffer, size_t nbytes, u64_t where) +{ + u64_t here; + ssize_t w; + + if (lseek64(fd, make64(0,0), SEEK_CUR, &here) < 0) return(-1); + if (lseek64(fd, where, SEEK_SET, NULL) < 0) return(-1); + if ((w = write(fd, buffer, nbytes)) < 0) { + int e = errno; + lseek64(fd, here, SEEK_SET, NULL); + errno = e; + return(-1); + } + + if (lseek64(fd, here, SEEK_SET, NULL) < 0) return(-1); + + return(w); +} + ssize_t pwrite(int fd, const void *buffer, size_t nbytes, off_t where) { off_t here; diff --git a/nbsd_include/sys/ucred.h b/nbsd_include/sys/ucred.h index 5babf6fb5..3dfe8c1e2 100644 --- a/nbsd_include/sys/ucred.h +++ b/nbsd_include/sys/ucred.h @@ -5,14 +5,23 @@ struct ucred_old { pid_t pid; short uid; - char gid; + char gid; }; struct ucred { - pid_t pid; - uid_t uid; - gid_t gid; + pid_t pid; + uid_t uid; + gid_t gid; +}; + +/* Userland's view of credentials. This should not change */ +struct uucred { + unsigned short cr_unused; /* not used, compat */ + uid_t cr_uid; /* effective user id */ + gid_t cr_gid; /* effective group id */ + short cr_ngroups; /* number of groups */ + gid_t cr_groups[NGROUPS_MAX]; /* groups */ }; #endif diff --git a/nbsd_include/unistd.h b/nbsd_include/unistd.h index 445775424..eef4fffd3 100644 --- a/nbsd_include/unistd.h +++ b/nbsd_include/unistd.h @@ -327,6 +327,8 @@ pid_t getnpid(endpoint_t proc_ep); uid_t getnuid(endpoint_t proc_ep); gid_t getngid(endpoint_t proc_ep); int getnucred(endpoint_t proc_ep, struct ucred *ucred); +ssize_t pread64(int fd, void *buf, size_t count, u64_t where); +ssize_t pwrite64(int fd, const void *buf, size_t count, u64_t where); #endif /* __MINIX */ #endif /* __minix */