Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

1478 lines
35 KiB
C

static char sccsid[] = "@(#)79 1.19.1.17 src/bos/kernel/specfs/specsubs.c, sysspecfs, bos411, 9428A410j 6/10/94 16:50:15";
/*
* COMPONENT_NAME: (SYSSPECFS) Special File System
*
* FUNCTIONS: dev_vp, devget, devput, devqry, devsetsth, devtosth,
* fdtosth, fifo_vp, ic_specaccess, smark, snput, spec_clone,
* spec_vp, specacc, specaccess, specchk_link, specchk_rename
*
* ORIGINS: 27
*
* IBM CONFIDENTIAL -- (IBM Confidential Restricted when
* combined with the aggregated modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1988, 1993
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
/*
* LEVEL 1, 5 Years Bull Confidential Information
*/
#include "sys/types.h"
#include "sys/param.h"
#include "sys/user.h"
#include "sys/vfs.h"
#include "sys/vnode.h"
#include "sys/errno.h"
#include "sys/device.h"
#include "sys/vattr.h"
#include "sys/sleep.h"
#include "sys/malloc.h"
#include "sys/specnode.h"
#include "sys/id.h"
#include "sys/gpai.h"
#include "sys/fs_locks.h"
#include "sys/lockname.h"
#include "sys/lock_alloc.h"
extern struct vnodeops spec_vnops;
extern struct vnodeops fifo_vnops;
extern int gn_reclk_count;
struct hnode hnodetable[NHNODE]; /* specfs hash table */
int spec_generation = 1; /* generation counter for specnodes */
/*
* Set up pools for the interesting specfs structures.
* Initial allocation for each structure type is approximately
* one page of memory.
*/
extern struct galloc specnode_pool;
static struct galloc *snpool = &specnode_pool;
extern struct galloc devnode_pool;
static struct galloc *dnpool = &devnode_pool;
extern struct galloc fifonode_pool;
static struct galloc *fnpool = &fifonode_pool;
/*
* NAME: spec_vp(vpp)
*
* FUNCTION: This function is used to exchange a PFS vnode for a
* spec vnode. It is called by callers of the lookup
* vnode operations of physical file systems. These callers
* pass in the PFS vnode returned by the lookup vnode
* operation after determining that the vnode represents
* a special file.
* This function examines character special devices to
* determine whether they are actually multiplexed devices.
*
* PARAMETERS: vpp - pointer to PFS vnode and spec vnode return
*
* RETURN: 0
*/
int
spec_vp(
struct vnode ** vpp) /* PFS & specfs (return) vnodes */
{
int rc;
switch ((*vpp)->v_type)
{
case VBLK:
rc = dev_vp(vpp, VBLK, 0);
break;
case VCHR:
/* For mpx bases, the channel number is -1, all others
* have channel number zero */
if (mpx_dev(VTOGP(*vpp)->gn_rdev))
rc = dev_vp(vpp, VMPC, BASE_MPX);
else
rc = dev_vp(vpp, VCHR, 0);
break;
case VMPC:
/* We cannot get a multiplexed device from the PFS. */
rc = EINVAL;
break;
case VFIFO:
rc = fifo_vp(vpp);
break;
default:
rc = 0;
}
return rc;
}
/*
* NAME: dev_vp(vpp, vtype, chan)
*
* FUNCTION: This function is used to exchange a PFS vnode for a
* spec vnode. It is called by callers of the lookup
* vnode operations of physical file systems. These callers
* pass in the PFS vnode returned by the lookup vnode
* operation after determining that the vnode represents
* a device special file.
* This function is also called by mpx_lookup to get a
* spec vnode for the specified mpx channel of the base
* mpx provided.
*
* PARAMETERS: vpp - pointer to PFS vnode and spec vnode return
* vtype - vnode type of spec vnode to return
* chan - channel number of device to find
*
* RETURN: 0
*/
int
dev_vp(
struct vnode ** vpp, /* PFS & specfs (return) vnodes */
int vtype, /* vnode type of spec vnode */
chan_t chan) /* channel number of device */
{
struct vnode * vp; /* PFS vnode */
struct gnode * gp; /* PFS gnode for special file */
struct specnode * snp; /* specnode for device special */
struct devnode * dp; /* devnode for device */
struct gnode * sgp; /* spec gnode for special file */
struct vnode * tvp; /* temp specfs vnode */
int sgen; /* saved specnode generation */
int rc; /* return code */
gp = VTOGP(*vpp);
vp = *vpp;
/* Get the devnode for the device. */
if (rc = devget(gp->gn_rdev, chan, vtype, &dp))
return rc;
/*
* Upon successful completion of devget() the devnode will
* be returned locked.
*/
/* Search for a specnode on the devnode which currently references
* the PFS's gnode. Multiple channels of mpx devices reference the
* base's PFS gnode, so the channel numbers must match also.
*/
for (snp = dp->dv_specnodes; snp; snp = snp->sn_next)
{
if (snp->sn_pfsgnode == gp && STOSGP(snp)->gn_chan == chan)
{
/* Save the specnode generation to verify that we still
* have the same specnode after getting a lock on it.
*/
sgen = snp->sn_gen;
/* Lock the specnode we found. */
SPECNODE_LOCK(snp);
/* Verify that this is still the specnode that we
* wanted and that it is not going away.
*/
if ((snp->sn_gen != sgen) || (snp->sn_count == 0))
{
/* Not what we need. Continue the search
* with the next specnode on the list.
*/
SPECNODE_UNLOCK(snp);
continue;
}
/* Bump the specnode count. */
snp->sn_count++;
devput(dp);
/* Search for the appropriate vnode on the specnode's
* list. The specnode must be locked during this
* search and the call to vn_get().
*/
for (tvp = snp->sn_vnode; tvp; tvp = tvp->v_next)
if (tvp->v_pfsvnode == vp)
{
/* Hold the vnode and unlock and put
* the specnode. Release the PFS vnode
* because we don't attach it.
*/
VNOP_HOLD(tvp);
VNOP_RELE(vp);
*vpp = tvp;
snput(snp);
return 0;
}
/* If we didn't find the vnode, then continue with
* the code which follows the specnode creation.
*/
break;
}
}
/* If no specnode found then create one. */
if (snp == NULL)
{
/* Allocate memory for a specnode. */
if ((snp = (struct specnode *)gpai_alloc(snpool)) == NULL)
{
devput(dp);
return ENOMEM;
}
/* Set the specnode generation number. */
snp->sn_gen = fetch_and_add(&spec_generation, 1);
/* fill in the gnode */
sgp = STOSGP(snp);
sgp->gn_type = vtype;
sgp->gn_flags = 0;
sgp->gn_seg = INVLSID;
sgp->gn_mwrcnt = 0;
sgp->gn_mrdcnt = 0;
sgp->gn_rdcnt = 0;
sgp->gn_wrcnt = 0;
sgp->gn_excnt = 0;
sgp->gn_rshcnt = 0;
sgp->gn_ops = &spec_vnops;
sgp->gn_vnode = NULL;
sgp->gn_rdev = gp->gn_rdev;
sgp->gn_chan = chan;
sgp->gn_data = (caddr_t)snp;
sgp->gn_reclk_event = EVENT_NULL;
sgp->gn_filocks = NULL;
sgp->gn_data = (caddr_t)snp;
lock_alloc(&sgp->gn_reclk_lock, LOCK_ALLOC_PAGED,
RECLK_LOCK_CLASS, gn_reclk_count++);
simple_lock_init(&sgp->gn_reclk_lock);
/* fill in the specnode */
snp->sn_vnode = NULL; /* added later */
snp->sn_count = 1;
snp->sn_pfsgnode = gp;
snp->sn_attr = NULL;
/* The specnode must be locked at this point because
* it will be visible to other processes as soon as
* it is put on the devnode's list.
*/
SPECNODE_LOCK(snp);
/* connect specnode to devnode */
snp->sn_devnode = dp;
snp->sn_next = dp->dv_specnodes;
dp->dv_specnodes = snp;
/* at last, unlock the devnode */
DEVNODE_UNLOCK(dp);
}
/*
* At this point in time, we have a locked specnode and
* an unlocked devnode.
*/
/* Allocate the specfs vnode from the PFS vnode's vfs.
* This ensures that those in the LFS that use the vfs
* obtained from the vnode we return will get the right vfs.
*/
if (rc = vn_get((*vpp)->v_vfsp, STOSGP(snp), &tvp))
{
snput(snp);
return rc;
}
/* Attach the PFS vnode to the specfs vnode and return the
* specfs vnode.
*/
tvp->v_pfsvnode = *vpp;
*vpp = tvp;
SPECNODE_UNLOCK(snp);
return 0;
}
int
devget (
dev_t dev, /* device number of device */
chan_t chan, /* device channel number */
int vtype, /* vnode type for gnode */
struct devnode **dpp) /* devnode for return */
{
struct devnode *dp; /* devnode to return */
struct hnode * hdp; /* hash bucket for devnode */
struct gnode * gp; /* dev gnode of device */
int status; /* device switch status */
int rc; /* return code */
/* Find the hash list and lock it for the duration of the
* search and the addition of a new node (if necessary).
*/
hdp = DEVHASH(dev, chan);
SPECHASH_LOCK(hdp);
/* Search the hash for the matching devnode. If the devnode
* is found, increment the devnode hold count and return
* the devnode.
*/
for ( dp = hdp->hdv_forw;
dp != (struct devnode *)hdp;
dp = dp->dv_forw)
{
if (dp->dv_dev == dev && dp->dv_chan == chan)
{
/* Lock the devnode, then make sure it is valid. */
DEVNODE_LOCK(dp);
if ((dp->dv_count == 0)
|| (dp->dv_dev != dev)
|| (dp->dv_chan != chan))
{
DEVNODE_UNLOCK(dp);
continue;
}
/* Unlock the spechash_lock since we found it */
SPECHASH_UNLOCK(hdp);
/* We found the devnode in the hash table. Increment
* the count and return the locked devnode.
*/
dp->dv_count++;
*dpp = dp;
return 0;
}
}
/* Allocate memory for a devnode since we didn't find it. */
if ((dp = (struct devnode *)gpai_alloc(dnpool)) == NULL)
rc = ENOMEM;
else
{
/* Initialize the fields of the devnode */
bzero(&dp->dv_gnode, sizeof(struct gnode));
dp->dv_lastr = 0;
dp->dv_pdata = NULL;
/* fill in the fields of the devnode */
dp->dv_dev = dev;
dp->dv_flag = 0;
dp->dv_count = 1;
dp->dv_specnodes = NULL;
/* must return locked devnode */
DEVNODE_LOCK(dp);
/* fill in the fields of the gnode */
gp = DTODGP(dp);
gp->gn_data = (caddr_t)dp;
gp->gn_rdev = dev;
gp->gn_chan = chan;
gp->gn_type = vtype;
gp->gn_seg = INVLSID;
gp->gn_ops = NULL;
gp->gn_vnode = NULL;
/* add the devnode to the spec hash */
dp->dv_forw = hdp->hdv_forw;
dp->dv_back = (struct devnode *)hdp;
dp->dv_forw->dv_back = dp;
hdp->hdv_forw = dp;
rc = 0;
}
/* unlock spechash here since devnode is now on hash list */
SPECHASH_UNLOCK(hdp);
*dpp = dp;
return rc;
}
int
devput (
struct devnode * dp) /* devnode to put */
{
int count;
struct hnode * hdp; /* hash bucket for devnode */
/* check the devnode hold count */
assert(dp->dv_count > 0);
/* The caller must be holding the lock for this devnode */
ASSERT(lock_mine(&dp->dv_lock));
count = --dp->dv_count;
/* NOTE:
* When the count on the devnode is decremented to zero, the
* devnode will not be reused. (The devget logic enforces this.)
* Therefore, we do not need to re-acquire the devnode lock and
* test the count again after getting the spechash lock.
*/
if (count == 0)
{
/* Deallocate the channel while holding the DEVNODE_LOCK
* to allow the channel to be reused if a subsequent
* lookup on the channel is in progress and waiting on
* the devnode lock in devget.
*/
if (MPX_CHANNEL(DTODGP(dp)))
{
DD_ENT((void), (*devsw[major(dp->dv_dev)].d_mpx)
(dp->dv_dev, &dp->dv_chan, NULL),
IPRI_NOPREMPT, major(dp->dv_dev));
}
/* get the hash chain the devnode resides in */
hdp = DEVHASH(dp->dv_dev, dp->dv_chan);
/* We can't take spechash lock while holding the devnode. */
DEVNODE_UNLOCK(dp);
/* Take the spechash lock for this hash chain. */
SPECHASH_LOCK(hdp);
/* remove the devnode from the hash */
dp->dv_forw->dv_back = dp->dv_back;
dp->dv_back->dv_forw = dp->dv_forw;
/* Unlock the spechash chain */
SPECHASH_UNLOCK(hdp);
/* check the open counts */
assert((dp->dv_rdcnt+dp->dv_wrcnt+dp->dv_mntcnt) == 0);
/* free the devnode */
gpai_free(dnpool, dp);
}
else
DEVNODE_UNLOCK(dp);
return 0;
}
/*
* NAME: fifo_vp(vpp)
*
* FUNCTION: This function is used to exchange a PFS vnode for a
* spec vnode. It is called by callers of the lookup
* vnode operations of physical file systems. These callers
* pass in the PFS vnode returned by the lookup vnode
* operation after determining that the vnode represents
* a fifo.
*
* PARAMETERS: vpp - pointer to PFS vnode and spec vnode return
*
* RETURN: 0
*/
int
fifo_vp(
struct vnode ** vpp) /* PFS & specfs (return) vnodes */
{
struct vnode * tvp; /* temp specfs vnode */
struct vnode * vp; /* PFS vnode */
struct gnode * gp; /* PFS gnode for special file */
struct specnode * snp; /* specnode for device special */
struct gnode * sgp; /* spec gnode for fifo */
struct fifonode * ffp; /* fifonode for fifo file */
struct hnode * hffp; /* hash bucket for fifonode */
int rc; /* return code */
int sgen; /* saved specnode generation */
vp = *vpp;
gp = VTOGP(vp);
snp = NULL;
hffp = FIFOHASH(gp);
/* Hold the spechash_lock before searching for the object */
SPECHASH_LOCK(hffp);
/* search for a fifo which references the gnode */
for ( ffp = hffp->hff_forw;
ffp != (struct fifonode *)hffp;
ffp = ffp->ff_forw)
{
if (ffp->ff_dev == NODEVICE && ffp->ff_pfsgnode == gp)
{
/* lock the specnode for the fifonode we found */
snp = ffp->ff_specnode;
sgen = snp->sn_gen;
SPECNODE_LOCK(snp);
/* verify that the fifonode is still what we wanted
* and that it is still valid
*/
if ((snp->sn_gen != sgen) || (snp->sn_count == 0))
{
/* if not, then continue the search */
SPECNODE_UNLOCK(snp);
snp = NULL;
continue;
}
/* Bump the specnode count and stop the search. */
snp->sn_count++;
break;
}
}
/* Create a fifonode/specnode pair if we didn't find them. */
if (snp == NULL)
{
/* Allocate memory for the fifonode and specnode
* since we didn't find the fifonode.
*/
if (((ffp = (struct fifonode *)gpai_alloc(fnpool)) == NULL)
|| ((snp = (struct specnode *)gpai_alloc(snpool)) == NULL))
{
SPECHASH_UNLOCK(hffp);
if (ffp)
gpai_free(fnpool, ffp);
return ENOMEM;
}
snp->sn_gen = fetch_and_add(&spec_generation, 1);
/* Initialize fields in the fifonode */
ffp->ff_size = 0;
ffp->ff_wptr = 0;
ffp->ff_rptr = 0;
ffp->ff_poll = 0;
ffp->ff_flag = 0;
ffp->ff_rcnt = 0;
ffp->ff_wcnt = 0;
/* Initialize the fields in the specnode */
snp->sn_next = NULL;
bzero(&snp->sn_gnode, sizeof(struct gnode));
/* fill in the fifonode */
ffp->ff_dev = NODEVICE;
ffp->ff_pfsgnode = gp;
ffp->ff_wevent = EVENT_NULL;
ffp->ff_revent = EVENT_NULL;
/* put fifonode in spec hash */
ffp->ff_forw = hffp->hff_forw;
ffp->ff_back = (struct fifonode *)hffp;
ffp->ff_forw->ff_back = ffp;
hffp->hff_forw = ffp;
sgp = STOSGP(snp);
/* fill in the gnode */
sgp->gn_type = gp->gn_type;
sgp->gn_ops = &fifo_vnops;
sgp->gn_rdev = NODEVICE;
sgp->gn_data = (caddr_t)snp;
sgp->gn_seg = INVLSID;
lock_alloc(&sgp->gn_reclk_lock, LOCK_ALLOC_PAGED,
RECLK_LOCK_CLASS, gn_reclk_count++);
simple_lock_init(&sgp->gn_reclk_lock);
/* fill in the specnode */
snp->sn_vnode = NULL; /* added later */
snp->sn_count = 1;
snp->sn_pfsgnode = gp;
snp->sn_attr = NULL;
SPECNODE_LOCK(snp);
/* connect specnode to fifonode */
snp->sn_fifonode = ffp;
ffp->ff_specnode = snp;
}
/* Everything is connected. Unlock spechash lock */
SPECHASH_UNLOCK(hffp);
/* At this point, we have a locked specnode. */
/* Search for the appropriate vnode on the specnode's list.
* The specnode must be locked during this search and the
* call to vn_get().
*/
for (tvp = snp->sn_vnode; tvp; tvp = tvp->v_next)
if (tvp->v_pfsvnode == vp)
{
/* Hold the vnode and unlock and put the specnode.
* Release the PFS vnode because we don't attach it.
*/
VNOP_HOLD(tvp);
VNOP_RELE(vp);
*vpp = tvp;
snput(snp);
return 0;
}
/* Allocate the specfs vnode from the PFS vnode's vfs.
* This ensures that those in the LFS that use the vfs
* obtained from the vnode we return will get the right vfs.
*/
if (rc = vn_get((*vpp)->v_vfsp, STOSGP(snp), &tvp))
{
snput(snp);
return rc;
}
/* Attach the PFS vnode to the specfs vnode and return the
* specfs vnode.
*/
tvp->v_pfsvnode = *vpp;
SPECNODE_UNLOCK(snp);
*vpp = tvp;
return 0;
}
int
specchk_rename (
struct vnode ** svpp, /* rename source vnode */
struct vnode ** dvpp) /* rename destination vnode */
{
struct vnode * svp; /* rename source vnode */
struct vnode * dvp; /* rename destination vnode */
int rc = 0; /* return code */
svp = *svpp;
dvp = *dvpp;
/* Check the file to be renamed and translate spec vnodes
* to PFS vnodes.
*/
switch (svp->v_type)
{
case VMPC:
/* It is invalid to try to rename
* an mpx device channel.
*/
if (VTOGP(svp)->gn_chan != BASE_MPX)
{
rc = EINVAL;
break;
}
/* fall through */
case VBLK:
case VCHR:
case VFIFO:
if (!svp->v_pfsvnode)
return EINVAL;
/* only pass the PFS vnode to the PFS */
*svpp = svp->v_pfsvnode;
break;
default:
/* renaming a non-special file */
break;
}
/* Check the file to be renamed over, if any, and translate
* spec vnodes to PFS vnodes.
*/
if (!rc && dvp)
switch (dvp->v_type)
{
case VMPC:
/* It is invalid to try to rename
* over an mpx device channel.
*/
if (VTOGP(dvp)->gn_chan != BASE_MPX)
{
rc = EINVAL;
break;
}
/* fall through */
case VBLK:
case VCHR:
case VFIFO:
/* only pass the PFS vnode to the PFS */
*dvpp = dvp->v_pfsvnode;
break;
default:
/* renaming over a non-special file */
break;
}
return rc;
}
int
specchk_link(
struct vnode ** vpp)
{
struct vnode * tvp;
int vtype;
tvp = *vpp;
vtype = tvp->v_type;
if ( vtype != VCHR &&
vtype != VBLK &&
vtype != VMPC &&
vtype != VFIFO)
return 0;
/* can't link to mpx channels */
if (vtype == VMPC && MPX_CHANNEL(VTOGP(tvp)))
return EINVAL;
if (!tvp->v_pfsvnode)
return EINVAL;
*vpp = tvp->v_pfsvnode;
/* Hold the PFS vnode because we are returning it,
* and release the spec vnode as the LFS no longer has it.
*/
VNOP_HOLD(tvp->v_pfsvnode);
VNOP_RELE(tvp);
return 0;
}
/*
* NAME: devqry(dev, chan)
*
* FUNCTION: This function determines whether a given device is currently
* open. A boolean value is returned indicating if it is.
* This will indicate whether any minor number or channel of
* the device is open. If a channel other than BASE_MPX is
* specified, then the minor number and channel number are
* also checked.
*
* PARAMETERS: dev - device number to search for (both major and minor)
* chan - specific channel to search for.
* if set to BASE_MPX, then ignore it.
*
* RETURN : 1 - if the device is open
* 0 - if the argument is invalid or the device is not open.
*/
/* arguments for search routine called by gpai_srch */
struct srchargs {
dev_t dev; /* device number to search for */
chan_t chan; /* channel number to search for */
};
/* search routine called by gpai_srch */
static int devsrch(struct devnode *, struct srchargs *);
int
devqry (
dev_t dev, /* device number to search for */
chan_t chan) /* channel number to search for */
{
struct devnode *dp; /* devnode being examined */
struct hnode *hdp; /* hash chain being examined */
struct srchargs sa; /* arguments for search routine */
int rc = 0; /* return code */
if (major(dev) < DEVCNT) /* validate the device number argument */
{
/*
* Search all devnodes for one that
* matches the device number and channel.
*/
sa.dev = dev;
sa.chan = chan;
rc = gpai_srch(dnpool, 0, 0, devsrch, &sa);
}
return(rc);
}
static int
devsrch(struct devnode *dp, struct srchargs *dsa)
{
int rc = 0;
if (major(dp->dv_dev) == major(dsa->dev))
{
if ((dsa->chan != BASE_MPX)
&& (dp->dv_dev != dsa->dev || dp->dv_chan != dsa->chan))
rc = 0;
/* make sure the device is open */
else if ((dp->dv_rdcnt + dp->dv_wrcnt + dp->dv_mntcnt) > 0)
rc = 1;
}
return rc;
}
int
snput(struct specnode *snp)
{
struct gnode * gp; /* spec gnode of specnode */
struct specnode *tsnp; /* temp specnode ptr in list */
struct specnode *bsnp; /* back specnode ptr in list */
struct devnode *dp; /* devnode for specnode */
struct fifonode *ffp; /* fifonode for specnode */
struct hnode *fhp; /* hash chain for fifo node */
int rc = 0; /* return code */
int count; /* local specnode count */
assert(snp->sn_count > 0);
/* The caller should be holding the specnode lock for this specnode */
ASSERT(lock_mine(&snp->sn_lock));
/* Decrement the specnode hold count and check for last access. */
if (--snp->sn_count != 0)
/* specnode still in use; unlock it and return */
SPECNODE_UNLOCK(snp);
else
{
/* clear the generation number so that this specnode
* can't be reclaimed.
*/
snp->sn_gen = 0;
switch (snp->sn_type)
{
case VMPC:
case VBLK:
case VCHR:
/* Remove the specnode from the devnode list and
* release the devnode.
*/
SPECNODE_UNLOCK(snp);
/* devnode must still be valid because of the
* specnode reference to it */
dp = STODP(snp);
DEVNODE_LOCK(dp);
bsnp = NULL;
for ( tsnp = dp->dv_specnodes;
tsnp != snp;
tsnp = tsnp->sn_next)
bsnp = tsnp;
if (bsnp)
bsnp->sn_next = snp->sn_next;
else
dp->dv_specnodes = snp->sn_next;
/* devput unlocks devnode_lock */
devput(dp);
break;
case VFIFO:
/* Get the fifonode. The specnode must then be
* unlocked before we can get the spechash lock.
* The specnode will not be reused because its
* count is zero. This is enforced in fifo_vp.
*/
ffp = STOFP(snp);
SPECNODE_UNLOCK(snp);
/* There is only one specnode per fifonode.
* If the fifonode is on a hash chain (i.e.,
* it is a fifo and not a pipe) remove it.
*/
if (ffp->ff_forw != NULL)
{
/* Get the hash chain and lock it. */
fhp = FIFOHASH(ffp->ff_pfsgnode);
SPECHASH_LOCK(fhp);
ffp->ff_forw->ff_back = ffp->ff_back;
ffp->ff_back->ff_forw = ffp->ff_forw;
SPECHASH_UNLOCK(fhp);
}
/* free the fifonode */
gpai_free(fnpool, ffp);
break;
default:
rc = EINVAL;
}
/* free any in-core attributes */
if (snp->sn_attr)
{
/* free any in-core ACLs */
if (snp->sn_acl)
free((long)snp->sn_acl & ~ACL_INCORE);
free(snp->sn_attr);
}
/* free the gn_reclk_lock */
gp = STOSGP(snp);
lock_free(&gp->gn_reclk_lock);
/* free the specnode */
gpai_free(snpool, snp);
}
return rc;
}
int
smark( struct specnode *snp,
int atype,
struct ucred *crp)
{
struct timestruc_t ts;
/* The caller must be holding the specnode lock */
ASSERT(lock_mine(&snp->sn_lock));
curtime(&ts);
/* if no in-core attributes, change PFS times */
if (snp->sn_attr == NULL)
return VNOP_SETATTR(snp->sn_vnode->v_pfsvnode,
V_STIME,
(atype & IACC)? &ts : 0,
(atype & IUPD)? &ts : 0,
(atype & ICHG)? &ts : 0,
crp);
/* update in-core times */
if (atype & IACC)
snp->sn_atime = ts;
if (atype & IUPD)
snp->sn_mtime = ts;
if (atype & ICHG)
snp->sn_ctime = ts;
return 0;
}
/*
* NAME: ic_specaccess(snp, mode, who, crp)
*
* FUNCTION: Check rwx permissions: If file has an ACL and I_SACL
* is on then use the ACL, otherwise use the mode bits.
*
* PARAMETERS: snp - specnode to check permissions on
* mode - mode to check for ie rwx
* who - one of: ACC_SELF
* ACC_OTHERS
* ACC_ANY
* ACC_ALL
*
* RETURNS: 0 - success
* EINVAL - invalid mode or who argument
*/
int
ic_specaccess (
struct specnode *snp, /* specnode for in-core check */
int mode, /* access mode(s) to check */
int who, /* who to check access for */
struct ucred *crp) /* ptr to relevant credentials */
{
int rc; /* return code */
/*
* We must be holding the specnode lock of this specnode
*/
ASSERT(lock_mine(&snp->sn_lock));
/* check the validity of "mode" */
if (mode & ~0x7)
return EINVAL;
if (who == ACC_SELF)
rc = specacc(snp, mode, crp);
else if (who == ACC_OTHERS || who == ACC_ANY || who == ACC_ALL)
{
switch (mode)
{
case R_ACC:
case W_ACC:
case X_ACC:
break;
default:
return EINVAL;
}
if (who == ACC_ALL)
{
if ( /* the access is denied the owner */
(!((snp->sn_mode >> 6) & mode)) ||
/* the access is denied the group */
(!((snp->sn_mode >> 3) & mode)) ||
/* the access is not granted by default */
(!(snp->sn_mode & mode)))
return EACCES;
return 0;
}
/* instant success if mode bits for
* "others" allows access
*/
if (snp->sn_mode & mode)
return 0;
if (who == ACC_ANY)
{
if (((snp->sn_mode >> 6) & mode) == mode)
return 0;
}
if (((snp->sn_mode >> 3) & mode) == mode)
return 0;
return EACCES;
}
else
return EINVAL;
return rc;
}
int
specaccess(
struct vnode * vp, /* spec vnode for access check */
int flags, /* open style flags */
struct ucred * crp) /* credentials */
{
struct specnode *snp; /* specnode for this vnode */
int amode = 0; /* access mode needed */
/*
* We must have the specnode locked upon call to this routine
*/
ASSERT(lock_mine(&VTOSP(vp)->sn_lock));
if (flags & FREAD)
amode |= R_ACC;
/* only FWRITE and FTRUNC (not FAPPEND) imply writing */
if (flags & (FWRITE|FTRUNC))
amode |= W_ACC;
/* Let the PFS handle the access checking for everything but
* mpx channels and pipes.
*/
if (vp->v_pfsvnode && !MPX_CHANNEL(VTOGP(vp)))
return VNOP_ACCESS(vp->v_pfsvnode, amode, ACC_SELF, crp);
return specacc(VTOSP(vp), amode, crp);
}
int
specacc(
struct specnode *snp, /* specnode for access check */
int amode, /* access mode to check */
struct ucred *crp) /* ptr to credentials */
{
int deny; /* prohibited access types */
int perm; /* permitted access modes */
int restr; /* restricted access modes */
if (GETEUID() == snp->sn_uid)
{
perm = (snp->sn_mode >> 6) & 07;
/* This is also a restriction! */
restr = ~perm & 07;
}
else if (groupmember_cr(snp->sn_gid, crp))
{
perm = (snp->sn_mode >> 3) & 07;
restr = 0;
}
else /* other */
{
perm = snp->sn_mode & 07;
restr = ~perm & 07;
}
/* check for access modes which are permitted and
* not restricted.
*/
deny = (amode & ~(perm & ~restr));
if (deny == 0)
return 0;
if (privcheck_cr(BYPASS_DAC, crp) == 0)
return 0;
if (deny & R_ACC)
if (privcheck_cr(BYPASS_DAC_READ, crp))
return EACCES;
if (deny & W_ACC)
if (privcheck_cr(BYPASS_DAC_WRITE, crp))
return EACCES;
if (deny & X_ACC)
if (privcheck_cr(BYPASS_DAC_EXEC, crp))
return EACCES;
/* priveleges allow access */
return 0;
}
#define ESTUPID EBUSY
/*
* NAME: spec_clone(svpp, flags, ext, vinfop, crp)
*
* FUNCTION: clone a special file
*
* PARAMETERS: svpp - old specnode on way in
* shiny new cloned specnode on way out
* flags - open flags
* ext - whatever was sent via openx (extra? external?)
* vinfop - not used (XXX so why is it here?)
* crp - credential
*
* RETURNS: 0 - success
* !0 - errors from lower level routines
*
* We have previously called the dd open entry point and it has
* returned ECLONEME, an indication that it wishes to do some
* finagling with the devno before we start using it. The vnode, etc.
* we eventually set up will be inaccessible via the file system.
* It may, however, share a devnode with a special file opened the
* normal way.
*
* for example:
*
* suppose dev(28,-) is the clone dd (the minor number doesn't matter)
* someone opens dev(28,30)
* this is really a request to clone dev(30,x) (the minor number is
* assigned by the dd)
* the original VNOP_OPEN() (i.e., spec_open()) in openpnp() fails ECLONEME
* openpnp() recognizes the failure and calls spec_clone
* we allocate a new specnode and a new specfs vnode
* we then re-call the dev(28,30) dd open entry point, requesting cloning
* the dd (one of them) assigns a new minor number for device major 30
* and sends the information back to us (it also actually does the open)
* we get a devnode for this new (to us) device and finish filling in and
* connecting the nodes
* we release the old (dev(28,30)) nodes and return the new one to
* openpnp()
*/
int
spec_clone(
struct vnode **svpp, /* uncloned spec vnode in, cloned out */
int flags,
int ext,
caddr_t vinfop,
struct ucred *crp)
{
struct vfs *oldvfsp; /* PFS vnode's vfs */
struct vnode *osvp; /* old spec vnode */
struct gnode *osgp; /* old spec gnode for special */
struct specnode *snp = NULL; /* specnode for device special */
struct devnode *dp = NULL; /* devnode for device */
struct gnode *dgp; /* devnode's gnode */
struct gnode *sgp; /* spec gnode for special file */
struct vnode *nsvp = NULL; /* new specfs vnode */
int rc; /* return codes */
dev_t olddev; /* the device the user opened */
dev_t newdev; /* the new device the dd wants */
caddr_t pdata; /* the driver returns something
here we'd rather be ignorant
of */
int noctl; /* no controlling tty? */
struct timestruc_t t;
/* remember the old spec vnode and gnode */
osvp = *svpp;
osgp = VTOGP(osvp);
olddev = osvp->v_pfsvnode->v_rdev;
oldvfsp = osvp->v_pfsvnode->v_vfsp;
/*
* now re-call the device open, asking to be cloned
* Much of the following code is reproduced from
* cdev_open(cdev_subr.c) and devcopen(devsubs.c).
*/
/* We pass only the allowed open flags and whether the caller
* was in kernel mode.
*/
flags &= (FMASK | FKERNEL);
/* Hang onto the state of controlling terminal to see if
* it is established by the device open.
* XXX Is this possible for a clone open?
*/
noctl = (u.u_ttyp == NULL);
/* I can't tell if it's busy yet. */
if (flags & DMOUNT)
return ESTUPID; /* XXX */
/*
* We overload the ext parameter. On the way in it's
* the address of a variable containing the original
* user-supplied ext. The driver must use extra
* indirection to get to the real ext. On the way out
* the device driver uses it to tell us the new device
* it has picked. This subtrefuge is only used at the
* second open call for drivers that have returned
* ECLONEME, i.e., only for clone opens.
*/
*(int *)(&newdev) = ext;
DD_ENT(rc = ,
(*devsw[major(olddev)].d_open)(olddev,
(flags & DMASK) | DDOCLONE,
&pdata,
(int *)&newdev),
IPRI_NOPREMPT, major(olddev));
if (rc)
return(rc);
/*
* allocate and initialize a new specnode
*/
if ((snp = (struct specnode *)gpai_alloc(snpool)) == NULL)
{
rc = ENOMEM;
goto out;
}
snp->sn_gen = fetch_and_add(&spec_generation, 1);
/* remember where the gnode is */
sgp = STOSGP(snp);
/* fill in the gnode */
sgp->gn_type = VCHR;
sgp->gn_flags = 0;
sgp->gn_seg = INVLSID;
sgp->gn_mwrcnt = 0;
sgp->gn_mrdcnt = 0;
sgp->gn_rdcnt = 0;
sgp->gn_wrcnt = 0;
sgp->gn_excnt = 0;
sgp->gn_rshcnt = 0;
sgp->gn_ops = &spec_vnops;
sgp->gn_vnode = NULL;
sgp->gn_rdev = olddev; /* this will change, natch */
sgp->gn_chan = 0;
sgp->gn_reclk_event = EVENT_NULL;
sgp->gn_filocks = NULL;
sgp->gn_data = (caddr_t)snp;
lock_alloc(&sgp->gn_reclk_lock, LOCK_ALLOC_PAGED,
RECLK_LOCK_CLASS, gn_reclk_count++);
simple_lock_init(&sgp->gn_reclk_lock);
/* fill in the specnode */
snp->sn_vnode = NULL; /* added later, in vn_get */
snp->sn_count = 1;
snp->sn_pfsgnode = NULL; /* it's anonymous */
snp->sn_attr = NULL;
/* there's no correct devnode to connect to yet */
snp->sn_devnode = NULL;
if ((snp->sn_attr = malloc(sizeof *snp->sn_attr)) == NULL)
{
rc = ENOMEM;
goto out;
}
curtime(&t);
bzero(snp->sn_attr, sizeof *snp->sn_attr); /* XXX */
snp->sn_mode = IFCHR;
snp->sn_uid = crp->cr_uid;
snp->sn_gid = crp->cr_gid;
snp->sn_atime = t;
snp->sn_mtime = t;
snp->sn_ctime = t;
snp->sn_acl = NULL;
/*
* now allocate and initialize a new spec vnode
*/
/* Allocate the specfs vnode from the PFS vnode's vfs.
* This ensures that those in the LFS that use the vfs
* obtained from the vnode we return will get the right vfs.
*/
if (rc = vn_get(oldvfsp, sgp, &nsvp))
goto out;
nsvp->v_pfsvnode = NULL;
/* stuff the new device into the specnode's gnode */
sgp->gn_rdev = newdev;
/* at last we can get a devnode */
if (rc = devget(sgp->gn_rdev, NULL, VCHR, &dp))
goto out;
/* and tuck it's private data away safely */
dp->dv_pdata = pdata;
dgp = DTODGP(dp);
/* adjust open counts */
if (flags & DREAD)
dgp->gn_rdcnt += 1;
if (flags & DWRITE)
dgp->gn_wrcnt += 1;
if (flags & DMOUNT)
dgp->gn_mntcnt += 1;
/* connect specnode to devnode */
snp->sn_devnode = dp;
snp->sn_next = dp->dv_specnodes;
dp->dv_specnodes = snp;
VNOP_RELE(osvp);
/* pass back the new specfs vnode */
*svpp = nsvp;
/* Save the controlling terminal device number if the device
* open established the controlling terminal.
*/
if (noctl && u.u_ttyp)
u.u_ttyd = newdev;
/* Unlock the devnode */
DEVNODE_UNLOCK(dp);
/* return success */
return 0;
out:
DD_ENT( (void), (*devsw[major(newdev)].d_close)
(newdev, 0, 0),IPRI_NOPREMPT, major(newdev));
if (snp->sn_attr)
free(snp->sn_attr);
if (snp)
gpai_free(snpool, snp);
if (nsvp)
vn_free(nsvp);
if (dp)
devput(dp);
return rc;
}
/* devtosth -- convert a device (maj,min pair) to that private data
* we're ignorant of
* XXX this isn't the only place I make assumptions about channels
* and vtypes. can I ignore mpx devices?
*/
caddr_t
devtosth(dev_t dev)
{
struct devnode *dp;
int lockt;
caddr_t rv;
/* protect against finding fifos */
if (dev == NODEVICE)
return NULL;
lockt = FS_KLOCK(&kernel_lock, LOCK_SHORT);
/* Get the devnode for the device. */
if (devget(dev, NULL, VCHR, &dp))
rv = NULL;
else {
rv = dp->dv_pdata;
devput(dp);
}
if (lockt != LOCK_NEST)
FS_KUNLOCK(&kernel_lock);
return rv;
}
extern int fp_getdevno(struct file *fp, dev_t *devp, chan_t *chanp);
/* fdtosth -- convert a fd (presumably for an open device)
* to that private data we're ignorant of
* XXX this isn't the only place I make assumptions about channels
* and vtypes
*/
caddr_t
fdtosth(int fd)
{
struct file *fp;
struct vnode *vp;
struct devnode *dp;
dev_t dev;
caddr_t rv = NULL;
int lockt;
if ((lockt = IS_LOCKED(&kernel_lock)) != 0)
unlockl(&kernel_lock);
/* get the file pointer ... */
if (getft(fd, &fp, DTYPE_VNODE))
goto out2;
/* XXX I'm calling this partly because I'm unsure about
the significance of the f_type field */
if (fp_getdevno(fp, &dev, (chan_t *)0)) /* dgb added third arg */
goto out;
/* XXX as noted above, I may be overachieving here */
if (fp->f_vnode->v_vntype != VCHR) /* dgb uses fp to find vnode */
goto out;
/* and then the devnode */
if (devget(dev, NULL, VCHR, &dp))
goto out;
else {
rv = dp->dv_pdata;
devput(dp);
}
out:
/* Decrement the use count on the file descriptor */
ufdrele(fd);
out2:
if (lockt)
lockl(&kernel_lock, LOCK_SHORT);
return rv;
}
/* devsetsth -- stuff something we're ignorant of into the private
* data for a devnode corresponding to a device (maj,min pair)
*
* XXX this isn't the only place I make assumptions about channels
* and vtypes
*/
int
devsetsth(dev_t dev, caddr_t pdata)
{
int rc = 0;
struct devnode *dp;
int lockt;
if ((lockt = IS_LOCKED(&kernel_lock)) != 0 )
unlockl(&kernel_lock);
/* Get the devnode for the device. */
if (!(rc = devget(dev, NULL, VCHR, &dp))) {
dp->dv_pdata = pdata;
devput(dp);
}
if (lockt)
lockl(&kernel_lock, LOCK_SHORT);
return rc;
}