From ee97bef8dd89a184a3515d56baff6c1a781f1ba6 Mon Sep 17 00:00:00 2001 From: des Date: Sat, 7 Apr 2001 19:51:12 +0000 Subject: Let pseudofs into the warmth of the FreeBSD CVS repo. It's not finished yet (I still have to find a way to implement process- dependent nodes without consuming too much memory, and the permission system needs tightening up), but it's becoming hard to work on without a repo (I've accidentally almost nuked it once already), and it works (except for the lack of process-dependent nodes, that is). I was supposed to commit this a week ago, but timed out waiting for jkh to reply to some questions I had. Pass him a spoonful of bad karma :) --- sys/fs/pseudofs/pseudofs.c | 172 +++++++++++++++ sys/fs/pseudofs/pseudofs.h | 181 ++++++++++++++++ sys/fs/pseudofs/pseudofs_fileno.c | 285 ++++++++++++++++++++++++ sys/fs/pseudofs/pseudofs_internal.h | 64 ++++++ sys/fs/pseudofs/pseudofs_vncache.c | 168 +++++++++++++++ sys/fs/pseudofs/pseudofs_vnops.c | 419 ++++++++++++++++++++++++++++++++++++ 6 files changed, 1289 insertions(+) create mode 100644 sys/fs/pseudofs/pseudofs.c create mode 100644 sys/fs/pseudofs/pseudofs.h create mode 100644 sys/fs/pseudofs/pseudofs_fileno.c create mode 100644 sys/fs/pseudofs/pseudofs_internal.h create mode 100644 sys/fs/pseudofs/pseudofs_vncache.c create mode 100644 sys/fs/pseudofs/pseudofs_vnops.c (limited to 'sys/fs/pseudofs') diff --git a/sys/fs/pseudofs/pseudofs.c b/sys/fs/pseudofs/pseudofs.c new file mode 100644 index 0000000..474fb3a --- /dev/null +++ b/sys/fs/pseudofs/pseudofs.c @@ -0,0 +1,172 @@ +/*- + * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +SYSCTL_NODE(_vfs, OID_AUTO, pfs, CTLFLAG_RW, 0, + "pseudofs"); + +/* + * Mount a pseudofs instance + */ +int +pfs_mount(struct pfs_info *pi, struct mount *mp, char *path, caddr_t data, + struct nameidata *ndp, struct proc *p) +{ + struct statfs *sbp; + + if (mp->mnt_flag & MNT_UPDATE) + return (EOPNOTSUPP); + + mp->mnt_flag |= MNT_LOCAL; + mp->mnt_data = (qaddr_t)pi; + vfs_getnewfsid(mp); + + sbp = &mp->mnt_stat; + bcopy(pi->pi_name, sbp->f_mntfromname, sizeof pi->pi_name); + sbp->f_bsize = PAGE_SIZE; + sbp->f_iosize = PAGE_SIZE; + sbp->f_blocks = 1; + sbp->f_bfree = 0; + sbp->f_bavail = 0; + sbp->f_files = 1; + sbp->f_ffree = 0; + + return (0); +} + +/* + * Unmount a pseudofs instance + */ +int +pfs_unmount(struct mount *mp, int mntflags, struct proc *p) +{ + struct pfs_info *pi; + int error; + + pi = (struct pfs_info *)mp->mnt_data; + + /* XXX do stuff with pi... */ + + error = vflush(mp, 0, (mntflags & MNT_FORCE) ? FORCECLOSE : 0); + return (error); +} + +/* + * Return a root vnode + */ +int +pfs_root(struct mount *mp, struct vnode **vpp) +{ + struct pfs_info *pi; + + pi = (struct pfs_info *)mp->mnt_data; + return pfs_vncache_alloc(mp, vpp, pi->pi_root); +} + +/* + * Return filesystem stats + */ +int +pfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p) +{ + bcopy(&mp->mnt_stat, sbp, sizeof *sbp); + return (0); +} + +/* + * Initialize a pseudofs instance + */ +int +pfs_init(struct pfs_info *pi, struct vfsconf *vfc) +{ + mtx_init(&pi->pi_mutex, "pseudofs", MTX_DEF); + pfs_fileno_init(pi); + printf("%s registered\n", pi->pi_name); + return (0); +} + +/* + * Destroy a pseudofs instance + */ +int +pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc) +{ + pfs_fileno_uninit(pi); + mtx_destroy(&pi->pi_mutex); + printf("%s unregistered\n", pi->pi_name); + return (0); +} + +/* + * Handle load / unload events + */ +static int +pfs_modevent(module_t mod, int evt, void *arg) +{ + switch (evt) { + case MOD_LOAD: + pfs_fileno_load(); + pfs_vncache_load(); + break; + case MOD_UNLOAD: + case MOD_SHUTDOWN: + pfs_vncache_unload(); + pfs_fileno_unload(); + break; + default: + printf("pseudofs: unexpected event type %d\n", evt); + break; + } + return 0; +} + +/* + * Module declaration + */ +static moduledata_t pseudofs_data = { + "pseudofs", + pfs_modevent, + NULL +}; +DECLARE_MODULE(pseudofs, pseudofs_data, SI_SUB_EXEC, SI_ORDER_FIRST); +MODULE_VERSION(pseudofs, 1); diff --git a/sys/fs/pseudofs/pseudofs.h b/sys/fs/pseudofs/pseudofs.h new file mode 100644 index 0000000..928d2db --- /dev/null +++ b/sys/fs/pseudofs/pseudofs.h @@ -0,0 +1,181 @@ +/*- + * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. + * + * $FreeBSD$ + */ + +#ifndef _PSEUDOFS_H_INCLUDED +#define _PSEUDOFS_H_INCLUDED + +/* + * Limits and constants + */ +#define PFS_NAMELEN 24 +#define PFS_DELEN (8 + PFS_NAMELEN) + +typedef enum { + pfstype_none = 0, + pfstype_root, + pfstype_dir, + pfstype_this, + pfstype_parent, + pfstype_file, + pfstype_symlink, + pfstype_procdep +} pfs_type_t; + +/* Flags */ +#define PFS_DYNAMIC 1 + +/* + * Data structures + */ +struct pfs_info; +struct pfs_node; +struct pfs_bitmap; + +typedef int (*pfs_fill_t)(struct pfs_node *, struct proc *, struct sbuf *); + +struct pfs_bitmap; /* opaque */ + +/* + * pfs_info: describes a pseudofs instance + */ +struct pfs_info { + char pi_name[MFSNAMELEN]; + struct pfs_node *pi_root; + /* members below this line aren't initialized */ + /* currently, the mutex is only used to protect the bitmap */ + struct mtx pi_mutex; + struct pfs_bitmap *pi_bitmap; +}; + +/* + * pfs_node: describes a node (file or directory) within a pseudofs + */ +struct pfs_node { + char pn_name[PFS_NAMELEN]; + pfs_type_t pn_type; + int pn_flags; + uid_t pn_uid; + gid_t pn_gid; + mode_t pn_mode; + union { + void *_pn_data; + pfs_fill_t _pn_func; + struct pfs_node *_pn_nodes; + } u1; +#define pn_data u1._pn_data +#define pn_func u1._pn_func +#define pn_nodes u1._pn_nodes + /* members below this line aren't initialized */ + struct pfs_node *pn_parent; + union { + u_int32_t _pn_fileno; + struct pfs_node *_pn_shadow; + } u2; +#define pn_fileno u2._pn_fileno +#define pn_shadow u2._pn_shadow +}; + +#define PFS_NODE(name, type, flags, uid, gid, mode, data) \ + { (name), (type), (flags), (uid), (gid), (mode), { (data) } } +#define PFS_DIR(name, flags, uid, gid, mode, nodes) \ + PFS_NODE(name, pfstype_dir, flags, uid, gid, mode, nodes) +#define PFS_ROOT(nodes) \ + PFS_NODE("/", pfstype_root, 0, 0, 0, 0555, nodes) +#define PFS_THIS \ + PFS_NODE(".", pfstype_this, 0, 0, 0, 0, NULL) +#define PFS_PARENT \ + PFS_NODE("..", pfstype_parent, 0, 0, 0, 0, NULL) +#define PFS_FILE(name, flags, uid, gid, mode, func) \ + PFS_NODE(name, pfstype_file, flags, uid, gid, mode, func) +#define PFS_SYMLINK(name, flags, uid, gid, mode, func) \ + PFS_NODE(name, pfstype_symlink, flags, uid, gid, mode, func) +#define PFS_LASTNODE \ + PFS_NODE("", pfstype_none, 0, 0, 0, 0, NULL) + +/* + * VFS interface + */ +int pfs_mount (struct pfs_info *pi, + struct mount *mp, char *path, caddr_t data, + struct nameidata *ndp, struct proc *p); +int pfs_unmount (struct mount *mp, int mntflags, + struct proc *p); +int pfs_root (struct mount *mp, struct vnode **vpp); +int pfs_statfs (struct mount *mp, struct statfs *sbp, + struct proc *p); +int pfs_init (struct pfs_info *pi, struct vfsconf *vfc); +int pfs_uninit (struct pfs_info *pi, struct vfsconf *vfc); + +/* + * Now for some initialization magic... + */ +#define PSEUDOFS(name, root) \ + \ +static struct pfs_info name##_info = { \ + #name, \ + &(root) \ +}; \ + \ +static int \ +_##name##_mount(struct mount *mp, char *path, caddr_t data, \ + struct nameidata *ndp, struct proc *p) { \ + return pfs_mount(&name##_info, mp, path, data, ndp, p); \ +} \ + \ +static int \ +_##name##_init(struct vfsconf *vfc) { \ + return pfs_init(&name##_info, vfc); \ +} \ + \ +static int \ +_##name##_uninit(struct vfsconf *vfc) { \ + return pfs_uninit(&name##_info, vfc); \ +} \ + \ +static struct vfsops testfs_vfsops = { \ + _##name##_mount, \ + vfs_stdstart, \ + pfs_unmount, \ + pfs_root, \ + vfs_stdquotactl, \ + pfs_statfs, \ + vfs_stdsync, \ + vfs_stdvget, \ + vfs_stdfhtovp, \ + vfs_stdcheckexp, \ + vfs_stdvptofh, \ + _##name##_init, \ + _##name##_uninit, \ + vfs_stdextattrctl, \ +}; \ +VFS_SET(name##_vfsops, name, VFCF_SYNTHETIC); \ +MODULE_DEPEND(name, pseudofs, 1, 1, 1); + +#endif diff --git a/sys/fs/pseudofs/pseudofs_fileno.c b/sys/fs/pseudofs/pseudofs_fileno.c new file mode 100644 index 0000000..877b7b8 --- /dev/null +++ b/sys/fs/pseudofs/pseudofs_fileno.c @@ -0,0 +1,285 @@ +/*- + * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +static MALLOC_DEFINE(M_PFSFILENO, "pseudofs_fileno", "pseudofs fileno bitmap"); + +static struct mtx pfs_fileno_mutex; + +#define PFS_BITMAP_SIZE 4096 +#define PFS_SLOT_BITS (sizeof(unsigned int) * CHAR_BIT) +#define PFS_BITMAP_BITS (PFS_BITMAP_SIZE * PFS_SLOT_BITS) +struct pfs_bitmap { + u_int32_t pb_offset; + int pb_used; + unsigned int pb_bitmap[PFS_BITMAP_SIZE]; + struct pfs_bitmap *pb_next; +}; + +/* + * Initialization + */ +void +pfs_fileno_load(void) +{ + mtx_init(&pfs_fileno_mutex, "pseudofs_fileno", MTX_DEF); +} + +/* + * Teardown + */ +void +pfs_fileno_unload(void) +{ + mtx_destroy(&pfs_fileno_mutex); +} + +/* + * Initialize fileno bitmap + */ +void +pfs_fileno_init(struct pfs_info *pi) +{ + struct pfs_bitmap *pb; + + MALLOC(pb, struct pfs_bitmap *, sizeof *pb, + M_PFSFILENO, M_WAITOK|M_ZERO); + + mtx_lock(&pi->pi_mutex); + + pb->pb_bitmap[0] = 07; + pb->pb_used = 3; + pi->pi_bitmap = pb; + pi->pi_root->pn_fileno = 2; + + mtx_unlock(&pi->pi_mutex); +} + +/* + * Tear down fileno bitmap + */ +void +pfs_fileno_uninit(struct pfs_info *pi) +{ + struct pfs_bitmap *pb, *npb; + int used; + + mtx_lock(&pi->pi_mutex); + + pb = pi->pi_bitmap; + pi->pi_bitmap = NULL; + + mtx_unlock(&pi->pi_mutex); + + for (used = 0; pb; pb = npb) { + npb = pb->pb_next; + used += pb->pb_used; + FREE(pb, M_PFSFILENO); + } + if (used > 2) + printf("WARNING: %d file numbers still in use\n", used); +} + +/* + * Get the next available file number + */ +static u_int32_t +pfs_get_fileno(struct pfs_info *pi) +{ + struct pfs_bitmap *pb, *ppb; + u_int32_t fileno; + unsigned int *p; + int i; + + mtx_lock(&pi->pi_mutex); + + /* look for the first page with free bits */ + for (ppb = NULL, pb = pi->pi_bitmap; pb; ppb = pb, pb = pb->pb_next) + if (pb->pb_used != PFS_BITMAP_BITS) + break; + + /* out of pages? */ + if (pb == NULL) { + mtx_unlock(&pi->pi_mutex); + MALLOC(pb, struct pfs_bitmap *, sizeof *pb, + M_PFSFILENO, M_WAITOK|M_ZERO); + mtx_lock(&pi->pi_mutex); + /* protect against possible race */ + while (ppb->pb_next) + ppb = ppb->pb_next; + pb->pb_offset = ppb->pb_offset + PFS_BITMAP_BITS; + ppb->pb_next = pb; + } + + /* find the first free slot */ + for (i = 0; i < PFS_BITMAP_SIZE; ++i) + if (pb->pb_bitmap[i] != UINT_MAX) + break; + + /* find the first available bit and flip it */ + fileno = pb->pb_offset + i * PFS_SLOT_BITS; + p = &pb->pb_bitmap[i]; + for (i = 0; i < PFS_SLOT_BITS; ++i, ++fileno) + if ((*p & (unsigned int)(1 << i)) == 0) + break; + KASSERT(i < PFS_SLOT_BITS, + ("slot has free bits, yet doesn't")); + *p |= (unsigned int)(1 << i); + ++pb->pb_used; + + mtx_unlock(&pi->pi_mutex); + + return fileno; +} + +/* + * Free a file number + */ +static void +pfs_free_fileno(struct pfs_info *pi, u_int32_t fileno) +{ + struct pfs_bitmap *pb; + unsigned int *p; + int i; + + mtx_lock(&pi->pi_mutex); + + /* find the right page */ + for (pb = pi->pi_bitmap; + pb && fileno >= PFS_BITMAP_BITS; + pb = pb->pb_next, fileno -= PFS_BITMAP_BITS) + /* nothing */ ; + KASSERT(pb, + ("fileno isn't in any bitmap")); + + /* find the right bit in the right slot and flip it */ + p = &pb->pb_bitmap[fileno / PFS_SLOT_BITS]; + i = fileno % PFS_SLOT_BITS; + KASSERT(*p & (unsigned int)(1 << i), + ("fileno is already free")); + *p &= ~((unsigned int)(1 << i)); + --pb->pb_used; + + mtx_unlock(&pi->pi_mutex); +} + +/* + * Allocate a file number + */ +void +pfs_fileno_alloc(struct pfs_info *pi, struct pfs_node *pn) +{ + /* make sure our parent has a file number */ + if (pn->pn_parent && !pn->pn_parent->pn_fileno) + pfs_fileno_alloc(pi, pn->pn_parent); + + switch (pn->pn_type) { + case pfstype_root: + case pfstype_dir: + case pfstype_file: + case pfstype_symlink: + pn->pn_fileno = pfs_get_fileno(pi); + break; + case pfstype_this: + KASSERT(pn->pn_parent != NULL, + ("pfstype_this node has no parent")); + pn->pn_fileno = pn->pn_parent->pn_fileno; + break; + case pfstype_parent: + KASSERT(pn->pn_parent != NULL, + ("pfstype_parent node has no parent")); + if (pn->pn_parent == pi->pi_root) { + pn->pn_fileno = pn->pn_parent->pn_fileno; + break; + } + KASSERT(pn->pn_parent->pn_parent != NULL, + ("pfstype_parent node has no grandparent")); + pn->pn_fileno = pn->pn_parent->pn_parent->pn_fileno; + break; + case pfstype_procdep: + KASSERT(1, + ("pfs_fileno_alloc() called for pfstype_procdep node")); + break; + case pfstype_none: + KASSERT(1, + ("pfs_fileno_alloc() called for pfstype_none node")); + break; + } + + printf("pfs_fileno_alloc(): %s: ", pi->pi_name); + if (pn->pn_parent) { + if (pn->pn_parent->pn_parent) { + printf("%s/", pn->pn_parent->pn_parent->pn_name); + } + printf("%s/", pn->pn_parent->pn_name); + } + printf("%s -> %d\n", pn->pn_name, pn->pn_fileno); +} + +/* + * Release a file number + */ +void +pfs_fileno_free(struct pfs_info *pi, struct pfs_node *pn) +{ + switch (pn->pn_type) { + case pfstype_root: + case pfstype_dir: + case pfstype_file: + case pfstype_symlink: + pfs_free_fileno(pi, pn->pn_fileno); + break; + case pfstype_this: + case pfstype_parent: + /* ignore these, as they don't "own" their file number */ + break; + case pfstype_procdep: + KASSERT(1, + ("pfs_fileno_free() called for pfstype_procdep node")); + break; + case pfstype_none: + KASSERT(1, + ("pfs_fileno_free() called for pfstype_none node")); + break; + } +} diff --git a/sys/fs/pseudofs/pseudofs_internal.h b/sys/fs/pseudofs/pseudofs_internal.h new file mode 100644 index 0000000..0e25c47 --- /dev/null +++ b/sys/fs/pseudofs/pseudofs_internal.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. + * + * $FreeBSD$ + */ + +#ifndef _PSEUDOFS_INTERNAL_H_INCLUDED +#define _PSEUDOFS_INTERNAL_H_INCLUDED + +/* + * Sysctl subtree + */ +SYSCTL_DECL(_vfs_pfs); + +/* + * Vnode cache + */ +void pfs_vncache_load (void); +void pfs_vncache_unload (void); +int pfs_vncache_alloc (struct mount *, struct vnode **, + struct pfs_node *); +int pfs_vncache_free (struct vnode *); + +/* + * File number bitmap + */ +void pfs_fileno_load (void); +void pfs_fileno_unload (void); +void pfs_fileno_init (struct pfs_info *); +void pfs_fileno_uninit (struct pfs_info *); +void pfs_fileno_alloc (struct pfs_info *, struct pfs_node *); +void pfs_fileno_free (struct pfs_info *, struct pfs_node *); + +/* + * Shadow manager + */ +void pfs_create_shadow (struct pfs_info *, struct pfs_node *, pid_t); +void pfs_reap_shadows (pid_t); + +#endif diff --git a/sys/fs/pseudofs/pseudofs_vncache.c b/sys/fs/pseudofs/pseudofs_vncache.c new file mode 100644 index 0000000..86b837e --- /dev/null +++ b/sys/fs/pseudofs/pseudofs_vncache.c @@ -0,0 +1,168 @@ +/*- + * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static MALLOC_DEFINE(M_PFSVNCACHE, "pseudofs_vncache", "pseudofs vnode cache"); + +static struct mtx pfs_vncache_mutex; + +struct pfs_vnode { + struct vnode *pv_vnode; + struct pfs_vnode *pv_next; +} *pfs_vncache; + +SYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, + "pseudofs vnode cache"); + +static int pfs_vncache_hits; +SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, &pfs_vncache_hits, 0, + "number of cache hits since initialization"); + +static int pfs_vncache_misses; +SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, &pfs_vncache_misses, 0, + "number of cache misses since initialization"); + +extern vop_t **pfs_vnodeop_p; + +/* + * Initialize vnode cache + */ +void +pfs_vncache_load(void) +{ + mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", MTX_DEF); +} + +/* + * Tear down vnode cache + */ +void +pfs_vncache_unload(void) +{ + mtx_destroy(&pfs_vncache_mutex); +} + +/* + * Allocate a vnode + */ +int +pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, struct pfs_node *pn) +{ + struct pfs_vnode *pv; + int error; + + mtx_lock(&pfs_vncache_mutex); + + /* see if the vnode is in the cache */ + for (pv = pfs_vncache; pv; pv = pv->pv_next) + if (pv->pv_vnode->v_data == pn) + if (vget(pv->pv_vnode, 0, curproc) == 0) { + ++pfs_vncache_hits; + *vpp = pv->pv_vnode; + mtx_unlock(&pfs_vncache_mutex); + return (0); + } + ++pfs_vncache_misses; + + /* nope, get a new one */ + MALLOC(pv, struct pfs_vnode *, sizeof *pv, M_PFSVNCACHE, M_WAITOK); + error = getnewvnode(VT_PSEUDOFS, mp, pfs_vnodeop_p, vpp); + if (error) { + mtx_unlock(&pfs_vncache_mutex); + return (error); + } + (*vpp)->v_data = pn; + switch (pn->pn_type) { + case pfstype_root: + (*vpp)->v_flag = VROOT; +#if 0 + printf("root vnode allocated\n"); +#endif + case pfstype_dir: + case pfstype_this: + case pfstype_parent: + (*vpp)->v_type = VDIR; + break; + case pfstype_file: + (*vpp)->v_type = VREG; + break; + case pfstype_symlink: + (*vpp)->v_type = VLNK; + break; + default: + panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); + } + pv->pv_vnode = *vpp; + pv->pv_next = pfs_vncache; + pfs_vncache = pv; + mtx_unlock(&pfs_vncache_mutex); + return (0); +} + +/* + * Free a vnode + */ +int +pfs_vncache_free(struct vnode *vp) +{ + struct pfs_vnode *prev, *pv; + + mtx_lock(&pfs_vncache_mutex); + for (prev = NULL, pv = pfs_vncache; pv; prev = pv, pv = pv->pv_next) + if (pv->pv_vnode == vp) + break; + if (!pv) + printf("pfs_vncache_free(): not in cache\n"); /* it should be! */ +#if 0 + if (vp->v_data == ((struct pfs_info *)vp->v_mount->mnt_data)->pi_root) + printf("root vnode reclaimed\n"); +#endif + vp->v_data = NULL; + if (pv) { + if (prev) + prev->pv_next = pv->pv_next; + else + pfs_vncache = pv->pv_next; + FREE(pv, M_PFSVNCACHE); + } + mtx_unlock(&pfs_vncache_mutex); + return (0); +} diff --git a/sys/fs/pseudofs/pseudofs_vnops.c b/sys/fs/pseudofs/pseudofs_vnops.c new file mode 100644 index 0000000..5516100 --- /dev/null +++ b/sys/fs/pseudofs/pseudofs_vnops.c @@ -0,0 +1,419 @@ +/*- + * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Verify permissions + */ +static int +pfs_access(struct vop_access_args *va) +{ + struct vnode *vn = va->a_vp; + struct vattr vattr; + int error; + + error = VOP_GETATTR(vn, &vattr, va->a_cred, va->a_p); + if (error) + return (error); + error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid, + vattr.va_gid, va->a_mode, va->a_cred, NULL); + return (error); +} + +/* + * Close a file or directory + */ +static int +pfs_close(struct vop_close_args *va) +{ + return (0); +} + +/* + * Get file attributes + */ +static int +pfs_getattr(struct vop_getattr_args *va) +{ + struct vnode *vn = va->a_vp; + struct pfs_node *pn = (struct pfs_node *)vn->v_data; + struct vattr *vap = va->a_vap; + + VATTR_NULL(vap); + vap->va_type = vn->v_type; + vap->va_mode = pn->pn_mode; + vap->va_fileid = pn->pn_fileno; + vap->va_flags = 0; + vap->va_blocksize = PAGE_SIZE; + vap->va_bytes = vap->va_size = 0; + vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0]; + vap->va_nlink = 1; + nanotime(&vap->va_ctime); + vap->va_atime = vap->va_mtime = vap->va_ctime; + vap->va_uid = pn->pn_uid; + vap->va_gid = pn->pn_gid; + + return (0); +} + +/* + * Look up a file or directory + */ +static int +pfs_lookup(struct vop_lookup_args *va) +{ + struct vnode *dvp = va->a_dvp; + struct vnode **vpp = va->a_vpp; + struct componentname *cnp = va->a_cnp; +#if 0 + struct pfs_info *pi = (struct pfs_info *)dvp->v_mount->mnt_data; +#endif + struct pfs_node *pd = (struct pfs_node *)dvp->v_data, *pn; + struct proc *p; + char *pname; + int error, i; + pid_t pid; + + if (dvp->v_type != VDIR) + return (ENOTDIR); + + /* don't support CREATE, RENAME or DELETE */ + if (cnp->cn_nameiop != LOOKUP) + return (EROFS); + + /* shortcut */ + if (cnp->cn_namelen >= PFS_NAMELEN) + return (ENOENT); + + /* self */ + pname = cnp->cn_nameptr; + if (cnp->cn_namelen == 1 && *pname == '.') { + pn = pd; + *vpp = dvp; + VREF(dvp); + goto got_vnode; + } + + /* parent */ + if (cnp->cn_flags & ISDOTDOT) { + if (pd->pn_type == pfstype_root) + return (EIO); + KASSERT(pd->pn_parent, ("non-root directory has no parent")); + return pfs_vncache_alloc(dvp->v_mount, vpp, pd->pn_parent); + } + + /* process dependent */ + for (i = 0, pid = 0; i < cnp->cn_namelen && isdigit(pname[i]); ++i) + pid = pid * 10 + pname[i] - '0'; + /* XXX assume that 8 digits is the maximum safe length for a pid */ + if (i == cnp->cn_namelen && i < 8) { + /* see if this directory has process-dependent children */ + for (pn = pd->pn_nodes; pn->pn_type; ++pn) + if (pn->pn_type == pfstype_procdep) + break; + if (pn->pn_type) { + /* XXX pfind(0) should DTRT here */ + p = pid ? pfind(pid) : &proc0; + if (p == NULL) + return (ENOENT); + if (p_can(cnp->cn_proc, p, P_CAN_SEE, NULL)) + /* pretend it doesn't exist */ + return (ENOENT); +#if 0 + if (!pn->pn_shadow) + pfs_create_shadow(pn, p); + pn = pn->pn_shadow; + goto got_pnode; +#else + /* not yet implemented */ + return (EIO); +#endif + } + } + + /* something else */ + for (pn = pd->pn_nodes; pn->pn_type; ++pn) { + for (i = 0; i < cnp->cn_namelen && pn->pn_name[i]; ++i) + if (pname[i] != pn->pn_name[i]) + break; + if (i == cnp->cn_namelen) + goto got_pnode; + } + + return (ENOENT); + got_pnode: + if (!pn->pn_parent) + pn->pn_parent = pd; + error = pfs_vncache_alloc(dvp->v_mount, vpp, pn); + if (error) + return error; + got_vnode: + if (cnp->cn_flags & MAKEENTRY) + cache_enter(dvp, *vpp, cnp); + return (0); +} + +/* + * Open a file or directory. + */ +static int +pfs_open(struct vop_open_args *va) +{ + /* XXX */ + return (0); +} + +/* + * Read from a file + */ +static int +pfs_read(struct vop_read_args *va) +{ + struct vnode *vn = va->a_vp; + struct pfs_node *pn = vn->v_data; + struct uio *uio = va->a_uio; + struct sbuf sb; + char *ps; + int error, xlen; + + if (vn->v_type != VREG) + return (EINVAL); + + error = sbuf_new(&sb, NULL, uio->uio_offset + uio->uio_resid, 0); + if (error) + return (EIO); + + error = (pn->pn_func)(pn, curproc, &sb); + + /* XXX we should possibly detect and handle overflows */ + sbuf_finish(&sb); + ps = sbuf_data(&sb) + uio->uio_offset; + xlen = sbuf_len(&sb) - uio->uio_offset; + xlen = imin(xlen, uio->uio_resid); + error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio)); + sbuf_delete(&sb); + return (error); +} + +/* + * Return directory entries. + */ +static int +pfs_readdir(struct vop_readdir_args *va) +{ + struct vnode *vn = va->a_vp; + struct pfs_info *pi; + struct pfs_node *pd, *pn; + struct dirent entry; + struct uio *uio; +#if 0 + struct proc *p; +#endif + off_t offset; + int error, i, resid; + + if (vn->v_type != VDIR) + return (ENOTDIR); + pi = (struct pfs_info *)vn->v_mount->mnt_data; + pd = (struct pfs_node *)vn->v_data; + pn = pd->pn_nodes; + uio = va->a_uio; + + /* only allow reading entire entries */ + offset = uio->uio_offset; + resid = uio->uio_resid; + if (offset < 0 || offset % PFS_DELEN != 0 || resid < PFS_DELEN) + return (EINVAL); + + /* skip unwanted entries */ + for (; pn->pn_type && offset > 0; ++pn, offset -= PFS_DELEN) + /* nothing */ ; + + /* fill in entries */ + entry.d_reclen = PFS_DELEN; + for (; pn->pn_type && resid > 0; ++pn) { + if (!pn->pn_parent) + pn->pn_parent = pd; + if (!pn->pn_fileno) + pfs_fileno_alloc(pi, pn); + entry.d_fileno = pn->pn_fileno; + /* PFS_DELEN was picked to fit PFS_NAMLEN */ + for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i) + entry.d_name[i] = pn->pn_name[i]; + entry.d_name[i] = 0; + entry.d_namlen = i; + switch (pn->pn_type) { + case pfstype_root: + case pfstype_dir: + case pfstype_this: + case pfstype_parent: + entry.d_type = DT_DIR; + break; + case pfstype_file: + entry.d_type = DT_REG; + break; + case pfstype_symlink: + entry.d_type = DT_LNK; + break; + case pfstype_procdep: + /* don't handle process-dependent nodes here */ + continue; + default: + panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type); + } + if ((error = uiomove((caddr_t)&entry, PFS_DELEN, uio))) + return (error); + offset += PFS_DELEN; + resid -= PFS_DELEN; + } +#if 0 + for (pn = pd->pn_nodes; pn->pn_type && resid > 0; ++pn) { + if (pn->pn_type != pfstype_procdep) + continue; + + sx_slock(&allproc_lock); + p = LIST_FIRST(&allproc); + + sx_sunlock(&allproc_lock); + offset += PFS_DELEN; + resid -= PFS_DELEN; + break; + } +#endif + + uio->uio_offset += offset; + return (0); +} + +/* + * Read a symbolic link + */ +static int +pfs_readlink(struct vop_readlink_args *va) +{ + struct vnode *vn = va->a_vp; + struct pfs_node *pn = vn->v_data; + struct uio *uio = va->a_uio; + char buf[MAXPATHLEN], *ps; + struct sbuf sb; + int error, xlen; + + if (vn->v_type != VLNK) + return (EINVAL); + + /* sbuf_new() can't fail with a static buffer */ + sbuf_new(&sb, buf, sizeof buf, 0); + + error = (pn->pn_func)(pn, curproc, &sb); + + /* XXX we should detect and handle overflows */ + sbuf_finish(&sb); + ps = sbuf_data(&sb) + uio->uio_offset; + xlen = sbuf_len(&sb) - uio->uio_offset; + xlen = imin(xlen, uio->uio_resid); + error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio)); + sbuf_delete(&sb); + return (error); +} + +/* + * Reclaim a vnode + */ +static int +pfs_reclaim(struct vop_reclaim_args *va) +{ + return (pfs_vncache_free(va->a_vp)); +} + +/* + * Set attributes + */ +static int +pfs_setattr(struct vop_setattr_args *va) +{ + if (va->a_vap->va_flags != VNOVAL) + return (EOPNOTSUPP); + return (0); +} + +/* + * Dummy operations + */ +static int pfs_erofs(void *va) { return (EROFS); } +static int pfs_null(void *va) { return (0); } + +/* + * Vnode operations + */ +vop_t **pfs_vnodeop_p; +static struct vnodeopv_entry_desc pfs_vnodeop_entries[] = { + { &vop_default_desc, (vop_t *)vop_defaultop }, + { &vop_access_desc, (vop_t *)pfs_access }, + { &vop_close_desc, (vop_t *)pfs_close }, + { &vop_create_desc, (vop_t *)pfs_erofs }, + { &vop_getattr_desc, (vop_t *)pfs_getattr }, + { &vop_link_desc, (vop_t *)pfs_erofs }, + { &vop_lookup_desc, (vop_t *)pfs_lookup }, + { &vop_mkdir_desc, (vop_t *)pfs_erofs }, + { &vop_open_desc, (vop_t *)pfs_open }, + { &vop_read_desc, (vop_t *)pfs_read }, + { &vop_readdir_desc, (vop_t *)pfs_readdir }, + { &vop_readlink_desc, (vop_t *)pfs_readlink }, + { &vop_reclaim_desc, (vop_t *)pfs_reclaim }, + { &vop_remove_desc, (vop_t *)pfs_erofs }, + { &vop_rename_desc, (vop_t *)pfs_erofs }, + { &vop_rmdir_desc, (vop_t *)pfs_erofs }, + { &vop_setattr_desc, (vop_t *)pfs_setattr }, + { &vop_symlink_desc, (vop_t *)pfs_erofs }, + { &vop_write_desc, (vop_t *)pfs_erofs }, + /* XXX I've probably forgotten a few that need pfs_erofs */ + { NULL, (vop_t *)NULL } +}; + +static struct vnodeopv_desc pfs_vnodeop_opv_desc = + { &pfs_vnodeop_p, pfs_vnodeop_entries }; + +VNODEOP_SET(pfs_vnodeop_opv_desc); + -- cgit v1.1