Import librefuse and libpuffs

Import libpuffs and our port of libpuffs. The port was done as part of
GSoC 2011 FUSE project, done by Evgeniy Ivanov. The librefuse import
did not require any porting efforts. Libpuffs has been modified to
understand our VFS-FS protocol and translate between that and PUFFS. As
an example that it works, fuse-ntfs-3g from pkgsrc can be compiled and
used to mount ntfs partitions:
mount -t ntfs-3g <device> <mountpoint>

FUSE only works with the asynchronous version of VFS. See <docs/UPDATING> on
how to run AVFS.

This patch further includes some changes to mount(1) and mount(2) so it's
possible to use file systems provided by pkgsrc (note: manual modifications
to /etc/system.conf are still needed. There has been made an exception for
fuse-ntfs-3g, so it already as an entry).
This commit is contained in:
Thomas Veerman 2011-11-14 11:53:05 +00:00
parent a209c3ae12
commit 490e0de548
54 changed files with 11436 additions and 54 deletions

View file

@ -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");

View file

@ -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

View file

@ -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"

View file

@ -10,11 +10,13 @@
#include <minix/syslib.h>
#include <minix/rs.h>
#include <paths.h>
#include <unistd.h>
#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,16 +98,27 @@ 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)) {
char *testpath;
testpath = strtok(fspath, ":");
do {
if (strlen(testpath) + strlen(type) >= sizeof(path)) {
errno = E2BIG;
return -1;
return(-1);
}
strcpy(path, FSPATH);
strcpy(path, testpath);
strcat(path, type);
if(stat(path, &statbuf) != 0) {
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) +
@ -115,8 +127,9 @@ int mountflags;
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) {
fprintf(stderr, "mount: couldn't run %s\n", cmd);
@ -135,7 +148,7 @@ int mountflags;
r = _syscall(VFS_PROC_NR, MOUNT, &m);
if (r != OK && !use_existing) {
/* If mount() failed, tell RS to shutdown MFS process.
/* If mount() failed, tell RS to shutdown FS process.
* No error check - won't do anything with this error anyway.
*/
rs_down(label);

View file

@ -1,5 +1,25 @@
#include <lib.h>
#include <unistd.h>
#include <minix/u64.h>
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)
{

View file

@ -1,5 +1,25 @@
#include <lib.h>
#include <unistd.h>
#include <minix/u64.h>
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)
{

17
lib/libpuffs/Makefile Normal file
View file

@ -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 <bsd.lib.mk>

350
lib/libpuffs/callcontext.c Normal file
View file

@ -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 <sys/cdefs.h>
#if !defined(lint)
__RCSID("$NetBSD: callcontext.c,v 1.23 2008/08/11 16:23:37 pooka Exp $");
#endif /* !lint */
#include <sys/types.h>
#include <sys/mman.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
#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->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->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->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->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);
}

259
lib/libpuffs/creds.c Normal file
View file

@ -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 <sys/cdefs.h>
#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 <sys/types.h>
#include <sys/param.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#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;
}

356
lib/libpuffs/device.c Normal file
View file

@ -0,0 +1,356 @@
#include "fs.h"
#include <minix/com.h>
#include <minix/endpoint.h>
#include <minix/safecopies.h>
#include <minix/u64.h>
#include <string.h>
#include "drivers.h"
#include <minix/vfsif.h>
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);
}

17
lib/libpuffs/drivers.h Normal file
View file

@ -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 */

31
lib/libpuffs/fs.h Normal file
View file

@ -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 <minix/config.h> /* MUST be first */
#include <minix/ansi.h> /* MUST be second */
#include <sys/types.h>
#include <minix/const.h>
#include <minix/type.h>
#include <minix/dmap.h>
#include <limits.h>
#include <errno.h>
#include <minix/syslib.h>
#include <minix/sysutil.h>
#include "proto.h"
#include "glo.h"
#endif /* LIBPUFFS_FS_H */

61
lib/libpuffs/glo.h Normal file
View file

@ -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 <minix/vfsif.h>
#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 */

103
lib/libpuffs/inode.c Normal file
View file

@ -0,0 +1,103 @@
/*
* Created (MFS based):
* June 2011 (Evgeniy Ivanov)
*/
#include "fs.h"
#include <string.h>
#include <assert.h>
#include <minix/vfsif.h>
#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);
}

560
lib/libpuffs/link.c Normal file
View file

@ -0,0 +1,560 @@
#include "fs.h"
#include <stdlib.h>
#include <assert.h>
#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,
&copylen);
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);
}

53
lib/libpuffs/misc.c Normal file
View file

@ -0,0 +1,53 @@
/* Created (MFS based):
* June 2011 (Evgeniy Ivanov)
*/
#include "fs.h"
#include <assert.h>
#include <minix/vfsif.h>
#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);
}

13
lib/libpuffs/mntopts.h Normal file
View file

@ -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

141
lib/libpuffs/mount.c Normal file
View file

@ -0,0 +1,141 @@
/* Created (MFS based):
* June 2011 (Evgeniy Ivanov)
*/
#include "fs.h"
#include <fcntl.h>
#include <string.h>
#include <minix/com.h>
#include <sys/stat.h>
#include <minix/ds.h>
#include <minix/vfsif.h>
#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);
}

589
lib/libpuffs/null.c Normal file
View file

@ -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 <sys/cdefs.h>
#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 <sys/types.h>
#include <sys/time.h>
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <utime.h>
#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;
}

378
lib/libpuffs/open.c Normal file
View file

@ -0,0 +1,378 @@
/* Created (MFS based):
* June 2011 (Evgeniy Ivanov)
*/
#include "fs.h"
#include <sys/stat.h>
#include <string.h>
#include <assert.h>
#include <minix/com.h>
#include <minix/vfsif.h>
#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);
}

550
lib/libpuffs/path.c Normal file
View file

@ -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 <sys/cdefs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <puffs.h>
#include <stdlib.h>
#include <string.h>
#include <minix/endpoint.h>
#include <minix/vfsif.h>
#include <minix/libminixfs.h>
#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/<link> or
* /already/processed/path/<link>/not/yet/processed/path
* After expanding the <link>, the path will look like
* <expandedlink> or
* <expandedlink>/not/yet/processed
* In both cases user_path must have enough room to hold <expandedlink>.
* However, in the latter case we have to move /not/yet/processed to the
* right place first, before we expand <link>. When strlen(<expandedlink>) is
* smaller than strlen(/already/processes/path), we move the suffix to the
* left. Is strlen(<expandedlink>) 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 <link>, we have to move the 'suffix'
* to the right place.
*/
if (slen + llen + 1 > sizeof(user_path))
return(ENAMETOOLONG);/* <expandedlink>+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); /* <expandedlink> + \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);
}

299
lib/libpuffs/path_puffs.c Normal file
View file

@ -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 <sys/cdefs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/hash.h>
#include <assert.h>
#include <errno.h>
#include <puffs.h>
#include <stdlib.h>
#include <string.h>
#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);
}

221
lib/libpuffs/pnode.c Normal file
View file

@ -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 <sys/cdefs.h>
#if !defined(lint)
__RCSID("$NetBSD: pnode.c,v 1.10 2008/08/12 19:44:39 pooka Exp $");
#endif /* !lint */
#include <sys/types.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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;
}

145
lib/libpuffs/protect.c Normal file
View file

@ -0,0 +1,145 @@
/* Created (MFS based):
* June 2011 (Evgeniy Ivanov)
*/
#include "fs.h"
#include <minix/vfsif.h>
#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);
}

71
lib/libpuffs/proto.h Normal file
View file

@ -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 */

542
lib/libpuffs/puffs.3 Normal file
View file

@ -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

774
lib/libpuffs/puffs.c Normal file
View file

@ -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 <sys/cdefs.h>
#if !defined(lint)
__RCSID("$NetBSD: puffs.c,v 1.92.4.4 2009/10/27 20:37:38 bouyer Exp $");
#endif /* !lint */
#include <sys/param.h>
#include <sys/mount.h>
#include <minix/endpoint.h>
#include <minix/vfsif.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "puffs.h"
#include "puffs_priv.h"
#ifdef PUFFS_WITH_THREADS
#include <pthread.h>
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<<stackshift);
}
pu->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;
}

747
lib/libpuffs/puffs.h Normal file
View file

@ -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 <sys/param.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/time.h>
#include <sys/queue.h>
#include "puffs_msgif.h"
#include <mntopts.h>
#include <time.h>
#include <stdbool.h>
#include <string.h>
#include <stdint.h>
#include <dirent.h>
/* 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_CC_STACKSHIFT_DEFAULT)
#define PUFFS_STACKSIZE_MIN ((size_t)-1)
#define DENT_DOT 0
#define DENT_DOTDOT 1
#define DENT_ADJ(a) ((a)-2) /* nth request means dir's n-2th */
/*
* protos
*/
#define PUFFSOP_PROTOS(fsname) \
int fsname##_fs_unmount(struct puffs_usermount *, int); \
int fsname##_fs_statvfs(struct puffs_usermount *, \
struct statvfs *); \
int fsname##_fs_sync(struct puffs_usermount *, int, \
const struct puffs_cred *cred); \
int fsname##_fs_fhtonode(struct puffs_usermount *, void *, \
size_t, struct puffs_newinfo *); \
int fsname##_fs_nodetofh(struct puffs_usermount *, \
puffs_cookie_t, void *, size_t *); \
void fsname##_fs_suspend(struct puffs_usermount *, int); \
\
int fsname##_node_lookup(struct puffs_usermount *, \
puffs_cookie_t, struct puffs_newinfo *, \
const struct puffs_cn *); \
int fsname##_node_create(struct puffs_usermount *, \
puffs_cookie_t, struct puffs_newinfo *, \
const struct puffs_cn *, const struct vattr *); \
int fsname##_node_mknod(struct puffs_usermount *, \
puffs_cookie_t, struct puffs_newinfo *, \
const struct puffs_cn *, const struct vattr *); \
int fsname##_node_open(struct puffs_usermount *, \
puffs_cookie_t, int, const struct puffs_cred *); \
int fsname##_node_close(struct puffs_usermount *, \
puffs_cookie_t, int, const struct puffs_cred *); \
int fsname##_node_access(struct puffs_usermount *, \
puffs_cookie_t, int, const struct puffs_cred *); \
int fsname##_node_getattr(struct puffs_usermount *, \
puffs_cookie_t, struct vattr *, const struct puffs_cred *); \
int fsname##_node_setattr(struct puffs_usermount *, \
puffs_cookie_t, const struct vattr *, \
const struct puffs_cred *); \
int fsname##_node_poll(struct puffs_usermount *, \
puffs_cookie_t, int *); \
int fsname##_node_mmap(struct puffs_usermount *, \
puffs_cookie_t, vm_prot_t, const struct puffs_cred *); \
int fsname##_node_fsync(struct puffs_usermount *, \
puffs_cookie_t, const struct puffs_cred *, int, \
off_t, off_t); \
int fsname##_node_seek(struct puffs_usermount *, \
puffs_cookie_t, off_t, off_t, const struct puffs_cred *); \
int fsname##_node_remove(struct puffs_usermount *, \
puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *); \
int fsname##_node_link(struct puffs_usermount *, \
puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *); \
int fsname##_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 fsname##_node_mkdir(struct puffs_usermount *, \
puffs_cookie_t, struct puffs_newinfo *, \
const struct puffs_cn *, const struct vattr *); \
int fsname##_node_rmdir(struct puffs_usermount *, \
puffs_cookie_t, puffs_cookie_t, const struct puffs_cn *); \
int fsname##_node_symlink(struct puffs_usermount *, \
puffs_cookie_t, struct puffs_newinfo *, \
const struct puffs_cn *, const struct vattr *, \
const char *); \
int fsname##_node_readdir(struct puffs_usermount *, \
puffs_cookie_t, struct dirent *, off_t *, size_t *, \
const struct puffs_cred *, int *, off_t *, size_t *); \
int fsname##_node_readlink(struct puffs_usermount *, \
puffs_cookie_t, const struct puffs_cred *, char *, \
size_t *); \
int fsname##_node_reclaim(struct puffs_usermount *, \
puffs_cookie_t); \
int fsname##_node_inactive(struct puffs_usermount *, \
puffs_cookie_t); \
int fsname##_node_print(struct puffs_usermount *, \
puffs_cookie_t); \
int fsname##_node_pathconf(struct puffs_usermount *, \
puffs_cookie_t, int, int *); \
int fsname##_node_advlock(struct puffs_usermount *, \
puffs_cookie_t, void *, int, struct flock *, int); \
int fsname##_node_read(struct puffs_usermount *, puffs_cookie_t,\
uint8_t *, off_t, size_t *, const struct puffs_cred *, int);\
int fsname##_node_write(struct puffs_usermount *, \
puffs_cookie_t, uint8_t *, off_t, size_t *, \
const struct puffs_cred *, int); \
int fsname##_node_abortop(struct puffs_usermount *, \
puffs_cookie_t, const struct puffs_cn *);
#define PUFFSOP_INIT(ops) \
ops = malloc(sizeof(struct puffs_ops)); \
memset(ops, 0, sizeof(struct puffs_ops))
#define PUFFSOP_SET(ops, fsname, fsornode, opname) \
(ops)->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_ */

93
lib/libpuffs/puffs_cc.3 Normal file
View file

@ -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

167
lib/libpuffs/puffs_cred.3 Normal file
View file

@ -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

261
lib/libpuffs/puffs_msgif.h Normal file
View file

@ -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 <sys/param.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/statvfs.h>
#include <sys/ucred.h>
#include <time.h>
#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:
* <uep> 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_ */

102
lib/libpuffs/puffs_node.3 Normal file
View file

@ -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

763
lib/libpuffs/puffs_ops.3 Normal file
View file

@ -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

125
lib/libpuffs/puffs_path.3 Normal file
View file

@ -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

276
lib/libpuffs/puffs_priv.h Normal file
View file

@ -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 <sys/types.h>
#include <ucontext.h>
/* 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 <pthread.h>
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_ */

178
lib/libpuffs/read.c Normal file
View file

@ -0,0 +1,178 @@
/* Created (MFS based):
* February 2010 (Evgeniy Ivanov)
*/
#include "fs.h"
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <minix/com.h>
#include <minix/u64.h>
#include <minix/vfsif.h>
#include <assert.h>
#include <sys/param.h>
#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);
}

121
lib/libpuffs/stadir.c Normal file
View file

@ -0,0 +1,121 @@
/* Created (MFS based):
* February 2010 (Evgeniy Ivanov)
*/
#include "fs.h"
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/statvfs.h>
#include <minix/vfsif.h>
#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);
}

325
lib/libpuffs/subr.c Normal file
View file

@ -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 <sys/cdefs.h>
#if !defined(lint)
__RCSID("$NetBSD: subr.c,v 1.23 2008/08/12 19:44:39 pooka Exp $");
#endif /* !lint */
#include <sys/types.h>
#include <sys/dirent.h>
#include <sys/time.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#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;
}

46
lib/libpuffs/table.c Normal file
View file

@ -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 */
};

43
lib/libpuffs/time.c Normal file
View file

@ -0,0 +1,43 @@
/* Created (MFS based):
* February 2010 (Evgeniy Ivanov)
*/
#include "fs.h"
#include <minix/callnr.h>
#include <minix/com.h>
#include <minix/vfsif.h>
#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);
}

114
lib/libpuffs/utility.c Normal file
View file

@ -0,0 +1,114 @@
/* Created (MFS based):
* February 2010 (Evgeniy Ivanov)
*/
#include "fs.h"
#include <stdarg.h>
#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);
}

19
lib/librefuse/Makefile Normal file
View file

@ -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 <bsd.lib.mk>

25
lib/librefuse/TODO Normal file
View file

@ -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

193
lib/librefuse/fuse.h Normal file
View file

@ -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 <sys/types.h>
#include <puffs.h>
#include <utime.h>
#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 <fuse_opt.h>
#endif

70
lib/librefuse/fuse_opt.h Normal file
View file

@ -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_ */

243
lib/librefuse/refuse.3 Normal file
View file

@ -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.

1446
lib/librefuse/refuse.c Normal file

File diff suppressed because it is too large Load diff

368
lib/librefuse/refuse_opt.c Normal file
View file

@ -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 <sys/types.h>
#include <err.h>
#include <fuse.h>
#include <fuse_opt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View file

@ -0,0 +1,4 @@
# $NetBSD: shlib_version,v 1.3 2010/05/21 10:53:41 pooka Exp $
#
major=2
minor=0

View file

@ -10,6 +10,7 @@
#include <minix/syslib.h>
#include <minix/rs.h>
#include <paths.h>
#include <unistd.h>
#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,14 +103,39 @@ int mountflags;
}
/* start the fs-server if not using existing one */
if (!use_existing) {
/* 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) {
fprintf(stderr, "mount: couldn't run %s\n", cmd);
@ -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);

View file

@ -10,6 +10,26 @@ __weak_alias(pread, _pread)
#include <lib.h>
#include <unistd.h>
#include <minix/u64.h>
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)
{

View file

@ -8,6 +8,27 @@
__weak_alias(pwrite, _pwrite)
#endif
#include <minix/u64.h>
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;

View file

@ -15,4 +15,13 @@ struct ucred
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

View file

@ -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 */