Init
This commit is contained in:
10
sys/tmpfs/Makefile
Normal file
10
sys/tmpfs/Makefile
Normal 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
81
sys/tmpfs/tmp.h
Normal 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
192
sys/tmpfs/tmp_dir.c
Normal 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
232
sys/tmpfs/tmp_subr.c
Normal 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
688
sys/tmpfs/tmp_tnode.c
Normal 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
275
sys/tmpfs/tmp_vfsops.c
Normal 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
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
23
sys/tmpfs/tmpdir.h
Normal 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
58
sys/tmpfs/tmpnode.h
Normal 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
|
||||
Reference in New Issue
Block a user