This commit is contained in:
seta75D
2021-10-11 18:37:13 -03:00
commit ff309bfe1c
14130 changed files with 3180272 additions and 0 deletions

10
sys/tmpfs/Makefile Normal file
View File

@@ -0,0 +1,10 @@
#
# @(#)Makefile 1.1 94/10/31 SMI
#
HFILES = tmp.h tmpnode.h tmpdir.h
HDIR=$(DESTDIR)/usr/include/tmpfs
install_h: $(HFILES)
install -d -m 755 $(HDIR)
install -m 444 $(HFILES) $(HDIR)

81
sys/tmpfs/tmp.h Normal file
View File

@@ -0,0 +1,81 @@
/* @(#)tmp.h 1.1 94/10/31 SMI */
/*
* Utility and library routines that look at directories want to
* `see' something like an inode. While each tmpnode has a unique
* number (its memory address), the address turns out (signed long)
* to be unsuitable for library utilities. The following data structure
* is used to allocate a (small) tmpfs index number for these purposes.
*/
#define TMPIMAPNODES 128
#define TMPIMAPSIZE TMPIMAPNODES/NBBY
#define MAXMNTLEN 512 /* max length of pathname tmpfs is mounted on */
struct tmpimap {
u_char timap_bits[TMPIMAPSIZE]; /* bitmap of available index numbers */
/* 0 == free number */
struct tmpimap *timap_next; /* ptr to more index numbers */
};
/*
* Temporary file system per-mount data and other random stuff
* There is a linked list of these things rooted at tmpfs_mountp
*/
struct tmount {
struct tmount *tm_next; /* for linked list */
struct vfs *tm_vfsp; /* filesystem's vfs struct */
struct tmpnode *tm_rootnode; /* root tmpnode */
u_int tm_mntno; /* minor # of mounted `device' */
struct tmpimap tm_inomap; /* inode allocator maps */
u_int tm_direntries; /* number of directory entries */
u_int tm_directories; /* number of directories */
u_int tm_files; /* number of regular files */
u_int tm_kmemspace; /* bytes of kmem_alloc'd memory */
u_int tm_anonmem; /* bytes of anon memory actually used */
char tm_mntpath[MAXMNTLEN]; /* name of tmpfs mount point */
};
#ifdef KERNEL
char *tmp_memalloc();
void tmp_memfree();
#define GET_TIME(tv) ((*tv) = time)
#define VFSP_TO_TM(vfsp) ((struct tmount *)(vfsp)->vfs_data)
#define VP_TO_TM(vp) ((struct tmount *)(vp)->v_vfsp->vfs_data)
#define VP_TO_TN(vp) ((struct tmpnode *)(vp)->v_data)
#endif KERNEL
/*
* Don't allocate more anon pages for tmp files if free anon space
* goes under TMPHIWATER. Hideous deadlocks can occur. This can be
* patched in tmpfs_hiwater.
* XXX better heuristic needed
*/
#define TMPHIWATER 4*1024*1024 /* ie, 4 Megabytes */
/*
* Each tmpfs can allocate only a certain amount of kernel memory,
* which is used for directories (may change), anon maps, inode maps,
* and other goodies. This is statically set (during first tmp_mount())
* as a percent of physmem. The actual percentage can be patched in
* tmpfs_maxprockmem.
* XXX better heuristic needed
*/
#define TMPMAXPROCKMEM 2 /* Means 2 procent of physical memory */
/*
* patchable variables controlling debugging output
* defined in tmp_vnodeops XXX
*/
#define TMPFSDEBUG 1 /* XXX REMOVE ALL OF THESE FOR FCS!! */
#ifdef TMPFSDEBUG
extern int tmpfsdebug; /* general debugging (e.g. function calls) */
extern int tmpdebugerrs; /* report non-fatal error conditions */
extern int tmplockdebug; /* report on tmpnode locking and unlocking */
extern int tmpdirdebug; /* report of file and directory manipulation */
extern int tmprwdebug; /* read/write debugging */
extern int tmpdebugalloc; /* tmpfs memory and swap allocation */
#endif TMPFSDEBUG

192
sys/tmpfs/tmp_dir.c Normal file
View File

@@ -0,0 +1,192 @@
/* @(#)tmp_dir.c 1.1 94/10/31 SMI */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/errno.h>
#include <sys/ucred.h>
#include <sys/stat.h>
#include <debug/debug.h>
#include <tmpfs/tmpnode.h>
#include <tmpfs/tmp.h>
#include <tmpfs/tmpdir.h>
#include <sys/debug.h>
/*
* search directory 'parent' for entry 'name'
* return tmpnode LOCKED in 'foundtp'
* XXX should use dnlc
*/
tdirlookup(parent, name, foundtp, required_perms, cred)
struct tmpnode *parent;
char *name;
struct tmpnode **foundtp;
int required_perms;
struct ucred *cred;
{
int error;
struct tdirent *tdp;
extern func_t caller();
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdirdebug)
printf("tdirlookup: parent %x name %s\n", parent, name);
#endif TMPFSDEBUG
if (error = taccess(parent, cred, required_perms))
return (error);
for (tdp = parent->tn_dir; tdp; tdp = tdp->td_next) {
if (strcmp(tdp->td_name, name) == 0) {
*foundtp = tdp->td_tmpnode;
tmpnode_get(*foundtp);
#ifdef TMPFSDEBUG
if (tmpdirdebug)
printf("tdirlookup: returning %x\n", *foundtp);
#endif TMPFSDEBUG
tdirclose(parent);
return (0);
}
}
tdirclose(parent);
return (ENOENT);
}
/*
* enter 'name' and tmpnode * 'tp' into directory of tmpnode 'dir' on tmpfs 'tm'
*/
tdirenter(tm, dir, name, tp, required_perms, cred)
struct tmount *tm;
struct tmpnode *dir;
char *name;
struct tmpnode *tp;
int required_perms;
struct ucred *cred;
{
int error;
struct tdirent tdtemplate, *tdp;
struct tdirent *tpdp;
extern char *strcpy();
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdirdebug)
printf("tdirenter: tm %x dir %x name %s tp %x\n", tm, dir,
name, tp);
#endif TMPFSDEBUG
/* check permissions */
if (error = taccess(dir, cred, required_perms))
return (error);
/* set up the directory entry template */
tdtemplate.td_namelen = strlen(name);
tdtemplate.td_reclen = TDIRSIZ(&tdtemplate);
if ((tdp = (struct tdirent *)tmp_memalloc(tm,
tdtemplate.td_reclen)) == NULL) {
tdirclose(dir);
#ifdef TMPFSDEBUG
if (tmpdebugerrs || tmpdirdebug)
printf("tdirenter:- no space\n");
#endif TMPFSDEBUG
return (ENOSPC);
}
dir->tn_attr.va_size += tdtemplate.td_reclen;
/* now initialize the real dir entry */
tdp->td_namelen = tdtemplate.td_namelen;
tdp->td_reclen = tdtemplate.td_reclen;
tdp->td_tmpnode = tp;
tdp->td_next = NULL;
(void) strcpy(tdp->td_name, name);
/* check for virgin directory */
if ((tpdp = dir->tn_dir) == NULL)
dir->tn_dir = tdp;
else { /* install at end of of directory list */
/* XXX FIX ME should install after . && .. ?? */
while (tpdp->td_next != NULL)
tpdp = tpdp->td_next;
tpdp->td_next = tdp;
}
tdirclose(dir);
tp->tn_attr.va_nlink++;
tm->tm_direntries++;
return (0);
}
/*
* Delete entry whose tmpnode is tp from directory 'dir' on tmpfs 'tm'.
* Remember to free dir entry space and decrement link count on tmpnode 'tp'
*/
/*ARGSUSED*/
tdirdelete(tm, dir, tp, nm, cred)
register struct tmount *tm;
register struct tmpnode *dir, *tp;
register char *nm;
struct ucred *cred;
{
register struct tdirent *tpdp, *lasttdp = NULL;
register int error;
register u_int size;
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdirdebug)
printf("tdirdelete: tm %x dir %x tp %x nm %s\n", tm, dir,
tp, nm);
#endif TMPFSDEBUG
if (error = taccess(dir, cred, VWRITE)) {
return (error);
}
if ((dir->tn_attr.va_mode & TSVTX) && cred->cr_uid != 0 &&
cred->cr_uid != dir->tn_attr.va_uid &&
tp->tn_attr.va_uid != cred->cr_uid) {
return (EPERM);
}
if ((tpdp = dir->tn_dir) == NULL)
panic("null directory list");
for (; tpdp; lasttdp = tpdp, tpdp = tpdp->td_next) {
if ((tp == tpdp->td_tmpnode) &&
(strcmp(tpdp->td_name, nm) == 0)) {
size = tpdp->td_reclen;
dir->tn_attr.va_size -= size;
tm->tm_direntries--;
lasttdp->td_next = tpdp->td_next;
tmp_memfree(tm, (char *)tpdp, size);
ASSERT(tp->tn_attr.va_nlink > 0);
tp->tn_attr.va_nlink--;
if (tp->tn_attr.va_type == VDIR && tp != dir) {
/*
* account for ".." entry in directory
*/
dir->tn_attr.va_nlink--;
}
return (0);
}
}
return (ENOENT);
}
/*
* check directory permission before doing an operation
*/
tdiropen(dir, cred)
struct tmpnode *dir;
struct ucred *cred;
{
int error;
if (error = taccess(dir, cred, VREAD)) {
return (error);
}
if (dir->tn_attr.va_type != VDIR) {
return (ENOTDIR);
}
return (0);
}
/*ARGSUSED*/
tdirclose(tp)
struct tmpnode *tp;
{
}

232
sys/tmpfs/tmp_subr.c Normal file
View File

@@ -0,0 +1,232 @@
/* @(#)tmp_subr.c 1.1 94/10/31 SMI */
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <debug/debug.h>
#include <vm/anon.h>
#include <vm/seg_map.h>
#include <sys/time.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/ucred.h>
#include <tmpfs/tmp.h>
#include <tmpfs/tmpnode.h>
newnode(t, mode, uid, gid)
struct tmpnode *t;
u_int mode;
uid_t uid;
gid_t gid;
{
t->tn_attr.va_mode = mode;
t->tn_attr.va_uid = uid;
t->tn_attr.va_gid = gid;
t->tn_attr.va_rdev = 0;
created(t);
}
created(t)
struct tmpnode *t;
{
modified(t);
t->tn_attr.va_ctime = t->tn_attr.va_mtime;
}
modified(t)
struct tmpnode *t;
{
accessed(t);
t->tn_attr.va_mtime = t->tn_attr.va_atime;
}
accessed(t)
struct tmpnode *t;
{
extern struct timeval time;
t->tn_attr.va_atime = time;
}
isparent(from, to)
struct tmpnode *from;
struct tmpnode *to;
{
int error;
struct tmpnode *prev_tp, *curr_tp;
struct ucred rootcred;
rootcred.cr_uid = 0;
curr_tp = to;
tmpnode_get(curr_tp);
while (from != curr_tp) {
error = tdirlookup(curr_tp, "..", &prev_tp, VEXEC, &rootcred);
if (error) {
tmpnode_put(curr_tp);
return (0);
}
if (curr_tp == prev_tp) { /* at root */
tmpnode_put(curr_tp);
tmpnode_put(prev_tp);
return (0);
}
tmpnode_put(curr_tp);
curr_tp = prev_tp;
}
tmpnode_put(curr_tp);
return (1);
}
#define MODESHIFT 3
taccess(tp, cred, access)
struct tmpnode *tp;
struct ucred *cred;
int access;
{
/*
* Superuser always gets access
*/
if (cred->cr_uid == 0)
return (0);
/*
* Check access based on owner, group and
* public permissions in tmpnode.
*/
if (cred->cr_uid != tp->tn_attr.va_uid) {
access >>= MODESHIFT;
if (ingroup(tp->tn_attr.va_gid, cred) == 0)
access >>= MODESHIFT;
}
if ((tp->tn_attr.va_mode & access) == access)
return (0);
return (EACCES);
}
ingroup(gid, cred)
gid_t gid;
struct ucred *cred;
{
int i;
if (gid == cred->cr_gid) {
return (1);
}
for (i = 0; i < NGROUPS && cred->cr_groups[i] != NOGROUP; i++) {
if (gid == cred->cr_groups[i]) {
return (1);
}
}
return (0);
}
/*
* alloc index numbers (inode #) for a new tmpfs file.
* vfs_getnum() does part of the job for us. hope no one hacks on it.
*/
long
tmpimapalloc(tm)
register struct tmount *tm;
{
register struct tmpimap *tmapp;
register int i, bitnum;
for (i = 0, tmapp = &tm->tm_inomap; tmapp;
i++, tmapp = tmapp->timap_next) {
if ((bitnum = vfs_getnum((char *)tmapp->timap_bits,
TMPIMAPSIZE)) != -1)
return ((i*TMPIMAPNODES)+bitnum);
if (tmapp->timap_next == (struct tmpimap *)NULL)
tmapp->timap_next = (struct tmpimap *)tmp_memalloc(tm,
sizeof (struct tmpimap));
}
return (-1);
}
/*
* free index number of a (being destroyed) tmpfs file
* vfs_putnum() does part of the job for us. hope no one hacks on it.
* XXX should free allocated tmpimap struct memory when files free up
*/
tmpimapfree(tm, number)
register struct tmount *tm;
register long number;
{
register int i;
register struct tmpimap *tmapp;
void vfs_putnum();
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdirdebug)
printf("tmpimapfree: freeing tm %x number %d index %d\n", tm,
number, (number % TMPIMAPNODES));
#endif TMPFSDEBUG
for (i = 1, tmapp = &tm->tm_inomap; tmapp;
i++, tmapp = tmapp->timap_next) {
if (number < i*TMPIMAPNODES) { /* this is our map! */
vfs_putnum((char *)tmapp->timap_bits,
(int)(number % TMPIMAPNODES));
return (0);
}
}
return (-1);
}
/*
* tmpfs kernel memory allocation
* does some bookkeeping, calls kmem_zalloc() for the honey
*/
char *
tmp_memalloc(tm, size)
register struct tmount *tm;
register u_int size;
{
extern u_int tmpfs_maxkmem;
extern int tmp_kmemspace;
#ifdef TMPFSDEBUG
extern func_t caller();
#endif TMPFSDEBUG
#ifdef TMPFSDEBUG
if (tmpdebugalloc)
printf("tmp_memalloc: tm %x size %x\n", tm, size);
#endif TMPFSDEBUG
if ((tm->tm_kmemspace + size) < tmpfs_maxkmem) {
tm->tm_kmemspace += size;
tmp_kmemspace += size;
return (new_kmem_zalloc(size, KMEM_SLEEP));
}
#ifdef TMPFSDEBUG
if (tmpdebugerrs)
printf("tmp_memalloc: out of space. Called by %x\n", caller());
#endif TMPFSDEBUG
return (NULL);
}
/*
* tmpfs kernel memory freer
* does some bookkeeping, calls kmem_free()
*/
void
tmp_memfree(tm, cp, size)
register struct tmount *tm;
register char *cp;
register u_int size;
{
#ifdef TMPFSDEBUG
if (tmpdebugalloc)
printf("tmp_memfree: tm %x cp %x size %d\n", tm, cp, size);
#endif TMPFSDEBUG
if (tm->tm_kmemspace < size) {
panic("tmp_memfree()- bad memory bookkeeping");
}
kmem_free(cp, size);
tm->tm_kmemspace -= size;
tmp_kmemspace -= size;
}

688
sys/tmpfs/tmp_tnode.c Normal file
View File

@@ -0,0 +1,688 @@
/* @(#)tmp_tnode.c 1.1 94/10/31 SMI */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/ucred.h>
#include <sys/user.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/errno.h>
#include <sys/kmem_alloc.h>
#include <debug/debug.h>
#include <vm/seg.h>
#include <vm/seg_map.h>
#include <vm/anon.h>
#include <vm/page.h>
#include <tmpfs/tmp.h>
#include <tmpfs/tmpnode.h>
#include <tmpfs/tmpdir.h>
#include <sys/debug.h>
static struct anon_map *tmpamap_freelist;
static int tmpamap_freeincr = 4;
extern func_t caller();
extern int tmp_anonmem;
extern int tmp_files;
extern int tmp_anonalloc;
struct tmpnode *
tmpnode_alloc(tm, type)
struct tmount *tm;
enum vtype type;
{
struct tmpnode *t;
extern struct vnodeops tmp_vnodeops;
extern long tmpimapalloc();
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc)
printf("tmpnode_alloc: tm %x type %d\n", tm, type);
#endif TMPFSDEBUG
t = (struct tmpnode *)tmp_memalloc(tm, sizeof (struct tmpnode));
if (t == NULL)
return (NULL);
/*
* Muck with the doubly linked lists, if appropriate
*/
if (tm->tm_rootnode != (struct tmpnode *)NULL) {
if ((t->tn_forw = tm->tm_rootnode->tn_forw) != NULL)
t->tn_forw->tn_back = t;
t->tn_back = tm->tm_rootnode;
tm->tm_rootnode->tn_forw = t;
}
t->tn_attr.va_nodeid = tmpimapalloc(tm);
t->tn_attr.va_type = type;
t->tn_attr.va_blocksize = PAGESIZE;
t->tn_attr.va_fsid = 0xFFFF &
(long)makedev(vfs_fixedmajor(tm->tm_vfsp), 0xFF & tm->tm_mntno);
t->tn_vnode.v_vfsp = tm->tm_vfsp;
t->tn_vnode.v_type = type;
t->tn_vnode.v_data = (char *)t;
t->tn_vnode.v_op = &tmp_vnodeops;
t->tn_amapp = (struct anon_map *)new_kmem_fast_alloc(
(caddr_t *)&tmpamap_freelist, sizeof (*tmpamap_freelist),
tmpamap_freeincr, KMEM_SLEEP);
t->tn_amapp->refcnt = 1; /* keep till last link OR mapping */
t->tn_amapp->size = 0;
t->tn_amapp->swresv = 0;
t->tn_amapp->flags = 0;
t->tn_amapp->anon = (struct anon **)NULL;
switch (type) {
case VDIR:
tm->tm_directories++;
tmp_files++;
break;
case VREG:
case VBLK:
case VCHR:
case VLNK:
case VSOCK:
case VFIFO:
tm->tm_files++;
tmp_files++;
break;
default:
panic("tmpnode_alloc()- unknown file type\n");
break;
}
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc)
printf("tmpnode_alloc: returning tp %x\n", t);
#endif TMPFSDEBUG
tmpnode_get(t);
return (t);
}
void
tmpnode_get(tp)
struct tmpnode *tp;
{
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmplockdebug)
printf("tmpnode_get: tp %x count %d caller %x\n", tp,
tp->tn_count, caller());
#endif TMPFSDEBUG
tp->tn_flags |= TREF;
#ifdef TMPFSDEBUG
if (tmplockdebug && (tp->tn_count > 0) &&
(tp->tn_owner != uniqpid()))
printf("tmpnode_get: gonna sleep on lock!\n");
#endif TMPFSDEBUG
(void) tmpnode_lock(tp);
VN_HOLD(TP_TO_VP(tp));
}
void
tmpnode_put(tp)
struct tmpnode *tp;
{
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdirdebug || tmplockdebug)
printf("tmpnode_put: tp %x caller %x\n", tp, caller());
#endif TMPFSDEBUG
(void) tmpnode_unlock(tp);
VN_RELE(TP_TO_VP(tp));
}
tmpnode_inactive(tm, tp)
struct tmount *tm;
struct tmpnode *tp;
{
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdirdebug || tmplockdebug)
printf("tmpnode_inactive: tp %x count %d\n", tp, tp->tn_count);
#endif TMPFSDEBUG
if (tp->tn_attr.va_nlink <= 0)
tmpnode_free(tm, tp);
else
tp->tn_flags = 0;
}
/*
* free tmpnode and all its associated anonymous memory (if any)
*/
void
tmpnode_free(tm, tp)
struct tmount *tm;
struct tmpnode *tp;
{
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc)
printf("tmpnode_free: tp %x nlink %d type %x\n", tp,
tp->tn_attr.va_nlink, tp->tn_attr.va_type);
#endif TMPFSDEBUG
switch (tp->tn_attr.va_type) {
case VDIR:
/* free . & .. */
ASSERT(tp->tn_dir == NULL);
tp->tn_attr.va_nlink -= 2;
(void) tmpnode_trunc(tm, tp, (u_long)0);
tm->tm_directories--;
tmp_files--;
break;
case VLNK:
tmp_memfree(tm, (char *)tp->tn_symlink,
(u_int)tp->tn_attr.va_size);
tm->tm_files--;
tmp_files--;
break;
case VSOCK:
case VFIFO:
case VBLK:
case VCHR:
case VREG:
/*
* See comment below on anon maps
*/
if (tp->tn_amapp->refcnt == 1)
(void) tmpnode_trunc(tm, tp, (u_long)0);
tm->tm_files--;
tmp_files--;
break;
default:
panic("tmpnode_free()- unknown file type\n");
break;
}
/* adjust links in list */
if ((tp->tn_back->tn_forw = tp->tn_forw) != NULL)
tp->tn_forw->tn_back = tp->tn_back;
(void) tmpimapfree(tm, tp->tn_attr.va_nodeid);
/*
* Decrement anon map's reference count
* If the count goes to 0, then free the kmem_alloc'd anon map.
* If the count >= 1, then let the
* mapping process free it up (during a segvn_free)
*/
if (--tp->tn_amapp->refcnt == 0) {
kmem_fast_free((caddr_t *)&tmpamap_freelist,
(caddr_t)tp->tn_amapp);
#ifdef TMPFSDEBUG
if (tmpdebugalloc)
printf("tmpnode_free: free amap for tp %x\n", tp);
#endif TMPFSDEBUG
}
tmp_memfree(tm, (char *)tp, sizeof (struct tmpnode));
}
tmpnode_lock(tp)
struct tmpnode *tp;
{
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmplockdebug)
printf("tmpnode_lock: tp %x caller %x\n", tp, caller());
#endif TMPFSDEBUG
while (tp->tn_flags & TLOCKED && tp->tn_owner != uniqpid()) {
tp->tn_flags |= TWANTED;
#ifdef TMPFSDEBUG
if (tmplockdebug)
printf("tmpnode_lock: pid %x sleeping on tp %x\n",
uniqpid(), tp);
#endif TMPFSDEBUG
(void) sleep((caddr_t)(tp), PINOD);
}
tp->tn_owner = uniqpid();
tp->tn_count++;
tp->tn_flags |= TLOCKED;
/* can't swap process holding a tmpnode lock */
masterprocp->p_swlocks++;
}
tmpnode_unlock(tp)
struct tmpnode *tp;
{
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmplockdebug)
printf("tmpnode_unlock: tp %x count %d caller %x\n",
tp, tp->tn_count, caller());
#endif TMPFSDEBUG
if (--tp->tn_count < 0)
panic("tmpnode_unlock()- bad count");
masterprocp->p_swlocks--;
if (tp->tn_count == 0) {
tp->tn_flags &= ~TLOCKED;
if (tp->tn_flags & TWANTED) {
tp->tn_flags &= ~TWANTED;
wakeup((caddr_t)tp);
}
}
}
/*
* patchable variable used to limit the amount of anon space taken by
* a tmpfs so that we can attempt to avoid deadlock situations
* XXX this should be set at first mount to some reasonable value
* (e.g. MAX(1/4 total available swap space, 4MB) ??)
*/
int tmpfs_hiwater = TMPHIWATER;
/*ARGSUSED*/
tmpnode_findpage(tm, tp, offset)
register struct tmount *tm;
register struct tmpnode *tp;
register u_int offset;
{
register int pageno = btop(offset);
register struct anon *ap;
struct anon *anon_alloc();
register int allocated = 0;
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("tmpnode_findpage: tp %x offset %x\n", tp, offset);
#endif TMPFSDEBUG
/*
* Has the size of the file grown off the limits of the anon array?
*/
if (offset >= tp->tn_amapp->size)
if (tmpnode_growmap(tp, offset) == 0)
return (-1);
/*
* now check to see if there is an allocated anon page
* for this access.
*/
AMAP_LOCK(tp->tn_amapp);
if (tp->tn_amapp->anon[pageno] == NULL) {
allocated = 1;
if (anoninfo.ani_free < btop(tmpfs_hiwater)) {
#ifdef TMPFSDEBUG
if (tmpdebugerrs)
printf("tmpnode_findpage: out of anon space\n");
#endif TMPFSDEBUG
AMAP_UNLOCK(tp->tn_amapp);
return (-1);
}
if ((ap = anon_alloc()) == NULL) {
#ifdef TMPFSDEBUG
if (tmpdebugerrs)
printf("tmpnode_findpage: anon_alloc failed\n");
#endif TMPFSDEBUG
AMAP_UNLOCK(tp->tn_amapp);
return (-1);
}
tmp_anonalloc++;
tp->tn_amapp->anon[pageno] = ap;
}
AMAP_UNLOCK(tp->tn_amapp);
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("tmpnode_findpage: allocated %d anon[%d] %x\n",
allocated, pageno, tp->tn_amapp->anon[pageno]);
#endif TMPFSDEBUG
return (allocated);
}
struct timeval tmp_lastgrowtime;
/*
* grow the anon pointer array to cover 'offset' bytes plus slack
* The anon_map is always locked before allocating a new anon array
* and copying the old anon array contents into it. This is necessary
* to prevent the `seg_vn' fault handling routine from using the old
* anon array as a result of a fault on a memory mapped "tmpfs" file.
*/
tmpnode_growmap(tp, offset)
register struct tmpnode *tp;
register u_int offset;
{
register int i, oldsize, newsize;
register struct anon **newapp;
extern struct timeval time;
/*
* calculate new length, rounding up in TMAP_ALLOC clicks
* to avoid reallocating the anon array each time the file grows
* XXX faster with shifts
*/
newsize = ((offset + TMAP_ALLOC)/TMAP_ALLOC)*TMAP_ALLOC;
oldsize = tp->tn_amapp->size;
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc)
printf("tmpnode_growmap: tp %x oldsize %x newsize %x\n",
tp, oldsize, newsize);
#endif TMPFSDEBUG
newapp = (struct anon **)
new_kmem_zalloc(btop(newsize) * sizeof (struct anon *), KMEM_SLEEP);
/*
* copy old array (if it exists)
* could have used anon_dup()/anon_free() combination
*/
AMAP_LOCK(tp->tn_amapp);
if (tp->tn_amapp->anon != NULL) {
for (i = 0; i < btop(oldsize); i++) {
newapp[i] = tp->tn_amapp->anon[i];
}
tmp_lastgrowtime = time;
kmem_free((char *)tp->tn_amapp->anon,
btop(oldsize) * sizeof (struct anon *));
}
tp->tn_amapp->anon = newapp;
tp->tn_amapp->size = newsize;
AMAP_UNLOCK(tp->tn_amapp);
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("tmpnode_growmap: new anonmap %x\n", tp->tn_amapp->anon);
#endif TMPFSDEBUG
return (1);
}
/*
* tmpnode_trunc()- set length of tmpnode
*/
/*ARGSUSED*/
tmpnode_trunc(tm, tp, newsize)
struct tmount *tm;
struct tmpnode *tp;
u_long newsize;
{
register u_int oldsize;
register struct tdirent *tdp, *tdpx;
register u_int delta;
register int pagecreate = 0;
extern void swap_xlate();
oldsize = tp->tn_attr.va_size;
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc) {
printf("tmpnode_trunc: tp %x oldsz %d newsz %d",
tp, newsize, oldsize);
printf(" type %d caller %x\n", tp->tn_attr.va_type, caller());
}
#endif TMPFSDEBUG
if (newsize == oldsize)
return (0);
switch (tp->tn_attr.va_type) {
case VREG:
if (tp->tn_amapp->refcnt > 1) /* lock first? */
return (EACCES);
if (newsize > oldsize) {
delta = newsize - oldsize;
/*
* grow the size of the anonmap here in case
* someone maps the file. Count the space we're
* growing the file here because we can't detect
* the actual allocation through a page fault.
* rwtmp or segvn_fault will fill in the holes
* in anonmap as needed
* XXX unfortunately, when the holes are filled in
* in segvn_fault, ani_free is decremented changing
* the percentage of anonymous memory tmpfs uses
*/
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("ttrunc: growing %d bytes\n", delta);
#endif TMPFSDEBUG
if (btopr(newsize) != btopr(oldsize))
pagecreate = 1;
if (tmp_resv(tm, tp, oldsize, newsize, pagecreate))
return (ENOSPC);
(void) tmpnode_growmap(tp, (u_int)newsize);
break;
}
/*
* XXX - we need to fail here if someone has a mapping to
* this tmpnode. This is because there is no way to alert
* the segment layer that the file has shrunk wreaking
* havoc if one were to decide to access the anonmap
* past its current size
*/
if (tp->tn_amapp->refcnt > 1)
return (EBUSY);
AMAP_LOCK(tp->tn_amapp);
/*
* delete entire anonmap for tmpnode
*/
if (newsize == 0 && tp->tn_amapp->refcnt == 1) { /* XXX */
u_int anonextra =
ptob(btopr(tp->tn_amapp->swresv) - btopr(oldsize));
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc)
printf("ttrunc: shrinking %d bytes to 0\n",
oldsize);
#endif TMPFSDEBUG
anon_free(tp->tn_amapp->anon, tp->tn_amapp->swresv);
tmp_anonalloc -= btopr(oldsize); /* XXX holes? */
kmem_free((char *)tp->tn_amapp->anon,
btop(tp->tn_amapp->size) * sizeof (struct anon *));
ASSERT(tp->tn_amapp->swresv >= oldsize);
tmp_unresv(tm, tp, oldsize, 0);
/*
* anonextra is nonzero if someone had a mapping to
* the file and accessed it causing page faults
* to fill holes
*/
if (anonextra) {
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc)
printf("ttrunc: anonextra %d\n",
anonextra);
#endif TMPFSDEBUG
anon_unresv(anonextra);
}
tp->tn_amapp->size = 0;
tp->tn_amapp->anon = NULL;
AMAP_UNLOCK(tp->tn_amapp);
break;
}
/*
* free anon pages if necessary
*/
if (btopr(newsize) != btopr(oldsize)) {
delta = oldsize - newsize;
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc) {
printf("ttrunc: shrinking %d bytes", delta);
printf(" anonfree anon[%d] size:%d\n",
btopr(newsize),
oldsize - roundup(newsize, PAGESIZE));
}
#endif TMPFSDEBUG
tmp_unresv(tm, tp, oldsize, newsize);
anon_free(&tp->tn_amapp->anon[btopr(newsize)],
(u_int)(oldsize - roundup(newsize, PAGESIZE)));
tmp_anonalloc -= btopr(oldsize) - btop(newsize);
}
/*
* zero remainder of page
*/
if (tp->tn_amapp->anon[btop(newsize)] != NULL) {
struct anon *ap = tp->tn_amapp->anon[btop(newsize)];
struct vnode *swapvp;
u_int swapoff;
register struct page *pp;
u_int offset = newsize & PAGEOFFSET;
swap_xlate(ap, &swapvp, &swapoff);
pp = ap->un.an_page;
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("ttrunc: zero %d bytes in anon[%d] pp:%x\n",
PAGESIZE - offset, btop(newsize), pp);
#endif TMPFSDEBUG
if (pp != NULL && pp->p_vnode == swapvp &&
pp->p_offset == swapoff && !pp->p_gone) {
if (pp->p_free)
page_reclaim(pp);
pagezero(pp, offset, PAGESIZE - offset);
} else {
char *base;
base = segmap_getmap(segkmap, swapvp,
swapoff & MAXBMASK);
(void) kzero(base + (swapoff & MAXBOFFSET) +
offset, PAGESIZE - offset);
(void) segmap_release(segkmap, base, 0);
}
}
AMAP_UNLOCK(tp->tn_amapp);
break;
case VLNK:
if (newsize != 0)
return (EINVAL);
tmp_memfree(tm, (char *)tp->tn_symlink,
(u_int)tp->tn_attr.va_size);
tp->tn_symlink = NULL;
break;
case VDIR:
if (newsize != 0)
return (EINVAL);
#ifdef TMPFSDEBUG
if (tmpdebugalloc || tmpdirdebug)
printf("ttrunc: dir %x caller %x\n", tp, caller());
#endif TMPFSDEBUG
for (tdp = tp->tn_dir; tdp; tdp = tdpx) {
#ifdef TMPFSDEBUG
if (tmpdebugalloc || tmpdirdebug)
printf("ttrunc: deleting %s\n", tdp->td_name);
#endif TMPFSDEBUG
tdpx = tdp->td_next;
tm->tm_direntries--;
tmp_memfree(tm, (char *)tdp, (u_int)tdp->td_reclen);
}
tp->tn_dir = NULL;
break;
default:
return (0);
}
tp->tn_attr.va_size = newsize;
return (0);
}
/*
* check for any actively used files
* called from tmp_unmount
*/
tmpnode_checkopen(tm)
register struct tmount *tm;
{
register struct tmpnode *tnp;
/*
* The rootnode is always referenced with a v_count >= 1
* If v_count > 1, we're busy.
*/
if (tm->tm_rootnode->tn_vnode.v_count > 1)
return (1);
for (tnp = tm->tm_rootnode->tn_forw; tnp; tnp = tnp->tn_forw)
if (tnp->tn_flags & TREF) {
#ifdef TMPFSDEBUG
if (tmpdebugerrs)
printf("tmpnode_checkopen: %x is referenced\n",
tnp);
#endif TMPFSDEBUG
return (1);
}
return (0);
}
/*
* free up all resources associated with the file system
* called from tmp_unmount
*/
tmpnode_freeall(tm)
register struct tmount *tm;
{
register struct tmpnode *tnp, *tnpx;
register struct tmpimap *tmapp0, *tmapp1;
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("tmpnode_freeall: tm %x\n", tm);
#endif TMPFSDEBUG
for (tnp = tm->tm_rootnode; tnp; tnp = tnpx) {
switch (tnp->tn_attr.va_type) {
case VDIR:
tm->tm_directories--;
tmp_files--;
(void) tmpnode_trunc(tm, tnp, (u_long)0);
break;
case VLNK:
case VREG:
case VBLK:
case VCHR:
case VFIFO:
case VSOCK:
tm->tm_files--;
tmp_files--;
(void) tmpnode_trunc(tm, tnp, (u_long)0);
break;
default:
break; /* XXX shouldn't this be a panic? */
}
tnpx = tnp->tn_forw;
tmp_memfree(tm, (char *)tnp, sizeof (struct tmpnode));
}
/*
* Free the inode maps
*/
tmapp0 = tm->tm_inomap.timap_next;
while (tmapp0 != NULL) {
tmapp1 = tmapp0->timap_next;
tmp_memfree(tm, (char *)tmapp0, sizeof (struct tmpimap));
tmapp0 = tmapp1;
}
}
int
tmp_resv(tm, tp, oldsize, newsize, pagecreate)
register struct tmount *tm;
register struct tmpnode *tp;
register u_int oldsize;
register u_int newsize;
register int pagecreate;
{
u_int delta = ptob(btopr(newsize) - btopr(oldsize));
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc)
printf("tmp_resv: tm %x tp %x delta %d\n", tm, tp, delta);
#endif TMPFSDEBUG
/*
* don't do anon_resv unless we actually need to reserve a page
* this is because of a rounding error that occurs because
* anon_resv does a btopr(delta)
*/
if (pagecreate && (anon_resv(delta) == 0))
return (1);
tm->tm_anonmem += delta;
tp->tn_amapp->swresv += delta;
tmp_anonmem += delta;
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc)
printf("tmp_resv: ani_resv %d\n", anoninfo.ani_resv);
#endif TMPFSDEBUG
return (0);
}
tmp_unresv(tm, tp, oldsize, newsize)
register struct tmount *tm;
register struct tmpnode *tp;
register u_int oldsize;
register u_int newsize;
{
u_int delta = ptob(btopr(oldsize) - btopr(newsize));
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc)
printf("tmp_unresv: tm %x tp %x delta %d\n", tm, tp, delta);
#endif TMPFSDEBUG
anon_unresv(delta);
tm->tm_anonmem -= delta;
tp->tn_amapp->swresv -= delta;
tmp_anonmem -= delta;
#ifdef TMPFSDEBUG
if (tmpfsdebug || tmpdebugalloc)
printf("tmp_unresv: ani_resv %d\n", anoninfo.ani_resv);
#endif TMPFSDEBUG
}

275
sys/tmpfs/tmp_vfsops.c Normal file
View File

@@ -0,0 +1,275 @@
/* @(#)tmp_vfsops.c 1.1 94/10/31 SMI */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <sys/kmem_alloc.h>
#include <vm/anon.h>
#include <tmpfs/tmpnode.h>
#include <tmpfs/tmp.h>
#include <tmpfs/tmpdir.h>
/*
* tmpfs vfs operations.
*/
static int tmp_mount();
static int tmp_unmount();
static int tmp_root();
static int tmp_statfs();
static int tmp_sync();
static int tmp_vget();
static int tmp_mountroot();
static int tmp_swapvp();
struct vfsops tmp_vfsops = {
tmp_mount,
tmp_unmount,
tmp_root,
tmp_statfs,
tmp_sync,
tmp_vget,
tmp_mountroot,
tmp_swapvp
};
/*
* patchable variables, otherwise tmpfs_maxkmem set on first tmp_mount.
*/
u_int tmpfs_maxprockmem = TMPMAXPROCKMEM; /* percent of kernel memory */
/* this tmpfs can use */
u_int tmpfs_maxkmem = 0; /* patch at the risk of your life */
#define TMPMAPSIZE 32/NBBY
static char tmpfs_minmap[TMPMAPSIZE]; /* map for minor dev num allocation */
static struct tmount *tmpfs_mountp = 0; /* linked list of tmpfs mount structs */
/*ARGSUSED*/
static int
tmp_mount(vfsp, path, data)
struct vfs *vfsp;
char *path;
caddr_t data;
{
int error;
register struct tmount *tmx;
struct tmount *tm;
struct tmpnode *tp;
struct ucred rootcred;
extern int physmem;
extern long tmpimapalloc();
u_int len;
if (tmpfs_maxkmem == 0) { /* first mount or (god forbid) patch */
tmpfs_maxkmem = MAX(PAGESIZE,
(ptob(physmem) * tmpfs_maxprockmem)/100);
}
/* allocate and initialize tmount structure */
tm = (struct tmount *)
new_kmem_zalloc(sizeof (struct tmount), KMEM_SLEEP);
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("tmp_mount: vfsp %x tm %x path %s\n", vfsp, tm, path);
#endif TMPFSDEBUG
/* link the structure into the list */
tmx = tmpfs_mountp;
if (tmx == (struct tmount *)NULL)
tmpfs_mountp = tm;
else {
for (; tmx->tm_next; tmx = tmx->tm_next)
;
tmx->tm_next = tm;
}
tm->tm_vfsp = vfsp;
if ((tm->tm_mntno = vfs_getnum(tmpfs_minmap, TMPMAPSIZE)) == -1) {
kmem_free((char *)tm, sizeof (struct tmount));
return (EAGAIN); /* why not 'mount table full'? */
}
/*
* Kludge Alert!
* inodes 0 and 1 have traditionally been unused.
* the next two calls have the effect of invalidating 0 and 1
* for tmpfs use
*/
(void)tmpimapalloc(tm);
(void)tmpimapalloc(tm);
vfsp->vfs_data = (caddr_t)tm;
vfsp->vfs_fsid.val[0] = tm->tm_mntno;
vfsp->vfs_fsid.val[1] = MOUNT_TMP;
(void) copystr(path, tm->tm_mntpath, sizeof (tm->tm_mntpath) - 1, &len);
bzero(tm->tm_mntpath + len, sizeof (tm->tm_mntpath) - len);
/* allocate and initialize root tmpnode structure */
tp = tmpnode_alloc(tm, VDIR);
if (tp == NULL) {
kmem_free((char *)tm, sizeof (struct tmount));
return (ENOSPC);
}
tmpnode_unlock(tp);
tm->tm_rootnode = tp;
newnode(tp, S_IFDIR | 0777, 0, 0); /* XXX Permissions? */
tp->tn_vnode.v_flag |= VROOT;
rootcred.cr_uid = 0;
rootcred.cr_gid = 0;
if ((error = tdirenter(tm, tp, ".", tp, VWRITE, &rootcred)) ||
(error = tdirenter(tm, tp, "..", tp, VWRITE, &rootcred))) {
tmpnode_free(tm, tp);
kmem_free((char *)tm, sizeof (struct tmount));
return (error); /* XXX Could we lose some memory? */
}
return (0);
}
static int
tmp_sync()
{
return (0);
}
/*
* global statistics
*/
int tmp_anonmem; /* amount of anon space reserved for all tmpfs */
int tmp_kmemspace; /* amount of kernel heap used by all tmpfs */
int tmp_files; /* number of files or directories in all tmpfs */
int tmp_anonalloc; /* approximate # of anon pages allocated to tmpfs */
static int
tmp_unmount(vfsp)
struct vfs *vfsp;
{
register struct tmount *tm = VFSP_TO_TM(vfsp);
register struct tmount *tmx;
void vfs_putnum();
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("tmp_unmount: tm %x\n", tm);
#endif TMPFSDEBUG
/*
* Don't close down the tmpfs if there are ANY opened files
*/
if (tmpnode_checkopen(tm))
return (EBUSY);
/*
* Remove from tmp mount list
*/
if ((tmx = tmpfs_mountp) == tm)
tmpfs_mountp = tm->tm_next;
else
for (; tmx; tmx = tmx->tm_next)
if (tmx->tm_next == tm)
tmx->tm_next = tm->tm_next;
vfs_putnum(tmpfs_minmap, (int)tm->tm_mntno);
/*
* must free all kmemalloc'd and anonalloc'd memory associated with
* this filesystem
*/
(void)tmpnode_freeall(tm); /* frees all files and directories */
if ((tm->tm_anonmem != 0) || (tm->tm_files != 0) ||
(tm->tm_directories != 0) || (tm->tm_direntries != 0) ||
(tm->tm_kmemspace != 0)) {
#ifdef TMPFSDEBUG
if (tm || tmpdebugerrs) { /* always true for now */
printf("tmpnode_freeall bad bookkeeping tm %x\n", tm);
printf("files = %d\n", tm->tm_files);
printf("directories = %d\n", tm->tm_directories);
printf("direntries = %d\n", tm->tm_direntries);
printf("anonmem = %d\n", tm->tm_anonmem);
printf("kmemspace = %d\n", tm->tm_kmemspace);
}
#endif TMPFSDEBUG
}
kmem_free((char *)tm, sizeof (struct tmount));
return (0);
}
static int
tmp_root(vfsp, vpp)
struct vfs *vfsp;
struct vnode **vpp;
{
struct tmount *tm = VFSP_TO_TM(vfsp);
struct tmpnode *tp = tm->tm_rootnode;
tmpnode_get(tp);
tmpnode_unlock(tp);
*vpp = TP_TO_VP(tp);
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("tmp_root: tm %x tp %x vpp\n", tm, tp, *vpp);
#endif TMPFSDEBUG
return (0);
}
static int
tmp_statfs(vfsp, sbp)
struct vfs *vfsp;
struct statfs *sbp;
{
struct tmount *tm = VFSP_TO_TM(vfsp);
extern int tmpfs_hiwater;
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("tmp_statfs: tm %x sbp %x\n", tm, sbp);
#endif TMPFSDEBUG
sbp->f_bsize = PAGESIZE;
sbp->f_blocks = anoninfo.ani_free - btop(tmpfs_hiwater) +
btop(tmp_anonmem);
sbp->f_bfree = sbp->f_blocks - btop(tm->tm_anonmem);
sbp->f_bavail = sbp->f_bfree;
/*
* The maximum number of files available is the number of tmpnodes we
* can allocate from the remaining kernel memory available to tmpfs.
*/
sbp->f_ffree = (tmpfs_maxkmem - tmp_kmemspace)/sizeof (struct tmpnode);
sbp->f_files = sbp->f_ffree + tmp_files;
if (sbp->f_bfree < 0) {
sbp->f_blocks -= sbp->f_bfree;
sbp->f_files -= sbp->f_bfree;
sbp->f_bfree = sbp->f_bavail = sbp->f_ffree = 0;
}
sbp->f_fsid = vfsp->vfs_fsid;
return (0);
}
/*ARGSUSED*/
static int
tmp_vget(vfsp, vpp, fidp)
struct vfs *vfsp;
struct vnode **vpp;
struct fid *fidp;
{
#ifdef TMPFSDEBUG
if (tmpfsdebug)
printf("tmp_vget: vfsp %x fidp %x\n", vfsp, fidp);
#endif TMPFSDEBUG
*vpp = NULL;
return (0);
}
static int
tmp_mountroot()
{
return (EINVAL);
}
static int
tmp_swapvp()
{
return (EINVAL);
}

1347
sys/tmpfs/tmp_vnodeops.c Normal file

File diff suppressed because it is too large Load Diff

23
sys/tmpfs/tmpdir.h Normal file
View File

@@ -0,0 +1,23 @@
/* @(#)tmpdir.h 1.1 94/10/31 SMI */
/*
* directory entry for temporary file system
* modelled after ufs/fsdir.h
*/
#undef MAXNAMLEN
#define MAXNAMLEN 255
struct tdirent {
u_short td_reclen; /* length of this record */
u_short td_namelen; /* string length in td_name */
struct tmpnode *td_tmpnode; /* tnode for this file */
struct tdirent *td_next; /* next directory entry */
char td_name[MAXNAMLEN+1]; /* no longer than this */
};
#define TDIRSIZ(tdp) \
((sizeof (struct tdirent) - (MAXNAMLEN+1)) + \
(((tdp)->td_namelen+1 + 3) &~ 3))
#define TEMPTYDIRSIZE 32 /* fudge cookie */

58
sys/tmpfs/tmpnode.h Normal file
View File

@@ -0,0 +1,58 @@
/* @(#)tmpnode.h 1.1 94/10/31 SMI */
#include <vm/seg_vn.h>
/*
* tmpnode is the (tmp)inode for the anonymous page based file system
*/
struct tmpnode {
u_int tn_flags; /* see T flags below */
int tn_count; /* links to tmpnode */
struct tmpnode *tn_back; /* linked list of tmpnodes */
struct tmpnode *tn_forw; /* linked list of tmpnodes */
long tn_owner; /* proc/thread locking node */
union {
struct tdirent *un_dir; /* pointer to directory list */
char *un_symlink; /* pointer to symlink */
} un_tmpnode;
struct vnode tn_vnode; /* vnode for this tmpnode */
struct anon_map *tn_amapp; /* anonymous pages map */
struct vattr tn_attr; /* attributes */
};
#define tn_dir un_tmpnode.un_dir
#define tn_symlink un_tmpnode.un_symlink
/* tn_flags */
#define TLOCKED 0x1 /* tmpnode is locked */
#define TWANTED 0x2 /* tmpnode wanted */
#define TREF 0x4 /* tmpnode referenced */
/* modes */
#define TFMT 0170000 /* type of file */
#define TFIFO 0010000 /* named pipe (fifo) */
#define TFCHR 0020000 /* character special */
#define TFDIR 0040000 /* directory */
#define TFBLK 0060000 /* block special */
#define TFREG 0100000 /* regular */
#define TFLNK 0120000 /* symbolic link */
#define TFSOCK 0140000 /* socket */
#define TSUID 04000 /* set user id on execution */
#define TSGID 02000 /* set group id on execution */
#define TSVTX 01000 /* save swapped text even after use */
#define TREAD 0400 /* read, write, execute permissions */
#define TWRITE 0200
#define TEXEC 0100
#define TMAP_ALLOC 131072 /* anon map's increment click size */
#define TP_TO_VP(tp) (&(tp)->tn_vnode)
#define VP_TO_TP(vp) ((struct tmpnode *)(vp)->v_data)
#ifdef KERNEL
struct tmpnode *tmpnode_alloc();
void tmpnode_get();
void tmpnode_free();
void tmpnode_put();
#endif KERNEL