1478 lines
35 KiB
C
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;
|
|
}
|