diff options
Diffstat (limited to 'sys/fs')
-rw-r--r-- | sys/fs/nwfs/nwfs.h | 82 | ||||
-rw-r--r-- | sys/fs/nwfs/nwfs_io.c | 650 | ||||
-rw-r--r-- | sys/fs/nwfs/nwfs_ioctl.c | 103 | ||||
-rw-r--r-- | sys/fs/nwfs/nwfs_mount.h | 78 | ||||
-rw-r--r-- | sys/fs/nwfs/nwfs_node.c | 321 | ||||
-rw-r--r-- | sys/fs/nwfs/nwfs_node.h | 100 | ||||
-rw-r--r-- | sys/fs/nwfs/nwfs_subr.c | 669 | ||||
-rw-r--r-- | sys/fs/nwfs/nwfs_subr.h | 84 | ||||
-rw-r--r-- | sys/fs/nwfs/nwfs_vfsops.c | 515 | ||||
-rw-r--r-- | sys/fs/nwfs/nwfs_vnops.c | 1128 |
10 files changed, 3730 insertions, 0 deletions
diff --git a/sys/fs/nwfs/nwfs.h b/sys/fs/nwfs/nwfs.h new file mode 100644 index 0000000..950d18a --- /dev/null +++ b/sys/fs/nwfs/nwfs.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 _NWFS_H_ +#define _NWFS_H_ + +#include <nwfs/nwfs_mount.h> + +#define NR_OPEN 0 +#define NW_NSB_DOS (1 << NW_NS_DOS) +#define NW_NSB_MAC (1 << NW_NS_MAC) +#define NW_NSB_NFS (1 << NW_NS_NFS) +#define NW_NSB_FTAM (1 << NW_NS_FTAM) +#define NW_NSB_OS2 (1 << NW_NS_OS2) + +#define NWFSIOC_GETCONN _IOR('n',1,int) +#define NWFSIOC_GETEINFO _IOR('n',2,struct nw_entry_info) +#define NWFSIOC_GETNS _IOR('n',3,int) + +#ifdef KERNEL + +#include <sys/vnode.h> +#include <sys/mount.h> + +struct nwfsnode; + +struct nwmount { + struct nwfs_args m; + struct mount *mp; + struct ncp_handle *connh; + int name_space; + struct nwnode *n_root; + u_int32_t n_volume; + ncpfid n_rootent; + int n_id; +}; + +#define VFSTONWFS(mntp) ((struct nwmount *)((mntp)->mnt_data)) +#define NWFSTOVFS(mnp) ((struct mount *)((mnp)->mount)) +#define VTOVFS(vp) ((vp)->v_mount) +#define VTONWFS(vp) (VFSTONWFS(VTOVFS(vp))) +#define NWFSTOCONN(nmp) ((nmp)->connh->nh_conn) + +int ncp_conn_logged_in(struct nwmount *); +int nwfs_ioctl(struct vop_ioctl_args *ap); +int nwfs_doio(struct buf *bp, struct ucred *cr, struct proc *p); +int nwfs_vinvalbuf(struct vnode *vp, int flags, struct ucred *cred, + struct proc *p, int intrflg); +#endif /* KERNEL */ + +#endif /* _NWFS_H_ */ diff --git a/sys/fs/nwfs/nwfs_io.c b/sys/fs/nwfs/nwfs_io.c new file mode 100644 index 0000000..21339b3 --- /dev/null +++ b/sys/fs/nwfs/nwfs_io.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/resourcevar.h> /* defines plimit structure in proc struct */ +#include <sys/kernel.h> +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/unistd.h> +#include <sys/vnode.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/dirent.h> +#include <sys/signalvar.h> +#include <sys/sysctl.h> + +#include <vm/vm.h> +#include <vm/vm_prot.h> +#include <vm/vm_page.h> +#include <vm/vm_extern.h> +#include <vm/vm_zone.h> +#include <vm/vm_object.h> +#include <vm/vm_pager.h> +#include <vm/vnode_pager.h> + +#include <sys/ioccom.h> +#include <netncp/ncp.h> +#include <netncp/ncp_conn.h> +#include <netncp/ncp_subr.h> +#include <netncp/ncp_ncp.h> +#include <netncp/ncp_rq.h> + +#include <nwfs/nwfs.h> +#include <nwfs/nwfs_node.h> +#include <nwfs/nwfs_subr.h> + +static int nwfs_fastlookup = 1; + +extern struct linker_set sysctl_vfs_nwfs; + +SYSCTL_DECL(_vfs_nwfs); +SYSCTL_INT(_vfs_nwfs, OID_AUTO, fastlookup, CTLFLAG_RW, &nwfs_fastlookup, 0, ""); + + +extern int nwfs_pbuf_freecnt; + +#define DE_SIZE (sizeof(struct dirent)) +#define NWFS_RWCACHE + +static int +nwfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred) { + struct nwmount *nmp = VTONWFS(vp); + int error, count, i, len; + struct dirent dp; + struct nwnode *np = VTONW(vp); + struct nw_entry_info fattr; + struct vnode *newvp; + struct nameidata nami, *ndp = &nami; + struct componentname *cnp = &ndp->ni_cnd; + ncpfid fid; + u_char *cp; + + np = VTONW(vp); + NCPVNDEBUG("dirname='%s'\n",np->n_name); + if (uio->uio_resid < DE_SIZE || (uio->uio_offset < 0)) + return (EINVAL); + error = 0; + count = 0; + ndp->ni_dvp = vp; + i = uio->uio_offset / DE_SIZE; /* offset in directory */ + if (i == 0) { + error = ncp_initsearch(vp, uio->uio_procp, cred); + if (error) { + NCPVNDEBUG("cannot initialize search, error=%d",error); + return( error ); + } + } + + for (; uio->uio_resid >= DE_SIZE; i++) { + bzero((char *) &dp, DE_SIZE); + dp.d_reclen = DE_SIZE; + switch (i) { + case 0: /* `.' */ + case 1: /* `..' */ + dp.d_fileno = (i == 0) ? np->n_fid.f_id : np->n_parent.f_id; + if (!dp.d_fileno) dp.d_fileno = NWFS_ROOT_INO; + dp.d_namlen = i + 1; + dp.d_name[0] = '.'; + dp.d_name[1] = '.'; + dp.d_name[i + 1] = '\0'; + dp.d_type = DT_DIR; + break; + default: + error = ncp_search_for_file_or_subdir(nmp, &np->n_seq, &fattr, uio->uio_procp, cred); + if (error && error < 0x80) break; + dp.d_fileno = fattr.dirEntNum; + dp.d_type = (fattr.attributes & aDIR) ? DT_DIR : DT_REG; + dp.d_namlen = fattr.nameLen; + bcopy(fattr.entryName, dp.d_name, dp.d_namlen); + dp.d_name[dp.d_namlen] = '\0'; +#if 0 + if (error && eofflag) { + /* *eofflag = 1;*/ + break; + } +#endif + break; + } + if (nwfs_fastlookup && !error && i > 1) { + fid.f_id = fattr.dirEntNum; + fid.f_parent = np->n_fid.f_id; + error = nwfs_nget(vp->v_mount, fid, &fattr, vp, &newvp); + if (!error) { + VTONW(newvp)->n_ctime = VTONW(newvp)->n_vattr.va_ctime.tv_sec; + cnp->cn_nameptr = dp.d_name; + len = cnp->cn_namelen = dp.d_namlen; + ndp->ni_vp = newvp; + cnp->cn_hash = 0; + for (cp = cnp->cn_nameptr; len; len--, cp++) + cnp->cn_hash += *cp; + cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp); + vput(newvp); + } else + error = 0; + } + if (error >= 0x80) { + error = 0; + break; + } + if ((error = uiomove((caddr_t)&dp, DE_SIZE, uio))) + break; + } + + uio->uio_offset = i * DE_SIZE; + return (error); +} + +int +nwfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred) { + struct nwmount *nmp = VFSTONWFS(vp->v_mount); + struct nwnode *np = VTONW(vp); + struct proc *p; + struct vattr vattr; + int error, biosize; + + if (vp->v_type != VREG && vp->v_type != VDIR) { + printf("%s: vn types other than VREG or VDIR are unsupported !\n",__FUNCTION__); + return EIO; + } + if (uiop->uio_resid == 0) return 0; + if (uiop->uio_offset < 0) return EINVAL; +/* if (uiop->uio_offset + uiop->uio_resid > nmp->nm_maxfilesize) + return (EFBIG);*/ + p = uiop->uio_procp; + if (vp->v_type == VDIR) { + error = nwfs_readvdir(vp, uiop, cred); + return error; + } + biosize = NWFSTOCONN(nmp)->buffer_size; + if (np->n_flag & NMODIFIED) { + nwfs_attr_cacheremove(vp); + error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) return (error); + np->n_mtime = vattr.va_mtime.tv_sec; + } else { + error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) return (error); + if (np->n_mtime != vattr.va_mtime.tv_sec) { + error = nwfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + if (error) return (error); + np->n_mtime = vattr.va_mtime.tv_sec; + } + } + error = ncp_read(NWFSTOCONN(nmp), &np->n_fh, uiop,cred); + return (error); +} + +int +nwfs_writevnode(vp, uiop, cred, ioflag) + register struct vnode *vp; + register struct uio *uiop; + struct ucred *cred; + int ioflag; +{ + struct nwmount *nmp = VTONWFS(vp); + struct nwnode *np = VTONW(vp); + struct proc *p; +/* struct vattr vattr;*/ + int error = 0; + + if (vp->v_type != VREG) { + printf("%s: vn types other than VREG unsupported !\n",__FUNCTION__); + return EIO; + } + NCPVNDEBUG("ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); + if (uiop->uio_offset < 0) return EINVAL; +/* if (uiop->uio_offset + uiop->uio_resid > nmp->nm_maxfilesize) + return (EFBIG);*/ + p = uiop->uio_procp; + if (ioflag & (IO_APPEND | IO_SYNC)) { + if (np->n_flag & NMODIFIED) { + nwfs_attr_cacheremove(vp); + error = nwfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + if (error) return (error); + } + if (ioflag & IO_APPEND) { + /* We can relay only on local information about file size, + * because until file is closed NetWare will not return + * the correct size. */ +#if notyet + nwfs_attr_cacheremove(vp); + error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) return (error); +#endif + uiop->uio_offset = np->n_size; + } + } + if (uiop->uio_resid == 0) return 0; + if (p && uiop->uio_offset + uiop->uio_resid > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) { + psignal(p, SIGXFSZ); + return (EFBIG); + } + error = ncp_write(NWFSTOCONN(nmp), &np->n_fh, uiop, cred); + NCPVNDEBUG("after: ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); + if (!error) { + if (uiop->uio_offset > np->n_size) { + np->n_vattr.va_size = np->n_size = uiop->uio_offset; + vnode_pager_setsize(vp, np->n_size); + } + } + return (error); +} + +/* + * Do an I/O operation to/from a cache block. + */ +int +nwfs_doio(bp, cr, p) + register struct buf *bp; + struct ucred *cr; + struct proc *p; +{ + register struct uio *uiop; + register struct vnode *vp; + struct nwnode *np; + struct nwmount *nmp; + int error = 0; + struct uio uio; + struct iovec io; + + vp = bp->b_vp; + np = VTONW(vp); + nmp = VFSTONWFS(vp->v_mount); + uiop = &uio; + uiop->uio_iov = &io; + uiop->uio_iovcnt = 1; + uiop->uio_segflg = UIO_SYSSPACE; + uiop->uio_procp = p; + if (bp->b_flags & B_READ) { + io.iov_len = uiop->uio_resid = bp->b_bcount; + io.iov_base = bp->b_data; + uiop->uio_rw = UIO_READ; + switch (vp->v_type) { + case VREG: + uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE; + error = ncp_read(NWFSTOCONN(nmp), &np->n_fh, uiop, cr); + if (error) + break; + if (uiop->uio_resid) { + int left = uiop->uio_resid; + int nread = bp->b_bcount - left; + if (left > 0) + bzero((char *)bp->b_data + nread, left); + } + break; +/* case VDIR: + nfsstats.readdir_bios++; + uiop->uio_offset = ((u_quad_t)bp->b_lblkno) * NFS_DIRBLKSIZ; + if (nmp->nm_flag & NFSMNT_RDIRPLUS) { + error = nfs_readdirplusrpc(vp, uiop, cr); + if (error == NFSERR_NOTSUPP) + nmp->nm_flag &= ~NFSMNT_RDIRPLUS; + } + if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0) + error = nfs_readdirrpc(vp, uiop, cr); + if (error == 0 && uiop->uio_resid == bp->b_bcount) + bp->b_flags |= B_INVAL; + break; +*/ + default: + printf("nwfs_doio: type %x unexpected\n",vp->v_type); + break; + }; + if (error) { + bp->b_flags |= B_ERROR; + bp->b_error = error; + } + } else { /* write */ + if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size) + bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE); + + if (bp->b_dirtyend > bp->b_dirtyoff) { + io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff; + uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; + io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; + uiop->uio_rw = UIO_WRITE; + bp->b_flags |= B_WRITEINPROG; + error = ncp_write(NWFSTOCONN(nmp), &np->n_fh, uiop, cr); + bp->b_flags &= ~B_WRITEINPROG; + + /* + * For an interrupted write, the buffer is still valid + * and the write hasn't been pushed to the server yet, + * so we can't set B_ERROR and report the interruption + * by setting B_EINTR. For the B_ASYNC case, B_EINTR + * is not relevant, so the rpc attempt is essentially + * a noop. For the case of a V3 write rpc not being + * committed to stable storage, the block is still + * dirty and requires either a commit rpc or another + * write rpc with iomode == NFSV3WRITE_FILESYNC before + * the block is reused. This is indicated by setting + * the B_DELWRI and B_NEEDCOMMIT flags. + */ + if (error == EINTR + || (!error && (bp->b_flags & B_NEEDCOMMIT))) { + int s; + + s = splbio(); + bp->b_flags &= ~(B_INVAL|B_NOCACHE); + if ((bp->b_flags & B_ASYNC) == 0) + bp->b_flags |= B_EINTR; + if ((bp->b_flags & B_PAGING) == 0) { + bdirty(bp); + bp->b_flags &= ~B_DONE; + } + if ((bp->b_flags & B_ASYNC) == 0) + bp->b_flags |= B_EINTR; + splx(s); + } else { + if (error) { + bp->b_flags |= B_ERROR; + bp->b_error /*= np->n_error */= error; +/* np->n_flag |= NWRITEERR;*/ + } + bp->b_dirtyoff = bp->b_dirtyend = 0; + } + } else { + bp->b_resid = 0; + biodone(bp); + return (0); + } + } + bp->b_resid = uiop->uio_resid; + biodone(bp); + return (error); +} + +/* + * Vnode op for VM getpages. + * Wish wish .... get rid from multiple IO routines + */ +int +nwfs_getpages(ap) + struct vop_getpages_args /* { + struct vnode *a_vp; + vm_page_t *a_m; + int a_count; + int a_reqpage; + vm_ooffset_t a_offset; + } */ *ap; +{ +#ifndef NWFS_RWCACHE + return vnode_pager_generic_getpages(ap->a_vp, ap->a_m, ap->a_count, + ap->a_reqpage); +#else + int i, error, nextoff, size, toff, npages, count; + struct uio uio; + struct iovec iov; + vm_offset_t kva; + struct buf *bp; + struct vnode *vp; + struct proc *p; + struct ucred *cred; + struct nwmount *nmp; + struct nwnode *np; + vm_page_t *pages; + + vp = ap->a_vp; + p = curproc; /* XXX */ + cred = curproc->p_ucred; /* XXX */ + np = VTONW(vp); + nmp = VFSTONWFS(vp->v_mount); + pages = ap->a_m; + count = ap->a_count; + + if (vp->v_object == NULL) { + printf("nwfs_getpages: called with non-merged cache vnode??\n"); + return VM_PAGER_ERROR; + } + + bp = getpbuf(&nwfs_pbuf_freecnt); + npages = btoc(count); + kva = (vm_offset_t) bp->b_data; + pmap_qenter(kva, pages, npages); + + iov.iov_base = (caddr_t) kva; + iov.iov_len = count; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = IDX_TO_OFF(pages[0]->pindex); + uio.uio_resid = count; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_procp = p; + + error = ncp_read(NWFSTOCONN(nmp), &np->n_fh, &uio,cred); + pmap_qremove(kva, npages); + + relpbuf(bp, &nwfs_pbuf_freecnt); + + if (error && (uio.uio_resid == count)) { + printf("nwfs_getpages: error %d\n",error); + for (i = 0; i < npages; i++) { + if (ap->a_reqpage != i) + vnode_pager_freepage(pages[i]); + } + return VM_PAGER_ERROR; + } + + size = count - uio.uio_resid; + + for (i = 0, toff = 0; i < npages; i++, toff = nextoff) { + vm_page_t m; + nextoff = toff + PAGE_SIZE; + m = pages[i]; + + m->flags &= ~PG_ZERO; + + if (nextoff <= size) { + m->valid = VM_PAGE_BITS_ALL; + m->dirty = 0; + } else { + int nvalid = ((size + DEV_BSIZE - 1) - toff) & ~(DEV_BSIZE - 1); + vm_page_set_validclean(m, 0, nvalid); + } + + if (i != ap->a_reqpage) { + /* + * Whether or not to leave the page activated is up in + * the air, but we should put the page on a page queue + * somewhere (it already is in the object). Result: + * It appears that emperical results show that + * deactivating pages is best. + */ + + /* + * Just in case someone was asking for this page we + * now tell them that it is ok to use. + */ + if (!error) { + if (m->flags & PG_WANTED) + vm_page_activate(m); + else + vm_page_deactivate(m); + vm_page_wakeup(m); + } else { + vnode_pager_freepage(m); + } + } + } + return 0; +#endif /* NWFS_RWCACHE */ +} + +/* + * Vnode op for VM putpages. + * possible bug: all IO done in sync mode + * Note that vop_close always invalidate pages before close, so it's + * not necessary to open vnode. + */ +int +nwfs_putpages(ap) + struct vop_putpages_args /* { + struct vnode *a_vp; + vm_page_t *a_m; + int a_count; + int a_sync; + int *a_rtvals; + vm_ooffset_t a_offset; + } */ *ap; +{ + int error; + struct vnode *vp = ap->a_vp; + struct proc *p; + struct ucred *cred; + +#ifndef NWFS_RWCACHE + p = curproc; /* XXX */ + cred = p->p_ucred; /* XXX */ + VOP_OPEN(vp, FWRITE, cred, p); + error = vnode_pager_generic_putpages(ap->a_vp, ap->a_m, ap->a_count, + ap->a_sync, ap->a_rtvals); + VOP_CLOSE(vp, FWRITE, cred, p); + return error; +#else + struct uio uio; + struct iovec iov; + vm_offset_t kva; + struct buf *bp; + int i, npages, count; + int *rtvals; + struct nwmount *nmp; + struct nwnode *np; + vm_page_t *pages; + + p = curproc; /* XXX */ + cred = p->p_ucred; /* XXX */ +/* VOP_OPEN(vp, FWRITE, cred, p);*/ + np = VTONW(vp); + nmp = VFSTONWFS(vp->v_mount); + pages = ap->a_m; + count = ap->a_count; + rtvals = ap->a_rtvals; + npages = btoc(count); + + for (i = 0; i < npages; i++) { + rtvals[i] = VM_PAGER_AGAIN; + } + + bp = getpbuf(&nwfs_pbuf_freecnt); + kva = (vm_offset_t) bp->b_data; + pmap_qenter(kva, pages, npages); + + iov.iov_base = (caddr_t) kva; + iov.iov_len = count; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = IDX_TO_OFF(pages[0]->pindex); + uio.uio_resid = count; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_WRITE; + uio.uio_procp = p; + NCPVNDEBUG("ofs=%d,resid=%d\n",(int)uio.uio_offset, uio.uio_resid); + + error = ncp_write(NWFSTOCONN(nmp), &np->n_fh, &uio, cred); +/* VOP_CLOSE(vp, FWRITE, cred, p);*/ + NCPVNDEBUG("paged write done: %d\n", error); + + pmap_qremove(kva, npages); + relpbuf(bp, &nwfs_pbuf_freecnt); + + if (!error) { + int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE; + for (i = 0; i < nwritten; i++) { + rtvals[i] = VM_PAGER_OK; + pages[i]->dirty = 0; + } + } + return rtvals[0]; +#endif /* NWFS_RWCACHE */ +} +/* + * Flush and invalidate all dirty buffers. If another process is already + * doing the flush, just wait for completion. + */ +int +nwfs_vinvalbuf(vp, flags, cred, p, intrflg) + struct vnode *vp; + int flags; + struct ucred *cred; + struct proc *p; + int intrflg; +{ + register struct nwnode *np = VTONW(vp); +/* struct nwmount *nmp = VTONWFS(vp);*/ + int error = 0, slpflag, slptimeo; + + if (vp->v_flag & VXLOCK) { + return (0); + } + if (intrflg) { + slpflag = PCATCH; + slptimeo = 2 * hz; + } else { + slpflag = 0; + slptimeo = 0; + } + while (np->n_flag & NFLUSHINPROG) { + np->n_flag |= NFLUSHWANT; + error = tsleep((caddr_t)&np->n_flag, PRIBIO + 2, "nwfsvinv", slptimeo); + error = ncp_chkintr(NWFSTOCONN(VTONWFS(vp)), p); + if (error == EINTR && intrflg) + return EINTR; + } + np->n_flag |= NFLUSHINPROG; + error = vinvalbuf(vp, flags, cred, p, slpflag, 0); + while (error) { + if (intrflg && (error == ERESTART || error == EINTR)) { + np->n_flag &= ~NFLUSHINPROG; + if (np->n_flag & NFLUSHWANT) { + np->n_flag &= ~NFLUSHWANT; + wakeup((caddr_t)&np->n_flag); + } + return EINTR; + } + error = vinvalbuf(vp, flags, cred, p, slpflag, 0); + } + np->n_flag &= ~(NMODIFIED | NFLUSHINPROG); + if (np->n_flag & NFLUSHWANT) { + np->n_flag &= ~NFLUSHWANT; + wakeup((caddr_t)&np->n_flag); + } + return (error); +} diff --git a/sys/fs/nwfs/nwfs_ioctl.c b/sys/fs/nwfs/nwfs_ioctl.c new file mode 100644 index 0000000..6b4b510 --- /dev/null +++ b/sys/fs/nwfs/nwfs_ioctl.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/mount.h> +#include <sys/unistd.h> +#include <sys/vnode.h> +#include <sys/malloc.h> +#include <sys/dirent.h> +#include <sys/signalvar.h> +#include <sys/ioccom.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_conn.h> +#include <netncp/ncp_subr.h> + +#include <nwfs/nwfs.h> +#include <nwfs/nwfs_node.h> +#include <nwfs/nwfs_subr.h> + +int +nwfs_ioctl(ap) + struct vop_ioctl_args /* { + struct vnode *a_vp; + u_long a_command; + caddr_t a_data; + int fflag; + struct ucred *cred; + struct proc *p; + } */ *ap; +{ + int error; + struct proc *p = ap->a_p; + struct ucred *cred = ap->a_cred; + struct vnode *vp = ap->a_vp; + struct nwnode *np = VTONW(vp); + struct nwmount *nmp = VTONWFS(vp); + struct ncp_conn *conn = NWFSTOCONN(nmp); + struct ncp_handle *hp; + struct nw_entry_info *fap; + void *data = ap->a_data; + + switch (ap->a_command) { + case NWFSIOC_GETCONN: + error = ncp_conn_lock(conn, p, cred, NCPM_READ); + if (error) break; + error = ncp_conn_gethandle(conn, p, &hp); + ncp_conn_unlock(conn, p); + if (error) break; + *(int*)data = hp->nh_id; + break; + case NWFSIOC_GETEINFO: + if ((error = VOP_ACCESS(vp, VEXEC, cred, p))) break; + fap = data; + error = ncp_obtain_info(nmp, np->n_fid.f_id, 0, NULL, fap, + ap->a_p,ap->a_cred); + strcpy(fap->entryName, np->n_name); + fap->nameLen = np->n_nmlen; + break; + case NWFSIOC_GETNS: + if ((error = VOP_ACCESS(vp, VEXEC, cred, p))) break; + *(int*)data = nmp->name_space; + break; + default: + error = EINVAL; + } + return (error); +} diff --git a/sys/fs/nwfs/nwfs_mount.h b/sys/fs/nwfs/nwfs_mount.h new file mode 100644 index 0000000..56a3241 --- /dev/null +++ b/sys/fs/nwfs/nwfs_mount.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 _NWFS_MOUNT_H_ +#define _NWFS_MOUNT_H_ + +#ifndef _NCP_NCP_NLS_H_ +#include <netncp/ncp_nls.h> +#endif + +#define NWFS_VERMAJ 1 +#define NWFS_VERMIN 3200 +#define NWFS_VERSION (NWFS_VERMAJ*100000 + NWFS_VERMIN) + +/* Values for flags */ +#define NWFS_MOUNT_SOFT 0x0001 +#define WNFS_MOUNT_INTR 0x0002 +#define NWFS_MOUNT_STRONG 0x0004 +#define NWFS_MOUNT_NO_OS2 0x0008 +#define NWFS_MOUNT_NO_NFS 0x0010 +#define NWFS_MOUNT_NO_LONG 0x0020 +#define NWFS_MOUNT_GET_SYSENT 0x0040 /* special case, look to vfsops :) */ +#define NWFS_MOUNT_HAVE_NLS 0x0080 + + +/* Layout of the mount control block for a netware file system. */ +struct nwfs_args { + int connRef; /* connection reference */ + char mount_point[MAXPATHLEN]; + u_int flags; + u_char mounted_vol[NCP_VOLNAME_LEN + 1]; + u_char root_path[512+1]; + int version; + uid_t uid; + gid_t gid; + mode_t file_mode; + mode_t dir_mode; + struct ncp_nlstables nls; +}; + +#ifdef KERNEL + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_NWFSMNT); +#endif + +#endif /* KERNEL */ +#endif /* !_NWFS_MOUNT_H_ */ diff --git a/sys/fs/nwfs/nwfs_node.c b/sys/fs/nwfs/nwfs_node.c new file mode 100644 index 0000000..55e19b7 --- /dev/null +++ b/sys/fs/nwfs/nwfs_node.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/time.h> +#include <sys/proc.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/vnode.h> +#include <sys/malloc.h> +#include <sys/sysctl.h> +#include <vm/vm.h> +#include <vm/vm_extern.h> +#include <vm/vm_prot.h> +#include <vm/vm_page.h> +#include <vm/vm_object.h> +#include <vm/vm_pager.h> +#include <vm/vnode_pager.h> +#include <vm/vm_zone.h> +#include <sys/queue.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_conn.h> +#include <netncp/ncp_subr.h> + +#include <nwfs/nwfs.h> +#include <nwfs/nwfs_mount.h> +#include <nwfs/nwfs_node.h> +#include <nwfs/nwfs_subr.h> + +#define NWNOHASH(fhsum) (&nwhashtbl[(fhsum.f_id) & nwnodehash]) + +extern vop_t **nwfs_vnodeop_p; + +static LIST_HEAD(nwnode_hash_head,nwnode) *nwhashtbl; +static u_long nwnodehash; +static int nwhashlock = 0; + +MALLOC_DEFINE(M_NWNODE, "NWFS node", "NWFS vnode private part"); +MALLOC_DEFINE(M_NWFSHASH, "NWFS hash", "NWFS has table"); + +static int nwfs_sysctl_vnprint SYSCTL_HANDLER_ARGS; + +extern struct linker_set sysctl_vfs_nwfs; + +SYSCTL_DECL(_vfs_nwfs); + +SYSCTL_PROC(_vfs_nwfs, OID_AUTO, vnprint, CTLFLAG_WR|CTLTYPE_OPAQUE, + NULL, 0, nwfs_sysctl_vnprint, "S,vnlist", "vnode hash"); + +void +nwfs_hash_init(void) { + nwhashtbl = hashinit(desiredvnodes, M_NWFSHASH, &nwnodehash); +} + +void +nwfs_hash_free(void) { + free(nwhashtbl, M_NWFSHASH); +} + +int +nwfs_sysctl_vnprint SYSCTL_HANDLER_ARGS { + struct nwnode *np; + struct nwnode_hash_head *nhpp; + struct vnode *vp; + int i; + + if (nwfs_debuglevel == 0) + return 0; + printf("Name:uc:hc:fid:pfid\n"); + for(i = 0; i <= nwnodehash; i++) { + nhpp = &nwhashtbl[i]; + for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) { + vp = NWTOV(np); + printf("%s:%d:%d:%d:%d\n",np->n_name,vp->v_usecount,vp->v_holdcnt, + np->n_fid.f_id, np->n_fid.f_parent); + } + } + return 0; +} + +/* + * Allocate new nwfsnode/vnode from given nwnode. + * Vnode referenced and not locked. + */ +int +nwfs_allocvp(struct mount *mp, ncpfid fid, struct vnode **vpp) { + struct proc *p = curproc; /* XXX */ + struct nwnode *np, *np2; + struct nwnode_hash_head *nhpp; + struct vnode *vp; + int error; + +retry: + nhpp = NWNOHASH(fid); +loop: + for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) { + vp = NWTOV(np); + if (mp != vp->v_mount || !NWCMPF(&fid, &np->n_fid)) + continue; + if (vget(vp, LK_EXCLUSIVE, p)) + goto loop; + *vpp = vp; + return(0); + } + + /* lock list, or waiting in malloc can cause problems */ + if (nwhashlock) { + while(nwhashlock) { + nwhashlock = -1; + tsleep((caddr_t) &nwhashlock, PVM, "nwfsvp", 0); + } + goto loop; + } + nwhashlock = 1; + /* + * Do the MALLOC before the getnewvnode since doing so afterward + * might cause a bogus v_data pointer to get dereferenced + * elsewhere if MALLOC should block. + */ + MALLOC(np, struct nwnode *, sizeof *np, M_NWNODE, M_WAITOK); + error = getnewvnode(VT_NWFS, mp, nwfs_vnodeop_p, &vp); + if (error) { + if (nwhashlock < 0) + wakeup(&nwhashlock); + nwhashlock = 0; + *vpp = 0; + FREE(np, M_NWNODE); + return (error); + } + *vpp = vp; + bzero(np,sizeof(*np)); + vp->v_data = np; + np->n_vnode = vp; + np->n_mount = VFSTONWFS(mp); + np->n_fid = fid; + for (np2 = nhpp->lh_first; np2 != 0; np2 = np->n_hash.le_next) { + if (mp != NWTOV(np2)->v_mount || !NWCMPF(&fid, &np2->n_fid)) + continue; + vrele(vp); + FREE(np, M_NWNODE); + if (nwhashlock < 0) + wakeup(&nwhashlock); + nwhashlock = 0; + goto retry; + } + LIST_INSERT_HEAD(nhpp, np, n_hash); + if (nwhashlock < 0) + wakeup(&nwhashlock); + nwhashlock = 0; + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + np->n_flag |= NNEW; + return (error); +} + +int +nwfs_lookupnp(struct nwmount *nmp, ncpfid fid, struct nwnode **npp) { + struct nwnode *np; + struct nwnode_hash_head *nhpp; + + nhpp = NWNOHASH(fid); + for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) { + if (nmp != np->n_mount || !NWCMPF(&fid, &np->n_fid)) + continue; + *npp = np; + return(0); + } + return ENOENT; +} + +/* + * Free nwnode, and give vnode back to system + */ +int +nwfs_reclaim(ap) + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct vnode *dvp = NULL, *vp = ap->a_vp; + struct nwnode *dnp, *np = VTONW(vp); + struct nwmount *nmp=VTONWFS(vp); + + NCPVNDEBUG("%s,%d\n", np->n_name, vp->v_usecount); + if (np->n_refparent) { + np->n_refparent = 0; + if (nwfs_lookupnp(nmp, np->n_parent, &dnp) == 0) { + dvp = dnp->n_vnode; + } else { + NCPVNDEBUG("%s: has no parent ?\n",np->n_name); + } + } + LIST_REMOVE(np, n_hash); + cache_purge(vp); + if (nmp->n_root == np) { + nmp->n_root = NULL; + } + vp->v_data = NULL; + FREE(np, M_NWNODE); + if (dvp) { + vrele(dvp); + } + return (0); +} + +int +nwfs_inactive(ap) + struct vop_inactive_args /* { + struct vnode *a_vp; + struct proc *a_p; + } */ *ap; +{ + struct proc *p = ap->a_p; + struct ucred *cred = p->p_ucred; + struct vnode *vp = ap->a_vp; + struct nwnode *np = VTONW(vp); + int error; + + NCPVNDEBUG("%s: %d\n", VTONW(vp)->n_name, vp->v_usecount); + if (np->opened) { + error = nwfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + error = ncp_close_file(NWFSTOCONN(VTONWFS(vp)), &np->n_fh, p, cred); + np->opened = 0; + } + VOP_UNLOCK(vp, 0, p); + return (0); +} +/* + * routines to maintain vnode attributes cache + * nwfs_attr_cacheenter: unpack np.i to va structure + */ +void +nwfs_attr_cacheenter(struct vnode *vp, struct nw_entry_info *fi) { + struct nwnode *np = VTONW(vp); + struct nwmount *nmp = VTONWFS(vp); + register struct vattr *va = &np->n_vattr; + + va->va_type = vp->v_type; /* vnode type (for create) */ + if (vp->v_type == VREG) { + if (va->va_size != fi->dataStreamSize) { + va->va_size = fi->dataStreamSize; + vnode_pager_setsize(vp, va->va_size); + } + va->va_mode = nmp->m.file_mode; /* files access mode and type */ + } else if (vp->v_type == VDIR) { + va->va_size = 16384; /* should be a better way ... */ + va->va_mode = nmp->m.dir_mode; /* files access mode and type */ + } else + return; + np->n_size = va->va_size; + va->va_nlink = 1; /* number of references to file */ + va->va_uid = nmp->m.uid; /* owner user id */ + va->va_gid = nmp->m.gid; /* owner group id */ + va->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; + va->va_fileid = np->n_fid.f_id; /* file id */ + if (va->va_fileid == 0) + va->va_fileid = NWFS_ROOT_INO; + va->va_blocksize=nmp->connh->nh_conn->buffer_size;/* blocksize preferred for i/o */ + /* time of last modification */ + ncp_dos2unixtime(fi->modifyDate, fi->modifyTime, 0, &va->va_mtime); + /* time of last access */ + ncp_dos2unixtime(fi->lastAccessDate, 0, 0, &va->va_atime); + va->va_ctime = va->va_mtime; /* time file changed */ + va->va_gen = VNOVAL; /* generation number of file */ + va->va_flags = 0; /* flags defined for file */ + va->va_rdev = VNOVAL; /* device the special file represents */ + va->va_bytes = va->va_size; /* bytes of disk space held by file */ + va->va_filerev = 0; /* file modification number */ + va->va_vaflags = 0; /* operations flags */ + np->n_vattr = *va; + if (np->n_mtime == 0) { + np->n_mtime = va->va_mtime.tv_sec; + } + np->n_atime = time_second; + return; +} + +int +nwfs_attr_cachelookup(struct vnode *vp, struct vattr *va) { + struct nwnode *np = VTONW(vp); + int diff; + + diff = time_second - np->n_atime; + if (diff > 2) { /* XXX should be configurable */ + return ENOENT; + } + *va = np->n_vattr; + return 0; +} diff --git a/sys/fs/nwfs/nwfs_node.h b/sys/fs/nwfs/nwfs_node.h new file mode 100644 index 0000000..c88c4b4 --- /dev/null +++ b/sys/fs/nwfs/nwfs_node.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 _NWFS_NODE_H_ +#define _NWFS_NODE_H_ + +#define NWFS_ROOT_INO 0x7ffffffd +#define NWFS_ROOTVOL "#.ROOT" + +/* Bits for nwnode.n_flag */ +#define NFLUSHINPROG 0x0001 +#define NFLUSHWANT 0x0002 /* they should gone ... */ +#define NMODIFIED 0x0004 /* bogus, until async IO implemented */ +#define NNEW 0x0008 /* vnode has been allocated */ +#define NVOLUME 0x0010 /* vnode references a volume */ + +#ifndef VT_NWFS +#define VT_NWFS VT_TFS +#endif + +struct nwnode { + LIST_ENTRY(nwnode) n_hash; + struct vnode *n_vnode; + struct vattr n_vattr; + struct nwmount *n_mount; + time_t n_atime; /* attributes cache time*/ + time_t n_ctime; + time_t n_mtime; + int n_flag; + ncpfid n_parent; + ncpfid n_fid; + int n_refparent; + u_long n_attr; /* LH */ + u_long n_size; + u_long n_dosfid; + int opened; +/* int access;*/ + u_long n_origfh; + ncp_fh n_fh; + struct nw_search_seq n_seq; + u_char n_nmlen; + u_char n_name[256]; +}; + +#define VTONW(vp) ((struct nwnode *)(vp)->v_data) +#define NWTOV(np) ((struct vnode *)(np)->n_vnode) +#define NWCMPF(f1,f2) ((f1)->f_parent == (f2)->f_parent && \ + (f1)->f_id == (f2)->f_id) +#define NWCMPN(np1,np2) NWCMPF(&(np1)->n_fid, &(np2)->n_fid) +#define NWCMPV(vp1,vp2) NWCMPN(VTONW(vp1),VTONW(vp2)) + +void nwfs_hash_init(void); +void nwfs_hash_free(void); +int nwfs_allocvp(struct mount *mp, ncpfid fid, struct vnode **vpp); +int nwfs_lookupnp(struct nwmount *nmp, ncpfid fid, struct nwnode **npp); +int nwfs_inactive __P((struct vop_inactive_args *)); +int nwfs_reclaim __P((struct vop_reclaim_args *)); +int nwfs_nget(struct mount *mp, ncpfid fid, struct nw_entry_info *fap, + struct vnode *dvp, struct vnode **vpp); + +int nwfs_getpages __P((struct vop_getpages_args *)); +int nwfs_putpages __P((struct vop_putpages_args *)); +int nwfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred); +int nwfs_writevnode(struct vnode *vp, struct uio *uiop, struct ucred *cred, int ioflag); +void nwfs_attr_cacheenter(struct vnode *vp, struct nw_entry_info *fi); +int nwfs_attr_cachelookup(struct vnode *vp,struct vattr *va); + +#define nwfs_attr_cacheremove(vp) VTONW(vp)->n_atime = 0 + +#endif /* _NWFS_NODE_H_ */ diff --git a/sys/fs/nwfs/nwfs_subr.c b/sys/fs/nwfs/nwfs_subr.c new file mode 100644 index 0000000..d57d675 --- /dev/null +++ b/sys/fs/nwfs/nwfs_subr.c @@ -0,0 +1,669 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <machine/clock.h> +#include <sys/time.h> +#include <sys/namei.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_conn.h> +#include <netncp/ncp_ncp.h> +#include <netncp/ncp_subr.h> +#include <netncp/ncp_user.h> +#include <netncp/ncp_rq.h> +#include <netncp/nwerror.h> + +#include <nwfs/nwfs.h> +#include <nwfs/nwfs_node.h> +#include <nwfs/nwfs_subr.h> + +MALLOC_DEFINE(M_NWFSDATA, "NWFS data", "NWFS private data"); + +static void +ncp_extract_file_info(struct nwmount *nmp, struct ncp_rq *rqp, struct nw_entry_info *target) { + u_char name_len; + const int info_struct_size = sizeof(struct nw_entry_info) - 257; + + ncp_rp_mem(rqp,(caddr_t)target,info_struct_size); + name_len = ncp_rp_byte(rqp); + target->nameLen = name_len; + ncp_rp_mem(rqp,(caddr_t)target->entryName, name_len); + target->entryName[name_len] = '\0'; + ncp_path2unix(target->entryName, target->entryName, name_len, &nmp->m.nls); + return; +} + +static void +ncp_update_file_info(struct nwmount *nmp, struct ncp_rq *rqp, + struct nw_entry_info *target) +{ + int info_struct_size = sizeof(struct nw_entry_info) - 257; + + ncp_rp_mem(rqp,(caddr_t)target,info_struct_size); + return; +} + +int +ncp_initsearch(struct vnode *dvp,struct proc *p,struct ucred *cred) +{ + struct nwmount *nmp = VTONWFS(dvp); + struct ncp_conn *conn = NWFSTOCONN(nmp); + struct nwnode *np = VTONW(dvp); + u_int8_t volnum = nmp->n_volume; + u_int32_t dirent = np->n_fid.f_id; + int error; + DECLARE_RQ; + + NCPNDEBUG("vol=%d,dir=%d\n", volnum, dirent); + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 2); /* subfunction */ + ncp_rq_byte(rqp, nmp->name_space); + ncp_rq_byte(rqp, 0); /* reserved */ + ncp_rq_dbase_path(rqp, volnum, dirent, 0, NULL, NULL); + checkbad(ncp_request(conn,rqp)); + ncp_rp_mem(rqp,(caddr_t)&np->n_seq, sizeof(np->n_seq)); + NCP_RQ_EXIT; + return error; +} + +int +ncp_search_for_file_or_subdir(struct nwmount *nmp, + struct nw_search_seq *seq, + struct nw_entry_info *target, + struct proc *p,struct ucred *cred) +{ + struct ncp_conn *conn = NWFSTOCONN(nmp); + int error; + DECLARE_RQ; + + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 3); /* subfunction */ + ncp_rq_byte(rqp, nmp->name_space); + ncp_rq_byte(rqp, 0); /* data stream */ + ncp_rq_word_lh(rqp, 0xffff); /* Search attribs */ + ncp_rq_dword(rqp, IM_ALL); /* return info mask */ + ncp_rq_mem(rqp, (caddr_t)seq, 9); + ncp_rq_byte(rqp, 2); /* 2 byte pattern */ + ncp_rq_byte(rqp, 0xff); /* following is a wildcard */ + ncp_rq_byte(rqp, '*'); + checkbad(ncp_request(conn,rqp)); + ncp_rp_mem(rqp,(caddr_t)seq, sizeof(*seq)); + ncp_rp_byte(rqp); /* skip */ + ncp_extract_file_info(nmp, rqp, target); + NCP_RQ_EXIT; + return error; +} + +/* + * Returns information for a (one-component) name relative to the specified + * directory. + */ +int +ncp_obtain_info(struct nwmount *nmp, u_int32_t dirent, + int namelen, char *path, struct nw_entry_info *target, + struct proc *p,struct ucred *cred) +{ + struct ncp_conn *conn=NWFSTOCONN(nmp); + int error; + u_char volnum = nmp->n_volume, ns; + DECLARE_RQ; + + if (target == NULL) { + NCPFATAL("target == NULL\n"); + return EINVAL; + } + ns = (path == NULL || path[0] == 0) ? NW_NS_DOS : nmp->name_space; + NCP_RQ_HEAD(87, p, cred); + ncp_rq_byte(rqp, 6); /* subfunction */ + ncp_rq_byte(rqp, ns); + ncp_rq_byte(rqp, ns); /* DestNameSpace */ + ncp_rq_word(rqp, htons(0xff00)); /* get all */ + ncp_rq_dword(rqp, IM_ALL); + ncp_rq_dbase_path(rqp, volnum, dirent, namelen, path, &nmp->m.nls); + checkbad(ncp_request(conn,rqp)); + if (path) + ncp_extract_file_info(nmp, rqp, target); + else + ncp_update_file_info(nmp, rqp, target); + NCP_RQ_EXIT; + return error; +} +/* + * lookup name pointed by cnp in directory dvp and return file info in np. + * May be I should create a little cache, but another way is to minimize + * number of calls, on other hand, in multiprocess environment ... + */ +int +ncp_lookup(struct vnode *dvp, int len, char *name, struct nw_entry_info *fap, + struct proc *p,struct ucred *cred) +{ + struct nwmount *nmp; + struct nwnode *dnp = VTONW(dvp); + struct ncp_conn *conn; + int error; + + if (!dvp || dvp->v_type != VDIR) { + nwfs_printf("dvp is NULL or not a directory.\n"); + return (ENOENT); + } + nmp = VTONWFS(dvp); + conn = NWFSTOCONN(nmp); + + if (len == 1 && name[0] == '.') { + if (strcmp(dnp->n_name, NWFS_ROOTVOL) == 0) { + error = ncp_obtain_info(nmp, dnp->n_fid.f_id, 0, NULL, + fap, p, cred); + } else { + error = ncp_obtain_info(nmp, dnp->n_fid.f_parent, + dnp->n_nmlen, dnp->n_name, fap, p, cred); + } + return error; + } else if (len == 2 && name[0] == '.' && name[1] == '.') { + printf("%s: knows NOTHING about '..'\n", __FUNCTION__); + return EIO; + } else { + error = ncp_obtain_info(nmp, dnp->n_fid.f_id, + len, name, fap, p, cred); + } + return error; +} + +static void ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh); +static void +ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh) { + fh->val1 = (fh->val.val32 = sfd); + return; +} + +/* + * If both dir and name are NULL, then in target there's already a looked-up + * entry that wants to be opened. + */ +int +ncp_open_create_file_or_subdir(struct nwmount *nmp,struct vnode *dvp,int namelen, + char *name, int open_create_mode, u_int32_t create_attributes, + int desired_acc_rights, struct ncp_open_info *nop, + struct proc *p,struct ucred *cred) +{ + + struct ncp_conn *conn=NWFSTOCONN(nmp); + u_int16_t search_attribs = SA_ALL & (~SA_SUBDIR_FILES); + u_int8_t volnum; + u_int32_t dirent; + int error; + DECLARE_RQ; + + volnum = nmp->n_volume; + dirent = VTONW(dvp)->n_fid.f_id; + if ((create_attributes & aDIR) != 0) { + search_attribs |= SA_SUBDIR_FILES; + } + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 1);/* subfunction */ + ncp_rq_byte(rqp, nmp->name_space); + ncp_rq_byte(rqp, open_create_mode); + ncp_rq_word(rqp, search_attribs); + ncp_rq_dword(rqp, IM_ALL); + ncp_rq_dword(rqp, create_attributes); + /* + * The desired acc rights seem to be the inherited rights mask for + * directories + */ + ncp_rq_word(rqp, desired_acc_rights); + ncp_rq_dbase_path(rqp, volnum, dirent, namelen, name, &nmp->m.nls); + checkbad(ncp_request(conn,rqp)); + + nop->origfh = ncp_rp_dword_lh(rqp); + nop->action = ncp_rp_byte(rqp); + ncp_rp_byte(rqp); /* skip */ + ncp_extract_file_info(nmp, rqp, &nop->fattr); + ConvertToNWfromDWORD(nop->origfh, &nop->fh); + NCP_RQ_EXIT; + switch(error) { + case NWE_FILE_NO_CREATE_PRIV: + error = EACCES; + break; + } + return error; +} + +int +ncp_close_file(struct ncp_conn *conn, ncp_fh *fh,struct proc *p,struct ucred *cred) { + int error; + DECLARE_RQ; + + NCP_RQ_HEAD(66,p,cred); + ncp_rq_byte(rqp, 0); + ncp_rq_mem(rqp, (caddr_t)fh, 6); + error = ncp_request(conn,rqp); + NCP_RQ_EXIT_NB; + return error; +} + +int +ncp_DeleteNSEntry(struct nwmount *nmp, u_int32_t dirent, + int namelen,char *name,struct proc *p,struct ucred *cred) +{ + int error; + struct ncp_conn *conn=NWFSTOCONN(nmp); + DECLARE_RQ; + + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 8); /* subfunction */ + ncp_rq_byte(rqp, nmp->name_space); + ncp_rq_byte(rqp, 0); /* reserved */ + ncp_rq_word(rqp, SA_ALL); /* search attribs: all */ + ncp_rq_dbase_path(rqp, nmp->n_volume, dirent, namelen, name, &nmp->m.nls); + error = ncp_request(conn,rqp); + NCP_RQ_EXIT_NB; + return error; +} + +int +ncp_nsrename(struct ncp_conn *conn, int volume, int ns, int oldtype, + struct ncp_nlstables *nt, + nwdirent fdir, char *old_name, int oldlen, + nwdirent tdir, char *new_name, int newlen, + struct proc *p, struct ucred *cred) +{ + DECLARE_RQ; + int error; + + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 4); + ncp_rq_byte(rqp, ns); + ncp_rq_byte(rqp, 1); + ncp_rq_word(rqp, oldtype); + /* source Handle Path */ + ncp_rq_byte(rqp, volume); + ncp_rq_dword(rqp, fdir); + ncp_rq_byte(rqp, 1); + ncp_rq_byte(rqp, 1); /* 1 source component */ + /* dest Handle Path */ + ncp_rq_byte(rqp, volume); + ncp_rq_dword(rqp, tdir); + ncp_rq_byte(rqp, 1); + ncp_rq_byte(rqp, 1); /* 1 destination component */ + ncp_rq_pathstring(rqp, oldlen, old_name, nt); + ncp_rq_pathstring(rqp, newlen, new_name, nt); + error = ncp_request(conn,rqp); + NCP_RQ_EXIT_NB; + return error; +} + +int +ncp_modify_file_or_subdir_dos_info(struct nwmount *nmp, struct vnode *vp, + u_int32_t info_mask, + struct nw_modify_dos_info *info, + struct proc *p,struct ucred *cred) +{ + struct nwnode *np=VTONW(vp); + u_int8_t volnum = nmp->n_volume; + u_int32_t dirent = np->n_fid.f_id; + struct ncp_conn *conn=NWFSTOCONN(nmp); + int error; + DECLARE_RQ; + + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 7); /* subfunction */ + ncp_rq_byte(rqp, nmp->name_space); + ncp_rq_byte(rqp, 0); /* reserved */ + ncp_rq_word(rqp, htons(0x0680)); /* search attribs: all */ + ncp_rq_dword(rqp, info_mask); + ncp_rq_mem(rqp, (caddr_t)info, sizeof(*info)); + ncp_rq_dbase_path(rqp, volnum, dirent, 0, NULL, NULL); + error = ncp_request(conn,rqp); + NCP_RQ_EXIT_NB; + return error; +} + +int +ncp_setattr(vp, vap, cred, procp) + register struct vnode *vp; + register struct vattr *vap; + struct ucred *cred; + struct proc *procp; +{ + struct nwmount *nmp=VTONWFS(vp); + struct nwnode *np=VTONW(vp); + struct ncp_open_info nwn; + struct ncp_conn *conn=NWFSTOCONN(nmp); + struct nw_modify_dos_info info; + int error = 0, info_mask; + DECLARE_RQ; + + if (vap->va_size != VNOVAL) { + error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0, + AR_WRITE | AR_READ, &nwn,procp,cred); + if (error) return error; + NCP_RQ_HEAD(73,procp,cred); + ncp_rq_byte(rqp, 0); + ncp_rq_mem(rqp, (caddr_t)&nwn.fh, 6); + ncp_rq_dword(rqp, htonl(vap->va_size)); + ncp_rq_word_hl(rqp, 0); + checkbad(ncp_request(conn,rqp)); + np->n_vattr.va_size = np->n_size = vap->va_size; + NCP_RQ_EXIT; + ncp_close_file(conn, &nwn.fh, procp, cred); + if (error) return error; + } + info_mask = 0; + bzero(&info, sizeof(info)); + + if (vap->va_mtime.tv_sec != VNOVAL) { + info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE); + ncp_unix2dostime(&vap->va_mtime, &info.modifyDate, &info.modifyTime, NULL); + } + if (vap->va_atime.tv_sec != VNOVAL) { + info_mask |= (DM_LAST_ACCESS_DATE); + ncp_unix2dostime(&vap->va_atime, &info.lastAccessDate,NULL,NULL); + } + if (info_mask) { + error = ncp_modify_file_or_subdir_dos_info(nmp, vp, info_mask, &info,procp,cred); + } + return (error); +} + +int +ncp_get_volume_info_with_number(struct ncp_conn *conn, + int n, struct ncp_volume_info *target, + struct proc *p,struct ucred *cred) { + int error,len; + DECLARE_RQ; + + NCP_RQ_HEAD_S(22,44,p,cred); + ncp_rq_byte(rqp,n); + checkbad(ncp_request(conn,rqp)); + target->total_blocks = ncp_rp_dword_lh(rqp); + target->free_blocks = ncp_rp_dword_lh(rqp); + target->purgeable_blocks = ncp_rp_dword_lh(rqp); + target->not_yet_purgeable_blocks = ncp_rp_dword_lh(rqp); + target->total_dir_entries = ncp_rp_dword_lh(rqp); + target->available_dir_entries = ncp_rp_dword_lh(rqp); + ncp_rp_dword_lh(rqp); + target->sectors_per_block = ncp_rp_byte(rqp); + bzero(&target->volume_name, sizeof(target->volume_name)); + len = ncp_rp_byte(rqp); + if (len > NCP_VOLNAME_LEN) { + error = ENAMETOOLONG; + } else { + ncp_rp_mem(rqp,(caddr_t)&target->volume_name, len); + } + NCP_RQ_EXIT; + return error; +} + +int +ncp_get_namespaces(struct ncp_conn *conn, u_int32_t volume, int *nsf, + struct proc *p,struct ucred *cred) { + int error; + u_int8_t ns; + u_int16_t nscnt; + DECLARE_RQ; + + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 24); /* Subfunction: Get Loaded Name Spaces */ + ncp_rq_word(rqp, 0); + ncp_rq_byte(rqp, volume); + checkbad(ncp_request(conn,rqp)); + nscnt = ncp_rp_word_lh(rqp); + *nsf = 0; + while (nscnt-- > 0) { + ns = ncp_rp_byte(rqp); + *nsf |= 1 << ns; + } + NCP_RQ_EXIT; + return error; +} + +int +ncp_lookup_volume(struct ncp_conn *conn, char *volname, + u_char *volNum, u_int32_t *dirEnt, + struct proc *p,struct ucred *cred) +{ + int error; + DECLARE_RQ; + + NCPNDEBUG("looking up vol %s\n", volname); + NCP_RQ_HEAD(87,p,cred); + ncp_rq_byte(rqp, 22); /* Subfunction: Generate dir handle */ + ncp_rq_byte(rqp, 0); /* src name space */ + ncp_rq_byte(rqp, 0); /* dst name space, always zero */ + ncp_rq_word(rqp, 0); /* dstNSIndicator */ + + ncp_rq_byte(rqp, 0); /* faked volume number */ + ncp_rq_dword(rqp, 0); /* faked dir_base */ + ncp_rq_byte(rqp, 0xff); /* Don't have a dir_base */ + ncp_rq_byte(rqp, 1); /* 1 path component */ + ncp_rq_pstring(rqp, volname); + checkbad(ncp_request(conn,rqp)); + ncp_rp_dword_lh(rqp); /* NSDirectoryBase*/ + *dirEnt = ncp_rp_dword_lh(rqp); + *volNum = ncp_rp_byte(rqp); + NCP_RQ_EXIT; + return error; +} + +/* + * Time & date conversion routines taken from msdosfs. Although leap + * year calculation is bogus, it's sufficient before 2100 :) + */ +/* + * This is the format of the contents of the deTime field in the direntry + * structure. + * We don't use bitfields because we don't know how compilers for + * arbitrary machines will lay them out. + */ +#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ +#define DT_2SECONDS_SHIFT 0 +#define DT_MINUTES_MASK 0x7E0 /* minutes */ +#define DT_MINUTES_SHIFT 5 +#define DT_HOURS_MASK 0xF800 /* hours */ +#define DT_HOURS_SHIFT 11 + +/* + * This is the format of the contents of the deDate field in the direntry + * structure. + */ +#define DD_DAY_MASK 0x1F /* day of month */ +#define DD_DAY_SHIFT 0 +#define DD_MONTH_MASK 0x1E0 /* month */ +#define DD_MONTH_SHIFT 5 +#define DD_YEAR_MASK 0xFE00 /* year - 1980 */ +#define DD_YEAR_SHIFT 9 +/* + * Total number of days that have passed for each month in a regular year. + */ +static u_short regyear[] = { + 31, 59, 90, 120, 151, 181, + 212, 243, 273, 304, 334, 365 +}; + +/* + * Total number of days that have passed for each month in a leap year. + */ +static u_short leapyear[] = { + 31, 60, 91, 121, 152, 182, + 213, 244, 274, 305, 335, 366 +}; + +/* + * Variables used to remember parts of the last time conversion. Maybe we + * can avoid a full conversion. + */ +static u_long lasttime; +static u_long lastday; +static u_short lastddate; +static u_short lastdtime; +/* + * Convert the unix version of time to dos's idea of time to be used in + * file timestamps. The passed in unix time is assumed to be in GMT. + */ +void +ncp_unix2dostime(tsp, ddp, dtp, dhp) + struct timespec *tsp; + u_int16_t *ddp; + u_int16_t *dtp; + u_int8_t *dhp; +{ + u_long t; + u_long days; + u_long inc; + u_long year; + u_long month; + u_short *months; + + /* + * If the time from the last conversion is the same as now, then + * skip the computations and use the saved result. + */ + t = tsp->tv_sec - (tz.tz_minuteswest * 60) + - (wall_cmos_clock ? adjkerntz : 0); + /* - daylight savings time correction */ + t &= ~1; + if (lasttime != t) { + lasttime = t; + lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) + + (((t / 60) % 60) << DT_MINUTES_SHIFT) + + (((t / 3600) % 24) << DT_HOURS_SHIFT); + + /* + * If the number of days since 1970 is the same as the last + * time we did the computation then skip all this leap year + * and month stuff. + */ + days = t / (24 * 60 * 60); + if (days != lastday) { + lastday = days; + for (year = 1970;; year++) { + inc = year & 0x03 ? 365 : 366; + if (days < inc) + break; + days -= inc; + } + months = year & 0x03 ? regyear : leapyear; + for (month = 0; days >= months[month]; month++) + ; + if (month > 0) + days -= months[month - 1]; + lastddate = ((days + 1) << DD_DAY_SHIFT) + + ((month + 1) << DD_MONTH_SHIFT); + /* + * Remember dos's idea of time is relative to 1980. + * unix's is relative to 1970. If somehow we get a + * time before 1980 then don't give totally crazy + * results. + */ + if (year > 1980) + lastddate += (year - 1980) << DD_YEAR_SHIFT; + } + } + if (dtp) + *dtp = lastdtime; + if (dhp) + *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; + + *ddp = lastddate; +} + +/* + * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that + * interval there were 8 regular years and 2 leap years. + */ +#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) + +static u_short lastdosdate; +static u_long lastseconds; + +/* + * Convert from dos' idea of time to unix'. This will probably only be + * called from the stat(), and fstat() system calls and so probably need + * not be too efficient. + */ +void +ncp_dos2unixtime(dd, dt, dh, tsp) + u_int dd; + u_int dt; + u_int dh; + struct timespec *tsp; +{ + u_long seconds; + u_long month; + u_long year; + u_long days; + u_short *months; + + if (dd == 0) { + /* + * Uninitialized field, return the epoch. + */ + tsp->tv_sec = 0; + tsp->tv_nsec = 0; + return; + } + seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) + + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 + + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 + + dh / 100; + /* + * If the year, month, and day from the last conversion are the + * same then use the saved value. + */ + if (lastdosdate != dd) { + lastdosdate = dd; + days = 0; + year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; + days = year * 365; + days += year / 4 + 1; /* add in leap days */ + if ((year & 0x03) == 0) + days--; /* if year is a leap year */ + months = year & 0x03 ? regyear : leapyear; + month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; + if (month < 1 || month > 12) { + month = 1; + } + if (month > 1) + days += months[month - 2]; + days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; + lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; + } + tsp->tv_sec = seconds + lastseconds + (tz.tz_minuteswest * 60) + + adjkerntz; + /* + daylight savings time correction */ + tsp->tv_nsec = (dh % 100) * 10000000; +} diff --git a/sys/fs/nwfs/nwfs_subr.h b/sys/fs/nwfs/nwfs_subr.h new file mode 100644 index 0000000..6112933 --- /dev/null +++ b/sys/fs/nwfs/nwfs_subr.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 _NWFS_SUBR_H_ +#define _NWFS_SUBR_H_ + +extern int nwfs_debuglevel; + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_NWFSDATA); +#endif + +int ncp_initsearch(struct vnode *dvp,struct proc *p,struct ucred *cred); +int ncp_search_for_file_or_subdir(struct nwmount *nmp,struct nw_search_seq *seq, + struct nw_entry_info *target, + struct proc *p,struct ucred *cred); +int ncp_lookup(struct vnode *dvp, int len, char *name, struct nw_entry_info *fap, + struct proc *p,struct ucred *cred); +int ncp_lookup_volume(struct ncp_conn *conn, char *volname, + u_char *volNum, u_int32_t *dirEnt, + struct proc *p,struct ucred *cred); +int ncp_close_file(struct ncp_conn *conn, ncp_fh *fh, + struct proc *p,struct ucred *cred); +int ncp_open_create_file_or_subdir(struct nwmount *nmp,struct vnode *dvp, int namelen,char *name, + int open_create_mode, u_int32_t create_attributes, + int desired_acc_rights, struct ncp_open_info *nop, + struct proc *p,struct ucred *cred); +int ncp_DeleteNSEntry(struct nwmount *nmp, + u_int32_t dirent, int namelen, char *name, + struct proc *p,struct ucred *cred); +int ncp_nsrename(struct ncp_conn *conn, int volume, int ns, int oldtype, + struct ncp_nlstables *nt, + nwdirent fdir, char *old_name, int oldlen, + nwdirent tdir, char *new_name, int newlen, + struct proc *p, struct ucred *cred); +int ncp_obtain_info(struct nwmount *nmp, u_int32_t dirent, + int namelen, char *path, struct nw_entry_info *target, + struct proc *p,struct ucred *cred); +int ncp_modify_file_or_subdir_dos_info(struct nwmount *nmp, struct vnode *vp, + u_int32_t info_mask, + struct nw_modify_dos_info *info, + struct proc *p,struct ucred *cred); +int ncp_setattr(struct vnode *,struct vattr *,struct ucred *,struct proc *); +int ncp_get_namespaces(struct ncp_conn *conn, u_int32_t volume, int *nsf, + struct proc *p,struct ucred *cred); +int ncp_get_volume_info_with_number(struct ncp_conn *conn, + int n, struct ncp_volume_info *target, + struct proc *p,struct ucred *cred); + +void ncp_unix2dostime __P((struct timespec *tsp, u_int16_t *ddp, + u_int16_t *dtp, u_int8_t *dhp)); +void ncp_dos2unixtime __P((u_int dd, u_int dt, u_int dh, struct timespec *tsp)); + +#endif /* !_NWFS_SUBR_H_ */ diff --git a/sys/fs/nwfs/nwfs_vfsops.c b/sys/fs/nwfs/nwfs_vfsops.c new file mode 100644 index 0000000..53ecf98 --- /dev/null +++ b/sys/fs/nwfs/nwfs_vfsops.c @@ -0,0 +1,515 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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_ncp.h" +#ifndef NCP +#error "NWFS requires NCP protocol" +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/sysctl.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/malloc.h> +#include <sys/buf.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_conn.h> +#include <netncp/ncp_sock.h> +#include <netncp/ncp_subr.h> +#include <netncp/ncp_ncp.h> +#include <netncp/ncp_rq.h> +#include <netncp/ncp_nls.h> + +#include <nwfs/nwfs.h> +#include <nwfs/nwfs_node.h> +#include <nwfs/nwfs_subr.h> + +int nwfs_debuglevel = 0; + +static int nwfs_version = NWFS_VERSION; + +SYSCTL_DECL(_vfs_nwfs); +SYSCTL_NODE(_vfs, OID_AUTO, nwfs, CTLFLAG_RW, 0, "Netware file system"); +SYSCTL_INT(_vfs_nwfs, OID_AUTO, version, CTLFLAG_RD, &nwfs_version, 0, ""); +SYSCTL_INT(_vfs_nwfs, OID_AUTO, debuglevel, CTLFLAG_RW, &nwfs_debuglevel, 0, ""); + +static int nwfs_mount __P((struct mount *, char *, caddr_t, + struct nameidata *, struct proc *)); +static int nwfs_quotactl __P((struct mount *, int, uid_t, caddr_t, + struct proc *)); +static int nwfs_root __P((struct mount *, struct vnode **)); +static int nwfs_start __P((struct mount *, int, struct proc *)); +static int nwfs_statfs __P((struct mount *, struct statfs *, + struct proc *)); +static int nwfs_sync __P((struct mount *, int, struct ucred *, + struct proc *)); +static int nwfs_unmount __P((struct mount *, int, struct proc *)); +static int nwfs_init __P((struct vfsconf *vfsp)); +static int nwfs_uninit __P((struct vfsconf *vfsp)); + +static struct vfsops nwfs_vfsops = { + nwfs_mount, + nwfs_start, + nwfs_unmount, + nwfs_root, + nwfs_quotactl, + nwfs_statfs, + nwfs_sync, + vfs_stdvget, + vfs_stdfhtovp, /* shouldn't happen */ + vfs_stdcheckexp, + vfs_stdvptofh, /* shouldn't happen */ + nwfs_init, + nwfs_uninit +}; + + +VFS_SET(nwfs_vfsops, nwfs, VFCF_NETWORK); + +int nwfs_pbuf_freecnt = -1; /* start out unlimited */ +static int nwfsid = 1; + +static int +nwfs_initnls(struct nwmount *nmp) { + char *pc, *pe; + int error = 0; +#define COPY_TABLE(t,d) { \ + if (t) { \ + error = copyin((t), pc, 256); \ + if (error) break; \ + } else \ + bcopy(d, pc, 256); \ + (t) = pc; pc += 256; \ + } + + nmp->m.nls.opt |= NWHP_NLS | NWHP_DOS; + if ((nmp->m.flags & NWFS_MOUNT_HAVE_NLS) == 0) { + nmp->m.nls.tolower = ncp_defnls.tolower; + nmp->m.nls.toupper = ncp_defnls.toupper; + nmp->m.nls.n2u = ncp_defnls.n2u; + nmp->m.nls.u2n = ncp_defnls.u2n; + return 0; + } + MALLOC(pe, char *, 256 * 4, M_NWFSDATA, M_WAITOK); + if (pe == NULL) return ENOMEM; + pc = pe; + do { + COPY_TABLE(nmp->m.nls.tolower, ncp_defnls.tolower); + COPY_TABLE(nmp->m.nls.toupper, ncp_defnls.toupper); + COPY_TABLE(nmp->m.nls.n2u, ncp_defnls.n2u); + COPY_TABLE(nmp->m.nls.u2n, ncp_defnls.u2n); + } while(0); + if (error) { + free(pe, M_NWFSDATA); + return error; + } + return 0; +} +/* + * mp - path - addr in user space of mount point (ie /usr or whatever) + * data - addr in user space of mount params + */ +static int nwfs_mount(struct mount *mp, char *path, caddr_t data, + struct nameidata *ndp, struct proc *p) +{ + struct nwfs_args args; /* will hold data from mount request */ + size_t size; + int error; + struct nwmount *nmp = NULL; + struct ncp_conn *conn = NULL; + struct ncp_handle *handle = NULL; + struct vnode *vp; + char *pc,*pe; + + if (data == NULL) { + nwfs_printf("missing data argument\n"); + return 1; + } + if (mp->mnt_flag & MNT_UPDATE) { + nwfs_printf("MNT_UPDATE not implemented"); + return (EOPNOTSUPP); + } + error = copyin(data, (caddr_t)&args, sizeof(struct nwfs_args)); + if (error) + return (error); + if (args.version != NWFS_VERSION) { + nwfs_printf("mount version mismatch: kernel=%d, mount=%d\n",NWFS_VERSION,args.version); + return (1); + } + error = ncp_conn_getbyref(args.connRef,p,p->p_ucred,NCPM_EXECUTE,&conn); + if (error) { + nwfs_printf("invalid connection refernce %d\n",args.connRef); + return (error); + } + error = ncp_conn_gethandle(conn, NULL, &handle); + if (error) { + nwfs_printf("can't get connection handle\n"); + return (error); + } + ncp_conn_unlock(conn,p); /* we keep the ref */ + mp->mnt_stat.f_iosize = conn->buffer_size; + /* We must malloc our own mount info */ + MALLOC(nmp,struct nwmount *,sizeof(struct nwmount),M_NWFSDATA,M_USE_RESERVE); + if (nmp == NULL) { + nwfs_printf("could not alloc nwmount\n"); + error = ENOMEM; + goto bad; + } + bzero(nmp,sizeof(*nmp)); + mp->mnt_data = (qaddr_t)nmp; + nmp->connh = handle; + nmp->n_root = NULL; + nmp->n_id = nwfsid++; + nmp->m = args; + nmp->m.file_mode = (nmp->m.file_mode & + (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; + nmp->m.dir_mode = (nmp->m.dir_mode & + (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; + if ((error = nwfs_initnls(nmp)) != 0) goto bad; + (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); + bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); + pc = mp->mnt_stat.f_mntfromname; + pe = pc+sizeof(mp->mnt_stat.f_mntfromname); + bzero(pc, MNAMELEN); + *(pc++) = '/'; + pc = index(strncpy(pc, conn->li.server, pe-pc-2),0); + if (pc < pe-1) { + *(pc++) = ':'; + pc=index(strncpy(pc, conn->li.user, pe-pc-2),0); + if (pc < pe-1) { + *(pc++) = '/'; + strncpy(pc, nmp->m.mounted_vol, pe-pc-2); + } + } + /* protect against invalid mount points */ + nmp->m.mount_point[sizeof(nmp->m.mount_point)-1] = '\0'; + vfs_getnewfsid(mp); + error = nwfs_root(mp, &vp); + if (error) + goto bad; + /* + * Lose the lock but keep the ref. + */ + VOP_UNLOCK(vp, 0, curproc); + NCPVODEBUG("rootvp.vrefcnt=%d\n",vp->v_usecount); + return error; +bad: + if (nmp) + free(nmp, M_NWFSDATA); + if (handle) + ncp_conn_puthandle(handle, NULL, 0); + return error; +} + +/* Unmount the filesystem described by mp. */ +static int +nwfs_unmount(struct mount *mp, int mntflags, struct proc *p) +{ + struct nwmount *nmp = VFSTONWFS(mp); + struct ncp_conn *conn; + struct vnode *vp; + int error, flags; + + NCPVODEBUG("nwfs_unmount: flags=%04x\n",mntflags); + flags = 0; + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + error = VFS_ROOT(mp,&vp); + if (error) return (error); + if (vp->v_usecount > 2) { + printf("nwfs_unmount: usecnt=%d\n",vp->v_usecount); + vput(vp); + return (EBUSY); + } + error = vflush(mp, vp, flags); + if (error) { + vput(vp); + return (error); + } + /* + * There are two reference counts and one lock to get rid of here. + */ + NCPVODEBUG("v_use: %d\n",vp->v_usecount); + vput(vp); + NCPVODEBUG("v_use after vput: %d\n",vp->v_usecount); + vrele(vp); + NCPVODEBUG("v_use after vrele: %d\n",vp->v_usecount); + vgone(vp); + NCPVODEBUG("v_gone finished !!!!\n"); + conn = NWFSTOCONN(nmp); + ncp_conn_puthandle(nmp->connh,NULL,0); + if (ncp_conn_lock(conn,p,p->p_ucred,NCPM_WRITE | NCPM_EXECUTE) == 0) { + if(ncp_disconnect(conn)) + ncp_conn_unlock(conn,p); + } + mp->mnt_data = (qaddr_t)0; + if (nmp->m.flags & NWFS_MOUNT_HAVE_NLS) + free(nmp->m.nls.tolower, M_NWFSDATA); + free(nmp, M_NWFSDATA); + mp->mnt_flag &= ~MNT_LOCAL; + return (error); +} + +/* Return locked vnode to root of a filesystem */ +static int +nwfs_root(struct mount *mp, struct vnode **vpp) { + struct vnode *vp; + struct nwmount *nmp; + struct nwnode *np; + struct ncp_conn *conn; + struct nw_entry_info fattr; + struct proc *p = curproc; + struct ucred *cred = p->p_ucred; + int error, nsf, opt; + u_char vol; + + nmp = VFSTONWFS(mp); + conn = NWFSTOCONN(nmp); + if (nmp->n_root) { + *vpp = NWTOV(nmp->n_root); + vget(*vpp, LK_EXCLUSIVE, curproc); + return 0; + } + error = ncp_lookup_volume(conn, nmp->m.mounted_vol, &vol, + &nmp->n_rootent.f_id, p, cred); + if (error) + return ENOENT; + nmp->n_volume = vol; + error = ncp_get_namespaces(conn, vol, &nsf, p, cred); + if (error) + return ENOENT; + if (nsf & NW_NSB_OS2) { + NCPVODEBUG("volume %s has os2 namespace\n",nmp->m.mounted_vol); + if ((nmp->m.flags & NWFS_MOUNT_NO_OS2) == 0) { + nmp->name_space = NW_NS_OS2; + nmp->m.nls.opt &= ~NWHP_DOS; + } + } + opt = nmp->m.nls.opt; + nsf = opt & (NWHP_UPPER | NWHP_LOWER); + if (opt & NWHP_DOS) { + if (nsf == (NWHP_UPPER | NWHP_LOWER)) { + nmp->m.nls.opt &= ~(NWHP_LOWER | NWHP_UPPER); + } else if (nsf == 0) { + nmp->m.nls.opt |= NWHP_LOWER; + } + } else { + if (nsf == (NWHP_UPPER | NWHP_LOWER)) { + nmp->m.nls.opt &= ~(NWHP_LOWER | NWHP_UPPER); + } + } + if (nmp->m.root_path[0]) { + nmp->m.root_path[0]--; + error = ncp_obtain_info(nmp, nmp->n_rootent.f_id, + -nmp->m.root_path[0], nmp->m.root_path, &fattr, p, cred); + if (error) { + NCPFATAL("Invalid root path specified\n"); + return ENOENT; + } + nmp->n_rootent.f_parent = fattr.dirEntNum; + nmp->m.root_path[0]++; + error = ncp_obtain_info(nmp, nmp->n_rootent.f_id, + -nmp->m.root_path[0], nmp->m.root_path, &fattr, p, cred); + if (error) { + NCPFATAL("Invalid root path specified\n"); + return ENOENT; + } + nmp->n_rootent.f_id = fattr.dirEntNum; + } else { + error = ncp_obtain_info(nmp, nmp->n_rootent.f_id, + 0, NULL, &fattr, p, cred); + if (error) { + NCPFATAL("Can't obtain volume info\n"); + return ENOENT; + } + fattr.nameLen = strlen(strcpy(fattr.entryName, NWFS_ROOTVOL)); + nmp->n_rootent.f_parent = nmp->n_rootent.f_id; + } + error = nwfs_nget(mp, nmp->n_rootent, &fattr, NULL, &vp); + if (error) + return (error); + vp->v_flag |= VROOT; + np = VTONW(vp); + if (nmp->m.root_path[0] == 0) + np->n_flag |= NVOLUME; + nmp->n_root = np; +/* error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) { + vput(vp); + NCPFATAL("Can't get root directory entry\n"); + return error; + }*/ + *vpp = vp; + return (0); +} + +/* + * Vfs start routine, a no-op. + */ +/* ARGSUSED */ +static int +nwfs_start(mp, flags, p) + struct mount *mp; + int flags; + struct proc *p; +{ + NCPVODEBUG("flags=%04x\n",flags); + return (0); +} + +/* + * Do operations associated with quotas, not supported + */ +/* ARGSUSED */ +static int +nwfs_quotactl(mp, cmd, uid, arg, p) + struct mount *mp; + int cmd; + uid_t uid; + caddr_t arg; + struct proc *p; +{ + NCPVODEBUG("return EOPNOTSUPP\n"); + return (EOPNOTSUPP); +} + +/*ARGSUSED*/ +int +nwfs_init(struct vfsconf *vfsp) +{ + + nwfs_hash_init(); + nwfs_pbuf_freecnt = nswbuf / 2 + 1; + NCPVODEBUG("always happy to load!\n"); + return (0); +} + +/*ARGSUSED*/ +int +nwfs_uninit(struct vfsconf *vfsp) +{ + + nwfs_hash_free(); + NCPVODEBUG("unloaded\n"); + return (0); +} + +/* + * nwfs_statfs call + */ +int +nwfs_statfs(mp, sbp, p) + struct mount *mp; + register struct statfs *sbp; + struct proc *p; +{ + struct nwmount *nmp = VFSTONWFS(mp); + int error = 0, secsize; + struct nwnode *np = nmp->n_root; + struct ncp_volume_info vi; + + if (np == NULL) return EINVAL; + error = ncp_get_volume_info_with_number(NWFSTOCONN(nmp), nmp->n_volume, &vi,p,p->p_ucred); + if (error) return error; + secsize = 512; /* XXX how to get real value ??? */ + sbp->f_spare2=0; /* placeholder */ + /* fundamental file system block size */ + sbp->f_bsize = vi.sectors_per_block*secsize; + /* optimal transfer block size */ + sbp->f_iosize = NWFSTOCONN(nmp)->buffer_size; + /* total data blocks in file system */ + sbp->f_blocks= vi.total_blocks; + /* free blocks in fs */ + sbp->f_bfree = vi.free_blocks + vi.purgeable_blocks; + /* free blocks avail to non-superuser */ + sbp->f_bavail= vi.free_blocks+vi.purgeable_blocks; + /* total file nodes in file system */ + sbp->f_files = vi.total_dir_entries; + /* free file nodes in fs */ + sbp->f_ffree = vi.available_dir_entries; + sbp->f_flags = 0; /* copy of mount exported flags */ + if (sbp != &mp->mnt_stat) { + sbp->f_fsid = mp->mnt_stat.f_fsid; /* file system id */ + sbp->f_owner = mp->mnt_stat.f_owner; /* user that mounted the filesystem */ + sbp->f_type = mp->mnt_vfc->vfc_typenum; /* type of filesystem */ + bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); + bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); + } + strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); + return 0; +} + +/* + * Flush out the buffer cache + */ +/* ARGSUSED */ +static int +nwfs_sync(mp, waitfor, cred, p) + struct mount *mp; + int waitfor; + struct ucred *cred; + struct proc *p; +{ + register struct vnode *vp; + int error, allerror = 0; + /* + * Force stale buffer cache information to be flushed. + */ +loop: + for (vp = mp->mnt_vnodelist.lh_first; + vp != NULL; + vp = vp->v_mntvnodes.le_next) { + /* + * If the vnode that we are about to sync is no longer + * associated with this mount point, start over. + */ + if (vp->v_mount != mp) + goto loop; + if (VOP_ISLOCKED(vp) || TAILQ_EMPTY(&vp->v_dirtyblkhd) || + waitfor == MNT_LAZY) + continue; + if (vget(vp, LK_EXCLUSIVE, p)) + goto loop; + error = VOP_FSYNC(vp, cred, waitfor, p); + if (error) + allerror = error; + vput(vp); + } + return (allerror); +} diff --git a/sys/fs/nwfs/nwfs_vnops.c b/sys/fs/nwfs/nwfs_vnops.c new file mode 100644 index 0000000..ab9f220 --- /dev/null +++ b/sys/fs/nwfs/nwfs_vnops.c @@ -0,0 +1,1128 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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 Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/resourcevar.h> /* defines plimit structure in proc struct */ +#include <sys/kernel.h> +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/mount.h> +#include <sys/unistd.h> +#include <sys/vnode.h> +#include <sys/malloc.h> +#include <sys/dirent.h> +#include <sys/signalvar.h> +#include <sys/ioccom.h> + +#include <vm/vm.h> +#include <vm/vm_extern.h> +#include <vm/vm_zone.h> +#include <vm/vnode_pager.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_conn.h> +#include <netncp/ncp_subr.h> +#include <netncp/nwerror.h> +#include <netncp/ncp_nls.h> + +#include <nwfs/nwfs.h> +#include <nwfs/nwfs_node.h> +#include <nwfs/nwfs_subr.h> + +/* + * Prototypes for NWFS vnode operations + */ +static int nwfs_create __P((struct vop_create_args *)); +static int nwfs_mknod __P((struct vop_mknod_args *)); +static int nwfs_open __P((struct vop_open_args *)); +static int nwfs_close __P((struct vop_close_args *)); +static int nwfs_access __P((struct vop_access_args *)); +static int nwfs_getattr __P((struct vop_getattr_args *)); +static int nwfs_setattr __P((struct vop_setattr_args *)); +static int nwfs_read __P((struct vop_read_args *)); +static int nwfs_write __P((struct vop_write_args *)); +static int nwfs_fsync __P((struct vop_fsync_args *)); +static int nwfs_remove __P((struct vop_remove_args *)); +static int nwfs_link __P((struct vop_link_args *)); +static int nwfs_lookup __P((struct vop_lookup_args *)); +static int nwfs_rename __P((struct vop_rename_args *)); +static int nwfs_mkdir __P((struct vop_mkdir_args *)); +static int nwfs_rmdir __P((struct vop_rmdir_args *)); +static int nwfs_symlink __P((struct vop_symlink_args *)); +static int nwfs_readdir __P((struct vop_readdir_args *)); +static int nwfs_abortop __P((struct vop_abortop_args *)); +static int nwfs_bmap __P((struct vop_bmap_args *)); +static int nwfs_strategy __P((struct vop_strategy_args *)); +static int nwfs_print __P((struct vop_print_args *)); +static int nwfs_pathconf __P((struct vop_pathconf_args *ap)); + +/* Global vfs data structures for nwfs */ +vop_t **nwfs_vnodeop_p; +static struct vnodeopv_entry_desc nwfs_vnodeop_entries[] = { + { &vop_default_desc, (vop_t *) vop_defaultop }, + { &vop_abortop_desc, (vop_t *) nwfs_abortop }, + { &vop_access_desc, (vop_t *) nwfs_access }, + { &vop_bmap_desc, (vop_t *) nwfs_bmap }, + { &vop_open_desc, (vop_t *) nwfs_open }, + { &vop_close_desc, (vop_t *) nwfs_close }, + { &vop_create_desc, (vop_t *) nwfs_create }, + { &vop_fsync_desc, (vop_t *) nwfs_fsync }, + { &vop_getattr_desc, (vop_t *) nwfs_getattr }, + { &vop_getpages_desc, (vop_t *) nwfs_getpages }, + { &vop_putpages_desc, (vop_t *) nwfs_putpages }, + { &vop_ioctl_desc, (vop_t *) nwfs_ioctl }, + { &vop_inactive_desc, (vop_t *) nwfs_inactive }, + { &vop_islocked_desc, (vop_t *) vop_noislocked }, + { &vop_link_desc, (vop_t *) nwfs_link }, + { &vop_lock_desc, (vop_t *) vop_sharedlock }, + { &vop_lookup_desc, (vop_t *) nwfs_lookup }, + { &vop_mkdir_desc, (vop_t *) nwfs_mkdir }, + { &vop_mknod_desc, (vop_t *) nwfs_mknod }, + { &vop_pathconf_desc, (vop_t *) nwfs_pathconf }, + { &vop_print_desc, (vop_t *) nwfs_print }, + { &vop_read_desc, (vop_t *) nwfs_read }, + { &vop_readdir_desc, (vop_t *) nwfs_readdir }, + { &vop_reclaim_desc, (vop_t *) nwfs_reclaim }, + { &vop_remove_desc, (vop_t *) nwfs_remove }, + { &vop_rename_desc, (vop_t *) nwfs_rename }, + { &vop_rmdir_desc, (vop_t *) nwfs_rmdir }, + { &vop_setattr_desc, (vop_t *) nwfs_setattr }, + { &vop_strategy_desc, (vop_t *) nwfs_strategy }, + { &vop_symlink_desc, (vop_t *) nwfs_symlink }, + { &vop_unlock_desc, (vop_t *) vop_nounlock }, + { &vop_write_desc, (vop_t *) nwfs_write }, + { NULL, NULL } +}; +static struct vnodeopv_desc nwfs_vnodeop_opv_desc = + { &nwfs_vnodeop_p, nwfs_vnodeop_entries }; + +VNODEOP_SET(nwfs_vnodeop_opv_desc); + +void nwfs_createname(char *_name,struct vnode *vp,struct componentname *cnp); + + +void nwfs_createname(char *_name,struct vnode *vp,struct componentname *cnp){ + strncpy(_name, cnp->cn_nameptr, cnp->cn_namelen); + _name[cnp->cn_namelen] = '\0'; + ncp_str_upper(_name); +} +/* + * nwfs_access vnode op + * for now just return ok + */ +static int +nwfs_access(ap) + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + struct ucred *cred = ap->a_cred; + u_int mode = ap->a_mode; + struct nwmount *nmp = VTONWFS(vp); + int error = 0; + + NCPVNDEBUG("\n"); + if ((ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { + switch (vp->v_type) { + case VREG: case VDIR: case VLNK: + return (EROFS); + default: + break; + } + } + if (cred->cr_uid == 0) + return 0; + if (cred->cr_uid != nmp->m.uid) { + mode >>= 3; + if (!groupmember(nmp->m.gid, cred)) + mode >>= 3; + } + error = (((vp->v_type == VREG) ? nmp->m.file_mode : nmp->m.dir_mode) & mode) == mode ? 0 : EACCES; + return error; +} +/* + * nwfs_open vnode op + */ +/* ARGSUSED */ +static int +nwfs_open(ap) + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + int mode = ap->a_mode; + struct nwnode *np = VTONW(vp); + struct ncp_open_info no; + struct nwmount *nmp = VTONWFS(vp); + struct vattr vattr; + int error, nwm; + + NCPVNDEBUG("%s,%d\n",np->n_name, np->opened); + if (vp->v_type != VREG && vp->v_type != VDIR) { + NCPFATAL("open eacces vtype=%d\n", vp->v_type); + return (EACCES); + } + if (vp->v_type == VDIR) return 0; /* nothing to do now */ + if (np->n_flag & NMODIFIED) { + if ((error = nwfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) + return (error); + np->n_atime = 0; + error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p); + if (error) return (error); + np->n_mtime = vattr.va_mtime.tv_sec; + } else { + error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p); + if (error) return (error); + if (np->n_mtime != vattr.va_mtime.tv_sec) { + if ((error = nwfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) + return (error); + np->n_mtime = vattr.va_mtime.tv_sec; + } + } + if (np->opened) { + np->opened++; + return 0; + } + nwm = AR_READ; + if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) + nwm |= AR_WRITE; + error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, + 0, nwm, &no, ap->a_p, ap->a_cred); + if (error) { + if (mode & FWRITE) + return EACCES; + nwm = AR_READ; + error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0, + nwm, &no, ap->a_p,ap->a_cred); + } + if (!error) { + np->opened++; + np->n_fh = no.fh; + np->n_origfh = no.origfh; + } + np->n_atime = 0; + return (error); +} + +static int +nwfs_close(ap) + struct vop_close_args /* { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + register struct nwnode *np = VTONW(vp); + int error; + + NCPVNDEBUG("name=%s,pid=%d,c=%d\n",np->n_name,ap->a_p->p_pid,np->opened); + + if (vp->v_type == VDIR) return 0; /* nothing to do now */ + error = 0; + simple_lock(&vp->v_interlock); + if (np->opened == 0) { + simple_unlock(&vp->v_interlock); + return 0; + } + error = nwfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); + if (--np->opened == 0) { + error = ncp_close_file(NWFSTOCONN(VTONWFS(vp)), &np->n_fh, ap->a_p,ap->a_cred); + } + simple_unlock(&vp->v_interlock); + np->n_atime = 0; + return (error); +} + +/* + * nwfs_getattr call from vfs. + */ +static int +nwfs_getattr(ap) + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + register struct nwnode *np = VTONW(vp); + struct vattr *va=ap->a_vap; + struct nwmount *nmp = VTONWFS(vp); + struct nw_entry_info fattr; + int error; + u_int32_t oldsize; + + NCPVNDEBUG("%lx:%d: '%s' %d\n", (long)vp, nmp->n_volume, np->n_name, (vp->v_flag & VROOT) != 0); + error = nwfs_attr_cachelookup(vp,va); + if (!error) return 0; + NCPVNDEBUG("not in cache\n"); + oldsize = np->n_size; + if (np->n_flag & NVOLUME) { + error = ncp_obtain_info(nmp, np->n_fid.f_id, 0, NULL, &fattr, + ap->a_p,ap->a_cred); + } else { + error = ncp_obtain_info(nmp, np->n_fid.f_parent, np->n_nmlen, + np->n_name, &fattr, ap->a_p, ap->a_cred); + } + if (error) { + NCPVNDEBUG("error %d\n", error); + return error; + } + nwfs_attr_cacheenter(vp, &fattr); + *va = np->n_vattr; + if (np->opened) + np->n_size = oldsize; + return (0); +} +/* + * nwfs_setattr call from vfs. + */ +static int +nwfs_setattr(ap) + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + register struct nwnode *np = VTONW(vp); + register struct vattr *vap = ap->a_vap; + u_quad_t tsize=0; + int error = 0; + + NCPVNDEBUG("\n"); + if (vap->va_flags != VNOVAL) + return (EOPNOTSUPP); + /* + * Disallow write attempts if the filesystem is mounted read-only. + */ + if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || + vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || + vap->va_mode != (mode_t)VNOVAL) &&(vp->v_mount->mnt_flag & MNT_RDONLY)) + return (EROFS); + if (vap->va_size != VNOVAL) { + switch (vp->v_type) { + case VDIR: + return (EISDIR); + case VREG: + /* + * Disallow write attempts if the filesystem is + * mounted read-only. + */ + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + vnode_pager_setsize(vp, (u_long)vap->va_size); + tsize = np->n_size; + np->n_size = vap->va_size; + break; + default: + return EINVAL; + }; + } + error = ncp_setattr(vp, vap, ap->a_cred, ap->a_p); + if (error && vap->va_size != VNOVAL) { + np->n_size = tsize; + vnode_pager_setsize(vp, (u_long)tsize); + } + np->n_atime = 0; /* invalidate cache */ + VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); + np->n_mtime = vap->va_mtime.tv_sec; + return (0); +} +/* + * nwfs_read call. + */ +static int +nwfs_read(ap) + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + struct uio *uio=ap->a_uio; + int error; + NCPVNDEBUG("nwfs_read:\n"); + + if (vp->v_type != VREG && vp->v_type != VDIR) + return (EPERM); + error = nwfs_readvnode(vp, uio, ap->a_cred); + return error; +} + +static int +nwfs_write(ap) + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + int error; + + NCPVNDEBUG("%d,ofs=%d,sz=%d\n",vp->v_type, (int)uio->uio_offset, uio->uio_resid); + + if (vp->v_type != VREG) + return (EPERM); + error = nwfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag); + return(error); +} +/* + * nwfs_create call + * Create a regular file. On entry the directory to contain the file being + * created is locked. We must release before we return. We must also free + * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or + * only if the SAVESTART bit in cn_flags is clear on success. + */ +static int +nwfs_create(ap) + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + register struct vnode *dvp = ap->a_dvp; + register struct vattr *vap = ap->a_vap; + struct vnode **vpp=ap->a_vpp; + register struct componentname *cnp = ap->a_cnp; + struct vnode *vp = (struct vnode *)0; + int error = 0, fmode; + struct vattr vattr; + struct nwnode *np; + struct ncp_open_info no; + struct nwmount *nmp=VTONWFS(dvp); + ncpfid fid; + + + NCPVNDEBUG("\n"); + *vpp = NULL; + if (vap->va_type == VSOCK) + return (EOPNOTSUPP); + if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc))) { + VOP_ABORTOP(dvp, cnp); + return (error); + } + fmode = AR_READ | AR_WRITE; +/* if (vap->va_vaflags & VA_EXCLUSIVE) + fmode |= AR_DENY_READ | AR_DENY_WRITE;*/ + + error = ncp_open_create_file_or_subdir(nmp,dvp,cnp->cn_namelen,cnp->cn_nameptr, + OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, + 0, fmode, &no, cnp->cn_proc, cnp->cn_cred); + if (!error) { + error = ncp_close_file(NWFSTOCONN(nmp), &no.fh, cnp->cn_proc,cnp->cn_cred); + fid.f_parent = VTONW(dvp)->n_fid.f_id; + fid.f_id = no.fattr.dirEntNum; + error = nwfs_nget(VTOVFS(dvp), fid, &no.fattr, dvp, &vp); + if (!error) { + np = VTONW(vp); + np->opened = 0; + *vpp = vp; + } + if (cnp->cn_flags & MAKEENTRY) + cache_enter(dvp, vp, cnp); + } + zfree(namei_zone, cnp->cn_pnbuf); + return (error); +} + +/* + * nwfs_remove call. It isn't possible to emulate UFS behaivour because + * NetWare doesn't allow delete/rename operations on an opened file. + */ +static int +nwfs_remove(ap) + struct vop_remove_args /* { + struct vnodeop_desc *a_desc; + struct vnode * a_dvp; + struct vnode * a_vp; + struct componentname * a_cnp; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + register struct vnode *dvp = ap->a_dvp; + register struct componentname *cnp = ap->a_cnp; + register struct nwnode *np = VTONW(vp); + struct nwmount *nmp = VTONWFS(vp); + int error; + + if (vp->v_type == VDIR || np->opened || vp->v_usecount != 1) { + error = EPERM; + } else if (!ncp_conn_valid(NWFSTOCONN(nmp))) { + error = EIO; + } else { + cache_purge(vp); + error = ncp_DeleteNSEntry(nmp, VTONW(dvp)->n_fid.f_id, + cnp->cn_namelen,cnp->cn_nameptr,cnp->cn_proc,cnp->cn_cred); + if (error == 0x899c) error = EACCES; + } + zfree(namei_zone, cnp->cn_pnbuf); + return (error); +} + +/* + * nwfs_file rename call + */ +static int +nwfs_rename(ap) + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap; +{ + register struct vnode *fvp = ap->a_fvp; + register struct vnode *tvp = ap->a_tvp; + register struct vnode *fdvp = ap->a_fdvp; + register struct vnode *tdvp = ap->a_tdvp; + register struct componentname *tcnp = ap->a_tcnp; + register struct componentname *fcnp = ap->a_fcnp; + struct nwmount *nmp=VTONWFS(fvp); + u_int16_t oldtype = 6; + int error=0; + + /* Check for cross-device rename */ + if ((fvp->v_mount != tdvp->v_mount) || + (tvp && (fvp->v_mount != tvp->v_mount))) { + error = EXDEV; + goto out; + } + + if (tvp && tvp->v_usecount > 1) { + error = EBUSY; + goto out; + } + if (tvp && tvp != fvp) { + error = ncp_DeleteNSEntry(nmp, VTONW(tdvp)->n_fid.f_id, + tcnp->cn_namelen, tcnp->cn_nameptr, + tcnp->cn_proc, tcnp->cn_cred); + if (error == 0x899c) error = EACCES; + if (error) + goto out; + } + if (fvp->v_type == VDIR) { + oldtype |= NW_TYPE_SUBDIR; + } else if (fvp->v_type == VREG) { + oldtype |= NW_TYPE_FILE; + } else + return EINVAL; + error = ncp_nsrename(NWFSTOCONN(nmp), nmp->n_volume, nmp->name_space, + oldtype, &nmp->m.nls, + VTONW(fdvp)->n_fid.f_id, fcnp->cn_nameptr, fcnp->cn_namelen, + VTONW(tdvp)->n_fid.f_id, tcnp->cn_nameptr, tcnp->cn_namelen, + tcnp->cn_proc,tcnp->cn_cred); + + if (error == 0x8992) + error = EEXIST; + if (fvp->v_type == VDIR) { + if (tvp != NULL && tvp->v_type == VDIR) + cache_purge(tdvp); + cache_purge(fdvp); + } +out: + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + vrele(fdvp); + vrele(fvp); + nwfs_attr_cacheremove(fdvp); + nwfs_attr_cacheremove(tdvp); + /* + * Need to get rid of old vnodes, because netware will change + * file id on rename + */ + vgone(fvp); + if (tvp) + vgone(tvp); + /* + * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry. + */ + if (error == ENOENT) + error = 0; + return (error); +} + +/* + * nwfs hard link create call + * Netware filesystems don't know what links are. But since we already called + * nwfs_lookup() with create and lockparent, the parent is locked so we + * have to free it before we return the error. + */ +static int +nwfs_link(ap) + struct vop_link_args /* { + struct vnode *a_tdvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; +{ +/* VOP_ABORTOP(ap->a_tdvp, ap->a_cnp);*/ + zfree(namei_zone, ap->a_cnp->cn_pnbuf); + return EOPNOTSUPP; +} + +/* + * nwfs_symlink link create call + * Netware filesystems don't know what symlinks are. + */ +static int +nwfs_symlink(ap) + struct vop_symlink_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + char *a_target; + } */ *ap; +{ + zfree(namei_zone, ap->a_cnp->cn_pnbuf); + /* VOP_ABORTOP(ap->a_dvp, ap->a_cnp); ??? */ + return (EOPNOTSUPP); +} + +static int nwfs_mknod(ap) + struct vop_mknod_args /* { + } */ *ap; +{ + return (EOPNOTSUPP); +} + +/* + * nwfs_mkdir call + */ +static int +nwfs_mkdir(ap) + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + register struct vnode *dvp = ap->a_dvp; +/* register struct vattr *vap = ap->a_vap;*/ + register struct componentname *cnp = ap->a_cnp; + register int len=cnp->cn_namelen; + struct ncp_open_info no; + struct nwnode *np; + struct vnode *newvp = (struct vnode *)0; + ncpfid fid; + int error = 0; + struct vattr vattr; + char *name=cnp->cn_nameptr; + + if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc))) { + VOP_ABORTOP(dvp, cnp); + return (error); + } + if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))) { + VOP_ABORTOP(dvp, cnp); + return EEXIST; + } + if (ncp_open_create_file_or_subdir(VTONWFS(dvp),dvp, cnp->cn_namelen, + cnp->cn_nameptr,OC_MODE_CREATE, aDIR, 0xffff, + &no, cnp->cn_proc, cnp->cn_cred) != 0) { + error = EACCES; + } else { + error = 0; + } + if (!error) { + fid.f_parent = VTONW(dvp)->n_fid.f_id; + fid.f_id = no.fattr.dirEntNum; + error = nwfs_nget(VTOVFS(dvp), fid, &no.fattr, dvp, &newvp); + if (!error) { + np = VTONW(newvp); + newvp->v_type = VDIR; + *ap->a_vpp = newvp; + } + } + zfree(namei_zone, cnp->cn_pnbuf); + return (error); +} + +/* + * nwfs_remove directory call + */ +static int +nwfs_rmdir(ap) + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + register struct vnode *dvp = ap->a_dvp; + register struct componentname *cnp = ap->a_cnp; + struct nwmount *nmp = VTONWFS(vp); + struct nwnode *dnp = VTONW(dvp); + int error = EIO; + + if (dvp == vp) return EINVAL; + if (!ncp_conn_valid(NWFSTOCONN(nmp))) return(error); + + error = ncp_DeleteNSEntry(nmp, dnp->n_fid.f_id, + cnp->cn_namelen, cnp->cn_nameptr,cnp->cn_proc,cnp->cn_cred); + if (error == NWE_DIR_NOT_EMPTY) error = ENOTEMPTY; + zfree(namei_zone, cnp->cn_pnbuf); + dnp->n_flag |= NMODIFIED; + nwfs_attr_cacheremove(dvp); + cache_purge(dvp); + cache_purge(vp); + return (error); +} + +/* + * nwfs_readdir call + */ +static int +nwfs_readdir(ap) + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + int *a_eofflag; + u_long *a_cookies; + int a_ncookies; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + register struct uio *uio = ap->a_uio; + int error; + + if (vp->v_type != VDIR) + return (EPERM); + if (ap->a_ncookies) { + printf("nwfs_readdir: no support for cookies now..."); + return (EOPNOTSUPP); + } + + error = nwfs_readvnode(vp, uio, ap->a_cred); + return error; +} +/* ARGSUSED */ +static int +nwfs_fsync(ap) + struct vop_fsync_args /* { + struct vnodeop_desc *a_desc; + struct vnode * a_vp; + struct ucred * a_cred; + int a_waitfor; + struct proc * a_p; + } */ *ap; +{ +/* return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_p, 1));*/ + return (0); +} +/* ARGSUSED */ +static +int nwfs_print (ap) + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap; +{ + return (0); +} + +static int nwfs_pathconf (ap) + struct vop_pathconf_args /* { + struct vnode *vp; + int name; + int *retval; + } */ *ap; +{ + int name=ap->a_name, error=0; + int *retval=ap->a_retval; + + switch(name){ + case _PC_LINK_MAX: + *retval=0; + break; + case _PC_NAME_MAX: + *retval=NCP_MAX_FILENAME; /* XXX from nwfsnode */ + break; + case _PC_PATH_MAX: + *retval=NCP_MAXPATHLEN; /* XXX from nwfsnode */ + break; + default: + error=EINVAL; + } + return(error); +} + +static int nwfs_strategy (ap) + struct vop_strategy_args /* { + struct buf *a_bp + } */ *ap; +{ + struct buf *bp=ap->a_bp; + struct ucred *cr; + struct proc *p; + int error = 0; + + NCPVNDEBUG("\n"); + if (bp->b_flags & B_PHYS) + panic("nwfs physio"); + if (bp->b_flags & B_ASYNC) + p = (struct proc *)0; + else + p = curproc; /* XXX */ + if (bp->b_flags & B_READ) + cr = bp->b_rcred; + else + cr = bp->b_wcred; + /* + * If the op is asynchronous and an i/o daemon is waiting + * queue the request, wake it up and wait for completion + * otherwise just do it ourselves. + */ + if ((bp->b_flags & B_ASYNC) == 0 ) + error = nwfs_doio(bp, cr, p); + return (error); +} + +static int +nwfs_bmap(ap) + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + int *a_runb; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + + if (ap->a_vpp != NULL) + *ap->a_vpp = vp; + if (ap->a_bnp != NULL) + *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize); + if (ap->a_runp != NULL) + *ap->a_runp = 0; + if (ap->a_runb != NULL) + *ap->a_runb = 0; + return (0); +} +/* + * nwfs abort op, called after namei() when a CREATE/DELETE isn't actually + * done. Currently nothing to do. + */ +/* ARGSUSED */ +int +nwfs_abortop(ap) + struct vop_abortop_args /* { + struct vnode *a_dvp; + struct componentname *a_cnp; + } */ *ap; +{ + + if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) + zfree(namei_zone, ap->a_cnp->cn_pnbuf); + return (0); +} + +int +nwfs_nget(struct mount *mp, ncpfid fid, struct nw_entry_info *fap, + struct vnode *dvp, struct vnode **vpp) +{ + int error; + struct nwnode *newnp; + struct vnode *vp; + + *vpp = NULL; + error = nwfs_allocvp(mp, fid, &vp); + if (error) + return error; + newnp = VTONW(vp); + if (fap) { + if (newnp->n_flag & NNEW) { + newnp->n_nmlen = fap->nameLen; + bcopy(fap->entryName, newnp->n_name, newnp->n_nmlen); + newnp->n_name[fap->nameLen] = 0; + } + newnp->n_attr = fap->attributes; + newnp->n_dosfid = fap->DosDirNum; + vp->v_type = newnp->n_attr & aDIR ? VDIR : VREG; + nwfs_attr_cacheenter(vp, fap); + } + if (dvp) { + newnp->n_parent = VTONW(dvp)->n_fid; + if ((newnp->n_flag & NNEW) && vp->v_type == VDIR) { + if ((dvp->v_flag & VROOT) == 0) { + newnp->n_refparent = 1; + vref(dvp); /* vhold */ + } + } + } else { + if ((newnp->n_flag & NNEW) && vp->v_type == VREG) + printf("new vnode '%s' borned without parent ?\n",newnp->n_name); + } + newnp->n_flag &= ~NNEW; + *vpp = vp; + return 0; +} + +/* + * How to keep the brain busy ... + * Currently lookup routine can make two lookup for vnode. This can be + * avoided by reorg the code. + */ +int +nwfs_lookup(ap) + struct vop_lookup_args /* { + struct vnodeop_desc *a_desc; + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap; +{ + struct componentname *cnp = ap->a_cnp; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + int flags = cnp->cn_flags; + struct vnode *vp; + struct nwmount *nmp; + struct mount *mp = dvp->v_mount; + struct nwnode *dnp, *npp; + struct nw_entry_info fattr, *fap; + ncpfid fid; + int nameiop=cnp->cn_nameiop, islastcn; + int lockparent, wantparent, error = 0, notfound; + struct proc *p = cnp->cn_proc; + char _name[cnp->cn_namelen+1]; + bcopy(cnp->cn_nameptr,_name,cnp->cn_namelen); + _name[cnp->cn_namelen]=0; + + if (dvp->v_type != VDIR) + return (ENOTDIR); + if ((flags & ISDOTDOT) && (dvp->v_flag & VROOT)) { + printf("nwfs_lookup: invalid '..'\n"); + return EIO; + } + + NCPVNDEBUG("%d '%s' in '%s' id=d\n", nameiop, _name, + VTONW(dvp)->n_name/*, VTONW(dvp)->n_name*/); + + islastcn = flags & ISLASTCN; + if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP)) + return (EROFS); + if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, p))) + return (error); + lockparent = flags & LOCKPARENT; + wantparent = flags & (LOCKPARENT|WANTPARENT); + nmp = VFSTONWFS(mp); + dnp = VTONW(dvp); +/* +printf("dvp %d:%d:%d\n", (int)mp, (int)dvp->v_flag & VROOT, (int)flags & ISDOTDOT); +*/ + error = ncp_pathcheck(cnp->cn_nameptr, cnp->cn_namelen, &nmp->m.nls, + (nameiop == CREATE || nameiop == RENAME) && (nmp->m.nls.opt & NWHP_NOSTRICT) == 0); + if (error) + return ENOENT; + + error = cache_lookup(dvp, vpp, cnp); + NCPVNDEBUG("cache_lookup returned %d\n",error); + if (error > 0) + return error; + if (error) { /* name was found */ + struct vattr vattr; + int vpid; + + vp = *vpp; + vpid = vp->v_id; + if (dvp == vp) { /* lookup on current */ + vref(vp); + error = 0; + NCPVNDEBUG("cached '.'"); + } else if (flags & ISDOTDOT) { + VOP_UNLOCK(dvp, 0, p); /* unlock parent */ + error = vget(vp, LK_EXCLUSIVE, p); + if (!error && lockparent && islastcn) + error = vn_lock(dvp, LK_EXCLUSIVE, p); + } else { + error = vget(vp, LK_EXCLUSIVE, p); + if (!lockparent || error || !islastcn) + VOP_UNLOCK(dvp, 0, p); + } + if (!error) { + if (vpid == vp->v_id) { + if (!VOP_GETATTR(vp, &vattr, cnp->cn_cred, p) + && vattr.va_ctime.tv_sec == VTONW(vp)->n_ctime) { + if (nameiop != LOOKUP && islastcn) + cnp->cn_flags |= SAVENAME; + NCPVNDEBUG("use cached vnode"); + return (0); + } + cache_purge(vp); + } + vput(vp); + if (lockparent && dvp != vp && islastcn) + VOP_UNLOCK(dvp, 0, p); + } + error = vn_lock(dvp, LK_EXCLUSIVE, p); + *vpp = NULLVP; + if (error) + return (error); + } + /* not in cache, so ... */ + error = 0; + *vpp = NULLVP; + fap = NULL; + if (flags & ISDOTDOT) { + if (NWCMPF(&dnp->n_parent, &nmp->n_rootent)) { + fid = nmp->n_rootent; + fap = NULL; + notfound = 0; + } else { + error = nwfs_lookupnp(nmp, dnp->n_parent, &npp); + if (error) { + return error; + } + fid = dnp->n_parent; + fap = &fattr; + /*np = *npp;*/ + notfound = ncp_obtain_info(nmp, npp->n_dosfid, + 0, NULL, fap, p, cnp->cn_cred); + } + } else { + fap = &fattr; + notfound = ncp_lookup(dvp, cnp->cn_namelen, cnp->cn_nameptr, + fap, p, cnp->cn_cred); + fid.f_parent = dnp->n_fid.f_id; + fid.f_id = fap->dirEntNum; + NCPVNDEBUG("call to ncp_lookup returned=%d\n",notfound); + } + if (notfound && notfound < 0x80 ) + return (notfound); /* hard error */ + if (notfound) { /* entry not found */ + /* Handle RENAME or CREATE case... */ + if ((nameiop == CREATE || nameiop == RENAME) && wantparent && islastcn) { + cnp->cn_flags |= SAVENAME; + if (!lockparent) + VOP_UNLOCK(dvp, 0, p); + return (EJUSTRETURN); + } + return ENOENT; + }/* else { + NCPVNDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum); + }*/ + /* handle DELETE case ... */ + if (nameiop == DELETE && islastcn) { /* delete last component */ + error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, cnp->cn_proc); + if (error) return (error); + if (NWCMPF(&dnp->n_fid, &fid)) { /* we found ourselfs */ + VREF(dvp); + *vpp = dvp; + return 0; + } + error = nwfs_nget(mp, fid, fap, dvp, &vp); + if (error) return (error); + *vpp = vp; + cnp->cn_flags |= SAVENAME; /* I free it later */ + if (!lockparent) VOP_UNLOCK(dvp,0,p); + return (0); + } + if (nameiop == RENAME && islastcn && wantparent) { + error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, cnp->cn_proc); + if (error) return (error); + if (NWCMPF(&dnp->n_fid, &fid)) return EISDIR; + error = nwfs_nget(mp, fid, fap, dvp, &vp); + if (error) return (error); + *vpp = vp; + cnp->cn_flags |= SAVENAME; + if (!lockparent) + VOP_UNLOCK(dvp,0,p); + return (0); + } + if (flags & ISDOTDOT) { + VOP_UNLOCK(dvp, 0, p); /* race to get the inode */ + error = nwfs_nget(mp, fid, NULL, NULL, &vp); + if (error) { + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); + return (error); + } + if (lockparent && islastcn && + (error = vn_lock(dvp, LK_EXCLUSIVE, p))) { + vput(vp); + return (error); + } + *vpp = vp; + } else if (NWCMPF(&dnp->n_fid, &fid)) { + vref(dvp); + *vpp = dvp; + } else { + error = nwfs_nget(mp, fid, fap, dvp, &vp); + if (error) return (error); + *vpp = vp; + NCPVNDEBUG("lookup: getnewvp!\n"); + if (!lockparent || !islastcn) + VOP_UNLOCK(dvp, 0, p); + } + if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) { + VTONW(*vpp)->n_ctime = VTONW(*vpp)->n_vattr.va_ctime.tv_sec; + cache_enter(dvp, *vpp, cnp); + } + return (0); +} |