Index: head/sys/dev/ccd/ccd.c =================================================================== --- head/sys/dev/ccd/ccd.c (revision 62549) +++ head/sys/dev/ccd/ccd.c (revision 62550) @@ -1,1712 +1,1713 @@ /* $FreeBSD$ */ /* $NetBSD: ccd.c,v 1.22 1995/12/08 19:13:26 thorpej Exp $ */ /* * Copyright (c) 1995 Jason R. Thorpe. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project * by Jason R. Thorpe. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: Utah $Hdr: cd.c 1.6 90/11/28$ * * @(#)cd.c 8.2 (Berkeley) 11/16/93 */ /* * "Concatenated" disk driver. * * Dynamic configuration and disklabel support by: * Jason R. Thorpe * Numerical Aerodynamic Simulation Facility * Mail Stop 258-6 * NASA Ames Research Center * Moffett Field, CA 94035 */ #include "ccd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CCDDEBUG) && !defined(DEBUG) #define DEBUG #endif #ifdef DEBUG #define CCDB_FOLLOW 0x01 #define CCDB_INIT 0x02 #define CCDB_IO 0x04 #define CCDB_LABEL 0x08 #define CCDB_VNODE 0x10 static int ccddebug = CCDB_FOLLOW | CCDB_INIT | CCDB_IO | CCDB_LABEL | CCDB_VNODE; SYSCTL_INT(_debug, OID_AUTO, ccddebug, CTLFLAG_RW, &ccddebug, 0, ""); #undef DEBUG #endif #define ccdunit(x) dkunit(x) #define ccdpart(x) dkpart(x) /* This is how mirroring works (only writes are special): When initiating a write, ccdbuffer() returns two "struct ccdbuf *"s linked together by the cb_mirror field. "cb_pflags & CCDPF_MIRROR_DONE" is set to 0 on both of them. When a component returns to ccdiodone(), it checks if "cb_pflags & CCDPF_MIRROR_DONE" is set or not. If not, it sets the partner's flag and returns. If it is, it means its partner has already returned, so it will go to the regular cleanup. */ struct ccdbuf { struct bio cb_buf; /* new I/O buf */ struct bio *cb_obp; /* ptr. to original I/O buf */ struct ccdbuf *cb_freenext; /* free list link */ int cb_unit; /* target unit */ int cb_comp; /* target component */ int cb_pflags; /* mirror/parity status flag */ struct ccdbuf *cb_mirror; /* mirror counterpart */ }; /* bits in cb_pflags */ #define CCDPF_MIRROR_DONE 1 /* if set, mirror counterpart is done */ #define CCDLABELDEV(dev) \ (makedev(major((dev)), dkmakeminor(ccdunit((dev)), 0, RAW_PART))) static d_open_t ccdopen; static d_close_t ccdclose; static d_strategy_t ccdstrategy; static d_ioctl_t ccdioctl; static d_dump_t ccddump; static d_psize_t ccdsize; #define NCCDFREEHIWAT 16 #define CDEV_MAJOR 74 #define BDEV_MAJOR 21 static struct cdevsw ccd_cdevsw = { /* open */ ccdopen, /* close */ ccdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ ccdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ ccdstrategy, /* name */ "ccd", /* maj */ CDEV_MAJOR, /* dump */ ccddump, /* psize */ ccdsize, /* flags */ D_DISK, /* bmaj */ BDEV_MAJOR }; /* called during module initialization */ static void ccdattach __P((void)); static int ccd_modevent __P((module_t, int, void *)); /* called by biodone() at interrupt time */ static void ccdiodone __P((struct bio *bp)); static void ccdstart __P((struct ccd_softc *, struct bio *)); static void ccdinterleave __P((struct ccd_softc *, int)); static void ccdintr __P((struct ccd_softc *, struct bio *)); static int ccdinit __P((struct ccddevice *, char **, struct proc *)); static int ccdlookup __P((char *, struct proc *p, struct vnode **)); static void ccdbuffer __P((struct ccdbuf **ret, struct ccd_softc *, struct bio *, daddr_t, caddr_t, long)); static void ccdgetdisklabel __P((dev_t)); static void ccdmakedisklabel __P((struct ccd_softc *)); static int ccdlock __P((struct ccd_softc *)); static void ccdunlock __P((struct ccd_softc *)); #ifdef DEBUG static void printiinfo __P((struct ccdiinfo *)); #endif /* Non-private for the benefit of libkvm. */ struct ccd_softc *ccd_softc; struct ccddevice *ccddevs; struct ccdbuf *ccdfreebufs; static int numccdfreebufs; static int numccd = 0; /* * getccdbuf() - Allocate and zero a ccd buffer. * * This routine is called at splbio(). */ static __inline struct ccdbuf * getccdbuf(struct ccdbuf *cpy) { struct ccdbuf *cbp; /* * Allocate from freelist or malloc as necessary */ if ((cbp = ccdfreebufs) != NULL) { ccdfreebufs = cbp->cb_freenext; --numccdfreebufs; } else { cbp = malloc(sizeof(struct ccdbuf), M_DEVBUF, M_WAITOK); } /* * Used by mirroring code */ if (cpy) bcopy(cpy, cbp, sizeof(struct ccdbuf)); else bzero(cbp, sizeof(struct ccdbuf)); /* * independant struct bio initialization */ return(cbp); } /* * putccdbuf() - Free a ccd buffer. * * This routine is called at splbio(). */ static __inline void putccdbuf(struct ccdbuf *cbp) { if (numccdfreebufs < NCCDFREEHIWAT) { cbp->cb_freenext = ccdfreebufs; ccdfreebufs = cbp; ++numccdfreebufs; } else { free((caddr_t)cbp, M_DEVBUF); } } /* * Number of blocks to untouched in front of a component partition. * This is to avoid violating its disklabel area when it starts at the * beginning of the slice. */ #if !defined(CCD_OFFSET) #define CCD_OFFSET 16 #endif /* * Called by main() during pseudo-device attachment. All we need * to do is allocate enough space for devices to be configured later, and * add devsw entries. */ static void ccdattach() { int i; int num = NCCD; if (num > 1) printf("ccd0-%d: Concatenated disk drivers\n", num-1); else printf("ccd0: Concatenated disk driver\n"); ccd_softc = (struct ccd_softc *)malloc(num * sizeof(struct ccd_softc), M_DEVBUF, M_NOWAIT); ccddevs = (struct ccddevice *)malloc(num * sizeof(struct ccddevice), M_DEVBUF, M_NOWAIT); if ((ccd_softc == NULL) || (ccddevs == NULL)) { printf("WARNING: no memory for concatenated disks\n"); if (ccd_softc != NULL) free(ccd_softc, M_DEVBUF); if (ccddevs != NULL) free(ccddevs, M_DEVBUF); return; } numccd = num; bzero(ccd_softc, num * sizeof(struct ccd_softc)); bzero(ccddevs, num * sizeof(struct ccddevice)); cdevsw_add(&ccd_cdevsw); /* XXX: is this necessary? */ for (i = 0; i < numccd; ++i) ccddevs[i].ccd_dk = -1; } static int ccd_modevent(mod, type, data) module_t mod; int type; void *data; { int error = 0; switch (type) { case MOD_LOAD: ccdattach(); break; case MOD_UNLOAD: printf("ccd0: Unload not supported!\n"); error = EOPNOTSUPP; break; default: /* MOD_SHUTDOWN etc */ break; } return (error); } DEV_MODULE(ccd, ccd_modevent, NULL); static int ccdinit(ccd, cpaths, p) struct ccddevice *ccd; char **cpaths; struct proc *p; { struct ccd_softc *cs = &ccd_softc[ccd->ccd_unit]; struct ccdcinfo *ci = NULL; /* XXX */ size_t size; int ix; struct vnode *vp; size_t minsize; int maxsecsize; struct partinfo dpart; struct ccdgeom *ccg = &cs->sc_geom; char tmppath[MAXPATHLEN]; int error = 0; #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccdinit: unit %d\n", ccd->ccd_unit); #endif cs->sc_size = 0; cs->sc_ileave = ccd->ccd_interleave; cs->sc_nccdisks = ccd->ccd_ndev; /* Allocate space for the component info. */ cs->sc_cinfo = malloc(cs->sc_nccdisks * sizeof(struct ccdcinfo), M_DEVBUF, M_WAITOK); /* * Verify that each component piece exists and record * relevant information about it. */ maxsecsize = 0; minsize = 0; for (ix = 0; ix < cs->sc_nccdisks; ix++) { vp = ccd->ccd_vpp[ix]; ci = &cs->sc_cinfo[ix]; ci->ci_vp = vp; /* * Copy in the pathname of the component. */ bzero(tmppath, sizeof(tmppath)); /* sanity */ if ((error = copyinstr(cpaths[ix], tmppath, MAXPATHLEN, &ci->ci_pathlen)) != 0) { #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccd%d: can't copy path, error = %d\n", ccd->ccd_unit, error); #endif goto fail; } ci->ci_path = malloc(ci->ci_pathlen, M_DEVBUF, M_WAITOK); bcopy(tmppath, ci->ci_path, ci->ci_pathlen); ci->ci_dev = vn_todev(vp); /* * Get partition information for the component. */ if ((error = VOP_IOCTL(vp, DIOCGPART, (caddr_t)&dpart, FREAD, p->p_ucred, p)) != 0) { #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccd%d: %s: ioctl failed, error = %d\n", ccd->ccd_unit, ci->ci_path, error); #endif goto fail; } if (dpart.part->p_fstype == FS_BSDFFS) { maxsecsize = ((dpart.disklab->d_secsize > maxsecsize) ? dpart.disklab->d_secsize : maxsecsize); size = dpart.part->p_size - CCD_OFFSET; } else { #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccd%d: %s: incorrect partition type\n", ccd->ccd_unit, ci->ci_path); #endif error = EFTYPE; goto fail; } /* * Calculate the size, truncating to an interleave * boundary if necessary. */ if (cs->sc_ileave > 1) size -= size % cs->sc_ileave; if (size == 0) { #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccd%d: %s: size == 0\n", ccd->ccd_unit, ci->ci_path); #endif error = ENODEV; goto fail; } if (minsize == 0 || size < minsize) minsize = size; ci->ci_size = size; cs->sc_size += size; } /* * Don't allow the interleave to be smaller than * the biggest component sector. */ if ((cs->sc_ileave > 0) && (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) { #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccd%d: interleave must be at least %d\n", ccd->ccd_unit, (maxsecsize / DEV_BSIZE)); #endif error = EINVAL; goto fail; } /* * If uniform interleave is desired set all sizes to that of * the smallest component. This will guarentee that a single * interleave table is generated. * * Lost space must be taken into account when calculating the * overall size. Half the space is lost when CCDF_MIRROR is * specified. One disk is lost when CCDF_PARITY is specified. */ if (ccd->ccd_flags & CCDF_UNIFORM) { for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) { ci->ci_size = minsize; } if (ccd->ccd_flags & CCDF_MIRROR) { /* * Check to see if an even number of components * have been specified. The interleave must also * be non-zero in order for us to be able to * guarentee the topology. */ if (cs->sc_nccdisks % 2) { printf("ccd%d: mirroring requires an even number of disks\n", ccd->ccd_unit ); error = EINVAL; goto fail; } if (cs->sc_ileave == 0) { printf("ccd%d: an interleave must be specified when mirroring\n", ccd->ccd_unit); error = EINVAL; goto fail; } cs->sc_size = (cs->sc_nccdisks/2) * minsize; } else if (ccd->ccd_flags & CCDF_PARITY) { cs->sc_size = (cs->sc_nccdisks-1) * minsize; } else { if (cs->sc_ileave == 0) { printf("ccd%d: an interleave must be specified when using parity\n", ccd->ccd_unit); error = EINVAL; goto fail; } cs->sc_size = cs->sc_nccdisks * minsize; } } /* * Construct the interleave table. */ ccdinterleave(cs, ccd->ccd_unit); /* * Create pseudo-geometry based on 1MB cylinders. It's * pretty close. */ ccg->ccg_secsize = maxsecsize; ccg->ccg_ntracks = 1; ccg->ccg_nsectors = 1024 * 1024 / ccg->ccg_secsize; ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors; /* * Add an devstat entry for this device. */ devstat_add_entry(&cs->device_stats, "ccd", ccd->ccd_unit, ccg->ccg_secsize, DEVSTAT_ALL_SUPPORTED, DEVSTAT_TYPE_STORARRAY |DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_ARRAY); cs->sc_flags |= CCDF_INITED; cs->sc_cflags = ccd->ccd_flags; /* So we can find out later... */ cs->sc_unit = ccd->ccd_unit; return (0); fail: while (ci > cs->sc_cinfo) { ci--; free(ci->ci_path, M_DEVBUF); } free(cs->sc_cinfo, M_DEVBUF); return (error); } static void ccdinterleave(cs, unit) struct ccd_softc *cs; int unit; { struct ccdcinfo *ci, *smallci; struct ccdiinfo *ii; daddr_t bn, lbn; int ix; u_long size; #ifdef DEBUG if (ccddebug & CCDB_INIT) printf("ccdinterleave(%x): ileave %d\n", cs, cs->sc_ileave); #endif /* * Allocate an interleave table. The worst case occurs when each * of N disks is of a different size, resulting in N interleave * tables. * * Chances are this is too big, but we don't care. */ size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo); cs->sc_itable = (struct ccdiinfo *)malloc(size, M_DEVBUF, M_WAITOK); bzero((caddr_t)cs->sc_itable, size); /* * Trivial case: no interleave (actually interleave of disk size). * Each table entry represents a single component in its entirety. * * An interleave of 0 may not be used with a mirror or parity setup. */ if (cs->sc_ileave == 0) { bn = 0; ii = cs->sc_itable; for (ix = 0; ix < cs->sc_nccdisks; ix++) { /* Allocate space for ii_index. */ ii->ii_index = malloc(sizeof(int), M_DEVBUF, M_WAITOK); ii->ii_ndisk = 1; ii->ii_startblk = bn; ii->ii_startoff = 0; ii->ii_index[0] = ix; bn += cs->sc_cinfo[ix].ci_size; ii++; } ii->ii_ndisk = 0; #ifdef DEBUG if (ccddebug & CCDB_INIT) printiinfo(cs->sc_itable); #endif return; } /* * The following isn't fast or pretty; it doesn't have to be. */ size = 0; bn = lbn = 0; for (ii = cs->sc_itable; ; ii++) { /* * Allocate space for ii_index. We might allocate more then * we use. */ ii->ii_index = malloc((sizeof(int) * cs->sc_nccdisks), M_DEVBUF, M_WAITOK); /* * Locate the smallest of the remaining components */ smallci = NULL; for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) { if (ci->ci_size > size && (smallci == NULL || ci->ci_size < smallci->ci_size)) { smallci = ci; } } /* * Nobody left, all done */ if (smallci == NULL) { ii->ii_ndisk = 0; break; } /* * Record starting logical block using an sc_ileave blocksize. */ ii->ii_startblk = bn / cs->sc_ileave; /* * Record starting comopnent block using an sc_ileave * blocksize. This value is relative to the beginning of * a component disk. */ ii->ii_startoff = lbn; /* * Determine how many disks take part in this interleave * and record their indices. */ ix = 0; for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) { if (ci->ci_size >= smallci->ci_size) { ii->ii_index[ix++] = ci - cs->sc_cinfo; } } ii->ii_ndisk = ix; bn += ix * (smallci->ci_size - size); lbn = smallci->ci_size / cs->sc_ileave; size = smallci->ci_size; } #ifdef DEBUG if (ccddebug & CCDB_INIT) printiinfo(cs->sc_itable); #endif } /* ARGSUSED */ static int ccdopen(dev, flags, fmt, p) dev_t dev; int flags, fmt; struct proc *p; { int unit = ccdunit(dev); struct ccd_softc *cs; struct disklabel *lp; int error = 0, part, pmask; #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdopen(%x, %x)\n", dev, flags); #endif if (unit >= numccd) return (ENXIO); cs = &ccd_softc[unit]; if ((error = ccdlock(cs)) != 0) return (error); lp = &cs->sc_label; part = ccdpart(dev); pmask = (1 << part); /* * If we're initialized, check to see if there are any other * open partitions. If not, then it's safe to update * the in-core disklabel. */ if ((cs->sc_flags & CCDF_INITED) && (cs->sc_openmask == 0)) ccdgetdisklabel(dev); /* Check that the partition exists. */ if (part != RAW_PART && ((part >= lp->d_npartitions) || (lp->d_partitions[part].p_fstype == FS_UNUSED))) { error = ENXIO; goto done; } cs->sc_openmask |= pmask; done: ccdunlock(cs); return (0); } /* ARGSUSED */ static int ccdclose(dev, flags, fmt, p) dev_t dev; int flags, fmt; struct proc *p; { int unit = ccdunit(dev); struct ccd_softc *cs; int error = 0, part; #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdclose(%x, %x)\n", dev, flags); #endif if (unit >= numccd) return (ENXIO); cs = &ccd_softc[unit]; if ((error = ccdlock(cs)) != 0) return (error); part = ccdpart(dev); /* ...that much closer to allowing unconfiguration... */ cs->sc_openmask &= ~(1 << part); ccdunlock(cs); return (0); } static void ccdstrategy(bp) struct bio *bp; { int unit = ccdunit(bp->bio_dev); struct ccd_softc *cs = &ccd_softc[unit]; int s; int wlabel; struct disklabel *lp; #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdstrategy(%x): unit %d\n", bp, unit); #endif if ((cs->sc_flags & CCDF_INITED) == 0) { bp->bio_error = ENXIO; bp->bio_flags |= BIO_ERROR; goto done; } /* If it's a nil transfer, wake up the top half now. */ if (bp->bio_bcount == 0) goto done; lp = &cs->sc_label; /* * Do bounds checking and adjust transfer. If there's an * error, the bounds check will flag that for us. */ wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING); if (ccdpart(bp->bio_dev) != RAW_PART) { if (bounds_check_with_label(bp, lp, wlabel) <= 0) goto done; } else { int pbn; /* in sc_secsize chunks */ long sz; /* in sc_secsize chunks */ pbn = bp->bio_blkno / (cs->sc_geom.ccg_secsize / DEV_BSIZE); sz = howmany(bp->bio_bcount, cs->sc_geom.ccg_secsize); /* * If out of bounds return an error. If at the EOF point, * simply read or write less. */ if (pbn < 0 || pbn >= cs->sc_size) { bp->bio_resid = bp->bio_bcount; if (pbn != cs->sc_size) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; } goto done; } /* * If the request crosses EOF, truncate the request. */ if (pbn + sz > cs->sc_size) { bp->bio_bcount = (cs->sc_size - pbn) * cs->sc_geom.ccg_secsize; } } bp->bio_resid = bp->bio_bcount; /* * "Start" the unit. */ s = splbio(); ccdstart(cs, bp); splx(s); return; done: biodone(bp); } static void ccdstart(cs, bp) struct ccd_softc *cs; struct bio *bp; { long bcount, rcount; struct ccdbuf *cbp[4]; /* XXX! : 2 reads and 2 writes for RAID 4/5 */ caddr_t addr; daddr_t bn; struct partition *pp; #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdstart(%x, %x)\n", cs, bp); #endif /* Record the transaction start */ devstat_start_transaction(&cs->device_stats); /* * Translate the partition-relative block number to an absolute. */ bn = bp->bio_blkno; if (ccdpart(bp->bio_dev) != RAW_PART) { pp = &cs->sc_label.d_partitions[ccdpart(bp->bio_dev)]; bn += pp->p_offset; } /* * Allocate component buffers and fire off the requests */ addr = bp->bio_data; for (bcount = bp->bio_bcount; bcount > 0; bcount -= rcount) { ccdbuffer(cbp, cs, bp, bn, addr, bcount); rcount = cbp[0]->cb_buf.bio_bcount; if (cs->sc_cflags & CCDF_MIRROR) { /* * Mirroring. Writes go to both disks, reads are * taken from whichever disk seems most appropriate. * * We attempt to localize reads to the disk whos arm * is nearest the read request. We ignore seeks due * to writes when making this determination and we * also try to avoid hogging. */ if (cbp[0]->cb_buf.bio_cmd == BIO_WRITE) { BIO_STRATEGY(&cbp[0]->cb_buf, 0); BIO_STRATEGY(&cbp[1]->cb_buf, 0); } else { int pick = cs->sc_pick; daddr_t range = cs->sc_size / 16; if (bn < cs->sc_blk[pick] - range || bn > cs->sc_blk[pick] + range ) { cs->sc_pick = pick = 1 - pick; } cs->sc_blk[pick] = bn + btodb(rcount); BIO_STRATEGY(&cbp[pick]->cb_buf, 0); } } else { /* * Not mirroring */ BIO_STRATEGY(&cbp[0]->cb_buf, 0); } bn += btodb(rcount); addr += rcount; } } /* * Build a component buffer header. */ static void ccdbuffer(cb, cs, bp, bn, addr, bcount) struct ccdbuf **cb; struct ccd_softc *cs; struct bio *bp; daddr_t bn; caddr_t addr; long bcount; { struct ccdcinfo *ci, *ci2 = NULL; /* XXX */ struct ccdbuf *cbp; daddr_t cbn, cboff; off_t cbc; #ifdef DEBUG if (ccddebug & CCDB_IO) printf("ccdbuffer(%x, %x, %d, %x, %d)\n", cs, bp, bn, addr, bcount); #endif /* * Determine which component bn falls in. */ cbn = bn; cboff = 0; if (cs->sc_ileave == 0) { /* * Serially concatenated and neither a mirror nor a parity * config. This is a special case. */ daddr_t sblk; sblk = 0; for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++) sblk += ci->ci_size; cbn -= sblk; } else { struct ccdiinfo *ii; int ccdisk, off; /* * Calculate cbn, the logical superblock (sc_ileave chunks), * and cboff, a normal block offset (DEV_BSIZE chunks) relative * to cbn. */ cboff = cbn % cs->sc_ileave; /* DEV_BSIZE gran */ cbn = cbn / cs->sc_ileave; /* DEV_BSIZE * ileave gran */ /* * Figure out which interleave table to use. */ for (ii = cs->sc_itable; ii->ii_ndisk; ii++) { if (ii->ii_startblk > cbn) break; } ii--; /* * off is the logical superblock relative to the beginning * of this interleave block. */ off = cbn - ii->ii_startblk; /* * We must calculate which disk component to use (ccdisk), * and recalculate cbn to be the superblock relative to * the beginning of the component. This is typically done by * adding 'off' and ii->ii_startoff together. However, 'off' * must typically be divided by the number of components in * this interleave array to be properly convert it from a * CCD-relative logical superblock number to a * component-relative superblock number. */ if (ii->ii_ndisk == 1) { /* * When we have just one disk, it can't be a mirror * or a parity config. */ ccdisk = ii->ii_index[0]; cbn = ii->ii_startoff + off; } else { if (cs->sc_cflags & CCDF_MIRROR) { /* * We have forced a uniform mapping, resulting * in a single interleave array. We double * up on the first half of the available * components and our mirror is in the second * half. This only works with a single * interleave array because doubling up * doubles the number of sectors, so there * cannot be another interleave array because * the next interleave array's calculations * would be off. */ int ndisk2 = ii->ii_ndisk / 2; ccdisk = ii->ii_index[off % ndisk2]; cbn = ii->ii_startoff + off / ndisk2; ci2 = &cs->sc_cinfo[ccdisk + ndisk2]; } else if (cs->sc_cflags & CCDF_PARITY) { /* * XXX not implemented yet */ int ndisk2 = ii->ii_ndisk - 1; ccdisk = ii->ii_index[off % ndisk2]; cbn = ii->ii_startoff + off / ndisk2; if (cbn % ii->ii_ndisk <= ccdisk) ccdisk++; } else { ccdisk = ii->ii_index[off % ii->ii_ndisk]; cbn = ii->ii_startoff + off / ii->ii_ndisk; } } ci = &cs->sc_cinfo[ccdisk]; /* * Convert cbn from a superblock to a normal block so it * can be used to calculate (along with cboff) the normal * block index into this particular disk. */ cbn *= cs->sc_ileave; } /* * Fill in the component buf structure. */ cbp = getccdbuf(NULL); cbp->cb_buf.bio_cmd = bp->bio_cmd; cbp->cb_buf.bio_done = ccdiodone; cbp->cb_buf.bio_dev = ci->ci_dev; /* XXX */ cbp->cb_buf.bio_blkno = cbn + cboff + CCD_OFFSET; cbp->cb_buf.bio_offset = dbtob(cbn + cboff + CCD_OFFSET); cbp->cb_buf.bio_data = addr; if (cs->sc_ileave == 0) cbc = dbtob((off_t)(ci->ci_size - cbn)); else cbc = dbtob((off_t)(cs->sc_ileave - cboff)); cbp->cb_buf.bio_bcount = (cbc < bcount) ? cbc : bcount; cbp->cb_buf.bio_caller1 = (void*)cbp->cb_buf.bio_bcount; /* * context for ccdiodone */ cbp->cb_obp = bp; cbp->cb_unit = cs - ccd_softc; cbp->cb_comp = ci - cs->sc_cinfo; #ifdef DEBUG if (ccddebug & CCDB_IO) printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n", ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->cb_buf.bio_blkno, cbp->cb_buf.bio_data, cbp->cb_buf.bio_bcount); #endif cb[0] = cbp; /* * Note: both I/O's setup when reading from mirror, but only one * will be executed. */ if (cs->sc_cflags & CCDF_MIRROR) { /* mirror, setup second I/O */ cbp = getccdbuf(cb[0]); cbp->cb_buf.bio_dev = ci2->ci_dev; cbp->cb_comp = ci2 - cs->sc_cinfo; cb[1] = cbp; /* link together the ccdbuf's and clear "mirror done" flag */ cb[0]->cb_mirror = cb[1]; cb[1]->cb_mirror = cb[0]; cb[0]->cb_pflags &= ~CCDPF_MIRROR_DONE; cb[1]->cb_pflags &= ~CCDPF_MIRROR_DONE; } } static void ccdintr(cs, bp) struct ccd_softc *cs; struct bio *bp; { #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdintr(%x, %x)\n", cs, bp); #endif /* * Request is done for better or worse, wakeup the top half. */ if (bp->bio_flags & BIO_ERROR) bp->bio_resid = bp->bio_bcount; devstat_end_transaction_bio(&cs->device_stats, bp); biodone(bp); } /* * Called at interrupt time. * Mark the component as done and if all components are done, * take a ccd interrupt. */ static void ccdiodone(ibp) struct bio *ibp; { struct ccdbuf *cbp = (struct ccdbuf *)ibp; struct bio *bp = cbp->cb_obp; int unit = cbp->cb_unit; int count, s; s = splbio(); #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdiodone(%x)\n", cbp); if (ccddebug & CCDB_IO) { printf("ccdiodone: bp %x bcount %d resid %d\n", bp, bp->bio_bcount, bp->bio_resid); printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n", cbp->cb_buf.bio_dev, cbp->cb_comp, cbp, cbp->cb_buf.bio_blkno, cbp->cb_buf.bio_data, cbp->cb_buf.bio_bcount); } #endif /* * If an error occured, report it. If this is a mirrored * configuration and the first of two possible reads, do not * set the error in the bp yet because the second read may * succeed. */ if (cbp->cb_buf.bio_flags & BIO_ERROR) { const char *msg = ""; if ((ccd_softc[unit].sc_cflags & CCDF_MIRROR) && (cbp->cb_buf.bio_cmd == BIO_READ) && (cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) { /* * We will try our read on the other disk down * below, also reverse the default pick so if we * are doing a scan we do not keep hitting the * bad disk first. */ struct ccd_softc *cs = &ccd_softc[unit]; msg = ", trying other disk"; cs->sc_pick = 1 - cs->sc_pick; cs->sc_blk[cs->sc_pick] = bp->bio_blkno; } else { bp->bio_flags |= BIO_ERROR; bp->bio_error = cbp->cb_buf.bio_error ? cbp->cb_buf.bio_error : EIO; } printf("ccd%d: error %d on component %d block %d (ccd block %d)%s\n", unit, bp->bio_error, cbp->cb_comp, (int)cbp->cb_buf.bio_blkno, bp->bio_blkno, msg); } /* * Process mirror. If we are writing, I/O has been initiated on both * buffers and we fall through only after both are finished. * * If we are reading only one I/O is initiated at a time. If an * error occurs we initiate the second I/O and return, otherwise * we free the second I/O without initiating it. */ if (ccd_softc[unit].sc_cflags & CCDF_MIRROR) { if (cbp->cb_buf.bio_cmd == BIO_WRITE) { /* * When writing, handshake with the second buffer * to determine when both are done. If both are not * done, return here. */ if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) { cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE; putccdbuf(cbp); splx(s); return; } } else { /* * When reading, either dispose of the second buffer * or initiate I/O on the second buffer if an error * occured with this one. */ if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) { if (cbp->cb_buf.bio_flags & BIO_ERROR) { cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE; BIO_STRATEGY(&cbp->cb_mirror->cb_buf, 0); putccdbuf(cbp); splx(s); return; } else { putccdbuf(cbp->cb_mirror); /* fall through */ } } } } /* * use bio_caller1 to determine how big the original request was rather * then bio_bcount, because bio_bcount may have been truncated for EOF. * * XXX We check for an error, but we do not test the resid for an * aligned EOF condition. This may result in character & block * device access not recognizing EOF properly when read or written * sequentially, but will not effect filesystems. */ count = (long)cbp->cb_buf.bio_caller1; putccdbuf(cbp); /* * If all done, "interrupt". */ bp->bio_resid -= count; if (bp->bio_resid < 0) panic("ccdiodone: count"); if (bp->bio_resid == 0) ccdintr(&ccd_softc[unit], bp); splx(s); } static int ccdioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { int unit = ccdunit(dev); int i, j, lookedup = 0, error = 0; int part, pmask, s; struct ccd_softc *cs; struct ccd_ioctl *ccio = (struct ccd_ioctl *)data; struct ccddevice ccd; char **cpp; struct vnode **vpp; if (unit >= numccd) return (ENXIO); cs = &ccd_softc[unit]; bzero(&ccd, sizeof(ccd)); switch (cmd) { case CCDIOCSET: if (cs->sc_flags & CCDF_INITED) return (EBUSY); if ((flag & FWRITE) == 0) return (EBADF); if ((error = ccdlock(cs)) != 0) return (error); /* Fill in some important bits. */ ccd.ccd_unit = unit; ccd.ccd_interleave = ccio->ccio_ileave; if (ccd.ccd_interleave == 0 && ((ccio->ccio_flags & CCDF_MIRROR) || (ccio->ccio_flags & CCDF_PARITY))) { printf("ccd%d: disabling mirror/parity, interleave is 0\n", unit); ccio->ccio_flags &= ~(CCDF_MIRROR | CCDF_PARITY); } if ((ccio->ccio_flags & CCDF_MIRROR) && (ccio->ccio_flags & CCDF_PARITY)) { printf("ccd%d: can't specify both mirror and parity, using mirror\n", unit); ccio->ccio_flags &= ~CCDF_PARITY; } if ((ccio->ccio_flags & (CCDF_MIRROR | CCDF_PARITY)) && !(ccio->ccio_flags & CCDF_UNIFORM)) { printf("ccd%d: mirror/parity forces uniform flag\n", unit); ccio->ccio_flags |= CCDF_UNIFORM; } ccd.ccd_flags = ccio->ccio_flags & CCDF_USERMASK; /* * Allocate space for and copy in the array of * componet pathnames and device numbers. */ cpp = malloc(ccio->ccio_ndisks * sizeof(char *), M_DEVBUF, M_WAITOK); vpp = malloc(ccio->ccio_ndisks * sizeof(struct vnode *), M_DEVBUF, M_WAITOK); error = copyin((caddr_t)ccio->ccio_disks, (caddr_t)cpp, ccio->ccio_ndisks * sizeof(char **)); if (error) { free(vpp, M_DEVBUF); free(cpp, M_DEVBUF); ccdunlock(cs); return (error); } #ifdef DEBUG if (ccddebug & CCDB_INIT) for (i = 0; i < ccio->ccio_ndisks; ++i) printf("ccdioctl: component %d: 0x%x\n", i, cpp[i]); #endif for (i = 0; i < ccio->ccio_ndisks; ++i) { #ifdef DEBUG if (ccddebug & CCDB_INIT) printf("ccdioctl: lookedup = %d\n", lookedup); #endif if ((error = ccdlookup(cpp[i], p, &vpp[i])) != 0) { for (j = 0; j < lookedup; ++j) (void)vn_close(vpp[j], FREAD|FWRITE, p->p_ucred, p); free(vpp, M_DEVBUF); free(cpp, M_DEVBUF); ccdunlock(cs); return (error); } ++lookedup; } ccd.ccd_cpp = cpp; ccd.ccd_vpp = vpp; ccd.ccd_ndev = ccio->ccio_ndisks; /* * Initialize the ccd. Fills in the softc for us. */ if ((error = ccdinit(&ccd, cpp, p)) != 0) { for (j = 0; j < lookedup; ++j) (void)vn_close(vpp[j], FREAD|FWRITE, p->p_ucred, p); bzero(&ccd_softc[unit], sizeof(struct ccd_softc)); free(vpp, M_DEVBUF); free(cpp, M_DEVBUF); ccdunlock(cs); return (error); } /* * The ccd has been successfully initialized, so * we can place it into the array and read the disklabel. */ bcopy(&ccd, &ccddevs[unit], sizeof(ccd)); ccio->ccio_unit = unit; ccio->ccio_size = cs->sc_size; ccdgetdisklabel(dev); ccdunlock(cs); break; case CCDIOCCLR: if ((cs->sc_flags & CCDF_INITED) == 0) return (ENXIO); if ((flag & FWRITE) == 0) return (EBADF); if ((error = ccdlock(cs)) != 0) return (error); /* Don't unconfigure if any other partitions are open */ part = ccdpart(dev); pmask = (1 << part); if ((cs->sc_openmask & ~pmask)) { ccdunlock(cs); return (EBUSY); } /* * Free ccd_softc information and clear entry. */ /* Close the components and free their pathnames. */ for (i = 0; i < cs->sc_nccdisks; ++i) { /* * XXX: this close could potentially fail and * cause Bad Things. Maybe we need to force * the close to happen? */ #ifdef DEBUG if (ccddebug & CCDB_VNODE) vprint("CCDIOCCLR: vnode info", cs->sc_cinfo[i].ci_vp); #endif (void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE, p->p_ucred, p); free(cs->sc_cinfo[i].ci_path, M_DEVBUF); } /* Free interleave index. */ for (i = 0; cs->sc_itable[i].ii_ndisk; ++i) free(cs->sc_itable[i].ii_index, M_DEVBUF); /* Free component info and interleave table. */ free(cs->sc_cinfo, M_DEVBUF); free(cs->sc_itable, M_DEVBUF); cs->sc_flags &= ~CCDF_INITED; /* * Free ccddevice information and clear entry. */ free(ccddevs[unit].ccd_cpp, M_DEVBUF); free(ccddevs[unit].ccd_vpp, M_DEVBUF); ccd.ccd_dk = -1; bcopy(&ccd, &ccddevs[unit], sizeof(ccd)); /* * And remove the devstat entry. */ devstat_remove_entry(&cs->device_stats); /* This must be atomic. */ s = splhigh(); ccdunlock(cs); bzero(cs, sizeof(struct ccd_softc)); splx(s); break; case DIOCGDINFO: if ((cs->sc_flags & CCDF_INITED) == 0) return (ENXIO); *(struct disklabel *)data = cs->sc_label; break; case DIOCGPART: if ((cs->sc_flags & CCDF_INITED) == 0) return (ENXIO); ((struct partinfo *)data)->disklab = &cs->sc_label; ((struct partinfo *)data)->part = &cs->sc_label.d_partitions[ccdpart(dev)]; break; case DIOCWDINFO: case DIOCSDINFO: if ((cs->sc_flags & CCDF_INITED) == 0) return (ENXIO); if ((flag & FWRITE) == 0) return (EBADF); if ((error = ccdlock(cs)) != 0) return (error); cs->sc_flags |= CCDF_LABELLING; error = setdisklabel(&cs->sc_label, (struct disklabel *)data, 0); if (error == 0) { if (cmd == DIOCWDINFO) error = writedisklabel(CCDLABELDEV(dev), &cs->sc_label); } cs->sc_flags &= ~CCDF_LABELLING; ccdunlock(cs); if (error) return (error); break; case DIOCWLABEL: if ((cs->sc_flags & CCDF_INITED) == 0) return (ENXIO); if ((flag & FWRITE) == 0) return (EBADF); if (*(int *)data != 0) cs->sc_flags |= CCDF_WLABEL; else cs->sc_flags &= ~CCDF_WLABEL; break; default: return (ENOTTY); } return (0); } static int ccdsize(dev) dev_t dev; { struct ccd_softc *cs; int part, size; if (ccdopen(dev, 0, S_IFCHR, curproc)) return (-1); cs = &ccd_softc[ccdunit(dev)]; part = ccdpart(dev); if ((cs->sc_flags & CCDF_INITED) == 0) return (-1); if (cs->sc_label.d_partitions[part].p_fstype != FS_SWAP) size = -1; else size = cs->sc_label.d_partitions[part].p_size; if (ccdclose(dev, 0, S_IFCHR, curproc)) return (-1); return (size); } static int ccddump(dev) dev_t dev; { /* Not implemented. */ return ENXIO; } /* * Lookup the provided name in the filesystem. If the file exists, * is a valid block device, and isn't being used by anyone else, * set *vpp to the file's vnode. */ static int ccdlookup(path, p, vpp) char *path; struct proc *p; struct vnode **vpp; /* result */ { struct nameidata nd; struct vnode *vp; - int error; + int error, flags; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, path, p); - if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) { + flags = FREAD | FWRITE; + if ((error = vn_open(&nd, &flags, 0)) != 0) { #ifdef DEBUG if (ccddebug & CCDB_FOLLOW|CCDB_INIT) printf("ccdlookup: vn_open error = %d\n", error); #endif return (error); } vp = nd.ni_vp; if (vp->v_usecount > 1) { error = EBUSY; goto bad; } if (!vn_isdisk(vp, &error)) goto bad; #ifdef DEBUG if (ccddebug & CCDB_VNODE) vprint("ccdlookup: vnode info", vp); #endif VOP_UNLOCK(vp, 0, p); NDFREE(&nd, NDF_ONLY_PNBUF); *vpp = vp; return (0); bad: VOP_UNLOCK(vp, 0, p); NDFREE(&nd, NDF_ONLY_PNBUF); /* vn_close does vrele() for vp */ (void)vn_close(vp, FREAD|FWRITE, p->p_ucred, p); return (error); } /* * Read the disklabel from the ccd. If one is not present, fake one * up. */ static void ccdgetdisklabel(dev) dev_t dev; { int unit = ccdunit(dev); struct ccd_softc *cs = &ccd_softc[unit]; char *errstring; struct disklabel *lp = &cs->sc_label; struct ccdgeom *ccg = &cs->sc_geom; bzero(lp, sizeof(*lp)); lp->d_secperunit = cs->sc_size; lp->d_secsize = ccg->ccg_secsize; lp->d_nsectors = ccg->ccg_nsectors; lp->d_ntracks = ccg->ccg_ntracks; lp->d_ncylinders = ccg->ccg_ncylinders; lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; strncpy(lp->d_typename, "ccd", sizeof(lp->d_typename)); lp->d_type = DTYPE_CCD; strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); lp->d_rpm = 3600; lp->d_interleave = 1; lp->d_flags = 0; lp->d_partitions[RAW_PART].p_offset = 0; lp->d_partitions[RAW_PART].p_size = cs->sc_size; lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; lp->d_npartitions = RAW_PART + 1; lp->d_bbsize = BBSIZE; /* XXX */ lp->d_sbsize = SBSIZE; /* XXX */ lp->d_magic = DISKMAGIC; lp->d_magic2 = DISKMAGIC; lp->d_checksum = dkcksum(&cs->sc_label); /* * Call the generic disklabel extraction routine. */ errstring = readdisklabel(CCDLABELDEV(dev), &cs->sc_label); if (errstring != NULL) ccdmakedisklabel(cs); #ifdef DEBUG /* It's actually extremely common to have unlabeled ccds. */ if (ccddebug & CCDB_LABEL) if (errstring != NULL) printf("ccd%d: %s\n", unit, errstring); #endif } /* * Take care of things one might want to take care of in the event * that a disklabel isn't present. */ static void ccdmakedisklabel(cs) struct ccd_softc *cs; { struct disklabel *lp = &cs->sc_label; /* * For historical reasons, if there's no disklabel present * the raw partition must be marked FS_BSDFFS. */ lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS; strncpy(lp->d_packname, "default label", sizeof(lp->d_packname)); } /* * Wait interruptibly for an exclusive lock. * * XXX * Several drivers do this; it should be abstracted and made MP-safe. */ static int ccdlock(cs) struct ccd_softc *cs; { int error; while ((cs->sc_flags & CCDF_LOCKED) != 0) { cs->sc_flags |= CCDF_WANTED; if ((error = tsleep(cs, PRIBIO | PCATCH, "ccdlck", 0)) != 0) return (error); } cs->sc_flags |= CCDF_LOCKED; return (0); } /* * Unlock and wake up any waiters. */ static void ccdunlock(cs) struct ccd_softc *cs; { cs->sc_flags &= ~CCDF_LOCKED; if ((cs->sc_flags & CCDF_WANTED) != 0) { cs->sc_flags &= ~CCDF_WANTED; wakeup(cs); } } #ifdef DEBUG static void printiinfo(ii) struct ccdiinfo *ii; { int ix, i; for (ix = 0; ii->ii_ndisk; ix++, ii++) { printf(" itab[%d]: #dk %d sblk %d soff %d", ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff); for (i = 0; i < ii->ii_ndisk; i++) printf(" %d", ii->ii_index[i]); printf("\n"); } } #endif /* Local Variables: */ /* c-argdecl-indent: 8 */ /* c-continued-statement-offset: 8 */ /* c-indent-level: 8 */ /* End: */ Index: head/sys/dev/vn/vn.c =================================================================== --- head/sys/dev/vn/vn.c (revision 62549) +++ head/sys/dev/vn/vn.c (revision 62550) @@ -1,786 +1,786 @@ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: Utah Hdr: vn.c 1.13 94/04/02 * * from: @(#)vn.c 8.6 (Berkeley) 4/1/94 * $FreeBSD$ */ /* * Vnode disk driver. * * Block/character interface to a vnode. Allows one to treat a file * as a disk (e.g. build a filesystem in it, mount it, etc.). * * NOTE 1: This uses the VOP_BMAP/VOP_STRATEGY interface to the vnode * instead of a simple VOP_RDWR. We do this to avoid distorting the * local buffer cache. * * NOTE 2: There is a security issue involved with this driver. * Once mounted all access to the contents of the "mapped" file via * the special file is controlled by the permissions on the special * file, the protection of the mapped file is ignored (effectively, * by using root credentials in all transactions). * * NOTE 3: Doesn't interact with leases, should it? */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static d_ioctl_t vnioctl; static d_open_t vnopen; static d_close_t vnclose; static d_psize_t vnsize; static d_strategy_t vnstrategy; #define CDEV_MAJOR 43 #define BDEV_MAJOR 15 #define VN_BSIZE_BEST 8192 /* * cdevsw * D_DISK we want to look like a disk * D_CANFREE We support BIO_DELETE */ static struct cdevsw vn_cdevsw = { /* open */ vnopen, /* close */ vnclose, /* read */ physread, /* write */ physwrite, /* ioctl */ vnioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ vnstrategy, /* name */ "vn", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ vnsize, /* flags */ D_DISK|D_CANFREE, /* bmaj */ BDEV_MAJOR }; #define getvnbuf() \ ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK)) #define putvnbuf(bp) \ free((caddr_t)(bp), M_DEVBUF) struct vn_softc { int sc_unit; int sc_flags; /* flags */ int sc_size; /* size of vn, sc_secsize scale */ int sc_secsize; /* sector size */ struct diskslices *sc_slices; struct vnode *sc_vp; /* vnode if not NULL */ vm_object_t sc_object; /* backing object if not NULL */ struct ucred *sc_cred; /* credentials */ int sc_maxactive; /* max # of active requests */ u_long sc_options; /* options */ SLIST_ENTRY(vn_softc) sc_list; }; static SLIST_HEAD(, vn_softc) vn_list; /* sc_flags */ #define VNF_INITED 0x01 #define VNF_READONLY 0x02 static u_long vn_options; #define IFOPT(vn,opt) if (((vn)->sc_options|vn_options) & (opt)) #define TESTOPT(vn,opt) (((vn)->sc_options|vn_options) & (opt)) static int vnsetcred (struct vn_softc *vn, struct ucred *cred); static void vnclear (struct vn_softc *vn); static int vn_modevent (module_t, int, void *); static int vniocattach_file (struct vn_softc *, struct vn_ioctl *, dev_t dev, int flag, struct proc *p); static int vniocattach_swap (struct vn_softc *, struct vn_ioctl *, dev_t dev, int flag, struct proc *p); static int vnclose(dev_t dev, int flags, int mode, struct proc *p) { struct vn_softc *vn = dev->si_drv1; IFOPT(vn, VN_LABELS) if (vn->sc_slices != NULL) dsclose(dev, mode, vn->sc_slices); return (0); } static struct vn_softc * vnfindvn(dev_t dev) { int unit; struct vn_softc *vn; unit = dkunit(dev); vn = dev->si_drv1; if (!vn) { SLIST_FOREACH(vn, &vn_list, sc_list) { if (vn->sc_unit == unit) { dev->si_drv1 = vn; break; } } } if (!vn) { vn = malloc(sizeof *vn, M_DEVBUF, M_WAITOK); if (!vn) return (NULL); bzero(vn, sizeof *vn); vn->sc_unit = unit; dev->si_drv1 = vn; make_dev(&vn_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0640, "vn%d", unit); SLIST_INSERT_HEAD(&vn_list, vn, sc_list); } return (vn); } static int vnopen(dev_t dev, int flags, int mode, struct proc *p) { struct vn_softc *vn; /* * Locate preexisting device */ if ((vn = dev->si_drv1) == NULL) vn = vnfindvn(dev); /* * Update si_bsize fields for device. This data will be overriden by * the slice/parition code for vn accesses through partitions, and * used directly if you open the 'whole disk' device. * * si_bsize_best must be reinitialized in case VN has been * reconfigured, plus make it at least VN_BSIZE_BEST for efficiency. */ dev->si_bsize_phys = vn->sc_secsize; dev->si_bsize_best = vn->sc_secsize; if (dev->si_bsize_best < VN_BSIZE_BEST) dev->si_bsize_best = VN_BSIZE_BEST; if ((flags & FWRITE) && (vn->sc_flags & VNF_READONLY)) return (EACCES); IFOPT(vn, VN_FOLLOW) printf("vnopen(%s, 0x%x, 0x%x, %p)\n", devtoname(dev), flags, mode, (void *)p); /* * Initialize label */ IFOPT(vn, VN_LABELS) { if (vn->sc_flags & VNF_INITED) { struct disklabel label; /* Build label for whole disk. */ bzero(&label, sizeof label); label.d_secsize = vn->sc_secsize; label.d_nsectors = 32; label.d_ntracks = 64 / (vn->sc_secsize / DEV_BSIZE); label.d_secpercyl = label.d_nsectors * label.d_ntracks; label.d_ncylinders = vn->sc_size / label.d_secpercyl; label.d_secperunit = vn->sc_size; label.d_partitions[RAW_PART].p_size = vn->sc_size; return (dsopen(dev, mode, 0, &vn->sc_slices, &label)); } if (dkslice(dev) != WHOLE_DISK_SLICE || dkpart(dev) != RAW_PART || mode != S_IFCHR) { return (ENXIO); } } return(0); } /* * vnstrategy: * * Run strategy routine for VN device. We use VOP_READ/VOP_WRITE calls * for vnode-backed vn's, and the new vm_pager_strategy() call for * vm_object-backed vn's. * * NOTE: bp->b_blkno is DEV_BSIZE'd. We must generate bp->b_pblkno for * our uio or vn_pager_strategy() call that is vn->sc_secsize'd */ static void vnstrategy(struct bio *bp) { int unit; struct vn_softc *vn; int error; int isvplocked = 0; unit = dkunit(bp->bio_dev); vn = bp->bio_dev->si_drv1; if (vn == NULL) vn = vnfindvn(bp->bio_dev); IFOPT(vn, VN_DEBUG) printf("vnstrategy(%p): unit %d\n", bp, unit); if ((vn->sc_flags & VNF_INITED) == 0) { bp->bio_error = ENXIO; bp->bio_flags |= BIO_ERROR; biodone(bp); return; } bp->bio_resid = bp->bio_bcount; IFOPT(vn, VN_LABELS) { if (vn->sc_slices != NULL && dscheck(bp, vn->sc_slices) <= 0) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; biodone(bp); return; } } else { int pbn; /* in sc_secsize chunks */ long sz; /* in sc_secsize chunks */ /* * Check for required alignment. Transfers must be a valid * multiple of the sector size. */ if (bp->bio_bcount % vn->sc_secsize != 0 || bp->bio_blkno % (vn->sc_secsize / DEV_BSIZE) != 0) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; biodone(bp); return; } pbn = bp->bio_blkno / (vn->sc_secsize / DEV_BSIZE); sz = howmany(bp->bio_bcount, vn->sc_secsize); /* * If out of bounds return an error. If at the EOF point, * simply read or write less. */ if (pbn < 0 || pbn >= vn->sc_size) { if (pbn != vn->sc_size) { bp->bio_error = EINVAL; /* XXX bp->b_flags |= B_INVAL; */ bp->bio_flags |= BIO_ERROR; } biodone(bp); return; } /* * If the request crosses EOF, truncate the request. */ if (pbn + sz > vn->sc_size) { bp->bio_bcount = (vn->sc_size - pbn) * vn->sc_secsize; bp->bio_resid = bp->bio_bcount; } bp->bio_pblkno = pbn; } if (vn->sc_vp && (bp->bio_cmd == BIO_DELETE)) { /* * Not handled for vnode-backed element yet. */ biodone(bp); } else if (vn->sc_vp) { /* * VNODE I/O * * If an error occurs, we set BIO_ERROR but we do not set * B_INVAL because (for a write anyway), the buffer is * still valid. */ struct uio auio; struct iovec aiov; bzero(&auio, sizeof(auio)); aiov.iov_base = bp->bio_data; aiov.iov_len = bp->bio_bcount; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = (vm_ooffset_t)bp->bio_pblkno * vn->sc_secsize; auio.uio_segflg = UIO_SYSSPACE; if(bp->bio_cmd == BIO_READ) auio.uio_rw = UIO_READ; else auio.uio_rw = UIO_WRITE; auio.uio_resid = bp->bio_bcount; auio.uio_procp = curproc; if (!VOP_ISLOCKED(vn->sc_vp, NULL)) { isvplocked = 1; vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curproc); } if(bp->bio_cmd == BIO_READ) error = VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred); else error = VOP_WRITE(vn->sc_vp, &auio, 0, vn->sc_cred); if (isvplocked) { VOP_UNLOCK(vn->sc_vp, 0, curproc); isvplocked = 0; } bp->bio_resid = auio.uio_resid; if (error) { bp->bio_error = error; bp->bio_flags |= BIO_ERROR; } biodone(bp); } else if (vn->sc_object) { /* * OBJT_SWAP I/O * * ( handles read, write, freebuf ) * * Note: if we pre-reserved swap, BIO_DELETE is disabled */ #if 0 KASSERT((bp->b_bufsize & (vn->sc_secsize - 1)) == 0, ("vnstrategy: buffer %p too small for physio", bp)); #endif if ((bp->bio_cmd == BIO_DELETE) && TESTOPT(vn, VN_RESERVE)) { biodone(bp); } else { vm_pager_strategy(vn->sc_object, bp); } } else { bp->bio_flags |= BIO_ERROR; bp->bio_error = EINVAL; biodone(bp); } } /* ARGSUSED */ static int vnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct vn_softc *vn; struct vn_ioctl *vio; int error; u_long *f; vn = dev->si_drv1; IFOPT(vn,VN_FOLLOW) printf("vnioctl(%s, 0x%lx, %p, 0x%x, %p): unit %d\n", devtoname(dev), cmd, (void *)data, flag, (void *)p, dkunit(dev)); switch (cmd) { case VNIOCATTACH: case VNIOCDETACH: case VNIOCGSET: case VNIOCGCLEAR: case VNIOCUSET: case VNIOCUCLEAR: goto vn_specific; } IFOPT(vn,VN_LABELS) { if (vn->sc_slices != NULL) { error = dsioctl(dev, cmd, data, flag, &vn->sc_slices); if (error != ENOIOCTL) return (error); } if (dkslice(dev) != WHOLE_DISK_SLICE || dkpart(dev) != RAW_PART) return (ENOTTY); } vn_specific: error = suser(p); if (error) return (error); vio = (struct vn_ioctl *)data; f = (u_long*)data; switch (cmd) { case VNIOCATTACH: if (vn->sc_flags & VNF_INITED) return(EBUSY); if (vio->vn_file == NULL) error = vniocattach_swap(vn, vio, dev, flag, p); else error = vniocattach_file(vn, vio, dev, flag, p); break; case VNIOCDETACH: if ((vn->sc_flags & VNF_INITED) == 0) return(ENXIO); /* * XXX handle i/o in progress. Return EBUSY, or wait, or * flush the i/o. * XXX handle multiple opens of the device. Return EBUSY, * or revoke the fd's. * How are these problems handled for removable and failing * hardware devices? (Hint: They are not) */ vnclear(vn); IFOPT(vn, VN_FOLLOW) printf("vnioctl: CLRed\n"); break; case VNIOCGSET: vn_options |= *f; *f = vn_options; break; case VNIOCGCLEAR: vn_options &= ~(*f); *f = vn_options; break; case VNIOCUSET: vn->sc_options |= *f; *f = vn->sc_options; break; case VNIOCUCLEAR: vn->sc_options &= ~(*f); *f = vn->sc_options; break; default: error = ENOTTY; break; } return(error); } /* * vniocattach_file: * * Attach a file to a VN partition. Return the size in the vn_size * field. */ static int vniocattach_file(vn, vio, dev, flag, p) struct vn_softc *vn; struct vn_ioctl *vio; dev_t dev; int flag; struct proc *p; { struct vattr vattr; struct nameidata nd; int error, flags; flags = FREAD|FWRITE; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p); - error = vn_open(&nd, flags, 0); + error = vn_open(&nd, &flags, 0); if (error) { if (error != EACCES && error != EPERM && error != EROFS) return (error); flags &= ~FWRITE; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p); - error = vn_open(&nd, flags, 0); + error = vn_open(&nd, &flags, 0); if (error) return (error); } NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp->v_type != VREG || (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p))) { VOP_UNLOCK(nd.ni_vp, 0, p); (void) vn_close(nd.ni_vp, flags, p->p_ucred, p); return (error ? error : EINVAL); } VOP_UNLOCK(nd.ni_vp, 0, p); vn->sc_secsize = DEV_BSIZE; vn->sc_vp = nd.ni_vp; /* * If the size is specified, override the file attributes. Note that * the vn_size argument is in PAGE_SIZE sized blocks. */ if (vio->vn_size) vn->sc_size = (quad_t)vio->vn_size * PAGE_SIZE / vn->sc_secsize; else vn->sc_size = vattr.va_size / vn->sc_secsize; error = vnsetcred(vn, p->p_ucred); if (error) { (void) vn_close(nd.ni_vp, flags, p->p_ucred, p); return(error); } vn->sc_flags |= VNF_INITED; if (flags == FREAD) vn->sc_flags |= VNF_READONLY; IFOPT(vn, VN_LABELS) { /* * Reopen so that `ds' knows which devices are open. * If this is the first VNIOCSET, then we've * guaranteed that the device is the cdev and that * no other slices or labels are open. Otherwise, * we rely on VNIOCCLR not being abused. */ error = vnopen(dev, flag, S_IFCHR, p); if (error) vnclear(vn); } IFOPT(vn, VN_FOLLOW) printf("vnioctl: SET vp %p size %x blks\n", vn->sc_vp, vn->sc_size); return(0); } /* * vniocattach_swap: * * Attach swap backing store to a VN partition of the size specified * in vn_size. */ static int vniocattach_swap(vn, vio, dev, flag, p) struct vn_softc *vn; struct vn_ioctl *vio; dev_t dev; int flag; struct proc *p; { int error; /* * Range check. Disallow negative sizes or any size less then the * size of a page. Then round to a page. */ if (vio->vn_size <= 0) return(EDOM); /* * Allocate an OBJT_SWAP object. * * sc_secsize is PAGE_SIZE'd * * vio->vn_size is in PAGE_SIZE'd chunks. * sc_size must be in PAGE_SIZE'd chunks. * Note the truncation. */ vn->sc_secsize = PAGE_SIZE; vn->sc_size = vio->vn_size; vn->sc_object = vm_pager_allocate(OBJT_SWAP, NULL, vn->sc_secsize * (vm_ooffset_t)vio->vn_size, VM_PROT_DEFAULT, 0); IFOPT(vn, VN_RESERVE) { if (swap_pager_reserve(vn->sc_object, 0, vn->sc_size) < 0) { vm_pager_deallocate(vn->sc_object); vn->sc_object = NULL; return(EDOM); } } vn->sc_flags |= VNF_INITED; error = vnsetcred(vn, p->p_ucred); if (error == 0) { IFOPT(vn, VN_LABELS) { /* * Reopen so that `ds' knows which devices are open. * If this is the first VNIOCSET, then we've * guaranteed that the device is the cdev and that * no other slices or labels are open. Otherwise, * we rely on VNIOCCLR not being abused. */ error = vnopen(dev, flag, S_IFCHR, p); } } if (error == 0) { IFOPT(vn, VN_FOLLOW) { printf("vnioctl: SET vp %p size %x\n", vn->sc_vp, vn->sc_size); } } if (error) vnclear(vn); return(error); } /* * Duplicate the current processes' credentials. Since we are called only * as the result of a SET ioctl and only root can do that, any future access * to this "disk" is essentially as root. Note that credentials may change * if some other uid can write directly to the mapped file (NFS). */ int vnsetcred(struct vn_softc *vn, struct ucred *cred) { char *tmpbuf; int error = 0; /* * Set credits in our softc */ if (vn->sc_cred) crfree(vn->sc_cred); vn->sc_cred = crdup(cred); /* * Horrible kludge to establish credentials for NFS XXX. */ if (vn->sc_vp) { struct uio auio; struct iovec aiov; tmpbuf = malloc(vn->sc_secsize, M_TEMP, M_WAITOK); bzero(&auio, sizeof(auio)); aiov.iov_base = tmpbuf; aiov.iov_len = vn->sc_secsize; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_SYSSPACE; auio.uio_resid = aiov.iov_len; vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curproc); error = VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred); VOP_UNLOCK(vn->sc_vp, 0, curproc); free(tmpbuf, M_TEMP); } return (error); } void vnclear(struct vn_softc *vn) { struct proc *p = curproc; /* XXX */ IFOPT(vn, VN_FOLLOW) printf("vnclear(%p): vp=%p\n", vn, vn->sc_vp); if (vn->sc_slices != NULL) dsgone(&vn->sc_slices); vn->sc_flags &= ~VNF_INITED; if (vn->sc_vp != NULL) { (void)vn_close(vn->sc_vp, vn->sc_flags & VNF_READONLY ? FREAD : (FREAD|FWRITE), vn->sc_cred, p); vn->sc_vp = NULL; } vn->sc_flags &= ~VNF_READONLY; if (vn->sc_cred) { crfree(vn->sc_cred); vn->sc_cred = NULL; } if (vn->sc_object != NULL) { vm_pager_deallocate(vn->sc_object); vn->sc_object = NULL; } vn->sc_size = 0; } static int vnsize(dev_t dev) { struct vn_softc *vn; vn = dev->si_drv1; if (!vn) return(-1); if ((vn->sc_flags & VNF_INITED) == 0) return(-1); return(vn->sc_size); } static int vn_modevent(module_t mod, int type, void *data) { struct vn_softc *vn; switch (type) { case MOD_LOAD: cdevsw_add(&vn_cdevsw); break; case MOD_UNLOAD: /* fall through */ case MOD_SHUTDOWN: for (;;) { vn = SLIST_FIRST(&vn_list); if (!vn) break; SLIST_REMOVE_HEAD(&vn_list, sc_list); if (vn->sc_flags & VNF_INITED) vnclear(vn); free(vn, M_DEVBUF); } break; default: break; } return 0; } DEV_MODULE(vn, vn_modevent, 0); Index: head/sys/geom/geom_ccd.c =================================================================== --- head/sys/geom/geom_ccd.c (revision 62549) +++ head/sys/geom/geom_ccd.c (revision 62550) @@ -1,1712 +1,1713 @@ /* $FreeBSD$ */ /* $NetBSD: ccd.c,v 1.22 1995/12/08 19:13:26 thorpej Exp $ */ /* * Copyright (c) 1995 Jason R. Thorpe. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project * by Jason R. Thorpe. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: Utah $Hdr: cd.c 1.6 90/11/28$ * * @(#)cd.c 8.2 (Berkeley) 11/16/93 */ /* * "Concatenated" disk driver. * * Dynamic configuration and disklabel support by: * Jason R. Thorpe * Numerical Aerodynamic Simulation Facility * Mail Stop 258-6 * NASA Ames Research Center * Moffett Field, CA 94035 */ #include "ccd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CCDDEBUG) && !defined(DEBUG) #define DEBUG #endif #ifdef DEBUG #define CCDB_FOLLOW 0x01 #define CCDB_INIT 0x02 #define CCDB_IO 0x04 #define CCDB_LABEL 0x08 #define CCDB_VNODE 0x10 static int ccddebug = CCDB_FOLLOW | CCDB_INIT | CCDB_IO | CCDB_LABEL | CCDB_VNODE; SYSCTL_INT(_debug, OID_AUTO, ccddebug, CTLFLAG_RW, &ccddebug, 0, ""); #undef DEBUG #endif #define ccdunit(x) dkunit(x) #define ccdpart(x) dkpart(x) /* This is how mirroring works (only writes are special): When initiating a write, ccdbuffer() returns two "struct ccdbuf *"s linked together by the cb_mirror field. "cb_pflags & CCDPF_MIRROR_DONE" is set to 0 on both of them. When a component returns to ccdiodone(), it checks if "cb_pflags & CCDPF_MIRROR_DONE" is set or not. If not, it sets the partner's flag and returns. If it is, it means its partner has already returned, so it will go to the regular cleanup. */ struct ccdbuf { struct bio cb_buf; /* new I/O buf */ struct bio *cb_obp; /* ptr. to original I/O buf */ struct ccdbuf *cb_freenext; /* free list link */ int cb_unit; /* target unit */ int cb_comp; /* target component */ int cb_pflags; /* mirror/parity status flag */ struct ccdbuf *cb_mirror; /* mirror counterpart */ }; /* bits in cb_pflags */ #define CCDPF_MIRROR_DONE 1 /* if set, mirror counterpart is done */ #define CCDLABELDEV(dev) \ (makedev(major((dev)), dkmakeminor(ccdunit((dev)), 0, RAW_PART))) static d_open_t ccdopen; static d_close_t ccdclose; static d_strategy_t ccdstrategy; static d_ioctl_t ccdioctl; static d_dump_t ccddump; static d_psize_t ccdsize; #define NCCDFREEHIWAT 16 #define CDEV_MAJOR 74 #define BDEV_MAJOR 21 static struct cdevsw ccd_cdevsw = { /* open */ ccdopen, /* close */ ccdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ ccdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ ccdstrategy, /* name */ "ccd", /* maj */ CDEV_MAJOR, /* dump */ ccddump, /* psize */ ccdsize, /* flags */ D_DISK, /* bmaj */ BDEV_MAJOR }; /* called during module initialization */ static void ccdattach __P((void)); static int ccd_modevent __P((module_t, int, void *)); /* called by biodone() at interrupt time */ static void ccdiodone __P((struct bio *bp)); static void ccdstart __P((struct ccd_softc *, struct bio *)); static void ccdinterleave __P((struct ccd_softc *, int)); static void ccdintr __P((struct ccd_softc *, struct bio *)); static int ccdinit __P((struct ccddevice *, char **, struct proc *)); static int ccdlookup __P((char *, struct proc *p, struct vnode **)); static void ccdbuffer __P((struct ccdbuf **ret, struct ccd_softc *, struct bio *, daddr_t, caddr_t, long)); static void ccdgetdisklabel __P((dev_t)); static void ccdmakedisklabel __P((struct ccd_softc *)); static int ccdlock __P((struct ccd_softc *)); static void ccdunlock __P((struct ccd_softc *)); #ifdef DEBUG static void printiinfo __P((struct ccdiinfo *)); #endif /* Non-private for the benefit of libkvm. */ struct ccd_softc *ccd_softc; struct ccddevice *ccddevs; struct ccdbuf *ccdfreebufs; static int numccdfreebufs; static int numccd = 0; /* * getccdbuf() - Allocate and zero a ccd buffer. * * This routine is called at splbio(). */ static __inline struct ccdbuf * getccdbuf(struct ccdbuf *cpy) { struct ccdbuf *cbp; /* * Allocate from freelist or malloc as necessary */ if ((cbp = ccdfreebufs) != NULL) { ccdfreebufs = cbp->cb_freenext; --numccdfreebufs; } else { cbp = malloc(sizeof(struct ccdbuf), M_DEVBUF, M_WAITOK); } /* * Used by mirroring code */ if (cpy) bcopy(cpy, cbp, sizeof(struct ccdbuf)); else bzero(cbp, sizeof(struct ccdbuf)); /* * independant struct bio initialization */ return(cbp); } /* * putccdbuf() - Free a ccd buffer. * * This routine is called at splbio(). */ static __inline void putccdbuf(struct ccdbuf *cbp) { if (numccdfreebufs < NCCDFREEHIWAT) { cbp->cb_freenext = ccdfreebufs; ccdfreebufs = cbp; ++numccdfreebufs; } else { free((caddr_t)cbp, M_DEVBUF); } } /* * Number of blocks to untouched in front of a component partition. * This is to avoid violating its disklabel area when it starts at the * beginning of the slice. */ #if !defined(CCD_OFFSET) #define CCD_OFFSET 16 #endif /* * Called by main() during pseudo-device attachment. All we need * to do is allocate enough space for devices to be configured later, and * add devsw entries. */ static void ccdattach() { int i; int num = NCCD; if (num > 1) printf("ccd0-%d: Concatenated disk drivers\n", num-1); else printf("ccd0: Concatenated disk driver\n"); ccd_softc = (struct ccd_softc *)malloc(num * sizeof(struct ccd_softc), M_DEVBUF, M_NOWAIT); ccddevs = (struct ccddevice *)malloc(num * sizeof(struct ccddevice), M_DEVBUF, M_NOWAIT); if ((ccd_softc == NULL) || (ccddevs == NULL)) { printf("WARNING: no memory for concatenated disks\n"); if (ccd_softc != NULL) free(ccd_softc, M_DEVBUF); if (ccddevs != NULL) free(ccddevs, M_DEVBUF); return; } numccd = num; bzero(ccd_softc, num * sizeof(struct ccd_softc)); bzero(ccddevs, num * sizeof(struct ccddevice)); cdevsw_add(&ccd_cdevsw); /* XXX: is this necessary? */ for (i = 0; i < numccd; ++i) ccddevs[i].ccd_dk = -1; } static int ccd_modevent(mod, type, data) module_t mod; int type; void *data; { int error = 0; switch (type) { case MOD_LOAD: ccdattach(); break; case MOD_UNLOAD: printf("ccd0: Unload not supported!\n"); error = EOPNOTSUPP; break; default: /* MOD_SHUTDOWN etc */ break; } return (error); } DEV_MODULE(ccd, ccd_modevent, NULL); static int ccdinit(ccd, cpaths, p) struct ccddevice *ccd; char **cpaths; struct proc *p; { struct ccd_softc *cs = &ccd_softc[ccd->ccd_unit]; struct ccdcinfo *ci = NULL; /* XXX */ size_t size; int ix; struct vnode *vp; size_t minsize; int maxsecsize; struct partinfo dpart; struct ccdgeom *ccg = &cs->sc_geom; char tmppath[MAXPATHLEN]; int error = 0; #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccdinit: unit %d\n", ccd->ccd_unit); #endif cs->sc_size = 0; cs->sc_ileave = ccd->ccd_interleave; cs->sc_nccdisks = ccd->ccd_ndev; /* Allocate space for the component info. */ cs->sc_cinfo = malloc(cs->sc_nccdisks * sizeof(struct ccdcinfo), M_DEVBUF, M_WAITOK); /* * Verify that each component piece exists and record * relevant information about it. */ maxsecsize = 0; minsize = 0; for (ix = 0; ix < cs->sc_nccdisks; ix++) { vp = ccd->ccd_vpp[ix]; ci = &cs->sc_cinfo[ix]; ci->ci_vp = vp; /* * Copy in the pathname of the component. */ bzero(tmppath, sizeof(tmppath)); /* sanity */ if ((error = copyinstr(cpaths[ix], tmppath, MAXPATHLEN, &ci->ci_pathlen)) != 0) { #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccd%d: can't copy path, error = %d\n", ccd->ccd_unit, error); #endif goto fail; } ci->ci_path = malloc(ci->ci_pathlen, M_DEVBUF, M_WAITOK); bcopy(tmppath, ci->ci_path, ci->ci_pathlen); ci->ci_dev = vn_todev(vp); /* * Get partition information for the component. */ if ((error = VOP_IOCTL(vp, DIOCGPART, (caddr_t)&dpart, FREAD, p->p_ucred, p)) != 0) { #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccd%d: %s: ioctl failed, error = %d\n", ccd->ccd_unit, ci->ci_path, error); #endif goto fail; } if (dpart.part->p_fstype == FS_BSDFFS) { maxsecsize = ((dpart.disklab->d_secsize > maxsecsize) ? dpart.disklab->d_secsize : maxsecsize); size = dpart.part->p_size - CCD_OFFSET; } else { #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccd%d: %s: incorrect partition type\n", ccd->ccd_unit, ci->ci_path); #endif error = EFTYPE; goto fail; } /* * Calculate the size, truncating to an interleave * boundary if necessary. */ if (cs->sc_ileave > 1) size -= size % cs->sc_ileave; if (size == 0) { #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccd%d: %s: size == 0\n", ccd->ccd_unit, ci->ci_path); #endif error = ENODEV; goto fail; } if (minsize == 0 || size < minsize) minsize = size; ci->ci_size = size; cs->sc_size += size; } /* * Don't allow the interleave to be smaller than * the biggest component sector. */ if ((cs->sc_ileave > 0) && (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) { #ifdef DEBUG if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) printf("ccd%d: interleave must be at least %d\n", ccd->ccd_unit, (maxsecsize / DEV_BSIZE)); #endif error = EINVAL; goto fail; } /* * If uniform interleave is desired set all sizes to that of * the smallest component. This will guarentee that a single * interleave table is generated. * * Lost space must be taken into account when calculating the * overall size. Half the space is lost when CCDF_MIRROR is * specified. One disk is lost when CCDF_PARITY is specified. */ if (ccd->ccd_flags & CCDF_UNIFORM) { for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) { ci->ci_size = minsize; } if (ccd->ccd_flags & CCDF_MIRROR) { /* * Check to see if an even number of components * have been specified. The interleave must also * be non-zero in order for us to be able to * guarentee the topology. */ if (cs->sc_nccdisks % 2) { printf("ccd%d: mirroring requires an even number of disks\n", ccd->ccd_unit ); error = EINVAL; goto fail; } if (cs->sc_ileave == 0) { printf("ccd%d: an interleave must be specified when mirroring\n", ccd->ccd_unit); error = EINVAL; goto fail; } cs->sc_size = (cs->sc_nccdisks/2) * minsize; } else if (ccd->ccd_flags & CCDF_PARITY) { cs->sc_size = (cs->sc_nccdisks-1) * minsize; } else { if (cs->sc_ileave == 0) { printf("ccd%d: an interleave must be specified when using parity\n", ccd->ccd_unit); error = EINVAL; goto fail; } cs->sc_size = cs->sc_nccdisks * minsize; } } /* * Construct the interleave table. */ ccdinterleave(cs, ccd->ccd_unit); /* * Create pseudo-geometry based on 1MB cylinders. It's * pretty close. */ ccg->ccg_secsize = maxsecsize; ccg->ccg_ntracks = 1; ccg->ccg_nsectors = 1024 * 1024 / ccg->ccg_secsize; ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors; /* * Add an devstat entry for this device. */ devstat_add_entry(&cs->device_stats, "ccd", ccd->ccd_unit, ccg->ccg_secsize, DEVSTAT_ALL_SUPPORTED, DEVSTAT_TYPE_STORARRAY |DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_ARRAY); cs->sc_flags |= CCDF_INITED; cs->sc_cflags = ccd->ccd_flags; /* So we can find out later... */ cs->sc_unit = ccd->ccd_unit; return (0); fail: while (ci > cs->sc_cinfo) { ci--; free(ci->ci_path, M_DEVBUF); } free(cs->sc_cinfo, M_DEVBUF); return (error); } static void ccdinterleave(cs, unit) struct ccd_softc *cs; int unit; { struct ccdcinfo *ci, *smallci; struct ccdiinfo *ii; daddr_t bn, lbn; int ix; u_long size; #ifdef DEBUG if (ccddebug & CCDB_INIT) printf("ccdinterleave(%x): ileave %d\n", cs, cs->sc_ileave); #endif /* * Allocate an interleave table. The worst case occurs when each * of N disks is of a different size, resulting in N interleave * tables. * * Chances are this is too big, but we don't care. */ size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo); cs->sc_itable = (struct ccdiinfo *)malloc(size, M_DEVBUF, M_WAITOK); bzero((caddr_t)cs->sc_itable, size); /* * Trivial case: no interleave (actually interleave of disk size). * Each table entry represents a single component in its entirety. * * An interleave of 0 may not be used with a mirror or parity setup. */ if (cs->sc_ileave == 0) { bn = 0; ii = cs->sc_itable; for (ix = 0; ix < cs->sc_nccdisks; ix++) { /* Allocate space for ii_index. */ ii->ii_index = malloc(sizeof(int), M_DEVBUF, M_WAITOK); ii->ii_ndisk = 1; ii->ii_startblk = bn; ii->ii_startoff = 0; ii->ii_index[0] = ix; bn += cs->sc_cinfo[ix].ci_size; ii++; } ii->ii_ndisk = 0; #ifdef DEBUG if (ccddebug & CCDB_INIT) printiinfo(cs->sc_itable); #endif return; } /* * The following isn't fast or pretty; it doesn't have to be. */ size = 0; bn = lbn = 0; for (ii = cs->sc_itable; ; ii++) { /* * Allocate space for ii_index. We might allocate more then * we use. */ ii->ii_index = malloc((sizeof(int) * cs->sc_nccdisks), M_DEVBUF, M_WAITOK); /* * Locate the smallest of the remaining components */ smallci = NULL; for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) { if (ci->ci_size > size && (smallci == NULL || ci->ci_size < smallci->ci_size)) { smallci = ci; } } /* * Nobody left, all done */ if (smallci == NULL) { ii->ii_ndisk = 0; break; } /* * Record starting logical block using an sc_ileave blocksize. */ ii->ii_startblk = bn / cs->sc_ileave; /* * Record starting comopnent block using an sc_ileave * blocksize. This value is relative to the beginning of * a component disk. */ ii->ii_startoff = lbn; /* * Determine how many disks take part in this interleave * and record their indices. */ ix = 0; for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) { if (ci->ci_size >= smallci->ci_size) { ii->ii_index[ix++] = ci - cs->sc_cinfo; } } ii->ii_ndisk = ix; bn += ix * (smallci->ci_size - size); lbn = smallci->ci_size / cs->sc_ileave; size = smallci->ci_size; } #ifdef DEBUG if (ccddebug & CCDB_INIT) printiinfo(cs->sc_itable); #endif } /* ARGSUSED */ static int ccdopen(dev, flags, fmt, p) dev_t dev; int flags, fmt; struct proc *p; { int unit = ccdunit(dev); struct ccd_softc *cs; struct disklabel *lp; int error = 0, part, pmask; #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdopen(%x, %x)\n", dev, flags); #endif if (unit >= numccd) return (ENXIO); cs = &ccd_softc[unit]; if ((error = ccdlock(cs)) != 0) return (error); lp = &cs->sc_label; part = ccdpart(dev); pmask = (1 << part); /* * If we're initialized, check to see if there are any other * open partitions. If not, then it's safe to update * the in-core disklabel. */ if ((cs->sc_flags & CCDF_INITED) && (cs->sc_openmask == 0)) ccdgetdisklabel(dev); /* Check that the partition exists. */ if (part != RAW_PART && ((part >= lp->d_npartitions) || (lp->d_partitions[part].p_fstype == FS_UNUSED))) { error = ENXIO; goto done; } cs->sc_openmask |= pmask; done: ccdunlock(cs); return (0); } /* ARGSUSED */ static int ccdclose(dev, flags, fmt, p) dev_t dev; int flags, fmt; struct proc *p; { int unit = ccdunit(dev); struct ccd_softc *cs; int error = 0, part; #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdclose(%x, %x)\n", dev, flags); #endif if (unit >= numccd) return (ENXIO); cs = &ccd_softc[unit]; if ((error = ccdlock(cs)) != 0) return (error); part = ccdpart(dev); /* ...that much closer to allowing unconfiguration... */ cs->sc_openmask &= ~(1 << part); ccdunlock(cs); return (0); } static void ccdstrategy(bp) struct bio *bp; { int unit = ccdunit(bp->bio_dev); struct ccd_softc *cs = &ccd_softc[unit]; int s; int wlabel; struct disklabel *lp; #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdstrategy(%x): unit %d\n", bp, unit); #endif if ((cs->sc_flags & CCDF_INITED) == 0) { bp->bio_error = ENXIO; bp->bio_flags |= BIO_ERROR; goto done; } /* If it's a nil transfer, wake up the top half now. */ if (bp->bio_bcount == 0) goto done; lp = &cs->sc_label; /* * Do bounds checking and adjust transfer. If there's an * error, the bounds check will flag that for us. */ wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING); if (ccdpart(bp->bio_dev) != RAW_PART) { if (bounds_check_with_label(bp, lp, wlabel) <= 0) goto done; } else { int pbn; /* in sc_secsize chunks */ long sz; /* in sc_secsize chunks */ pbn = bp->bio_blkno / (cs->sc_geom.ccg_secsize / DEV_BSIZE); sz = howmany(bp->bio_bcount, cs->sc_geom.ccg_secsize); /* * If out of bounds return an error. If at the EOF point, * simply read or write less. */ if (pbn < 0 || pbn >= cs->sc_size) { bp->bio_resid = bp->bio_bcount; if (pbn != cs->sc_size) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; } goto done; } /* * If the request crosses EOF, truncate the request. */ if (pbn + sz > cs->sc_size) { bp->bio_bcount = (cs->sc_size - pbn) * cs->sc_geom.ccg_secsize; } } bp->bio_resid = bp->bio_bcount; /* * "Start" the unit. */ s = splbio(); ccdstart(cs, bp); splx(s); return; done: biodone(bp); } static void ccdstart(cs, bp) struct ccd_softc *cs; struct bio *bp; { long bcount, rcount; struct ccdbuf *cbp[4]; /* XXX! : 2 reads and 2 writes for RAID 4/5 */ caddr_t addr; daddr_t bn; struct partition *pp; #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdstart(%x, %x)\n", cs, bp); #endif /* Record the transaction start */ devstat_start_transaction(&cs->device_stats); /* * Translate the partition-relative block number to an absolute. */ bn = bp->bio_blkno; if (ccdpart(bp->bio_dev) != RAW_PART) { pp = &cs->sc_label.d_partitions[ccdpart(bp->bio_dev)]; bn += pp->p_offset; } /* * Allocate component buffers and fire off the requests */ addr = bp->bio_data; for (bcount = bp->bio_bcount; bcount > 0; bcount -= rcount) { ccdbuffer(cbp, cs, bp, bn, addr, bcount); rcount = cbp[0]->cb_buf.bio_bcount; if (cs->sc_cflags & CCDF_MIRROR) { /* * Mirroring. Writes go to both disks, reads are * taken from whichever disk seems most appropriate. * * We attempt to localize reads to the disk whos arm * is nearest the read request. We ignore seeks due * to writes when making this determination and we * also try to avoid hogging. */ if (cbp[0]->cb_buf.bio_cmd == BIO_WRITE) { BIO_STRATEGY(&cbp[0]->cb_buf, 0); BIO_STRATEGY(&cbp[1]->cb_buf, 0); } else { int pick = cs->sc_pick; daddr_t range = cs->sc_size / 16; if (bn < cs->sc_blk[pick] - range || bn > cs->sc_blk[pick] + range ) { cs->sc_pick = pick = 1 - pick; } cs->sc_blk[pick] = bn + btodb(rcount); BIO_STRATEGY(&cbp[pick]->cb_buf, 0); } } else { /* * Not mirroring */ BIO_STRATEGY(&cbp[0]->cb_buf, 0); } bn += btodb(rcount); addr += rcount; } } /* * Build a component buffer header. */ static void ccdbuffer(cb, cs, bp, bn, addr, bcount) struct ccdbuf **cb; struct ccd_softc *cs; struct bio *bp; daddr_t bn; caddr_t addr; long bcount; { struct ccdcinfo *ci, *ci2 = NULL; /* XXX */ struct ccdbuf *cbp; daddr_t cbn, cboff; off_t cbc; #ifdef DEBUG if (ccddebug & CCDB_IO) printf("ccdbuffer(%x, %x, %d, %x, %d)\n", cs, bp, bn, addr, bcount); #endif /* * Determine which component bn falls in. */ cbn = bn; cboff = 0; if (cs->sc_ileave == 0) { /* * Serially concatenated and neither a mirror nor a parity * config. This is a special case. */ daddr_t sblk; sblk = 0; for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++) sblk += ci->ci_size; cbn -= sblk; } else { struct ccdiinfo *ii; int ccdisk, off; /* * Calculate cbn, the logical superblock (sc_ileave chunks), * and cboff, a normal block offset (DEV_BSIZE chunks) relative * to cbn. */ cboff = cbn % cs->sc_ileave; /* DEV_BSIZE gran */ cbn = cbn / cs->sc_ileave; /* DEV_BSIZE * ileave gran */ /* * Figure out which interleave table to use. */ for (ii = cs->sc_itable; ii->ii_ndisk; ii++) { if (ii->ii_startblk > cbn) break; } ii--; /* * off is the logical superblock relative to the beginning * of this interleave block. */ off = cbn - ii->ii_startblk; /* * We must calculate which disk component to use (ccdisk), * and recalculate cbn to be the superblock relative to * the beginning of the component. This is typically done by * adding 'off' and ii->ii_startoff together. However, 'off' * must typically be divided by the number of components in * this interleave array to be properly convert it from a * CCD-relative logical superblock number to a * component-relative superblock number. */ if (ii->ii_ndisk == 1) { /* * When we have just one disk, it can't be a mirror * or a parity config. */ ccdisk = ii->ii_index[0]; cbn = ii->ii_startoff + off; } else { if (cs->sc_cflags & CCDF_MIRROR) { /* * We have forced a uniform mapping, resulting * in a single interleave array. We double * up on the first half of the available * components and our mirror is in the second * half. This only works with a single * interleave array because doubling up * doubles the number of sectors, so there * cannot be another interleave array because * the next interleave array's calculations * would be off. */ int ndisk2 = ii->ii_ndisk / 2; ccdisk = ii->ii_index[off % ndisk2]; cbn = ii->ii_startoff + off / ndisk2; ci2 = &cs->sc_cinfo[ccdisk + ndisk2]; } else if (cs->sc_cflags & CCDF_PARITY) { /* * XXX not implemented yet */ int ndisk2 = ii->ii_ndisk - 1; ccdisk = ii->ii_index[off % ndisk2]; cbn = ii->ii_startoff + off / ndisk2; if (cbn % ii->ii_ndisk <= ccdisk) ccdisk++; } else { ccdisk = ii->ii_index[off % ii->ii_ndisk]; cbn = ii->ii_startoff + off / ii->ii_ndisk; } } ci = &cs->sc_cinfo[ccdisk]; /* * Convert cbn from a superblock to a normal block so it * can be used to calculate (along with cboff) the normal * block index into this particular disk. */ cbn *= cs->sc_ileave; } /* * Fill in the component buf structure. */ cbp = getccdbuf(NULL); cbp->cb_buf.bio_cmd = bp->bio_cmd; cbp->cb_buf.bio_done = ccdiodone; cbp->cb_buf.bio_dev = ci->ci_dev; /* XXX */ cbp->cb_buf.bio_blkno = cbn + cboff + CCD_OFFSET; cbp->cb_buf.bio_offset = dbtob(cbn + cboff + CCD_OFFSET); cbp->cb_buf.bio_data = addr; if (cs->sc_ileave == 0) cbc = dbtob((off_t)(ci->ci_size - cbn)); else cbc = dbtob((off_t)(cs->sc_ileave - cboff)); cbp->cb_buf.bio_bcount = (cbc < bcount) ? cbc : bcount; cbp->cb_buf.bio_caller1 = (void*)cbp->cb_buf.bio_bcount; /* * context for ccdiodone */ cbp->cb_obp = bp; cbp->cb_unit = cs - ccd_softc; cbp->cb_comp = ci - cs->sc_cinfo; #ifdef DEBUG if (ccddebug & CCDB_IO) printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n", ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->cb_buf.bio_blkno, cbp->cb_buf.bio_data, cbp->cb_buf.bio_bcount); #endif cb[0] = cbp; /* * Note: both I/O's setup when reading from mirror, but only one * will be executed. */ if (cs->sc_cflags & CCDF_MIRROR) { /* mirror, setup second I/O */ cbp = getccdbuf(cb[0]); cbp->cb_buf.bio_dev = ci2->ci_dev; cbp->cb_comp = ci2 - cs->sc_cinfo; cb[1] = cbp; /* link together the ccdbuf's and clear "mirror done" flag */ cb[0]->cb_mirror = cb[1]; cb[1]->cb_mirror = cb[0]; cb[0]->cb_pflags &= ~CCDPF_MIRROR_DONE; cb[1]->cb_pflags &= ~CCDPF_MIRROR_DONE; } } static void ccdintr(cs, bp) struct ccd_softc *cs; struct bio *bp; { #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdintr(%x, %x)\n", cs, bp); #endif /* * Request is done for better or worse, wakeup the top half. */ if (bp->bio_flags & BIO_ERROR) bp->bio_resid = bp->bio_bcount; devstat_end_transaction_bio(&cs->device_stats, bp); biodone(bp); } /* * Called at interrupt time. * Mark the component as done and if all components are done, * take a ccd interrupt. */ static void ccdiodone(ibp) struct bio *ibp; { struct ccdbuf *cbp = (struct ccdbuf *)ibp; struct bio *bp = cbp->cb_obp; int unit = cbp->cb_unit; int count, s; s = splbio(); #ifdef DEBUG if (ccddebug & CCDB_FOLLOW) printf("ccdiodone(%x)\n", cbp); if (ccddebug & CCDB_IO) { printf("ccdiodone: bp %x bcount %d resid %d\n", bp, bp->bio_bcount, bp->bio_resid); printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n", cbp->cb_buf.bio_dev, cbp->cb_comp, cbp, cbp->cb_buf.bio_blkno, cbp->cb_buf.bio_data, cbp->cb_buf.bio_bcount); } #endif /* * If an error occured, report it. If this is a mirrored * configuration and the first of two possible reads, do not * set the error in the bp yet because the second read may * succeed. */ if (cbp->cb_buf.bio_flags & BIO_ERROR) { const char *msg = ""; if ((ccd_softc[unit].sc_cflags & CCDF_MIRROR) && (cbp->cb_buf.bio_cmd == BIO_READ) && (cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) { /* * We will try our read on the other disk down * below, also reverse the default pick so if we * are doing a scan we do not keep hitting the * bad disk first. */ struct ccd_softc *cs = &ccd_softc[unit]; msg = ", trying other disk"; cs->sc_pick = 1 - cs->sc_pick; cs->sc_blk[cs->sc_pick] = bp->bio_blkno; } else { bp->bio_flags |= BIO_ERROR; bp->bio_error = cbp->cb_buf.bio_error ? cbp->cb_buf.bio_error : EIO; } printf("ccd%d: error %d on component %d block %d (ccd block %d)%s\n", unit, bp->bio_error, cbp->cb_comp, (int)cbp->cb_buf.bio_blkno, bp->bio_blkno, msg); } /* * Process mirror. If we are writing, I/O has been initiated on both * buffers and we fall through only after both are finished. * * If we are reading only one I/O is initiated at a time. If an * error occurs we initiate the second I/O and return, otherwise * we free the second I/O without initiating it. */ if (ccd_softc[unit].sc_cflags & CCDF_MIRROR) { if (cbp->cb_buf.bio_cmd == BIO_WRITE) { /* * When writing, handshake with the second buffer * to determine when both are done. If both are not * done, return here. */ if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) { cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE; putccdbuf(cbp); splx(s); return; } } else { /* * When reading, either dispose of the second buffer * or initiate I/O on the second buffer if an error * occured with this one. */ if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) { if (cbp->cb_buf.bio_flags & BIO_ERROR) { cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE; BIO_STRATEGY(&cbp->cb_mirror->cb_buf, 0); putccdbuf(cbp); splx(s); return; } else { putccdbuf(cbp->cb_mirror); /* fall through */ } } } } /* * use bio_caller1 to determine how big the original request was rather * then bio_bcount, because bio_bcount may have been truncated for EOF. * * XXX We check for an error, but we do not test the resid for an * aligned EOF condition. This may result in character & block * device access not recognizing EOF properly when read or written * sequentially, but will not effect filesystems. */ count = (long)cbp->cb_buf.bio_caller1; putccdbuf(cbp); /* * If all done, "interrupt". */ bp->bio_resid -= count; if (bp->bio_resid < 0) panic("ccdiodone: count"); if (bp->bio_resid == 0) ccdintr(&ccd_softc[unit], bp); splx(s); } static int ccdioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { int unit = ccdunit(dev); int i, j, lookedup = 0, error = 0; int part, pmask, s; struct ccd_softc *cs; struct ccd_ioctl *ccio = (struct ccd_ioctl *)data; struct ccddevice ccd; char **cpp; struct vnode **vpp; if (unit >= numccd) return (ENXIO); cs = &ccd_softc[unit]; bzero(&ccd, sizeof(ccd)); switch (cmd) { case CCDIOCSET: if (cs->sc_flags & CCDF_INITED) return (EBUSY); if ((flag & FWRITE) == 0) return (EBADF); if ((error = ccdlock(cs)) != 0) return (error); /* Fill in some important bits. */ ccd.ccd_unit = unit; ccd.ccd_interleave = ccio->ccio_ileave; if (ccd.ccd_interleave == 0 && ((ccio->ccio_flags & CCDF_MIRROR) || (ccio->ccio_flags & CCDF_PARITY))) { printf("ccd%d: disabling mirror/parity, interleave is 0\n", unit); ccio->ccio_flags &= ~(CCDF_MIRROR | CCDF_PARITY); } if ((ccio->ccio_flags & CCDF_MIRROR) && (ccio->ccio_flags & CCDF_PARITY)) { printf("ccd%d: can't specify both mirror and parity, using mirror\n", unit); ccio->ccio_flags &= ~CCDF_PARITY; } if ((ccio->ccio_flags & (CCDF_MIRROR | CCDF_PARITY)) && !(ccio->ccio_flags & CCDF_UNIFORM)) { printf("ccd%d: mirror/parity forces uniform flag\n", unit); ccio->ccio_flags |= CCDF_UNIFORM; } ccd.ccd_flags = ccio->ccio_flags & CCDF_USERMASK; /* * Allocate space for and copy in the array of * componet pathnames and device numbers. */ cpp = malloc(ccio->ccio_ndisks * sizeof(char *), M_DEVBUF, M_WAITOK); vpp = malloc(ccio->ccio_ndisks * sizeof(struct vnode *), M_DEVBUF, M_WAITOK); error = copyin((caddr_t)ccio->ccio_disks, (caddr_t)cpp, ccio->ccio_ndisks * sizeof(char **)); if (error) { free(vpp, M_DEVBUF); free(cpp, M_DEVBUF); ccdunlock(cs); return (error); } #ifdef DEBUG if (ccddebug & CCDB_INIT) for (i = 0; i < ccio->ccio_ndisks; ++i) printf("ccdioctl: component %d: 0x%x\n", i, cpp[i]); #endif for (i = 0; i < ccio->ccio_ndisks; ++i) { #ifdef DEBUG if (ccddebug & CCDB_INIT) printf("ccdioctl: lookedup = %d\n", lookedup); #endif if ((error = ccdlookup(cpp[i], p, &vpp[i])) != 0) { for (j = 0; j < lookedup; ++j) (void)vn_close(vpp[j], FREAD|FWRITE, p->p_ucred, p); free(vpp, M_DEVBUF); free(cpp, M_DEVBUF); ccdunlock(cs); return (error); } ++lookedup; } ccd.ccd_cpp = cpp; ccd.ccd_vpp = vpp; ccd.ccd_ndev = ccio->ccio_ndisks; /* * Initialize the ccd. Fills in the softc for us. */ if ((error = ccdinit(&ccd, cpp, p)) != 0) { for (j = 0; j < lookedup; ++j) (void)vn_close(vpp[j], FREAD|FWRITE, p->p_ucred, p); bzero(&ccd_softc[unit], sizeof(struct ccd_softc)); free(vpp, M_DEVBUF); free(cpp, M_DEVBUF); ccdunlock(cs); return (error); } /* * The ccd has been successfully initialized, so * we can place it into the array and read the disklabel. */ bcopy(&ccd, &ccddevs[unit], sizeof(ccd)); ccio->ccio_unit = unit; ccio->ccio_size = cs->sc_size; ccdgetdisklabel(dev); ccdunlock(cs); break; case CCDIOCCLR: if ((cs->sc_flags & CCDF_INITED) == 0) return (ENXIO); if ((flag & FWRITE) == 0) return (EBADF); if ((error = ccdlock(cs)) != 0) return (error); /* Don't unconfigure if any other partitions are open */ part = ccdpart(dev); pmask = (1 << part); if ((cs->sc_openmask & ~pmask)) { ccdunlock(cs); return (EBUSY); } /* * Free ccd_softc information and clear entry. */ /* Close the components and free their pathnames. */ for (i = 0; i < cs->sc_nccdisks; ++i) { /* * XXX: this close could potentially fail and * cause Bad Things. Maybe we need to force * the close to happen? */ #ifdef DEBUG if (ccddebug & CCDB_VNODE) vprint("CCDIOCCLR: vnode info", cs->sc_cinfo[i].ci_vp); #endif (void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE, p->p_ucred, p); free(cs->sc_cinfo[i].ci_path, M_DEVBUF); } /* Free interleave index. */ for (i = 0; cs->sc_itable[i].ii_ndisk; ++i) free(cs->sc_itable[i].ii_index, M_DEVBUF); /* Free component info and interleave table. */ free(cs->sc_cinfo, M_DEVBUF); free(cs->sc_itable, M_DEVBUF); cs->sc_flags &= ~CCDF_INITED; /* * Free ccddevice information and clear entry. */ free(ccddevs[unit].ccd_cpp, M_DEVBUF); free(ccddevs[unit].ccd_vpp, M_DEVBUF); ccd.ccd_dk = -1; bcopy(&ccd, &ccddevs[unit], sizeof(ccd)); /* * And remove the devstat entry. */ devstat_remove_entry(&cs->device_stats); /* This must be atomic. */ s = splhigh(); ccdunlock(cs); bzero(cs, sizeof(struct ccd_softc)); splx(s); break; case DIOCGDINFO: if ((cs->sc_flags & CCDF_INITED) == 0) return (ENXIO); *(struct disklabel *)data = cs->sc_label; break; case DIOCGPART: if ((cs->sc_flags & CCDF_INITED) == 0) return (ENXIO); ((struct partinfo *)data)->disklab = &cs->sc_label; ((struct partinfo *)data)->part = &cs->sc_label.d_partitions[ccdpart(dev)]; break; case DIOCWDINFO: case DIOCSDINFO: if ((cs->sc_flags & CCDF_INITED) == 0) return (ENXIO); if ((flag & FWRITE) == 0) return (EBADF); if ((error = ccdlock(cs)) != 0) return (error); cs->sc_flags |= CCDF_LABELLING; error = setdisklabel(&cs->sc_label, (struct disklabel *)data, 0); if (error == 0) { if (cmd == DIOCWDINFO) error = writedisklabel(CCDLABELDEV(dev), &cs->sc_label); } cs->sc_flags &= ~CCDF_LABELLING; ccdunlock(cs); if (error) return (error); break; case DIOCWLABEL: if ((cs->sc_flags & CCDF_INITED) == 0) return (ENXIO); if ((flag & FWRITE) == 0) return (EBADF); if (*(int *)data != 0) cs->sc_flags |= CCDF_WLABEL; else cs->sc_flags &= ~CCDF_WLABEL; break; default: return (ENOTTY); } return (0); } static int ccdsize(dev) dev_t dev; { struct ccd_softc *cs; int part, size; if (ccdopen(dev, 0, S_IFCHR, curproc)) return (-1); cs = &ccd_softc[ccdunit(dev)]; part = ccdpart(dev); if ((cs->sc_flags & CCDF_INITED) == 0) return (-1); if (cs->sc_label.d_partitions[part].p_fstype != FS_SWAP) size = -1; else size = cs->sc_label.d_partitions[part].p_size; if (ccdclose(dev, 0, S_IFCHR, curproc)) return (-1); return (size); } static int ccddump(dev) dev_t dev; { /* Not implemented. */ return ENXIO; } /* * Lookup the provided name in the filesystem. If the file exists, * is a valid block device, and isn't being used by anyone else, * set *vpp to the file's vnode. */ static int ccdlookup(path, p, vpp) char *path; struct proc *p; struct vnode **vpp; /* result */ { struct nameidata nd; struct vnode *vp; - int error; + int error, flags; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, path, p); - if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) { + flags = FREAD | FWRITE; + if ((error = vn_open(&nd, &flags, 0)) != 0) { #ifdef DEBUG if (ccddebug & CCDB_FOLLOW|CCDB_INIT) printf("ccdlookup: vn_open error = %d\n", error); #endif return (error); } vp = nd.ni_vp; if (vp->v_usecount > 1) { error = EBUSY; goto bad; } if (!vn_isdisk(vp, &error)) goto bad; #ifdef DEBUG if (ccddebug & CCDB_VNODE) vprint("ccdlookup: vnode info", vp); #endif VOP_UNLOCK(vp, 0, p); NDFREE(&nd, NDF_ONLY_PNBUF); *vpp = vp; return (0); bad: VOP_UNLOCK(vp, 0, p); NDFREE(&nd, NDF_ONLY_PNBUF); /* vn_close does vrele() for vp */ (void)vn_close(vp, FREAD|FWRITE, p->p_ucred, p); return (error); } /* * Read the disklabel from the ccd. If one is not present, fake one * up. */ static void ccdgetdisklabel(dev) dev_t dev; { int unit = ccdunit(dev); struct ccd_softc *cs = &ccd_softc[unit]; char *errstring; struct disklabel *lp = &cs->sc_label; struct ccdgeom *ccg = &cs->sc_geom; bzero(lp, sizeof(*lp)); lp->d_secperunit = cs->sc_size; lp->d_secsize = ccg->ccg_secsize; lp->d_nsectors = ccg->ccg_nsectors; lp->d_ntracks = ccg->ccg_ntracks; lp->d_ncylinders = ccg->ccg_ncylinders; lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; strncpy(lp->d_typename, "ccd", sizeof(lp->d_typename)); lp->d_type = DTYPE_CCD; strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); lp->d_rpm = 3600; lp->d_interleave = 1; lp->d_flags = 0; lp->d_partitions[RAW_PART].p_offset = 0; lp->d_partitions[RAW_PART].p_size = cs->sc_size; lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; lp->d_npartitions = RAW_PART + 1; lp->d_bbsize = BBSIZE; /* XXX */ lp->d_sbsize = SBSIZE; /* XXX */ lp->d_magic = DISKMAGIC; lp->d_magic2 = DISKMAGIC; lp->d_checksum = dkcksum(&cs->sc_label); /* * Call the generic disklabel extraction routine. */ errstring = readdisklabel(CCDLABELDEV(dev), &cs->sc_label); if (errstring != NULL) ccdmakedisklabel(cs); #ifdef DEBUG /* It's actually extremely common to have unlabeled ccds. */ if (ccddebug & CCDB_LABEL) if (errstring != NULL) printf("ccd%d: %s\n", unit, errstring); #endif } /* * Take care of things one might want to take care of in the event * that a disklabel isn't present. */ static void ccdmakedisklabel(cs) struct ccd_softc *cs; { struct disklabel *lp = &cs->sc_label; /* * For historical reasons, if there's no disklabel present * the raw partition must be marked FS_BSDFFS. */ lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS; strncpy(lp->d_packname, "default label", sizeof(lp->d_packname)); } /* * Wait interruptibly for an exclusive lock. * * XXX * Several drivers do this; it should be abstracted and made MP-safe. */ static int ccdlock(cs) struct ccd_softc *cs; { int error; while ((cs->sc_flags & CCDF_LOCKED) != 0) { cs->sc_flags |= CCDF_WANTED; if ((error = tsleep(cs, PRIBIO | PCATCH, "ccdlck", 0)) != 0) return (error); } cs->sc_flags |= CCDF_LOCKED; return (0); } /* * Unlock and wake up any waiters. */ static void ccdunlock(cs) struct ccd_softc *cs; { cs->sc_flags &= ~CCDF_LOCKED; if ((cs->sc_flags & CCDF_WANTED) != 0) { cs->sc_flags &= ~CCDF_WANTED; wakeup(cs); } } #ifdef DEBUG static void printiinfo(ii) struct ccdiinfo *ii; { int ix, i; for (ix = 0; ii->ii_ndisk; ix++, ii++) { printf(" itab[%d]: #dk %d sblk %d soff %d", ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff); for (i = 0; i < ii->ii_ndisk; i++) printf(" %d", ii->ii_index[i]); printf("\n"); } } #endif /* Local Variables: */ /* c-argdecl-indent: 8 */ /* c-continued-statement-offset: 8 */ /* c-indent-level: 8 */ /* End: */ Index: head/sys/kern/kern_acct.c =================================================================== --- head/sys/kern/kern_acct.c (revision 62549) +++ head/sys/kern/kern_acct.c (revision 62550) @@ -1,332 +1,333 @@ /*- * Copyright (c) 1994 Christopher G. Demetriou * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kern_acct.c 8.1 (Berkeley) 6/14/93 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The routines implemented in this file are described in: * Leffler, et al.: The Design and Implementation of the 4.3BSD * UNIX Operating System (Addison Welley, 1989) * on pages 62-63. * * Arguably, to simplify accounting operations, this mechanism should * be replaced by one in which an accounting log file (similar to /dev/klog) * is read by a user process, etc. However, that has its own problems. */ /* * Internal accounting functions. * The former's operation is described in Leffler, et al., and the latter * was provided by UCB with the 4.4BSD-Lite release */ static comp_t encode_comp_t __P((u_long, u_long)); static void acctwatch __P((void *)); /* * Accounting callout handle used for periodic scheduling of * acctwatch. */ static struct callout_handle acctwatch_handle = CALLOUT_HANDLE_INITIALIZER(&acctwatch_handle); /* * Accounting vnode pointer, and saved vnode pointer. */ static struct vnode *acctp; static struct vnode *savacctp; /* * Values associated with enabling and disabling accounting */ static int acctsuspend = 2; /* stop accounting when < 2% free space left */ SYSCTL_INT(_kern, OID_AUTO, acct_suspend, CTLFLAG_RW, &acctsuspend, 0, "percentage of free disk space below which accounting stops"); static int acctresume = 4; /* resume when free space risen to > 4% */ SYSCTL_INT(_kern, OID_AUTO, acct_resume, CTLFLAG_RW, &acctresume, 0, "percentage of free disk space above which accounting resumes"); static int acctchkfreq = 15; /* frequency (in seconds) to check space */ SYSCTL_INT(_kern, OID_AUTO, acct_chkfreq, CTLFLAG_RW, &acctchkfreq, 0, "frequency for checking the free space"); /* * Accounting system call. Written based on the specification and * previous implementation done by Mark Tinguely. */ int acct(a1, uap) struct proc *a1; struct acct_args /* { syscallarg(char *) path; } */ *uap; { struct proc *p = curproc; /* XXX */ struct nameidata nd; - int error; + int error, flags; /* Make sure that the caller is root. */ error = suser(p); if (error) return (error); /* * If accounting is to be started to a file, open that file for * writing and make sure it's a 'normal'. */ if (SCARG(uap, path) != NULL) { NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); - error = vn_open(&nd, FWRITE, 0); + flags = FWRITE; + error = vn_open(&nd, &flags, 0); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); VOP_UNLOCK(nd.ni_vp, 0, p); if (nd.ni_vp->v_type != VREG) { vn_close(nd.ni_vp, FWRITE, p->p_ucred, p); return (EACCES); } } /* * If accounting was previously enabled, kill the old space-watcher, * close the file, and (if no new file was specified, leave). */ if (acctp != NULLVP || savacctp != NULLVP) { untimeout(acctwatch, NULL, acctwatch_handle); error = vn_close((acctp != NULLVP ? acctp : savacctp), FWRITE, p->p_ucred, p); acctp = savacctp = NULLVP; } if (SCARG(uap, path) == NULL) return (error); /* * Save the new accounting file vnode, and schedule the new * free space watcher. */ acctp = nd.ni_vp; acctwatch(NULL); return (error); } /* * Write out process accounting information, on process exit. * Data to be written out is specified in Leffler, et al. * and are enumerated below. (They're also noted in the system * "acct.h" header file.) */ int acct_process(p) struct proc *p; { struct acct acct; struct rusage *r; struct timeval ut, st, tmp; int t; struct vnode *vp; /* If accounting isn't enabled, don't bother */ vp = acctp; if (vp == NULLVP) return (0); /* * Get process accounting information. */ /* (1) The name of the command that ran */ bcopy(p->p_comm, acct.ac_comm, sizeof acct.ac_comm); /* (2) The amount of user and system time that was used */ calcru(p, &ut, &st, NULL); acct.ac_utime = encode_comp_t(ut.tv_sec, ut.tv_usec); acct.ac_stime = encode_comp_t(st.tv_sec, st.tv_usec); /* (3) The elapsed time the commmand ran (and its starting time) */ acct.ac_btime = p->p_stats->p_start.tv_sec; microtime(&tmp); timevalsub(&tmp, &p->p_stats->p_start); acct.ac_etime = encode_comp_t(tmp.tv_sec, tmp.tv_usec); /* (4) The average amount of memory used */ r = &p->p_stats->p_ru; tmp = ut; timevaladd(&tmp, &st); t = tmp.tv_sec * hz + tmp.tv_usec / tick; if (t) acct.ac_mem = (r->ru_ixrss + r->ru_idrss + r->ru_isrss) / t; else acct.ac_mem = 0; /* (5) The number of disk I/O operations done */ acct.ac_io = encode_comp_t(r->ru_inblock + r->ru_oublock, 0); /* (6) The UID and GID of the process */ acct.ac_uid = p->p_cred->p_ruid; acct.ac_gid = p->p_cred->p_rgid; /* (7) The terminal from which the process was started */ if ((p->p_flag & P_CONTROLT) && p->p_pgrp->pg_session->s_ttyp) acct.ac_tty = dev2udev(p->p_pgrp->pg_session->s_ttyp->t_dev); else acct.ac_tty = NOUDEV; /* (8) The boolean flags that tell how the process terminated, etc. */ acct.ac_flag = p->p_acflag; /* * Eliminate any file size rlimit. */ if (p->p_limit->p_refcnt > 1 && (p->p_limit->p_lflags & PL_SHAREMOD) == 0) { p->p_limit->p_refcnt--; p->p_limit = limcopy(p->p_limit); } p->p_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; /* * Write the accounting information to the file. */ VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); return (vn_rdwr(UIO_WRITE, vp, (caddr_t)&acct, sizeof (acct), (off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT, p->p_ucred, (int *)0, p)); } /* * Encode_comp_t converts from ticks in seconds and microseconds * to ticks in 1/AHZ seconds. The encoding is described in * Leffler, et al., on page 63. */ #define MANTSIZE 13 /* 13 bit mantissa. */ #define EXPSIZE 3 /* Base 8 (3 bit) exponent. */ #define MAXFRACT ((1 << MANTSIZE) - 1) /* Maximum fractional value. */ static comp_t encode_comp_t(s, us) u_long s, us; { int exp, rnd; exp = 0; rnd = 0; s *= AHZ; s += us / (1000000 / AHZ); /* Maximize precision. */ while (s > MAXFRACT) { rnd = s & (1 << (EXPSIZE - 1)); /* Round up? */ s >>= EXPSIZE; /* Base 8 exponent == 3 bit shift. */ exp++; } /* If we need to round up, do it (and handle overflow correctly). */ if (rnd && (++s > MAXFRACT)) { s >>= EXPSIZE; exp++; } /* Clean it up and polish it off. */ exp <<= MANTSIZE; /* Shift the exponent into place */ exp += s; /* and add on the mantissa. */ return (exp); } /* * Periodically check the file system to see if accounting * should be turned on or off. Beware the case where the vnode * has been vgone()'d out from underneath us, e.g. when the file * system containing the accounting file has been forcibly unmounted. */ /* ARGSUSED */ static void acctwatch(a) void *a; { struct statfs sb; if (savacctp != NULLVP) { if (savacctp->v_type == VBAD) { (void) vn_close(savacctp, FWRITE, NOCRED, NULL); savacctp = NULLVP; return; } (void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0); if (sb.f_bavail > acctresume * sb.f_blocks / 100) { acctp = savacctp; savacctp = NULLVP; log(LOG_NOTICE, "Accounting resumed\n"); } } else { if (acctp == NULLVP) return; if (acctp->v_type == VBAD) { (void) vn_close(acctp, FWRITE, NOCRED, NULL); acctp = NULLVP; return; } (void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0); if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) { savacctp = acctp; acctp = NULLVP; log(LOG_NOTICE, "Accounting suspended\n"); } } acctwatch_handle = timeout(acctwatch, NULL, acctchkfreq * hz); } Index: head/sys/kern/kern_ktrace.c =================================================================== --- head/sys/kern/kern_ktrace.c (revision 62549) +++ head/sys/kern/kern_ktrace.c (revision 62550) @@ -1,534 +1,535 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kern_ktrace.c 8.2 (Berkeley) 9/23/93 * $FreeBSD$ */ #include "opt_ktrace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_KTRACE, "KTRACE", "KTRACE"); #ifdef KTRACE static struct ktr_header *ktrgetheader __P((int type)); static void ktrwrite __P((struct vnode *, struct ktr_header *, struct uio *)); static int ktrcanset __P((struct proc *,struct proc *)); static int ktrsetchildren __P((struct proc *,struct proc *,int,int,struct vnode *)); static int ktrops __P((struct proc *,struct proc *,int,int,struct vnode *)); static struct ktr_header * ktrgetheader(type) int type; { register struct ktr_header *kth; struct proc *p = curproc; /* XXX */ MALLOC(kth, struct ktr_header *, sizeof (struct ktr_header), M_KTRACE, M_WAITOK); kth->ktr_type = type; microtime(&kth->ktr_time); kth->ktr_pid = p->p_pid; bcopy(p->p_comm, kth->ktr_comm, MAXCOMLEN + 1); return (kth); } void ktrsyscall(vp, code, narg, args) struct vnode *vp; int code, narg; register_t args[]; { struct ktr_header *kth; struct ktr_syscall *ktp; register int len = offsetof(struct ktr_syscall, ktr_args) + (narg * sizeof(register_t)); struct proc *p = curproc; /* XXX */ register_t *argp; int i; p->p_traceflag |= KTRFAC_ACTIVE; kth = ktrgetheader(KTR_SYSCALL); MALLOC(ktp, struct ktr_syscall *, len, M_KTRACE, M_WAITOK); ktp->ktr_code = code; ktp->ktr_narg = narg; argp = &ktp->ktr_args[0]; for (i = 0; i < narg; i++) *argp++ = args[i]; kth->ktr_buf = (caddr_t)ktp; kth->ktr_len = len; ktrwrite(vp, kth, NULL); FREE(ktp, M_KTRACE); FREE(kth, M_KTRACE); p->p_traceflag &= ~KTRFAC_ACTIVE; } void ktrsysret(vp, code, error, retval) struct vnode *vp; int code, error; register_t retval; { struct ktr_header *kth; struct ktr_sysret ktp; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; kth = ktrgetheader(KTR_SYSRET); ktp.ktr_code = code; ktp.ktr_error = error; ktp.ktr_retval = retval; /* what about val2 ? */ kth->ktr_buf = (caddr_t)&ktp; kth->ktr_len = sizeof(struct ktr_sysret); ktrwrite(vp, kth, NULL); FREE(kth, M_KTRACE); p->p_traceflag &= ~KTRFAC_ACTIVE; } void ktrnamei(vp, path) struct vnode *vp; char *path; { struct ktr_header *kth; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; kth = ktrgetheader(KTR_NAMEI); kth->ktr_len = strlen(path); kth->ktr_buf = path; ktrwrite(vp, kth, NULL); FREE(kth, M_KTRACE); p->p_traceflag &= ~KTRFAC_ACTIVE; } void ktrgenio(vp, fd, rw, uio, error) struct vnode *vp; int fd; enum uio_rw rw; struct uio *uio; int error; { struct ktr_header *kth; struct ktr_genio ktg; struct proc *p = curproc; /* XXX */ if (error) return; p->p_traceflag |= KTRFAC_ACTIVE; kth = ktrgetheader(KTR_GENIO); ktg.ktr_fd = fd; ktg.ktr_rw = rw; kth->ktr_buf = (caddr_t)&ktg; kth->ktr_len = sizeof(struct ktr_genio); uio->uio_offset = 0; uio->uio_rw = UIO_READ; ktrwrite(vp, kth, uio); FREE(kth, M_KTRACE); p->p_traceflag &= ~KTRFAC_ACTIVE; } void ktrpsig(vp, sig, action, mask, code) struct vnode *vp; int sig; sig_t action; sigset_t *mask; int code; { struct ktr_header *kth; struct ktr_psig kp; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; kth = ktrgetheader(KTR_PSIG); kp.signo = (char)sig; kp.action = action; kp.mask = *mask; kp.code = code; kth->ktr_buf = (caddr_t)&kp; kth->ktr_len = sizeof (struct ktr_psig); ktrwrite(vp, kth, NULL); FREE(kth, M_KTRACE); p->p_traceflag &= ~KTRFAC_ACTIVE; } void ktrcsw(vp, out, user) struct vnode *vp; int out, user; { struct ktr_header *kth; struct ktr_csw kc; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; kth = ktrgetheader(KTR_CSW); kc.out = out; kc.user = user; kth->ktr_buf = (caddr_t)&kc; kth->ktr_len = sizeof (struct ktr_csw); ktrwrite(vp, kth, NULL); FREE(kth, M_KTRACE); p->p_traceflag &= ~KTRFAC_ACTIVE; } #endif /* Interface and common routines */ /* * ktrace system call */ #ifndef _SYS_SYSPROTO_H_ struct ktrace_args { char *fname; int ops; int facs; int pid; }; #endif /* ARGSUSED */ int ktrace(curp, uap) struct proc *curp; register struct ktrace_args *uap; { #ifdef KTRACE register struct vnode *vp = NULL; register struct proc *p; struct pgrp *pg; int facs = uap->facs & ~KTRFAC_ROOT; int ops = KTROP(uap->ops); int descend = uap->ops & KTRFLAG_DESCEND; int ret = 0; - int error = 0; + int flags, error = 0; struct nameidata nd; curp->p_traceflag |= KTRFAC_ACTIVE; if (ops != KTROP_CLEAR) { /* * an operation which requires a file argument. */ NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->fname, curp); - error = vn_open(&nd, FREAD|FWRITE|O_NOFOLLOW, 0); + flags = FREAD | FWRITE | O_NOFOLLOW; + error = vn_open(&nd, &flags, 0); if (error) { curp->p_traceflag &= ~KTRFAC_ACTIVE; return (error); } NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; VOP_UNLOCK(vp, 0, curp); if (vp->v_type != VREG) { (void) vn_close(vp, FREAD|FWRITE, curp->p_ucred, curp); curp->p_traceflag &= ~KTRFAC_ACTIVE; return (EACCES); } } /* * Clear all uses of the tracefile */ if (ops == KTROP_CLEARFILE) { LIST_FOREACH(p, &allproc, p_list) { if (p->p_tracep == vp) { if (ktrcanset(curp, p)) { p->p_tracep = NULL; p->p_traceflag = 0; (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p); } else error = EPERM; } } goto done; } /* * need something to (un)trace (XXX - why is this here?) */ if (!facs) { error = EINVAL; goto done; } /* * do it */ if (uap->pid < 0) { /* * by process group */ pg = pgfind(-uap->pid); if (pg == NULL) { error = ESRCH; goto done; } LIST_FOREACH(p, &pg->pg_members, p_pglist) if (descend) ret |= ktrsetchildren(curp, p, ops, facs, vp); else ret |= ktrops(curp, p, ops, facs, vp); } else { /* * by pid */ p = pfind(uap->pid); if (p == NULL) { error = ESRCH; goto done; } if (descend) ret |= ktrsetchildren(curp, p, ops, facs, vp); else ret |= ktrops(curp, p, ops, facs, vp); } if (!ret) error = EPERM; done: if (vp != NULL) (void) vn_close(vp, FWRITE, curp->p_ucred, curp); curp->p_traceflag &= ~KTRFAC_ACTIVE; return (error); #else return ENOSYS; #endif } /* * utrace system call */ /* ARGSUSED */ int utrace(curp, uap) struct proc *curp; register struct utrace_args *uap; { #ifdef KTRACE struct ktr_header *kth; struct proc *p = curproc; /* XXX */ register caddr_t cp; if (!KTRPOINT(p, KTR_USER)) return (0); p->p_traceflag |= KTRFAC_ACTIVE; kth = ktrgetheader(KTR_USER); MALLOC(cp, caddr_t, uap->len, M_KTRACE, M_WAITOK); if (!copyin(uap->addr, cp, uap->len)) { kth->ktr_buf = cp; kth->ktr_len = uap->len; ktrwrite(p->p_tracep, kth, NULL); } FREE(kth, M_KTRACE); FREE(cp, M_KTRACE); p->p_traceflag &= ~KTRFAC_ACTIVE; return (0); #else return (ENOSYS); #endif } #ifdef KTRACE static int ktrops(curp, p, ops, facs, vp) struct proc *p, *curp; int ops, facs; struct vnode *vp; { if (!ktrcanset(curp, p)) return (0); if (ops == KTROP_SET) { if (p->p_tracep != vp) { /* * if trace file already in use, relinquish */ if (p->p_tracep != NULL) vrele(p->p_tracep); VREF(vp); p->p_tracep = vp; } p->p_traceflag |= facs; if (curp->p_ucred->cr_uid == 0) p->p_traceflag |= KTRFAC_ROOT; } else { /* KTROP_CLEAR */ if (((p->p_traceflag &= ~facs) & KTRFAC_MASK) == 0) { /* no more tracing */ p->p_traceflag = 0; if (p->p_tracep != NULL) { vrele(p->p_tracep); p->p_tracep = NULL; } } } return (1); } static int ktrsetchildren(curp, top, ops, facs, vp) struct proc *curp, *top; int ops, facs; struct vnode *vp; { register struct proc *p; register int ret = 0; p = top; for (;;) { ret |= ktrops(curp, p, ops, facs, vp); /* * If this process has children, descend to them next, * otherwise do any siblings, and if done with this level, * follow back up the tree (but not past top). */ if (!LIST_EMPTY(&p->p_children)) p = LIST_FIRST(&p->p_children); else for (;;) { if (p == top) return (ret); if (LIST_NEXT(p, p_sibling)) { p = LIST_NEXT(p, p_sibling); break; } p = p->p_pptr; } } /*NOTREACHED*/ } static void ktrwrite(vp, kth, uio) struct vnode *vp; register struct ktr_header *kth; struct uio *uio; { struct uio auio; struct iovec aiov[2]; register struct proc *p = curproc; /* XXX */ int error; if (vp == NULL) return; auio.uio_iov = &aiov[0]; auio.uio_offset = 0; auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_WRITE; aiov[0].iov_base = (caddr_t)kth; aiov[0].iov_len = sizeof(struct ktr_header); auio.uio_resid = sizeof(struct ktr_header); auio.uio_iovcnt = 1; auio.uio_procp = curproc; if (kth->ktr_len > 0) { auio.uio_iovcnt++; aiov[1].iov_base = kth->ktr_buf; aiov[1].iov_len = kth->ktr_len; auio.uio_resid += kth->ktr_len; if (uio != NULL) kth->ktr_len += uio->uio_resid; } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); (void)VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_WRITE(vp, &auio, IO_UNIT | IO_APPEND, p->p_ucred); if (error == 0 && uio != NULL) { (void)VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_WRITE(vp, uio, IO_UNIT | IO_APPEND, p->p_ucred); } VOP_UNLOCK(vp, 0, p); if (!error) return; /* * If error encountered, give up tracing on this vnode. */ log(LOG_NOTICE, "ktrace write failed, errno %d, tracing stopped\n", error); LIST_FOREACH(p, &allproc, p_list) { if (p->p_tracep == vp) { p->p_tracep = NULL; p->p_traceflag = 0; vrele(vp); } } } /* * Return true if caller has permission to set the ktracing state * of target. Essentially, the target can't possess any * more permissions than the caller. KTRFAC_ROOT signifies that * root previously set the tracing status on the target process, and * so, only root may further change it. * * TODO: check groups. use caller effective gid. */ static int ktrcanset(callp, targetp) struct proc *callp, *targetp; { register struct pcred *caller = callp->p_cred; register struct pcred *target = targetp->p_cred; if (!PRISON_CHECK(callp, targetp)) return (0); if ((caller->pc_ucred->cr_uid == target->p_ruid && target->p_ruid == target->p_svuid && caller->p_rgid == target->p_rgid && /* XXX */ target->p_rgid == target->p_svgid && (targetp->p_traceflag & KTRFAC_ROOT) == 0) || caller->pc_ucred->cr_uid == 0) return (1); return (0); } #endif /* KTRACE */ Index: head/sys/kern/kern_linker.c =================================================================== --- head/sys/kern/kern_linker.c (revision 62549) +++ head/sys/kern/kern_linker.c (revision 62550) @@ -1,1379 +1,1380 @@ /*- * Copyright (c) 1997-2000 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linker_if.h" #ifdef KLD_DEBUG int kld_debug = 0; #endif static char *linker_search_path(const char *name); static const char *linker_basename(const char* path); MALLOC_DEFINE(M_LINKER, "linker", "kernel linker"); linker_file_t linker_kernel_file; static struct lock lock; /* lock for the file list */ static linker_class_list_t classes; static linker_file_list_t linker_files; static int next_file_id = 1; /* XXX wrong name; we're looking at version provision tags here, not modules */ typedef TAILQ_HEAD(, modlist) modlisthead_t; struct modlist { TAILQ_ENTRY(modlist) link; /* chain together all modules */ linker_file_t container; const char *name; }; typedef struct modlist *modlist_t; static modlisthead_t found_modules; static char * linker_strdup(const char *str) { char *result; if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL) strcpy(result, str); return(result); } static void linker_init(void* arg) { lockinit(&lock, PVM, "klink", 0, 0); TAILQ_INIT(&classes); TAILQ_INIT(&linker_files); } SYSINIT(linker, SI_SUB_KLD, SI_ORDER_FIRST, linker_init, 0); int linker_add_class(linker_class_t lc) { kobj_class_compile((kobj_class_t) lc); TAILQ_INSERT_TAIL(&classes, lc, link); return 0; } static void linker_file_sysinit(linker_file_t lf) { struct linker_set* sysinits; struct sysinit** sipp; struct sysinit** xipp; struct sysinit* save; KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n", lf->filename)); sysinits = (struct linker_set*) linker_file_lookup_symbol(lf, "sysinit_set", 0); KLD_DPF(FILE, ("linker_file_sysinit: SYSINITs %p\n", sysinits)); if (!sysinits) return; /* * Perform a bubble sort of the system initialization objects by * their subsystem (primary key) and order (secondary key). * * Since some things care about execution order, this is the * operation which ensures continued function. */ for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { for (xipp = sipp + 1; *xipp; xipp++) { if ((*sipp)->subsystem <= (*xipp)->subsystem || ((*sipp)->subsystem == (*xipp)->subsystem && (*sipp)->order <= (*xipp)->order)) continue; /* skip*/ save = *sipp; *sipp = *xipp; *xipp = save; } } /* * Traverse the (now) ordered list of system initialization tasks. * Perform each task, and continue on to the next task. */ for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { if ((*sipp)->subsystem == SI_SUB_DUMMY) continue; /* skip dummy task(s)*/ /* Call function */ (*((*sipp)->func))((*sipp)->udata); } } static void linker_file_sysuninit(linker_file_t lf) { struct linker_set* sysuninits; struct sysinit** sipp; struct sysinit** xipp; struct sysinit* save; KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n", lf->filename)); sysuninits = (struct linker_set*) linker_file_lookup_symbol(lf, "sysuninit_set", 0); KLD_DPF(FILE, ("linker_file_sysuninit: SYSUNINITs %p\n", sysuninits)); if (!sysuninits) return; /* * Perform a reverse bubble sort of the system initialization objects * by their subsystem (primary key) and order (secondary key). * * Since some things care about execution order, this is the * operation which ensures continued function. */ for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) { for (xipp = sipp + 1; *xipp; xipp++) { if ((*sipp)->subsystem >= (*xipp)->subsystem || ((*sipp)->subsystem == (*xipp)->subsystem && (*sipp)->order >= (*xipp)->order)) continue; /* skip*/ save = *sipp; *sipp = *xipp; *xipp = save; } } /* * Traverse the (now) ordered list of system initialization tasks. * Perform each task, and continue on to the next task. */ for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) { if ((*sipp)->subsystem == SI_SUB_DUMMY) continue; /* skip dummy task(s)*/ /* Call function */ (*((*sipp)->func))((*sipp)->udata); } } static void linker_file_register_sysctls(linker_file_t lf) { struct linker_set* sysctls; KLD_DPF(FILE, ("linker_file_register_sysctls: registering SYSCTLs for %s\n", lf->filename)); sysctls = (struct linker_set*) linker_file_lookup_symbol(lf, "sysctl_set", 0); KLD_DPF(FILE, ("linker_file_register_sysctls: SYSCTLs %p\n", sysctls)); if (!sysctls) return; sysctl_register_set(sysctls); } static void linker_file_unregister_sysctls(linker_file_t lf) { struct linker_set* sysctls; KLD_DPF(FILE, ("linker_file_unregister_sysctls: registering SYSCTLs for %s\n", lf->filename)); sysctls = (struct linker_set*) linker_file_lookup_symbol(lf, "sysctl_set", 0); KLD_DPF(FILE, ("linker_file_unregister_sysctls: SYSCTLs %p\n", sysctls)); if (!sysctls) return; sysctl_unregister_set(sysctls); } static int linker_file_register_modules(linker_file_t lf) { int error, mcount; struct linker_set *modules; struct mod_metadata **mdpp; const moduledata_t *moddata; struct sysinit **sipp; KLD_DPF(FILE, ("linker_file_register_modules: registering modules in %s\n", lf->filename)); modules = (struct linker_set*) linker_file_lookup_symbol(lf, "modmetadata_set", 0); mcount = 0; if (modules) { for (mdpp = (struct mod_metadata**)modules->ls_items; *mdpp; mdpp++) { if ((*mdpp)->md_type != MDT_MODULE) continue; mcount++; moddata = (*mdpp)->md_data; KLD_DPF(FILE, ("Registering module %s in %s\n", moddata->name, lf->filename)); error = module_register(moddata, lf); if (error) printf("Module %s failed to register: %d\n", moddata->name, error); } } if (mcount) return mcount; /* Do not mix old and new style */ /* Hack - handle old kld's without metadata */ modules = (struct linker_set*) linker_file_lookup_symbol(lf, "sysinit_set", 0); if (modules) { for (sipp = (struct sysinit **)modules->ls_items; *sipp; sipp++) { if ((*sipp)->func != module_register_init) continue; mcount++; moddata = (*sipp)->udata; printf("Old-style KLD file %s found\n", moddata->name); error = module_register(moddata, lf); if (error) printf("Old-style KLD file %s failed to register: %d\n", moddata->name, error); } } return mcount; } static void linker_init_kernel_modules(void) { linker_file_register_modules(linker_kernel_file); } SYSINIT(linker_kernel, SI_SUB_KLD, SI_ORDER_ANY, linker_init_kernel_modules, 0); int linker_load_file(const char* filename, linker_file_t* result) { linker_class_t lc; linker_file_t lf; int foundfile, error = 0; /* Refuse to load modules if securelevel raised */ if (securelevel > 0) return EPERM; lf = linker_find_file_by_name(filename); if (lf) { KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename)); *result = lf; lf->refs++; goto out; } lf = NULL; foundfile = 0; for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n", filename, lc->desc)); error = LINKER_LOAD_FILE(lc, filename, &lf); /* * If we got something other than ENOENT, then it exists but we cannot * load it for some other reason. */ if (error != ENOENT) foundfile = 1; if (lf) { linker_file_register_modules(lf); linker_file_register_sysctls(lf); linker_file_sysinit(lf); lf->flags |= LINKER_FILE_LINKED; *result = lf; error = 0; goto out; } } /* * Less than ideal, but tells the user whether it failed to load or * the module was not found. */ if (foundfile) error = ENOEXEC; /* Format not recognised (or unloadable) */ else error = ENOENT; /* Nothing found */ out: return error; } linker_file_t linker_find_file_by_name(const char* filename) { linker_file_t lf = 0; char *koname; koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); if (koname == NULL) goto out; sprintf(koname, "%s.ko", filename); lockmgr(&lock, LK_SHARED, 0, curproc); for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { if (!strcmp(lf->filename, koname)) break; if (!strcmp(lf->filename, filename)) break; } lockmgr(&lock, LK_RELEASE, 0, curproc); out: if (koname) free(koname, M_LINKER); return lf; } linker_file_t linker_find_file_by_id(int fileid) { linker_file_t lf = 0; lockmgr(&lock, LK_SHARED, 0, curproc); for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) if (lf->id == fileid) break; lockmgr(&lock, LK_RELEASE, 0, curproc); return lf; } linker_file_t linker_make_file(const char* pathname, linker_class_t lc) { linker_file_t lf = 0; const char *filename; filename = linker_basename(pathname); KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename)); lockmgr(&lock, LK_EXCLUSIVE, 0, curproc); lf = (linker_file_t) kobj_create((kobj_class_t) lc, M_LINKER, M_WAITOK); if (!lf) goto out; lf->refs = 1; lf->userrefs = 0; lf->flags = 0; lf->filename = linker_strdup(filename); lf->id = next_file_id++; lf->ndeps = 0; lf->deps = NULL; STAILQ_INIT(&lf->common); TAILQ_INIT(&lf->modules); TAILQ_INSERT_TAIL(&linker_files, lf, link); out: lockmgr(&lock, LK_RELEASE, 0, curproc); return lf; } int linker_file_unload(linker_file_t file) { module_t mod, next; modlist_t ml, nextml; struct common_symbol* cp; int error = 0; int i; /* Refuse to unload modules if securelevel raised */ if (securelevel > 0) return EPERM; KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs)); lockmgr(&lock, LK_EXCLUSIVE, 0, curproc); if (file->refs == 1) { KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n")); /* * Inform any modules associated with this file. */ for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) { next = module_getfnext(mod); /* * Give the module a chance to veto the unload. */ if ((error = module_unload(mod)) != 0) { KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n", mod)); lockmgr(&lock, LK_RELEASE, 0, curproc); goto out; } module_release(mod); } } file->refs--; if (file->refs > 0) { lockmgr(&lock, LK_RELEASE, 0, curproc); goto out; } for (ml = TAILQ_FIRST(&found_modules); ml; ml = nextml) { nextml = TAILQ_NEXT(ml, link); if (ml->container == file) { TAILQ_REMOVE(&found_modules, ml, link); } } /* Don't try to run SYSUNINITs if we are unloaded due to a link error */ if (file->flags & LINKER_FILE_LINKED) { linker_file_sysuninit(file); linker_file_unregister_sysctls(file); } TAILQ_REMOVE(&linker_files, file, link); lockmgr(&lock, LK_RELEASE, 0, curproc); if (file->deps) { for (i = 0; i < file->ndeps; i++) linker_file_unload(file->deps[i]); free(file->deps, M_LINKER); file->deps = NULL; } for (cp = STAILQ_FIRST(&file->common); cp; cp = STAILQ_FIRST(&file->common)) { STAILQ_REMOVE(&file->common, cp, common_symbol, link); free(cp, M_LINKER); } LINKER_UNLOAD(file); if (file->filename) { free(file->filename, M_LINKER); file->filename = NULL; } kobj_delete((kobj_t) file, M_LINKER); out: return error; } int linker_file_add_dependancy(linker_file_t file, linker_file_t dep) { linker_file_t* newdeps; newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*), M_LINKER, M_WAITOK); if (newdeps == NULL) return ENOMEM; bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*)); if (file->deps) { bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*)); free(file->deps, M_LINKER); } file->deps = newdeps; file->deps[file->ndeps] = dep; file->ndeps++; return 0; } caddr_t linker_file_lookup_symbol(linker_file_t file, const char* name, int deps) { c_linker_sym_t sym; linker_symval_t symval; caddr_t address; size_t common_size = 0; int i; KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n", file, name, deps)); if (LINKER_LOOKUP_SYMBOL(file, name, &sym) == 0) { LINKER_SYMBOL_VALUES(file, sym, &symval); if (symval.value == 0) /* * For commons, first look them up in the dependancies and * only allocate space if not found there. */ common_size = symval.size; else { KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value)); return symval.value; } } if (deps) { for (i = 0; i < file->ndeps; i++) { address = linker_file_lookup_symbol(file->deps[i], name, 0); if (address) { KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address)); return address; } } } if (common_size > 0) { /* * This is a common symbol which was not found in the * dependancies. We maintain a simple common symbol table in * the file object. */ struct common_symbol* cp; for (cp = STAILQ_FIRST(&file->common); cp; cp = STAILQ_NEXT(cp, link)) if (!strcmp(cp->name, name)) { KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address)); return cp->address; } /* * Round the symbol size up to align. */ common_size = (common_size + sizeof(int) - 1) & -sizeof(int); cp = malloc(sizeof(struct common_symbol) + common_size + strlen(name) + 1, M_LINKER, M_WAITOK); if (!cp) { KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n")); return 0; } bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1); cp->address = (caddr_t) (cp + 1); cp->name = cp->address + common_size; strcpy(cp->name, name); bzero(cp->address, common_size); STAILQ_INSERT_TAIL(&file->common, cp, link); KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address)); return cp->address; } KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n")); return 0; } #ifdef DDB /* * DDB Helpers. DDB has to look across multiple files with their own * symbol tables and string tables. * * Note that we do not obey list locking protocols here. We really don't * need DDB to hang because somebody's got the lock held. We'll take the * chance that the files list is inconsistant instead. */ int linker_ddb_lookup(const char *symstr, c_linker_sym_t *sym) { linker_file_t lf; for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { if (LINKER_LOOKUP_SYMBOL(lf, symstr, sym) == 0) return 0; } return ENOENT; } int linker_ddb_search_symbol(caddr_t value, c_linker_sym_t *sym, long *diffp) { linker_file_t lf; u_long off = (uintptr_t)value; u_long diff, bestdiff; c_linker_sym_t best; c_linker_sym_t es; best = 0; bestdiff = off; for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { if (LINKER_SEARCH_SYMBOL(lf, value, &es, &diff) != 0) continue; if (es != 0 && diff < bestdiff) { best = es; bestdiff = diff; } if (bestdiff == 0) break; } if (best) { *sym = best; *diffp = bestdiff; return 0; } else { *sym = 0; *diffp = off; return ENOENT; } } int linker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval) { linker_file_t lf; for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { if (LINKER_SYMBOL_VALUES(lf, sym, symval) == 0) return 0; } return ENOENT; } #endif /* * Syscalls. */ int kldload(struct proc* p, struct kldload_args* uap) { char* pathname, *realpath; const char *filename; linker_file_t lf; int error = 0; p->p_retval[0] = -1; if (securelevel > 0) /* redundant, but that's OK */ return EPERM; if ((error = suser(p)) != 0) return error; realpath = NULL; pathname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); if ((error = copyinstr(SCARG(uap, file), pathname, MAXPATHLEN, NULL)) != 0) goto out; realpath = linker_search_path(pathname); if (realpath == NULL) { error = ENOENT; goto out; } /* Can't load more than one file with the same name */ filename = linker_basename(realpath); if (linker_find_file_by_name(filename)) { error = EEXIST; goto out; } if ((error = linker_load_file(realpath, &lf)) != 0) goto out; lf->userrefs++; p->p_retval[0] = lf->id; out: if (pathname) free(pathname, M_TEMP); if (realpath) free(realpath, M_LINKER); return error; } int kldunload(struct proc* p, struct kldunload_args* uap) { linker_file_t lf; int error = 0; if (securelevel > 0) /* redundant, but that's OK */ return EPERM; if ((error = suser(p)) != 0) return error; lf = linker_find_file_by_id(SCARG(uap, fileid)); if (lf) { KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); if (lf->userrefs == 0) { printf("kldunload: attempt to unload file that was loaded by the kernel\n"); error = EBUSY; goto out; } lf->userrefs--; error = linker_file_unload(lf); if (error) lf->userrefs++; } else error = ENOENT; out: return error; } int kldfind(struct proc* p, struct kldfind_args* uap) { char* pathname; const char *filename; linker_file_t lf; int error = 0; p->p_retval[0] = -1; pathname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); if ((error = copyinstr(SCARG(uap, file), pathname, MAXPATHLEN, NULL)) != 0) goto out; filename = linker_basename(pathname); lf = linker_find_file_by_name(filename); if (lf) p->p_retval[0] = lf->id; else error = ENOENT; out: if (pathname) free(pathname, M_TEMP); return error; } int kldnext(struct proc* p, struct kldnext_args* uap) { linker_file_t lf; int error = 0; if (SCARG(uap, fileid) == 0) { if (TAILQ_FIRST(&linker_files)) p->p_retval[0] = TAILQ_FIRST(&linker_files)->id; else p->p_retval[0] = 0; return 0; } lf = linker_find_file_by_id(SCARG(uap, fileid)); if (lf) { if (TAILQ_NEXT(lf, link)) p->p_retval[0] = TAILQ_NEXT(lf, link)->id; else p->p_retval[0] = 0; } else error = ENOENT; return error; } int kldstat(struct proc* p, struct kldstat_args* uap) { linker_file_t lf; int error = 0; int version; struct kld_file_stat* stat; int namelen; lf = linker_find_file_by_id(SCARG(uap, fileid)); if (!lf) { error = ENOENT; goto out; } stat = SCARG(uap, stat); /* * Check the version of the user's structure. */ if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) goto out; if (version != sizeof(struct kld_file_stat)) { error = EINVAL; goto out; } namelen = strlen(lf->filename) + 1; if (namelen > MAXPATHLEN) namelen = MAXPATHLEN; if ((error = copyout(lf->filename, &stat->name[0], namelen)) != 0) goto out; if ((error = copyout(&lf->refs, &stat->refs, sizeof(int))) != 0) goto out; if ((error = copyout(&lf->id, &stat->id, sizeof(int))) != 0) goto out; if ((error = copyout(&lf->address, &stat->address, sizeof(caddr_t))) != 0) goto out; if ((error = copyout(&lf->size, &stat->size, sizeof(size_t))) != 0) goto out; p->p_retval[0] = 0; out: return error; } int kldfirstmod(struct proc* p, struct kldfirstmod_args* uap) { linker_file_t lf; int error = 0; lf = linker_find_file_by_id(SCARG(uap, fileid)); if (lf) { if (TAILQ_FIRST(&lf->modules)) p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules)); else p->p_retval[0] = 0; } else error = ENOENT; return error; } int kldsym(struct proc *p, struct kldsym_args *uap) { char *symstr = NULL; c_linker_sym_t sym; linker_symval_t symval; linker_file_t lf; struct kld_sym_lookup lookup; int error = 0; if ((error = copyin(SCARG(uap, data), &lookup, sizeof(lookup))) != 0) goto out; if (lookup.version != sizeof(lookup) || SCARG(uap, cmd) != KLDSYM_LOOKUP) { error = EINVAL; goto out; } symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0) goto out; if (SCARG(uap, fileid) != 0) { lf = linker_find_file_by_id(SCARG(uap, fileid)); if (lf == NULL) { error = ENOENT; goto out; } if (LINKER_LOOKUP_SYMBOL(lf, symstr, &sym) == 0 && LINKER_SYMBOL_VALUES(lf, sym, &symval) == 0) { lookup.symvalue = (uintptr_t)symval.value; lookup.symsize = symval.size; error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); } else error = ENOENT; } else { for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { if (LINKER_LOOKUP_SYMBOL(lf, symstr, &sym) == 0 && LINKER_SYMBOL_VALUES(lf, sym, &symval) == 0) { lookup.symvalue = (uintptr_t)symval.value; lookup.symsize = symval.size; error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); break; } } if (!lf) error = ENOENT; } out: if (symstr) free(symstr, M_TEMP); return error; } /* * Preloaded module support */ static modlist_t modlist_lookup(const char *name) { modlist_t mod; for (mod = TAILQ_FIRST(&found_modules); mod; mod = TAILQ_NEXT(mod, link)) { if (!strcmp(mod->name, name)) return mod; } return NULL; } /* * This routine is cheap and nasty but will work for data pointers. */ static void * linker_reloc_ptr(linker_file_t lf, void *offset) { return lf->address + (uintptr_t)offset; } static void linker_preload(void* arg) { caddr_t modptr; char *modname; char *modtype; linker_file_t lf; linker_class_t lc; int error, mcount; struct linker_set *sysinits; linker_file_list_t loaded_files; linker_file_list_t depended_files; struct linker_set *deps; struct mod_metadata *mp; int i; int resolves; modlist_t mod; TAILQ_INIT(&loaded_files); TAILQ_INIT(&depended_files); TAILQ_INIT(&found_modules); error = 0; modptr = NULL; while ((modptr = preload_search_next_name(modptr)) != NULL) { modname = (char *)preload_search_info(modptr, MODINFO_NAME); modtype = (char *)preload_search_info(modptr, MODINFO_TYPE); if (modname == NULL) { printf("Preloaded module at %p does not have a name!\n", modptr); continue; } if (modtype == NULL) { printf("Preloaded module at %p does not have a type!\n", modptr); continue; } printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr); lf = NULL; for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { error = LINKER_LINK_PRELOAD(lc, modname, &lf); if (error) { lf = NULL; break; } } if (lf) TAILQ_INSERT_TAIL(&loaded_files, lf, loaded); } /* * First get a list of stuff in the kernel. */ deps = (struct linker_set*) linker_file_lookup_symbol(linker_kernel_file, MDT_SETNAME, 0); if (deps) { for (i = 0; i < deps->ls_length; i++) { mp = deps->ls_items[i]; if (mp->md_type != MDT_VERSION) continue; modname = mp->md_cval; if (modlist_lookup(modname) != NULL) { printf("module %s already present!\n", modname); /* XXX what can we do? this is a build error. :-( */ continue; } mod = malloc(sizeof(struct modlist), M_LINKER, M_NOWAIT); if (mod == NULL) panic("no memory for module list"); bzero(mod, sizeof(*mod)); mod->container = linker_kernel_file; mod->name = modname; TAILQ_INSERT_TAIL(&found_modules, mod, link); } } /* * this is a once-off kinky bubble sort * resolve relocation dependency requirements */ restart: for (lf = TAILQ_FIRST(&loaded_files); lf; lf = TAILQ_NEXT(lf, loaded)) { deps = (struct linker_set*) linker_file_lookup_symbol(lf, MDT_SETNAME, 0); /* * First, look to see if we would successfully link with this stuff. */ resolves = 1; /* unless we know otherwise */ if (deps) { for (i = 0; i < deps->ls_length; i++) { mp = linker_reloc_ptr(lf, deps->ls_items[i]); if (mp->md_type != MDT_DEPEND) continue; modname = linker_reloc_ptr(lf, mp->md_cval); if (modlist_lookup(modname) == NULL) { /* ok, the module isn't here yet, we are not finished */ resolves = 0; } } } /* * OK, if we found our modules, we can link. So, "provide" the * modules inside and add it to the end of the link order list. */ if (resolves) { if (deps) { for (i = 0; i < deps->ls_length; i++) { mp = linker_reloc_ptr(lf, deps->ls_items[i]); if (mp->md_type != MDT_VERSION) continue; modname = linker_reloc_ptr(lf, mp->md_cval); if (modlist_lookup(modname) != NULL) { printf("module %s already present!\n", modname); linker_file_unload(lf); TAILQ_REMOVE(&loaded_files, lf, loaded); goto restart; /* we changed the tailq next ptr */ } mod = malloc(sizeof(struct modlist), M_LINKER, M_NOWAIT); if (mod == NULL) panic("no memory for module list"); bzero(mod, sizeof(*mod)); mod->container = lf; mod->name = modname; TAILQ_INSERT_TAIL(&found_modules, mod, link); } } TAILQ_REMOVE(&loaded_files, lf, loaded); TAILQ_INSERT_TAIL(&depended_files, lf, loaded); /* * Since we provided modules, we need to restart the sort so * that the previous files that depend on us have a chance. * Also, we've busted the tailq next pointer with the REMOVE. */ goto restart; } } /* * At this point, we check to see what could not be resolved.. */ for (lf = TAILQ_FIRST(&loaded_files); lf; lf = TAILQ_NEXT(lf, loaded)) { printf("KLD file %s is missing dependencies\n", lf->filename); linker_file_unload(lf); TAILQ_REMOVE(&loaded_files, lf, loaded); } /* * We made it. Finish off the linking in the order we determined. */ for (lf = TAILQ_FIRST(&depended_files); lf; lf = TAILQ_NEXT(lf, loaded)) { if (linker_kernel_file) { linker_kernel_file->refs++; error = linker_file_add_dependancy(lf, linker_kernel_file); if (error) panic("cannot add dependency"); } lf->userrefs++; /* so we can (try to) kldunload it */ deps = (struct linker_set*) linker_file_lookup_symbol(lf, MDT_SETNAME, 0); if (deps) { for (i = 0; i < deps->ls_length; i++) { mp = linker_reloc_ptr(lf, deps->ls_items[i]); if (mp->md_type != MDT_DEPEND) continue; modname = linker_reloc_ptr(lf, mp->md_cval); mod = modlist_lookup(modname); mod->container->refs++; error = linker_file_add_dependancy(lf, mod->container); if (error) panic("cannot add dependency"); } } /* Now do relocation etc using the symbol search paths established by the dependencies */ error = LINKER_LINK_PRELOAD_FINISH(lf); if (error) { printf("KLD file %s - could not finalize loading\n", lf->filename); linker_file_unload(lf); continue; } mcount = linker_file_register_modules(lf); sysinits = (struct linker_set*) linker_file_lookup_symbol(lf, "sysinit_set", 0); if (sysinits) sysinit_add((struct sysinit **)sysinits->ls_items); linker_file_register_sysctls(lf); lf->flags |= LINKER_FILE_LINKED; } /* woohoo! we made it! */ } SYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0); /* * Search for a not-loaded module by name. * * Modules may be found in the following locations: * * - preloaded (result is just the module name) * - on disk (result is full path to module) * * If the module name is qualified in any way (contains path, etc.) * the we simply return a copy of it. * * The search path can be manipulated via sysctl. Note that we use the ';' * character as a separator to be consistent with the bootloader. */ static char linker_path[MAXPATHLEN] = "/;/boot/;/modules/"; SYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path, sizeof(linker_path), "module load search path"); static char *linker_ext_list[] = { ".ko", "", NULL }; static char * linker_search_path(const char *name) { struct nameidata nd; struct proc *p = curproc; /* XXX */ char *cp, *ep, *result, **cpp; - int error, extlen, len; + int error, extlen, len, flags; enum vtype type; /* qualified at all? */ if (index(name, '/')) return(linker_strdup(name)); extlen = 0; for (cpp = linker_ext_list; *cpp; cpp++) { len = strlen(*cpp); if (len > extlen) extlen = len; } extlen++; /* trailing '\0' */ /* traverse the linker path */ cp = linker_path; len = strlen(name); for (;;) { /* find the end of this component */ for (ep = cp; (*ep != 0) && (*ep != ';'); ep++) ; result = malloc((len + (ep - cp) + extlen), M_LINKER, M_WAITOK); if (result == NULL) /* actually ENOMEM */ return(NULL); for (cpp = linker_ext_list; *cpp; cpp++) { strncpy(result, cp, ep - cp); strcpy(result + (ep - cp), name); strcat(result, *cpp); /* * Attempt to open the file, and return the path if we succeed * and it's a regular file. */ NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p); - error = vn_open(&nd, FREAD, 0); + flags = FREAD; + error = vn_open(&nd, &flags, 0); if (error == 0) { NDFREE(&nd, NDF_ONLY_PNBUF); type = nd.ni_vp->v_type; VOP_UNLOCK(nd.ni_vp, 0, p); vn_close(nd.ni_vp, FREAD, p->p_ucred, p); if (type == VREG) return(result); } } free(result, M_LINKER); if (*ep == 0) break; cp = ep + 1; } return(NULL); } static const char * linker_basename(const char* path) { const char *filename; filename = rindex(path, '/'); if (filename == NULL) return path; if (filename[1]) filename++; return filename; } /* * Find a file which contains given module and load it, * if "parent" is not NULL, register a reference to it. */ static int linker_load_module(const char *modname, struct linker_file *parent) { linker_file_t lfdep; const char *filename; char *pathname; int error; /* * There will be a system to look up or guess a file name from * a module name. * For now we just try to load a file with the same name. */ pathname = linker_search_path(modname); if (pathname == NULL) return ENOENT; /* Can't load more than one file with the same basename */ filename = linker_basename(pathname); if (linker_find_file_by_name(filename)) { error = EEXIST; goto out; } do { error = linker_load_file(pathname, &lfdep); if (error) break; if (parent) { error = linker_file_add_dependancy(parent, lfdep); if (error) break; } } while(0); out: if (pathname) free(pathname, M_LINKER); return error; } /* * This routine is responsible for finding dependencies of userland * initiated kldload(2)'s of files. */ int linker_load_dependancies(linker_file_t lf) { linker_file_t lfdep; struct linker_set *deps; struct mod_metadata *mp, *nmp; modlist_t mod; char *modname, *nmodname; int i, j, error = 0; /* * All files are dependant on /kernel. */ if (linker_kernel_file) { linker_kernel_file->refs++; error = linker_file_add_dependancy(lf, linker_kernel_file); if (error) return error; } deps = (struct linker_set*) linker_file_lookup_symbol(lf, MDT_SETNAME, 0); if (deps != NULL) { for (i = 0; i < deps->ls_length; i++) { mp = linker_reloc_ptr(lf, deps->ls_items[i]); if (mp->md_type != MDT_VERSION) continue; modname = linker_reloc_ptr(lf, mp->md_cval); if (modlist_lookup(modname) != NULL) { printf("module %s already present!\n", modname); return EEXIST; } } } if (deps != NULL) { for (i = 0; i < deps->ls_length; i++) { mp = linker_reloc_ptr(lf, deps->ls_items[i]); if (mp->md_type != MDT_DEPEND) continue; modname = linker_reloc_ptr(lf, mp->md_cval); nmodname = NULL; for (j = 0; j < deps->ls_length; j++) { nmp = linker_reloc_ptr(lf, deps->ls_items[j]); if (nmp->md_type != MDT_VERSION) continue; nmodname = linker_reloc_ptr(lf, nmp->md_cval); if (strcmp(modname, nmodname) == 0) break; } if (j < deps->ls_length) /* early exit, it's a self reference */ continue; mod = modlist_lookup(modname); if (mod) { /* woohoo, it's loaded already */ lfdep = mod->container; lfdep->refs++; error = linker_file_add_dependancy(lf, lfdep); if (error) break; continue; } error = linker_load_module(modname, lf); if (error) { printf("KLD %s: depends on %s - not available\n", lf->filename, modname); break; } } } if (error == 0 && deps) { for (i = 0; i < deps->ls_length; i++) { mp = linker_reloc_ptr(lf, deps->ls_items[i]); if (mp->md_type != MDT_VERSION) continue; modname = linker_reloc_ptr(lf, mp->md_cval); mod = malloc(sizeof(struct modlist), M_LINKER, M_NOWAIT); if (mod == NULL) panic("no memory for module list"); bzero(mod, sizeof(*mod)); mod->container = lf; mod->name = modname; TAILQ_INSERT_TAIL(&found_modules, mod, link); } } return error; } Index: head/sys/kern/kern_sig.c =================================================================== --- head/sys/kern/kern_sig.c (revision 62549) +++ head/sys/kern/kern_sig.c (revision 62550) @@ -1,1740 +1,1741 @@ /* * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kern_sig.c 8.7 (Berkeley) 4/18/94 * $FreeBSD$ */ #include "opt_compat.h" #include "opt_ktrace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ONSIG 32 /* NSIG for osig* syscalls. XXX. */ static int coredump __P((struct proc *)); static int do_sigaction __P((struct proc *p, int sig, struct sigaction *act, struct sigaction *oact, int old)); static int do_sigprocmask __P((struct proc *p, int how, sigset_t *set, sigset_t *oset, int old)); static char *expand_name __P((const char *, uid_t, pid_t)); static int killpg1 __P((struct proc *cp, int sig, int pgid, int all)); static int sig_ffs __P((sigset_t *set)); static int sigprop __P((int sig)); static void stop __P((struct proc *)); static int filt_sigattach(struct knote *kn); static void filt_sigdetach(struct knote *kn); static int filt_signal(struct knote *kn, long hint); struct filterops sig_filtops = { 0, filt_sigattach, filt_sigdetach, filt_signal }; static int kern_logsigexit = 1; SYSCTL_INT(_kern, KERN_LOGSIGEXIT, logsigexit, CTLFLAG_RW, &kern_logsigexit, 0, "Log processes quitting on abnormal signals to syslog(3)"); /* * Can process p, with pcred pc, send the signal sig to process q? */ #define CANSIGNAL(p, q, sig) \ (!p_trespass(p, q) || \ ((sig) == SIGCONT && (q)->p_session == (p)->p_session)) /* * Policy -- Can real uid ruid with ucred uc send a signal to process q? */ #define CANSIGIO(ruid, uc, q) \ ((uc)->cr_uid == 0 || \ (ruid) == (q)->p_cred->p_ruid || \ (uc)->cr_uid == (q)->p_cred->p_ruid || \ (ruid) == (q)->p_ucred->cr_uid || \ (uc)->cr_uid == (q)->p_ucred->cr_uid) int sugid_coredump; SYSCTL_INT(_kern, OID_AUTO, sugid_coredump, CTLFLAG_RW, &sugid_coredump, 0, "Enable coredumping set user/group ID processes"); static int do_coredump = 1; SYSCTL_INT(_kern, OID_AUTO, coredump, CTLFLAG_RW, &do_coredump, 0, "Enable/Disable coredumps"); /* * Signal properties and actions. * The array below categorizes the signals and their default actions * according to the following properties: */ #define SA_KILL 0x01 /* terminates process by default */ #define SA_CORE 0x02 /* ditto and coredumps */ #define SA_STOP 0x04 /* suspend process */ #define SA_TTYSTOP 0x08 /* ditto, from tty */ #define SA_IGNORE 0x10 /* ignore by default */ #define SA_CONT 0x20 /* continue if suspended */ #define SA_CANTMASK 0x40 /* non-maskable, catchable */ static int sigproptbl[NSIG] = { SA_KILL, /* SIGHUP */ SA_KILL, /* SIGINT */ SA_KILL|SA_CORE, /* SIGQUIT */ SA_KILL|SA_CORE, /* SIGILL */ SA_KILL|SA_CORE, /* SIGTRAP */ SA_KILL|SA_CORE, /* SIGABRT */ SA_KILL|SA_CORE, /* SIGEMT */ SA_KILL|SA_CORE, /* SIGFPE */ SA_KILL, /* SIGKILL */ SA_KILL|SA_CORE, /* SIGBUS */ SA_KILL|SA_CORE, /* SIGSEGV */ SA_KILL|SA_CORE, /* SIGSYS */ SA_KILL, /* SIGPIPE */ SA_KILL, /* SIGALRM */ SA_KILL, /* SIGTERM */ SA_IGNORE, /* SIGURG */ SA_STOP, /* SIGSTOP */ SA_STOP|SA_TTYSTOP, /* SIGTSTP */ SA_IGNORE|SA_CONT, /* SIGCONT */ SA_IGNORE, /* SIGCHLD */ SA_STOP|SA_TTYSTOP, /* SIGTTIN */ SA_STOP|SA_TTYSTOP, /* SIGTTOU */ SA_IGNORE, /* SIGIO */ SA_KILL, /* SIGXCPU */ SA_KILL, /* SIGXFSZ */ SA_KILL, /* SIGVTALRM */ SA_KILL, /* SIGPROF */ SA_IGNORE, /* SIGWINCH */ SA_IGNORE, /* SIGINFO */ SA_KILL, /* SIGUSR1 */ SA_KILL, /* SIGUSR2 */ }; static __inline int sigprop(int sig) { if (sig > 0 && sig < NSIG) return (sigproptbl[_SIG_IDX(sig)]); return (0); } static __inline int sig_ffs(sigset_t *set) { int i; for (i = 0; i < _SIG_WORDS; i++) if (set->__bits[i]) return (ffs(set->__bits[i]) + (i * 32)); return (0); } /* * do_sigaction * sigaction * osigaction */ static int do_sigaction(p, sig, act, oact, old) struct proc *p; register int sig; struct sigaction *act, *oact; int old; { register struct sigacts *ps = p->p_sigacts; if (sig <= 0 || sig > _SIG_MAXSIG) return (EINVAL); if (oact) { oact->sa_handler = ps->ps_sigact[_SIG_IDX(sig)]; oact->sa_mask = ps->ps_catchmask[_SIG_IDX(sig)]; oact->sa_flags = 0; if (SIGISMEMBER(ps->ps_sigonstack, sig)) oact->sa_flags |= SA_ONSTACK; if (!SIGISMEMBER(ps->ps_sigintr, sig)) oact->sa_flags |= SA_RESTART; if (SIGISMEMBER(ps->ps_sigreset, sig)) oact->sa_flags |= SA_RESETHAND; if (SIGISMEMBER(ps->ps_signodefer, sig)) oact->sa_flags |= SA_NODEFER; if (SIGISMEMBER(ps->ps_siginfo, sig)) oact->sa_flags |= SA_SIGINFO; if (sig == SIGCHLD && p->p_procsig->ps_flag & PS_NOCLDSTOP) oact->sa_flags |= SA_NOCLDSTOP; if (sig == SIGCHLD && p->p_procsig->ps_flag & PS_NOCLDWAIT) oact->sa_flags |= SA_NOCLDWAIT; } if (act) { if ((sig == SIGKILL || sig == SIGSTOP) && act->sa_handler != SIG_DFL) return (EINVAL); /* * Change setting atomically. */ (void) splhigh(); ps->ps_catchmask[_SIG_IDX(sig)] = act->sa_mask; SIG_CANTMASK(ps->ps_catchmask[_SIG_IDX(sig)]); if (act->sa_flags & SA_SIGINFO) { ps->ps_sigact[_SIG_IDX(sig)] = act->sa_handler; SIGADDSET(ps->ps_siginfo, sig); } else { ps->ps_sigact[_SIG_IDX(sig)] = (__sighandler_t *)act->sa_sigaction; SIGDELSET(ps->ps_siginfo, sig); } if (!(act->sa_flags & SA_RESTART)) SIGADDSET(ps->ps_sigintr, sig); else SIGDELSET(ps->ps_sigintr, sig); if (act->sa_flags & SA_ONSTACK) SIGADDSET(ps->ps_sigonstack, sig); else SIGDELSET(ps->ps_sigonstack, sig); if (act->sa_flags & SA_RESETHAND) SIGADDSET(ps->ps_sigreset, sig); else SIGDELSET(ps->ps_sigreset, sig); if (act->sa_flags & SA_NODEFER) SIGADDSET(ps->ps_signodefer, sig); else SIGDELSET(ps->ps_signodefer, sig); #ifdef COMPAT_SUNOS if (act->sa_flags & SA_USERTRAMP) SIGADDSET(ps->ps_usertramp, sig); else SIGDELSET(ps->ps_usertramp, seg); #endif if (sig == SIGCHLD) { if (act->sa_flags & SA_NOCLDSTOP) p->p_procsig->ps_flag |= PS_NOCLDSTOP; else p->p_procsig->ps_flag &= ~PS_NOCLDSTOP; if (act->sa_flags & SA_NOCLDWAIT) { /* * Paranoia: since SA_NOCLDWAIT is implemented * by reparenting the dying child to PID 1 (and * trust it to reap the zombie), PID 1 itself * is forbidden to set SA_NOCLDWAIT. */ if (p->p_pid == 1) p->p_procsig->ps_flag &= ~PS_NOCLDWAIT; else p->p_procsig->ps_flag |= PS_NOCLDWAIT; } else p->p_procsig->ps_flag &= ~PS_NOCLDWAIT; } /* * Set bit in p_sigignore for signals that are set to SIG_IGN, * and for signals set to SIG_DFL where the default is to * ignore. However, don't put SIGCONT in p_sigignore, as we * have to restart the process. */ if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || (sigprop(sig) & SA_IGNORE && ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL)) { /* never to be seen again */ SIGDELSET(p->p_siglist, sig); if (sig != SIGCONT) /* easier in psignal */ SIGADDSET(p->p_sigignore, sig); SIGDELSET(p->p_sigcatch, sig); } else { SIGDELSET(p->p_sigignore, sig); if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL) SIGDELSET(p->p_sigcatch, sig); else SIGADDSET(p->p_sigcatch, sig); } if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL || !old) SIGDELSET(ps->ps_osigset, sig); else SIGADDSET(ps->ps_osigset, sig); (void) spl0(); } return (0); } #ifndef _SYS_SYSPROTO_H_ struct sigaction_args { int sig; struct sigaction *act; struct sigaction *oact; }; #endif /* ARGSUSED */ int sigaction(p, uap) struct proc *p; register struct sigaction_args *uap; { struct sigaction act, oact; register struct sigaction *actp, *oactp; int error; actp = (uap->act != NULL) ? &act : NULL; oactp = (uap->oact != NULL) ? &oact : NULL; if (actp) { error = copyin(uap->act, actp, sizeof(act)); if (error) return (error); } error = do_sigaction(p, uap->sig, actp, oactp, 0); if (oactp && !error) { error = copyout(oactp, uap->oact, sizeof(oact)); } return (error); } #ifndef _SYS_SYSPROTO_H_ struct osigaction_args { int signum; struct osigaction *nsa; struct osigaction *osa; }; #endif /* ARGSUSED */ int osigaction(p, uap) struct proc *p; register struct osigaction_args *uap; { struct osigaction sa; struct sigaction nsa, osa; register struct sigaction *nsap, *osap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); nsap = (uap->nsa != NULL) ? &nsa : NULL; osap = (uap->osa != NULL) ? &osa : NULL; if (nsap) { error = copyin(uap->nsa, &sa, sizeof(sa)); if (error) return (error); nsap->sa_handler = sa.sa_handler; nsap->sa_flags = sa.sa_flags; OSIG2SIG(sa.sa_mask, nsap->sa_mask); } error = do_sigaction(p, uap->signum, nsap, osap, 1); if (osap && !error) { sa.sa_handler = osap->sa_handler; sa.sa_flags = osap->sa_flags; SIG2OSIG(osap->sa_mask, sa.sa_mask); error = copyout(&sa, uap->osa, sizeof(sa)); } return (error); } /* * Initialize signal state for process 0; * set to ignore signals that are ignored by default. */ void siginit(p) struct proc *p; { register int i; for (i = 1; i <= NSIG; i++) if (sigprop(i) & SA_IGNORE && i != SIGCONT) SIGADDSET(p->p_sigignore, i); } /* * Reset signals for an exec of the specified process. */ void execsigs(p) register struct proc *p; { register struct sigacts *ps = p->p_sigacts; register int sig; /* * Reset caught signals. Held signals remain held * through p_sigmask (unless they were caught, * and are now ignored by default). */ while (SIGNOTEMPTY(p->p_sigcatch)) { sig = sig_ffs(&p->p_sigcatch); SIGDELSET(p->p_sigcatch, sig); if (sigprop(sig) & SA_IGNORE) { if (sig != SIGCONT) SIGADDSET(p->p_sigignore, sig); SIGDELSET(p->p_siglist, sig); } ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } /* * Reset stack state to the user stack. * Clear set of signals caught on the signal stack. */ p->p_sigstk.ss_flags = SS_DISABLE; p->p_sigstk.ss_size = 0; p->p_sigstk.ss_sp = 0; /* * Reset no zombies if child dies flag as Solaris does. */ p->p_procsig->ps_flag &= ~PS_NOCLDWAIT; } /* * do_sigprocmask() - MP SAFE ONLY IF p == curproc * * Manipulate signal mask. This routine is MP SAFE *ONLY* if * p == curproc. Also remember that in order to remain MP SAFE * no spl*() calls may be made. */ static int do_sigprocmask(p, how, set, oset, old) struct proc *p; int how; sigset_t *set, *oset; int old; { int error; if (oset != NULL) *oset = p->p_sigmask; error = 0; if (set != NULL) { switch (how) { case SIG_BLOCK: SIG_CANTMASK(*set); SIGSETOR(p->p_sigmask, *set); break; case SIG_UNBLOCK: SIGSETNAND(p->p_sigmask, *set); break; case SIG_SETMASK: SIG_CANTMASK(*set); if (old) SIGSETLO(p->p_sigmask, *set); else p->p_sigmask = *set; break; default: error = EINVAL; break; } } return (error); } /* * sigprocmask() - MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct sigprocmask_args { int how; const sigset_t *set; sigset_t *oset; }; #endif int sigprocmask(p, uap) register struct proc *p; struct sigprocmask_args *uap; { sigset_t set, oset; sigset_t *setp, *osetp; int error; setp = (uap->set != NULL) ? &set : NULL; osetp = (uap->oset != NULL) ? &oset : NULL; if (setp) { error = copyin(uap->set, setp, sizeof(set)); if (error) return (error); } error = do_sigprocmask(p, uap->how, setp, osetp, 0); if (osetp && !error) { error = copyout(osetp, uap->oset, sizeof(oset)); } return (error); } /* * osigprocmask() - MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct osigprocmask_args { int how; osigset_t mask; }; #endif int osigprocmask(p, uap) register struct proc *p; struct osigprocmask_args *uap; { sigset_t set, oset; int error; OSIG2SIG(uap->mask, set); error = do_sigprocmask(p, uap->how, &set, &oset, 1); SIG2OSIG(oset, p->p_retval[0]); return (error); } #ifndef _SYS_SYSPROTO_H_ struct sigpending_args { sigset_t *set; }; #endif /* ARGSUSED */ int sigpending(p, uap) struct proc *p; struct sigpending_args *uap; { return (copyout(&p->p_siglist, uap->set, sizeof(sigset_t))); } #ifndef _SYS_SYSPROTO_H_ struct osigpending_args { int dummy; }; #endif /* ARGSUSED */ int osigpending(p, uap) struct proc *p; struct osigpending_args *uap; { SIG2OSIG(p->p_siglist, p->p_retval[0]); return (0); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Generalized interface signal handler, 4.3-compatible. */ #ifndef _SYS_SYSPROTO_H_ struct osigvec_args { int signum; struct sigvec *nsv; struct sigvec *osv; }; #endif /* ARGSUSED */ int osigvec(p, uap) struct proc *p; register struct osigvec_args *uap; { struct sigvec vec; struct sigaction nsa, osa; register struct sigaction *nsap, *osap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); nsap = (uap->nsv != NULL) ? &nsa : NULL; osap = (uap->osv != NULL) ? &osa : NULL; if (nsap) { error = copyin(uap->nsv, &vec, sizeof(vec)); if (error) return (error); nsap->sa_handler = vec.sv_handler; OSIG2SIG(vec.sv_mask, nsap->sa_mask); nsap->sa_flags = vec.sv_flags; nsap->sa_flags ^= SA_RESTART; /* opposite of SV_INTERRUPT */ #ifdef COMPAT_SUNOS nsap->sa_flags |= SA_USERTRAMP; #endif } error = do_sigaction(p, uap->signum, nsap, osap, 1); if (osap && !error) { vec.sv_handler = osap->sa_handler; SIG2OSIG(osap->sa_mask, vec.sv_mask); vec.sv_flags = osap->sa_flags; vec.sv_flags &= ~SA_NOCLDWAIT; vec.sv_flags ^= SA_RESTART; #ifdef COMPAT_SUNOS vec.sv_flags &= ~SA_NOCLDSTOP; #endif error = copyout(&vec, uap->osv, sizeof(vec)); } return (error); } #ifndef _SYS_SYSPROTO_H_ struct osigblock_args { int mask; }; #endif int osigblock(p, uap) register struct proc *p; struct osigblock_args *uap; { sigset_t set; OSIG2SIG(uap->mask, set); SIG_CANTMASK(set); (void) splhigh(); SIG2OSIG(p->p_sigmask, p->p_retval[0]); SIGSETOR(p->p_sigmask, set); (void) spl0(); return (0); } #ifndef _SYS_SYSPROTO_H_ struct osigsetmask_args { int mask; }; #endif int osigsetmask(p, uap) struct proc *p; struct osigsetmask_args *uap; { sigset_t set; OSIG2SIG(uap->mask, set); SIG_CANTMASK(set); (void) splhigh(); SIG2OSIG(p->p_sigmask, p->p_retval[0]); SIGSETLO(p->p_sigmask, set); (void) spl0(); return (0); } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Suspend process until signal, providing mask to be set * in the meantime. Note nonstandard calling convention: * libc stub passes mask, not pointer, to save a copyin. */ #ifndef _SYS_SYSPROTO_H_ struct sigsuspend_args { const sigset_t *sigmask; }; #endif /* ARGSUSED */ int sigsuspend(p, uap) register struct proc *p; struct sigsuspend_args *uap; { sigset_t mask; register struct sigacts *ps = p->p_sigacts; int error; error = copyin(uap->sigmask, &mask, sizeof(mask)); if (error) return (error); /* * When returning from sigsuspend, we want * the old mask to be restored after the * signal handler has finished. Thus, we * save it here and mark the sigacts structure * to indicate this. */ p->p_oldsigmask = p->p_sigmask; p->p_flag |= P_OLDMASK; SIG_CANTMASK(mask); p->p_sigmask = mask; while (tsleep((caddr_t) ps, PPAUSE|PCATCH, "pause", 0) == 0) /* void */; /* always return EINTR rather than ERESTART... */ return (EINTR); } #ifndef _SYS_SYSPROTO_H_ struct osigsuspend_args { osigset_t mask; }; #endif /* ARGSUSED */ int osigsuspend(p, uap) register struct proc *p; struct osigsuspend_args *uap; { sigset_t mask; register struct sigacts *ps = p->p_sigacts; p->p_oldsigmask = p->p_sigmask; p->p_flag |= P_OLDMASK; OSIG2SIG(uap->mask, mask); SIG_CANTMASK(mask); SIGSETLO(p->p_sigmask, mask); while (tsleep((caddr_t) ps, PPAUSE|PCATCH, "opause", 0) == 0) /* void */; /* always return EINTR rather than ERESTART... */ return (EINTR); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) #ifndef _SYS_SYSPROTO_H_ struct osigstack_args { struct sigstack *nss; struct sigstack *oss; }; #endif /* ARGSUSED */ int osigstack(p, uap) struct proc *p; register struct osigstack_args *uap; { struct sigstack ss; int error = 0; ss.ss_sp = p->p_sigstk.ss_sp; ss.ss_onstack = p->p_sigstk.ss_flags & SS_ONSTACK; if (uap->oss && (error = copyout(&ss, uap->oss, sizeof(struct sigstack)))) return (error); if (uap->nss && (error = copyin(uap->nss, &ss, sizeof(ss))) == 0) { p->p_sigstk.ss_sp = ss.ss_sp; p->p_sigstk.ss_size = 0; p->p_sigstk.ss_flags |= ss.ss_onstack & SS_ONSTACK; p->p_flag |= P_ALTSTACK; } return (error); } #endif /* COMPAT_43 || COMPAT_SUNOS */ #ifndef _SYS_SYSPROTO_H_ struct sigaltstack_args { stack_t *ss; stack_t *oss; }; #endif /* ARGSUSED */ int sigaltstack(p, uap) struct proc *p; register struct sigaltstack_args *uap; { stack_t ss; int error; if ((p->p_flag & P_ALTSTACK) == 0) p->p_sigstk.ss_flags |= SS_DISABLE; if (uap->oss && (error = copyout(&p->p_sigstk, uap->oss, sizeof(stack_t)))) return (error); if (uap->ss == 0) return (0); if ((error = copyin(uap->ss, &ss, sizeof(ss)))) return (error); if (ss.ss_flags & SS_DISABLE) { if (p->p_sigstk.ss_flags & SS_ONSTACK) return (EINVAL); p->p_flag &= ~P_ALTSTACK; p->p_sigstk.ss_flags = ss.ss_flags; return (0); } if (ss.ss_size < MINSIGSTKSZ) return (ENOMEM); p->p_flag |= P_ALTSTACK; p->p_sigstk = ss; return (0); } /* * Common code for kill process group/broadcast kill. * cp is calling process. */ int killpg1(cp, sig, pgid, all) register struct proc *cp; int sig, pgid, all; { register struct proc *p; struct pgrp *pgrp; int nfound = 0; if (all) /* * broadcast */ LIST_FOREACH(p, &allproc, p_list) { if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || p == cp || !CANSIGNAL(cp, p, sig)) continue; nfound++; if (sig) psignal(p, sig); } else { if (pgid == 0) /* * zero pgid means send to my process group. */ pgrp = cp->p_pgrp; else { pgrp = pgfind(pgid); if (pgrp == NULL) return (ESRCH); } LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || p->p_stat == SZOMB || !CANSIGNAL(cp, p, sig)) continue; nfound++; if (sig) psignal(p, sig); } } return (nfound ? 0 : ESRCH); } #ifndef _SYS_SYSPROTO_H_ struct kill_args { int pid; int signum; }; #endif /* ARGSUSED */ int kill(cp, uap) register struct proc *cp; register struct kill_args *uap; { register struct proc *p; if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); if (uap->pid > 0) { /* kill single process */ if ((p = pfind(uap->pid)) == NULL) return (ESRCH); if (!CANSIGNAL(cp, p, uap->signum)) return (EPERM); if (uap->signum) psignal(p, uap->signum); return (0); } switch (uap->pid) { case -1: /* broadcast signal */ return (killpg1(cp, uap->signum, 0, 1)); case 0: /* signal own process group */ return (killpg1(cp, uap->signum, 0, 0)); default: /* negative explicit process group */ return (killpg1(cp, uap->signum, -uap->pid, 0)); } /* NOTREACHED */ } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) #ifndef _SYS_SYSPROTO_H_ struct okillpg_args { int pgid; int signum; }; #endif /* ARGSUSED */ int okillpg(p, uap) struct proc *p; register struct okillpg_args *uap; { if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); return (killpg1(p, uap->signum, uap->pgid, 0)); } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Send a signal to a process group. */ void gsignal(pgid, sig) int pgid, sig; { struct pgrp *pgrp; if (pgid && (pgrp = pgfind(pgid))) pgsignal(pgrp, sig, 0); } /* * Send a signal to a process group. If checktty is 1, * limit to members which have a controlling terminal. */ void pgsignal(pgrp, sig, checkctty) struct pgrp *pgrp; int sig, checkctty; { register struct proc *p; if (pgrp) LIST_FOREACH(p, &pgrp->pg_members, p_pglist) if (checkctty == 0 || p->p_flag & P_CONTROLT) psignal(p, sig); } /* * Send a signal caused by a trap to the current process. * If it will be caught immediately, deliver it with correct code. * Otherwise, post it normally. */ void trapsignal(p, sig, code) struct proc *p; register int sig; u_long code; { register struct sigacts *ps = p->p_sigacts; if ((p->p_flag & P_TRACED) == 0 && SIGISMEMBER(p->p_sigcatch, sig) && SIGISMEMBER(p->p_sigmask, sig)) { p->p_stats->p_ru.ru_nsignals++; #ifdef KTRACE if (KTRPOINT(p, KTR_PSIG)) ktrpsig(p->p_tracep, sig, ps->ps_sigact[_SIG_IDX(sig)], &p->p_sigmask, code); #endif (*p->p_sysent->sv_sendsig)(ps->ps_sigact[_SIG_IDX(sig)], sig, &p->p_sigmask, code); SIGSETOR(p->p_sigmask, ps->ps_catchmask[_SIG_IDX(sig)]); if (!SIGISMEMBER(ps->ps_signodefer, sig)) SIGADDSET(p->p_sigmask, sig); if (SIGISMEMBER(ps->ps_sigreset, sig)) { /* * See do_sigaction() for origin of this code. */ SIGDELSET(p->p_sigcatch, sig); if (sig != SIGCONT && sigprop(sig) & SA_IGNORE) SIGADDSET(p->p_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } } else { p->p_code = code; /* XXX for core dump/debugger */ p->p_sig = sig; /* XXX to verify code */ psignal(p, sig); } } /* * Send the signal to the process. If the signal has an action, the action * is usually performed by the target process rather than the caller; we add * the signal to the set of pending signals for the process. * * Exceptions: * o When a stop signal is sent to a sleeping process that takes the * default action, the process is stopped without awakening it. * o SIGCONT restarts stopped processes (or puts them back to sleep) * regardless of the signal action (eg, blocked or ignored). * * Other ignored signals are discarded immediately. */ void psignal(p, sig) register struct proc *p; register int sig; { register int s, prop; register sig_t action; if (sig > _SIG_MAXSIG || sig <= 0) { printf("psignal: signal %d\n", sig); panic("psignal signal number"); } KNOTE(&p->p_klist, NOTE_SIGNAL | sig); prop = sigprop(sig); /* * If proc is traced, always give parent a chance; * if signal event is tracked by procfs, give *that* * a chance, as well. */ if ((p->p_flag & P_TRACED) || (p->p_stops & S_SIG)) action = SIG_DFL; else { /* * If the signal is being ignored, * then we forget about it immediately. * (Note: we don't set SIGCONT in p_sigignore, * and if it is set to SIG_IGN, * action will be SIG_DFL here.) */ if (SIGISMEMBER(p->p_sigignore, sig) || (p->p_flag & P_WEXIT)) return; if (SIGISMEMBER(p->p_sigmask, sig)) action = SIG_HOLD; else if (SIGISMEMBER(p->p_sigcatch, sig)) action = SIG_CATCH; else action = SIG_DFL; } if (p->p_nice > NZERO && action == SIG_DFL && (prop & SA_KILL) && (p->p_flag & P_TRACED) == 0) p->p_nice = NZERO; if (prop & SA_CONT) SIG_STOPSIGMASK(p->p_siglist); if (prop & SA_STOP) { /* * If sending a tty stop signal to a member of an orphaned * process group, discard the signal here if the action * is default; don't stop the process below if sleeping, * and don't clear any pending SIGCONT. */ if (prop & SA_TTYSTOP && p->p_pgrp->pg_jobc == 0 && action == SIG_DFL) return; SIG_CONTSIGMASK(p->p_siglist); } SIGADDSET(p->p_siglist, sig); /* * Defer further processing for signals which are held, * except that stopped processes must be continued by SIGCONT. */ if (action == SIG_HOLD && (!(prop & SA_CONT) || p->p_stat != SSTOP)) return; s = splhigh(); switch (p->p_stat) { case SSLEEP: /* * If process is sleeping uninterruptibly * we can't interrupt the sleep... the signal will * be noticed when the process returns through * trap() or syscall(). */ if ((p->p_flag & P_SINTR) == 0) goto out; /* * Process is sleeping and traced... make it runnable * so it can discover the signal in issignal() and stop * for the parent. */ if (p->p_flag & P_TRACED) goto run; /* * If SIGCONT is default (or ignored) and process is * asleep, we are finished; the process should not * be awakened. */ if ((prop & SA_CONT) && action == SIG_DFL) { SIGDELSET(p->p_siglist, sig); goto out; } /* * When a sleeping process receives a stop * signal, process immediately if possible. * All other (caught or default) signals * cause the process to run. */ if (prop & SA_STOP) { if (action != SIG_DFL) goto runfast; /* * If a child holding parent blocked, * stopping could cause deadlock. */ if (p->p_flag & P_PPWAIT) goto out; SIGDELSET(p->p_siglist, sig); p->p_xstat = sig; if ((p->p_pptr->p_procsig->ps_flag & PS_NOCLDSTOP) == 0) psignal(p->p_pptr, SIGCHLD); stop(p); goto out; } else goto runfast; /*NOTREACHED*/ case SSTOP: /* * If traced process is already stopped, * then no further action is necessary. */ if (p->p_flag & P_TRACED) goto out; /* * Kill signal always sets processes running. */ if (sig == SIGKILL) goto runfast; if (prop & SA_CONT) { /* * If SIGCONT is default (or ignored), we continue the * process but don't leave the signal in p_siglist, as * it has no further action. If SIGCONT is held, we * continue the process and leave the signal in * p_siglist. If the process catches SIGCONT, let it * handle the signal itself. If it isn't waiting on * an event, then it goes back to run state. * Otherwise, process goes back to sleep state. */ if (action == SIG_DFL) SIGDELSET(p->p_siglist, sig); if (action == SIG_CATCH) goto runfast; if (p->p_wchan == 0) goto run; p->p_stat = SSLEEP; goto out; } if (prop & SA_STOP) { /* * Already stopped, don't need to stop again. * (If we did the shell could get confused.) */ SIGDELSET(p->p_siglist, sig); goto out; } /* * If process is sleeping interruptibly, then simulate a * wakeup so that when it is continued, it will be made * runnable and can look at the signal. But don't make * the process runnable, leave it stopped. */ if (p->p_wchan && p->p_flag & P_SINTR) unsleep(p); goto out; default: /* * SRUN, SIDL, SZOMB do nothing with the signal, * other than kicking ourselves if we are running. * It will either never be noticed, or noticed very soon. */ if (p == curproc) signotify(p); #ifdef SMP else if (p->p_stat == SRUN) forward_signal(p); #endif goto out; } /*NOTREACHED*/ runfast: /* * Raise priority to at least PUSER. */ if (p->p_priority > PUSER) p->p_priority = PUSER; run: setrunnable(p); out: splx(s); } /* * If the current process has received a signal (should be caught or cause * termination, should interrupt current syscall), return the signal number. * Stop signals with default action are processed immediately, then cleared; * they aren't returned. This is checked after each entry to the system for * a syscall or trap (though this can usually be done without calling issignal * by checking the pending signal masks in the CURSIG macro.) The normal call * sequence is * * while (sig = CURSIG(curproc)) * postsig(sig); */ int issignal(p) register struct proc *p; { sigset_t mask; register int sig, prop; for (;;) { int traced = (p->p_flag & P_TRACED) || (p->p_stops & S_SIG); mask = p->p_siglist; SIGSETNAND(mask, p->p_sigmask); if (p->p_flag & P_PPWAIT) SIG_STOPSIGMASK(mask); if (!SIGNOTEMPTY(mask)) /* no signal to send */ return (0); sig = sig_ffs(&mask); prop = sigprop(sig); STOPEVENT(p, S_SIG, sig); /* * We should see pending but ignored signals * only if P_TRACED was on when they were posted. */ if (SIGISMEMBER(p->p_sigignore, sig) && (traced == 0)) { SIGDELSET(p->p_siglist, sig); continue; } if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) { /* * If traced, always stop, and stay * stopped until released by the parent. */ p->p_xstat = sig; psignal(p->p_pptr, SIGCHLD); do { stop(p); mi_switch(); } while (!trace_req(p) && p->p_flag & P_TRACED); /* * If the traced bit got turned off, go back up * to the top to rescan signals. This ensures * that p_sig* and ps_sigact are consistent. */ if ((p->p_flag & P_TRACED) == 0) continue; /* * If parent wants us to take the signal, * then it will leave it in p->p_xstat; * otherwise we just look for signals again. */ SIGDELSET(p->p_siglist, sig); /* clear old signal */ sig = p->p_xstat; if (sig == 0) continue; /* * Put the new signal into p_siglist. If the * signal is being masked, look for other signals. */ SIGADDSET(p->p_siglist, sig); if (SIGISMEMBER(p->p_sigmask, sig)) continue; } /* * Decide whether the signal should be returned. * Return the signal's number, or fall through * to clear it from the pending mask. */ switch ((int)(intptr_t)p->p_sigacts->ps_sigact[_SIG_IDX(sig)]) { case (int)SIG_DFL: /* * Don't take default actions on system processes. */ if (p->p_pid <= 1) { #ifdef DIAGNOSTIC /* * Are you sure you want to ignore SIGSEGV * in init? XXX */ printf("Process (pid %lu) got signal %d\n", (u_long)p->p_pid, sig); #endif break; /* == ignore */ } /* * If there is a pending stop signal to process * with default action, stop here, * then clear the signal. However, * if process is member of an orphaned * process group, ignore tty stop signals. */ if (prop & SA_STOP) { if (p->p_flag & P_TRACED || (p->p_pgrp->pg_jobc == 0 && prop & SA_TTYSTOP)) break; /* == ignore */ p->p_xstat = sig; stop(p); if ((p->p_pptr->p_procsig->ps_flag & PS_NOCLDSTOP) == 0) psignal(p->p_pptr, SIGCHLD); mi_switch(); break; } else if (prop & SA_IGNORE) { /* * Except for SIGCONT, shouldn't get here. * Default action is to ignore; drop it. */ break; /* == ignore */ } else return (sig); /*NOTREACHED*/ case (int)SIG_IGN: /* * Masking above should prevent us ever trying * to take action on an ignored signal other * than SIGCONT, unless process is traced. */ if ((prop & SA_CONT) == 0 && (p->p_flag & P_TRACED) == 0) printf("issignal\n"); break; /* == ignore */ default: /* * This signal has an action, let * postsig() process it. */ return (sig); } SIGDELSET(p->p_siglist, sig); /* take the signal! */ } /* NOTREACHED */ } /* * Put the argument process into the stopped state and notify the parent * via wakeup. Signals are handled elsewhere. The process must not be * on the run queue. */ void stop(p) register struct proc *p; { p->p_stat = SSTOP; p->p_flag &= ~P_WAITED; wakeup((caddr_t)p->p_pptr); } /* * Take the action for the specified signal * from the current set of pending signals. */ void postsig(sig) register int sig; { register struct proc *p = curproc; struct sigacts *ps = p->p_sigacts; sig_t action; sigset_t returnmask; int code; KASSERT(sig != 0, ("postsig")); SIGDELSET(p->p_siglist, sig); action = ps->ps_sigact[_SIG_IDX(sig)]; #ifdef KTRACE if (KTRPOINT(p, KTR_PSIG)) ktrpsig(p->p_tracep, sig, action, p->p_flag & P_OLDMASK ? &p->p_oldsigmask : &p->p_sigmask, 0); #endif STOPEVENT(p, S_SIG, sig); if (action == SIG_DFL) { /* * Default action, where the default is to kill * the process. (Other cases were ignored above.) */ sigexit(p, sig); /* NOTREACHED */ } else { /* * If we get here, the signal must be caught. */ KASSERT(action != SIG_IGN && !SIGISMEMBER(p->p_sigmask, sig), ("postsig action")); /* * Set the new mask value and also defer further * occurrences of this signal. * * Special case: user has done a sigsuspend. Here the * current mask is not of interest, but rather the * mask from before the sigsuspend is what we want * restored after the signal processing is completed. */ (void) splhigh(); if (p->p_flag & P_OLDMASK) { returnmask = p->p_oldsigmask; p->p_flag &= ~P_OLDMASK; } else returnmask = p->p_sigmask; SIGSETOR(p->p_sigmask, ps->ps_catchmask[_SIG_IDX(sig)]); if (!SIGISMEMBER(ps->ps_signodefer, sig)) SIGADDSET(p->p_sigmask, sig); if (SIGISMEMBER(ps->ps_sigreset, sig)) { /* * See do_sigaction() for origin of this code. */ SIGDELSET(p->p_sigcatch, sig); if (sig != SIGCONT && sigprop(sig) & SA_IGNORE) SIGADDSET(p->p_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } (void) spl0(); p->p_stats->p_ru.ru_nsignals++; if (p->p_sig != sig) { code = 0; } else { code = p->p_code; p->p_code = 0; p->p_sig = 0; } (*p->p_sysent->sv_sendsig)(action, sig, &returnmask, code); } } /* * Kill the current process for stated reason. */ void killproc(p, why) struct proc *p; char *why; { log(LOG_ERR, "pid %d (%s), uid %d, was killed: %s\n", p->p_pid, p->p_comm, p->p_cred && p->p_ucred ? p->p_ucred->cr_uid : -1, why); psignal(p, SIGKILL); } /* * Force the current process to exit with the specified signal, dumping core * if appropriate. We bypass the normal tests for masked and caught signals, * allowing unrecoverable failures to terminate the process without changing * signal state. Mark the accounting record with the signal termination. * If dumping core, save the signal number for the debugger. Calls exit and * does not return. */ void sigexit(p, sig) register struct proc *p; int sig; { p->p_acflag |= AXSIG; if (sigprop(sig) & SA_CORE) { p->p_sig = sig; /* * Log signals which would cause core dumps * (Log as LOG_INFO to appease those who don't want * these messages.) * XXX : Todo, as well as euid, write out ruid too */ if (coredump(p) == 0) sig |= WCOREFLAG; if (kern_logsigexit) log(LOG_INFO, "pid %d (%s), uid %d: exited on signal %d%s\n", p->p_pid, p->p_comm, p->p_cred && p->p_ucred ? p->p_ucred->cr_uid : -1, sig &~ WCOREFLAG, sig & WCOREFLAG ? " (core dumped)" : ""); } exit1(p, W_EXITCODE(0, sig)); /* NOTREACHED */ } static char corefilename[MAXPATHLEN+1] = {"%N.core"}; SYSCTL_STRING(_kern, OID_AUTO, corefile, CTLFLAG_RW, corefilename, sizeof(corefilename), "process corefile name format string"); /* * expand_name(name, uid, pid) * Expand the name described in corefilename, using name, uid, and pid. * corefilename is a printf-like string, with three format specifiers: * %N name of process ("name") * %P process id (pid) * %U user id (uid) * For example, "%N.core" is the default; they can be disabled completely * by using "/dev/null", or all core files can be stored in "/cores/%U/%N-%P". * This is controlled by the sysctl variable kern.corefile (see above). */ static char * expand_name(name, uid, pid) const char *name; uid_t uid; pid_t pid; { char *temp; char buf[11]; /* Buffer for pid/uid -- max 4B */ int i, n; char *format = corefilename; size_t namelen; temp = malloc(MAXPATHLEN + 1, M_TEMP, M_NOWAIT); if (temp == NULL) return NULL; namelen = strlen(name); for (i = 0, n = 0; n < MAXPATHLEN && format[i]; i++) { int l; switch (format[i]) { case '%': /* Format character */ i++; switch (format[i]) { case '%': temp[n++] = '%'; break; case 'N': /* process name */ if ((n + namelen) > MAXPATHLEN) { log(LOG_ERR, "pid %d (%s), uid (%u): Path `%s%s' is too long\n", pid, name, uid, temp, name); free(temp, M_TEMP); return NULL; } memcpy(temp+n, name, namelen); n += namelen; break; case 'P': /* process id */ l = sprintf(buf, "%u", pid); if ((n + l) > MAXPATHLEN) { log(LOG_ERR, "pid %d (%s), uid (%u): Path `%s%s' is too long\n", pid, name, uid, temp, name); free(temp, M_TEMP); return NULL; } memcpy(temp+n, buf, l); n += l; break; case 'U': /* user id */ l = sprintf(buf, "%u", uid); if ((n + l) > MAXPATHLEN) { log(LOG_ERR, "pid %d (%s), uid (%u): Path `%s%s' is too long\n", pid, name, uid, temp, name); free(temp, M_TEMP); return NULL; } memcpy(temp+n, buf, l); n += l; break; default: log(LOG_ERR, "Unknown format character %c in `%s'\n", format[i], format); } break; default: temp[n++] = format[i]; } } temp[n] = '\0'; return temp; } /* * Dump a process' core. The main routine does some * policy checking, and creates the name of the coredump; * then it passes on a vnode and a size limit to the process-specific * coredump routine if there is one; if there _is not_ one, it returns * ENOSYS; otherwise it returns the error from the process-specific routine. */ static int coredump(p) register struct proc *p; { register struct vnode *vp; register struct ucred *cred = p->p_ucred; struct nameidata nd; struct vattr vattr; - int error, error1; + int error, error1, flags; char *name; /* name of corefile */ off_t limit; STOPEVENT(p, S_CORE, 0); if (((sugid_coredump == 0) && p->p_flag & P_SUGID) || do_coredump == 0) return (EFAULT); /* * Note that the bulk of limit checking is done after * the corefile is created. The exception is if the limit * for corefiles is 0, in which case we don't bother * creating the corefile at all. This layout means that * a corefile is truncated instead of not being created, * if it is larger than the limit. */ limit = p->p_rlimit[RLIMIT_CORE].rlim_cur; if (limit == 0) return 0; name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p); - error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR); + flags = O_CREAT | FWRITE | O_NOFOLLOW; + error = vn_open(&nd, &flags, S_IRUSR | S_IWUSR); free(name, M_TEMP); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; /* Don't dump to non-regular files or files with links. */ if (vp->v_type != VREG || VOP_GETATTR(vp, &vattr, cred, p) || vattr.va_nlink != 1) { error = EFAULT; goto out; } VATTR_NULL(&vattr); vattr.va_size = 0; VOP_LEASE(vp, p, cred, LEASE_WRITE); VOP_SETATTR(vp, &vattr, cred, p); p->p_acflag |= ACORE; error = p->p_sysent->sv_coredump ? p->p_sysent->sv_coredump(p, vp, limit) : ENOSYS; out: VOP_UNLOCK(vp, 0, p); error1 = vn_close(vp, FWRITE, cred, p); if (error == 0) error = error1; return (error); } /* * Nonexistent system call-- signal process (may want to handle it). * Flag error in case process won't see signal immediately (blocked or ignored). */ #ifndef _SYS_SYSPROTO_H_ struct nosys_args { int dummy; }; #endif /* ARGSUSED */ int nosys(p, args) struct proc *p; struct nosys_args *args; { psignal(p, SIGSYS); return (EINVAL); } /* * Send a signal to a SIGIO or SIGURG to a process or process group using * stored credentials rather than those of the current process. */ void pgsigio(sigio, sig, checkctty) struct sigio *sigio; int sig, checkctty; { if (sigio == NULL) return; if (sigio->sio_pgid > 0) { if (CANSIGIO(sigio->sio_ruid, sigio->sio_ucred, sigio->sio_proc)) psignal(sigio->sio_proc, sig); } else if (sigio->sio_pgid < 0) { struct proc *p; LIST_FOREACH(p, &sigio->sio_pgrp->pg_members, p_pglist) if (CANSIGIO(sigio->sio_ruid, sigio->sio_ucred, p) && (checkctty == 0 || (p->p_flag & P_CONTROLT))) psignal(p, sig); } } static int filt_sigattach(struct knote *kn) { struct proc *p = curproc; kn->kn_ptr.p_proc = p; kn->kn_flags |= EV_CLEAR; /* automatically set */ /* XXX lock the proc here while adding to the list? */ SLIST_INSERT_HEAD(&p->p_klist, kn, kn_selnext); return (0); } static void filt_sigdetach(struct knote *kn) { struct proc *p = kn->kn_ptr.p_proc; SLIST_REMOVE(&p->p_klist, kn, knote, kn_selnext); } /* * signal knotes are shared with proc knotes, so we apply a mask to * the hint in order to differentiate them from process hints. This * could be avoided by using a signal-specific knote list, but probably * isn't worth the trouble. */ static int filt_signal(struct knote *kn, long hint) { if (hint & NOTE_SIGNAL) { hint &= ~NOTE_SIGNAL; if (kn->kn_id == hint) kn->kn_data++; } return (kn->kn_data != 0); } Index: head/sys/kern/link_aout.c =================================================================== --- head/sys/kern/link_aout.c (revision 62549) +++ head/sys/kern/link_aout.c (revision 62550) @@ -1,550 +1,551 @@ /*- * Copyright (c) 1997-2000 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __alpha__ #define FREEBSD_AOUT 1 #include #include #include #include #include #include #include #include #include #include "linker_if.h" #ifndef __ELF__ #include #include #include #endif #include #include typedef struct aout_file { struct linker_file lf; /* Common fields */ int preloaded; /* Was this pre-loader */ char* address; /* Load address */ struct _dynamic* dynamic; /* Symbol table etc. */ } *aout_file_t; static int link_aout_link_preload(linker_class_t lc, const char* modname, linker_file_t*); static int link_aout_link_preload_finish(linker_file_t); static int link_aout_load_file(linker_class_t lc, const char*, linker_file_t*); static int link_aout_lookup_symbol(linker_file_t, const char*, c_linker_sym_t*); static int link_aout_symbol_values(linker_file_t file, c_linker_sym_t sym, linker_symval_t* symval); static int link_aout_search_symbol(linker_file_t lf, caddr_t value, c_linker_sym_t* sym, long* diffp); static void link_aout_unload_file(linker_file_t); static void link_aout_unload_preload(linker_file_t); static kobj_method_t link_aout_methods[] = { KOBJMETHOD(linker_lookup_symbol, link_aout_lookup_symbol), KOBJMETHOD(linker_symbol_values, link_aout_symbol_values), KOBJMETHOD(linker_search_symbol, link_aout_search_symbol), KOBJMETHOD(linker_unload, link_aout_unload_file), KOBJMETHOD(linker_load_file, link_aout_load_file), KOBJMETHOD(linker_link_preload, link_aout_link_preload), KOBJMETHOD(linker_link_preload_finish, link_aout_link_preload_finish), { 0, 0 } }; static struct linker_class link_aout_class = { "a.out", link_aout_methods, sizeof(struct aout_file) }; static int relocate_file(aout_file_t af); /* * The kernel symbol table starts here. */ extern struct _dynamic _DYNAMIC; static void link_aout_init(void* arg) { #ifndef __ELF__ struct _dynamic* dp = &_DYNAMIC; #endif linker_add_class(&link_aout_class); #ifndef __ELF__ if (dp) { aout_file_t af; linker_kernel_file = linker_make_file(kernelname, af, &link_aout_class); if (linker_kernel_file == NULL) panic("link_aout_init: Can't create linker structures for kernel"); af = (aout_file_t) linker_kernel_file; af->address = 0; af->dynamic = dp; linker_kernel_file->address = (caddr_t) KERNBASE; linker_kernel_file->size = -(long)linker_kernel_file->address; } #endif } SYSINIT(link_aout, SI_SUB_KLD, SI_ORDER_THIRD, link_aout_init, 0); static int link_aout_link_preload(linker_class_t lc, const char* filename, linker_file_t* result) { caddr_t modptr, baseptr; char *type; struct exec *ehdr; aout_file_t af; linker_file_t lf; /* Look to see if we have the module preloaded. */ modptr = preload_search_by_name(filename); if (modptr == NULL) return ENOENT; if (((type = (char *)preload_search_info(modptr, MODINFO_TYPE)) == NULL) || strcmp(type, "a.out module") || ((baseptr = preload_search_info(modptr, MODINFO_ADDR)) == NULL) || ((ehdr = (struct exec *)preload_search_info(modptr, MODINFO_METADATA | MODINFOMD_AOUTEXEC)) == NULL)) return(0); /* we can't handle this */ /* Register with kld */ lf = linker_make_file(filename, &link_aout_class); if (lf == NULL) { return(ENOMEM); } af = (aout_file_t) lf; /* Looks like we can handle this one */ filename = preload_search_info(modptr, MODINFO_NAME); af->preloaded = 1; af->address = baseptr; /* Assume _DYNAMIC is the first data item. */ af->dynamic = (struct _dynamic*)(af->address + ehdr->a_text); if (af->dynamic->d_version != LD_VERSION_BSD) { linker_file_unload(lf); return(0); /* we can't handle this */ } af->dynamic->d_un.d_sdt = (struct section_dispatch_table *) ((char *)af->dynamic->d_un.d_sdt + (vm_offset_t)af->address); lf->address = af->address; lf->size = ehdr->a_text + ehdr->a_data + ehdr->a_bss; *result = lf; return(0); } static int link_aout_link_preload_finish(linker_file_t lf) { aout_file_t af; int error; af = (aout_file_t) lf; error = relocate_file(af); if (error) { linker_file_unload(lf); return(error); } return(0); } static int link_aout_load_file(linker_class_t lc, const char* filename, linker_file_t* result) { struct nameidata nd; struct proc* p = curproc; /* XXX */ int error = 0; - int resid; + int resid, flags; struct exec header; aout_file_t af; linker_file_t lf = 0; NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, p); - error = vn_open(&nd, FREAD, 0); + flags = FREAD; + error = vn_open(&nd, &flags, 0); if (error) return error; NDFREE(&nd, NDF_ONLY_PNBUF); /* * Read the a.out header from the file. */ error = vn_rdwr(UIO_READ, nd.ni_vp, (void*) &header, sizeof header, 0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if (error) goto out; if (N_BADMAG(header) || !(N_GETFLAG(header) & EX_DYNAMIC)) goto out; /* * We have an a.out file, so make some space to read it in. */ lf = linker_make_file(filename, &link_aout_class); if (lf == NULL) { error = ENOMEM; goto out; } af = (aout_file_t) lf; af->address = malloc(header.a_text + header.a_data + header.a_bss, M_LINKER, M_WAITOK); /* * Read the text and data sections and zero the bss. */ error = vn_rdwr(UIO_READ, nd.ni_vp, (void*) af->address, header.a_text + header.a_data, 0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if (error) goto out; bzero(af->address + header.a_text + header.a_data, header.a_bss); /* * Assume _DYNAMIC is the first data item. */ af->dynamic = (struct _dynamic*) (af->address + header.a_text); if (af->dynamic->d_version != LD_VERSION_BSD) { error = ENOEXEC; goto out; } af->dynamic->d_un.d_sdt = (struct section_dispatch_table *) ((char *)af->dynamic->d_un.d_sdt + (vm_offset_t)af->address); lf->address = af->address; lf->size = header.a_text + header.a_data + header.a_bss; error = linker_load_dependancies(lf); if (error) goto out; error = relocate_file(af); if (error) goto out; *result = lf; out: if (error && lf) linker_file_unload(lf); VOP_UNLOCK(nd.ni_vp, 0, p); vn_close(nd.ni_vp, FREAD, p->p_ucred, p); return error; } static void link_aout_unload_file(linker_file_t file) { aout_file_t af = (aout_file_t) file; if (af->preloaded) { link_aout_unload_preload(file); return; } if (af->address) free(af->address, M_LINKER); } static void link_aout_unload_preload(linker_file_t file) { if (file->filename) preload_delete_name(file->filename); } /* * XXX i386 dependant. */ static long read_relocation(struct relocation_info* r, char* addr) { int length = r->r_length; if (length == 0) return *(u_char*) addr; else if (length == 1) return *(u_short*) addr; else if (length == 2) return *(u_int*) addr; else printf("link_aout: unsupported relocation size %d\n", r->r_length); return 0; } static void write_relocation(struct relocation_info* r, char* addr, long value) { int length = r->r_length; if (length == 0) *(u_char*) addr = value; else if (length == 1) *(u_short*) addr = value; else if (length == 2) *(u_int*) addr = value; else printf("link_aout: unsupported relocation size %d\n", r->r_length); } #define AOUT_RELOC(af, type, off) (type*) ((af)->address + (off)) static int relocate_file(aout_file_t af) { struct relocation_info* rel; struct relocation_info* erel; struct relocation_info* r; struct nzlist* symbolbase; char* stringbase; struct nzlist* np; char* sym; long relocation; rel = AOUT_RELOC(af, struct relocation_info, LD_REL(af->dynamic)); erel = AOUT_RELOC(af, struct relocation_info, LD_REL(af->dynamic) + LD_RELSZ(af->dynamic)); symbolbase = AOUT_RELOC(af, struct nzlist, LD_SYMBOL(af->dynamic)); stringbase = AOUT_RELOC(af, char, LD_STRINGS(af->dynamic)); for (r = rel; r < erel; r++) { char* addr; if (r->r_address == 0) break; addr = AOUT_RELOC(af, char, r->r_address); if (r->r_extern) { np = &symbolbase[r->r_symbolnum]; sym = &stringbase[np->nz_strx]; if (sym[0] != '_') { printf("link_aout: bad symbol name %s\n", sym); relocation = 0; } else relocation = (intptr_t) linker_file_lookup_symbol(&af->lf, sym + 1, np->nz_type != (N_SETV+N_EXT)); if (!relocation) { printf("link_aout: symbol %s not found\n", sym); return ENOENT; } relocation += read_relocation(r, addr); if (r->r_jmptable) { printf("link_aout: can't cope with jump table relocations\n"); continue; } if (r->r_pcrel) relocation -= (intptr_t) af->address; if (r->r_copy) { printf("link_aout: can't cope with copy relocations\n"); continue; } write_relocation(r, addr, relocation); } else { write_relocation(r, addr, (intptr_t)(read_relocation(r, addr) + af->address)); } } return 0; } static long symbol_hash_value(aout_file_t af, const char* name) { long hashval; const char* p; hashval = '_'; /* fake a starting '_' for C symbols */ for (p = name; *p; p++) hashval = (hashval << 1) + *p; return (hashval & 0x7fffffff) % LD_BUCKETS(af->dynamic); } int link_aout_lookup_symbol(linker_file_t file, const char* name, c_linker_sym_t* sym) { aout_file_t af = (aout_file_t) file; long hashval; struct rrs_hash* hashbase; struct nzlist* symbolbase; char* stringbase; struct rrs_hash* hp; struct nzlist* np; char* cp; if (LD_BUCKETS(af->dynamic) == 0) return 0; hashbase = AOUT_RELOC(af, struct rrs_hash, LD_HASH(af->dynamic)); symbolbase = AOUT_RELOC(af, struct nzlist, LD_SYMBOL(af->dynamic)); stringbase = AOUT_RELOC(af, char, LD_STRINGS(af->dynamic)); restart: hashval = symbol_hash_value(af, name); hp = &hashbase[hashval]; if (hp->rh_symbolnum == -1) return ENOENT; while (hp) { np = (struct nzlist *) &symbolbase[hp->rh_symbolnum]; cp = stringbase + np->nz_strx; /* * Note: we fake the leading '_' for C symbols. */ if (cp[0] == '_' && !strcmp(cp + 1, name)) break; if (hp->rh_next == 0) hp = NULL; else hp = &hashbase[hp->rh_next]; } if (hp == NULL) /* * Not found. */ return ENOENT; /* * Check for an aliased symbol, whatever that is. */ if (np->nz_type == N_INDR+N_EXT) { name = stringbase + (++np)->nz_strx + 1; /* +1 for '_' */ goto restart; } /* * Check this is an actual definition of the symbol. */ if (np->nz_value == 0) return ENOENT; if (np->nz_type == N_UNDF+N_EXT && np->nz_value != 0) { if (np->nz_other == AUX_FUNC) /* weak function */ return ENOENT; } *sym = (linker_sym_t) np; return 0; } static int link_aout_symbol_values(linker_file_t file, c_linker_sym_t sym, linker_symval_t* symval) { aout_file_t af = (aout_file_t) file; const struct nzlist* np = (const struct nzlist*) sym; char* stringbase; long numsym = LD_STABSZ(af->dynamic) / sizeof(struct nzlist); struct nzlist *symbase; /* Is it one of ours? It could be another module... */ symbase = AOUT_RELOC(af, struct nzlist, LD_SYMBOL(af->dynamic)); if (np < symbase) return ENOENT; if ((np - symbase) > numsym) return ENOENT; stringbase = AOUT_RELOC(af, char, LD_STRINGS(af->dynamic)); symval->name = stringbase + np->nz_strx + 1; /* +1 for '_' */ if (np->nz_type == N_UNDF+N_EXT && np->nz_value != 0) { symval->value = 0; symval->size = np->nz_value; } else { symval->value = AOUT_RELOC(af, char, np->nz_value); symval->size = np->nz_size; } return 0; } static int link_aout_search_symbol(linker_file_t lf, caddr_t value, c_linker_sym_t* sym, long* diffp) { aout_file_t af = (aout_file_t) lf; u_long off = (uintptr_t) (void *) value; u_long diff = off; u_long sp_nz_value; struct nzlist* sp; struct nzlist* ep; struct nzlist* best = 0; for (sp = AOUT_RELOC(af, struct nzlist, LD_SYMBOL(af->dynamic)), ep = (struct nzlist *) ((caddr_t) sp + LD_STABSZ(af->dynamic)); sp < ep; sp++) { if (sp->nz_name == 0) continue; sp_nz_value = sp->nz_value + (uintptr_t) (void *) af->address; if (off >= sp_nz_value) { if (off - sp_nz_value < diff) { diff = off - sp_nz_value; best = sp; if (diff == 0) break; } else if (off - sp_nz_value == diff) { best = sp; } } } if (best == 0) *diffp = off; else *diffp = diff; *sym = (linker_sym_t) best; return 0; } #endif /* !__alpha__ */ Index: head/sys/kern/link_elf.c =================================================================== --- head/sys/kern/link_elf.c (revision 62549) +++ head/sys/kern/link_elf.c (revision 62550) @@ -1,1049 +1,1050 @@ /*- * Copyright (c) 1998-2000 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SPARSE_MAPPING #include #include #include #endif #include #include #include #include "linker_if.h" typedef struct elf_file { struct linker_file lf; /* Common fields */ int preloaded; /* Was file pre-loaded */ caddr_t address; /* Relocation address */ #ifdef SPARSE_MAPPING vm_object_t object; /* VM object to hold file pages */ #endif Elf_Dyn* dynamic; /* Symbol table etc. */ Elf_Off nbuckets; /* DT_HASH info */ Elf_Off nchains; const Elf_Off* buckets; const Elf_Off* chains; caddr_t hash; caddr_t strtab; /* DT_STRTAB */ int strsz; /* DT_STRSZ */ const Elf_Sym* symtab; /* DT_SYMTAB */ Elf_Addr* got; /* DT_PLTGOT */ const Elf_Rel* pltrel; /* DT_JMPREL */ int pltrelsize; /* DT_PLTRELSZ */ const Elf_Rela* pltrela; /* DT_JMPREL */ int pltrelasize; /* DT_PLTRELSZ */ const Elf_Rel* rel; /* DT_REL */ int relsize; /* DT_RELSZ */ const Elf_Rela* rela; /* DT_RELA */ int relasize; /* DT_RELASZ */ caddr_t modptr; const Elf_Sym* ddbsymtab; /* The symbol table we are using */ long ddbsymcnt; /* Number of symbols */ caddr_t ddbstrtab; /* String table */ long ddbstrcnt; /* number of bytes in string table */ caddr_t symbase; /* malloc'ed symbold base */ caddr_t strbase; /* malloc'ed string base */ #ifdef DDB struct link_map gdb; /* hooks for gdb */ #endif } *elf_file_t; static int link_elf_link_preload(linker_class_t cls, const char*, linker_file_t*); static int link_elf_link_preload_finish(linker_file_t); static int link_elf_load_file(linker_class_t, const char*, linker_file_t*); static int link_elf_lookup_symbol(linker_file_t, const char*, c_linker_sym_t*); static int link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*); static int link_elf_search_symbol(linker_file_t, caddr_t value, c_linker_sym_t* sym, long* diffp); static void link_elf_unload_file(linker_file_t); static void link_elf_unload_preload(linker_file_t); static kobj_method_t link_elf_methods[] = { KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), KOBJMETHOD(linker_symbol_values, link_elf_symbol_values), KOBJMETHOD(linker_search_symbol, link_elf_search_symbol), KOBJMETHOD(linker_unload, link_elf_unload_file), KOBJMETHOD(linker_load_file, link_elf_load_file), KOBJMETHOD(linker_link_preload, link_elf_link_preload), KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish), { 0, 0 } }; static struct linker_class link_elf_class = { #if ELF_TARG_CLASS == ELFCLASS32 "elf32", #else "elf64", #endif link_elf_methods, sizeof(struct elf_file) }; static int parse_dynamic(elf_file_t ef); static int relocate_file(elf_file_t ef); static int link_elf_preload_parse_symbols(elf_file_t ef); #ifdef DDB /* * A list of loaded modules for GDB to use for loading symbols. */ struct r_debug r_debug; #define GDB_STATE(s) r_debug.r_state = s; r_debug_state(); /* * Function for the debugger to set a breakpoint on to gain control. */ void r_debug_state(void) { } #endif /* * The kernel symbol table starts here. */ extern struct _dynamic _DYNAMIC; static void link_elf_init(void* arg) { #ifdef __ELF__ Elf_Dyn *dp; caddr_t modptr, baseptr, sizeptr; elf_file_t ef; char *modname; #endif linker_add_class(&link_elf_class); #ifdef __ELF__ dp = (Elf_Dyn*) &_DYNAMIC; if (dp) { modname = NULL; modptr = preload_search_by_type("elf kernel"); if (modptr) modname = (char *)preload_search_info(modptr, MODINFO_NAME); if (modname == NULL) modname = "kernel"; linker_kernel_file = linker_make_file(modname, &link_elf_class); if (linker_kernel_file == NULL) panic("link_elf_init: Can't create linker structures for kernel"); ef = (elf_file_t) linker_kernel_file; ef->preloaded = 1; ef->address = 0; #ifdef SPARSE_MAPPING ef->object = 0; #endif ef->dynamic = dp; parse_dynamic(ef); linker_kernel_file->address = (caddr_t) KERNBASE; linker_kernel_file->size = -(intptr_t)linker_kernel_file->address; if (modptr) { ef->modptr = modptr; baseptr = preload_search_info(modptr, MODINFO_ADDR); if (baseptr) linker_kernel_file->address = *(caddr_t *)baseptr; sizeptr = preload_search_info(modptr, MODINFO_SIZE); if (sizeptr) linker_kernel_file->size = *(size_t *)sizeptr; } (void)link_elf_preload_parse_symbols(ef); #ifdef DDB ef->gdb.l_addr = linker_kernel_file->address; ef->gdb.l_name = modname; ef->gdb.l_ld = dp; ef->gdb.l_prev = 0; ef->gdb.l_next = 0; r_debug.r_map = &ef->gdb; r_debug.r_brk = r_debug_state; r_debug.r_state = RT_CONSISTENT; r_debug_state(); /* say hello to gdb! */ #endif } #endif } SYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0); static int link_elf_preload_parse_symbols(elf_file_t ef) { caddr_t pointer; caddr_t ssym, esym, base; caddr_t strtab; int strcnt; Elf_Sym* symtab; int symcnt; if (ef->modptr == NULL) return 0; pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM); if (pointer == NULL) return 0; ssym = *(caddr_t *)pointer; pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM); if (pointer == NULL) return 0; esym = *(caddr_t *)pointer; base = ssym; symcnt = *(long *)base; base += sizeof(long); symtab = (Elf_Sym *)base; base += roundup(symcnt, sizeof(long)); if (base > esym || base < ssym) { printf("Symbols are corrupt!\n"); return EINVAL; } strcnt = *(long *)base; base += sizeof(long); strtab = base; base += roundup(strcnt, sizeof(long)); if (base > esym || base < ssym) { printf("Symbols are corrupt!\n"); return EINVAL; } ef->ddbsymtab = symtab; ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); ef->ddbstrtab = strtab; ef->ddbstrcnt = strcnt; return 0; } static int parse_dynamic(elf_file_t ef) { Elf_Dyn *dp; int plttype = DT_REL; for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { switch (dp->d_tag) { case DT_HASH: { /* From src/libexec/rtld-elf/rtld.c */ const Elf_Off *hashtab = (const Elf_Off *) (ef->address + dp->d_un.d_ptr); ef->nbuckets = hashtab[0]; ef->nchains = hashtab[1]; ef->buckets = hashtab + 2; ef->chains = ef->buckets + ef->nbuckets; break; } case DT_STRTAB: ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr); break; case DT_STRSZ: ef->strsz = dp->d_un.d_val; break; case DT_SYMTAB: ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr); break; case DT_SYMENT: if (dp->d_un.d_val != sizeof(Elf_Sym)) return ENOEXEC; break; case DT_PLTGOT: ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr); break; case DT_REL: ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); break; case DT_RELSZ: ef->relsize = dp->d_un.d_val; break; case DT_RELENT: if (dp->d_un.d_val != sizeof(Elf_Rel)) return ENOEXEC; break; case DT_JMPREL: ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); break; case DT_PLTRELSZ: ef->pltrelsize = dp->d_un.d_val; break; case DT_RELA: ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr); break; case DT_RELASZ: ef->relasize = dp->d_un.d_val; break; case DT_RELAENT: if (dp->d_un.d_val != sizeof(Elf_Rela)) return ENOEXEC; break; case DT_PLTREL: plttype = dp->d_un.d_val; if (plttype != DT_REL && plttype != DT_RELA) return ENOEXEC; break; #ifdef DDB case DT_DEBUG: dp->d_un.d_ptr = (Elf_Addr) &r_debug; break; #endif } } if (plttype == DT_RELA) { ef->pltrela = (const Elf_Rela *) ef->pltrel; ef->pltrel = NULL; ef->pltrelasize = ef->pltrelsize; ef->pltrelsize = 0; } ef->ddbsymtab = ef->symtab; ef->ddbsymcnt = ef->nchains; ef->ddbstrtab = ef->strtab; ef->ddbstrcnt = ef->strsz; return 0; } static void link_elf_error(const char *s) { printf("kldload: %s\n", s); } #ifdef DDB static void link_elf_add_gdb(struct link_map *l) { struct link_map *prev; /* * Scan to the end of the list. */ for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next) ; /* Link in the new entry. */ l->l_prev = prev; l->l_next = prev->l_next; prev->l_next = l; } static void link_elf_delete_gdb(struct link_map *l) { if (l->l_prev == NULL) { if ((r_debug.r_map = l->l_next) != NULL) l->l_next->l_prev = NULL; return; } if ((l->l_prev->l_next = l->l_next) != NULL) l->l_next->l_prev = l->l_prev; } #endif /* DDB */ static int link_elf_link_preload(linker_class_t cls, const char* filename, linker_file_t *result) { caddr_t modptr, baseptr, sizeptr, dynptr; char *type; elf_file_t ef; linker_file_t lf; int error; vm_offset_t dp; /* Look to see if we have the file preloaded */ modptr = preload_search_by_name(filename); if (modptr == NULL) return ENOENT; type = (char *)preload_search_info(modptr, MODINFO_TYPE); baseptr = preload_search_info(modptr, MODINFO_ADDR); sizeptr = preload_search_info(modptr, MODINFO_SIZE); dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC); if (type == NULL || strcmp(type, "elf module") != 0) return (EFTYPE); if (baseptr == NULL || sizeptr == NULL || dynptr == NULL) return (EINVAL); lf = linker_make_file(filename, &link_elf_class); if (lf == NULL) { return ENOMEM; } ef = (elf_file_t) lf; ef->preloaded = 1; ef->modptr = modptr; ef->address = *(caddr_t *)baseptr; #ifdef SPARSE_MAPPING ef->object = 0; #endif dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr; ef->dynamic = (Elf_Dyn *)dp; lf->address = ef->address; lf->size = *(size_t *)sizeptr; error = parse_dynamic(ef); if (error) { linker_file_unload(lf); return error; } *result = lf; return (0); } static int link_elf_link_preload_finish(linker_file_t lf) { elf_file_t ef; int error; ef = (elf_file_t) lf; #if 0 /* this will be more trouble than it's worth for now */ for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { if (dp->d_tag != DT_NEEDED) continue; modname = ef->strtab + dp->d_un.d_val; error = linker_load_module(modname, lf); if (error) goto out; } #endif error = relocate_file(ef); if (error) return error; (void)link_elf_preload_parse_symbols(ef); #ifdef DDB GDB_STATE(RT_ADD); ef->gdb.l_addr = lf->address; ef->gdb.l_name = lf->filename; ef->gdb.l_ld = ef->dynamic; link_elf_add_gdb(&ef->gdb); GDB_STATE(RT_CONSISTENT); #endif return (0); } static int link_elf_load_file(linker_class_t cls, const char* filename, linker_file_t* result) { struct nameidata nd; struct proc* p = curproc; /* XXX */ Elf_Ehdr *hdr; caddr_t firstpage; int nbytes, i; Elf_Phdr *phdr; Elf_Phdr *phlimit; Elf_Phdr *segs[2]; int nsegs; Elf_Phdr *phdyn; Elf_Phdr *phphdr; caddr_t mapbase; size_t mapsize; Elf_Off base_offset; Elf_Addr base_vaddr; Elf_Addr base_vlimit; int error = 0; - int resid; + int resid, flags; elf_file_t ef; linker_file_t lf; Elf_Shdr *shdr; int symtabindex; int symstrindex; int symcnt; int strcnt; shdr = NULL; lf = NULL; NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, p); - error = vn_open(&nd, FREAD, 0); + flags = FREAD; + error = vn_open(&nd, &flags, 0); if (error) return error; NDFREE(&nd, NDF_ONLY_PNBUF); /* * Read the elf header from the file. */ firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK); if (firstpage == NULL) { error = ENOMEM; goto out; } hdr = (Elf_Ehdr *)firstpage; error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); nbytes = PAGE_SIZE - resid; if (error) goto out; if (!IS_ELF(*hdr)) { error = ENOEXEC; goto out; } if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { link_elf_error("Unsupported file layout"); error = ENOEXEC; goto out; } if (hdr->e_ident[EI_VERSION] != EV_CURRENT || hdr->e_version != EV_CURRENT) { link_elf_error("Unsupported file version"); error = ENOEXEC; goto out; } if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { link_elf_error("Unsupported file type"); error = ENOEXEC; goto out; } if (hdr->e_machine != ELF_TARG_MACH) { link_elf_error("Unsupported machine"); error = ENOEXEC; goto out; } /* * We rely on the program header being in the first page. This is * not strictly required by the ABI specification, but it seems to * always true in practice. And, it simplifies things considerably. */ if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) && (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) && (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes))) link_elf_error("Unreadable program headers"); /* * Scan the program header entries, and save key information. * * We rely on there being exactly two load segments, text and data, * in that order. */ phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff); phlimit = phdr + hdr->e_phnum; nsegs = 0; phdyn = NULL; phphdr = NULL; while (phdr < phlimit) { switch (phdr->p_type) { case PT_LOAD: if (nsegs == 2) { link_elf_error("Too many sections"); error = ENOEXEC; goto out; } segs[nsegs] = phdr; ++nsegs; break; case PT_PHDR: phphdr = phdr; break; case PT_DYNAMIC: phdyn = phdr; break; } ++phdr; } if (phdyn == NULL) { link_elf_error("Object is not dynamically-linked"); error = ENOEXEC; goto out; } /* * Allocate the entire address space of the object, to stake out our * contiguous region, and to establish the base address for relocation. */ base_offset = trunc_page(segs[0]->p_offset); base_vaddr = trunc_page(segs[0]->p_vaddr); base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz); mapsize = base_vlimit - base_vaddr; lf = linker_make_file(filename, &link_elf_class); if (!lf) { error = ENOMEM; goto out; } ef = (elf_file_t) lf; #ifdef SPARSE_MAPPING ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT); if (ef->object == NULL) { free(ef, M_LINKER); error = ENOMEM; goto out; } vm_object_reference(ef->object); ef->address = (caddr_t) vm_map_min(kernel_map); error = vm_map_find(kernel_map, ef->object, 0, (vm_offset_t *) &ef->address, mapsize, 1, VM_PROT_ALL, VM_PROT_ALL, 0); if (error) { vm_object_deallocate(ef->object); ef->object = 0; goto out; } #else ef->address = malloc(mapsize, M_LINKER, M_WAITOK); if (!ef->address) { error = ENOMEM; goto out; } #endif mapbase = ef->address; /* * Read the text and data sections and zero the bss. */ for (i = 0; i < 2; i++) { caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr; error = vn_rdwr(UIO_READ, nd.ni_vp, segbase, segs[i]->p_filesz, segs[i]->p_offset, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if (error) { goto out; } bzero(segbase + segs[i]->p_filesz, segs[i]->p_memsz - segs[i]->p_filesz); #ifdef SPARSE_MAPPING /* * Wire down the pages */ vm_map_pageable(kernel_map, (vm_offset_t) segbase, (vm_offset_t) segbase + segs[i]->p_memsz, FALSE); #endif } ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr); lf->address = ef->address; lf->size = mapsize; error = parse_dynamic(ef); if (error) goto out; error = linker_load_dependancies(lf); if (error) goto out; #if 0 /* this will be more trouble than it's worth for now */ for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { if (dp->d_tag != DT_NEEDED) continue; modname = ef->strtab + dp->d_un.d_val; error = linker_load_module(modname, lf); if (error) goto out; } #endif error = relocate_file(ef); if (error) goto out; /* Try and load the symbol table if it's present. (you can strip it!) */ nbytes = hdr->e_shnum * hdr->e_shentsize; if (nbytes == 0 || hdr->e_shoff == 0) goto nosyms; shdr = malloc(nbytes, M_LINKER, M_WAITOK); if (shdr == NULL) { error = ENOMEM; goto out; } bzero(shdr, nbytes); error = vn_rdwr(UIO_READ, nd.ni_vp, (caddr_t)shdr, nbytes, hdr->e_shoff, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if (error) goto out; symtabindex = -1; symstrindex = -1; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_type == SHT_SYMTAB) { symtabindex = i; symstrindex = shdr[i].sh_link; } } if (symtabindex < 0 || symstrindex < 0) goto nosyms; symcnt = shdr[symtabindex].sh_size; ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK); strcnt = shdr[symstrindex].sh_size; ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); if (ef->symbase == NULL || ef->strbase == NULL) { error = ENOMEM; goto out; } error = vn_rdwr(UIO_READ, nd.ni_vp, ef->symbase, symcnt, shdr[symtabindex].sh_offset, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if (error) goto out; error = vn_rdwr(UIO_READ, nd.ni_vp, ef->strbase, strcnt, shdr[symstrindex].sh_offset, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if (error) goto out; ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); ef->ddbsymtab = (const Elf_Sym *)ef->symbase; ef->ddbstrcnt = strcnt; ef->ddbstrtab = ef->strbase; #ifdef DDB GDB_STATE(RT_ADD); ef->gdb.l_addr = lf->address; ef->gdb.l_name = filename; ef->gdb.l_ld = ef->dynamic; link_elf_add_gdb(&ef->gdb); GDB_STATE(RT_CONSISTENT); #endif nosyms: *result = lf; out: if (error && lf) linker_file_unload(lf); if (shdr) free(shdr, M_LINKER); if (firstpage) free(firstpage, M_LINKER); VOP_UNLOCK(nd.ni_vp, 0, p); vn_close(nd.ni_vp, FREAD, p->p_ucred, p); return error; } static void link_elf_unload_file(linker_file_t file) { elf_file_t ef = (elf_file_t) file; #ifdef DDB if (ef->gdb.l_ld) { GDB_STATE(RT_DELETE); link_elf_delete_gdb(&ef->gdb); GDB_STATE(RT_CONSISTENT); } #endif if (ef->preloaded) { link_elf_unload_preload(file); return; } #ifdef SPARSE_MAPPING if (ef->object) { vm_map_remove(kernel_map, (vm_offset_t) ef->address, (vm_offset_t) ef->address + (ef->object->size << PAGE_SHIFT)); vm_object_deallocate(ef->object); } #else if (ef->address) free(ef->address, M_LINKER); #endif if (ef->symbase) free(ef->symbase, M_LINKER); if (ef->strbase) free(ef->strbase, M_LINKER); } static void link_elf_unload_preload(linker_file_t file) { if (file->filename) preload_delete_name(file->filename); } static const char * symbol_name(elf_file_t ef, Elf_Word r_info) { const Elf_Sym *ref; if (ELF_R_SYM(r_info)) { ref = ef->symtab + ELF_R_SYM(r_info); return ef->strtab + ref->st_name; } else return NULL; } static int relocate_file(elf_file_t ef) { const Elf_Rel *rellim; const Elf_Rel *rel; const Elf_Rela *relalim; const Elf_Rela *rela; const char *symname; /* Perform relocations without addend if there are any: */ rel = ef->rel; if (rel) { rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); while (rel < rellim) { symname = symbol_name(ef, rel->r_info); if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) { printf("link_elf: symbol %s undefined\n", symname); return ENOENT; } rel++; } } /* Perform relocations with addend if there are any: */ rela = ef->rela; if (rela) { relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); while (rela < relalim) { symname = symbol_name(ef, rela->r_info); if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) { printf("link_elf: symbol %s undefined\n", symname); return ENOENT; } rela++; } } /* Perform PLT relocations without addend if there are any: */ rel = ef->pltrel; if (rel) { rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize); while (rel < rellim) { symname = symbol_name(ef, rel->r_info); if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) { printf("link_elf: symbol %s undefined\n", symname); return ENOENT; } rel++; } } /* Perform relocations with addend if there are any: */ rela = ef->pltrela; if (rela) { relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize); while (rela < relalim) { symname = symbol_name(ef, rela->r_info); if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) { printf("link_elf: symbol %s undefined\n", symname); return ENOENT; } rela++; } } return 0; } /* * Hash function for symbol table lookup. Don't even think about changing * this. It is specified by the System V ABI. */ static unsigned long elf_hash(const char *name) { const unsigned char *p = (const unsigned char *) name; unsigned long h = 0; unsigned long g; while (*p != '\0') { h = (h << 4) + *p++; if ((g = h & 0xf0000000) != 0) h ^= g >> 24; h &= ~g; } return h; } int link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) { elf_file_t ef = (elf_file_t) lf; unsigned long symnum; const Elf_Sym* symp; const char *strp; unsigned long hash; int i; /* First, search hashed global symbols */ hash = elf_hash(name); symnum = ef->buckets[hash % ef->nbuckets]; while (symnum != STN_UNDEF) { if (symnum >= ef->nchains) { printf("link_elf_lookup_symbol: corrupt symbol table\n"); return ENOENT; } symp = ef->symtab + symnum; if (symp->st_name == 0) { printf("link_elf_lookup_symbol: corrupt symbol table\n"); return ENOENT; } strp = ef->strtab + symp->st_name; if (strcmp(name, strp) == 0) { if (symp->st_shndx != SHN_UNDEF || (symp->st_value != 0 && ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { *sym = (c_linker_sym_t) symp; return 0; } else return ENOENT; } symnum = ef->chains[symnum]; } /* If we have not found it, look at the full table (if loaded) */ if (ef->symtab == ef->ddbsymtab) return ENOENT; /* Exhaustive search */ for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { strp = ef->ddbstrtab + symp->st_name; if (strcmp(name, strp) == 0) { if (symp->st_shndx != SHN_UNDEF || (symp->st_value != 0 && ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { *sym = (c_linker_sym_t) symp; return 0; } else return ENOENT; } } return ENOENT; } static int link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval) { elf_file_t ef = (elf_file_t) lf; const Elf_Sym* es = (const Elf_Sym*) sym; if (es >= ef->symtab && ((es - ef->symtab) < ef->nchains)) { symval->name = ef->strtab + es->st_name; symval->value = (caddr_t) ef->address + es->st_value; symval->size = es->st_size; return 0; } if (ef->symtab == ef->ddbsymtab) return ENOENT; if (es >= ef->ddbsymtab && ((es - ef->ddbsymtab) < ef->ddbsymcnt)) { symval->name = ef->ddbstrtab + es->st_name; symval->value = (caddr_t) ef->address + es->st_value; symval->size = es->st_size; return 0; } return ENOENT; } static int link_elf_search_symbol(linker_file_t lf, caddr_t value, c_linker_sym_t* sym, long* diffp) { elf_file_t ef = (elf_file_t) lf; u_long off = (uintptr_t) (void *) value; u_long diff = off; u_long st_value; const Elf_Sym* es; const Elf_Sym* best = 0; int i; for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) { if (es->st_name == 0) continue; st_value = es->st_value + (uintptr_t) (void *) ef->address; if (off >= st_value) { if (off - st_value < diff) { diff = off - st_value; best = es; if (diff == 0) break; } else if (off - st_value == diff) { best = es; } } } if (best == 0) *diffp = off; else *diffp = diff; *sym = (c_linker_sym_t) best; return 0; } Index: head/sys/kern/link_elf_obj.c =================================================================== --- head/sys/kern/link_elf_obj.c (revision 62549) +++ head/sys/kern/link_elf_obj.c (revision 62550) @@ -1,1049 +1,1050 @@ /*- * Copyright (c) 1998-2000 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SPARSE_MAPPING #include #include #include #endif #include #include #include #include "linker_if.h" typedef struct elf_file { struct linker_file lf; /* Common fields */ int preloaded; /* Was file pre-loaded */ caddr_t address; /* Relocation address */ #ifdef SPARSE_MAPPING vm_object_t object; /* VM object to hold file pages */ #endif Elf_Dyn* dynamic; /* Symbol table etc. */ Elf_Off nbuckets; /* DT_HASH info */ Elf_Off nchains; const Elf_Off* buckets; const Elf_Off* chains; caddr_t hash; caddr_t strtab; /* DT_STRTAB */ int strsz; /* DT_STRSZ */ const Elf_Sym* symtab; /* DT_SYMTAB */ Elf_Addr* got; /* DT_PLTGOT */ const Elf_Rel* pltrel; /* DT_JMPREL */ int pltrelsize; /* DT_PLTRELSZ */ const Elf_Rela* pltrela; /* DT_JMPREL */ int pltrelasize; /* DT_PLTRELSZ */ const Elf_Rel* rel; /* DT_REL */ int relsize; /* DT_RELSZ */ const Elf_Rela* rela; /* DT_RELA */ int relasize; /* DT_RELASZ */ caddr_t modptr; const Elf_Sym* ddbsymtab; /* The symbol table we are using */ long ddbsymcnt; /* Number of symbols */ caddr_t ddbstrtab; /* String table */ long ddbstrcnt; /* number of bytes in string table */ caddr_t symbase; /* malloc'ed symbold base */ caddr_t strbase; /* malloc'ed string base */ #ifdef DDB struct link_map gdb; /* hooks for gdb */ #endif } *elf_file_t; static int link_elf_link_preload(linker_class_t cls, const char*, linker_file_t*); static int link_elf_link_preload_finish(linker_file_t); static int link_elf_load_file(linker_class_t, const char*, linker_file_t*); static int link_elf_lookup_symbol(linker_file_t, const char*, c_linker_sym_t*); static int link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*); static int link_elf_search_symbol(linker_file_t, caddr_t value, c_linker_sym_t* sym, long* diffp); static void link_elf_unload_file(linker_file_t); static void link_elf_unload_preload(linker_file_t); static kobj_method_t link_elf_methods[] = { KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), KOBJMETHOD(linker_symbol_values, link_elf_symbol_values), KOBJMETHOD(linker_search_symbol, link_elf_search_symbol), KOBJMETHOD(linker_unload, link_elf_unload_file), KOBJMETHOD(linker_load_file, link_elf_load_file), KOBJMETHOD(linker_link_preload, link_elf_link_preload), KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish), { 0, 0 } }; static struct linker_class link_elf_class = { #if ELF_TARG_CLASS == ELFCLASS32 "elf32", #else "elf64", #endif link_elf_methods, sizeof(struct elf_file) }; static int parse_dynamic(elf_file_t ef); static int relocate_file(elf_file_t ef); static int link_elf_preload_parse_symbols(elf_file_t ef); #ifdef DDB /* * A list of loaded modules for GDB to use for loading symbols. */ struct r_debug r_debug; #define GDB_STATE(s) r_debug.r_state = s; r_debug_state(); /* * Function for the debugger to set a breakpoint on to gain control. */ void r_debug_state(void) { } #endif /* * The kernel symbol table starts here. */ extern struct _dynamic _DYNAMIC; static void link_elf_init(void* arg) { #ifdef __ELF__ Elf_Dyn *dp; caddr_t modptr, baseptr, sizeptr; elf_file_t ef; char *modname; #endif linker_add_class(&link_elf_class); #ifdef __ELF__ dp = (Elf_Dyn*) &_DYNAMIC; if (dp) { modname = NULL; modptr = preload_search_by_type("elf kernel"); if (modptr) modname = (char *)preload_search_info(modptr, MODINFO_NAME); if (modname == NULL) modname = "kernel"; linker_kernel_file = linker_make_file(modname, &link_elf_class); if (linker_kernel_file == NULL) panic("link_elf_init: Can't create linker structures for kernel"); ef = (elf_file_t) linker_kernel_file; ef->preloaded = 1; ef->address = 0; #ifdef SPARSE_MAPPING ef->object = 0; #endif ef->dynamic = dp; parse_dynamic(ef); linker_kernel_file->address = (caddr_t) KERNBASE; linker_kernel_file->size = -(intptr_t)linker_kernel_file->address; if (modptr) { ef->modptr = modptr; baseptr = preload_search_info(modptr, MODINFO_ADDR); if (baseptr) linker_kernel_file->address = *(caddr_t *)baseptr; sizeptr = preload_search_info(modptr, MODINFO_SIZE); if (sizeptr) linker_kernel_file->size = *(size_t *)sizeptr; } (void)link_elf_preload_parse_symbols(ef); #ifdef DDB ef->gdb.l_addr = linker_kernel_file->address; ef->gdb.l_name = modname; ef->gdb.l_ld = dp; ef->gdb.l_prev = 0; ef->gdb.l_next = 0; r_debug.r_map = &ef->gdb; r_debug.r_brk = r_debug_state; r_debug.r_state = RT_CONSISTENT; r_debug_state(); /* say hello to gdb! */ #endif } #endif } SYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0); static int link_elf_preload_parse_symbols(elf_file_t ef) { caddr_t pointer; caddr_t ssym, esym, base; caddr_t strtab; int strcnt; Elf_Sym* symtab; int symcnt; if (ef->modptr == NULL) return 0; pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM); if (pointer == NULL) return 0; ssym = *(caddr_t *)pointer; pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM); if (pointer == NULL) return 0; esym = *(caddr_t *)pointer; base = ssym; symcnt = *(long *)base; base += sizeof(long); symtab = (Elf_Sym *)base; base += roundup(symcnt, sizeof(long)); if (base > esym || base < ssym) { printf("Symbols are corrupt!\n"); return EINVAL; } strcnt = *(long *)base; base += sizeof(long); strtab = base; base += roundup(strcnt, sizeof(long)); if (base > esym || base < ssym) { printf("Symbols are corrupt!\n"); return EINVAL; } ef->ddbsymtab = symtab; ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); ef->ddbstrtab = strtab; ef->ddbstrcnt = strcnt; return 0; } static int parse_dynamic(elf_file_t ef) { Elf_Dyn *dp; int plttype = DT_REL; for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { switch (dp->d_tag) { case DT_HASH: { /* From src/libexec/rtld-elf/rtld.c */ const Elf_Off *hashtab = (const Elf_Off *) (ef->address + dp->d_un.d_ptr); ef->nbuckets = hashtab[0]; ef->nchains = hashtab[1]; ef->buckets = hashtab + 2; ef->chains = ef->buckets + ef->nbuckets; break; } case DT_STRTAB: ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr); break; case DT_STRSZ: ef->strsz = dp->d_un.d_val; break; case DT_SYMTAB: ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr); break; case DT_SYMENT: if (dp->d_un.d_val != sizeof(Elf_Sym)) return ENOEXEC; break; case DT_PLTGOT: ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr); break; case DT_REL: ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); break; case DT_RELSZ: ef->relsize = dp->d_un.d_val; break; case DT_RELENT: if (dp->d_un.d_val != sizeof(Elf_Rel)) return ENOEXEC; break; case DT_JMPREL: ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); break; case DT_PLTRELSZ: ef->pltrelsize = dp->d_un.d_val; break; case DT_RELA: ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr); break; case DT_RELASZ: ef->relasize = dp->d_un.d_val; break; case DT_RELAENT: if (dp->d_un.d_val != sizeof(Elf_Rela)) return ENOEXEC; break; case DT_PLTREL: plttype = dp->d_un.d_val; if (plttype != DT_REL && plttype != DT_RELA) return ENOEXEC; break; #ifdef DDB case DT_DEBUG: dp->d_un.d_ptr = (Elf_Addr) &r_debug; break; #endif } } if (plttype == DT_RELA) { ef->pltrela = (const Elf_Rela *) ef->pltrel; ef->pltrel = NULL; ef->pltrelasize = ef->pltrelsize; ef->pltrelsize = 0; } ef->ddbsymtab = ef->symtab; ef->ddbsymcnt = ef->nchains; ef->ddbstrtab = ef->strtab; ef->ddbstrcnt = ef->strsz; return 0; } static void link_elf_error(const char *s) { printf("kldload: %s\n", s); } #ifdef DDB static void link_elf_add_gdb(struct link_map *l) { struct link_map *prev; /* * Scan to the end of the list. */ for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next) ; /* Link in the new entry. */ l->l_prev = prev; l->l_next = prev->l_next; prev->l_next = l; } static void link_elf_delete_gdb(struct link_map *l) { if (l->l_prev == NULL) { if ((r_debug.r_map = l->l_next) != NULL) l->l_next->l_prev = NULL; return; } if ((l->l_prev->l_next = l->l_next) != NULL) l->l_next->l_prev = l->l_prev; } #endif /* DDB */ static int link_elf_link_preload(linker_class_t cls, const char* filename, linker_file_t *result) { caddr_t modptr, baseptr, sizeptr, dynptr; char *type; elf_file_t ef; linker_file_t lf; int error; vm_offset_t dp; /* Look to see if we have the file preloaded */ modptr = preload_search_by_name(filename); if (modptr == NULL) return ENOENT; type = (char *)preload_search_info(modptr, MODINFO_TYPE); baseptr = preload_search_info(modptr, MODINFO_ADDR); sizeptr = preload_search_info(modptr, MODINFO_SIZE); dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC); if (type == NULL || strcmp(type, "elf module") != 0) return (EFTYPE); if (baseptr == NULL || sizeptr == NULL || dynptr == NULL) return (EINVAL); lf = linker_make_file(filename, &link_elf_class); if (lf == NULL) { return ENOMEM; } ef = (elf_file_t) lf; ef->preloaded = 1; ef->modptr = modptr; ef->address = *(caddr_t *)baseptr; #ifdef SPARSE_MAPPING ef->object = 0; #endif dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr; ef->dynamic = (Elf_Dyn *)dp; lf->address = ef->address; lf->size = *(size_t *)sizeptr; error = parse_dynamic(ef); if (error) { linker_file_unload(lf); return error; } *result = lf; return (0); } static int link_elf_link_preload_finish(linker_file_t lf) { elf_file_t ef; int error; ef = (elf_file_t) lf; #if 0 /* this will be more trouble than it's worth for now */ for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { if (dp->d_tag != DT_NEEDED) continue; modname = ef->strtab + dp->d_un.d_val; error = linker_load_module(modname, lf); if (error) goto out; } #endif error = relocate_file(ef); if (error) return error; (void)link_elf_preload_parse_symbols(ef); #ifdef DDB GDB_STATE(RT_ADD); ef->gdb.l_addr = lf->address; ef->gdb.l_name = lf->filename; ef->gdb.l_ld = ef->dynamic; link_elf_add_gdb(&ef->gdb); GDB_STATE(RT_CONSISTENT); #endif return (0); } static int link_elf_load_file(linker_class_t cls, const char* filename, linker_file_t* result) { struct nameidata nd; struct proc* p = curproc; /* XXX */ Elf_Ehdr *hdr; caddr_t firstpage; int nbytes, i; Elf_Phdr *phdr; Elf_Phdr *phlimit; Elf_Phdr *segs[2]; int nsegs; Elf_Phdr *phdyn; Elf_Phdr *phphdr; caddr_t mapbase; size_t mapsize; Elf_Off base_offset; Elf_Addr base_vaddr; Elf_Addr base_vlimit; int error = 0; - int resid; + int resid, flags; elf_file_t ef; linker_file_t lf; Elf_Shdr *shdr; int symtabindex; int symstrindex; int symcnt; int strcnt; shdr = NULL; lf = NULL; NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, p); - error = vn_open(&nd, FREAD, 0); + flags = FREAD; + error = vn_open(&nd, &flags, 0); if (error) return error; NDFREE(&nd, NDF_ONLY_PNBUF); /* * Read the elf header from the file. */ firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK); if (firstpage == NULL) { error = ENOMEM; goto out; } hdr = (Elf_Ehdr *)firstpage; error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); nbytes = PAGE_SIZE - resid; if (error) goto out; if (!IS_ELF(*hdr)) { error = ENOEXEC; goto out; } if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { link_elf_error("Unsupported file layout"); error = ENOEXEC; goto out; } if (hdr->e_ident[EI_VERSION] != EV_CURRENT || hdr->e_version != EV_CURRENT) { link_elf_error("Unsupported file version"); error = ENOEXEC; goto out; } if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { link_elf_error("Unsupported file type"); error = ENOEXEC; goto out; } if (hdr->e_machine != ELF_TARG_MACH) { link_elf_error("Unsupported machine"); error = ENOEXEC; goto out; } /* * We rely on the program header being in the first page. This is * not strictly required by the ABI specification, but it seems to * always true in practice. And, it simplifies things considerably. */ if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) && (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) && (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes))) link_elf_error("Unreadable program headers"); /* * Scan the program header entries, and save key information. * * We rely on there being exactly two load segments, text and data, * in that order. */ phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff); phlimit = phdr + hdr->e_phnum; nsegs = 0; phdyn = NULL; phphdr = NULL; while (phdr < phlimit) { switch (phdr->p_type) { case PT_LOAD: if (nsegs == 2) { link_elf_error("Too many sections"); error = ENOEXEC; goto out; } segs[nsegs] = phdr; ++nsegs; break; case PT_PHDR: phphdr = phdr; break; case PT_DYNAMIC: phdyn = phdr; break; } ++phdr; } if (phdyn == NULL) { link_elf_error("Object is not dynamically-linked"); error = ENOEXEC; goto out; } /* * Allocate the entire address space of the object, to stake out our * contiguous region, and to establish the base address for relocation. */ base_offset = trunc_page(segs[0]->p_offset); base_vaddr = trunc_page(segs[0]->p_vaddr); base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz); mapsize = base_vlimit - base_vaddr; lf = linker_make_file(filename, &link_elf_class); if (!lf) { error = ENOMEM; goto out; } ef = (elf_file_t) lf; #ifdef SPARSE_MAPPING ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT); if (ef->object == NULL) { free(ef, M_LINKER); error = ENOMEM; goto out; } vm_object_reference(ef->object); ef->address = (caddr_t) vm_map_min(kernel_map); error = vm_map_find(kernel_map, ef->object, 0, (vm_offset_t *) &ef->address, mapsize, 1, VM_PROT_ALL, VM_PROT_ALL, 0); if (error) { vm_object_deallocate(ef->object); ef->object = 0; goto out; } #else ef->address = malloc(mapsize, M_LINKER, M_WAITOK); if (!ef->address) { error = ENOMEM; goto out; } #endif mapbase = ef->address; /* * Read the text and data sections and zero the bss. */ for (i = 0; i < 2; i++) { caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr; error = vn_rdwr(UIO_READ, nd.ni_vp, segbase, segs[i]->p_filesz, segs[i]->p_offset, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if (error) { goto out; } bzero(segbase + segs[i]->p_filesz, segs[i]->p_memsz - segs[i]->p_filesz); #ifdef SPARSE_MAPPING /* * Wire down the pages */ vm_map_pageable(kernel_map, (vm_offset_t) segbase, (vm_offset_t) segbase + segs[i]->p_memsz, FALSE); #endif } ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr); lf->address = ef->address; lf->size = mapsize; error = parse_dynamic(ef); if (error) goto out; error = linker_load_dependancies(lf); if (error) goto out; #if 0 /* this will be more trouble than it's worth for now */ for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { if (dp->d_tag != DT_NEEDED) continue; modname = ef->strtab + dp->d_un.d_val; error = linker_load_module(modname, lf); if (error) goto out; } #endif error = relocate_file(ef); if (error) goto out; /* Try and load the symbol table if it's present. (you can strip it!) */ nbytes = hdr->e_shnum * hdr->e_shentsize; if (nbytes == 0 || hdr->e_shoff == 0) goto nosyms; shdr = malloc(nbytes, M_LINKER, M_WAITOK); if (shdr == NULL) { error = ENOMEM; goto out; } bzero(shdr, nbytes); error = vn_rdwr(UIO_READ, nd.ni_vp, (caddr_t)shdr, nbytes, hdr->e_shoff, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if (error) goto out; symtabindex = -1; symstrindex = -1; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_type == SHT_SYMTAB) { symtabindex = i; symstrindex = shdr[i].sh_link; } } if (symtabindex < 0 || symstrindex < 0) goto nosyms; symcnt = shdr[symtabindex].sh_size; ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK); strcnt = shdr[symstrindex].sh_size; ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); if (ef->symbase == NULL || ef->strbase == NULL) { error = ENOMEM; goto out; } error = vn_rdwr(UIO_READ, nd.ni_vp, ef->symbase, symcnt, shdr[symtabindex].sh_offset, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if (error) goto out; error = vn_rdwr(UIO_READ, nd.ni_vp, ef->strbase, strcnt, shdr[symstrindex].sh_offset, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); if (error) goto out; ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); ef->ddbsymtab = (const Elf_Sym *)ef->symbase; ef->ddbstrcnt = strcnt; ef->ddbstrtab = ef->strbase; #ifdef DDB GDB_STATE(RT_ADD); ef->gdb.l_addr = lf->address; ef->gdb.l_name = filename; ef->gdb.l_ld = ef->dynamic; link_elf_add_gdb(&ef->gdb); GDB_STATE(RT_CONSISTENT); #endif nosyms: *result = lf; out: if (error && lf) linker_file_unload(lf); if (shdr) free(shdr, M_LINKER); if (firstpage) free(firstpage, M_LINKER); VOP_UNLOCK(nd.ni_vp, 0, p); vn_close(nd.ni_vp, FREAD, p->p_ucred, p); return error; } static void link_elf_unload_file(linker_file_t file) { elf_file_t ef = (elf_file_t) file; #ifdef DDB if (ef->gdb.l_ld) { GDB_STATE(RT_DELETE); link_elf_delete_gdb(&ef->gdb); GDB_STATE(RT_CONSISTENT); } #endif if (ef->preloaded) { link_elf_unload_preload(file); return; } #ifdef SPARSE_MAPPING if (ef->object) { vm_map_remove(kernel_map, (vm_offset_t) ef->address, (vm_offset_t) ef->address + (ef->object->size << PAGE_SHIFT)); vm_object_deallocate(ef->object); } #else if (ef->address) free(ef->address, M_LINKER); #endif if (ef->symbase) free(ef->symbase, M_LINKER); if (ef->strbase) free(ef->strbase, M_LINKER); } static void link_elf_unload_preload(linker_file_t file) { if (file->filename) preload_delete_name(file->filename); } static const char * symbol_name(elf_file_t ef, Elf_Word r_info) { const Elf_Sym *ref; if (ELF_R_SYM(r_info)) { ref = ef->symtab + ELF_R_SYM(r_info); return ef->strtab + ref->st_name; } else return NULL; } static int relocate_file(elf_file_t ef) { const Elf_Rel *rellim; const Elf_Rel *rel; const Elf_Rela *relalim; const Elf_Rela *rela; const char *symname; /* Perform relocations without addend if there are any: */ rel = ef->rel; if (rel) { rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); while (rel < rellim) { symname = symbol_name(ef, rel->r_info); if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) { printf("link_elf: symbol %s undefined\n", symname); return ENOENT; } rel++; } } /* Perform relocations with addend if there are any: */ rela = ef->rela; if (rela) { relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); while (rela < relalim) { symname = symbol_name(ef, rela->r_info); if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) { printf("link_elf: symbol %s undefined\n", symname); return ENOENT; } rela++; } } /* Perform PLT relocations without addend if there are any: */ rel = ef->pltrel; if (rel) { rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize); while (rel < rellim) { symname = symbol_name(ef, rel->r_info); if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) { printf("link_elf: symbol %s undefined\n", symname); return ENOENT; } rel++; } } /* Perform relocations with addend if there are any: */ rela = ef->pltrela; if (rela) { relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize); while (rela < relalim) { symname = symbol_name(ef, rela->r_info); if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) { printf("link_elf: symbol %s undefined\n", symname); return ENOENT; } rela++; } } return 0; } /* * Hash function for symbol table lookup. Don't even think about changing * this. It is specified by the System V ABI. */ static unsigned long elf_hash(const char *name) { const unsigned char *p = (const unsigned char *) name; unsigned long h = 0; unsigned long g; while (*p != '\0') { h = (h << 4) + *p++; if ((g = h & 0xf0000000) != 0) h ^= g >> 24; h &= ~g; } return h; } int link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) { elf_file_t ef = (elf_file_t) lf; unsigned long symnum; const Elf_Sym* symp; const char *strp; unsigned long hash; int i; /* First, search hashed global symbols */ hash = elf_hash(name); symnum = ef->buckets[hash % ef->nbuckets]; while (symnum != STN_UNDEF) { if (symnum >= ef->nchains) { printf("link_elf_lookup_symbol: corrupt symbol table\n"); return ENOENT; } symp = ef->symtab + symnum; if (symp->st_name == 0) { printf("link_elf_lookup_symbol: corrupt symbol table\n"); return ENOENT; } strp = ef->strtab + symp->st_name; if (strcmp(name, strp) == 0) { if (symp->st_shndx != SHN_UNDEF || (symp->st_value != 0 && ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { *sym = (c_linker_sym_t) symp; return 0; } else return ENOENT; } symnum = ef->chains[symnum]; } /* If we have not found it, look at the full table (if loaded) */ if (ef->symtab == ef->ddbsymtab) return ENOENT; /* Exhaustive search */ for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { strp = ef->ddbstrtab + symp->st_name; if (strcmp(name, strp) == 0) { if (symp->st_shndx != SHN_UNDEF || (symp->st_value != 0 && ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { *sym = (c_linker_sym_t) symp; return 0; } else return ENOENT; } } return ENOENT; } static int link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval) { elf_file_t ef = (elf_file_t) lf; const Elf_Sym* es = (const Elf_Sym*) sym; if (es >= ef->symtab && ((es - ef->symtab) < ef->nchains)) { symval->name = ef->strtab + es->st_name; symval->value = (caddr_t) ef->address + es->st_value; symval->size = es->st_size; return 0; } if (ef->symtab == ef->ddbsymtab) return ENOENT; if (es >= ef->ddbsymtab && ((es - ef->ddbsymtab) < ef->ddbsymcnt)) { symval->name = ef->ddbstrtab + es->st_name; symval->value = (caddr_t) ef->address + es->st_value; symval->size = es->st_size; return 0; } return ENOENT; } static int link_elf_search_symbol(linker_file_t lf, caddr_t value, c_linker_sym_t* sym, long* diffp) { elf_file_t ef = (elf_file_t) lf; u_long off = (uintptr_t) (void *) value; u_long diff = off; u_long st_value; const Elf_Sym* es; const Elf_Sym* best = 0; int i; for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) { if (es->st_name == 0) continue; st_value = es->st_value + (uintptr_t) (void *) ef->address; if (off >= st_value) { if (off - st_value < diff) { diff = off - st_value; best = es; if (diff == 0) break; } else if (off - st_value == diff) { best = es; } } } if (best == 0) *diffp = off; else *diffp = diff; *sym = (c_linker_sym_t) best; return 0; } Index: head/sys/kern/vfs_extattr.c =================================================================== --- head/sys/kern/vfs_extattr.c (revision 62549) +++ head/sys/kern/vfs_extattr.c (revision 62550) @@ -1,3565 +1,3575 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 * $FreeBSD$ */ /* For 4.3 integer FS ID compatibility */ #include "opt_compat.h" #include "opt_ffs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int change_dir __P((struct nameidata *ndp, struct proc *p)); static void checkdirs __P((struct vnode *olddp)); static int chroot_refuse_vdir_fds __P((struct filedesc *fdp)); static int getutimes __P((const struct timeval *, struct timespec *)); static int setfown __P((struct proc *, struct vnode *, uid_t, gid_t)); static int setfmode __P((struct proc *, struct vnode *, int)); static int setfflags __P((struct proc *, struct vnode *, int)); static int setutimes __P((struct proc *, struct vnode *, const struct timespec *, int)); static int usermount = 0; /* if 1, non-root can mount fs. */ int (*union_dircheckp) __P((struct proc *, struct vnode **, struct file *)); SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0, ""); /* * Virtual File System System Calls */ /* * Mount a file system. */ #ifndef _SYS_SYSPROTO_H_ struct mount_args { char *type; char *path; int flags; caddr_t data; }; #endif /* ARGSUSED */ int mount(p, uap) struct proc *p; register struct mount_args /* { syscallarg(char *) type; syscallarg(char *) path; syscallarg(int) flags; syscallarg(caddr_t) data; } */ *uap; { struct vnode *vp; struct mount *mp; struct vfsconf *vfsp; int error, flag = 0, flag2 = 0; struct vattr va; #ifdef COMPAT_43 u_long fstypenum; #endif struct nameidata nd; char fstypename[MFSNAMELEN]; if (usermount == 0 && (error = suser(p))) return (error); /* * Do not allow NFS export by non-root users. */ if (SCARG(uap, flags) & MNT_EXPORTED) { error = suser(p); if (error) return (error); } /* * Silently enforce MNT_NOSUID and MNT_NODEV for non-root users */ if (suser_xxx(p->p_ucred, 0, 0)) SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; /* * Get vnode to be covered */ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (SCARG(uap, flags) & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; flag = mp->mnt_flag; flag2 = mp->mnt_kern_flag; /* * We only allow the filesystem to be reloaded if it * is currently mounted read-only. */ if ((SCARG(uap, flags) & MNT_RELOAD) && ((mp->mnt_flag & MNT_RDONLY) == 0)) { vput(vp); return (EOPNOTSUPP); /* Needs translation */ } mp->mnt_flag |= SCARG(uap, flags) & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE); /* * Only root, or the user that did the original mount is * permitted to update it. */ if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid && (error = suser(p))) { vput(vp); return (error); } if (vfs_busy(mp, LK_NOWAIT, 0, p)) { vput(vp); return (EBUSY); } VOP_UNLOCK(vp, 0, p); goto update; } /* * If the user is not root, ensure that they own the directory * onto which we are attempting to mount. */ if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || (va.va_uid != p->p_ucred->cr_uid && (error = suser(p)))) { vput(vp); return (error); } if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) return (error); if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } #ifdef COMPAT_43 /* * Historically filesystem types were identified by number. If we * get an integer for the filesystem type instead of a string, we * check to see if it matches one of the historic filesystem types. */ fstypenum = (uintptr_t)SCARG(uap, type); if (fstypenum < maxvfsconf) { for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (vfsp->vfc_typenum == fstypenum) break; if (vfsp == NULL) { vput(vp); return (ENODEV); } strncpy(fstypename, vfsp->vfc_name, MFSNAMELEN); } else #endif /* COMPAT_43 */ if ((error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, NULL)) != 0) { vput(vp); return (error); } for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (!strcmp(vfsp->vfc_name, fstypename)) break; if (vfsp == NULL) { linker_file_t lf; /* Only load modules for root (very important!) */ if ((error = suser(p)) != 0) { vput(vp); return error; } error = linker_load_file(fstypename, &lf); if (error || lf == NULL) { vput(vp); if (lf == NULL) error = ENODEV; return error; } lf->userrefs++; /* lookup again, see if the VFS was loaded */ for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (!strcmp(vfsp->vfc_name, fstypename)) break; if (vfsp == NULL) { lf->userrefs--; linker_file_unload(lf); vput(vp); return (ENODEV); } } simple_lock(&vp->v_interlock); if ((vp->v_flag & VMOUNT) != 0 || vp->v_mountedhere != NULL) { simple_unlock(&vp->v_interlock); vput(vp); return (EBUSY); } vp->v_flag |= VMOUNT; simple_unlock(&vp->v_interlock); /* * Allocate and initialize the filesystem. */ mp = malloc(sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, LK_NOPAUSE); (void)vfs_busy(mp, LK_NOWAIT, 0, p); mp->mnt_op = vfsp->vfc_vfsops; mp->mnt_vfc = vfsp; vfsp->vfc_refcount++; mp->mnt_stat.f_type = vfsp->vfc_typenum; mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK; strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN); mp->mnt_vnodecovered = vp; mp->mnt_stat.f_owner = p->p_ucred->cr_uid; mp->mnt_iosize_max = DFLTPHYS; VOP_UNLOCK(vp, 0, p); update: /* * Set the mount level flags. */ if (SCARG(uap, flags) & MNT_RDONLY) mp->mnt_flag |= MNT_RDONLY; else if (mp->mnt_flag & MNT_RDONLY) mp->mnt_kern_flag |= MNTK_WANTRDWR; mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_NOATIME | MNT_NOSYMFOLLOW | MNT_IGNORE | MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR); mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_FORCE | MNT_NOSYMFOLLOW | MNT_IGNORE | MNT_NOATIME | MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR); /* * Mount the filesystem. * XXX The final recipients of VFS_MOUNT just overwrite the ndp they * get. No freeing of cn_pnbuf. */ error = VFS_MOUNT(mp, SCARG(uap, path), SCARG(uap, data), &nd, p); if (mp->mnt_flag & MNT_UPDATE) { vrele(vp); if (mp->mnt_kern_flag & MNTK_WANTRDWR) mp->mnt_flag &= ~MNT_RDONLY; mp->mnt_flag &=~ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE); mp->mnt_kern_flag &=~ MNTK_WANTRDWR; if (error) { mp->mnt_flag = flag; mp->mnt_kern_flag = flag2; } if ((mp->mnt_flag & MNT_RDONLY) == 0) { if (mp->mnt_syncer == NULL) error = vfs_allocate_syncvnode(mp); } else { if (mp->mnt_syncer != NULL) vrele(mp->mnt_syncer); mp->mnt_syncer = NULL; } vfs_unbusy(mp, p); return (error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* * Put the new filesystem on the mount list after root. */ cache_purge(vp); if (!error) { simple_lock(&vp->v_interlock); vp->v_flag &= ~VMOUNT; vp->v_mountedhere = mp; simple_unlock(&vp->v_interlock); simple_lock(&mountlist_slock); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); simple_unlock(&mountlist_slock); checkdirs(vp); VOP_UNLOCK(vp, 0, p); if ((mp->mnt_flag & MNT_RDONLY) == 0) error = vfs_allocate_syncvnode(mp); vfs_unbusy(mp, p); if ((error = VFS_START(mp, 0, p)) != 0) vrele(vp); } else { simple_lock(&vp->v_interlock); vp->v_flag &= ~VMOUNT; simple_unlock(&vp->v_interlock); mp->mnt_vfc->vfc_refcount--; vfs_unbusy(mp, p); free((caddr_t)mp, M_MOUNT); vput(vp); } return (error); } /* * Scan all active processes to see if any of them have a current * or root directory onto which the new filesystem has just been * mounted. If so, replace them with the new mount point. */ static void checkdirs(olddp) struct vnode *olddp; { struct filedesc *fdp; struct vnode *newdp; struct proc *p; if (olddp->v_usecount == 1) return; if (VFS_ROOT(olddp->v_mountedhere, &newdp)) panic("mount: lost mount"); LIST_FOREACH(p, &allproc, p_list) { fdp = p->p_fd; if (fdp->fd_cdir == olddp) { vrele(fdp->fd_cdir); VREF(newdp); fdp->fd_cdir = newdp; } if (fdp->fd_rdir == olddp) { vrele(fdp->fd_rdir); VREF(newdp); fdp->fd_rdir = newdp; } } if (rootvnode == olddp) { vrele(rootvnode); VREF(newdp); rootvnode = newdp; } vput(newdp); } /* * Unmount a file system. * * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). */ #ifndef _SYS_SYSPROTO_H_ struct unmount_args { char *path; int flags; }; #endif /* ARGSUSED */ int unmount(p, uap) struct proc *p; register struct unmount_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { register struct vnode *vp; struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); mp = vp->v_mount; /* * Only root, or the user that did the original mount is * permitted to unmount this filesystem. */ if ((mp->mnt_stat.f_owner != p->p_ucred->cr_uid) && (error = suser(p))) { vput(vp); return (error); } /* * Don't allow unmounting the root file system. */ if (mp->mnt_flag & MNT_ROOTFS) { vput(vp); return (EINVAL); } /* * Must be the root of the filesystem */ if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } vput(vp); return (dounmount(mp, SCARG(uap, flags), p)); } /* * Do the actual file system unmount. */ int dounmount(mp, flags, p) register struct mount *mp; int flags; struct proc *p; { struct vnode *coveredvp; int error; int async_flag; simple_lock(&mountlist_slock); mp->mnt_kern_flag |= MNTK_UNMOUNT; lockmgr(&mp->mnt_lock, LK_DRAIN | LK_INTERLOCK, &mountlist_slock, p); if (mp->mnt_flag & MNT_EXPUBLIC) vfs_setpublicfs(NULL, NULL, NULL); vfs_msync(mp, MNT_WAIT); async_flag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &=~ MNT_ASYNC; cache_purgevfs(mp); /* remove cache entries for this file sys */ if (mp->mnt_syncer != NULL) vrele(mp->mnt_syncer); if (((mp->mnt_flag & MNT_RDONLY) || (error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0) || (flags & MNT_FORCE)) error = VFS_UNMOUNT(mp, flags, p); simple_lock(&mountlist_slock); if (error) { if ((mp->mnt_flag & MNT_RDONLY) == 0 && mp->mnt_syncer == NULL) (void) vfs_allocate_syncvnode(mp); mp->mnt_kern_flag &= ~MNTK_UNMOUNT; mp->mnt_flag |= async_flag; lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK | LK_REENABLE, &mountlist_slock, p); if (mp->mnt_kern_flag & MNTK_MWAIT) wakeup((caddr_t)mp); return (error); } TAILQ_REMOVE(&mountlist, mp, mnt_list); if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) { coveredvp->v_mountedhere = (struct mount *)0; vrele(coveredvp); } mp->mnt_vfc->vfc_refcount--; if (!LIST_EMPTY(&mp->mnt_vnodelist)) panic("unmount: dangling vnode"); lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock, p); if (mp->mnt_kern_flag & MNTK_MWAIT) wakeup((caddr_t)mp); free((caddr_t)mp, M_MOUNT); return (0); } /* * Sync each mounted filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct sync_args { int dummy; }; #endif #ifdef DEBUG static int syncprt = 0; SYSCTL_INT(_debug, OID_AUTO, syncprt, CTLFLAG_RW, &syncprt, 0, ""); #endif /* ARGSUSED */ int sync(p, uap) struct proc *p; struct sync_args *uap; { register struct mount *mp, *nmp; int asyncflag; simple_lock(&mountlist_slock); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if ((mp->mnt_flag & MNT_RDONLY) == 0) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; vfs_msync(mp, MNT_NOWAIT); VFS_SYNC(mp, MNT_NOWAIT, ((p != NULL) ? p->p_ucred : NOCRED), p); mp->mnt_flag |= asyncflag; } simple_lock(&mountlist_slock); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); } simple_unlock(&mountlist_slock); #if 0 /* * XXX don't call vfs_bufstats() yet because that routine * was not imported in the Lite2 merge. */ #ifdef DIAGNOSTIC if (syncprt) vfs_bufstats(); #endif /* DIAGNOSTIC */ #endif return (0); } /* XXX PRISON: could be per prison flag */ static int prison_quotas; #if 0 SYSCTL_INT(_kern_prison, OID_AUTO, quotas, CTLFLAG_RW, &prison_quotas, 0, ""); #endif /* * Change filesystem quotas. */ #ifndef _SYS_SYSPROTO_H_ struct quotactl_args { char *path; int cmd; int uid; caddr_t arg; }; #endif /* ARGSUSED */ int quotactl(p, uap) struct proc *p; register struct quotactl_args /* { syscallarg(char *) path; syscallarg(int) cmd; syscallarg(int) uid; syscallarg(caddr_t) arg; } */ *uap; { register struct mount *mp; int error; struct nameidata nd; if (p->p_prison && !prison_quotas) return (EPERM); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); return (VFS_QUOTACTL(mp, SCARG(uap, cmd), SCARG(uap, uid), SCARG(uap, arg), p)); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct statfs_args { char *path; struct statfs *buf; }; #endif /* ARGSUSED */ int statfs(p, uap) struct proc *p; register struct statfs_args /* { syscallarg(char *) path; syscallarg(struct statfs *) buf; } */ *uap; { register struct mount *mp; register struct statfs *sp; int error; struct nameidata nd; struct statfs sb; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); error = VFS_STATFS(mp, sp, p); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct fstatfs_args { int fd; struct statfs *buf; }; #endif /* ARGSUSED */ int fstatfs(p, uap) struct proc *p; register struct fstatfs_args /* { syscallarg(int) fd; syscallarg(struct statfs *) buf; } */ *uap; { struct file *fp; struct mount *mp; register struct statfs *sp; int error; struct statfs sb; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); mp = ((struct vnode *)fp->f_data)->v_mount; sp = &mp->mnt_stat; error = VFS_STATFS(mp, sp, p); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); } /* * Get statistics on all filesystems. */ #ifndef _SYS_SYSPROTO_H_ struct getfsstat_args { struct statfs *buf; long bufsize; int flags; }; #endif int getfsstat(p, uap) struct proc *p; register struct getfsstat_args /* { syscallarg(struct statfs *) buf; syscallarg(long) bufsize; syscallarg(int) flags; } */ *uap; { register struct mount *mp, *nmp; register struct statfs *sp; caddr_t sfsp; long count, maxcount, error; maxcount = SCARG(uap, bufsize) / sizeof(struct statfs); sfsp = (caddr_t)SCARG(uap, buf); count = 0; simple_lock(&mountlist_slock); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if (sfsp && count < maxcount) { sp = &mp->mnt_stat; /* * If MNT_NOWAIT or MNT_LAZY is specified, do not * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY * overrides MNT_WAIT. */ if (((SCARG(uap, flags) & (MNT_LAZY|MNT_NOWAIT)) == 0 || (SCARG(uap, flags) & MNT_WAIT)) && (error = VFS_STATFS(mp, sp, p))) { simple_lock(&mountlist_slock); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); continue; } sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = copyout((caddr_t)sp, sfsp, sizeof(*sp)); if (error) { vfs_unbusy(mp, p); return (error); } sfsp += sizeof(*sp); } count++; simple_lock(&mountlist_slock); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); } simple_unlock(&mountlist_slock); if (sfsp && count > maxcount) p->p_retval[0] = maxcount; else p->p_retval[0] = count; return (0); } /* * Change current working directory to a given file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchdir_args { int fd; }; #endif /* ARGSUSED */ int fchdir(p, uap) struct proc *p; struct fchdir_args /* { syscallarg(int) fd; } */ *uap; { register struct filedesc *fdp = p->p_fd; struct vnode *vp, *tdp; struct mount *mp; struct file *fp; int error; if ((error = getvnode(fdp, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; VREF(vp); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); while (!error && (mp = vp->v_mountedhere) != NULL) { if (vfs_busy(mp, 0, 0, p)) continue; error = VFS_ROOT(mp, &tdp); vfs_unbusy(mp, p); if (error) break; vput(vp); vp = tdp; } if (error) { vput(vp); return (error); } VOP_UNLOCK(vp, 0, p); vrele(fdp->fd_cdir); fdp->fd_cdir = vp; return (0); } /* * Change current working directory (``.''). */ #ifndef _SYS_SYSPROTO_H_ struct chdir_args { char *path; }; #endif /* ARGSUSED */ int chdir(p, uap) struct proc *p; struct chdir_args /* { syscallarg(char *) path; } */ *uap; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = change_dir(&nd, p)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vrele(fdp->fd_cdir); fdp->fd_cdir = nd.ni_vp; return (0); } /* * Helper function for raised chroot(2) security function: Refuse if * any filedescriptors are open directories. */ static int chroot_refuse_vdir_fds(fdp) struct filedesc *fdp; { struct vnode *vp; struct file *fp; int error; int fd; for (fd = 0; fd < fdp->fd_nfiles ; fd++) { error = getvnode(fdp, fd, &fp); if (error) continue; vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) continue; return(EPERM); } return (0); } /* * This sysctl determines if we will allow a process to chroot(2) if it * has a directory open: * 0: disallowed for all processes. * 1: allowed for processes that were not already chroot(2)'ed. * 2: allowed for all processes. */ static int chroot_allow_open_directories = 1; SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW, &chroot_allow_open_directories, 0, ""); /* * Change notion of root (``/'') directory. */ #ifndef _SYS_SYSPROTO_H_ struct chroot_args { char *path; }; #endif /* ARGSUSED */ int chroot(p, uap) struct proc *p; struct chroot_args /* { syscallarg(char *) path; } */ *uap; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; error = suser_xxx(0, p, PRISON_ROOT); if (error) return (error); if (chroot_allow_open_directories == 0 || (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) error = chroot_refuse_vdir_fds(fdp); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = change_dir(&nd, p)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vrele(fdp->fd_rdir); fdp->fd_rdir = nd.ni_vp; if (!fdp->fd_jdir) { fdp->fd_jdir = nd.ni_vp; VREF(fdp->fd_jdir); } return (0); } /* * Common routine for chroot and chdir. */ static int change_dir(ndp, p) register struct nameidata *ndp; struct proc *p; { struct vnode *vp; int error; error = namei(ndp); if (error) return (error); vp = ndp->ni_vp; if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); if (error) vput(vp); else VOP_UNLOCK(vp, 0, p); return (error); } /* * Check permissions, allocate an open file structure, * and call the device open routine if any. */ #ifndef _SYS_SYSPROTO_H_ struct open_args { char *path; int flags; int mode; }; #endif int open(p, uap) struct proc *p; register struct open_args /* { syscallarg(char *) path; syscallarg(int) flags; syscallarg(int) mode; } */ *uap; { - register struct filedesc *fdp = p->p_fd; - register struct file *fp; - register struct vnode *vp; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + struct vattr vat; int cmode, flags, oflags; struct file *nfp; int type, indx, error; struct flock lf; struct nameidata nd; oflags = SCARG(uap, flags); if ((oflags & O_ACCMODE) == O_ACCMODE) return (EINVAL); flags = FFLAGS(oflags); error = falloc(p, &nfp, &indx); if (error) return (error); fp = nfp; cmode = ((SCARG(uap, mode) &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); p->p_dupfd = -indx - 1; /* XXX check for fdopen */ - error = vn_open(&nd, flags, cmode); + error = vn_open(&nd, &flags, cmode); if (error) { ffree(fp); if ((error == ENODEV || error == ENXIO) && p->p_dupfd >= 0 && /* XXX from fdopen */ (error = dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { p->p_retval[0] = indx; return (0); } if (error == ERESTART) error = EINTR; fdp->fd_ofiles[indx] = NULL; return (error); } p->p_dupfd = 0; NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; fp->f_data = (caddr_t)vp; fp->f_flag = flags & FMASK; fp->f_ops = &vnops; fp->f_type = (vp->v_type == VFIFO ? DTYPE_FIFO : DTYPE_VNODE); + VOP_UNLOCK(vp, 0, p); if (flags & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (flags & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((flags & FNONBLOCK) == 0) type |= F_WAIT; - VOP_UNLOCK(vp, 0, p); - if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) { - (void) vn_close(vp, fp->f_flag, fp->f_cred, p); - ffree(fp); - fdp->fd_ofiles[indx] = NULL; - return (error); - } - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) + goto bad; fp->f_flag |= FHASLOCK; } + if (flags & O_TRUNC) { + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VATTR_NULL(&vat); + vat.va_size = 0; + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + error = VOP_SETATTR(vp, &vat, p->p_ucred, p); + VOP_UNLOCK(vp, 0, p); + if (error) + goto bad; + } /* assert that vn_open created a backing object if one is needed */ KASSERT(!vn_canvmio(vp) || vp->v_object != NULL, ("open: vmio vnode has no backing object after vn_open")); - VOP_UNLOCK(vp, 0, p); p->p_retval[0] = indx; return (0); +bad: + (void) vn_close(vp, fp->f_flag, fp->f_cred, p); + ffree(fp); + fdp->fd_ofiles[indx] = NULL; + return (error); } #ifdef COMPAT_43 /* * Create a file. */ #ifndef _SYS_SYSPROTO_H_ struct ocreat_args { char *path; int mode; }; #endif int ocreat(p, uap) struct proc *p; register struct ocreat_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { struct open_args /* { syscallarg(char *) path; syscallarg(int) flags; syscallarg(int) mode; } */ nuap; SCARG(&nuap, path) = SCARG(uap, path); SCARG(&nuap, mode) = SCARG(uap, mode); SCARG(&nuap, flags) = O_WRONLY | O_CREAT | O_TRUNC; return (open(p, &nuap)); } #endif /* COMPAT_43 */ /* * Create a special file. */ #ifndef _SYS_SYSPROTO_H_ struct mknod_args { char *path; int mode; int dev; }; #endif /* ARGSUSED */ int mknod(p, uap) struct proc *p; register struct mknod_args /* { syscallarg(char *) path; syscallarg(int) mode; syscallarg(int) dev; } */ *uap; { register struct vnode *vp; struct vattr vattr; int error; int whiteout = 0; struct nameidata nd; switch (SCARG(uap, mode) & S_IFMT) { case S_IFCHR: case S_IFBLK: error = suser(p); break; default: error = suser_xxx(0, p, PRISON_ROOT); break; } if (error) return (error); bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) error = EEXIST; else { VATTR_NULL(&vattr); vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; vattr.va_rdev = SCARG(uap, dev); whiteout = 0; switch (SCARG(uap, mode) & S_IFMT) { case S_IFMT: /* used by badsect to flag bad sectors */ vattr.va_type = VBAD; break; case S_IFCHR: vattr.va_type = VCHR; break; case S_IFBLK: vattr.va_type = VBLK; break; case S_IFWHT: whiteout = 1; break; default: error = EINVAL; break; } } if (!error) { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); if (whiteout) error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); else { error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); } else { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (vp) vrele(vp); } ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mknod"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mknod"); return (error); } /* * Create a named pipe. */ #ifndef _SYS_SYSPROTO_H_ struct mkfifo_args { char *path; int mode; }; #endif /* ARGSUSED */ int mkfifo(p, uap) struct proc *p; register struct mkfifo_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { struct vattr vattr; int error; struct nameidata nd; bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); if (nd.ni_vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); return (EEXIST); } VATTR_NULL(&vattr); vattr.va_type = VFIFO; vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); return (error); } /* * Make a hard file link. */ #ifndef _SYS_SYSPROTO_H_ struct link_args { char *path; char *link; }; #endif /* ARGSUSED */ int link(p, uap) struct proc *p; register struct link_args /* { syscallarg(char *) path; syscallarg(char *) link; } */ *uap; { register struct vnode *vp; struct nameidata nd; int error; bwillwrite(); NDINIT(&nd, LOOKUP, FOLLOW|NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (vp->v_type == VDIR) error = EPERM; /* POSIX */ else { NDINIT(&nd, CREATE, LOCKPARENT|NOOBJ, UIO_USERSPACE, SCARG(uap, link), p); error = namei(&nd); if (!error) { if (nd.ni_vp != NULL) { if (nd.ni_vp) vrele(nd.ni_vp); error = EEXIST; } else { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); } NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); } } vrele(vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "link"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "link"); return (error); } /* * Make a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct symlink_args { char *path; char *link; }; #endif /* ARGSUSED */ int symlink(p, uap) struct proc *p; register struct symlink_args /* { syscallarg(char *) path; syscallarg(char *) link; } */ *uap; { struct vattr vattr; char *path; int error; struct nameidata nd; path = zalloc(namei_zone); if ((error = copyinstr(SCARG(uap, path), path, MAXPATHLEN, NULL)) != 0) goto out; bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT|NOOBJ, UIO_USERSPACE, SCARG(uap, link), p); if ((error = namei(&nd)) != 0) goto out; if (nd.ni_vp) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); error = EEXIST; goto out; } VATTR_NULL(&vattr); vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); NDFREE(&nd, NDF_ONLY_PNBUF); if (error == 0) vput(nd.ni_vp); vput(nd.ni_dvp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "symlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "symlink"); out: zfree(namei_zone, path); return (error); } /* * Delete a whiteout from the filesystem. */ /* ARGSUSED */ int undelete(p, uap) struct proc *p; register struct undelete_args /* { syscallarg(char *) path; } */ *uap; { int error; struct nameidata nd; bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, SCARG(uap, path), p); error = namei(&nd); if (error) return (error); if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); return (EEXIST); } VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "undelete"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "undelete"); return (error); } /* * Delete a name from the filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct unlink_args { char *path; }; #endif /* ARGSUSED */ int unlink(p, uap) struct proc *p; struct unlink_args /* { syscallarg(char *) path; } */ *uap; { register struct vnode *vp; int error; struct nameidata nd; bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EPERM; /* POSIX */ else { /* * The root of a mounted filesystem cannot be deleted. * * XXX: can this only be a VDIR case? */ if (vp->v_flag & VROOT) error = EBUSY; } if (!error) { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); } NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (vp != NULLVP) vput(vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "unlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "unlink"); return (error); } /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct lseek_args { int fd; int pad; off_t offset; int whence; }; #endif int lseek(p, uap) struct proc *p; register struct lseek_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) offset; syscallarg(int) whence; } */ *uap; { struct ucred *cred = p->p_ucred; register struct filedesc *fdp = p->p_fd; register struct file *fp; struct vattr vattr; int error; if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (ESPIPE); switch (SCARG(uap, whence)) { case L_INCR: fp->f_offset += SCARG(uap, offset); break; case L_XTND: error=VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p); if (error) return (error); fp->f_offset = SCARG(uap, offset) + vattr.va_size; break; case L_SET: fp->f_offset = SCARG(uap, offset); break; default: return (EINVAL); } *(off_t *)(p->p_retval) = fp->f_offset; return (0); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct olseek_args { int fd; long offset; int whence; }; #endif int olseek(p, uap) struct proc *p; register struct olseek_args /* { syscallarg(int) fd; syscallarg(long) offset; syscallarg(int) whence; } */ *uap; { struct lseek_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) offset; syscallarg(int) whence; } */ nuap; int error; SCARG(&nuap, fd) = SCARG(uap, fd); SCARG(&nuap, offset) = SCARG(uap, offset); SCARG(&nuap, whence) = SCARG(uap, whence); error = lseek(p, &nuap); return (error); } #endif /* COMPAT_43 */ /* * Check access permissions. */ #ifndef _SYS_SYSPROTO_H_ struct access_args { char *path; int flags; }; #endif int access(p, uap) struct proc *p; register struct access_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { register struct ucred *cred = p->p_ucred; register struct vnode *vp; int error, flags, t_gid, t_uid; struct nameidata nd; t_uid = cred->cr_uid; t_gid = cred->cr_groups[0]; cred->cr_uid = p->p_cred->p_ruid; cred->cr_groups[0] = p->p_cred->p_rgid; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) goto out1; vp = nd.ni_vp; /* Flags == 0 means only check for existence. */ if (SCARG(uap, flags)) { flags = 0; if (SCARG(uap, flags) & R_OK) flags |= VREAD; if (SCARG(uap, flags) & W_OK) flags |= VWRITE; if (SCARG(uap, flags) & X_OK) flags |= VEXEC; if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) error = VOP_ACCESS(vp, flags, cred, p); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); out1: cred->cr_uid = t_uid; cred->cr_groups[0] = t_gid; return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct ostat_args { char *path; struct ostat *ub; }; #endif /* ARGSUSED */ int ostat(p, uap) struct proc *p; register struct ostat_args /* { syscallarg(char *) path; syscallarg(struct ostat *) ub; } */ *uap; { struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)SCARG(uap, ub), sizeof (osb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct olstat_args { char *path; struct ostat *ub; }; #endif /* ARGSUSED */ int olstat(p, uap) struct proc *p; register struct olstat_args /* { syscallarg(char *) path; syscallarg(struct ostat *) ub; } */ *uap; { struct vnode *vp; struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)SCARG(uap, ub), sizeof (osb)); return (error); } /* * Convert from an old to a new stat structure. */ void cvtstat(st, ost) struct stat *st; struct ostat *ost; { ost->st_dev = st->st_dev; ost->st_ino = st->st_ino; ost->st_mode = st->st_mode; ost->st_nlink = st->st_nlink; ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; if (st->st_size < (quad_t)1 << 32) ost->st_size = st->st_size; else ost->st_size = -2; ost->st_atime = st->st_atime; ost->st_mtime = st->st_mtime; ost->st_ctime = st->st_ctime; ost->st_blksize = st->st_blksize; ost->st_blocks = st->st_blocks; ost->st_flags = st->st_flags; ost->st_gen = st->st_gen; } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct stat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int stat(p, uap) struct proc *p; register struct stat_args /* { syscallarg(char *) path; syscallarg(struct stat *) ub; } */ *uap; { struct stat sb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); error = vn_stat(nd.ni_vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int lstat(p, uap) struct proc *p; register struct lstat_args /* { syscallarg(char *) path; syscallarg(struct stat *) ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); return (error); } void cvtnstat(sb, nsb) struct stat *sb; struct nstat *nsb; { nsb->st_dev = sb->st_dev; nsb->st_ino = sb->st_ino; nsb->st_mode = sb->st_mode; nsb->st_nlink = sb->st_nlink; nsb->st_uid = sb->st_uid; nsb->st_gid = sb->st_gid; nsb->st_rdev = sb->st_rdev; nsb->st_atimespec = sb->st_atimespec; nsb->st_mtimespec = sb->st_mtimespec; nsb->st_ctimespec = sb->st_ctimespec; nsb->st_size = sb->st_size; nsb->st_blocks = sb->st_blocks; nsb->st_blksize = sb->st_blksize; nsb->st_flags = sb->st_flags; nsb->st_gen = sb->st_gen; nsb->st_qspare[0] = sb->st_qspare[0]; nsb->st_qspare[1] = sb->st_qspare[1]; } #ifndef _SYS_SYSPROTO_H_ struct nstat_args { char *path; struct nstat *ub; }; #endif /* ARGSUSED */ int nstat(p, uap) struct proc *p; register struct nstat_args /* { syscallarg(char *) path; syscallarg(struct nstat *) ub; } */ *uap; { struct stat sb; struct nstat nsb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout((caddr_t)&nsb, (caddr_t)SCARG(uap, ub), sizeof (nsb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int nlstat(p, uap) struct proc *p; register struct nlstat_args /* { syscallarg(char *) path; syscallarg(struct nstat *) ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nstat nsb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout((caddr_t)&nsb, (caddr_t)SCARG(uap, ub), sizeof (nsb)); return (error); } /* * Get configurable pathname variables. */ #ifndef _SYS_SYSPROTO_H_ struct pathconf_args { char *path; int name; }; #endif /* ARGSUSED */ int pathconf(p, uap) struct proc *p; register struct pathconf_args /* { syscallarg(char *) path; syscallarg(int) name; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = VOP_PATHCONF(nd.ni_vp, SCARG(uap, name), p->p_retval); vput(nd.ni_vp); return (error); } /* * Return target name of a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct readlink_args { char *path; char *buf; int count; }; #endif /* ARGSUSED */ int readlink(p, uap) struct proc *p; register struct readlink_args /* { syscallarg(char *) path; syscallarg(char *) buf; syscallarg(int) count; } */ *uap; { register struct vnode *vp; struct iovec aiov; struct uio auio; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (vp->v_type != VLNK) error = EINVAL; else { aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); error = VOP_READLINK(vp, &auio, p->p_ucred); } vput(vp); p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } static int setfflags(p, vp, flags) struct proc *p; struct vnode *vp; int flags; { int error; struct vattr vattr; /* * Prevent non-root users from setting flags on devices. When * a device is reused, users can retain ownership of the device * if they are allowed to set flags and programs assume that * chown can't fail when done as root. */ if ((vp->v_type == VCHR || vp->v_type == VBLK) && ((error = suser_xxx(p->p_ucred, p, PRISON_ROOT)) != 0)) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_flags = flags; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); return (error); } /* * Change flags of a file given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chflags_args { char *path; int flags; }; #endif /* ARGSUSED */ int chflags(p, uap) struct proc *p; register struct chflags_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfflags(p, nd.ni_vp, SCARG(uap, flags)); vrele(nd.ni_vp); return error; } /* * Change flags of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchflags_args { int fd; int flags; }; #endif /* ARGSUSED */ int fchflags(p, uap) struct proc *p; register struct fchflags_args /* { syscallarg(int) fd; syscallarg(int) flags; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfflags(p, (struct vnode *) fp->f_data, SCARG(uap, flags)); } static int setfmode(p, vp, mode) struct proc *p; struct vnode *vp; int mode; { int error; struct vattr vattr; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_mode = mode & ALLPERMS; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); return error; } /* * Change mode of a file given path name. */ #ifndef _SYS_SYSPROTO_H_ struct chmod_args { char *path; int mode; }; #endif /* ARGSUSED */ int chmod(p, uap) struct proc *p; register struct chmod_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(p, nd.ni_vp, SCARG(uap, mode)); vrele(nd.ni_vp); return error; } /* * Change mode of a file given path name (don't follow links.) */ #ifndef _SYS_SYSPROTO_H_ struct lchmod_args { char *path; int mode; }; #endif /* ARGSUSED */ int lchmod(p, uap) struct proc *p; register struct lchmod_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(p, nd.ni_vp, SCARG(uap, mode)); vrele(nd.ni_vp); return error; } /* * Change mode of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchmod_args { int fd; int mode; }; #endif /* ARGSUSED */ int fchmod(p, uap) struct proc *p; register struct fchmod_args /* { syscallarg(int) fd; syscallarg(int) mode; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfmode(p, (struct vnode *)fp->f_data, SCARG(uap, mode)); } static int setfown(p, vp, uid, gid) struct proc *p; struct vnode *vp; uid_t uid; gid_t gid; { int error; struct vattr vattr; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_uid = uid; vattr.va_gid = gid; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); return error; } /* * Set ownership given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chown_args { char *path; int uid; int gid; }; #endif /* ARGSUSED */ int chown(p, uap) struct proc *p; register struct chown_args /* { syscallarg(char *) path; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(p, nd.ni_vp, SCARG(uap, uid), SCARG(uap, gid)); vrele(nd.ni_vp); return (error); } /* * Set ownership given a path name, do not cross symlinks. */ #ifndef _SYS_SYSPROTO_H_ struct lchown_args { char *path; int uid; int gid; }; #endif /* ARGSUSED */ int lchown(p, uap) struct proc *p; register struct lchown_args /* { syscallarg(char *) path; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(p, nd.ni_vp, SCARG(uap, uid), SCARG(uap, gid)); vrele(nd.ni_vp); return (error); } /* * Set ownership given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchown_args { int fd; int uid; int gid; }; #endif /* ARGSUSED */ int fchown(p, uap) struct proc *p; register struct fchown_args /* { syscallarg(int) fd; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfown(p, (struct vnode *)fp->f_data, SCARG(uap, uid), SCARG(uap, gid)); } static int getutimes(usrtvp, tsp) const struct timeval *usrtvp; struct timespec *tsp; { struct timeval tv[2]; int error; if (usrtvp == NULL) { microtime(&tv[0]); TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); tsp[1] = tsp[0]; } else { if ((error = copyin(usrtvp, tv, sizeof (tv))) != 0) return (error); TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); TIMEVAL_TO_TIMESPEC(&tv[1], &tsp[1]); } return 0; } static int setutimes(p, vp, ts, nullflag) struct proc *p; struct vnode *vp; const struct timespec *ts; int nullflag; { int error; struct vattr vattr; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_atime = ts[0]; vattr.va_mtime = ts[1]; if (nullflag) vattr.va_vaflags |= VA_UTIMES_NULL; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); return error; } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct utimes_args { char *path; struct timeval *tptr; }; #endif /* ARGSUSED */ int utimes(p, uap) struct proc *p; register struct utimes_args /* { syscallarg(char *) path; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct timeval *usrtvp; int error; struct nameidata nd; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(p, nd.ni_vp, ts, usrtvp == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct lutimes_args { char *path; struct timeval *tptr; }; #endif /* ARGSUSED */ int lutimes(p, uap) struct proc *p; register struct lutimes_args /* { syscallarg(char *) path; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct timeval *usrtvp; int error; struct nameidata nd; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(p, nd.ni_vp, ts, usrtvp == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct futimes_args { int fd; struct timeval *tptr; }; #endif /* ARGSUSED */ int futimes(p, uap) struct proc *p; register struct futimes_args /* { syscallarg(int ) fd; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct file *fp; struct timeval *usrtvp; int error; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setutimes(p, (struct vnode *)fp->f_data, ts, usrtvp == NULL); } /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct truncate_args { char *path; int pad; off_t length; }; #endif /* ARGSUSED */ int truncate(p, uap) struct proc *p; register struct truncate_args /* { syscallarg(char *) path; syscallarg(int) pad; syscallarg(off_t) length; } */ *uap; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; if (uap->length < 0) return(EINVAL); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0 && (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) { VATTR_NULL(&vattr); vattr.va_size = SCARG(uap, length); error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct ftruncate_args { int fd; int pad; off_t length; }; #endif /* ARGSUSED */ int ftruncate(p, uap) struct proc *p; register struct ftruncate_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) length; } */ *uap; { struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (uap->length < 0) return(EINVAL); if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FWRITE) == 0) return (EINVAL); vp = (struct vnode *)fp->f_data; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0) { VATTR_NULL(&vattr); vattr.va_size = SCARG(uap, length); error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); } VOP_UNLOCK(vp, 0, p); return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct otruncate_args { char *path; long length; }; #endif /* ARGSUSED */ int otruncate(p, uap) struct proc *p; register struct otruncate_args /* { syscallarg(char *) path; syscallarg(long) length; } */ *uap; { struct truncate_args /* { syscallarg(char *) path; syscallarg(int) pad; syscallarg(off_t) length; } */ nuap; SCARG(&nuap, path) = SCARG(uap, path); SCARG(&nuap, length) = SCARG(uap, length); return (truncate(p, &nuap)); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct oftruncate_args { int fd; long length; }; #endif /* ARGSUSED */ int oftruncate(p, uap) struct proc *p; register struct oftruncate_args /* { syscallarg(int) fd; syscallarg(long) length; } */ *uap; { struct ftruncate_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) length; } */ nuap; SCARG(&nuap, fd) = SCARG(uap, fd); SCARG(&nuap, length) = SCARG(uap, length); return (ftruncate(p, &nuap)); } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Sync an open file. */ #ifndef _SYS_SYSPROTO_H_ struct fsync_args { int fd; }; #endif /* ARGSUSED */ int fsync(p, uap) struct proc *p; struct fsync_args /* { syscallarg(int) fd; } */ *uap; { register struct vnode *vp; struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_object) vm_object_page_clean(vp->v_object, 0, 0, 0); error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); #ifdef SOFTUPDATES if (error == 0 && vp->v_mount && (vp->v_mount->mnt_flag & MNT_SOFTDEP)) error = softdep_fsync(vp); #endif VOP_UNLOCK(vp, 0, p); return (error); } /* * Rename files. Source and destination must either both be directories, * or both not be directories. If target is a directory, it must be empty. */ #ifndef _SYS_SYSPROTO_H_ struct rename_args { char *from; char *to; }; #endif /* ARGSUSED */ int rename(p, uap) struct proc *p; register struct rename_args /* { syscallarg(char *) from; syscallarg(char *) to; } */ *uap; { register struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; bwillwrite(); NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, SCARG(uap, from), p); if ((error = namei(&fromnd)) != 0) return (error); fvp = fromnd.ni_vp; NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | NOOBJ, UIO_USERSPACE, SCARG(uap, to), p); if (fromnd.ni_vp->v_type == VDIR) tond.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&tond)) != 0) { /* Translate error code for rename("dir1", "dir2/."). */ if (error == EISDIR && fvp->v_type == VDIR) error = EINVAL; NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } tdvp = tond.ni_dvp; tvp = tond.ni_vp; if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { error = ENOTDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; goto out; } } if (fvp == tdvp) error = EINVAL; /* * If source is the same as the destination (that is the * same inode number with the same name in the same directory), * then there is nothing to do. */ if (fvp == tvp && fromnd.ni_dvp == tdvp && fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, fromnd.ni_cnd.cn_namelen)) error = -1; out: if (!error) { VOP_LEASE(tdvp, p, p->p_ucred, LEASE_WRITE); if (fromnd.ni_dvp != tdvp) { VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); } if (tvp) { VOP_LEASE(tvp, p, p->p_ucred, LEASE_WRITE); } error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); } else { NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fromnd.ni_dvp); vrele(fvp); } vrele(tond.ni_startdir); ASSERT_VOP_UNLOCKED(fromnd.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(fromnd.ni_vp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_vp, "rename"); out1: if (fromnd.ni_startdir) vrele(fromnd.ni_startdir); if (error == -1) return (0); return (error); } /* * Make a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct mkdir_args { char *path; int mode; }; #endif /* ARGSUSED */ int mkdir(p, uap) struct proc *p; register struct mkdir_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); nd.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(vp); return (EEXIST); } VATTR_NULL(&vattr); vattr.va_type = VDIR; vattr.va_mode = (SCARG(uap, mode) & ACCESSPERMS) &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if (!error) vput(nd.ni_vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mkdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mkdir"); return (error); } /* * Remove a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct rmdir_args { char *path; }; #endif /* ARGSUSED */ int rmdir(p, uap) struct proc *p; struct rmdir_args /* { syscallarg(char *) path; } */ *uap; { register struct vnode *vp; int error; struct nameidata nd; bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) error = EBUSY; else { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } out: NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (vp != NULLVP) vput(vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "rmdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "rmdir"); return (error); } #ifdef COMPAT_43 /* * Read a block of directory entries in a file system independent format. */ #ifndef _SYS_SYSPROTO_H_ struct ogetdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int ogetdirentries(p, uap) struct proc *p; register struct ogetdirentries_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; syscallarg(long *) basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio, kuio; struct iovec aiov, kiov; struct dirent *dp, *edp; caddr_t dirbuf; int error, eofflag, readcnt; long loff; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); loff = auio.uio_offset = fp->f_offset; # if (BYTE_ORDER != LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen <= 0) { error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; } else # endif { kuio = auio; kuio.uio_iov = &kiov; kuio.uio_segflg = UIO_SYSSPACE; kiov.iov_len = SCARG(uap, count); MALLOC(dirbuf, caddr_t, SCARG(uap, count), M_TEMP, M_WAITOK); kiov.iov_base = dirbuf; error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = kuio.uio_offset; if (error == 0) { readcnt = SCARG(uap, count) - kuio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { # if (BYTE_ORDER == LITTLE_ENDIAN) /* * The expected low byte of * dp->d_namlen is our dp->d_type. * The high MBZ byte of dp->d_namlen * is our dp->d_namlen. */ dp->d_type = dp->d_namlen; dp->d_namlen = 0; # else /* * The dp->d_type is the high byte * of the expected dp->d_namlen, * so must be zero'ed. */ dp->d_type = 0; # endif if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, &auio); } FREE(dirbuf, M_TEMP); } VOP_UNLOCK(vp, 0, p); if (error) return (error); if (SCARG(uap, count) == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(p, &vp, fp); if (error == -1) goto unionread; if (error) return (error); } if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_data = (caddr_t) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } } error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), sizeof(long)); p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } #endif /* COMPAT_43 */ /* * Read a block of directory entries in a file system independent format. */ #ifndef _SYS_SYSPROTO_H_ struct getdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int getdirentries(p, uap) struct proc *p; register struct getdirentries_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; syscallarg(long *) basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; long loff; int error, eofflag; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); /* vn_lock(vp, LK_SHARED | LK_RETRY, p); */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); loff = auio.uio_offset = fp->f_offset; error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; VOP_UNLOCK(vp, 0, p); if (error) return (error); if (SCARG(uap, count) == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(p, &vp, fp); if (error == -1) goto unionread; if (error) return (error); } if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_data = (caddr_t) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } } if (SCARG(uap, basep) != NULL) { error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), sizeof(long)); } p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } #ifndef _SYS_SYSPROTO_H_ struct getdents_args { int fd; char *buf; size_t count; }; #endif int getdents(p, uap) struct proc *p; register struct getdents_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; } */ *uap; { struct getdirentries_args ap; ap.fd = uap->fd; ap.buf = uap->buf; ap.count = uap->count; ap.basep = NULL; return getdirentries(p, &ap); } /* * Set the mode mask for creation of filesystem nodes. * * MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct umask_args { int newmask; }; #endif int umask(p, uap) struct proc *p; struct umask_args /* { syscallarg(int) newmask; } */ *uap; { register struct filedesc *fdp; fdp = p->p_fd; p->p_retval[0] = fdp->fd_cmask; fdp->fd_cmask = SCARG(uap, newmask) & ALLPERMS; return (0); } /* * Void all references to file by ripping underlying filesystem * away from vnode. */ #ifndef _SYS_SYSPROTO_H_ struct revoke_args { char *path; }; #endif /* ARGSUSED */ int revoke(p, uap) struct proc *p; register struct revoke_args /* { syscallarg(char *) path; } */ *uap; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (vp->v_type != VCHR && vp->v_type != VBLK) { error = EINVAL; goto out; } if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0) goto out; if (p->p_ucred->cr_uid != vattr.va_uid && (error = suser_xxx(0, p, PRISON_ROOT))) goto out; if (vcount(vp) > 1) VOP_REVOKE(vp, REVOKEALL); out: vrele(vp); return (error); } /* * Convert a user file descriptor to a kernel file entry. */ int getvnode(fdp, fd, fpp) struct filedesc *fdp; int fd; struct file **fpp; { struct file *fp; if ((u_int)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE && fp->f_type != DTYPE_FIFO) return (EINVAL); *fpp = fp; return (0); } /* * Get (NFS) file handle */ #ifndef _SYS_SYSPROTO_H_ struct getfh_args { char *fname; fhandle_t *fhp; }; #endif int getfh(p, uap) struct proc *p; register struct getfh_args *uap; { struct nameidata nd; fhandle_t fh; register struct vnode *vp; int error; /* * Must be super user */ error = suser(p); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; bzero(&fh, sizeof(fh)); fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fh.fh_fid); vput(vp); if (error) return (error); error = copyout(&fh, uap->fhp, sizeof (fh)); return (error); } /* * syscall for the rpc.lockd to use to translate a NFS file handle into * an open descriptor. * * warning: do not remove the suser() call or this becomes one giant * security hole. */ #ifndef _SYS_SYSPROTO_H_ struct fhopen_args { const struct fhandle *u_fhp; int flags; }; #endif int fhopen(p, uap) struct proc *p; struct fhopen_args /* { syscallarg(const struct fhandle *) u_fhp; syscallarg(int) flags; } */ *uap; { struct mount *mp; struct vnode *vp; struct fhandle fhp; struct vattr vat; struct vattr *vap = &vat; struct flock lf; struct file *fp; register struct filedesc *fdp = p->p_fd; int fmode, mode, error, type; struct file *nfp; int indx; /* * Must be super user */ error = suser(p); if (error) return (error); fmode = FFLAGS(SCARG(uap, flags)); /* why not allow a non-read/write open for our lockd? */ if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) return (EINVAL); error = copyin(SCARG(uap,u_fhp), &fhp, sizeof(fhp)); if (error) return(error); /* find the mount point */ mp = vfs_getvfs(&fhp.fh_fsid); if (mp == NULL) return (ESTALE); /* now give me my vnode, it gets returned to me locked */ error = VFS_FHTOVP(mp, &fhp.fh_fid, &vp); if (error) return (error); /* * from now on we have to make sure not * to forget about the vnode * any error that causes an abort must vput(vp) * just set error = err and 'goto bad;'. */ /* * from vn_open */ if (vp->v_type == VLNK) { error = EMLINK; goto bad; } if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto bad; } mode = 0; if (fmode & (FWRITE | O_TRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto bad; } error = vn_writechk(vp); if (error) goto bad; mode |= VWRITE; } if (fmode & FREAD) mode |= VREAD; if (mode) { error = VOP_ACCESS(vp, mode, p->p_ucred, p); if (error) goto bad; } if (fmode & O_TRUNC) { VOP_UNLOCK(vp, 0, p); /* XXX */ VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */ VATTR_NULL(vap); vap->va_size = 0; error = VOP_SETATTR(vp, vap, p->p_ucred, p); if (error) goto bad; } error = VOP_OPEN(vp, fmode, p->p_ucred, p); if (error) goto bad; /* * Make sure that a VM object is created for VMIO support. */ if (vn_canvmio(vp) == TRUE) { if ((error = vfs_object_create(vp, p, p->p_ucred)) != 0) goto bad; } if (fmode & FWRITE) vp->v_writecount++; /* * end of vn_open code */ if ((error = falloc(p, &nfp, &indx)) != 0) goto bad; fp = nfp; nfp->f_data = (caddr_t)vp; nfp->f_flag = fmode & FMASK; nfp->f_ops = &vnops; nfp->f_type = DTYPE_VNODE; if (fmode & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (fmode & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((fmode & FNONBLOCK) == 0) type |= F_WAIT; VOP_UNLOCK(vp, 0, p); if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) { (void) vn_close(vp, fp->f_flag, fp->f_cred, p); ffree(fp); fdp->fd_ofiles[indx] = NULL; return (error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); fp->f_flag |= FHASLOCK; } if ((vp->v_type == VREG) && (vp->v_object == NULL)) vfs_object_create(vp, p, p->p_ucred); VOP_UNLOCK(vp, 0, p); p->p_retval[0] = indx; return (0); bad: vput(vp); return (error); } #ifndef _SYS_SYSPROTO_H_ struct fhstat_args { struct fhandle *u_fhp; struct stat *sb; }; #endif int fhstat(p, uap) struct proc *p; register struct fhstat_args /* { syscallarg(struct fhandle *) u_fhp; syscallarg(struct stat *) sb; } */ *uap; { struct stat sb; fhandle_t fh; struct mount *mp; struct vnode *vp; int error; /* * Must be super user */ error = suser(p); if (error) return (error); error = copyin(SCARG(uap, u_fhp), &fh, sizeof(fhandle_t)); if (error) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); error = copyout(&sb, SCARG(uap, sb), sizeof(sb)); return (error); } #ifndef _SYS_SYSPROTO_H_ struct fhstatfs_args { struct fhandle *u_fhp; struct statfs *buf; }; #endif int fhstatfs(p, uap) struct proc *p; struct fhstatfs_args /* { syscallarg(struct fhandle) *u_fhp; syscallarg(struct statfs) *buf; } */ *uap; { struct statfs *sp; struct mount *mp; struct vnode *vp; struct statfs sb; fhandle_t fh; int error; /* * Must be super user */ if ((error = suser(p))) return (error); if ((error = copyin(SCARG(uap, u_fhp), &fh, sizeof(fhandle_t))) != 0) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); mp = vp->v_mount; sp = &mp->mnt_stat; vput(vp); if ((error = VFS_STATFS(mp, sp, p)) != 0) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout(sp, SCARG(uap, buf), sizeof(*sp))); } /* * Syscall to push extended attribute configuration information into the * VFS. Accepts a path, which it converts to a mountpoint, as well as * a command (int cmd), and attribute name and misc data. For now, the * attribute name is left in userspace for consumption by the VFS_op. * It will probably be changed to be copied into sysspace by the * syscall in the future, once issues with various consumers of the * attribute code have raised their hands. * * Currently this is used only by UFS Extended Attributes. */ int extattrctl(p, uap) struct proc *p; struct extattrctl_args *uap; { struct nameidata nd; struct mount *mp; int error; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; NDFREE(&nd, 0); return (VFS_EXTATTRCTL(mp, SCARG(uap, cmd), SCARG(uap, attrname), SCARG(uap, arg), p)); } /* * Syscall to set a named extended attribute on a file or directory. * Accepts attribute name, and a uio structure pointing to the data to set. * The uio is consumed in the style of writev(). The real work happens * in VOP_SETEXTATTR(). */ int extattr_set_file(p, uap) struct proc *p; struct extattr_set_file_args *uap; { struct nameidata nd; struct uio auio; struct iovec *iov, *needfree = NULL, aiov[UIO_SMALLIOV]; char attrname[EXTATTR_MAXNAMELEN]; u_int iovlen, cnt; int error, i; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return (error); NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return(error); iovlen = uap->iovcnt * sizeof(struct iovec); if (uap->iovcnt > UIO_SMALLIOV) { if (uap->iovcnt > UIO_MAXIOV) { error = EINVAL; goto done; } MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK); needfree = iov; } else iov = aiov; auio.uio_iov = iov; auio.uio_iovcnt = uap->iovcnt; auio.uio_rw = UIO_WRITE; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_offset = 0; if ((error = copyin((caddr_t)uap->iovp, (caddr_t)iov, iovlen))) goto done; auio.uio_resid = 0; for (i = 0; i < uap->iovcnt; i++) { if (iov->iov_len > INT_MAX - auio.uio_resid) { error = EINVAL; goto done; } auio.uio_resid += iov->iov_len; iov++; } cnt = auio.uio_resid; error = VOP_SETEXTATTR(nd.ni_vp, attrname, &auio, p->p_cred->pc_ucred, p); if (auio.uio_resid != cnt && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; cnt -= auio.uio_resid; p->p_retval[0] = cnt; done: if (needfree) FREE(needfree, M_IOV); NDFREE(&nd, 0); return (error); } /* * Syscall to get a named extended attribute on a file or directory. * Accepts attribute name, and a uio structure pointing to a buffer for the * data. The uio is consumed in the style of readv(). The real work * happens in VOP_GETEXTATTR(); */ int extattr_get_file(p, uap) struct proc *p; struct extattr_get_file_args *uap; { struct nameidata nd; struct uio auio; struct iovec *iov, *needfree, aiov[UIO_SMALLIOV]; char attrname[EXTATTR_MAXNAMELEN]; u_int iovlen, cnt; int error, i; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); iovlen = uap->iovcnt * sizeof (struct iovec); if (uap->iovcnt > UIO_SMALLIOV) { if (uap->iovcnt > UIO_MAXIOV) { NDFREE(&nd, 0); return (EINVAL); } MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK); needfree = iov; } else { iov = aiov; needfree = NULL; } auio.uio_iov = iov; auio.uio_iovcnt = uap->iovcnt; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_offset = 0; if ((error = copyin((caddr_t)uap->iovp, (caddr_t)iov, iovlen))) goto done; auio.uio_resid = 0; for (i = 0; i < uap->iovcnt; i++) { if (iov->iov_len > INT_MAX - auio.uio_resid) { error = EINVAL; goto done; } auio.uio_resid += iov->iov_len; iov++; } cnt = auio.uio_resid; error = VOP_GETEXTATTR(nd.ni_vp, attrname, &auio, p->p_cred->pc_ucred, p); if (auio.uio_resid != cnt && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; cnt -= auio.uio_resid; p->p_retval[0] = cnt; done: if (needfree) FREE(needfree, M_IOV); NDFREE(&nd, 0); return(error); } /* * Syscall to delete a named extended attribute from a file or directory. * Accepts attribute name. The real work happens in VOP_SETEXTATTR(). */ int extattr_delete_file(p, uap) struct proc *p; struct extattr_delete_file_args *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return(error); NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return(error); error = VOP_SETEXTATTR(nd.ni_vp, attrname, NULL, p->p_cred->pc_ucred, p); NDFREE(&nd, 0); return(error); } Index: head/sys/kern/vfs_syscalls.c =================================================================== --- head/sys/kern/vfs_syscalls.c (revision 62549) +++ head/sys/kern/vfs_syscalls.c (revision 62550) @@ -1,3565 +1,3575 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 * $FreeBSD$ */ /* For 4.3 integer FS ID compatibility */ #include "opt_compat.h" #include "opt_ffs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int change_dir __P((struct nameidata *ndp, struct proc *p)); static void checkdirs __P((struct vnode *olddp)); static int chroot_refuse_vdir_fds __P((struct filedesc *fdp)); static int getutimes __P((const struct timeval *, struct timespec *)); static int setfown __P((struct proc *, struct vnode *, uid_t, gid_t)); static int setfmode __P((struct proc *, struct vnode *, int)); static int setfflags __P((struct proc *, struct vnode *, int)); static int setutimes __P((struct proc *, struct vnode *, const struct timespec *, int)); static int usermount = 0; /* if 1, non-root can mount fs. */ int (*union_dircheckp) __P((struct proc *, struct vnode **, struct file *)); SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0, ""); /* * Virtual File System System Calls */ /* * Mount a file system. */ #ifndef _SYS_SYSPROTO_H_ struct mount_args { char *type; char *path; int flags; caddr_t data; }; #endif /* ARGSUSED */ int mount(p, uap) struct proc *p; register struct mount_args /* { syscallarg(char *) type; syscallarg(char *) path; syscallarg(int) flags; syscallarg(caddr_t) data; } */ *uap; { struct vnode *vp; struct mount *mp; struct vfsconf *vfsp; int error, flag = 0, flag2 = 0; struct vattr va; #ifdef COMPAT_43 u_long fstypenum; #endif struct nameidata nd; char fstypename[MFSNAMELEN]; if (usermount == 0 && (error = suser(p))) return (error); /* * Do not allow NFS export by non-root users. */ if (SCARG(uap, flags) & MNT_EXPORTED) { error = suser(p); if (error) return (error); } /* * Silently enforce MNT_NOSUID and MNT_NODEV for non-root users */ if (suser_xxx(p->p_ucred, 0, 0)) SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; /* * Get vnode to be covered */ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (SCARG(uap, flags) & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; flag = mp->mnt_flag; flag2 = mp->mnt_kern_flag; /* * We only allow the filesystem to be reloaded if it * is currently mounted read-only. */ if ((SCARG(uap, flags) & MNT_RELOAD) && ((mp->mnt_flag & MNT_RDONLY) == 0)) { vput(vp); return (EOPNOTSUPP); /* Needs translation */ } mp->mnt_flag |= SCARG(uap, flags) & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE); /* * Only root, or the user that did the original mount is * permitted to update it. */ if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid && (error = suser(p))) { vput(vp); return (error); } if (vfs_busy(mp, LK_NOWAIT, 0, p)) { vput(vp); return (EBUSY); } VOP_UNLOCK(vp, 0, p); goto update; } /* * If the user is not root, ensure that they own the directory * onto which we are attempting to mount. */ if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || (va.va_uid != p->p_ucred->cr_uid && (error = suser(p)))) { vput(vp); return (error); } if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) return (error); if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } #ifdef COMPAT_43 /* * Historically filesystem types were identified by number. If we * get an integer for the filesystem type instead of a string, we * check to see if it matches one of the historic filesystem types. */ fstypenum = (uintptr_t)SCARG(uap, type); if (fstypenum < maxvfsconf) { for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (vfsp->vfc_typenum == fstypenum) break; if (vfsp == NULL) { vput(vp); return (ENODEV); } strncpy(fstypename, vfsp->vfc_name, MFSNAMELEN); } else #endif /* COMPAT_43 */ if ((error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, NULL)) != 0) { vput(vp); return (error); } for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (!strcmp(vfsp->vfc_name, fstypename)) break; if (vfsp == NULL) { linker_file_t lf; /* Only load modules for root (very important!) */ if ((error = suser(p)) != 0) { vput(vp); return error; } error = linker_load_file(fstypename, &lf); if (error || lf == NULL) { vput(vp); if (lf == NULL) error = ENODEV; return error; } lf->userrefs++; /* lookup again, see if the VFS was loaded */ for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (!strcmp(vfsp->vfc_name, fstypename)) break; if (vfsp == NULL) { lf->userrefs--; linker_file_unload(lf); vput(vp); return (ENODEV); } } simple_lock(&vp->v_interlock); if ((vp->v_flag & VMOUNT) != 0 || vp->v_mountedhere != NULL) { simple_unlock(&vp->v_interlock); vput(vp); return (EBUSY); } vp->v_flag |= VMOUNT; simple_unlock(&vp->v_interlock); /* * Allocate and initialize the filesystem. */ mp = malloc(sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, LK_NOPAUSE); (void)vfs_busy(mp, LK_NOWAIT, 0, p); mp->mnt_op = vfsp->vfc_vfsops; mp->mnt_vfc = vfsp; vfsp->vfc_refcount++; mp->mnt_stat.f_type = vfsp->vfc_typenum; mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK; strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN); mp->mnt_vnodecovered = vp; mp->mnt_stat.f_owner = p->p_ucred->cr_uid; mp->mnt_iosize_max = DFLTPHYS; VOP_UNLOCK(vp, 0, p); update: /* * Set the mount level flags. */ if (SCARG(uap, flags) & MNT_RDONLY) mp->mnt_flag |= MNT_RDONLY; else if (mp->mnt_flag & MNT_RDONLY) mp->mnt_kern_flag |= MNTK_WANTRDWR; mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_NOATIME | MNT_NOSYMFOLLOW | MNT_IGNORE | MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR); mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_FORCE | MNT_NOSYMFOLLOW | MNT_IGNORE | MNT_NOATIME | MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR); /* * Mount the filesystem. * XXX The final recipients of VFS_MOUNT just overwrite the ndp they * get. No freeing of cn_pnbuf. */ error = VFS_MOUNT(mp, SCARG(uap, path), SCARG(uap, data), &nd, p); if (mp->mnt_flag & MNT_UPDATE) { vrele(vp); if (mp->mnt_kern_flag & MNTK_WANTRDWR) mp->mnt_flag &= ~MNT_RDONLY; mp->mnt_flag &=~ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE); mp->mnt_kern_flag &=~ MNTK_WANTRDWR; if (error) { mp->mnt_flag = flag; mp->mnt_kern_flag = flag2; } if ((mp->mnt_flag & MNT_RDONLY) == 0) { if (mp->mnt_syncer == NULL) error = vfs_allocate_syncvnode(mp); } else { if (mp->mnt_syncer != NULL) vrele(mp->mnt_syncer); mp->mnt_syncer = NULL; } vfs_unbusy(mp, p); return (error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* * Put the new filesystem on the mount list after root. */ cache_purge(vp); if (!error) { simple_lock(&vp->v_interlock); vp->v_flag &= ~VMOUNT; vp->v_mountedhere = mp; simple_unlock(&vp->v_interlock); simple_lock(&mountlist_slock); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); simple_unlock(&mountlist_slock); checkdirs(vp); VOP_UNLOCK(vp, 0, p); if ((mp->mnt_flag & MNT_RDONLY) == 0) error = vfs_allocate_syncvnode(mp); vfs_unbusy(mp, p); if ((error = VFS_START(mp, 0, p)) != 0) vrele(vp); } else { simple_lock(&vp->v_interlock); vp->v_flag &= ~VMOUNT; simple_unlock(&vp->v_interlock); mp->mnt_vfc->vfc_refcount--; vfs_unbusy(mp, p); free((caddr_t)mp, M_MOUNT); vput(vp); } return (error); } /* * Scan all active processes to see if any of them have a current * or root directory onto which the new filesystem has just been * mounted. If so, replace them with the new mount point. */ static void checkdirs(olddp) struct vnode *olddp; { struct filedesc *fdp; struct vnode *newdp; struct proc *p; if (olddp->v_usecount == 1) return; if (VFS_ROOT(olddp->v_mountedhere, &newdp)) panic("mount: lost mount"); LIST_FOREACH(p, &allproc, p_list) { fdp = p->p_fd; if (fdp->fd_cdir == olddp) { vrele(fdp->fd_cdir); VREF(newdp); fdp->fd_cdir = newdp; } if (fdp->fd_rdir == olddp) { vrele(fdp->fd_rdir); VREF(newdp); fdp->fd_rdir = newdp; } } if (rootvnode == olddp) { vrele(rootvnode); VREF(newdp); rootvnode = newdp; } vput(newdp); } /* * Unmount a file system. * * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). */ #ifndef _SYS_SYSPROTO_H_ struct unmount_args { char *path; int flags; }; #endif /* ARGSUSED */ int unmount(p, uap) struct proc *p; register struct unmount_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { register struct vnode *vp; struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); mp = vp->v_mount; /* * Only root, or the user that did the original mount is * permitted to unmount this filesystem. */ if ((mp->mnt_stat.f_owner != p->p_ucred->cr_uid) && (error = suser(p))) { vput(vp); return (error); } /* * Don't allow unmounting the root file system. */ if (mp->mnt_flag & MNT_ROOTFS) { vput(vp); return (EINVAL); } /* * Must be the root of the filesystem */ if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } vput(vp); return (dounmount(mp, SCARG(uap, flags), p)); } /* * Do the actual file system unmount. */ int dounmount(mp, flags, p) register struct mount *mp; int flags; struct proc *p; { struct vnode *coveredvp; int error; int async_flag; simple_lock(&mountlist_slock); mp->mnt_kern_flag |= MNTK_UNMOUNT; lockmgr(&mp->mnt_lock, LK_DRAIN | LK_INTERLOCK, &mountlist_slock, p); if (mp->mnt_flag & MNT_EXPUBLIC) vfs_setpublicfs(NULL, NULL, NULL); vfs_msync(mp, MNT_WAIT); async_flag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &=~ MNT_ASYNC; cache_purgevfs(mp); /* remove cache entries for this file sys */ if (mp->mnt_syncer != NULL) vrele(mp->mnt_syncer); if (((mp->mnt_flag & MNT_RDONLY) || (error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0) || (flags & MNT_FORCE)) error = VFS_UNMOUNT(mp, flags, p); simple_lock(&mountlist_slock); if (error) { if ((mp->mnt_flag & MNT_RDONLY) == 0 && mp->mnt_syncer == NULL) (void) vfs_allocate_syncvnode(mp); mp->mnt_kern_flag &= ~MNTK_UNMOUNT; mp->mnt_flag |= async_flag; lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK | LK_REENABLE, &mountlist_slock, p); if (mp->mnt_kern_flag & MNTK_MWAIT) wakeup((caddr_t)mp); return (error); } TAILQ_REMOVE(&mountlist, mp, mnt_list); if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) { coveredvp->v_mountedhere = (struct mount *)0; vrele(coveredvp); } mp->mnt_vfc->vfc_refcount--; if (!LIST_EMPTY(&mp->mnt_vnodelist)) panic("unmount: dangling vnode"); lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock, p); if (mp->mnt_kern_flag & MNTK_MWAIT) wakeup((caddr_t)mp); free((caddr_t)mp, M_MOUNT); return (0); } /* * Sync each mounted filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct sync_args { int dummy; }; #endif #ifdef DEBUG static int syncprt = 0; SYSCTL_INT(_debug, OID_AUTO, syncprt, CTLFLAG_RW, &syncprt, 0, ""); #endif /* ARGSUSED */ int sync(p, uap) struct proc *p; struct sync_args *uap; { register struct mount *mp, *nmp; int asyncflag; simple_lock(&mountlist_slock); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if ((mp->mnt_flag & MNT_RDONLY) == 0) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; vfs_msync(mp, MNT_NOWAIT); VFS_SYNC(mp, MNT_NOWAIT, ((p != NULL) ? p->p_ucred : NOCRED), p); mp->mnt_flag |= asyncflag; } simple_lock(&mountlist_slock); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); } simple_unlock(&mountlist_slock); #if 0 /* * XXX don't call vfs_bufstats() yet because that routine * was not imported in the Lite2 merge. */ #ifdef DIAGNOSTIC if (syncprt) vfs_bufstats(); #endif /* DIAGNOSTIC */ #endif return (0); } /* XXX PRISON: could be per prison flag */ static int prison_quotas; #if 0 SYSCTL_INT(_kern_prison, OID_AUTO, quotas, CTLFLAG_RW, &prison_quotas, 0, ""); #endif /* * Change filesystem quotas. */ #ifndef _SYS_SYSPROTO_H_ struct quotactl_args { char *path; int cmd; int uid; caddr_t arg; }; #endif /* ARGSUSED */ int quotactl(p, uap) struct proc *p; register struct quotactl_args /* { syscallarg(char *) path; syscallarg(int) cmd; syscallarg(int) uid; syscallarg(caddr_t) arg; } */ *uap; { register struct mount *mp; int error; struct nameidata nd; if (p->p_prison && !prison_quotas) return (EPERM); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); return (VFS_QUOTACTL(mp, SCARG(uap, cmd), SCARG(uap, uid), SCARG(uap, arg), p)); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct statfs_args { char *path; struct statfs *buf; }; #endif /* ARGSUSED */ int statfs(p, uap) struct proc *p; register struct statfs_args /* { syscallarg(char *) path; syscallarg(struct statfs *) buf; } */ *uap; { register struct mount *mp; register struct statfs *sp; int error; struct nameidata nd; struct statfs sb; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); error = VFS_STATFS(mp, sp, p); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct fstatfs_args { int fd; struct statfs *buf; }; #endif /* ARGSUSED */ int fstatfs(p, uap) struct proc *p; register struct fstatfs_args /* { syscallarg(int) fd; syscallarg(struct statfs *) buf; } */ *uap; { struct file *fp; struct mount *mp; register struct statfs *sp; int error; struct statfs sb; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); mp = ((struct vnode *)fp->f_data)->v_mount; sp = &mp->mnt_stat; error = VFS_STATFS(mp, sp, p); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); } /* * Get statistics on all filesystems. */ #ifndef _SYS_SYSPROTO_H_ struct getfsstat_args { struct statfs *buf; long bufsize; int flags; }; #endif int getfsstat(p, uap) struct proc *p; register struct getfsstat_args /* { syscallarg(struct statfs *) buf; syscallarg(long) bufsize; syscallarg(int) flags; } */ *uap; { register struct mount *mp, *nmp; register struct statfs *sp; caddr_t sfsp; long count, maxcount, error; maxcount = SCARG(uap, bufsize) / sizeof(struct statfs); sfsp = (caddr_t)SCARG(uap, buf); count = 0; simple_lock(&mountlist_slock); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if (sfsp && count < maxcount) { sp = &mp->mnt_stat; /* * If MNT_NOWAIT or MNT_LAZY is specified, do not * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY * overrides MNT_WAIT. */ if (((SCARG(uap, flags) & (MNT_LAZY|MNT_NOWAIT)) == 0 || (SCARG(uap, flags) & MNT_WAIT)) && (error = VFS_STATFS(mp, sp, p))) { simple_lock(&mountlist_slock); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); continue; } sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = copyout((caddr_t)sp, sfsp, sizeof(*sp)); if (error) { vfs_unbusy(mp, p); return (error); } sfsp += sizeof(*sp); } count++; simple_lock(&mountlist_slock); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); } simple_unlock(&mountlist_slock); if (sfsp && count > maxcount) p->p_retval[0] = maxcount; else p->p_retval[0] = count; return (0); } /* * Change current working directory to a given file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchdir_args { int fd; }; #endif /* ARGSUSED */ int fchdir(p, uap) struct proc *p; struct fchdir_args /* { syscallarg(int) fd; } */ *uap; { register struct filedesc *fdp = p->p_fd; struct vnode *vp, *tdp; struct mount *mp; struct file *fp; int error; if ((error = getvnode(fdp, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; VREF(vp); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); while (!error && (mp = vp->v_mountedhere) != NULL) { if (vfs_busy(mp, 0, 0, p)) continue; error = VFS_ROOT(mp, &tdp); vfs_unbusy(mp, p); if (error) break; vput(vp); vp = tdp; } if (error) { vput(vp); return (error); } VOP_UNLOCK(vp, 0, p); vrele(fdp->fd_cdir); fdp->fd_cdir = vp; return (0); } /* * Change current working directory (``.''). */ #ifndef _SYS_SYSPROTO_H_ struct chdir_args { char *path; }; #endif /* ARGSUSED */ int chdir(p, uap) struct proc *p; struct chdir_args /* { syscallarg(char *) path; } */ *uap; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = change_dir(&nd, p)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vrele(fdp->fd_cdir); fdp->fd_cdir = nd.ni_vp; return (0); } /* * Helper function for raised chroot(2) security function: Refuse if * any filedescriptors are open directories. */ static int chroot_refuse_vdir_fds(fdp) struct filedesc *fdp; { struct vnode *vp; struct file *fp; int error; int fd; for (fd = 0; fd < fdp->fd_nfiles ; fd++) { error = getvnode(fdp, fd, &fp); if (error) continue; vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) continue; return(EPERM); } return (0); } /* * This sysctl determines if we will allow a process to chroot(2) if it * has a directory open: * 0: disallowed for all processes. * 1: allowed for processes that were not already chroot(2)'ed. * 2: allowed for all processes. */ static int chroot_allow_open_directories = 1; SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW, &chroot_allow_open_directories, 0, ""); /* * Change notion of root (``/'') directory. */ #ifndef _SYS_SYSPROTO_H_ struct chroot_args { char *path; }; #endif /* ARGSUSED */ int chroot(p, uap) struct proc *p; struct chroot_args /* { syscallarg(char *) path; } */ *uap; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; error = suser_xxx(0, p, PRISON_ROOT); if (error) return (error); if (chroot_allow_open_directories == 0 || (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) error = chroot_refuse_vdir_fds(fdp); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = change_dir(&nd, p)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vrele(fdp->fd_rdir); fdp->fd_rdir = nd.ni_vp; if (!fdp->fd_jdir) { fdp->fd_jdir = nd.ni_vp; VREF(fdp->fd_jdir); } return (0); } /* * Common routine for chroot and chdir. */ static int change_dir(ndp, p) register struct nameidata *ndp; struct proc *p; { struct vnode *vp; int error; error = namei(ndp); if (error) return (error); vp = ndp->ni_vp; if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); if (error) vput(vp); else VOP_UNLOCK(vp, 0, p); return (error); } /* * Check permissions, allocate an open file structure, * and call the device open routine if any. */ #ifndef _SYS_SYSPROTO_H_ struct open_args { char *path; int flags; int mode; }; #endif int open(p, uap) struct proc *p; register struct open_args /* { syscallarg(char *) path; syscallarg(int) flags; syscallarg(int) mode; } */ *uap; { - register struct filedesc *fdp = p->p_fd; - register struct file *fp; - register struct vnode *vp; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + struct vattr vat; int cmode, flags, oflags; struct file *nfp; int type, indx, error; struct flock lf; struct nameidata nd; oflags = SCARG(uap, flags); if ((oflags & O_ACCMODE) == O_ACCMODE) return (EINVAL); flags = FFLAGS(oflags); error = falloc(p, &nfp, &indx); if (error) return (error); fp = nfp; cmode = ((SCARG(uap, mode) &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); p->p_dupfd = -indx - 1; /* XXX check for fdopen */ - error = vn_open(&nd, flags, cmode); + error = vn_open(&nd, &flags, cmode); if (error) { ffree(fp); if ((error == ENODEV || error == ENXIO) && p->p_dupfd >= 0 && /* XXX from fdopen */ (error = dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { p->p_retval[0] = indx; return (0); } if (error == ERESTART) error = EINTR; fdp->fd_ofiles[indx] = NULL; return (error); } p->p_dupfd = 0; NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; fp->f_data = (caddr_t)vp; fp->f_flag = flags & FMASK; fp->f_ops = &vnops; fp->f_type = (vp->v_type == VFIFO ? DTYPE_FIFO : DTYPE_VNODE); + VOP_UNLOCK(vp, 0, p); if (flags & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (flags & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((flags & FNONBLOCK) == 0) type |= F_WAIT; - VOP_UNLOCK(vp, 0, p); - if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) { - (void) vn_close(vp, fp->f_flag, fp->f_cred, p); - ffree(fp); - fdp->fd_ofiles[indx] = NULL; - return (error); - } - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) + goto bad; fp->f_flag |= FHASLOCK; } + if (flags & O_TRUNC) { + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VATTR_NULL(&vat); + vat.va_size = 0; + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + error = VOP_SETATTR(vp, &vat, p->p_ucred, p); + VOP_UNLOCK(vp, 0, p); + if (error) + goto bad; + } /* assert that vn_open created a backing object if one is needed */ KASSERT(!vn_canvmio(vp) || vp->v_object != NULL, ("open: vmio vnode has no backing object after vn_open")); - VOP_UNLOCK(vp, 0, p); p->p_retval[0] = indx; return (0); +bad: + (void) vn_close(vp, fp->f_flag, fp->f_cred, p); + ffree(fp); + fdp->fd_ofiles[indx] = NULL; + return (error); } #ifdef COMPAT_43 /* * Create a file. */ #ifndef _SYS_SYSPROTO_H_ struct ocreat_args { char *path; int mode; }; #endif int ocreat(p, uap) struct proc *p; register struct ocreat_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { struct open_args /* { syscallarg(char *) path; syscallarg(int) flags; syscallarg(int) mode; } */ nuap; SCARG(&nuap, path) = SCARG(uap, path); SCARG(&nuap, mode) = SCARG(uap, mode); SCARG(&nuap, flags) = O_WRONLY | O_CREAT | O_TRUNC; return (open(p, &nuap)); } #endif /* COMPAT_43 */ /* * Create a special file. */ #ifndef _SYS_SYSPROTO_H_ struct mknod_args { char *path; int mode; int dev; }; #endif /* ARGSUSED */ int mknod(p, uap) struct proc *p; register struct mknod_args /* { syscallarg(char *) path; syscallarg(int) mode; syscallarg(int) dev; } */ *uap; { register struct vnode *vp; struct vattr vattr; int error; int whiteout = 0; struct nameidata nd; switch (SCARG(uap, mode) & S_IFMT) { case S_IFCHR: case S_IFBLK: error = suser(p); break; default: error = suser_xxx(0, p, PRISON_ROOT); break; } if (error) return (error); bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) error = EEXIST; else { VATTR_NULL(&vattr); vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; vattr.va_rdev = SCARG(uap, dev); whiteout = 0; switch (SCARG(uap, mode) & S_IFMT) { case S_IFMT: /* used by badsect to flag bad sectors */ vattr.va_type = VBAD; break; case S_IFCHR: vattr.va_type = VCHR; break; case S_IFBLK: vattr.va_type = VBLK; break; case S_IFWHT: whiteout = 1; break; default: error = EINVAL; break; } } if (!error) { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); if (whiteout) error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); else { error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); } else { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (vp) vrele(vp); } ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mknod"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mknod"); return (error); } /* * Create a named pipe. */ #ifndef _SYS_SYSPROTO_H_ struct mkfifo_args { char *path; int mode; }; #endif /* ARGSUSED */ int mkfifo(p, uap) struct proc *p; register struct mkfifo_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { struct vattr vattr; int error; struct nameidata nd; bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); if (nd.ni_vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); return (EEXIST); } VATTR_NULL(&vattr); vattr.va_type = VFIFO; vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); return (error); } /* * Make a hard file link. */ #ifndef _SYS_SYSPROTO_H_ struct link_args { char *path; char *link; }; #endif /* ARGSUSED */ int link(p, uap) struct proc *p; register struct link_args /* { syscallarg(char *) path; syscallarg(char *) link; } */ *uap; { register struct vnode *vp; struct nameidata nd; int error; bwillwrite(); NDINIT(&nd, LOOKUP, FOLLOW|NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (vp->v_type == VDIR) error = EPERM; /* POSIX */ else { NDINIT(&nd, CREATE, LOCKPARENT|NOOBJ, UIO_USERSPACE, SCARG(uap, link), p); error = namei(&nd); if (!error) { if (nd.ni_vp != NULL) { if (nd.ni_vp) vrele(nd.ni_vp); error = EEXIST; } else { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); } NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); } } vrele(vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "link"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "link"); return (error); } /* * Make a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct symlink_args { char *path; char *link; }; #endif /* ARGSUSED */ int symlink(p, uap) struct proc *p; register struct symlink_args /* { syscallarg(char *) path; syscallarg(char *) link; } */ *uap; { struct vattr vattr; char *path; int error; struct nameidata nd; path = zalloc(namei_zone); if ((error = copyinstr(SCARG(uap, path), path, MAXPATHLEN, NULL)) != 0) goto out; bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT|NOOBJ, UIO_USERSPACE, SCARG(uap, link), p); if ((error = namei(&nd)) != 0) goto out; if (nd.ni_vp) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); error = EEXIST; goto out; } VATTR_NULL(&vattr); vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); NDFREE(&nd, NDF_ONLY_PNBUF); if (error == 0) vput(nd.ni_vp); vput(nd.ni_dvp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "symlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "symlink"); out: zfree(namei_zone, path); return (error); } /* * Delete a whiteout from the filesystem. */ /* ARGSUSED */ int undelete(p, uap) struct proc *p; register struct undelete_args /* { syscallarg(char *) path; } */ *uap; { int error; struct nameidata nd; bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, SCARG(uap, path), p); error = namei(&nd); if (error) return (error); if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); return (EEXIST); } VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "undelete"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "undelete"); return (error); } /* * Delete a name from the filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct unlink_args { char *path; }; #endif /* ARGSUSED */ int unlink(p, uap) struct proc *p; struct unlink_args /* { syscallarg(char *) path; } */ *uap; { register struct vnode *vp; int error; struct nameidata nd; bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EPERM; /* POSIX */ else { /* * The root of a mounted filesystem cannot be deleted. * * XXX: can this only be a VDIR case? */ if (vp->v_flag & VROOT) error = EBUSY; } if (!error) { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); } NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (vp != NULLVP) vput(vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "unlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "unlink"); return (error); } /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct lseek_args { int fd; int pad; off_t offset; int whence; }; #endif int lseek(p, uap) struct proc *p; register struct lseek_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) offset; syscallarg(int) whence; } */ *uap; { struct ucred *cred = p->p_ucred; register struct filedesc *fdp = p->p_fd; register struct file *fp; struct vattr vattr; int error; if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (ESPIPE); switch (SCARG(uap, whence)) { case L_INCR: fp->f_offset += SCARG(uap, offset); break; case L_XTND: error=VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p); if (error) return (error); fp->f_offset = SCARG(uap, offset) + vattr.va_size; break; case L_SET: fp->f_offset = SCARG(uap, offset); break; default: return (EINVAL); } *(off_t *)(p->p_retval) = fp->f_offset; return (0); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct olseek_args { int fd; long offset; int whence; }; #endif int olseek(p, uap) struct proc *p; register struct olseek_args /* { syscallarg(int) fd; syscallarg(long) offset; syscallarg(int) whence; } */ *uap; { struct lseek_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) offset; syscallarg(int) whence; } */ nuap; int error; SCARG(&nuap, fd) = SCARG(uap, fd); SCARG(&nuap, offset) = SCARG(uap, offset); SCARG(&nuap, whence) = SCARG(uap, whence); error = lseek(p, &nuap); return (error); } #endif /* COMPAT_43 */ /* * Check access permissions. */ #ifndef _SYS_SYSPROTO_H_ struct access_args { char *path; int flags; }; #endif int access(p, uap) struct proc *p; register struct access_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { register struct ucred *cred = p->p_ucred; register struct vnode *vp; int error, flags, t_gid, t_uid; struct nameidata nd; t_uid = cred->cr_uid; t_gid = cred->cr_groups[0]; cred->cr_uid = p->p_cred->p_ruid; cred->cr_groups[0] = p->p_cred->p_rgid; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) goto out1; vp = nd.ni_vp; /* Flags == 0 means only check for existence. */ if (SCARG(uap, flags)) { flags = 0; if (SCARG(uap, flags) & R_OK) flags |= VREAD; if (SCARG(uap, flags) & W_OK) flags |= VWRITE; if (SCARG(uap, flags) & X_OK) flags |= VEXEC; if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) error = VOP_ACCESS(vp, flags, cred, p); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); out1: cred->cr_uid = t_uid; cred->cr_groups[0] = t_gid; return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct ostat_args { char *path; struct ostat *ub; }; #endif /* ARGSUSED */ int ostat(p, uap) struct proc *p; register struct ostat_args /* { syscallarg(char *) path; syscallarg(struct ostat *) ub; } */ *uap; { struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)SCARG(uap, ub), sizeof (osb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct olstat_args { char *path; struct ostat *ub; }; #endif /* ARGSUSED */ int olstat(p, uap) struct proc *p; register struct olstat_args /* { syscallarg(char *) path; syscallarg(struct ostat *) ub; } */ *uap; { struct vnode *vp; struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)SCARG(uap, ub), sizeof (osb)); return (error); } /* * Convert from an old to a new stat structure. */ void cvtstat(st, ost) struct stat *st; struct ostat *ost; { ost->st_dev = st->st_dev; ost->st_ino = st->st_ino; ost->st_mode = st->st_mode; ost->st_nlink = st->st_nlink; ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; if (st->st_size < (quad_t)1 << 32) ost->st_size = st->st_size; else ost->st_size = -2; ost->st_atime = st->st_atime; ost->st_mtime = st->st_mtime; ost->st_ctime = st->st_ctime; ost->st_blksize = st->st_blksize; ost->st_blocks = st->st_blocks; ost->st_flags = st->st_flags; ost->st_gen = st->st_gen; } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct stat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int stat(p, uap) struct proc *p; register struct stat_args /* { syscallarg(char *) path; syscallarg(struct stat *) ub; } */ *uap; { struct stat sb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); error = vn_stat(nd.ni_vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int lstat(p, uap) struct proc *p; register struct lstat_args /* { syscallarg(char *) path; syscallarg(struct stat *) ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); return (error); } void cvtnstat(sb, nsb) struct stat *sb; struct nstat *nsb; { nsb->st_dev = sb->st_dev; nsb->st_ino = sb->st_ino; nsb->st_mode = sb->st_mode; nsb->st_nlink = sb->st_nlink; nsb->st_uid = sb->st_uid; nsb->st_gid = sb->st_gid; nsb->st_rdev = sb->st_rdev; nsb->st_atimespec = sb->st_atimespec; nsb->st_mtimespec = sb->st_mtimespec; nsb->st_ctimespec = sb->st_ctimespec; nsb->st_size = sb->st_size; nsb->st_blocks = sb->st_blocks; nsb->st_blksize = sb->st_blksize; nsb->st_flags = sb->st_flags; nsb->st_gen = sb->st_gen; nsb->st_qspare[0] = sb->st_qspare[0]; nsb->st_qspare[1] = sb->st_qspare[1]; } #ifndef _SYS_SYSPROTO_H_ struct nstat_args { char *path; struct nstat *ub; }; #endif /* ARGSUSED */ int nstat(p, uap) struct proc *p; register struct nstat_args /* { syscallarg(char *) path; syscallarg(struct nstat *) ub; } */ *uap; { struct stat sb; struct nstat nsb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout((caddr_t)&nsb, (caddr_t)SCARG(uap, ub), sizeof (nsb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int nlstat(p, uap) struct proc *p; register struct nlstat_args /* { syscallarg(char *) path; syscallarg(struct nstat *) ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nstat nsb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout((caddr_t)&nsb, (caddr_t)SCARG(uap, ub), sizeof (nsb)); return (error); } /* * Get configurable pathname variables. */ #ifndef _SYS_SYSPROTO_H_ struct pathconf_args { char *path; int name; }; #endif /* ARGSUSED */ int pathconf(p, uap) struct proc *p; register struct pathconf_args /* { syscallarg(char *) path; syscallarg(int) name; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = VOP_PATHCONF(nd.ni_vp, SCARG(uap, name), p->p_retval); vput(nd.ni_vp); return (error); } /* * Return target name of a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct readlink_args { char *path; char *buf; int count; }; #endif /* ARGSUSED */ int readlink(p, uap) struct proc *p; register struct readlink_args /* { syscallarg(char *) path; syscallarg(char *) buf; syscallarg(int) count; } */ *uap; { register struct vnode *vp; struct iovec aiov; struct uio auio; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (vp->v_type != VLNK) error = EINVAL; else { aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); error = VOP_READLINK(vp, &auio, p->p_ucred); } vput(vp); p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } static int setfflags(p, vp, flags) struct proc *p; struct vnode *vp; int flags; { int error; struct vattr vattr; /* * Prevent non-root users from setting flags on devices. When * a device is reused, users can retain ownership of the device * if they are allowed to set flags and programs assume that * chown can't fail when done as root. */ if ((vp->v_type == VCHR || vp->v_type == VBLK) && ((error = suser_xxx(p->p_ucred, p, PRISON_ROOT)) != 0)) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_flags = flags; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); return (error); } /* * Change flags of a file given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chflags_args { char *path; int flags; }; #endif /* ARGSUSED */ int chflags(p, uap) struct proc *p; register struct chflags_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfflags(p, nd.ni_vp, SCARG(uap, flags)); vrele(nd.ni_vp); return error; } /* * Change flags of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchflags_args { int fd; int flags; }; #endif /* ARGSUSED */ int fchflags(p, uap) struct proc *p; register struct fchflags_args /* { syscallarg(int) fd; syscallarg(int) flags; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfflags(p, (struct vnode *) fp->f_data, SCARG(uap, flags)); } static int setfmode(p, vp, mode) struct proc *p; struct vnode *vp; int mode; { int error; struct vattr vattr; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_mode = mode & ALLPERMS; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); return error; } /* * Change mode of a file given path name. */ #ifndef _SYS_SYSPROTO_H_ struct chmod_args { char *path; int mode; }; #endif /* ARGSUSED */ int chmod(p, uap) struct proc *p; register struct chmod_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(p, nd.ni_vp, SCARG(uap, mode)); vrele(nd.ni_vp); return error; } /* * Change mode of a file given path name (don't follow links.) */ #ifndef _SYS_SYSPROTO_H_ struct lchmod_args { char *path; int mode; }; #endif /* ARGSUSED */ int lchmod(p, uap) struct proc *p; register struct lchmod_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(p, nd.ni_vp, SCARG(uap, mode)); vrele(nd.ni_vp); return error; } /* * Change mode of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchmod_args { int fd; int mode; }; #endif /* ARGSUSED */ int fchmod(p, uap) struct proc *p; register struct fchmod_args /* { syscallarg(int) fd; syscallarg(int) mode; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfmode(p, (struct vnode *)fp->f_data, SCARG(uap, mode)); } static int setfown(p, vp, uid, gid) struct proc *p; struct vnode *vp; uid_t uid; gid_t gid; { int error; struct vattr vattr; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_uid = uid; vattr.va_gid = gid; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); return error; } /* * Set ownership given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chown_args { char *path; int uid; int gid; }; #endif /* ARGSUSED */ int chown(p, uap) struct proc *p; register struct chown_args /* { syscallarg(char *) path; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(p, nd.ni_vp, SCARG(uap, uid), SCARG(uap, gid)); vrele(nd.ni_vp); return (error); } /* * Set ownership given a path name, do not cross symlinks. */ #ifndef _SYS_SYSPROTO_H_ struct lchown_args { char *path; int uid; int gid; }; #endif /* ARGSUSED */ int lchown(p, uap) struct proc *p; register struct lchown_args /* { syscallarg(char *) path; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(p, nd.ni_vp, SCARG(uap, uid), SCARG(uap, gid)); vrele(nd.ni_vp); return (error); } /* * Set ownership given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchown_args { int fd; int uid; int gid; }; #endif /* ARGSUSED */ int fchown(p, uap) struct proc *p; register struct fchown_args /* { syscallarg(int) fd; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfown(p, (struct vnode *)fp->f_data, SCARG(uap, uid), SCARG(uap, gid)); } static int getutimes(usrtvp, tsp) const struct timeval *usrtvp; struct timespec *tsp; { struct timeval tv[2]; int error; if (usrtvp == NULL) { microtime(&tv[0]); TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); tsp[1] = tsp[0]; } else { if ((error = copyin(usrtvp, tv, sizeof (tv))) != 0) return (error); TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); TIMEVAL_TO_TIMESPEC(&tv[1], &tsp[1]); } return 0; } static int setutimes(p, vp, ts, nullflag) struct proc *p; struct vnode *vp; const struct timespec *ts; int nullflag; { int error; struct vattr vattr; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_atime = ts[0]; vattr.va_mtime = ts[1]; if (nullflag) vattr.va_vaflags |= VA_UTIMES_NULL; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); return error; } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct utimes_args { char *path; struct timeval *tptr; }; #endif /* ARGSUSED */ int utimes(p, uap) struct proc *p; register struct utimes_args /* { syscallarg(char *) path; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct timeval *usrtvp; int error; struct nameidata nd; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(p, nd.ni_vp, ts, usrtvp == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct lutimes_args { char *path; struct timeval *tptr; }; #endif /* ARGSUSED */ int lutimes(p, uap) struct proc *p; register struct lutimes_args /* { syscallarg(char *) path; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct timeval *usrtvp; int error; struct nameidata nd; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(p, nd.ni_vp, ts, usrtvp == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct futimes_args { int fd; struct timeval *tptr; }; #endif /* ARGSUSED */ int futimes(p, uap) struct proc *p; register struct futimes_args /* { syscallarg(int ) fd; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct file *fp; struct timeval *usrtvp; int error; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setutimes(p, (struct vnode *)fp->f_data, ts, usrtvp == NULL); } /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct truncate_args { char *path; int pad; off_t length; }; #endif /* ARGSUSED */ int truncate(p, uap) struct proc *p; register struct truncate_args /* { syscallarg(char *) path; syscallarg(int) pad; syscallarg(off_t) length; } */ *uap; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; if (uap->length < 0) return(EINVAL); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0 && (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) { VATTR_NULL(&vattr); vattr.va_size = SCARG(uap, length); error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct ftruncate_args { int fd; int pad; off_t length; }; #endif /* ARGSUSED */ int ftruncate(p, uap) struct proc *p; register struct ftruncate_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) length; } */ *uap; { struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (uap->length < 0) return(EINVAL); if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FWRITE) == 0) return (EINVAL); vp = (struct vnode *)fp->f_data; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0) { VATTR_NULL(&vattr); vattr.va_size = SCARG(uap, length); error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); } VOP_UNLOCK(vp, 0, p); return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct otruncate_args { char *path; long length; }; #endif /* ARGSUSED */ int otruncate(p, uap) struct proc *p; register struct otruncate_args /* { syscallarg(char *) path; syscallarg(long) length; } */ *uap; { struct truncate_args /* { syscallarg(char *) path; syscallarg(int) pad; syscallarg(off_t) length; } */ nuap; SCARG(&nuap, path) = SCARG(uap, path); SCARG(&nuap, length) = SCARG(uap, length); return (truncate(p, &nuap)); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct oftruncate_args { int fd; long length; }; #endif /* ARGSUSED */ int oftruncate(p, uap) struct proc *p; register struct oftruncate_args /* { syscallarg(int) fd; syscallarg(long) length; } */ *uap; { struct ftruncate_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) length; } */ nuap; SCARG(&nuap, fd) = SCARG(uap, fd); SCARG(&nuap, length) = SCARG(uap, length); return (ftruncate(p, &nuap)); } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Sync an open file. */ #ifndef _SYS_SYSPROTO_H_ struct fsync_args { int fd; }; #endif /* ARGSUSED */ int fsync(p, uap) struct proc *p; struct fsync_args /* { syscallarg(int) fd; } */ *uap; { register struct vnode *vp; struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_object) vm_object_page_clean(vp->v_object, 0, 0, 0); error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); #ifdef SOFTUPDATES if (error == 0 && vp->v_mount && (vp->v_mount->mnt_flag & MNT_SOFTDEP)) error = softdep_fsync(vp); #endif VOP_UNLOCK(vp, 0, p); return (error); } /* * Rename files. Source and destination must either both be directories, * or both not be directories. If target is a directory, it must be empty. */ #ifndef _SYS_SYSPROTO_H_ struct rename_args { char *from; char *to; }; #endif /* ARGSUSED */ int rename(p, uap) struct proc *p; register struct rename_args /* { syscallarg(char *) from; syscallarg(char *) to; } */ *uap; { register struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; bwillwrite(); NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, SCARG(uap, from), p); if ((error = namei(&fromnd)) != 0) return (error); fvp = fromnd.ni_vp; NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | NOOBJ, UIO_USERSPACE, SCARG(uap, to), p); if (fromnd.ni_vp->v_type == VDIR) tond.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&tond)) != 0) { /* Translate error code for rename("dir1", "dir2/."). */ if (error == EISDIR && fvp->v_type == VDIR) error = EINVAL; NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } tdvp = tond.ni_dvp; tvp = tond.ni_vp; if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { error = ENOTDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; goto out; } } if (fvp == tdvp) error = EINVAL; /* * If source is the same as the destination (that is the * same inode number with the same name in the same directory), * then there is nothing to do. */ if (fvp == tvp && fromnd.ni_dvp == tdvp && fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, fromnd.ni_cnd.cn_namelen)) error = -1; out: if (!error) { VOP_LEASE(tdvp, p, p->p_ucred, LEASE_WRITE); if (fromnd.ni_dvp != tdvp) { VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); } if (tvp) { VOP_LEASE(tvp, p, p->p_ucred, LEASE_WRITE); } error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); } else { NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fromnd.ni_dvp); vrele(fvp); } vrele(tond.ni_startdir); ASSERT_VOP_UNLOCKED(fromnd.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(fromnd.ni_vp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_vp, "rename"); out1: if (fromnd.ni_startdir) vrele(fromnd.ni_startdir); if (error == -1) return (0); return (error); } /* * Make a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct mkdir_args { char *path; int mode; }; #endif /* ARGSUSED */ int mkdir(p, uap) struct proc *p; register struct mkdir_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); nd.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(vp); return (EEXIST); } VATTR_NULL(&vattr); vattr.va_type = VDIR; vattr.va_mode = (SCARG(uap, mode) & ACCESSPERMS) &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if (!error) vput(nd.ni_vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mkdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mkdir"); return (error); } /* * Remove a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct rmdir_args { char *path; }; #endif /* ARGSUSED */ int rmdir(p, uap) struct proc *p; struct rmdir_args /* { syscallarg(char *) path; } */ *uap; { register struct vnode *vp; int error; struct nameidata nd; bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) error = EBUSY; else { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } out: NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (vp != NULLVP) vput(vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "rmdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "rmdir"); return (error); } #ifdef COMPAT_43 /* * Read a block of directory entries in a file system independent format. */ #ifndef _SYS_SYSPROTO_H_ struct ogetdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int ogetdirentries(p, uap) struct proc *p; register struct ogetdirentries_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; syscallarg(long *) basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio, kuio; struct iovec aiov, kiov; struct dirent *dp, *edp; caddr_t dirbuf; int error, eofflag, readcnt; long loff; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); loff = auio.uio_offset = fp->f_offset; # if (BYTE_ORDER != LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen <= 0) { error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; } else # endif { kuio = auio; kuio.uio_iov = &kiov; kuio.uio_segflg = UIO_SYSSPACE; kiov.iov_len = SCARG(uap, count); MALLOC(dirbuf, caddr_t, SCARG(uap, count), M_TEMP, M_WAITOK); kiov.iov_base = dirbuf; error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = kuio.uio_offset; if (error == 0) { readcnt = SCARG(uap, count) - kuio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { # if (BYTE_ORDER == LITTLE_ENDIAN) /* * The expected low byte of * dp->d_namlen is our dp->d_type. * The high MBZ byte of dp->d_namlen * is our dp->d_namlen. */ dp->d_type = dp->d_namlen; dp->d_namlen = 0; # else /* * The dp->d_type is the high byte * of the expected dp->d_namlen, * so must be zero'ed. */ dp->d_type = 0; # endif if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, &auio); } FREE(dirbuf, M_TEMP); } VOP_UNLOCK(vp, 0, p); if (error) return (error); if (SCARG(uap, count) == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(p, &vp, fp); if (error == -1) goto unionread; if (error) return (error); } if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_data = (caddr_t) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } } error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), sizeof(long)); p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } #endif /* COMPAT_43 */ /* * Read a block of directory entries in a file system independent format. */ #ifndef _SYS_SYSPROTO_H_ struct getdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int getdirentries(p, uap) struct proc *p; register struct getdirentries_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; syscallarg(long *) basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; long loff; int error, eofflag; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); /* vn_lock(vp, LK_SHARED | LK_RETRY, p); */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); loff = auio.uio_offset = fp->f_offset; error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; VOP_UNLOCK(vp, 0, p); if (error) return (error); if (SCARG(uap, count) == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(p, &vp, fp); if (error == -1) goto unionread; if (error) return (error); } if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_data = (caddr_t) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } } if (SCARG(uap, basep) != NULL) { error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), sizeof(long)); } p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } #ifndef _SYS_SYSPROTO_H_ struct getdents_args { int fd; char *buf; size_t count; }; #endif int getdents(p, uap) struct proc *p; register struct getdents_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; } */ *uap; { struct getdirentries_args ap; ap.fd = uap->fd; ap.buf = uap->buf; ap.count = uap->count; ap.basep = NULL; return getdirentries(p, &ap); } /* * Set the mode mask for creation of filesystem nodes. * * MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct umask_args { int newmask; }; #endif int umask(p, uap) struct proc *p; struct umask_args /* { syscallarg(int) newmask; } */ *uap; { register struct filedesc *fdp; fdp = p->p_fd; p->p_retval[0] = fdp->fd_cmask; fdp->fd_cmask = SCARG(uap, newmask) & ALLPERMS; return (0); } /* * Void all references to file by ripping underlying filesystem * away from vnode. */ #ifndef _SYS_SYSPROTO_H_ struct revoke_args { char *path; }; #endif /* ARGSUSED */ int revoke(p, uap) struct proc *p; register struct revoke_args /* { syscallarg(char *) path; } */ *uap; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (vp->v_type != VCHR && vp->v_type != VBLK) { error = EINVAL; goto out; } if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0) goto out; if (p->p_ucred->cr_uid != vattr.va_uid && (error = suser_xxx(0, p, PRISON_ROOT))) goto out; if (vcount(vp) > 1) VOP_REVOKE(vp, REVOKEALL); out: vrele(vp); return (error); } /* * Convert a user file descriptor to a kernel file entry. */ int getvnode(fdp, fd, fpp) struct filedesc *fdp; int fd; struct file **fpp; { struct file *fp; if ((u_int)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE && fp->f_type != DTYPE_FIFO) return (EINVAL); *fpp = fp; return (0); } /* * Get (NFS) file handle */ #ifndef _SYS_SYSPROTO_H_ struct getfh_args { char *fname; fhandle_t *fhp; }; #endif int getfh(p, uap) struct proc *p; register struct getfh_args *uap; { struct nameidata nd; fhandle_t fh; register struct vnode *vp; int error; /* * Must be super user */ error = suser(p); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; bzero(&fh, sizeof(fh)); fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fh.fh_fid); vput(vp); if (error) return (error); error = copyout(&fh, uap->fhp, sizeof (fh)); return (error); } /* * syscall for the rpc.lockd to use to translate a NFS file handle into * an open descriptor. * * warning: do not remove the suser() call or this becomes one giant * security hole. */ #ifndef _SYS_SYSPROTO_H_ struct fhopen_args { const struct fhandle *u_fhp; int flags; }; #endif int fhopen(p, uap) struct proc *p; struct fhopen_args /* { syscallarg(const struct fhandle *) u_fhp; syscallarg(int) flags; } */ *uap; { struct mount *mp; struct vnode *vp; struct fhandle fhp; struct vattr vat; struct vattr *vap = &vat; struct flock lf; struct file *fp; register struct filedesc *fdp = p->p_fd; int fmode, mode, error, type; struct file *nfp; int indx; /* * Must be super user */ error = suser(p); if (error) return (error); fmode = FFLAGS(SCARG(uap, flags)); /* why not allow a non-read/write open for our lockd? */ if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) return (EINVAL); error = copyin(SCARG(uap,u_fhp), &fhp, sizeof(fhp)); if (error) return(error); /* find the mount point */ mp = vfs_getvfs(&fhp.fh_fsid); if (mp == NULL) return (ESTALE); /* now give me my vnode, it gets returned to me locked */ error = VFS_FHTOVP(mp, &fhp.fh_fid, &vp); if (error) return (error); /* * from now on we have to make sure not * to forget about the vnode * any error that causes an abort must vput(vp) * just set error = err and 'goto bad;'. */ /* * from vn_open */ if (vp->v_type == VLNK) { error = EMLINK; goto bad; } if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto bad; } mode = 0; if (fmode & (FWRITE | O_TRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto bad; } error = vn_writechk(vp); if (error) goto bad; mode |= VWRITE; } if (fmode & FREAD) mode |= VREAD; if (mode) { error = VOP_ACCESS(vp, mode, p->p_ucred, p); if (error) goto bad; } if (fmode & O_TRUNC) { VOP_UNLOCK(vp, 0, p); /* XXX */ VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */ VATTR_NULL(vap); vap->va_size = 0; error = VOP_SETATTR(vp, vap, p->p_ucred, p); if (error) goto bad; } error = VOP_OPEN(vp, fmode, p->p_ucred, p); if (error) goto bad; /* * Make sure that a VM object is created for VMIO support. */ if (vn_canvmio(vp) == TRUE) { if ((error = vfs_object_create(vp, p, p->p_ucred)) != 0) goto bad; } if (fmode & FWRITE) vp->v_writecount++; /* * end of vn_open code */ if ((error = falloc(p, &nfp, &indx)) != 0) goto bad; fp = nfp; nfp->f_data = (caddr_t)vp; nfp->f_flag = fmode & FMASK; nfp->f_ops = &vnops; nfp->f_type = DTYPE_VNODE; if (fmode & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (fmode & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((fmode & FNONBLOCK) == 0) type |= F_WAIT; VOP_UNLOCK(vp, 0, p); if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) { (void) vn_close(vp, fp->f_flag, fp->f_cred, p); ffree(fp); fdp->fd_ofiles[indx] = NULL; return (error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); fp->f_flag |= FHASLOCK; } if ((vp->v_type == VREG) && (vp->v_object == NULL)) vfs_object_create(vp, p, p->p_ucred); VOP_UNLOCK(vp, 0, p); p->p_retval[0] = indx; return (0); bad: vput(vp); return (error); } #ifndef _SYS_SYSPROTO_H_ struct fhstat_args { struct fhandle *u_fhp; struct stat *sb; }; #endif int fhstat(p, uap) struct proc *p; register struct fhstat_args /* { syscallarg(struct fhandle *) u_fhp; syscallarg(struct stat *) sb; } */ *uap; { struct stat sb; fhandle_t fh; struct mount *mp; struct vnode *vp; int error; /* * Must be super user */ error = suser(p); if (error) return (error); error = copyin(SCARG(uap, u_fhp), &fh, sizeof(fhandle_t)); if (error) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); error = copyout(&sb, SCARG(uap, sb), sizeof(sb)); return (error); } #ifndef _SYS_SYSPROTO_H_ struct fhstatfs_args { struct fhandle *u_fhp; struct statfs *buf; }; #endif int fhstatfs(p, uap) struct proc *p; struct fhstatfs_args /* { syscallarg(struct fhandle) *u_fhp; syscallarg(struct statfs) *buf; } */ *uap; { struct statfs *sp; struct mount *mp; struct vnode *vp; struct statfs sb; fhandle_t fh; int error; /* * Must be super user */ if ((error = suser(p))) return (error); if ((error = copyin(SCARG(uap, u_fhp), &fh, sizeof(fhandle_t))) != 0) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); mp = vp->v_mount; sp = &mp->mnt_stat; vput(vp); if ((error = VFS_STATFS(mp, sp, p)) != 0) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout(sp, SCARG(uap, buf), sizeof(*sp))); } /* * Syscall to push extended attribute configuration information into the * VFS. Accepts a path, which it converts to a mountpoint, as well as * a command (int cmd), and attribute name and misc data. For now, the * attribute name is left in userspace for consumption by the VFS_op. * It will probably be changed to be copied into sysspace by the * syscall in the future, once issues with various consumers of the * attribute code have raised their hands. * * Currently this is used only by UFS Extended Attributes. */ int extattrctl(p, uap) struct proc *p; struct extattrctl_args *uap; { struct nameidata nd; struct mount *mp; int error; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; NDFREE(&nd, 0); return (VFS_EXTATTRCTL(mp, SCARG(uap, cmd), SCARG(uap, attrname), SCARG(uap, arg), p)); } /* * Syscall to set a named extended attribute on a file or directory. * Accepts attribute name, and a uio structure pointing to the data to set. * The uio is consumed in the style of writev(). The real work happens * in VOP_SETEXTATTR(). */ int extattr_set_file(p, uap) struct proc *p; struct extattr_set_file_args *uap; { struct nameidata nd; struct uio auio; struct iovec *iov, *needfree = NULL, aiov[UIO_SMALLIOV]; char attrname[EXTATTR_MAXNAMELEN]; u_int iovlen, cnt; int error, i; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return (error); NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return(error); iovlen = uap->iovcnt * sizeof(struct iovec); if (uap->iovcnt > UIO_SMALLIOV) { if (uap->iovcnt > UIO_MAXIOV) { error = EINVAL; goto done; } MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK); needfree = iov; } else iov = aiov; auio.uio_iov = iov; auio.uio_iovcnt = uap->iovcnt; auio.uio_rw = UIO_WRITE; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_offset = 0; if ((error = copyin((caddr_t)uap->iovp, (caddr_t)iov, iovlen))) goto done; auio.uio_resid = 0; for (i = 0; i < uap->iovcnt; i++) { if (iov->iov_len > INT_MAX - auio.uio_resid) { error = EINVAL; goto done; } auio.uio_resid += iov->iov_len; iov++; } cnt = auio.uio_resid; error = VOP_SETEXTATTR(nd.ni_vp, attrname, &auio, p->p_cred->pc_ucred, p); if (auio.uio_resid != cnt && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; cnt -= auio.uio_resid; p->p_retval[0] = cnt; done: if (needfree) FREE(needfree, M_IOV); NDFREE(&nd, 0); return (error); } /* * Syscall to get a named extended attribute on a file or directory. * Accepts attribute name, and a uio structure pointing to a buffer for the * data. The uio is consumed in the style of readv(). The real work * happens in VOP_GETEXTATTR(); */ int extattr_get_file(p, uap) struct proc *p; struct extattr_get_file_args *uap; { struct nameidata nd; struct uio auio; struct iovec *iov, *needfree, aiov[UIO_SMALLIOV]; char attrname[EXTATTR_MAXNAMELEN]; u_int iovlen, cnt; int error, i; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); iovlen = uap->iovcnt * sizeof (struct iovec); if (uap->iovcnt > UIO_SMALLIOV) { if (uap->iovcnt > UIO_MAXIOV) { NDFREE(&nd, 0); return (EINVAL); } MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK); needfree = iov; } else { iov = aiov; needfree = NULL; } auio.uio_iov = iov; auio.uio_iovcnt = uap->iovcnt; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_offset = 0; if ((error = copyin((caddr_t)uap->iovp, (caddr_t)iov, iovlen))) goto done; auio.uio_resid = 0; for (i = 0; i < uap->iovcnt; i++) { if (iov->iov_len > INT_MAX - auio.uio_resid) { error = EINVAL; goto done; } auio.uio_resid += iov->iov_len; iov++; } cnt = auio.uio_resid; error = VOP_GETEXTATTR(nd.ni_vp, attrname, &auio, p->p_cred->pc_ucred, p); if (auio.uio_resid != cnt && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; cnt -= auio.uio_resid; p->p_retval[0] = cnt; done: if (needfree) FREE(needfree, M_IOV); NDFREE(&nd, 0); return(error); } /* * Syscall to delete a named extended attribute from a file or directory. * Accepts attribute name. The real work happens in VOP_SETEXTATTR(). */ int extattr_delete_file(p, uap) struct proc *p; struct extattr_delete_file_args *uap; { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return(error); NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return(error); error = VOP_SETEXTATTR(nd.ni_vp, attrname, NULL, p->p_cred->pc_ucred, p); NDFREE(&nd, 0); return(error); } Index: head/sys/kern/vfs_vnops.c =================================================================== --- head/sys/kern/vfs_vnops.c (revision 62549) +++ head/sys/kern/vfs_vnops.c (revision 62550) @@ -1,724 +1,713 @@ /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_vnops.c 8.2 (Berkeley) 1/21/94 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int vn_closefile __P((struct file *fp, struct proc *p)); static int vn_ioctl __P((struct file *fp, u_long com, caddr_t data, struct proc *p)); static int vn_read __P((struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct proc *p)); static int vn_poll __P((struct file *fp, int events, struct ucred *cred, struct proc *p)); static int vn_statfile __P((struct file *fp, struct stat *sb, struct proc *p)); static int vn_write __P((struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct proc *p)); struct fileops vnops = { vn_read, vn_write, vn_ioctl, vn_poll, vn_statfile, vn_closefile }; static int filt_nullattach(struct knote *kn); static int filt_vnattach(struct knote *kn); static void filt_vndetach(struct knote *kn); static int filt_vnode(struct knote *kn, long hint); static int filt_vnread(struct knote *kn, long hint); struct filterops vn_filtops = { 1, filt_vnattach, filt_vndetach, filt_vnode }; /* * XXX * filt_vnread is ufs-specific, so the attach routine should really * switch out to different filterops based on the vn filetype */ struct filterops vn_rwfiltops[] = { { 1, filt_vnattach, filt_vndetach, filt_vnread }, { 1, filt_nullattach, NULL, NULL }, }; /* * Common code for vnode open operations. * Check permissions, and call the VOP_OPEN or VOP_CREATE routine. * * Note that this does NOT free nameidata for the successful case, * due to the NDINIT being done elsewhere. */ int -vn_open(ndp, fmode, cmode) +vn_open(ndp, flagp, cmode) register struct nameidata *ndp; - int fmode, cmode; + int *flagp, cmode; { - register struct vnode *vp; - register struct proc *p = ndp->ni_cnd.cn_proc; - register struct ucred *cred = p->p_ucred; + struct vnode *vp; + struct proc *p = ndp->ni_cnd.cn_proc; + struct ucred *cred = p->p_ucred; struct vattr vat; struct vattr *vap = &vat; - int mode, error; + int mode, fmode, error; + fmode = *flagp; if (fmode & O_CREAT) { ndp->ni_cnd.cn_nameiop = CREATE; ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0) ndp->ni_cnd.cn_flags |= FOLLOW; bwillwrite(); - error = namei(ndp); - if (error) + if ((error = namei(ndp)) != 0) return (error); if (ndp->ni_vp == NULL) { VATTR_NULL(vap); vap->va_type = VREG; vap->va_mode = cmode; if (fmode & O_EXCL) vap->va_vaflags |= VA_EXCLUSIVE; VOP_LEASE(ndp->ni_dvp, p, cred, LEASE_WRITE); error = VOP_CREATE(ndp->ni_dvp, &ndp->ni_vp, &ndp->ni_cnd, vap); + vput(ndp->ni_dvp); if (error) { NDFREE(ndp, NDF_ONLY_PNBUF); - vput(ndp->ni_dvp); return (error); } - vput(ndp->ni_dvp); ASSERT_VOP_UNLOCKED(ndp->ni_dvp, "create"); ASSERT_VOP_LOCKED(ndp->ni_vp, "create"); fmode &= ~O_TRUNC; vp = ndp->ni_vp; } else { if (ndp->ni_dvp == ndp->ni_vp) vrele(ndp->ni_dvp); else vput(ndp->ni_dvp); ndp->ni_dvp = NULL; vp = ndp->ni_vp; if (fmode & O_EXCL) { error = EEXIST; goto bad; } fmode &= ~O_CREAT; } } else { ndp->ni_cnd.cn_nameiop = LOOKUP; ndp->ni_cnd.cn_flags = ((fmode & O_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKLEAF; - error = namei(ndp); - if (error) + if ((error = namei(ndp)) != 0) return (error); vp = ndp->ni_vp; } if (vp->v_type == VLNK) { error = EMLINK; goto bad; } if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto bad; } if ((fmode & O_CREAT) == 0) { mode = 0; if (fmode & (FWRITE | O_TRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto bad; } error = vn_writechk(vp); if (error) goto bad; mode |= VWRITE; } if (fmode & FREAD) mode |= VREAD; if (mode) { error = VOP_ACCESS(vp, mode, cred, p); if (error) goto bad; } } - if (fmode & O_TRUNC) { - VOP_UNLOCK(vp, 0, p); /* XXX */ - VOP_LEASE(vp, p, cred, LEASE_WRITE); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */ - VATTR_NULL(vap); - vap->va_size = 0; - error = VOP_SETATTR(vp, vap, cred, p); - if (error) - goto bad; - } - error = VOP_OPEN(vp, fmode, cred, p); - if (error) + if ((error = VOP_OPEN(vp, fmode, cred, p)) != 0) goto bad; /* * Make sure that a VM object is created for VMIO support. */ if (vn_canvmio(vp) == TRUE) { if ((error = vfs_object_create(vp, p, cred)) != 0) goto bad; } if (fmode & FWRITE) vp->v_writecount++; + *flagp = fmode; return (0); bad: NDFREE(ndp, NDF_ONLY_PNBUF); vput(vp); + *flagp = fmode; return (error); } /* * Check for write permissions on the specified vnode. * Prototype text segments cannot be written. */ int vn_writechk(vp) register struct vnode *vp; { /* * If there's shared text associated with * the vnode, try to free it up once. If * we fail, we can't allow writing. */ if (vp->v_flag & VTEXT) return (ETXTBSY); return (0); } /* * Vnode close call */ int vn_close(vp, flags, cred, p) register struct vnode *vp; int flags; struct ucred *cred; struct proc *p; { int error; if (flags & FWRITE) vp->v_writecount--; error = VOP_CLOSE(vp, flags, cred, p); vrele(vp); return (error); } static __inline int sequential_heuristic(struct uio *uio, struct file *fp) { /* * Sequential heuristic - detect sequential operation */ if ((uio->uio_offset == 0 && fp->f_seqcount > 0) || uio->uio_offset == fp->f_nextoff) { /* * XXX we assume that the filesystem block size is * the default. Not true, but still gives us a pretty * good indicator of how sequential the read operations * are. */ fp->f_seqcount += (uio->uio_resid + BKVASIZE - 1) / BKVASIZE; if (fp->f_seqcount >= 127) fp->f_seqcount = 127; return(fp->f_seqcount << 16); } /* * Not sequential, quick draw-down of seqcount */ if (fp->f_seqcount > 1) fp->f_seqcount = 1; else fp->f_seqcount = 0; return(0); } /* * Package up an I/O request on a vnode into a uio and do it. */ int vn_rdwr(rw, vp, base, len, offset, segflg, ioflg, cred, aresid, p) enum uio_rw rw; struct vnode *vp; caddr_t base; int len; off_t offset; enum uio_seg segflg; int ioflg; struct ucred *cred; int *aresid; struct proc *p; { struct uio auio; struct iovec aiov; int error; if ((ioflg & IO_NODELOCKED) == 0) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = base; aiov.iov_len = len; auio.uio_resid = len; auio.uio_offset = offset; auio.uio_segflg = segflg; auio.uio_rw = rw; auio.uio_procp = p; if (rw == UIO_READ) { error = VOP_READ(vp, &auio, ioflg, cred); } else { error = VOP_WRITE(vp, &auio, ioflg, cred); } if (aresid) *aresid = auio.uio_resid; else if (auio.uio_resid && error == 0) error = EIO; if ((ioflg & IO_NODELOCKED) == 0) VOP_UNLOCK(vp, 0, p); return (error); } /* * File table vnode read routine. */ static int vn_read(fp, uio, cred, flags, p) struct file *fp; struct uio *uio; struct ucred *cred; struct proc *p; int flags; { struct vnode *vp; int error, ioflag; KASSERT(uio->uio_procp == p, ("uio_procp %p is not p %p", uio->uio_procp, p)); vp = (struct vnode *)fp->f_data; ioflag = 0; if (fp->f_flag & FNONBLOCK) ioflag |= IO_NDELAY; VOP_LEASE(vp, p, cred, LEASE_READ); vn_lock(vp, LK_SHARED | LK_NOPAUSE | LK_RETRY, p); if ((flags & FOF_OFFSET) == 0) uio->uio_offset = fp->f_offset; ioflag |= sequential_heuristic(uio, fp); error = VOP_READ(vp, uio, ioflag, cred); if ((flags & FOF_OFFSET) == 0) fp->f_offset = uio->uio_offset; fp->f_nextoff = uio->uio_offset; VOP_UNLOCK(vp, 0, p); return (error); } /* * File table vnode write routine. */ static int vn_write(fp, uio, cred, flags, p) struct file *fp; struct uio *uio; struct ucred *cred; struct proc *p; int flags; { struct vnode *vp; int error, ioflag; KASSERT(uio->uio_procp == p, ("uio_procp %p is not p %p", uio->uio_procp, p)); vp = (struct vnode *)fp->f_data; if (vp->v_type == VREG) bwillwrite(); vp = (struct vnode *)fp->f_data; /* XXX needed? */ ioflag = IO_UNIT; if (vp->v_type == VREG && (fp->f_flag & O_APPEND)) ioflag |= IO_APPEND; if (fp->f_flag & FNONBLOCK) ioflag |= IO_NDELAY; if ((fp->f_flag & O_FSYNC) || (vp->v_mount && (vp->v_mount->mnt_flag & MNT_SYNCHRONOUS))) ioflag |= IO_SYNC; VOP_LEASE(vp, p, cred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if ((flags & FOF_OFFSET) == 0) uio->uio_offset = fp->f_offset; ioflag |= sequential_heuristic(uio, fp); error = VOP_WRITE(vp, uio, ioflag, cred); if ((flags & FOF_OFFSET) == 0) fp->f_offset = uio->uio_offset; fp->f_nextoff = uio->uio_offset; VOP_UNLOCK(vp, 0, p); return (error); } /* * File table vnode stat routine. */ static int vn_statfile(fp, sb, p) struct file *fp; struct stat *sb; struct proc *p; { struct vnode *vp = (struct vnode *)fp->f_data; return vn_stat(vp, sb, p); } int vn_stat(vp, sb, p) struct vnode *vp; register struct stat *sb; struct proc *p; { struct vattr vattr; register struct vattr *vap; int error; u_short mode; vap = &vattr; error = VOP_GETATTR(vp, vap, p->p_ucred, p); if (error) return (error); /* * Zero the spare stat fields */ sb->st_lspare = 0; sb->st_qspare[0] = 0; sb->st_qspare[1] = 0; /* * Copy from vattr table */ if (vap->va_fsid != VNOVAL) sb->st_dev = vap->va_fsid; else sb->st_dev = vp->v_mount->mnt_stat.f_fsid.val[0]; sb->st_ino = vap->va_fileid; mode = vap->va_mode; switch (vap->va_type) { case VREG: mode |= S_IFREG; break; case VDIR: mode |= S_IFDIR; break; case VBLK: mode |= S_IFBLK; break; case VCHR: mode |= S_IFCHR; break; case VLNK: mode |= S_IFLNK; /* This is a cosmetic change, symlinks do not have a mode. */ if (vp->v_mount->mnt_flag & MNT_NOSYMFOLLOW) sb->st_mode &= ~ACCESSPERMS; /* 0000 */ else sb->st_mode |= ACCESSPERMS; /* 0777 */ break; case VSOCK: mode |= S_IFSOCK; break; case VFIFO: mode |= S_IFIFO; break; default: return (EBADF); }; sb->st_mode = mode; sb->st_nlink = vap->va_nlink; sb->st_uid = vap->va_uid; sb->st_gid = vap->va_gid; sb->st_rdev = vap->va_rdev; sb->st_size = vap->va_size; sb->st_atimespec = vap->va_atime; sb->st_mtimespec = vap->va_mtime; sb->st_ctimespec = vap->va_ctime; /* * According to www.opengroup.org, the meaning of st_blksize is * "a filesystem-specific preferred I/O block size for this * object. In some filesystem types, this may vary from file * to file" * Default to zero to catch bogus uses of this field. */ if (vap->va_type == VREG) { sb->st_blksize = vap->va_blocksize; } else if (vn_isdisk(vp, NULL)) { sb->st_blksize = vp->v_rdev->si_bsize_best; if (sb->st_blksize < vp->v_rdev->si_bsize_phys) sb->st_blksize = vp->v_rdev->si_bsize_phys; if (sb->st_blksize < BLKDEV_IOSIZE) sb->st_blksize = BLKDEV_IOSIZE; } else { sb->st_blksize = 0; } sb->st_flags = vap->va_flags; if (suser_xxx(p->p_ucred, 0, 0)) sb->st_gen = 0; else sb->st_gen = vap->va_gen; #if (S_BLKSIZE == 512) /* Optimize this case */ sb->st_blocks = vap->va_bytes >> 9; #else sb->st_blocks = vap->va_bytes / S_BLKSIZE; #endif return (0); } /* * File table vnode ioctl routine. */ static int vn_ioctl(fp, com, data, p) struct file *fp; u_long com; caddr_t data; struct proc *p; { register struct vnode *vp = ((struct vnode *)fp->f_data); struct vattr vattr; int error; switch (vp->v_type) { case VREG: case VDIR: if (com == FIONREAD) { error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); if (error) return (error); *(int *)data = vattr.va_size - fp->f_offset; return (0); } if (com == FIONBIO || com == FIOASYNC) /* XXX */ return (0); /* XXX */ /* fall into ... */ default: #if 0 return (ENOTTY); #endif case VFIFO: case VCHR: case VBLK: if (com == FIODTYPE) { if (vp->v_type != VCHR && vp->v_type != VBLK) return (ENOTTY); *(int *)data = devsw(vp->v_rdev)->d_flags & D_TYPEMASK; return (0); } error = VOP_IOCTL(vp, com, data, fp->f_flag, p->p_ucred, p); if (error == 0 && com == TIOCSCTTY) { /* Do nothing if reassigning same control tty */ if (p->p_session->s_ttyvp == vp) return (0); /* Get rid of reference to old control tty */ if (p->p_session->s_ttyvp) vrele(p->p_session->s_ttyvp); p->p_session->s_ttyvp = vp; VREF(vp); } return (error); } } /* * File table vnode poll routine. */ static int vn_poll(fp, events, cred, p) struct file *fp; int events; struct ucred *cred; struct proc *p; { return (VOP_POLL(((struct vnode *)fp->f_data), events, cred, p)); } /* * Check that the vnode is still valid, and if so * acquire requested lock. */ int #ifndef DEBUG_LOCKS vn_lock(vp, flags, p) #else debug_vn_lock(vp, flags, p, filename, line) #endif struct vnode *vp; int flags; struct proc *p; #ifdef DEBUG_LOCKS const char *filename; int line; #endif { int error; do { if ((flags & LK_INTERLOCK) == 0) simple_lock(&vp->v_interlock); if (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; simple_unlock(&vp->v_interlock); tsleep((caddr_t)vp, PINOD, "vn_lock", 0); error = ENOENT; } else { #ifdef DEBUG_LOCKS vp->filename = filename; vp->line = line; #endif error = VOP_LOCK(vp, flags | LK_NOPAUSE | LK_INTERLOCK, p); if (error == 0) return (error); } flags &= ~LK_INTERLOCK; } while (flags & LK_RETRY); return (error); } /* * File table vnode close routine. */ static int vn_closefile(fp, p) struct file *fp; struct proc *p; { fp->f_ops = &badfileops; return (vn_close(((struct vnode *)fp->f_data), fp->f_flag, fp->f_cred, p)); } static int filt_vnattach(struct knote *kn) { struct vnode *vp; if (kn->kn_fp->f_type != DTYPE_VNODE && kn->kn_fp->f_type != DTYPE_FIFO) return (EBADF); vp = (struct vnode *)kn->kn_fp->f_data; /* * XXX * this is a hack simply to cause the filter attach to fail * for non-ufs filesystems, until the support for them is done. */ if ((vp)->v_tag != VT_UFS) return (EOPNOTSUPP); simple_lock(&vp->v_pollinfo.vpi_lock); SLIST_INSERT_HEAD(&vp->v_pollinfo.vpi_selinfo.si_note, kn, kn_selnext); simple_unlock(&vp->v_pollinfo.vpi_lock); return (0); } static void filt_vndetach(struct knote *kn) { struct vnode *vp = (struct vnode *)kn->kn_fp->f_data; simple_lock(&vp->v_pollinfo.vpi_lock); SLIST_REMOVE(&vp->v_pollinfo.vpi_selinfo.si_note, kn, knote, kn_selnext); simple_unlock(&vp->v_pollinfo.vpi_lock); } static int filt_vnode(struct knote *kn, long hint) { if (kn->kn_sfflags & hint) kn->kn_fflags |= hint; return (kn->kn_fflags != 0); } static int filt_nullattach(struct knote *kn) { return (ENXIO); } /*ARGSUSED*/ static int filt_vnread(struct knote *kn, long hint) { struct vnode *vp = (struct vnode *)kn->kn_fp->f_data; struct inode *ip = VTOI(vp); kn->kn_data = ip->i_size - kn->kn_fp->f_offset; return (kn->kn_data != 0); } Index: head/sys/sys/vnode.h =================================================================== --- head/sys/sys/vnode.h (revision 62549) +++ head/sys/sys/vnode.h (revision 62550) @@ -1,629 +1,629 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vnode.h 8.7 (Berkeley) 2/4/94 * $FreeBSD$ */ #ifndef _SYS_VNODE_H_ #define _SYS_VNODE_H_ #include #include #include #include #include /* * The vnode is the focus of all file activity in UNIX. There is a * unique vnode allocated for each active file, each current directory, * each mounted-on file, text file, and the root. */ /* * Vnode types. VNON means no type. */ enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD }; /* * Vnode tag types. * These are for the benefit of external programs only (e.g., pstat) * and should NEVER be inspected by the kernel. */ enum vtagtype { VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PC, VT_LFS, VT_LOFS, VT_FDESC, VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS, VT_UNION, VT_MSDOSFS, VT_DEVFS, VT_TFS, VT_VFS, VT_CODA, VT_NTFS, VT_HPFS, VT_NWFS }; /* * Each underlying filesystem allocates its own private area and hangs * it from v_data. If non-null, this area is freed in getnewvnode(). */ TAILQ_HEAD(buflists, buf); typedef int vop_t __P((void *)); struct namecache; /* * Reading or writing any of these items requires holding the appropriate lock. * v_freelist is locked by the global vnode_free_list simple lock. * v_mntvnodes is locked by the global mntvnodes simple lock. * v_flag, v_usecount, v_holdcount and v_writecount are * locked by the v_interlock simple lock. * v_pollinfo is locked by the lock contained inside it. */ struct vnode { u_long v_flag; /* vnode flags (see below) */ int v_usecount; /* reference count of users */ int v_writecount; /* reference count of writers */ int v_holdcnt; /* page & buffer references */ u_long v_id; /* capability identifier */ struct mount *v_mount; /* ptr to vfs we are in */ vop_t **v_op; /* vnode operations vector */ TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */ LIST_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */ struct buflists v_cleanblkhd; /* clean blocklist head */ struct buflists v_dirtyblkhd; /* dirty blocklist head */ LIST_ENTRY(vnode) v_synclist; /* vnodes with dirty buffers */ long v_numoutput; /* num of writes in progress */ enum vtype v_type; /* vnode type */ union { struct mount *vu_mountedhere;/* ptr to mounted vfs (VDIR) */ struct socket *vu_socket; /* unix ipc (VSOCK) */ struct { struct specinfo *vu_specinfo; /* device (VCHR, VBLK) */ SLIST_ENTRY(vnode) vu_specnext; } vu_spec; struct fifoinfo *vu_fifoinfo; /* fifo (VFIFO) */ } v_un; struct nqlease *v_lease; /* Soft reference to lease */ daddr_t v_lastw; /* last write (write cluster) */ daddr_t v_cstart; /* start block of cluster */ daddr_t v_lasta; /* last allocation */ int v_clen; /* length of current cluster */ struct vm_object *v_object; /* Place to store VM object */ struct simplelock v_interlock; /* lock on usecount and flag */ struct lock *v_vnlock; /* used for non-locking fs's */ enum vtagtype v_tag; /* type of underlying data */ void *v_data; /* private data for fs */ LIST_HEAD(, namecache) v_cache_src; /* Cache entries from us */ TAILQ_HEAD(, namecache) v_cache_dst; /* Cache entries to us */ struct vnode *v_dd; /* .. vnode */ u_long v_ddid; /* .. capability identifier */ struct { struct simplelock vpi_lock; /* lock to protect below */ struct selinfo vpi_selinfo; /* identity of poller(s) */ short vpi_events; /* what they are looking for */ short vpi_revents; /* what has happened */ } v_pollinfo; #ifdef DEBUG_LOCKS const char *filename; /* Source file doing locking */ int line; /* Line number doing locking */ #endif }; #define v_mountedhere v_un.vu_mountedhere #define v_socket v_un.vu_socket #define v_rdev v_un.vu_spec.vu_specinfo #define v_specnext v_un.vu_spec.vu_specnext #define v_fifoinfo v_un.vu_fifoinfo #define VN_POLLEVENT(vp, events) \ do { \ if ((vp)->v_pollinfo.vpi_events & (events)) \ vn_pollevent((vp), (events)); \ } while (0) /* * Vnode flags. */ #define VROOT 0x00001 /* root of its file system */ #define VTEXT 0x00002 /* vnode is a pure text prototype */ #define VSYSTEM 0x00004 /* vnode being used by kernel */ #define VISTTY 0x00008 /* vnode represents a tty */ #define VXLOCK 0x00100 /* vnode is locked to change underlying type */ #define VXWANT 0x00200 /* process is waiting for vnode */ #define VBWAIT 0x00400 /* waiting for output to complete */ /* open for business 0x00800 */ /* open for business 0x01000 */ #define VOBJBUF 0x02000 /* Allocate buffers in VM object */ /* open for business 0x04000 */ #define VAGE 0x08000 /* Insert vnode at head of free list */ #define VOLOCK 0x10000 /* vnode is locked waiting for an object */ #define VOWANT 0x20000 /* a process is waiting for VOLOCK */ #define VDOOMED 0x40000 /* This vnode is being recycled */ #define VFREE 0x80000 /* This vnode is on the freelist */ #define VTBFREE 0x100000 /* This vnode is on the to-be-freelist */ #define VONWORKLST 0x200000 /* On syncer work-list */ #define VMOUNT 0x400000 /* Mount in progress */ /* * Vnode attributes. A field value of VNOVAL represents a field whose value * is unavailable (getattr) or which is not to be changed (setattr). */ struct vattr { enum vtype va_type; /* vnode type (for create) */ u_short va_mode; /* files access mode and type */ short va_nlink; /* number of references to file */ uid_t va_uid; /* owner user id */ gid_t va_gid; /* owner group id */ udev_t va_fsid; /* file system id */ long va_fileid; /* file id */ u_quad_t va_size; /* file size in bytes */ long va_blocksize; /* blocksize preferred for i/o */ struct timespec va_atime; /* time of last access */ struct timespec va_mtime; /* time of last modification */ struct timespec va_ctime; /* time file changed */ u_long va_gen; /* generation number of file */ u_long va_flags; /* flags defined for file */ udev_t va_rdev; /* device the special file represents */ u_quad_t va_bytes; /* bytes of disk space held by file */ u_quad_t va_filerev; /* file modification number */ u_int va_vaflags; /* operations flags, see below */ long va_spare; /* remain quad aligned */ }; /* * Flags for va_vaflags. */ #define VA_UTIMES_NULL 0x01 /* utimes argument was NULL */ #define VA_EXCLUSIVE 0x02 /* exclusive create request */ /* * Flags for ioflag. (high 16 bits used to ask for read-ahead and * help with write clustering) */ #define IO_UNIT 0x01 /* do I/O as atomic unit */ #define IO_APPEND 0x02 /* append write to end */ #define IO_SYNC 0x04 /* do I/O synchronously */ #define IO_NODELOCKED 0x08 /* underlying node already locked */ #define IO_NDELAY 0x10 /* FNDELAY flag set in file table */ #define IO_VMIO 0x20 /* data already in VMIO space */ #define IO_INVAL 0x40 /* invalidate after I/O */ /* * Modes. Some values same as Ixxx entries from inode.h for now. */ #define VSUID 04000 /* set user id on execution */ #define VSGID 02000 /* set group id on execution */ #define VSVTX 01000 /* save swapped text even after use */ #define VREAD 00400 /* read, write, execute permissions */ #define VWRITE 00200 #define VEXEC 00100 /* * Token indicating no attribute value yet assigned. */ #define VNOVAL (-1) #ifdef _KERNEL #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_VNODE); #endif /* * Convert between vnode types and inode formats (since POSIX.1 * defines mode word of stat structure in terms of inode formats). */ extern enum vtype iftovt_tab[]; extern int vttoif_tab[]; #define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12]) #define VTTOIF(indx) (vttoif_tab[(int)(indx)]) #define MAKEIMODE(indx, mode) (int)(VTTOIF(indx) | (mode)) /* * Flags to various vnode functions. */ #define SKIPSYSTEM 0x0001 /* vflush: skip vnodes marked VSYSTEM */ #define FORCECLOSE 0x0002 /* vflush: force file closure */ #define WRITECLOSE 0x0004 /* vflush: only close writable files */ #define DOCLOSE 0x0008 /* vclean: close active files */ #define V_SAVE 0x0001 /* vinvalbuf: sync file first */ #define REVOKEALL 0x0001 /* vop_revoke: revoke all aliases */ #define VREF(vp) vref(vp) #ifdef DIAGNOSTIC #define VATTR_NULL(vap) vattr_null(vap) #else #define VATTR_NULL(vap) (*(vap) = va_null) /* initialize a vattr */ #endif /* DIAGNOSTIC */ #define NULLVP ((struct vnode *)NULL) #define VNODEOP_SET(f) \ C_SYSINIT(f##init, SI_SUB_VFS, SI_ORDER_SECOND, vfs_add_vnodeops, &f); \ C_SYSUNINIT(f##uninit, SI_SUB_VFS, SI_ORDER_SECOND, vfs_rm_vnodeops, &f); /* * Global vnode data. */ extern struct vnode *rootvnode; /* root (i.e. "/") vnode */ extern int desiredvnodes; /* number of vnodes desired */ extern time_t syncdelay; /* max time to delay syncing data */ extern time_t filedelay; /* time to delay syncing files */ extern time_t dirdelay; /* time to delay syncing directories */ extern time_t metadelay; /* time to delay syncing metadata */ extern struct vm_zone *namei_zone; extern int prtactive; /* nonzero to call vprint() */ extern struct vattr va_null; /* predefined null vattr structure */ extern int vfs_ioopt; /* * Macro/function to check for client cache inconsistency w.r.t. leasing. */ #define LEASE_READ 0x1 /* Check lease for readers */ #define LEASE_WRITE 0x2 /* Check lease for modifiers */ extern void (*lease_updatetime) __P((int deltat)); #define VSHOULDFREE(vp) \ (!((vp)->v_flag & (VFREE|VDOOMED)) && \ !(vp)->v_holdcnt && !(vp)->v_usecount && \ (!(vp)->v_object || \ !((vp)->v_object->ref_count || (vp)->v_object->resident_page_count))) #define VSHOULDBUSY(vp) \ (((vp)->v_flag & (VFREE|VTBFREE)) && \ ((vp)->v_holdcnt || (vp)->v_usecount)) #endif /* _KERNEL */ /* * Mods for extensibility. */ /* * Flags for vdesc_flags: */ #define VDESC_MAX_VPS 16 /* Low order 16 flag bits are reserved for willrele flags for vp arguments. */ #define VDESC_VP0_WILLRELE 0x0001 #define VDESC_VP1_WILLRELE 0x0002 #define VDESC_VP2_WILLRELE 0x0004 #define VDESC_VP3_WILLRELE 0x0008 #define VDESC_NOMAP_VPP 0x0100 #define VDESC_VPP_WILLRELE 0x0200 /* * VDESC_NO_OFFSET is used to identify the end of the offset list * and in places where no such field exists. */ #define VDESC_NO_OFFSET -1 /* * This structure describes the vnode operation taking place. */ struct vnodeop_desc { int vdesc_offset; /* offset in vector--first for speed */ char *vdesc_name; /* a readable name for debugging */ int vdesc_flags; /* VDESC_* flags */ /* * These ops are used by bypass routines to map and locate arguments. * Creds and procs are not needed in bypass routines, but sometimes * they are useful to (for example) transport layers. * Nameidata is useful because it has a cred in it. */ int *vdesc_vp_offsets; /* list ended by VDESC_NO_OFFSET */ int vdesc_vpp_offset; /* return vpp location */ int vdesc_cred_offset; /* cred location, if any */ int vdesc_proc_offset; /* proc location, if any */ int vdesc_componentname_offset; /* if any */ /* * Finally, we've got a list of private data (about each operation) * for each transport layer. (Support to manage this list is not * yet part of BSD.) */ caddr_t *vdesc_transports; }; #ifdef _KERNEL /* * A list of all the operation descs. */ extern struct vnodeop_desc *vnodeop_descs[]; /* * Interlock for scanning list of vnodes attached to a mountpoint */ extern struct simplelock mntvnode_slock; /* * This macro is very helpful in defining those offsets in the vdesc struct. * * This is stolen from X11R4. I ignored all the fancy stuff for * Crays, so if you decide to port this to such a serious machine, * you might want to consult Intrinsic.h's XtOffset{,Of,To}. */ #define VOPARG_OFFSET(p_type,field) \ ((int) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL))) #define VOPARG_OFFSETOF(s_type,field) \ VOPARG_OFFSET(s_type*,field) #define VOPARG_OFFSETTO(S_TYPE,S_OFFSET,STRUCT_P) \ ((S_TYPE)(((char*)(STRUCT_P))+(S_OFFSET))) /* * This structure is used to configure the new vnodeops vector. */ struct vnodeopv_entry_desc { struct vnodeop_desc *opve_op; /* which operation this is */ vop_t *opve_impl; /* code implementing this operation */ }; struct vnodeopv_desc { /* ptr to the ptr to the vector where op should go */ vop_t ***opv_desc_vector_p; struct vnodeopv_entry_desc *opv_desc_ops; /* null terminated list */ }; /* * A generic structure. * This can be used by bypass routines to identify generic arguments. */ struct vop_generic_args { struct vnodeop_desc *a_desc; /* other random data follows, presumably */ }; #ifdef DEBUG_VFS_LOCKS /* * Macros to aid in tracing VFS locking problems. Not totally * reliable since if the process sleeps between changing the lock * state and checking it with the assert, some other process could * change the state. They are good enough for debugging a single * filesystem using a single-threaded test. I find that 'cvs co src' * is a pretty good test. */ /* * [dfr] Kludge until I get around to fixing all the vfs locking. */ #define IS_LOCKING_VFS(vp) ((vp)->v_tag == VT_UFS \ || (vp)->v_tag == VT_MFS \ || (vp)->v_tag == VT_NFS \ || (vp)->v_tag == VT_LFS \ || (vp)->v_tag == VT_ISOFS \ || (vp)->v_tag == VT_MSDOSFS \ || (vp)->v_tag == VT_DEVFS) #define ASSERT_VOP_LOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && !VOP_ISLOCKED(_vp, NULL)) \ panic("%s: %p is not locked but should be", str, _vp); \ } while (0) #define ASSERT_VOP_UNLOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ int lockstate; \ \ if (_vp && IS_LOCKING_VFS(_vp)) { \ lockstate = VOP_ISLOCKED(_vp, curproc); \ if (lockstate == LK_EXCLUSIVE) \ panic("%s: %p is locked but should not be", \ str, _vp); \ } \ } while (0) #define ASSERT_VOP_ELOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && \ VOP_ISLOCKED(_vp, curproc) != LK_EXCLUSIVE) \ panic("%s: %p is not exclusive locked but should be", \ str, _vp); \ } while (0) #define ASSERT_VOP_ELOCKED_OTHER(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && \ VOP_ISLOCKED(_vp, curproc) != LK_EXCLOTHER) \ panic("%s: %p is not exclusive locked by another proc", \ str, _vp); \ } while (0) #define ASSERT_VOP_SLOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && \ VOP_ISLOCKED(_vp, NULL) != LK_SHARED) \ panic("%s: %p is not locked shared but should be", \ str, _vp); \ } while (0) #else #define ASSERT_VOP_LOCKED(vp, str) #define ASSERT_VOP_UNLOCKED(vp, str) #endif /* * VOCALL calls an op given an ops vector. We break it out because BSD's * vclean changes the ops vector and then wants to call ops with the old * vector. */ #define VOCALL(OPSV,OFF,AP) (( *((OPSV)[(OFF)])) (AP)) /* * This call works for vnodes in the kernel. */ #define VCALL(VP,OFF,AP) VOCALL((VP)->v_op,(OFF),(AP)) #define VDESC(OP) (& __CONCAT(OP,_desc)) #define VOFFSET(OP) (VDESC(OP)->vdesc_offset) /* * VMIO support inline */ extern int vmiodirenable; static __inline int vn_canvmio(struct vnode *vp) { if (vp && (vp->v_type == VREG || (vmiodirenable && vp->v_type == VDIR))) return(TRUE); return(FALSE); } /* * Finally, include the default set of vnode operations. */ #include "vnode_if.h" /* * Public vnode manipulation functions. */ struct componentname; struct file; struct mount; struct nameidata; struct ostat; struct proc; struct stat; struct nstat; struct ucred; struct uio; struct vattr; struct vnode; struct vop_bwrite_args; extern int (*lease_check_hook) __P((struct vop_lease_args *)); void addalias __P((struct vnode *vp, dev_t nvp_rdev)); void addaliasu __P((struct vnode *vp, udev_t nvp_rdev)); int bdevvp __P((dev_t dev, struct vnode **vpp)); /* cache_* may belong in namei.h. */ void cache_enter __P((struct vnode *dvp, struct vnode *vp, struct componentname *cnp)); int cache_lookup __P((struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)); void cache_purge __P((struct vnode *vp)); void cache_purgevfs __P((struct mount *mp)); void cvtstat __P((struct stat *st, struct ostat *ost)); void cvtnstat __P((struct stat *sb, struct nstat *nsb)); int getnewvnode __P((enum vtagtype tag, struct mount *mp, vop_t **vops, struct vnode **vpp)); int lease_check __P((struct vop_lease_args *ap)); int spec_vnoperate __P((struct vop_generic_args *)); int speedup_syncer __P((void)); int textvp_fullpath __P((struct proc *p, char **retbuf, char **retfreebuf)); void vattr_null __P((struct vattr *vap)); int vcount __P((struct vnode *vp)); void vdrop __P((struct vnode *)); int vfinddev __P((dev_t dev, enum vtype type, struct vnode **vpp)); void vfs_add_vnodeops __P((const void *)); void vfs_rm_vnodeops __P((const void *)); int vflush __P((struct mount *mp, struct vnode *skipvp, int flags)); int vget __P((struct vnode *vp, int lockflag, struct proc *p)); void vgone __P((struct vnode *vp)); void vgonel __P((struct vnode *vp, struct proc *p)); void vhold __P((struct vnode *)); int vinvalbuf __P((struct vnode *vp, int save, struct ucred *cred, struct proc *p, int slpflag, int slptimeo)); int vtruncbuf __P((struct vnode *vp, struct ucred *cred, struct proc *p, off_t length, int blksize)); void vprint __P((char *label, struct vnode *vp)); int vrecycle __P((struct vnode *vp, struct simplelock *inter_lkp, struct proc *p)); int vn_close __P((struct vnode *vp, int flags, struct ucred *cred, struct proc *p)); int vn_isdisk __P((struct vnode *vp, int *errp)); int vn_lock __P((struct vnode *vp, int flags, struct proc *p)); #ifdef DEBUG_LOCKS int debug_vn_lock __P((struct vnode *vp, int flags, struct proc *p, const char *filename, int line)); #define vn_lock(vp,flags,p) debug_vn_lock(vp,flags,p,__FILE__,__LINE__) #endif -int vn_open __P((struct nameidata *ndp, int fmode, int cmode)); +int vn_open __P((struct nameidata *ndp, int *flagp, int cmode)); void vn_pollevent __P((struct vnode *vp, int events)); void vn_pollgone __P((struct vnode *vp)); int vn_pollrecord __P((struct vnode *vp, struct proc *p, int events)); int vn_rdwr __P((enum uio_rw rw, struct vnode *vp, caddr_t base, int len, off_t offset, enum uio_seg segflg, int ioflg, struct ucred *cred, int *aresid, struct proc *p)); int vn_stat __P((struct vnode *vp, struct stat *sb, struct proc *p)); dev_t vn_todev __P((struct vnode *vp)); int vfs_cache_lookup __P((struct vop_lookup_args *ap)); int vfs_object_create __P((struct vnode *vp, struct proc *p, struct ucred *cred)); void vfs_timestamp __P((struct timespec *)); int vn_writechk __P((struct vnode *vp)); int vop_stdbwrite __P((struct vop_bwrite_args *ap)); int vop_stdislocked __P((struct vop_islocked_args *)); int vop_stdlock __P((struct vop_lock_args *)); int vop_stdunlock __P((struct vop_unlock_args *)); int vop_noislocked __P((struct vop_islocked_args *)); int vop_nolock __P((struct vop_lock_args *)); int vop_nopoll __P((struct vop_poll_args *)); int vop_nounlock __P((struct vop_unlock_args *)); int vop_stdpathconf __P((struct vop_pathconf_args *)); int vop_stdpoll __P((struct vop_poll_args *)); int vop_revoke __P((struct vop_revoke_args *)); int vop_sharedlock __P((struct vop_lock_args *)); int vop_eopnotsupp __P((struct vop_generic_args *ap)); int vop_ebadf __P((struct vop_generic_args *ap)); int vop_einval __P((struct vop_generic_args *ap)); int vop_enotty __P((struct vop_generic_args *ap)); int vop_defaultop __P((struct vop_generic_args *ap)); int vop_null __P((struct vop_generic_args *ap)); int vop_panic __P((struct vop_generic_args *ap)); void vput __P((struct vnode *vp)); void vrele __P((struct vnode *vp)); void vref __P((struct vnode *vp)); void vbusy __P((struct vnode *vp)); extern vop_t **default_vnodeop_p; extern vop_t **spec_vnodeop_p; extern TAILQ_HEAD(tobefreelist, vnode) vnode_tobefree_list; /* vnode free list */ #endif /* _KERNEL */ #endif /* !_SYS_VNODE_H_ */ Index: head/sys/ufs/ufs/ufs_extattr.c =================================================================== --- head/sys/ufs/ufs/ufs_extattr.c (revision 62549) +++ head/sys/ufs/ufs/ufs_extattr.c (revision 62550) @@ -1,816 +1,817 @@ /*- * Copyright (c) 1999, 2000 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * TrustedBSD Project - extended attribute support for UFS-like file systems */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MIN(a,b) (((a)<(b))?(a):(b)) static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute"); static int ufs_extattr_credcheck(struct ufs_extattr_list_entry *uele, u_int32_t fowner, struct ucred *cred, struct proc *p, int access); static int ufs_extattr_enable(struct ufsmount *ump, char *attrname, struct vnode *backing_vnode, struct proc *p); static int ufs_extattr_disable(struct ufsmount *ump, char *attrname, struct proc *p); static int ufs_extattr_get(struct vnode *vp, char *name, struct uio *uio, struct ucred *cred, struct proc *p); static int ufs_extattr_set(struct vnode *vp, char *name, struct uio *uio, struct ucred *cred, struct proc *p); static int ufs_extattr_rm(struct vnode *vp, char *name, struct ucred *cred, struct proc *p); /* * Per-FS attribute lock protecting attribute operations * XXX Right now there is a lot of lock contention due to having a single * lock per-FS; really, this should be far more fine-grained. */ static void ufs_extattr_uepm_lock(struct ufsmount *ump, struct proc *p) { /* ideally, LK_CANRECURSE would not be used, here */ lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE, 0, p); } static void ufs_extattr_uepm_unlock(struct ufsmount *ump, struct proc *p) { lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, p); } /* * Locate an attribute given a name and mountpoint. * Must be holding uepm lock for the mount point. */ static struct ufs_extattr_list_entry * ufs_exttatr_find_attr(struct ufsmount *ump, char *attrname) { struct ufs_extattr_list_entry *search_attribute; for (search_attribute = ump->um_extattr.uepm_list.lh_first; search_attribute; search_attribute = search_attribute->uele_entries.le_next) { if (!(strncmp(attrname, search_attribute->uele_attrname, UFS_EXTATTR_MAXEXTATTRNAME))) { return (search_attribute); } } return (0); } /* * Initialize per-FS structures supporting extended attributes. Do not * start extended attributes yet. */ void ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm) { uepm->uepm_flags = 0; LIST_INIT(&uepm->uepm_list); /* XXX is PVFS right, here? */ lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0); uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED; } /* * Start extended attribute support on an FS */ int ufs_extattr_start(struct mount *mp, struct proc *p) { struct ufsmount *ump; int error = 0; ump = VFSTOUFS(mp); ufs_extattr_uepm_lock(ump, p); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) { error = EOPNOTSUPP; goto unlock; } if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) { error = EBUSY; goto unlock; } ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED; crhold(p->p_ucred); ump->um_extattr.uepm_ucred = p->p_ucred; unlock: ufs_extattr_uepm_unlock(ump, p); return (error); } /* * Stop extended attribute support on an FS */ int ufs_extattr_stop(struct mount *mp, struct proc *p) { struct ufs_extattr_list_entry *uele; struct ufsmount *ump = VFSTOUFS(mp); int error = 0; ufs_extattr_uepm_lock(ump, p); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { error = EOPNOTSUPP; goto unlock; } while (ump->um_extattr.uepm_list.lh_first != NULL) { uele = ump->um_extattr.uepm_list.lh_first; ufs_extattr_disable(ump, uele->uele_attrname, p); } ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; crfree(ump->um_extattr.uepm_ucred); ump->um_extattr.uepm_ucred = NULL; unlock: ufs_extattr_uepm_unlock(ump, p); return (error); } /* * Enable a named attribute on the specified file system; provide a * backing vnode to hold the attribute data. */ static int ufs_extattr_enable(struct ufsmount *ump, char *attrname, struct vnode *backing_vnode, struct proc *p) { struct ufs_extattr_list_entry *attribute; struct iovec aiov; struct uio auio; int error = 0; if (backing_vnode->v_type != VREG) return (EINVAL); MALLOC(attribute, struct ufs_extattr_list_entry *, sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK); if (attribute == NULL) return (ENOMEM); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { error = EOPNOTSUPP; goto free_exit; } if (ufs_exttatr_find_attr(ump, attrname)) { error = EOPNOTSUPP; goto free_exit; } strncpy(attribute->uele_attrname, attrname, UFS_EXTATTR_MAXEXTATTRNAME); bzero(&attribute->uele_fileheader, sizeof(struct ufs_extattr_fileheader)); attribute->uele_backing_vnode = backing_vnode; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = (caddr_t) &attribute->uele_fileheader; aiov.iov_len = sizeof(struct ufs_extattr_fileheader); auio.uio_resid = sizeof(struct ufs_extattr_fileheader); auio.uio_offset = (off_t) 0; auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_READ; auio.uio_procp = (struct proc *) p; VOP_LEASE(backing_vnode, p, p->p_cred->pc_ucred, LEASE_WRITE); vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p); error = VOP_READ(backing_vnode, &auio, 0, ump->um_extattr.uepm_ucred); VOP_UNLOCK(backing_vnode, 0, p); if (error) { goto free_exit; } if (auio.uio_resid != 0) { printf("ufs_extattr_enable: malformed attribute header\n"); error = EINVAL; goto free_exit; } if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) { printf("ufs_extattr_enable: invalid attribute header magic\n"); error = EINVAL; goto free_exit; } if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) { printf("ufs_extattr_enable: incorrect attribute header " "version\n"); error = EINVAL; goto free_exit; } backing_vnode->v_flag |= VSYSTEM; LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, uele_entries); return (0); free_exit: FREE(attribute, M_UFS_EXTATTR); return (error); } /* * Disable extended attribute support on an FS */ static int ufs_extattr_disable(struct ufsmount *ump, char *attrname, struct proc *p) { struct ufs_extattr_list_entry *uele; int error = 0; uele = ufs_exttatr_find_attr(ump, attrname); if (!uele) return (ENOENT); LIST_REMOVE(uele, uele_entries); uele->uele_backing_vnode->v_flag &= ~VSYSTEM; error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, p->p_ucred, p); FREE(uele, M_UFS_EXTATTR); return (error); } /* * VFS call to manage extended attributes in UFS * attrname, arg are userspace pointers from the syscall */ int ufs_extattrctl(struct mount *mp, int cmd, char *attrname, caddr_t arg, struct proc *p) { struct nameidata nd; struct ufsmount *ump = VFSTOUFS(mp); struct vnode *vp; char local_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; /* inc null */ char *filename; - int error, len; + int error, len, flags; if ((error = suser_xxx(p->p_cred->pc_ucred, p, 0))) return (error); switch(cmd) { case UFS_EXTATTR_CMD_START: error = ufs_extattr_start(mp, p); return (error); case UFS_EXTATTR_CMD_STOP: return (ufs_extattr_stop(mp, p)); case UFS_EXTATTR_CMD_ENABLE: error = copyinstr(attrname, local_attrname, UFS_EXTATTR_MAXEXTATTRNAME, &len); if (error) return (error); filename = (char *) arg; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, filename, p); - error = vn_open(&nd, FREAD|FWRITE, 0); + flags = FREAD | FWRITE; + error = vn_open(&nd, &flags, 0); if (error) return (error); vp = nd.ni_vp; VOP_UNLOCK(vp, 0, p); ufs_extattr_uepm_lock(ump, p); error = ufs_extattr_enable(ump, local_attrname, vp, p); ufs_extattr_uepm_unlock(ump, p); return (error); case UFS_EXTATTR_CMD_DISABLE: error = copyinstr(attrname, local_attrname, UFS_EXTATTR_MAXEXTATTRNAME, &len); ufs_extattr_uepm_lock(ump, p); error = ufs_extattr_disable(ump, local_attrname, p); ufs_extattr_uepm_unlock(ump, p); return (error); default: return (EINVAL); } } /* * Credential check based on process requesting service, and per-attribute * permissions. */ static int ufs_extattr_credcheck(struct ufs_extattr_list_entry *uele, u_int32_t fowner, struct ucred *cred, struct proc *p, int access) { u_int uef_perm; switch(access) { case IREAD: uef_perm = uele->uele_fileheader.uef_read_perm; break; case IWRITE: uef_perm = uele->uele_fileheader.uef_write_perm; break; default: return (EACCES); } /* Kernel sponsoring request does so without passing a cred */ if (!cred) return (0); /* XXX there might eventually be a capability check here */ /* If it's set to root-only, check for suser(p) */ if (uef_perm == UFS_EXTATTR_PERM_ROOT && !suser(p)) return (0); /* Allow the owner if appropriate */ if (uef_perm == UFS_EXTATTR_PERM_OWNER && cred->cr_uid == fowner) return (0); /* Allow anyone if appropriate */ if (uef_perm == UFS_EXTATTR_PERM_ANYONE) return (0); return (EACCES); } /* * Vnode operating to retrieve a named extended attribute */ int ufs_vop_getextattr(struct vop_getextattr_args *ap) /* vop_getextattr { IN struct vnode *a_vp; IN char *a_name; INOUT struct uio *a_uio; IN struct ucred *a_cred; IN struct proc *a_p; }; */ { struct mount *mp = ap->a_vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); int error; ufs_extattr_uepm_lock(ump, ap->a_p); error = ufs_extattr_get(ap->a_vp, ap->a_name, ap->a_uio, ap->a_cred, ap->a_p); ufs_extattr_uepm_unlock(ump, ap->a_p); return (error); } /* * Real work associated with retrieving a named attribute--assumes that * the attribute lock has already been grabbed. */ static int ufs_extattr_get(struct vnode *vp, char *name, struct uio *uio, struct ucred *cred, struct proc *p) { struct ufs_extattr_list_entry *attribute; struct ufs_extattr_header ueh; struct iovec local_aiov; struct uio local_aio; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); struct inode *ip = VTOI(vp); off_t base_offset; size_t size, old_size; int error = 0; if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) return (EOPNOTSUPP); attribute = ufs_exttatr_find_attr(ump, name); if (!attribute) return (ENOENT); if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p, IREAD))) return (error); /* * Allow only offsets of zero to encourage the read/replace * extended attribute semantic. Otherwise we can't guarantee * atomicity, as we don't provide locks for extended * attributes. */ if (uio->uio_offset != 0) return (ENXIO); /* * Find base offset of header in file based on file header size, and * data header size + maximum data size, indexed by inode number */ base_offset = sizeof(struct ufs_extattr_fileheader) + ip->i_number * (sizeof(struct ufs_extattr_header) + attribute->uele_fileheader.uef_size); /* * Read in the data header to see if the data is defined, and if so * how much. */ bzero(&ueh, sizeof(struct ufs_extattr_header)); local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_READ; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_procp = p; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ); vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p); error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; /* defined? */ if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { error = ENOENT; goto vopunlock_exit; } /* valid for the current inode generation? */ if (ueh.ueh_i_gen != ip->i_gen) { /* * The inode itself has a different generation number * than the attribute data. For now, the best solution * is to coerce this to undefined, and let it get cleaned * up by the next write or extattrctl clean. */ printf("ufs_extattr: inode number inconsistency (%d, %d)\n", ueh.ueh_i_gen, ip->i_gen); error = ENOENT; goto vopunlock_exit; } /* local size consistency check */ if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { error = ENXIO; goto vopunlock_exit; } if (ueh.ueh_len < uio->uio_offset) { error = 0; goto vopunlock_exit; } /* allow for offset into the attr data */ uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); /* * Figure out maximum to transfer -- use buffer size and local data * limit. */ size = MIN(uio->uio_resid, ueh.ueh_len); old_size = uio->uio_resid; uio->uio_resid = size; error = VOP_READ(attribute->uele_backing_vnode, uio, 0, ump->um_extattr.uepm_ucred); if (error) { uio->uio_offset = 0; goto vopunlock_exit; } uio->uio_offset = 0; uio->uio_resid = old_size - (size - uio->uio_resid); vopunlock_exit: VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); return (error); } /* * Vnode operation to set a named attribute */ int ufs_vop_setextattr(struct vop_setextattr_args *ap) /* vop_setextattr { IN struct vnode *a_vp; IN char *a_name; INOUT struct uio *a_uio; IN struct ucred *a_cred; IN struct proc *a_p; }; */ { struct mount *mp = ap->a_vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); int error; ufs_extattr_uepm_lock(ump, ap->a_p); if (ap->a_uio) error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio, ap->a_cred, ap->a_p); else error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred, ap->a_p); ufs_extattr_uepm_unlock(ump, ap->a_p); return (error); } /* * Real work associated with setting a vnode's extended attributes; * assumes that the attribute lock has already been grabbed. */ static int ufs_extattr_set(struct vnode *vp, char *name, struct uio *uio, struct ucred *cred, struct proc *p) { struct ufs_extattr_list_entry *attribute; struct ufs_extattr_header ueh; struct iovec local_aiov; struct uio local_aio; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); struct inode *ip = VTOI(vp); off_t base_offset; int error = 0; if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) return (EOPNOTSUPP); attribute = ufs_exttatr_find_attr(ump, name); if (!attribute) return (ENOENT); if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p, IWRITE))) return (error); /* * Early rejection of invalid offsets/lengths * Reject: any offset but 0 (replace) * Any size greater than attribute size limit */ if (uio->uio_offset != 0 || uio->uio_resid > attribute->uele_fileheader.uef_size) return (ENXIO); /* * Find base offset of header in file based on file header size, and * data header size + maximum data size, indexed by inode number */ base_offset = sizeof(struct ufs_extattr_fileheader) + ip->i_number * (sizeof(struct ufs_extattr_header) + attribute->uele_fileheader.uef_size); /* * Write out a data header for the data */ ueh.ueh_len = uio->uio_resid; ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; ueh.ueh_i_gen = ip->i_gen; local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_WRITE; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_procp = p; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); /* * Acquire locks */ VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); /* * Don't need to get a lock on the backing file if the setattr is * being applied to the backing file, as the lock is already held */ if (attribute->uele_backing_vnode != vp) vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; if (local_aio.uio_resid != 0) { error = ENXIO; goto vopunlock_exit; } /* * Write out user data */ uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); error = VOP_WRITE(attribute->uele_backing_vnode, uio, IO_SYNC, ump->um_extattr.uepm_ucred); vopunlock_exit: uio->uio_offset = 0; if (attribute->uele_backing_vnode != vp) VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); return (error); } /* * Real work associated with removing an extended attribute from a vnode. * Assumes the attribute lock has already been grabbed. */ static int ufs_extattr_rm(struct vnode *vp, char *name, struct ucred *cred, struct proc *p) { struct ufs_extattr_list_entry *attribute; struct ufs_extattr_header ueh; struct iovec local_aiov; struct uio local_aio; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); struct inode *ip = VTOI(vp); off_t base_offset; int error = 0; if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) return (EOPNOTSUPP); attribute = ufs_exttatr_find_attr(ump, name); if (!attribute) return (ENOENT); if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p, IWRITE))) return (error); /* * Find base offset of header in file based on file header size, and * data header size + maximum data size, indexed by inode number */ base_offset = sizeof(struct ufs_extattr_fileheader) + ip->i_number * (sizeof(struct ufs_extattr_header) + attribute->uele_fileheader.uef_size); /* * Read in the data header to see if the data is defined */ bzero(&ueh, sizeof(struct ufs_extattr_header)); local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_READ; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_procp = p; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); /* * Don't need to get the lock on the backing vnode if the vnode we're * modifying is it, as we already hold the lock. */ if (attribute->uele_backing_vnode != vp) vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; /* defined? */ if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { error = ENOENT; goto vopunlock_exit; } /* flag it as not in use */ ueh.ueh_flags = 0; error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; if (local_aio.uio_resid != 0) error = ENXIO; vopunlock_exit: VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); return (error); } /* * Called by UFS when an inode is no longer active and should have its * attributes stripped. */ void ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p) { struct ufs_extattr_list_entry *uele; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); ufs_extattr_uepm_lock(ump, p); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { ufs_extattr_uepm_unlock(ump, p); return; } for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL; uele = uele->uele_entries.le_next) ufs_extattr_rm(vp, uele->uele_attrname, 0, p); ufs_extattr_uepm_unlock(ump, p); } Index: head/sys/ufs/ufs/ufs_quota.c =================================================================== --- head/sys/ufs/ufs/ufs_quota.c (revision 62549) +++ head/sys/ufs/ufs/ufs_quota.c (revision 62550) @@ -1,957 +1,958 @@ /* * Copyright (c) 1982, 1986, 1990, 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Robert Elz at The University of Melbourne. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_DQUOT, "UFS quota", "UFS quota entries"); /* * Quota name to error message mapping. */ static char *quotatypes[] = INITQFNAMES; static int chkdqchg __P((struct inode *, long, struct ucred *, int)); static int chkiqchg __P((struct inode *, long, struct ucred *, int)); static int dqget __P((struct vnode *, u_long, struct ufsmount *, int, struct dquot **)); static int dqsync __P((struct vnode *, struct dquot *)); static void dqflush __P((struct vnode *)); #ifdef DIAGNOSTIC static void dqref __P((struct dquot *)); static void chkdquot __P((struct inode *)); #endif /* * Set up the quotas for an inode. * * This routine completely defines the semantics of quotas. * If other criterion want to be used to establish quotas, the * MAXQUOTAS value in quotas.h should be increased, and the * additional dquots set up here. */ int getinoquota(ip) register struct inode *ip; { struct ufsmount *ump; struct vnode *vp = ITOV(ip); int error; ump = VFSTOUFS(vp->v_mount); /* * Set up the user quota based on file uid. * EINVAL means that quotas are not enabled. */ if (ip->i_dquot[USRQUOTA] == NODQUOT && (error = dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) && error != EINVAL) return (error); /* * Set up the group quota based on file gid. * EINVAL means that quotas are not enabled. */ if (ip->i_dquot[GRPQUOTA] == NODQUOT && (error = dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) && error != EINVAL) return (error); return (0); } /* * Update disk usage, and take corrective action. */ int chkdq(ip, change, cred, flags) register struct inode *ip; long change; struct ucred *cred; int flags; { register struct dquot *dq; register int i; int ncurblocks, error; #ifdef DIAGNOSTIC if ((flags & CHOWN) == 0) chkdquot(ip); #endif if (change == 0) return (0); if (change < 0) { for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, PINOD+1, "chkdq1", 0); } ncurblocks = dq->dq_curblocks + change; if (ncurblocks >= 0) dq->dq_curblocks = ncurblocks; else dq->dq_curblocks = 0; dq->dq_flags &= ~DQ_BLKS; dq->dq_flags |= DQ_MOD; } return (0); } if ((flags & FORCE) == 0 && cred->cr_uid != 0) { for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; error = chkdqchg(ip, change, cred, i); if (error) return (error); } } for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, PINOD+1, "chkdq2", 0); } /* Reset timer when crossing soft limit */ if (dq->dq_curblocks + change >= dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit) dq->dq_btime = time_second + VFSTOUFS(ITOV(ip)->v_mount)->um_btime[i]; dq->dq_curblocks += change; dq->dq_flags |= DQ_MOD; } return (0); } /* * Check for a valid change to a users allocation. * Issue an error message if appropriate. */ static int chkdqchg(ip, change, cred, type) struct inode *ip; long change; struct ucred *cred; int type; { register struct dquot *dq = ip->i_dquot[type]; long ncurblocks = dq->dq_curblocks + change; /* * If user would exceed their hard limit, disallow space allocation. */ if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) { if ((dq->dq_flags & DQ_BLKS) == 0 && ip->i_uid == cred->cr_uid) { uprintf("\n%s: write failed, %s disk limit reached\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type]); dq->dq_flags |= DQ_BLKS; } return (EDQUOT); } /* * If user is over their soft limit for too long, disallow space * allocation. Reset time limit as they cross their soft limit. */ if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) { if (dq->dq_curblocks < dq->dq_bsoftlimit) { dq->dq_btime = time_second + VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type]; if (ip->i_uid == cred->cr_uid) uprintf("\n%s: warning, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type], "disk quota exceeded"); return (0); } if (time_second > dq->dq_btime) { if ((dq->dq_flags & DQ_BLKS) == 0 && ip->i_uid == cred->cr_uid) { uprintf("\n%s: write failed, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type], "disk quota exceeded for too long"); dq->dq_flags |= DQ_BLKS; } return (EDQUOT); } } return (0); } /* * Check the inode limit, applying corrective action. */ int chkiq(ip, change, cred, flags) register struct inode *ip; long change; struct ucred *cred; int flags; { register struct dquot *dq; register int i; int ncurinodes, error; #ifdef DIAGNOSTIC if ((flags & CHOWN) == 0) chkdquot(ip); #endif if (change == 0) return (0); if (change < 0) { for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, PINOD+1, "chkiq1", 0); } ncurinodes = dq->dq_curinodes + change; if (ncurinodes >= 0) dq->dq_curinodes = ncurinodes; else dq->dq_curinodes = 0; dq->dq_flags &= ~DQ_INODS; dq->dq_flags |= DQ_MOD; } return (0); } if ((flags & FORCE) == 0 && cred->cr_uid != 0) { for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; error = chkiqchg(ip, change, cred, i); if (error) return (error); } } for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, PINOD+1, "chkiq2", 0); } /* Reset timer when crossing soft limit */ if (dq->dq_curinodes + change >= dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit) dq->dq_itime = time_second + VFSTOUFS(ITOV(ip)->v_mount)->um_itime[i]; dq->dq_curinodes += change; dq->dq_flags |= DQ_MOD; } return (0); } /* * Check for a valid change to a users allocation. * Issue an error message if appropriate. */ static int chkiqchg(ip, change, cred, type) struct inode *ip; long change; struct ucred *cred; int type; { register struct dquot *dq = ip->i_dquot[type]; long ncurinodes = dq->dq_curinodes + change; /* * If user would exceed their hard limit, disallow inode allocation. */ if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) { if ((dq->dq_flags & DQ_INODS) == 0 && ip->i_uid == cred->cr_uid) { uprintf("\n%s: write failed, %s inode limit reached\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type]); dq->dq_flags |= DQ_INODS; } return (EDQUOT); } /* * If user is over their soft limit for too long, disallow inode * allocation. Reset time limit as they cross their soft limit. */ if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) { if (dq->dq_curinodes < dq->dq_isoftlimit) { dq->dq_itime = time_second + VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type]; if (ip->i_uid == cred->cr_uid) uprintf("\n%s: warning, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type], "inode quota exceeded"); return (0); } if (time_second > dq->dq_itime) { if ((dq->dq_flags & DQ_INODS) == 0 && ip->i_uid == cred->cr_uid) { uprintf("\n%s: write failed, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type], "inode quota exceeded for too long"); dq->dq_flags |= DQ_INODS; } return (EDQUOT); } } return (0); } #ifdef DIAGNOSTIC /* * On filesystems with quotas enabled, it is an error for a file to change * size and not to have a dquot structure associated with it. */ static void chkdquot(ip) register struct inode *ip; { struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount); register int i; for (i = 0; i < MAXQUOTAS; i++) { if (ump->um_quotas[i] == NULLVP || (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING))) continue; if (ip->i_dquot[i] == NODQUOT) { vprint("chkdquot: missing dquot", ITOV(ip)); panic("chkdquot: missing dquot"); } } } #endif /* * Code to process quotactl commands. */ /* * Q_QUOTAON - set up a quota file for a particular file system. */ int quotaon(p, mp, type, fname) struct proc *p; struct mount *mp; register int type; caddr_t fname; { struct ufsmount *ump = VFSTOUFS(mp); struct vnode *vp, **vpp; struct vnode *nextvp; struct dquot *dq; - int error; + int error, flags; struct nameidata nd; vpp = &ump->um_quotas[type]; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, p); - error = vn_open(&nd, FREAD|FWRITE, 0); + flags = FREAD | FWRITE; + error = vn_open(&nd, &flags, 0); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; VOP_UNLOCK(vp, 0, p); if (vp->v_type != VREG) { (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p); return (EACCES); } if (*vpp != vp) quotaoff(p, mp, type); ump->um_qflags[type] |= QTF_OPENING; mp->mnt_flag |= MNT_QUOTA; vp->v_flag |= VSYSTEM; *vpp = vp; /* * Save the credential of the process that turned on quotas. * Set up the time limits for this quota. */ crhold(p->p_ucred); ump->um_cred[type] = p->p_ucred; ump->um_btime[type] = MAX_DQ_TIME; ump->um_itime[type] = MAX_IQ_TIME; if (dqget(NULLVP, 0, ump, type, &dq) == 0) { if (dq->dq_btime > 0) ump->um_btime[type] = dq->dq_btime; if (dq->dq_itime > 0) ump->um_itime[type] = dq->dq_itime; dqrele(NULLVP, dq); } /* * Search vnodes associated with this mount point, * adding references to quota file being opened. * NB: only need to add dquot's for inodes being modified. */ again: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) { nextvp = vp->v_mntvnodes.le_next; if (vp->v_type == VNON || vp->v_writecount == 0) continue; if (vget(vp, LK_EXCLUSIVE, p)) goto again; error = getinoquota(VTOI(vp)); if (error) { vput(vp); break; } vput(vp); if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp) goto again; } ump->um_qflags[type] &= ~QTF_OPENING; if (error) quotaoff(p, mp, type); return (error); } /* * Q_QUOTAOFF - turn off disk quotas for a filesystem. */ int quotaoff(p, mp, type) struct proc *p; struct mount *mp; register int type; { struct vnode *vp; struct vnode *qvp, *nextvp; struct ufsmount *ump = VFSTOUFS(mp); struct dquot *dq; struct inode *ip; int error; if ((qvp = ump->um_quotas[type]) == NULLVP) return (0); ump->um_qflags[type] |= QTF_CLOSING; /* * Search vnodes associated with this mount point, * deleting any references to quota file being closed. */ again: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) { nextvp = vp->v_mntvnodes.le_next; if (vp->v_type == VNON) continue; if (vget(vp, LK_EXCLUSIVE, p)) goto again; ip = VTOI(vp); dq = ip->i_dquot[type]; ip->i_dquot[type] = NODQUOT; dqrele(vp, dq); vput(vp); if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp) goto again; } dqflush(qvp); qvp->v_flag &= ~VSYSTEM; error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p); ump->um_quotas[type] = NULLVP; crfree(ump->um_cred[type]); ump->um_cred[type] = NOCRED; ump->um_qflags[type] &= ~QTF_CLOSING; for (type = 0; type < MAXQUOTAS; type++) if (ump->um_quotas[type] != NULLVP) break; if (type == MAXQUOTAS) mp->mnt_flag &= ~MNT_QUOTA; return (error); } /* * Q_GETQUOTA - return current values in a dqblk structure. */ int getquota(mp, id, type, addr) struct mount *mp; u_long id; int type; caddr_t addr; { struct dquot *dq; int error; error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq); if (error) return (error); error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk)); dqrele(NULLVP, dq); return (error); } /* * Q_SETQUOTA - assign an entire dqblk structure. */ int setquota(mp, id, type, addr) struct mount *mp; u_long id; int type; caddr_t addr; { register struct dquot *dq; struct dquot *ndq; struct ufsmount *ump = VFSTOUFS(mp); struct dqblk newlim; int error; error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)); if (error) return (error); error = dqget(NULLVP, id, ump, type, &ndq); if (error) return (error); dq = ndq; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, PINOD+1, "setqta", 0); } /* * Copy all but the current values. * Reset time limit if previously had no soft limit or were * under it, but now have a soft limit and are over it. */ newlim.dqb_curblocks = dq->dq_curblocks; newlim.dqb_curinodes = dq->dq_curinodes; if (dq->dq_id != 0) { newlim.dqb_btime = dq->dq_btime; newlim.dqb_itime = dq->dq_itime; } if (newlim.dqb_bsoftlimit && dq->dq_curblocks >= newlim.dqb_bsoftlimit && (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) newlim.dqb_btime = time_second + ump->um_btime[type]; if (newlim.dqb_isoftlimit && dq->dq_curinodes >= newlim.dqb_isoftlimit && (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) newlim.dqb_itime = time_second + ump->um_itime[type]; dq->dq_dqb = newlim; if (dq->dq_curblocks < dq->dq_bsoftlimit) dq->dq_flags &= ~DQ_BLKS; if (dq->dq_curinodes < dq->dq_isoftlimit) dq->dq_flags &= ~DQ_INODS; if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) dq->dq_flags |= DQ_FAKE; else dq->dq_flags &= ~DQ_FAKE; dq->dq_flags |= DQ_MOD; dqrele(NULLVP, dq); return (0); } /* * Q_SETUSE - set current inode and block usage. */ int setuse(mp, id, type, addr) struct mount *mp; u_long id; int type; caddr_t addr; { register struct dquot *dq; struct ufsmount *ump = VFSTOUFS(mp); struct dquot *ndq; struct dqblk usage; int error; error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk)); if (error) return (error); error = dqget(NULLVP, id, ump, type, &ndq); if (error) return (error); dq = ndq; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, PINOD+1, "setuse", 0); } /* * Reset time limit if have a soft limit and were * previously under it, but are now over it. */ if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit && usage.dqb_curblocks >= dq->dq_bsoftlimit) dq->dq_btime = time_second + ump->um_btime[type]; if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && usage.dqb_curinodes >= dq->dq_isoftlimit) dq->dq_itime = time_second + ump->um_itime[type]; dq->dq_curblocks = usage.dqb_curblocks; dq->dq_curinodes = usage.dqb_curinodes; if (dq->dq_curblocks < dq->dq_bsoftlimit) dq->dq_flags &= ~DQ_BLKS; if (dq->dq_curinodes < dq->dq_isoftlimit) dq->dq_flags &= ~DQ_INODS; dq->dq_flags |= DQ_MOD; dqrele(NULLVP, dq); return (0); } /* * Q_SYNC - sync quota files to disk. */ int qsync(mp) struct mount *mp; { struct ufsmount *ump = VFSTOUFS(mp); struct proc *p = curproc; /* XXX */ struct vnode *vp, *nextvp; struct dquot *dq; int i, error; /* * Check if the mount point has any quotas. * If not, simply return. */ for (i = 0; i < MAXQUOTAS; i++) if (ump->um_quotas[i] != NULLVP) break; if (i == MAXQUOTAS) return (0); /* * Search vnodes associated with this mount point, * synchronizing any modified dquot structures. */ simple_lock(&mntvnode_slock); again: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) { if (vp->v_mount != mp) goto again; nextvp = vp->v_mntvnodes.le_next; if (vp->v_type == VNON) continue; simple_lock(&vp->v_interlock); simple_unlock(&mntvnode_slock); error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p); if (error) { simple_lock(&mntvnode_slock); if (error == ENOENT) goto again; continue; } for (i = 0; i < MAXQUOTAS; i++) { dq = VTOI(vp)->i_dquot[i]; if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) dqsync(vp, dq); } vput(vp); simple_lock(&mntvnode_slock); if (vp->v_mntvnodes.le_next != nextvp) goto again; } simple_unlock(&mntvnode_slock); return (0); } /* * Code pertaining to management of the in-core dquot data structures. */ #define DQHASH(dqvp, id) \ (&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash]) static LIST_HEAD(dqhash, dquot) *dqhashtbl; static u_long dqhash; /* * Dquot free list. */ #define DQUOTINC 5 /* minimum free dquots desired */ static TAILQ_HEAD(dqfreelist, dquot) dqfreelist; static long numdquot, desireddquot = DQUOTINC; /* * Initialize the quota system. */ void dqinit() { dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash); TAILQ_INIT(&dqfreelist); } /* * Obtain a dquot structure for the specified identifier and quota file * reading the information from the file if necessary. */ static int dqget(vp, id, ump, type, dqp) struct vnode *vp; u_long id; register struct ufsmount *ump; register int type; struct dquot **dqp; { struct proc *p = curproc; /* XXX */ struct dquot *dq; struct dqhash *dqh; struct vnode *dqvp; struct iovec aiov; struct uio auio; int error; dqvp = ump->um_quotas[type]; if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { *dqp = NODQUOT; return (EINVAL); } /* * Check the cache first. */ dqh = DQHASH(dqvp, id); for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) { if (dq->dq_id != id || dq->dq_ump->um_quotas[dq->dq_type] != dqvp) continue; /* * Cache hit with no references. Take * the structure off the free list. */ if (dq->dq_cnt == 0) TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); DQREF(dq); *dqp = dq; return (0); } /* * Not in cache, allocate a new one. */ if (dqfreelist.tqh_first == NODQUOT && numdquot < MAXQUOTAS * desiredvnodes) desireddquot += DQUOTINC; if (numdquot < desireddquot) { dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK); bzero((char *)dq, sizeof *dq); numdquot++; } else { if ((dq = dqfreelist.tqh_first) == NULL) { tablefull("dquot"); *dqp = NODQUOT; return (EUSERS); } if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) panic("dqget: free dquot isn't"); TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); LIST_REMOVE(dq, dq_hash); } /* * Initialize the contents of the dquot structure. */ if (vp != dqvp) vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p); LIST_INSERT_HEAD(dqh, dq, dq_hash); DQREF(dq); dq->dq_flags = DQ_LOCK; dq->dq_id = id; dq->dq_ump = ump; dq->dq_type = type; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = (caddr_t)&dq->dq_dqb; aiov.iov_len = sizeof (struct dqblk); auio.uio_resid = sizeof (struct dqblk); auio.uio_offset = (off_t)(id * sizeof (struct dqblk)); auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_READ; auio.uio_procp = (struct proc *)0; error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); if (auio.uio_resid == sizeof(struct dqblk) && error == 0) bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk)); if (vp != dqvp) VOP_UNLOCK(dqvp, 0, p); if (dq->dq_flags & DQ_WANT) wakeup((caddr_t)dq); dq->dq_flags = 0; /* * I/O error in reading quota file, release * quota structure and reflect problem to caller. */ if (error) { LIST_REMOVE(dq, dq_hash); dqrele(vp, dq); *dqp = NODQUOT; return (error); } /* * Check for no limit to enforce. * Initialize time values if necessary. */ if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) dq->dq_flags |= DQ_FAKE; if (dq->dq_id != 0) { if (dq->dq_btime == 0) dq->dq_btime = time_second + ump->um_btime[type]; if (dq->dq_itime == 0) dq->dq_itime = time_second + ump->um_itime[type]; } *dqp = dq; return (0); } #ifdef DIAGNOSTIC /* * Obtain a reference to a dquot. */ static void dqref(dq) struct dquot *dq; { dq->dq_cnt++; } #endif /* * Release a reference to a dquot. */ void dqrele(vp, dq) struct vnode *vp; register struct dquot *dq; { if (dq == NODQUOT) return; if (dq->dq_cnt > 1) { dq->dq_cnt--; return; } if (dq->dq_flags & DQ_MOD) (void) dqsync(vp, dq); if (--dq->dq_cnt > 0) return; TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); } /* * Update the disk quota in the quota file. */ static int dqsync(vp, dq) struct vnode *vp; struct dquot *dq; { struct proc *p = curproc; /* XXX */ struct vnode *dqvp; struct iovec aiov; struct uio auio; int error; if (dq == NODQUOT) panic("dqsync: dquot"); if ((dq->dq_flags & DQ_MOD) == 0) return (0); if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP) panic("dqsync: file"); if (vp != dqvp) vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p); while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, PINOD+2, "dqsync", 0); if ((dq->dq_flags & DQ_MOD) == 0) { if (vp != dqvp) VOP_UNLOCK(dqvp, 0, p); return (0); } } dq->dq_flags |= DQ_LOCK; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = (caddr_t)&dq->dq_dqb; aiov.iov_len = sizeof (struct dqblk); auio.uio_resid = sizeof (struct dqblk); auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk)); auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_WRITE; auio.uio_procp = (struct proc *)0; error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); if (auio.uio_resid && error == 0) error = EIO; if (dq->dq_flags & DQ_WANT) wakeup((caddr_t)dq); dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); if (vp != dqvp) VOP_UNLOCK(dqvp, 0, p); return (error); } /* * Flush all entries from the cache for a particular vnode. */ static void dqflush(vp) register struct vnode *vp; { register struct dquot *dq, *nextdq; struct dqhash *dqh; /* * Move all dquot's that used to refer to this quota * file off their hash chains (they will eventually * fall off the head of the free list and be re-used). */ for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) { for (dq = dqh->lh_first; dq; dq = nextdq) { nextdq = dq->dq_hash.le_next; if (dq->dq_ump->um_quotas[dq->dq_type] != vp) continue; if (dq->dq_cnt) panic("dqflush: stray dquot"); LIST_REMOVE(dq, dq_hash); dq->dq_ump = (struct ufsmount *)0; } } }