diff options
author | bp <bp@FreeBSD.org> | 1999-10-02 04:06:24 +0000 |
---|---|---|
committer | bp <bp@FreeBSD.org> | 1999-10-02 04:06:24 +0000 |
commit | 99edac4b3b7b369e65394b2bd57332aaad8d6812 (patch) | |
tree | 648f799b08d76bc3345446a922bb7985b440fae9 /sys | |
parent | 4f9461c0bd8dd6d2fb8a4f0df36c1666e4e25c98 (diff) | |
download | FreeBSD-src-99edac4b3b7b369e65394b2bd57332aaad8d6812.zip FreeBSD-src-99edac4b3b7b369e65394b2bd57332aaad8d6812.tar.gz |
Import kernel part of ncplib: netncp and nwfs
Reviewed by: msmith, peter
Obtained from: ncplib
Diffstat (limited to 'sys')
38 files changed, 13112 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); +} diff --git a/sys/netncp/ncp.h b/sys/netncp/ncp.h new file mode 100644 index 0000000..4c285ef --- /dev/null +++ b/sys/netncp/ncp.h @@ -0,0 +1,391 @@ +/* + * ncp.h + * + * Copyright (C) 1995 by Volker Lendecke + * New version derived from original ncp.h, 1998 Boris Popov + * + * $FreeBSD$ + */ + +#ifndef _NCP_H_ +#define _NCP_H_ + +#define NCP_VERMAJ 1 +#define NCP_VERMIN 3200 +#define NCP_VERSION (NCP_VERMAJ*100000 + NCP_VERMIN) + +typedef u_int32_t nwdirent; + +typedef char nstr8; +typedef nstr8* pnstr8; +typedef u_int8_t nuint8; +typedef u_int8_t* pnuint8; +typedef u_int16_t nuint16; +typedef nuint16* pnuint16; +typedef u_int32_t nuint32; +typedef nuint32* pnuint32; + + +#define NCP_DEFAULT_BUFSIZE 1024 +#define NCP_MAX_BUFSIZE 1024 +#define NCP_MAX_PACKET_SIZE 4070 +#define NCP_MAXUSERNAMELEN 255 +#define NCP_MAXPASSWORDLEN 255 +#define NCP_MAXPATHLEN 255 +#define NCP_MAX_FILENAME 14 +#define NCP_FILE_ID_LEN 6 + +#define NCP_BINDERY_USER 0x0001 +#define NCP_BINDERY_UGROUP 0x0002 +#define NCP_BINDERY_PQUEUE 0x0003 +#define NCP_BINDERY_FSERVER 0x0004 +#define NCP_BINDERY_PSERVER 0x0007 +#define NCP_BINDERY_NAME_LEN 48 + +/* Handle Flags */ +#define NCP_HF_DIRSHORT 0 /* short directory handle */ +#define NCP_HF_DIRBASE 1 /* directory base */ +#define NCP_HF_NONE 0xff /* no handle or dirbase */ + +/* Options to negotiate */ +#define NCP_IPX_CHECKSUM 1 +#define NCP_SECURITY_LEVEL_SIGN_HEADERS 2 + +#ifndef NWCONN_HANDLE +#define NWCONN_HANDLE unsigned int +#define pNWCONN_HANDLE (unsigned int*) +#define NWCONN_NUM u_int16_t +#define NWCCODE unsigned int +#define NWDIR_HANDLE u_int8_t +#define NWFILE_HANDLE int +#endif + +struct ncp_fh_s { + u_int16_t val1; + union { + u_int32_t val32; + u_int16_t val16; + } val; +} __attribute__((packed)); + +typedef struct ncp_fh_s ncp_fh; + +typedef struct ncpfid_s { + nwdirent f_parent; + nwdirent f_id; +} ncpfid; + +/* -- Bindery properties -- */ +struct ncp_bindery_object { + u_int32_t object_id; + u_int16_t object_type; + u_int8_t object_name[NCP_BINDERY_NAME_LEN]; + u_int8_t object_flags; + u_int8_t object_security; + u_int8_t object_has_prop; +}; + +struct nw_property { + u_int8_t value[128]; + u_int8_t more_flag; + u_int8_t property_flag; +}; + +struct ncp_filesearch_info { + u_int8_t volume_number; + u_int16_t directory_id; + u_int16_t sequence_no; + u_int8_t access_rights; +}; + + +struct ncp_file_info { + u_int8_t file_id[NCP_FILE_ID_LEN]; + char file_name[NCP_MAX_FILENAME + 1]; + u_int8_t file_attributes; + u_int8_t file_mode; + u_int32_t file_length; + u_int16_t creation_date; + u_int16_t access_date; + u_int16_t update_date; + u_int16_t update_time; +}; + +struct nw_queue_job_entry { + u_int16_t InUse __attribute__((packed)); + u_int32_t prev __attribute__((packed)); + u_int32_t next __attribute__((packed)); + u_int32_t ClientStation __attribute__((packed)); + u_int32_t ClientTask __attribute__((packed)); + u_int32_t ClientObjectID __attribute__((packed)); + u_int32_t TargetServerID __attribute__((packed)); + u_int8_t TargetExecTime[6] __attribute__((packed)); + u_int8_t JobEntryTime[6] __attribute__((packed)); + u_int32_t JobNumber __attribute__((packed)); + u_int16_t JobType __attribute__((packed)); + u_int16_t JobPosition __attribute__((packed)); + u_int16_t JobControlFlags __attribute__((packed)); + u_int8_t FileNameLen __attribute__((packed)); + char JobFileName[13] __attribute__((packed)); + u_int32_t JobFileHandle __attribute__((packed)); + u_int32_t ServerStation __attribute__((packed)); + u_int32_t ServerTaskNumber __attribute__((packed)); + u_int32_t ServerObjectID __attribute__((packed)); + char JobTextDescription[50] __attribute__((packed)); + char ClientRecordArea[152] __attribute__((packed)); +}; + +struct queue_job { + struct nw_queue_job_entry j; + ncp_fh file_handle; +}; + +#define QJE_OPER_HOLD 0x80 +#define QJE_USER_HOLD 0x40 +#define QJE_ENTRYOPEN 0x20 +#define QJE_SERV_RESTART 0x10 +#define QJE_SERV_AUTO 0x08 + +/* ClientRecordArea for print jobs */ + +#define KEEP_ON 0x0400 +#define NO_FORM_FEED 0x0800 +#define NOTIFICATION 0x1000 +#define DELETE_FILE 0x2000 +#define EXPAND_TABS 0x4000 +#define PRINT_BANNER 0x8000 + +struct print_job_record { + u_int8_t Version __attribute__((packed)); + u_int8_t TabSize __attribute__((packed)); + u_int16_t Copies __attribute__((packed)); + u_int16_t CtrlFlags __attribute__((packed)); + u_int16_t Lines __attribute__((packed)); + u_int16_t Rows __attribute__((packed)); + char FormName[16] __attribute__((packed)); + u_int8_t Reserved[6] __attribute__((packed)); + char BannerName[13] __attribute__((packed)); + char FnameBanner[13] __attribute__((packed)); + char FnameHeader[14] __attribute__((packed)); + char Path[80] __attribute__((packed)); +}; + +struct ncp_station_addr { + u_int32_t NetWork; + u_int8_t Node[6]; + u_int16_t Socket; +} __attribute__((packed)); + +struct ncp_prop_login_control { + u_int8_t AccountExpireDate[3] __attribute__((packed)); + u_int8_t Disabled __attribute__((packed)); + u_int8_t PasswordExpireDate[3] __attribute__((packed)); + u_int8_t GraceLogins __attribute__((packed)); + u_int16_t PasswordExpireInterval __attribute__((packed)); + u_int8_t MaxGraceLogins __attribute__((packed)); + u_int8_t MinPasswordLength __attribute__((packed)); + u_int16_t MaxConnections __attribute__((packed)); + u_int8_t ConnectionTimeMask[42] __attribute__((packed)); + u_int8_t LastLogin[6] __attribute__((packed)); + u_int8_t RestrictionMask __attribute__((packed)); + u_int8_t reserved __attribute__((packed)); + u_int32_t MaxDiskUsage __attribute__((packed)); + u_int16_t BadLoginCount __attribute__((packed)); + u_int32_t BadLoginCountDown __attribute__((packed)); + struct ncp_station_addr LastIntruder __attribute__((packed)); +}; + +#define NCP_VOLNAME_LEN (16) +#define NCP_NUMBER_OF_VOLUMES (64) +struct ncp_volume_info { + u_int32_t total_blocks; + u_int32_t free_blocks; + u_int32_t purgeable_blocks; + u_int32_t not_yet_purgeable_blocks; + u_int32_t total_dir_entries; + u_int32_t available_dir_entries; + u_int8_t sectors_per_block; + char volume_name[NCP_VOLNAME_LEN + 1]; +}; +/* + * Name space constants, taken from NDK + */ +#define aRONLY (ntohl(0x01000000)) +#define aHIDDEN (ntohl(0x02000000)) +#define aSYSTEM (ntohl(0x04000000)) +#define aEXECUTE (ntohl(0x08000000)) +#define aDIR (ntohl(0x10000000)) +#define aARCH (ntohl(0x20000000)) + +/* Defines for Name Spaces */ +#define NW_NS_DOS 0 +#define NW_NS_MAC 1 +#define NW_NS_NFS 2 +#define NW_NS_FTAM 3 +#define NW_NS_OS2 4 + +/* for _ScanNSEntryInfo */ +#define IM_NAME 0x00000001 +#define IM_SPACE_ALLOCATED 0x00000002 +#define IM_ATTRIBUTES 0x00000004 +#define IM_SIZE 0x00000008 +#define IM_TOTAL_SIZE 0x00000010 +#define IM_EA 0x00000020 +#define IM_ARCHIVE 0x00000040 +#define IM_MODIFY 0x00000080 +#define IM_CREATION 0x00000100 +#define IM_OWNING_NAMESPACE 0x00000200 +#define IM_DIRECTORY 0x00000400 +#define IM_RIGHTS 0x00000800 +#define IM_ALMOST_ALL 0x00000FED +#define IM_ALL 0x00000FFF +#define IM_REFERENCE_ID 0x00001000 +#define IM_NS_ATTRIBUTES 0x00002000 +#define IM_COMPRESSED_INFO 0x80000000UL + +/* open/create modes */ +#define OC_MODE_OPEN 0x01 +#define OC_MODE_TRUNCATE 0x02 +#define OC_MODE_REPLACE 0x02 +#define OC_MODE_CREATE 0x08 + +/* open/create results */ +#define OC_ACTION_NONE 0x00 +#define OC_ACTION_OPEN 0x01 +#define OC_ACTION_CREATE 0x02 +#define OC_ACTION_TRUNCATE 0x04 +#define OC_ACTION_REPLACE 0x04 + +/* renameFlag in NSRename */ +#define NW_TYPE_FILE 0x8000 +#define NW_TYPE_SUBDIR 0x0010 + +#define NW_NAME_CONVERT 0x0003 /* don't report error and set comp mode */ +#define NW_NO_NAME_CONVERT 0x0004 /* only in specified name space */ + +/* search attributes */ +#ifndef SA_HIDDEN +#define SA_NORMAL 0x0000 +#define SA_HIDDEN 0x0002 +#define SA_SYSTEM 0x0004 +#define SA_SUBDIR_ONLY 0x0010 +#define SA_SUBDIR_FILES 0x8000 +#define SA_ALL 0x8006 +#endif + +/* access rights attributes */ +#ifndef AR_READ +#define AR_READ 0x0001 +#define AR_WRITE 0x0002 +#define AR_READ_ONLY 0x0001 +#define AR_WRITE_ONLY 0x0002 +#define AR_DENY_READ 0x0004 +#define AR_DENY_WRITE 0x0008 +#define AR_COMPATIBILITY 0x0010 +#define AR_WRITE_THROUGH 0x0040 +#define AR_OPEN_COMPRESSED 0x0100 +#endif + +struct nw_entry_info { + u_int32_t spaceAlloc; + u_int32_t attributes; /* LH */ + u_int16_t flags; /* internal */ + u_int32_t dataStreamSize; + u_int32_t totalStreamSize; + u_int16_t numberOfStreams; + u_int16_t creationTime; /* LH */ + u_int16_t creationDate; /* LH */ + u_int32_t creatorID; /* HL */ + u_int16_t modifyTime; /* LH */ + u_int16_t modifyDate; /* LH */ + u_int32_t modifierID; /* HL */ + u_int16_t lastAccessDate; /* LH */ + u_int16_t archiveTime; /* LH */ + u_int16_t archiveDate; /* LH */ + u_int32_t archiverID; /* HL */ + u_int16_t inheritedRightsMask; /* LH */ + u_int32_t dirEntNum; + u_int32_t DosDirNum; + u_int32_t volNumber; + u_int32_t EADataSize; + u_int32_t EAKeyCount; + u_int32_t EAKeySize; + u_int32_t NSCreator; + u_int8_t nameLen; + u_int8_t entryName[256]; +} __attribute__((packed)); + +typedef struct nw_entry_info NW_ENTRY_INFO; + +/* modify mask - use with MODIFY_DOS_INFO structure */ +#define DM_ATTRIBUTES 0x0002L +#define DM_CREATE_DATE 0x0004L +#define DM_CREATE_TIME 0x0008L +#define DM_CREATOR_ID 0x0010L +#define DM_ARCHIVE_DATE 0x0020L +#define DM_ARCHIVE_TIME 0x0040L +#define DM_ARCHIVER_ID 0x0080L +#define DM_MODIFY_DATE 0x0100L +#define DM_MODIFY_TIME 0x0200L +#define DM_MODIFIER_ID 0x0400L +#define DM_LAST_ACCESS_DATE 0x0800L +#define DM_INHERITED_RIGHTS_MASK 0x1000L)) +#define DM_MAXIMUM_SPACE 0x2000L + +struct nw_modify_dos_info { + u_int32_t attributes __attribute__((packed)); + u_int16_t creationDate __attribute__((packed)); + u_int16_t creationTime __attribute__((packed)); + u_int32_t creatorID __attribute__((packed)); + u_int16_t modifyDate __attribute__((packed)); + u_int16_t modifyTime __attribute__((packed)); + u_int32_t modifierID __attribute__((packed)); + u_int16_t archiveDate __attribute__((packed)); + u_int16_t archiveTime __attribute__((packed)); + u_int32_t archiverID __attribute__((packed)); + u_int16_t lastAccessDate __attribute__((packed)); + u_int16_t inheritanceGrantMask __attribute__((packed)); + u_int16_t inheritanceRevokeMask __attribute__((packed)); + u_int32_t maximumSpace __attribute__((packed)); +}; + +struct nw_search_seq { + u_int8_t volNumber; + u_int32_t dirNumber; + u_int32_t searchDirNumber; +} __attribute__((packed)); + +typedef struct nw_search_seq SEARCH_SEQUENCE; + +struct ncp_file_server_info { + u_int8_t ServerName[48] __attribute__((packed)); + u_int8_t FileServiceVersion __attribute__((packed)); + u_int8_t FileServiceSubVersion __attribute__((packed)); + u_int16_t MaximumServiceConnections __attribute__((packed)); + u_int16_t ConnectionsInUse __attribute__((packed)); + u_int16_t NumberMountedVolumes __attribute__((packed)); + u_int8_t Revision __attribute__((packed)); + u_int8_t SFTLevel __attribute__((packed)); + u_int8_t TTSLevel __attribute__((packed)); + u_int16_t MaxConnectionsEverUsed __attribute__((packed)); + u_int8_t AccountVersion __attribute__((packed)); + u_int8_t VAPVersion __attribute__((packed)); + u_int8_t QueueVersion __attribute__((packed)); + u_int8_t PrintVersion __attribute__((packed)); + u_int8_t VirtualConsoleVersion __attribute__((packed)); + u_int8_t RestrictionLevel __attribute__((packed)); + u_int8_t InternetBridge __attribute__((packed)); + u_int8_t Reserved[60] __attribute__((packed)); +}; + +struct nw_time_buffer { + u_int8_t year __attribute__((packed)); + u_int8_t month __attribute__((packed)); + u_int8_t day __attribute__((packed)); + u_int8_t hour __attribute__((packed)); + u_int8_t minute __attribute__((packed)); + u_int8_t second __attribute__((packed)); + u_int8_t wday __attribute__((packed)); +}; + +#endif /*_NCP_H_ */ diff --git a/sys/netncp/ncp_conn.c b/sys/netncp/ncp_conn.c new file mode 100644 index 0000000..d02ca48f --- /dev/null +++ b/sys/netncp/ncp_conn.c @@ -0,0 +1,541 @@ +/* + * 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$ + * + * Connection tables + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/stat.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/sysctl.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_subr.h> +#include <netncp/ncp_user.h> +#include <netncp/ncp_conn.h> + +SLIST_HEAD(ncp_handle_head,ncp_handle); + +int ncp_burst_enabled = 1; + +struct ncp_conn_head conn_list={NULL}; +static int ncp_conn_cnt = 0; +static int ncp_next_ref = 1; +static struct lock listlock; + +struct ncp_handle_head lhlist={NULL}; +static int ncp_next_handle = 1; +static struct lock lhlock; + +static int ncp_sysctl_connstat SYSCTL_HANDLER_ARGS; +static int ncp_conn_lock_any(struct ncp_conn *conn, struct proc *p, + struct ucred *cred); + +extern struct linker_set sysctl_net_ncp; + +SYSCTL_DECL(_net_ncp); +SYSCTL_NODE(_net, OID_AUTO, ncp, CTLFLAG_RW, NULL, "NetWare requester"); +SYSCTL_INT (_net_ncp, OID_AUTO, burst_enabled, CTLFLAG_RD, &ncp_burst_enabled, 0, ""); +SYSCTL_INT (_net_ncp, OID_AUTO, conn_cnt, CTLFLAG_RD, &ncp_conn_cnt, 0, ""); +SYSCTL_PROC(_net_ncp, OID_AUTO, conn_stat, CTLFLAG_RD|CTLTYPE_OPAQUE, + NULL, 0, ncp_sysctl_connstat, "S,connstat", "Connections list"); + +MALLOC_DEFINE(M_NCPDATA, "NCP data", "NCP private data"); + +int +ncp_conn_init(void) { + lockinit(&listlock, PSOCK, "ncpll", 0, 0); + lockinit(&lhlock, PSOCK, "ncplh", 0, 0); + return 0; +} + +int +ncp_conn_locklist(int flags, struct proc *p){ + return lockmgr(&listlock, flags | LK_CANRECURSE, 0, p); +} + +void +ncp_conn_unlocklist(struct proc *p){ + lockmgr(&listlock, LK_RELEASE, 0, p); +} + +int +ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode) { + int error; + + if (ncp_suser(cred) == 0 || cred->cr_uid == conn->nc_owner->cr_uid) + return 0; + mode >>= 3; + if (!groupmember(conn->nc_group, cred)) + mode >>= 3; + error = (conn->li.access_mode & mode) == mode ? 0 : EACCES; + return error; +} + +int +ncp_conn_lock_any(struct ncp_conn *conn, struct proc *p, struct ucred *cred) { + int error; + + if (conn->nc_id == 0) return EACCES; + error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, 0, p); + if (error == ERESTART) + return EINTR; + error = ncp_chkintr(conn, p); + if (error) { + lockmgr(&conn->nc_lock, LK_RELEASE, 0, p); + return error; + } + + if (conn->nc_id == 0) { + lockmgr(&conn->nc_lock, LK_RELEASE, 0, p); + return EACCES; + } + conn->procp = p; /* who currently operates */ + conn->ucred = cred; + return 0; +} + +int +ncp_conn_lock(struct ncp_conn *conn, struct proc *p, struct ucred *cred, int mode) { + int error; + + error = ncp_conn_access(conn,cred,mode); + if (error) return error; + return ncp_conn_lock_any(conn, p, cred); +} + +/* + * Lock conn but unlock connlist + */ +static int +ncp_conn_lock2(struct ncp_conn *conn, struct proc *p, struct ucred *cred, int mode) { + int error; + + error = ncp_conn_access(conn,cred,mode); + if (error) { + ncp_conn_unlocklist(p); + return error; + } + conn->nc_lwant++; + ncp_conn_unlocklist(p); + error = ncp_conn_lock_any(conn,p,cred); + conn->nc_lwant--; + if (conn->nc_lwant == 0) { + wakeup(&conn->nc_lwant); + } + return error; +} + +void +ncp_conn_unlock(struct ncp_conn *conn, struct proc *p) { + /* + * note, that LK_RELASE will do wakeup() instead of wakeup_one(). + * this will do a little overhead + */ + lockmgr(&conn->nc_lock, LK_RELEASE, 0, p); +} + +int +ncp_conn_assert_locked(struct ncp_conn *conn,char *checker, struct proc *p){ + if (conn->nc_lock.lk_flags & LK_HAVE_EXCL) return 0; + printf("%s: connection isn't locked!\n", checker); + return EIO; +} + +/* + * create, fill with defaults and return in locked state + */ +int +ncp_conn_alloc(struct proc *p, struct ucred *cred, struct ncp_conn **conn) +{ + int error; + struct ncp_conn *ncp; + + MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn), + M_NCPDATA, M_WAITOK); + if (ncp == NULL) return ENOMEM; + error = 0; + bzero(ncp,sizeof(*ncp)); + lockinit(&ncp->nc_lock, PZERO, "ncplck", 0, 0); + ncp_conn_cnt++; + ncp->nc_id = ncp_next_ref++; + ncp->nc_owner = cred; + ncp->seq = 0; + ncp->connid = 0xFFFF; + ncp_conn_lock_any(ncp, p, ncp->nc_owner); + *conn = ncp; + ncp_conn_locklist(LK_EXCLUSIVE, p); + SLIST_INSERT_HEAD(&conn_list,ncp,nc_next); + ncp_conn_unlocklist(p); + return (error); +} + +/* + * Remove the connection, on entry it must be locked + */ +int +ncp_conn_free(struct ncp_conn *ncp) { + int error; + struct ncp_conn *ncp1; + + if (ncp->nc_id == 0) { + printf("already!!!!\n"); + return EACCES; + } + if (ncp==NULL) { + NCPFATAL("conn==NULL !\n"); + return(EIO); + } + error = ncp_conn_assert_locked(ncp, __FUNCTION__, ncp->procp); + if (error) return error; + if (ncp->ref_cnt) { + NCPFATAL("there are %d referenses left\n",ncp->ref_cnt); + return(EBUSY); + } + /* + * Mark conn as died and wait for other process + */ + ncp->nc_id = 0; + ncp_conn_unlock(ncp,ncp->procp); + /* + * if signal is raised - how I do react ? + */ + lockmgr(&ncp->nc_lock, LK_DRAIN, 0, ncp->procp); + while (ncp->nc_lwant) { + printf("lwant = %d\n", ncp->nc_lwant); + tsleep(&ncp->nc_lwant, PZERO,"ncpdr",2*hz); + } + ncp_conn_locklist(LK_EXCLUSIVE, ncp->procp); + /* + * It is possible, that other process destroy connection while we draining, + * and free it. So, we must rescan list + */ + SLIST_FOREACH(ncp1, &conn_list, nc_next) { + if (ncp1 == ncp) break; + } + if (ncp1 == NULL) { + ncp_conn_unlocklist(ncp->procp); + return 0; + } + SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next); + ncp_conn_cnt--; + ncp_conn_unlocklist(ncp->procp); + if (ncp->li.user) free(ncp->li.user, M_NCPDATA); + if (ncp->li.password) free(ncp->li.password, M_NCPDATA); + crfree(ncp->nc_owner); + FREE(ncp, M_NCPDATA); + return (0); +} + +/* + * Lookup connection by handle, return a locked conn descriptor + */ +int +ncp_conn_getbyref(int ref,struct proc *p,struct ucred *cred, int mode, struct ncp_conn **connpp){ + struct ncp_conn *ncp; + int error=0; + + ncp_conn_locklist(LK_SHARED, p); + SLIST_FOREACH(ncp, &conn_list, nc_next) + if (ncp->nc_id == ref) break; + if (ncp == NULL) { + ncp_conn_unlocklist(p); + return(EBADF); + } + error = ncp_conn_lock2(ncp, p, cred, mode); + if (!error) + *connpp = ncp; + return (error); +} +/* + * find attached, but not logged in connection to specified server + */ +int +ncp_conn_getattached(struct ncp_conn_args *li,struct proc *p,struct ucred *cred,int mode, struct ncp_conn **connpp){ + struct ncp_conn *ncp, *ncp2=NULL; + int error = 0; + + ncp_conn_locklist(LK_SHARED, p); + SLIST_FOREACH(ncp, &conn_list, nc_next) { + if ((ncp->flags & NCPFL_LOGGED) != 0 || + strcmp(ncp->li.server,li->server) != 0 || + ncp->li.saddr.sa_len != li->saddr.sa_len || + memcmp(&ncp->li.saddr,&ncp->li.saddr,li->saddr.sa_len) != 0) + continue; + if (ncp_suser(cred) == 0 || + cred->cr_uid == ncp->nc_owner->cr_uid) + break; + error = ncp_conn_access(ncp,cred,mode); + if (!error && ncp2 == NULL) + ncp2 = ncp; + } + if (ncp == NULL) ncp = ncp2; + if (ncp == NULL) { + ncp_conn_unlocklist(p); + return(EBADF); + } + error = ncp_conn_lock2(ncp,p,cred,mode); + if (!error) + *connpp=ncp; + return (error); +} + +/* + * Lookup connection by server/user pair, return a locked conn descriptor. + * if li is NULL or server/user pair incomplete, try to select best connection + * based on owner. + * Connection selected in next order: + * 1. Try to search conn with ucred owner, if li is NULL also find a primary + * 2. If 1. fails try to get first suitable shared connection + * 3. If 2. fails then nothing can help to poor ucred owner + */ + +int +ncp_conn_getbyli(struct ncp_conn_args *li,struct proc *p,struct ucred *cred,int mode, struct ncp_conn **connpp){ + struct ncp_conn *ncp, *ncp2=NULL; + int error=0, partial, haveserv; + + partial = (li == NULL || li->server[0] == 0 || li->user == NULL); + haveserv = (li && li->server[0]); + ncp_conn_locklist(LK_SHARED, p); + SLIST_FOREACH(ncp, &conn_list, nc_next) { + if (partial) { + if (cred->cr_uid == ncp->nc_owner->cr_uid) { + if (haveserv) { + if (strcmp(ncp->li.server,li->server) == 0) + break; + } else { + if (ncp->flags & NCPFL_PRIMARY) + break; + ncp2 = ncp; + } + continue; + } + } else { + if (strcmp(ncp->li.server,li->server) != 0 || + ncp->li.user == NULL || + strcmp(ncp->li.user,li->user) != 0) + continue; + if (cred->cr_uid == ncp->nc_owner->cr_uid) + break; + if (ncp_suser(cred) == 0) + ncp2 = ncp; + } + error = ncp_conn_access(ncp,cred,mode); + if (!error && ncp2 == NULL) + ncp2 = ncp; + } + if (ncp == NULL) ncp = ncp2; + if (ncp == NULL) { + ncp_conn_unlocklist(p); + return(EBADF); + } + error = ncp_conn_lock2(ncp,p,cred,mode); + if (!error) + *connpp=ncp; + return (error); +} + +/* + * Set primary connection flag, since it have sence only for an owner, + * only owner can modify this flag. + * connection expected to be locked. + */ +int +ncp_conn_setprimary(struct ncp_conn *conn, int on){ + struct ncp_conn *ncp=NULL; + + if (conn->ucred->cr_uid != conn->nc_owner->cr_uid) + return EACCES; + ncp_conn_locklist(LK_SHARED, conn->procp); + SLIST_FOREACH(ncp, &conn_list, nc_next) { + if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid) + ncp->flags &= ~NCPFL_PRIMARY; + } + ncp_conn_unlocklist(conn->procp); + if (on) + conn->flags |= NCPFL_PRIMARY; + return 0; +} +/* + * Lease conn to given proc, returning unique handle + * problem: how locks should be applied ? + */ +int +ncp_conn_gethandle(struct ncp_conn *conn, struct proc *p, struct ncp_handle **handle){ + struct ncp_handle *refp; + + lockmgr(&lhlock, LK_EXCLUSIVE, 0, p); + SLIST_FOREACH(refp, &lhlist, nh_next) + if (refp->nh_conn == conn && p == refp->nh_proc) break; + if (refp) { + conn->ref_cnt++; + refp->nh_ref++; + *handle = refp; + lockmgr(&lhlock, LK_RELEASE, 0, p); + return 0; + } + MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,M_WAITOK); + if (refp == NULL) return ENOMEM; + bzero(refp,sizeof(*refp)); + SLIST_INSERT_HEAD(&lhlist,refp,nh_next); + refp->nh_ref++; + refp->nh_proc = p; + refp->nh_conn = conn; + refp->nh_id = ncp_next_handle++; + *handle = refp; + conn->ref_cnt++; + lockmgr(&lhlock, LK_RELEASE, 0, p); + return 0; +} +/* + * release reference, if force - ignore refcount + */ +int +ncp_conn_puthandle(struct ncp_handle *handle, struct proc *p, int force) { + struct ncp_handle *refp = handle; + + lockmgr(&lhlock, LK_EXCLUSIVE, 0, p); + refp->nh_ref--; + refp->nh_conn->ref_cnt--; + if (force) { + refp->nh_conn->ref_cnt -= refp->nh_ref; + refp->nh_ref = 0; + } + if (refp->nh_ref == 0) { + SLIST_REMOVE(&lhlist, refp, ncp_handle, nh_next); + FREE(refp, M_NCPDATA); + } + lockmgr(&lhlock, LK_RELEASE, 0, p); + return 0; +} +/* + * find a connHandle + */ +int +ncp_conn_findhandle(int connHandle, struct proc *p, struct ncp_handle **handle) { + struct ncp_handle *refp; + + lockmgr(&lhlock, LK_SHARED, 0, p); + SLIST_FOREACH(refp, &lhlist, nh_next) + if (refp->nh_proc == p && refp->nh_id == connHandle) break; + lockmgr(&lhlock, LK_RELEASE, 0, p); + if (refp == NULL) { + return EBADF; + } + *handle = refp; + return 0; +} +/* + * Clear handles associated with specified process + */ +int +ncp_conn_putprochandles(struct proc *p) { + struct ncp_handle *hp, *nhp; + int haveone = 0; + + lockmgr(&lhlock, LK_EXCLUSIVE, 0, p); + for (hp = lhlist.slh_first; hp; hp = nhp) { + nhp = hp->nh_next.sle_next; + if (hp->nh_proc != p) continue; + haveone = 1; + hp->nh_conn->ref_cnt -= hp->nh_ref; + SLIST_REMOVE(&lhlist, hp, ncp_handle, nh_next); + FREE(hp, M_NCPDATA); + } + lockmgr(&lhlock, LK_RELEASE, 0, p); + return haveone; +} +/* + * remove references in all possible connections, + * XXX - possible problem is a locked list. + */ +/*void +ncp_conn_list_rm_ref(pid_t pid) { + struct ncp_conn *ncp; + + ncp_conn_locklist(LK_SHARED, NULL); + SLIST_FOREACH(ncp, &conn_list, nc_next) { + ncp_conn_rm_ref(ncp,pid,1); + } + ncp_conn_unlocklist(NULL); + return; +} +*/ +int +ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs) { + bzero(ncs,sizeof(*ncs)); + ncs->li = ncp->li; + ncs->li.user = ncs->user; + if (ncp->li.user) + strcpy(ncs->user, ncp->li.user); + ncs->li.password = NULL; + ncs->connRef = ncp->nc_id; + ncs->ref_cnt = ncp->ref_cnt; + ncs->connid = ncp->connid; + ncs->owner = ncp->nc_owner->cr_uid; + ncs->group = ncp->nc_group; + ncs->flags = ncp->flags; + ncs->buffer_size = ncp->buffer_size; + return 0; +} + +static int +ncp_sysctl_connstat SYSCTL_HANDLER_ARGS { + int error; + struct ncp_conn_stat ncs; + struct ncp_conn *ncp; +/* struct ucred *cred = req->p->p_ucred;*/ + + error = 0; + ncp_conn_locklist(LK_SHARED, req->p); + error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt)); + SLIST_FOREACH(ncp, &conn_list, nc_next) { + if (error) break; + /* I can't do conn_lock while list is locked */ + ncp->nc_lwant++; + if (!error) { + ncp_conn_getinfo(ncp, &ncs); + } else { + bzero(&ncs,sizeof(ncs)); + ncs.connRef = ncp->nc_id; + strcpy(ncs.li.server,"***"); + } + ncp->nc_lwant--; + error = SYSCTL_OUT(req, &ncs, sizeof(ncs)); + } + ncp_conn_unlocklist(req->p); + return(error); +} diff --git a/sys/netncp/ncp_conn.h b/sys/netncp/ncp_conn.h new file mode 100644 index 0000000..8e7df8f --- /dev/null +++ b/sys/netncp/ncp_conn.h @@ -0,0 +1,238 @@ +/* + * 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 _NCP_CONN_H_ +#define _NCP_CONN_H_ + +#ifdef INET +#ifndef _NETINET_IN_H_ +#include <netinet/in.h> +#endif +#endif + +#ifdef IPX +#ifndef _NETIPX_IPX_H_ +#include <netipx/ipx.h> +#endif +#endif + +#ifndef _SYS_SOCKET_H_ +#include <sys/socket.h> +#endif + +/* type of transport we use */ +/*#define NCP_ON_IPX 0 +#define NCP_ON_TCP 1*/ + +/* flags field in conn structure */ +#define NCPFL_SOCONN 0x01 /* socket layer is up */ +#define NCPFL_ATTACHED 0x02 /* ncp layer is up */ +#define NCPFL_LOGGED 0x04 /* logged in to server */ +#define NCPFL_INVALID 0x08 /* last request was not completed */ +#define NCPFL_INTR 0x10 /* interrupted call */ +#define NCPFL_RESTORING 0x20 /* trying to reconnect */ +#define NCPFL_PERMANENT 0x40 /* no way to kill conn, when this set */ +#define NCPFL_PRIMARY 0x80 /* have meaning only for owner */ +#define NCPFL_SIGNACTIVE 0x1000 /* packet signing active */ +#define NCPFL_SIGNWANTED 0x2000 /* signing should start */ + +/* access mode for connection */ +#define NCPM_READ 0400 /* able to fetch conn params */ +#define NCPM_WRITE 0200 /* modify/close */ +#define NCPM_EXECUTE 0100 /* run requests */ + +#define NCP_DEFAULT_OWNER ((uid_t)-1) +#define NCP_DEFAULT_GROUP ((uid_t)-1) + + +/* args used to create connection */ +#define ncp_conn_loginfo ncp_conn_args +struct ncp_conn_args { + int opt; +#define NCP_OPT_WDOG 1 /* need watch dog socket */ +#define NCP_OPT_MSG 2 /* need message socket */ +#define NCP_OPT_SIGN 4 /* signatures wanted */ +#define NCP_OPT_BIND 8 /* force bindery login */ +#define NCP_OPT_PERMANENT 0x10 /* only for refernce, completly ignored */ +#define NCP_OPT_NOUPCASEPASS 0x20 /* leave password as is */ + int sig_level; /* wanted signature level */ + char server[NCP_BINDERY_NAME_LEN+1]; + char *user; + char *password; + u_int32_t objtype; + union { + struct sockaddr addr; +#ifdef IPX + struct sockaddr_ipx ipxaddr; +#endif +#ifdef INET + struct sockaddr_in inaddr; +#endif + } addr; + int timeout; /* ncp rq timeout */ + int retry_count; /* counts to give an error */ + uid_t owner; /* proposed owner of connection */ + gid_t group; /* proposed group of connection */ + mode_t access_mode; /* R/W - can do rq's, X - can see the conn */ +}; + +#define ipxaddr addr.ipxaddr +#define inaddr addr.inaddr +#define saddr addr.addr + +/* user side structure to issue ncp calls */ +struct ncp_buf { + int rqsize; /* request size without ncp header */ + int rpsize; /* reply size minus ncp header */ + int cc; /* completion code */ + int cs; /* connection state */ + char packet[NCP_MAX_PACKET_SIZE];/* Here we prepare requests and receive replies */ +}; + +/* + * Connection status, returned via sysctl(vfs.nwfs.connstat) + */ +struct ncp_conn_stat { + struct ncp_conn_args li; + int connRef; + int ref_cnt; + int connid; + int buffer_size; + int flags; + int sign_active; + uid_t owner; + gid_t group; + char user[NCP_MAXUSERNAMELEN+1]; +}; + +#ifdef KERNEL + +#ifndef LK_SHARED +#include <sys/lock.h> +#endif + +struct socket; +struct u_cred; + +SLIST_HEAD(ncp_conn_head,ncp_conn); + +struct ncp_rq; +struct ncp_conn; + +/* + * External and internal processes can reference connection only by handle. + * This gives us a freedom in maintance of underlying connections. + */ +struct ncp_handle { + SLIST_ENTRY(ncp_handle) nh_next; + int nh_id; /* handle id */ + struct ncp_conn*nh_conn; /* which conn we are refernce */ + struct proc * nh_proc; /* who owns the handle */ + int nh_ref; /* one process can asquire many handles, but we return the one */ +}; + +/* + * Describes any connection to server + */ +struct ncp_conn { + SLIST_ENTRY(ncp_conn) nc_next; + struct ncp_conn_args li; + struct ucred *nc_owner; + gid_t nc_group; + int flags; + int nc_id; + struct socket *ncp_so; + struct socket *wdg_so; + struct socket *msg_so; + struct socket *bc_so; + int ref_cnt; /* how many handles leased */ + SLIST_HEAD(ncp_ref_hd,ncp_ref) ref_list;/* list of handles */ + struct lock nc_lock; /* excl locks */ + int nc_lwant; /* number of wanted locks */ + struct proc *procp; /* pid currently operates */ + struct ucred *ucred; /* usr currently operates */ + struct ncp_rq *nc_rq; /* current request */ + /* Fields used to process ncp requests */ + int connid; /* assigned by server */ + u_int8_t seq; + int buffer_size; /* Negotiated bufsize */ + /* Fields used to make packet signatures */ + u_int32_t sign_root[2]; + u_int32_t sign_state[4]; /* md4 state */ +#ifdef NCPBURST + /* Fields used for packet bursting */ + u_long bc_pktseq; /* raw packet sequence */ + u_short bc_seq; /* burst sequence */ + u_long bc_locid; /* local connection id */ + u_long bc_remid; /* remote connection id */ + u_long bc_pktsize; /* negotiated burst packet size */ +#endif +}; + +#define ncp_conn_signwanted(conn) ((conn)->flags & NCPFL_SIGNWANTED) +#define ncp_conn_valid(conn) ((conn->flags & NCPFL_INVALID) == 0) +#define ncp_conn_invalidate(conn) {conn->flags |= NCPFL_INVALID;} + +int ncp_conn_init(void); +int ncp_conn_alloc(struct proc *p,struct ucred *cred, struct ncp_conn **connid); +int ncp_conn_free(struct ncp_conn *conn); +int ncp_conn_access(struct ncp_conn *conn,struct ucred *cred,mode_t mode); +int ncp_conn_lock(struct ncp_conn *conn,struct proc *p,struct ucred *cred,int mode); +void ncp_conn_unlock(struct ncp_conn *conn,struct proc *p); +int ncp_conn_assert_locked(struct ncp_conn *conn,char *checker,struct proc *p); +/*int ncp_conn_ref(struct ncp_conn *conn, pid_t pid); +int ncp_conn_rm_ref(struct ncp_conn *conn, pid_t pid, int force); +void ncp_conn_list_rm_ref(pid_t pid);*/ +int ncp_conn_getbyref(int connRef,struct proc *p,struct ucred *cred, int mode, + struct ncp_conn **connpp); +int ncp_conn_getbyli(struct ncp_conn_loginfo *li,struct proc *p,struct ucred *cred, + int mode, struct ncp_conn **connpp); +int ncp_conn_setprimary(struct ncp_conn *conn, int on); +int ncp_conn_locklist(int flags, struct proc *p); +void ncp_conn_unlocklist(struct proc *p); +int ncp_conn_gethandle(struct ncp_conn *conn, struct proc *p, struct ncp_handle **handle); +int ncp_conn_puthandle(struct ncp_handle *handle, struct proc *p, int force); +int ncp_conn_findhandle(int connHandle, struct proc *p, struct ncp_handle **handle); +int ncp_conn_getattached(struct ncp_conn_args *li,struct proc *p,struct ucred *cred,int mode, struct ncp_conn **connpp); +int ncp_conn_putprochandles(struct proc *p); +int ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs); + +extern struct ncp_conn_head conn_list; +extern int ncp_burst_enabled; + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_NCPDATA); +#endif + +#endif /* KERNEL */ +#endif /* _NCP_CONN_H_ */ diff --git a/sys/netncp/ncp_crypt.c b/sys/netncp/ncp_crypt.c new file mode 100644 index 0000000..9ee0665 --- /dev/null +++ b/sys/netncp/ncp_crypt.c @@ -0,0 +1,278 @@ +/* + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/poll.h> +#include <netncp/ncp.h> +#include <netncp/ncp_subr.h> + +/* + * Routines in this file based on work of Volker Lendecke + */ +/*$********************************************************* + $* + $* This code has been taken from DDJ 11/93, from an + $* article by Pawel Szczerbina. + $* + $* Password encryption routines follow. + $* Converted to C from Barry Nance's Pascal + $* prog published in the March -93 issue of Byte. + $* + $* Adapted to be useable for ncpfs by + $* Volker Lendecke <lendecke@namu01.gwdg.de> in + $* October 1995. + $* + $********************************************************* */ + + + +typedef unsigned char buf32[32]; + +static unsigned char encrypttable[256] = { +0x7, 0x8, 0x0, 0x8, 0x6, 0x4, 0xE, 0x4, 0x5, 0xC, 0x1, 0x7, 0xB, 0xF, 0xA, 0x8, +0xF, 0x8, 0xC, 0xC, 0x9, 0x4, 0x1, 0xE, 0x4, 0x6, 0x2, 0x4, 0x0, 0xA, 0xB, 0x9, +0x2, 0xF, 0xB, 0x1, 0xD, 0x2, 0x1, 0x9, 0x5, 0xE, 0x7, 0x0, 0x0, 0x2, 0x6, 0x6, +0x0, 0x7, 0x3, 0x8, 0x2, 0x9, 0x3, 0xF, 0x7, 0xF, 0xC, 0xF, 0x6, 0x4, 0xA, 0x0, +0x2, 0x3, 0xA, 0xB, 0xD, 0x8, 0x3, 0xA, 0x1, 0x7, 0xC, 0xF, 0x1, 0x8, 0x9, 0xD, +0x9, 0x1, 0x9, 0x4, 0xE, 0x4, 0xC, 0x5, 0x5, 0xC, 0x8, 0xB, 0x2, 0x3, 0x9, 0xE, +0x7, 0x7, 0x6, 0x9, 0xE, 0xF, 0xC, 0x8, 0xD, 0x1, 0xA, 0x6, 0xE, 0xD, 0x0, 0x7, +0x7, 0xA, 0x0, 0x1, 0xF, 0x5, 0x4, 0xB, 0x7, 0xB, 0xE, 0xC, 0x9, 0x5, 0xD, 0x1, +0xB, 0xD, 0x1, 0x3, 0x5, 0xD, 0xE, 0x6, 0x3, 0x0, 0xB, 0xB, 0xF, 0x3, 0x6, 0x4, +0x9, 0xD, 0xA, 0x3, 0x1, 0x4, 0x9, 0x4, 0x8, 0x3, 0xB, 0xE, 0x5, 0x0, 0x5, 0x2, +0xC, 0xB, 0xD, 0x5, 0xD, 0x5, 0xD, 0x2, 0xD, 0x9, 0xA, 0xC, 0xA, 0x0, 0xB, 0x3, +0x5, 0x3, 0x6, 0x9, 0x5, 0x1, 0xE, 0xE, 0x0, 0xE, 0x8, 0x2, 0xD, 0x2, 0x2, 0x0, +0x4, 0xF, 0x8, 0x5, 0x9, 0x6, 0x8, 0x6, 0xB, 0xA, 0xB, 0xF, 0x0, 0x7, 0x2, 0x8, +0xC, 0x7, 0x3, 0xA, 0x1, 0x4, 0x2, 0x5, 0xF, 0x7, 0xA, 0xC, 0xE, 0x5, 0x9, 0x3, +0xE, 0x7, 0x1, 0x2, 0xE, 0x1, 0xF, 0x4, 0xA, 0x6, 0xC, 0x6, 0xF, 0x4, 0x3, 0x0, +0xC, 0x0, 0x3, 0x6, 0xF, 0x8, 0x7, 0xB, 0x2, 0xD, 0xC, 0x6, 0xA, 0xA, 0x8, 0xD +}; + +static buf32 encryptkeys = { + 0x48, 0x93, 0x46, 0x67, 0x98, 0x3D, 0xE6, 0x8D, + 0xB7, 0x10, 0x7A, 0x26, 0x5A, 0xB9, 0xB1, 0x35, + 0x6B, 0x0F, 0xD5, 0x70, 0xAE, 0xFB, 0xAD, 0x11, + 0xF4, 0x47, 0xDC, 0xA7, 0xEC, 0xCF, 0x50, 0xC0 +}; + +/* + * Create table-based 16-bytes hash from a 32-bytes array + */ +static void +nw_hash(buf32 temp, unsigned char *target) { + short sum; + unsigned char b3; + int s, b2, i; + + sum = 0; + + for (b2 = 0; b2 <= 1; ++b2) { + for (s = 0; s <= 31; ++s) { + b3 = (temp[s] + sum) ^ (temp[(s + sum) & 31] - encryptkeys[s]); + sum += b3; + temp[s] = b3; + } + } + + for (i = 0; i <= 15; ++i) { + target[i] = encrypttable[temp[2 * i]] + | (encrypttable[temp[2 * i + 1]] << 4); + } +} + + +/* + * Create a 16-bytes pattern from given buffer based on a four bytes key + */ +void +nw_keyhash(const u_char *key, const u_char *buf, int buflen, u_char *target) { + int b2, d, s; + buf32 temp; + + while (buflen > 0 && buf[buflen - 1] == 0) + buflen--; + + bzero(temp, sizeof(temp)); + + d = 0; + while (buflen >= 32) { + for (s = 0; s <= 31; ++s) + temp[s] ^= buf[d++]; + buflen -= 32; + } + b2 = d; + if (buflen > 0) { + for (s = 0; s <= 31; ++s) { + if (d + buflen == b2) { + temp[s] ^= encryptkeys[s]; + b2 = d; + } else + temp[s] ^= buf[b2++]; + } + } + for (s = 0; s <= 31; ++s) + temp[s] ^= key[s & 3]; + + nw_hash(temp, target); +} + +/* + * Create an 8-bytes pattern from an 8-bytes key and 16-bytes of data + */ +void +nw_encrypt(const u_char *fra, const u_char *buf, u_char *target) { + buf32 k; + int s; + + nw_keyhash(fra, buf, 16, k); + nw_keyhash(fra + 4, buf, 16, k + 16); + + for (s = 0; s < 16; s++) + k[s] ^= k[31 - s]; + + for (s = 0; s < 8; s++) + *target++ = k[s] ^ k[15 - s]; +} + +#ifdef KERNEL +/* + * MD4 routine taken from libmd sources + */ +typedef u_int32_t UINT4; +typedef unsigned char *POINTER; + +#define PROTO_LIST(list) list + +static void Decode PROTO_LIST + ((UINT4 *, const unsigned char *, unsigned int)); + +/* Constants for MD4Transform routine. + */ +#define S11 3 +#define S12 7 +#define S13 11 +#define S14 19 +#define S21 3 +#define S22 5 +#define S23 9 +#define S24 13 +#define S31 3 +#define S32 9 +#define S33 11 +#define S34 15 + +/* F, G and H are basic MD4 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG and HH are transformations for rounds 1, 2 and 3 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s) { \ + (a) += F ((b), (c), (d)) + (x); \ + (a) = ROTATE_LEFT ((a), (s)); \ + } +#define GG(a, b, c, d, x, s) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \ + (a) = ROTATE_LEFT ((a), (s)); \ + } +#define HH(a, b, c, d, x, s) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \ + (a) = ROTATE_LEFT ((a), (s)); \ + } + +void +ncp_sign(const u_int32_t *state, const char *block, u_int32_t *ostate) { + UINT4 a, b, c, d, x[16]; + + Decode (x, block, 64); + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11); /* 1 */ + FF (d, a, b, c, x[ 1], S12); /* 2 */ + FF (c, d, a, b, x[ 2], S13); /* 3 */ + FF (b, c, d, a, x[ 3], S14); /* 4 */ + FF (a, b, c, d, x[ 4], S11); /* 5 */ + FF (d, a, b, c, x[ 5], S12); /* 6 */ + FF (c, d, a, b, x[ 6], S13); /* 7 */ + FF (b, c, d, a, x[ 7], S14); /* 8 */ + FF (a, b, c, d, x[ 8], S11); /* 9 */ + FF (d, a, b, c, x[ 9], S12); /* 10 */ + FF (c, d, a, b, x[10], S13); /* 11 */ + FF (b, c, d, a, x[11], S14); /* 12 */ + FF (a, b, c, d, x[12], S11); /* 13 */ + FF (d, a, b, c, x[13], S12); /* 14 */ + FF (c, d, a, b, x[14], S13); /* 15 */ + FF (b, c, d, a, x[15], S14); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 0], S21); /* 17 */ + GG (d, a, b, c, x[ 4], S22); /* 18 */ + GG (c, d, a, b, x[ 8], S23); /* 19 */ + GG (b, c, d, a, x[12], S24); /* 20 */ + GG (a, b, c, d, x[ 1], S21); /* 21 */ + GG (d, a, b, c, x[ 5], S22); /* 22 */ + GG (c, d, a, b, x[ 9], S23); /* 23 */ + GG (b, c, d, a, x[13], S24); /* 24 */ + GG (a, b, c, d, x[ 2], S21); /* 25 */ + GG (d, a, b, c, x[ 6], S22); /* 26 */ + GG (c, d, a, b, x[10], S23); /* 27 */ + GG (b, c, d, a, x[14], S24); /* 28 */ + GG (a, b, c, d, x[ 3], S21); /* 29 */ + GG (d, a, b, c, x[ 7], S22); /* 30 */ + GG (c, d, a, b, x[11], S23); /* 31 */ + GG (b, c, d, a, x[15], S24); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 0], S31); /* 33 */ + HH (d, a, b, c, x[ 8], S32); /* 34 */ + HH (c, d, a, b, x[ 4], S33); /* 35 */ + HH (b, c, d, a, x[12], S34); /* 36 */ + HH (a, b, c, d, x[ 2], S31); /* 37 */ + HH (d, a, b, c, x[10], S32); /* 38 */ + HH (c, d, a, b, x[ 6], S33); /* 39 */ + HH (b, c, d, a, x[14], S34); /* 40 */ + HH (a, b, c, d, x[ 1], S31); /* 41 */ + HH (d, a, b, c, x[ 9], S32); /* 42 */ + HH (c, d, a, b, x[ 5], S33); /* 43 */ + HH (b, c, d, a, x[13], S34); /* 44 */ + HH (a, b, c, d, x[ 3], S31); /* 45 */ + HH (d, a, b, c, x[11], S32); /* 46 */ + HH (c, d, a, b, x[ 7], S33); /* 47 */ + HH (b, c, d, a, x[15], S34); /* 48 */ + + ostate[0] = state[0] + a; + ostate[1] = state[1] + b; + ostate[2] = state[2] + c; + ostate[3] = state[3] + d; +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) + +UINT4 *output; +const unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +#endif /* KERNEL */ diff --git a/sys/netncp/ncp_login.c b/sys/netncp/ncp_login.c new file mode 100644 index 0000000..bac5ac5 --- /dev/null +++ b/sys/netncp/ncp_login.c @@ -0,0 +1,202 @@ +/* + * 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/errno.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/poll.h> +#include <sys/mbuf.h> +#include <sys/socket.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 <netncp/nwerror.h> + +static int ncp_login_encrypted(struct ncp_conn *conn, struct ncp_bindery_object *object, + unsigned char *key, unsigned char *passwd, + struct proc *p, struct ucred *cred); +static int ncp_login_unencrypted(struct ncp_conn *conn, u_int16_t object_type, + char *object_name, unsigned char *passwd, + struct proc *p, struct ucred *cred); +static int ncp_sign_start(struct ncp_conn *conn, char *logindata); +static int ncp_get_encryption_key(struct ncp_conn *conn, char *target); + +/* + * Initialize packet signatures. They a slightly modified MD4. + * The first 16 bytes of logindata are the shuffled password, + * the last 8 bytes the encryption key as received from the server. + */ +int +ncp_sign_start(struct ncp_conn *conn, char *logindata) { + char msg[64]; + u_int32_t state[4]; + + memcpy(msg, logindata, 24); + memcpy(msg + 24, "Authorized NetWare Client", 25); + bzero(msg + 24 + 25, sizeof(msg) - 24 - 25); + + conn->sign_state[0] = 0x67452301; + conn->sign_state[1] = 0xefcdab89; + conn->sign_state[2] = 0x98badcfe; + conn->sign_state[3] = 0x10325476; + ncp_sign(conn->sign_state, msg, state); + conn->sign_root[0] = state[0]; + conn->sign_root[1] = state[1]; + conn->flags |= NCPFL_SIGNACTIVE; + return 0; +} + +/* + * target is a 8-byte buffer + */ +int +ncp_get_encryption_key(struct ncp_conn *conn, char *target) { + int error; + DECLARE_RQ; + + NCP_RQ_HEAD_S(23,23,conn->procp,conn->ucred); + checkbad(ncp_request(conn,rqp)); + if (rqp->rpsize < 8) { + NCPFATAL("rpsize=%d < 8\n", rqp->rpsize); + return EIO; + } + ncp_rp_mem(rqp, target, 8); + NCP_RQ_EXIT; + return error; +} + +int +ncp_login_object(struct ncp_conn *conn, unsigned char *username, + int login_type, unsigned char *password, + struct proc *p,struct ucred *cred) +{ + int error; + unsigned char ncp_key[8]; + struct ncp_bindery_object user; + + if ((error = ncp_get_encryption_key(conn, ncp_key)) != 0) { + printf("%s: Warning: use unencrypted login\n", __FUNCTION__); + return ncp_login_unencrypted(conn, login_type, username, password,p,cred); + } + if ((error = ncp_get_bindery_object_id(conn, login_type, username, &user,p,cred)) != 0) { + return error; + } + error = ncp_login_encrypted(conn, &user, ncp_key, password,p,cred); + return error; +} + +int +ncp_login_encrypted(struct ncp_conn *conn, struct ncp_bindery_object *object, + unsigned char *key, unsigned char *passwd, + struct proc *p,struct ucred *cred) { + u_int32_t tmpID = htonl(object->object_id); + u_char buf[16 + 8]; + u_char encrypted[8]; + int error; + DECLARE_RQ; + + nw_keyhash((u_char*)&tmpID, passwd, strlen(passwd), buf); + nw_encrypt(key, buf, encrypted); + + NCP_RQ_HEAD_S(23,24,p,cred); + ncp_rq_mem(rqp, encrypted, 8); + ncp_rq_word_hl(rqp, object->object_type); + ncp_rq_pstring(rqp, object->object_name); + error = ncp_request(conn, rqp); + NCP_RQ_EXIT_NB; + if (conn->flags & NCPFL_SIGNWANTED) { + if (error == 0 || error == NWE_PASSWORD_EXPIRED) { + memcpy(buf + 16, key, 8); + error = ncp_sign_start(conn, buf); + } + } + return error; +} + +int +ncp_login_unencrypted(struct ncp_conn *conn, u_int16_t object_type, + char *object_name, unsigned char *passwd, + struct proc *p, struct ucred *cred) +{ + int error; + DECLARE_RQ; + + NCP_RQ_HEAD_S(23,20,conn->procp,conn->ucred); + ncp_rq_word_hl(rqp, object_type); + ncp_rq_pstring(rqp, object_name); + ncp_rq_pstring(rqp, passwd); + error = ncp_request(conn,rqp); + NCP_RQ_EXIT_NB; + return error; +} + +/* + * Login to specified server with username and password. + * conn should be locked. + */ +int +ncp_login(struct ncp_conn *conn, char *user, int objtype, char *password, + struct proc *p, struct ucred *cred) { + int error; + + if (ncp_suser(cred) != 0 && cred->cr_uid != conn->nc_owner->cr_uid) + return EACCES; + if (conn->flags & NCPFL_LOGGED) return EALREADY; + if ((conn->flags & NCPFL_ATTACHED) == 0) return ENOTCONN; + conn->li.user = ncp_str_dup(user); + conn->li.password = ncp_str_dup(password); + if (conn->li.user == NULL || conn->li.password == NULL) { + error = EINVAL; + goto bad; + } + ncp_str_upper(conn->li.user); + if ((conn->li.opt & NCP_OPT_NOUPCASEPASS) == 0) + ncp_str_upper(conn->li.password); + checkbad(ncp_login_object(conn, conn->li.user, objtype, conn->li.password,p,cred)); + conn->li.objtype = objtype; + conn->flags |= NCPFL_LOGGED; + return 0; +bad: + if (conn->li.user) free(conn->li.user, M_NCPDATA); + if (conn->li.password) free(conn->li.password, M_NCPDATA); + conn->li.user = conn->li.password = NULL; + return error; +} diff --git a/sys/netncp/ncp_mod.c b/sys/netncp/ncp_mod.c new file mode 100644 index 0000000..59325ba --- /dev/null +++ b/sys/netncp/ncp_mod.c @@ -0,0 +1,489 @@ +/* + * 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/sysproto.h> +#include <sys/exec.h> +#include <sys/sysent.h> +#include <sys/mount.h> +#include <sys/proc.h> +#include <sys/mount.h> +#include <sys/kernel.h> +#include <sys/sysctl.h> +#include <sys/malloc.h> +#include <sys/uio.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_user.h> +#include <netncp/ncp_rq.h> +#include <netncp/ncp_nls.h> + +int ncp_version = NCP_VERSION; + +static int ncp_sysent; + +SYSCTL_NODE(_net, OID_AUTO, ncp, CTLFLAG_RW, NULL, "NetWare requester"); +SYSCTL_INT(_net_ncp, OID_AUTO, sysent, CTLFLAG_RD, &ncp_sysent, 0, ""); +SYSCTL_INT(_net_ncp, OID_AUTO, version, CTLFLAG_RD, &ncp_version, 0, ""); + +static int +ncp_conn_frag_rq(struct ncp_conn *conn, struct proc *p, struct ncp_conn_frag *nfp); + +/* + * Attach to NCP server + */ +struct sncp_connect_args { + struct ncp_conn_args *li; + int *connHandle; +}; + +static int +__P(sncp_connect(struct proc *p, struct sncp_connect_args *uap)){ + int connHandle = 0, error; + struct ncp_conn *conn; + struct ncp_handle *handle; + struct ncp_conn_args li; + + checkbad(copyin(uap->li,&li,sizeof(li))); + checkbad(copyout(&connHandle,uap->connHandle,sizeof(connHandle))); /* check before */ + li.password = li.user = NULL; + error = ncp_conn_getattached(&li, p, p->p_ucred, NCPM_WRITE | NCPM_EXECUTE, &conn); + if (error) { + error = ncp_connect(&li, p, p->p_ucred, &conn); + } + if (!error) { + error = ncp_conn_gethandle(conn, p, &handle); + copyout(&handle->nh_id, uap->connHandle, sizeof(uap->connHandle)); + ncp_conn_unlock(conn,p); + } +bad: + p->p_retval[0]=error; + return error; +} + +struct sncp_request_args { + int connHandle; + int fn; + struct ncp_buf *ncpbuf; +}; + +static int ncp_conn_handler(struct proc *p, struct sncp_request_args *uap, + struct ncp_conn *conn, struct ncp_handle *handle); + +static int +__P(sncp_request(struct proc *p, struct sncp_request_args *uap)){ + int error = 0, rqsize; + struct ncp_conn *conn; + struct ncp_handle *handle; + DECLARE_RQ; + + error = ncp_conn_findhandle(uap->connHandle,p,&handle); + if (error) return error; + conn = handle->nh_conn; + if (uap->fn == NCP_CONN) + return ncp_conn_handler(p, uap, conn, handle); + error = copyin(&uap->ncpbuf->rqsize, &rqsize, sizeof(int)); + if (error) return(error); + error = ncp_conn_lock(conn,p,p->p_ucred,NCPM_EXECUTE); + if (error) return(error); + ncp_rq_head(rqp,NCP_REQUEST,uap->fn,p,p->p_ucred); + if (rqsize) + error = ncp_rq_usermem(rqp,(caddr_t)uap->ncpbuf->packet, rqsize); + if (!error) { + error = ncp_request(conn, rqp); + if (error == 0 && rqp->rpsize) + ncp_rp_usermem(rqp, (caddr_t)uap->ncpbuf->packet, + rqp->rpsize); + copyout(&rqp->cs, &uap->ncpbuf->cs, sizeof(rqp->cs)); + copyout(&rqp->cc, &uap->ncpbuf->cc, sizeof(rqp->cc)); + copyout(&rqp->rpsize, &uap->ncpbuf->rpsize, sizeof(rqp->rpsize)); + } + ncp_rq_done(rqp); + ncp_conn_unlock(conn,p); + return error; +} + +static int +ncp_conn_handler(struct proc *p, struct sncp_request_args *uap, + struct ncp_conn *conn, struct ncp_handle *hp) +{ + int error=0, rqsize, subfn; + struct ucred *cred; + + char *pdata; + + cred = p->p_ucred; + error = copyin(&uap->ncpbuf->rqsize, &rqsize, sizeof(int)); + if (error) return(error); + error = 0; + pdata = uap->ncpbuf->packet; + subfn = *(pdata++) & 0xff; + rqsize--; + switch (subfn) { + case NCP_CONN_READ: case NCP_CONN_WRITE: { + struct ncp_rw rwrq; + struct uio auio; + struct iovec iov; + + if (rqsize != sizeof(rwrq)) return (EBADRPC); + error = copyin(pdata,&rwrq,rqsize); + if (error) return (error); + iov.iov_base = rwrq.nrw_base; + iov.iov_len = rwrq.nrw_cnt; + auio.uio_iov = &iov; + auio.uio_iovcnt = 1; + auio.uio_offset = rwrq.nrw_offset; + auio.uio_resid = rwrq.nrw_cnt; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_rw = (subfn == NCP_CONN_READ) ? UIO_READ : UIO_WRITE; + auio.uio_procp = p; + error = ncp_conn_lock(conn,p,cred,NCPM_EXECUTE); + if (error) return(error); + if (subfn == NCP_CONN_READ) + error = ncp_read(conn, &rwrq.nrw_fh, &auio, cred); + else + error = ncp_write(conn, &rwrq.nrw_fh, &auio, cred); + rwrq.nrw_cnt -= auio.uio_resid; + ncp_conn_unlock(conn,p); + p->p_retval[0] = rwrq.nrw_cnt; + break; + } /* case int_read/write */ + case NCP_CONN_SETFLAGS: { + u_int16_t mask, flags; + + error = copyin(pdata,&mask, sizeof(mask)); + if (error) return error; + pdata += sizeof(mask); + error = copyin(pdata,&flags,sizeof(flags)); + if (error) return error; + error = ncp_conn_lock(conn,p,cred,NCPM_WRITE); + if (error) return error; + if (mask & NCPFL_PERMANENT) { + conn->flags &= ~NCPFL_PERMANENT; + conn->flags |= (flags & NCPFL_PERMANENT); + } + if (mask & NCPFL_PRIMARY) { + error = ncp_conn_setprimary(conn, flags & NCPFL_PRIMARY); + if (error) { + ncp_conn_unlock(conn,p); + break; + } + } + ncp_conn_unlock(conn,p); + break; + } + case NCP_CONN_LOGIN: { + struct ncp_conn_login la; + + if (rqsize != sizeof(la)) return (EBADRPC); + if ((error = copyin(pdata,&la,rqsize)) != 0) break; + error = ncp_conn_lock(conn, p, cred, NCPM_EXECUTE | NCPM_WRITE); + if (error) return error; + error = ncp_login(conn, la.username, la.objtype, la.password, p, p->p_ucred); + ncp_conn_unlock(conn, p); + p->p_retval[0] = error; + break; + } + case NCP_CONN_GETINFO: { + struct ncp_conn_stat ncs; + int len = sizeof(ncs); + + error = ncp_conn_lock(conn, p, p->p_ucred, NCPM_READ); + if (error) return error; + ncp_conn_getinfo(conn, &ncs); + copyout(&len, &uap->ncpbuf->rpsize, sizeof(int)); + error = copyout(&ncs, &uap->ncpbuf->packet, len); + ncp_conn_unlock(conn, p); + break; + } + case NCP_CONN_GETUSER: { + int len; + + error = ncp_conn_lock(conn, p, p->p_ucred, NCPM_READ); + if (error) return error; + len = (conn->li.user) ? strlen(conn->li.user) + 1 : 0; + copyout(&len, &uap->ncpbuf->rpsize, sizeof(int)); + if (len) { + error = copyout(conn->li.user, &uap->ncpbuf->packet, len); + } + ncp_conn_unlock(conn, p); + break; + } + case NCP_CONN_CONN2REF: { + int len = sizeof(int); + + error = ncp_conn_lock(conn, p, p->p_ucred, NCPM_READ); + if (error) return error; + copyout(&len, &uap->ncpbuf->rpsize, sizeof(int)); + if (len) { + error = copyout(&conn->nc_id, &uap->ncpbuf->packet, len); + } + ncp_conn_unlock(conn, p); + break; + } + case NCP_CONN_FRAG: { + struct ncp_conn_frag nf; + + if (rqsize != sizeof(nf)) return (EBADRPC); + if ((error = copyin(pdata, &nf, rqsize)) != 0) break; + error = ncp_conn_lock(conn, p, cred, NCPM_EXECUTE); + if (error) return error; + error = ncp_conn_frag_rq(conn, p, &nf); + ncp_conn_unlock(conn, p); + copyout(&nf, &pdata, sizeof(nf)); + p->p_retval[0] = error; + break; + } + case NCP_CONN_DUP: { + struct ncp_handle *newhp; + int len = sizeof(NWCONN_HANDLE); + + error = ncp_conn_lock(conn, p, cred, NCPM_READ); + if (error) break; + copyout(&len, &uap->ncpbuf->rpsize, len); + error = ncp_conn_gethandle(conn, p, &newhp); + if (!error) + error = copyout(&newhp->nh_id, uap->ncpbuf->packet, len); + ncp_conn_unlock(conn,p); + break; + } + case NCP_CONN_CONNCLOSE: { + error = ncp_conn_lock(conn, p, cred, NCPM_EXECUTE); + if (error) break; + ncp_conn_puthandle(hp, p, 0); + error = ncp_disconnect(conn); + if (error) + ncp_conn_unlock(conn, p); + break; + } + default: + error = EOPNOTSUPP; + } + return error; +} + +struct sncp_conn_scan_args { + struct ncp_conn_args *li; + int *connHandle; +}; + +static int +__P(sncp_conn_scan(struct proc *p, struct sncp_conn_scan_args *uap)){ + int connHandle = 0, error; + struct ncp_conn_args li, *lip; + struct ncp_conn *conn; + struct ncp_handle *hp; + char *user = NULL, *password = NULL; + + if (uap->li) { + if (copyin(uap->li,&li,sizeof(li))) return EFAULT; + lip = &li; + } else { + lip = NULL; + } + + if (lip != NULL) { + lip->server[sizeof(lip->server)-1]=0; /* just to make sure */ + ncp_str_upper(lip->server); + if (lip->user) { + user = ncp_str_dup(lip->user); + if (user == NULL) return EINVAL; + ncp_str_upper(user); + } + if (lip->password) { + password = ncp_str_dup(lip->password); + if (password == NULL) { + if (user) + free(user, M_NCPDATA); + return EINVAL; + } + ncp_str_upper(password); + } + lip->user = user; + lip->password = password; + } + error = ncp_conn_getbyli(lip,p,p->p_ucred,NCPM_EXECUTE,&conn); + if (!error) { /* already have this login */ + ncp_conn_gethandle(conn, p, &hp); + connHandle = hp->nh_id; + ncp_conn_unlock(conn,p); + copyout(&connHandle,uap->connHandle,sizeof(connHandle)); + } + if (user) free(user, M_NCPDATA); + if (password) free(password, M_NCPDATA); + p->p_retval[0] = error; + return error; + +} + +int +ncp_conn_frag_rq(struct ncp_conn *conn, struct proc *p, struct ncp_conn_frag *nfp){ + int error = 0, i, rpsize; + u_int32_t fsize; + NW_FRAGMENT *fp; + DECLARE_RQ; + + ncp_rq_head(rqp,NCP_REQUEST,nfp->fn,p,p->p_ucred); + if (nfp->rqfcnt) { + for(fp = nfp->rqf, i = 0; i < nfp->rqfcnt; i++, fp++) { + checkbad(ncp_rq_usermem(rqp,(caddr_t)fp->fragAddress, fp->fragSize)); + } + } + checkbad(ncp_request(conn, rqp)); + rpsize = rqp->rpsize; + if (rpsize && nfp->rpfcnt) { + for(fp = nfp->rpf, i = 0; i < nfp->rpfcnt; i++, fp++) { + checkbad(copyin(&fp->fragSize, &fsize, sizeof (fsize))); + fsize = min(fsize, rpsize); + checkbad(ncp_rp_usermem(rqp,(caddr_t)fp->fragAddress, fsize)); + rpsize -= fsize; + checkbad(copyout(&fsize, &fp->fragSize, sizeof (fsize))); + } + } + nfp->cs = rqp->cs; + nfp->cc = rqp->cc; + NCP_RQ_EXIT; + return error; +} + +/* + * Internal functions, here should be all calls that do not require connection. + * To simplify possible future movement to cdev, we use IOCTL macros. + * Pretty much of this stolen from ioctl() function. + */ +struct sncp_intfn_args { + u_long com; + caddr_t data; +}; + +static int +sncp_intfn(struct proc *p, struct sncp_intfn_args *uap) +{ + return ENOSYS; +} +/* + * define our new system calls + */ +static struct sysent newent[] = { + {2, (sy_call_t*)sncp_conn_scan}, + {2, (sy_call_t*)sncp_connect}, + {2, (sy_call_t*)sncp_intfn}, + {3, (sy_call_t*)sncp_request} +}; + +#define SC_SIZE sizeof(newent)/sizeof(struct sysent) +/* + * Miscellaneous modules must have their own save areas... + */ +static struct sysent oldent[SC_SIZE]; /* save are for old callslot entry*/ + +/* + * Number of syscall entries for a.out executables + */ +/*#define nsysent SYS_MAXSYSCALL*/ +#define nsysent (aout_sysvec.sv_size) + + +static int +ncp_load(void) { + int i, ff, scnt, err=0; + + while(1) { + /* Search the table looking for an enough number of slots... */ + for (scnt=0, ff = -1, i = 0; i < nsysent; i++) { + if (sysent[i].sy_call == (sy_call_t *)lkmnosys) { + if (ff == -1) { + ff = i; + scnt = 1; + } else { + scnt++; + if (scnt == SC_SIZE) break; + } + } else { + ff = -1; + } + } + /* out of allocable slots?*/ + if(i == nsysent || ff == -1) { + err = ENFILE; + break; + } + err = ncp_init(); + if (err) break; + bcopy(&sysent[ff], &oldent, sizeof(struct sysent)*SC_SIZE); + bcopy(&newent, &sysent[ff], sizeof(struct sysent)*SC_SIZE); + ncp_sysent = ff; /* slot in sysent[]*/ + printf("ncp_load: [%d-%d]\n",ff,i); + break; + } + + return( err); +} + +static int +ncp_unload(void) { + ncp_done(); + bcopy(&oldent, &sysent[ncp_sysent], sizeof(struct sysent) * SC_SIZE); + printf( "ncp_unload: unloaded\n"); + return 0; +} + +static int +ncp_mod_handler(module_t mod, int type, void *data) +{ + int error; + + switch (type) { + case MOD_LOAD: + error = ncp_load(); + break; + case MOD_UNLOAD: + error = ncp_unload(); + break; + default: + error = EINVAL; + } + return error; +} + \ +static moduledata_t ncp_mod = { + "ncp", + ncp_mod_handler, + NULL +}; +DECLARE_MODULE(ncp, ncp_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); diff --git a/sys/netncp/ncp_ncp.c b/sys/netncp/ncp_ncp.c new file mode 100644 index 0000000..943c0ca --- /dev/null +++ b/sys/netncp/ncp_ncp.c @@ -0,0 +1,613 @@ +/* + * 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$ + * + * Core of NCP protocol + */ +#include "opt_inet.h" +#include "opt_ipx.h" + +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/poll.h> +#include <sys/signalvar.h> +#include <sys/socketvar.h> +#include <sys/mbuf.h> + +#ifdef IPX +#include <netipx/ipx.h> +#include <netipx/ipx_var.h> +#endif + +#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/nwerror.h> + +static int ncp_do_request(struct ncp_conn *,struct ncp_rq *rqp); +static int ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target); +static int ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options); +static void ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size); + + +#ifdef NCP_DATA_DEBUG +static +void m_dumpm(struct mbuf *m) { + char *p; + int len; + printf("d="); + while(m) { + p=mtod(m,char *); + len=m->m_len; + printf("(%d)",len); + while(len--){ + printf("%02x ",((int)*(p++)) & 0xff); + } + m=m->m_next; + }; + printf("\n"); +} +#endif /* NCP_DATA_DEBUG */ + +int +ncp_chkintr(struct ncp_conn *conn, struct proc *p) +{ + sigset_t tmpset; + + if (p == NULL) + return 0; + tmpset = p->p_siglist; + SIGSETNAND(tmpset, p->p_sigmask); + SIGSETNAND(tmpset, p->p_sigignore); + if (SIGNOTEMPTY(p->p_siglist) && NCP_SIGMASK(tmpset)) + return EINTR; + return 0; +} + +/* + * Process initial NCP handshake (attach) + * NOTE: Since all functions below may change conn attributes, they + * should be called with LOCKED connection, also they use procp & ucred + */ +int +ncp_ncp_connect(struct ncp_conn *conn) { + int error; + struct ncp_rphdr *rp; + DECLARE_RQ; + + conn->flags &= ~(NCPFL_INVALID | NCPFL_SIGNACTIVE | NCPFL_SIGNWANTED); + conn->seq = 0; + checkbad(ncp_rq_head(rqp,NCP_ALLOC_SLOT,0,conn->procp,conn->ucred)); + error=ncp_do_request(conn,rqp); + if (!error) { + rp = mtod(rqp->rp, struct ncp_rphdr*); + conn->connid = rp->conn_low + (rp->conn_high << 8); + } + ncp_rq_done(rqp); + if (error) return error; + conn->flags |= NCPFL_ATTACHED; + + error = ncp_renegotiate_connparam(conn, NCP_DEFAULT_BUFSIZE, 0); + if (error == NWE_SIGNATURE_LEVEL_CONFLICT) { + printf("Unable to negotiate requested security level\n"); + error = EOPNOTSUPP; + } + if (error) { + ncp_ncp_disconnect(conn); + return error; + } +#ifdef NCPBURST + ncp_burst_connect(conn); +#endif +bad: + return error; +} + +int +ncp_ncp_disconnect(struct ncp_conn *conn) { + int error; + struct ncp_rqhdr *ncprq; + DECLARE_RQ; + + NCPSDEBUG("for connid=%d\n",conn->nc_id); +#ifdef NCPBURST + ncp_burst_disconnect(conn); +#endif + error=ncp_rq_head(rqp,NCP_FREE_SLOT,0,conn->procp,conn->ucred); + ncprq = mtod(rqp->rq,struct ncp_rqhdr*); + error=ncp_do_request(conn,rqp); + ncp_rq_done(rqp); + ncp_conn_invalidate(conn); + ncp_sock_disconnect(conn); + return 0; +} +/* + * Make a signature for the current packet and add it at the end of the + * packet. + */ +static void +ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size) { + u_char data[64]; + + bzero(data, sizeof(data)); + bcopy(conn->sign_root, data, 8); + setdle(data, 8, *size); + m_copydata(rqp->rq, sizeof(struct ncp_rqhdr)-1, + min((*size) - sizeof(struct ncp_rqhdr)+1, 52),data+12); + ncp_sign(conn->sign_state, data, conn->sign_state); + ncp_rq_mem(rqp, (void*)conn->sign_state, 8); + (*size) += 8; +} + +/* + * Low level send rpc, here we do not attempt to restore any connection, + * Connection expected to be locked + */ +static int +ncp_do_request(struct ncp_conn *conn, struct ncp_rq *rqp) { + int error=EIO,len, dosend, plen = 0, gotpacket, s; + struct socket *so; + struct proc *p = conn->procp; + struct ncp_rqhdr *rq; + struct ncp_rphdr *rp=NULL; + struct timeval tv; + struct mbuf *m, *mreply = NULL; + + conn->nc_rq = rqp; + if (p == NULL) + p = curproc; /* XXX maybe procpage ? */ + if (!ncp_conn_valid(conn)) { + printf("%s: conn not valid\n",__FUNCTION__); + return (error); + } + so = conn->ncp_so; + if (!so) { + printf("%s: ncp_so is NULL !\n",__FUNCTION__); + ncp_conn_invalidate(conn); /* wow ! how we do that ? */ + return EBADF; + } + /* + * Flush out replies on previous reqs + */ + s = splnet(); + while (1/*so->so_rcv.sb_cc*/) { + if (ncp_poll(so,POLLIN) == 0) break; + if (ncp_sock_recv(so,&m,&len) != 0) break; + m_freem(m); + } + rq = mtod(rqp->rq,struct ncp_rqhdr *); + rq->seq = conn->seq; + m = rqp->rq; + len = 0; + while (m) { + len += m->m_len; + m = m->m_next; + } + rqp->rq->m_pkthdr.len = len; + switch(rq->fn) { + case 0x15: case 0x16: case 0x17: case 0x23: + m = rqp->rq; + *((u_int16_t*)(mtod(m,u_int8_t*)+sizeof(*rq))) = htons(len-2-sizeof(*rq)); + break; + } + if (conn->flags & NCPFL_SIGNACTIVE) { + ncp_sign_packet(conn, rqp, &len); + rqp->rq->m_pkthdr.len = len; + } + rq->conn_low = conn->connid & 0xff; + /* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/ + /* XXX: this is temporary fix till I find a better solution */ + rq->task = rq->conn_low; + rq->conn_high = conn->connid >> 8; + rqp->rexmit = conn->li.retry_count; + for(dosend = 1;;) { + if (rqp->rexmit-- == 0) { + error = ETIMEDOUT; + break; + } + error = 0; + if (dosend) { + NCPSDEBUG("send:%04x f=%02x c=%d l=%d s=%d t=%d\n",rq->type, rq->fn, (rq->conn_high << 8) + rq->conn_low, + rqp->rq->m_pkthdr.len, rq->seq, rq->task + ); + error = ncp_sock_send(so, rqp->rq, rqp); + if (error) break; + } + tv.tv_sec = conn->li.timeout; + tv.tv_usec = 0; + error = ncp_sock_rselect(so, p, &tv, POLLIN); + if (error == EWOULDBLOCK ) /* timeout expired */ + continue; + error = ncp_chkintr(conn, p); + if (error == EINTR) /* we dont restart */ + break; + if (error) break; + /* + * At this point it is possible to get more than one + * reply from server. In general, last reply should be for + * current request, but not always. So, we loop through + * all replies to find the right answer and flush others. + */ + gotpacket = 0; /* nothing good found */ + dosend = 1; /* resend rq if error */ + for (;;) { + error = 0; + if (ncp_poll(so,POLLIN) == 0) break; +/* if (so->so_rcv.sb_cc == 0) { + break; + }*/ + error = ncp_sock_recv(so,&m,&len); + if (error) break; /* must be more checks !!! */ + if (m->m_len < sizeof(*rp)) { + m = m_pullup(m, sizeof(*rp)); + if (m == NULL) { + printf("%s: reply too short\n",__FUNCTION__); + continue; + } + } + rp = mtod(m, struct ncp_rphdr*); + if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) { + NCPSDEBUG("got positive acknowledge\n"); + m_freem(m); + rqp->rexmit = conn->li.retry_count; + dosend = 0; /* server just busy and will reply ASAP */ + continue; + } + NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type, + (rp->conn_high << 8) + rp->conn_low, len, rp->seq, rp->task, + rp->completion_code, rp->connection_state); + NCPDDEBUG(m); + if ( (rp->type == NCP_REPLY) && + ((rq->type == NCP_ALLOC_SLOT) || + ((rp->conn_low == rq->conn_low) && + (rp->conn_high == rq->conn_high) + ))) { + if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) { + dosend = 1; + } + if (rp->seq == rq->seq) { + if (gotpacket) { + m_freem(m); + } else { + gotpacket = 1; + mreply = m; + plen = len; + } + continue; /* look up other for other packets */ + } + } + m_freem(m); + NCPSDEBUG("reply mismatch\n"); + } /* for receive */ + if (error) break; + if (gotpacket) break; + /* try to resend, or just wait */ + } + splx(s); + conn->seq++; + if (error) { + NCPSDEBUG("error=%d\n",error); + if (error != EINTR) /* if not just interrupt */ + ncp_conn_invalidate(conn); /* only reconnect to restore */ + return(error); + } + if (conn->flags & NCPFL_SIGNACTIVE) { + /* XXX: check reply signature */ + m_adj(mreply, -8); + plen -= 8; + } + len = plen; + m = mreply; + rp = mtod(m, struct ncp_rphdr*); + len -= sizeof(*rp); + rqp->rpsize = len; + rqp->cc = error = rp->completion_code; + if (error) error |= 0x8900; /* server error */ + rqp->cs = rp->connection_state; + if (rqp->cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) { + NCPSDEBUG("server drop us\n"); + ncp_conn_invalidate(conn); + error = ECONNRESET; + } + rqp->rp = m; + rqp->mrp = m; + rqp->bpos = mtod(m, caddr_t) + sizeof(*rp); + return error; +} + +/* + * Here we will try to restore any loggedin & dropped connection, + * connection should be locked on entry + */ +int ncp_restore_login(struct ncp_conn *conn); +int +ncp_restore_login(struct ncp_conn *conn) { + int error, oldflags; + + if (conn->flags & NCPFL_RESTORING) { + printf("Hey, ncp_restore_login called twise !!!\n"); + return 0; + } + oldflags = conn->flags; + printf("Restoring connection, flags = %d\n",oldflags); + if ((oldflags & NCPFL_LOGGED) == 0) { + return ECONNRESET; /* no need to restore empty conn */ + } + conn->flags &= ~(NCPFL_LOGGED | NCPFL_ATTACHED); + conn->flags |= NCPFL_RESTORING; + do { /* not a loop */ + error = ncp_reconnect(conn); + if (error) break; + if (conn->li.user) + error = ncp_login_object(conn, conn->li.user, conn->li.objtype, conn->li.password,conn->procp,conn->ucred); + if (error) break; + conn->flags |= NCPFL_LOGGED; + } while(0); + if (error) { + conn->flags = oldflags | NCPFL_INVALID; + } + conn->flags &= ~NCPFL_RESTORING; + return error; +} + +int +ncp_request(struct ncp_conn *conn, struct ncp_rq *rqp) { + int error, rcnt; +/* struct ncp_rqhdr *rq = mtod(rqp->rq,struct ncp_rqhdr*);*/ + + error = ncp_conn_lock(conn,rqp->p,rqp->cred,NCPM_EXECUTE); + if (error) return error; + rcnt = NCP_RESTORE_COUNT; + for(;;) { + if (!ncp_conn_valid(conn)) { + if (rcnt==0) { + error = ECONNRESET; + break; + } + rcnt--; + error = ncp_restore_login(conn); + if (error) + continue; + } + error=ncp_do_request(conn, rqp); + if (ncp_conn_valid(conn)) /* not just error ! */ + break; + } + ncp_conn_unlock(conn,rqp->p); + return error; +} + +/* + * All negotiation functions expect a locked connection + */ +static int +ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target) { + int error; + DECLARE_RQ; + + NCP_RQ_HEAD(0x21,conn->procp,conn->ucred); + ncp_rq_word_hl(rqp, size); + checkbad(ncp_request(conn,rqp)); + *target = min(ncp_rp_word_hl(rqp), size); + NCP_RQ_EXIT; + return error; +} + +static int +ncp_negotiate_size_and_options(struct ncp_conn *conn, int size, int options, + int *ret_size, int *ret_options) { + int error; + int rs; + DECLARE_RQ; + + NCP_RQ_HEAD(0x61,conn->procp,conn->ucred); + ncp_rq_word_hl(rqp, size); + ncp_rq_byte(rqp, options); + checkbad(ncp_request(conn, rqp)); + rs = ncp_rp_word_hl(rqp); + *ret_size = (rs == 0) ? size : min(rs, size); + ncp_rp_word_hl(rqp); /* skip echo socket */ + *ret_options = ncp_rp_byte(rqp); + NCP_RQ_EXIT; + return error; +} + +static int +ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options) +{ + int neg_buffsize, error, options, sl; + + sl = conn->li.sig_level; + if (sl >= 2) + in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS; +#ifdef IPX + if (ipxcksum == 2) + in_options |= NCP_IPX_CHECKSUM; +#endif + error = ncp_negotiate_size_and_options(conn, buffsize, in_options, + &neg_buffsize, &options); + if (!error) { +#ifdef IPX + if ((options ^ in_options) & NCP_IPX_CHECKSUM) { + if (ipxcksum == 2) { + printf("Server refuses to support IPX checksums\n"); + return NWE_REQUESTER_FAILURE; + } + in_options |= NCP_IPX_CHECKSUM; + error = 1; + } +#endif /* IPX */ + if ((options ^ in_options) & 2) { + if (sl == 0 || sl == 3) + return NWE_SIGNATURE_LEVEL_CONFLICT; + if (sl == 1) { + in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS; + error = 1; + } + } + if (error) { + error = ncp_negotiate_size_and_options(conn, + buffsize, in_options, &neg_buffsize, &options); + if ((options ^ in_options) & 3) { + return NWE_SIGNATURE_LEVEL_CONFLICT; + } + } + } else { + in_options &= ~NCP_SECURITY_LEVEL_SIGN_HEADERS; + error = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE, + &neg_buffsize); + } + if (error) return error; + if ((neg_buffsize < 512) || (neg_buffsize > NCP_MAX_BUFSIZE)) + return EINVAL; + conn->buffer_size = neg_buffsize; + if (in_options & NCP_SECURITY_LEVEL_SIGN_HEADERS) + conn->flags |= NCPFL_SIGNWANTED; +#ifdef IPX + ncp_sock_checksum(conn, in_options & NCP_IPX_CHECKSUM); +#endif + return 0; +} + +int +ncp_reconnect(struct ncp_conn *conn) { + int error; + + /* close any open sockets */ + ncp_sock_disconnect(conn); + switch( conn->li.saddr.sa_family ) { +#ifdef IPX + case AF_IPX: + error = ncp_sock_connect_ipx(conn); + break; +#endif +#ifdef INET + case AF_INET: + error = ncp_sock_connect_in(conn); + break; +#endif + default: + return EPROTONOSUPPORT; + } + if (!error) + error = ncp_ncp_connect(conn); + return error; +} + +/* + * Create conn structure and try to do low level connect + * Server addr should be filled in. + */ +int +ncp_connect(struct ncp_conn_args *li, struct proc *p, struct ucred *cred, + struct ncp_conn **aconn) +{ + struct ncp_conn *conn; + struct ucred *owner; + int error, isroot; + + if (li->saddr.sa_family != AF_INET && li->saddr.sa_family != AF_IPX) + return EPROTONOSUPPORT; + isroot = ncp_suser(cred) == 0; + /* + * Only root can change ownership + */ + if (li->owner != NCP_DEFAULT_OWNER && !isroot) + return EPERM; + if (li->group != NCP_DEFAULT_GROUP && + !groupmember(li->group, cred) && !isroot) + return EPERM; + if (li->owner != NCP_DEFAULT_OWNER) { + owner = crget(); + owner->cr_uid = li->owner; + } else { + owner = cred; + crhold(owner); + } + error = ncp_conn_alloc(p, owner, &conn); + if (error) + return (error); + if (error) { + ncp_conn_free(conn); + return error; + } + conn->li = *li; + conn->nc_group = (li->group != NCP_DEFAULT_GROUP) ? + li->group : cred->cr_groups[0]; + + if (li->retry_count == 0) + conn->li.retry_count = NCP_RETRY_COUNT; + if (li->timeout == 0) + conn->li.timeout = NCP_RETRY_TIMEOUT; + error = ncp_reconnect(conn); + if (error) { + ncp_disconnect(conn); + } else { + *aconn=conn; + } + return error; +} +/* + * Break connection and deallocate memory + */ +int +ncp_disconnect(struct ncp_conn *conn) { + + if (ncp_conn_access(conn,conn->ucred,NCPM_WRITE)) + return EACCES; + if (conn->ref_cnt != 0) return EBUSY; + if (conn->flags & NCPFL_PERMANENT) return EBUSY; + if (ncp_conn_valid(conn)) { + ncp_ncp_disconnect(conn); + } + ncp_sock_disconnect(conn); + ncp_conn_free(conn); + return 0; +} + +void +ncp_check_rq(struct ncp_conn *conn){ + return; + if (conn->flags & NCPFL_INTR) return; + /* first, check for signals */ + if (ncp_chkintr(conn,conn->procp)) { + conn->flags |= NCPFL_INTR; + } + return; +} diff --git a/sys/netncp/ncp_ncp.h b/sys/netncp/ncp_ncp.h new file mode 100644 index 0000000..1d5b479 --- /dev/null +++ b/sys/netncp/ncp_ncp.h @@ -0,0 +1,118 @@ +/* + * 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 _NCP_NCP_H_ +#define _NCP_NCP_H_ + +#define NCP_ALLOC_SLOT 0x1111 +#define NCP_REQUEST 0x2222 +#define NCP_REPLY 0x3333 +#define NCP_FREE_SLOT 0x5555 +#define NCP_PACKET_BURST 0x7777 +#define NCP_POSITIVE_ACK 0x9999 + +/* + * Bits for connection state field in ncp_rphdr + */ +#define NCP_CS_BAD_CONN 0x01 /* no such connection */ +#define NCP_CS_NO_SLOTS 0x04 /* no connection slots available */ +#define NCP_CS_SERVER_DOWN 0x10 /* server in down state */ +#define NCP_CS_HAVE_BROADCAST 0x40 /* server holds broadcast for us */ + +#define NCP_RETRY_COUNT 5 +#define NCP_RETRY_TIMEOUT 10 +#define NCP_RESTORE_COUNT 2 /* how many times try to restore per + * single request, should be an _even_ */ + +struct ncp_rqhdr { + u_int16_t type; + u_int8_t seq; + u_int8_t conn_low; + u_int8_t task; + u_int8_t conn_high; + u_int8_t fn; + u_int8_t data[0]; +} __attribute__((packed)); + + +struct ncp_rphdr { + u_int16_t type; + u_int8_t seq; + u_int8_t conn_low; + u_int8_t task; + u_int8_t conn_high; + u_int8_t completion_code; + u_int8_t connection_state; + u_int8_t data[0]; +}__attribute__((packed)); + +#define BFL_ABT 0x04 +#define BFL_EOB 0x10 +#define BFL_SYS 0x80 + +#define BOP_READ 1L +#define BOP_WRITE 2L + +#define BERR_NONE 0 +#define BERR_INIT 1 +#define BERR_IO 2 +#define BERR_NODATA 3 +#define BERR_WRITE 4 + +struct ncp_bursthdr { + u_short bh_type; + u_char bh_flags; + u_char bh_streamtype; + u_long bh_srcid; + u_long bh_dstid; + u_long bh_seq; /* HL */ + u_long bh_send_delay; /* HL */ + u_short bh_bseq; /* HL */ + u_short bh_aseq; /* HL */ + u_long bh_blen; /* HL */ + u_long bh_dofs; /* HL */ + u_short bh_dlen; /* HL */ + u_short bh_misfrags; /* HL */ +} __attribute__((packed)); + + +int ncp_request(struct ncp_conn *conn,struct ncp_rq *rqp); +int ncp_ncp_connect(struct ncp_conn *conn); +int ncp_ncp_disconnect(struct ncp_conn *conn); +int ncp_reconnect(struct ncp_conn *conn); +int ncp_connect(struct ncp_conn_args *li,struct proc *p, struct ucred *cred,struct ncp_conn **aconn); +int ncp_disconnect(struct ncp_conn *conn); +int ncp_login(struct ncp_conn *conn, char *user, int objtype, char *password, + struct proc *p, struct ucred *cred); + +#endif /* _NCP_NCP_H_ */ diff --git a/sys/netncp/ncp_nls.c b/sys/netncp/ncp_nls.c new file mode 100644 index 0000000..5606030 --- /dev/null +++ b/sys/netncp/ncp_nls.c @@ -0,0 +1,305 @@ +/* + * 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. + * + * Character conversion routines + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/time.h> +#include <sys/lock.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_subr.h> +#include <netncp/ncp_nls.h> + +/* + * 0 - character disallowed in NetWare file name. + */ +static u_char ncp_u2n[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x21, 0x00, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20 */ + 0x28, 0x29, 0x00, 0x00, 0x00, 0x2d, 0x2e, 0x00, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30 */ + 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50 */ + 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x5e, 0x5f, + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70 */ + 0x78, 0x79, 0x7a, 0x7b, 0x00, 0x7d, 0x7e, 0x00, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80 */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90 */ + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0 */ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xbf, 0xbf, 0xbf, 0xbf, 0xb1, 0xb2, 0xb7, /* 0xb0 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0 */ + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +static u_char ncp_n2u[256] = { + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, /* 0x00 */ + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, /* 0x10 */ + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x5f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50 */ + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70 */ + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80 */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90 */ + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0 */ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xbf, 0xbf, 0xbf, 0xbf, 0xb1, 0xb2, 0xb7, /* 0xb0 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0 */ + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +static u_char ncp_u2l[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50 */ + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70 */ + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80 */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90 */ + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0 */ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0 */ + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +static u_char ncp_l2u[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */ + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 60-67 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 70-77 */ + 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80 */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90 */ + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0 */ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0 */ + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +struct ncp_nlstables ncp_defnls = { + ncp_u2l, ncp_l2u, ncp_n2u, ncp_u2n, 0 +}; + +void ncp_str_upper(char *name) { + while (*name) { + *name = ncp_defnls.toupper[(u_char)*name]; + name++; + } +} + +void ncp_str_lower(char *name) { + while (*name) { + *name = ncp_defnls.tolower[(u_char)*name]; + name++; + } +} + +/* + * Check if pathname is valid under given conditions. + */ +int +ncp_pathcheck(char *s, int len, struct ncp_nlstables *nt, int strict) { + u_char *tbl = NULL, sc; + int opt = nt->opt; + + if (opt & (NWHP_UPPER | NWHP_LOWER)) + tbl = (opt & NWHP_UPPER) ? nt->toupper : nt->tolower; + if ((opt & NWHP_DOS) == 0) { + while (len--) { + sc = (u_char)*(s++); + if (nt->u2n[sc] == 0) /* illegal char */ + return EINVAL; + if (tbl && strict && tbl[sc] != sc) + return EINVAL; + } + return 0; + } + while (len--) { + sc = (u_char)*(s++); + if (nt->u2n[sc] == 0) /* illegal char */ + return EINVAL; + if (sc == ' ') return EINVAL; + if (tbl && strict && tbl[sc] != sc) { + return EINVAL; + } + } + return 0; +} +/* + * Convert name from Unix to NetWare representation. + * XXX: it should be complementary with path2unix, but for now + * leave it as is. + */ +void +ncp_pathcopy(char *src, char *dst, int len, struct ncp_nlstables *nt) { + int donls; + u_char c; +/* char *d = dst, *s = src;*/ + + donls = (nt && (nt->opt & NWHP_NLS)); + if ((nt->opt & (NWHP_UPPER | NWHP_LOWER)) == 0) { + while (len--) { + *dst = donls ? nt->u2n[(u_char)*src] : *src; + dst++; + src++; + } + } else if (nt->opt & NWHP_DOS) { + while (len--) { + c = nt->toupper[(u_char)*src]; + *dst = donls ? nt->u2n[c] : c; + dst++; + src++; + } + return; + } else { /* probably incorrect... */ + while (len--) { + *dst = donls ? nt->u2n[(u_char)*src] : *src; + dst++; + src++; + } + } +/* printf("fromux: %s:%s\n", s, d);*/ +} + +/* + * Convert NetWare filename to Unix with optional conversions + */ +void +ncp_path2unix(char *src, char *dst, int len, struct ncp_nlstables *nt) { + int donls; + u_char c, *tbl; +/* char *d = dst, *s = src;*/ + +/* printf("toux(%02x): %s:",nt->opt, s);*/ + donls = (nt && (nt->opt & NWHP_NLS)); + if ((nt->opt & (NWHP_UPPER | NWHP_LOWER)) == 0) { + while (len--) { + c = *src; + *dst = donls ? nt->n2u[c] : c; + dst++; + src++; + } + return; + } + tbl = (nt->opt & NWHP_LOWER) ? nt->tolower : nt->toupper; + while (len--) { + c = *src; + *dst = tbl[donls ? nt->n2u[c] : c]; + dst++; + src++; + } +/* printf("%s\n", d);*/ +} diff --git a/sys/netncp/ncp_nls.h b/sys/netncp/ncp_nls.h new file mode 100644 index 0000000..1bec4a7 --- /dev/null +++ b/sys/netncp/ncp_nls.h @@ -0,0 +1,86 @@ +/* + * 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 _NCP_NCP_NLS_H_ +#define _NCP_NCP_NLS_H_ + +/* options for handle path & caseopt in mount struct */ +#define NWHP_HDB 0x01 /* have dir base */ +#define NWHP_UPPER 0x02 /* local names has upper case */ +#define NWHP_LOWER 0x04 /* --"-- lower case */ +#define NWHP_DOS 0x08 /* using dos name space */ +#define NWHP_NLS 0x10 /* do name translation via tables, any nmspc */ +#define NWHP_NOSTRICT 0x20 /* pretend to be a case insensitive */ + +struct ncp_nlstables { + u_char *tolower; /* local charset to lower case */ + u_char *toupper; /* local charset to upper case */ + u_char *n2u; /* NetWare to Unix */ + u_char *u2n; + int opt; /* may depend on context */ +}; + +#ifndef KERNEL +/* + * NLS, supported character conversion schemes. + * NCP_NLS_UNIXCHARSET_NETWARECHARSET + */ +#define NCP_NLS_AS_IS 1 +#define NCP_NLS_AS_IS_NAME "asis" +#define NCP_NLS_KOI_866 2 +#define NCP_NLS_KOI_866_NAME "koi2cp866" + +extern struct ncp_nlstables ncp_nls; /* active nls */ + +int ncp_nls_setrecode(int scheme); +int ncp_nls_setrecodebyname(char *name); +int ncp_nls_setlocale(char *name); +char* ncp_nls_str_n2u(char *dst, const char *src); +char* ncp_nls_str_u2n(char *dst, const char *src); +char* ncp_nls_mem_n2u(char *dst, const char *src, int size); +char* ncp_nls_mem_u2n(char *dst, const char *src, int size); + +#else /* !KERNEL */ + + +extern struct ncp_nlstables ncp_defnls; + +void ncp_str_upper(char *name); +void ncp_str_lower(char *name); +void ncp_pathcopy(char *src, char *dst, int len, struct ncp_nlstables *nt); +int ncp_pathcheck(char *s, int len, struct ncp_nlstables *nt, int strict); +void ncp_path2unix(char *src, char *dst, int len, struct ncp_nlstables *nt); + +#endif /* !KERNEL */ + +#endif /* _NCP_NCP_NLS_H_ */
\ No newline at end of file diff --git a/sys/netncp/ncp_rq.c b/sys/netncp/ncp_rq.c new file mode 100644 index 0000000..8cef7d2 --- /dev/null +++ b/sys/netncp/ncp_rq.c @@ -0,0 +1,591 @@ +/* + * 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. + * + * Routines to prepare request and fetch reply + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/mbuf.h> +#include <sys/uio.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_conn.h> +#include <netncp/ncp_rq.h> +#include <netncp/ncp_subr.h> +#include <netncp/ncp_ncp.h> +#include <netncp/ncp_nls.h> + +static struct mbuf* m_getm(struct mbuf *top, int len); + +int +ncp_rq_head(struct ncp_rq *rqp, u_int32_t ptype, u_int8_t fn,struct proc *p, + struct ucred *cred) +{ + struct mbuf *m; + struct ncp_rqhdr *rq; + struct ncp_bursthdr *brq; + caddr_t pstart; + + bzero(rqp, sizeof(*rqp)); + rqp->p = p; + rqp->cred = cred; + m = m_gethdr(M_WAIT, MT_DATA); + if (m == NULL) + return ENOBUFS; /* if M_WAIT ? */ + m->m_pkthdr.rcvif = NULL; + rqp->rq = rqp->mrq = m; + rqp->rp = NULL; + switch(ptype) { + case NCP_PACKET_BURST: + MH_ALIGN(m, sizeof(*brq) + 24); + m->m_len = sizeof(*brq); + brq = mtod(m, struct ncp_bursthdr *); + brq->bh_type = ptype; + brq->bh_streamtype = 0x2; + pstart = (caddr_t)brq; + break; + default: + MH_ALIGN(m, sizeof(*rq) + 2); /* possible len field in some functions */ + m->m_len = sizeof(*rq); + rq = mtod(m, struct ncp_rqhdr *); + rq->type = ptype; + rq->seq = 0; /* filled later */ + rq->fn = fn; + pstart = (caddr_t)rq; + break; + } + rqp->bpos = pstart + m->m_len; + return 0; +} + +int +ncp_rq_done(struct ncp_rq *rqp) { + m_freem(rqp->rq); + rqp->rq=NULL; + if (rqp->rp) m_freem(rqp->rp); + rqp->rp=NULL; + return (0); +} + +static struct mbuf* +m_getm(struct mbuf *top, int len) { + int rest = len, mlen; + struct mbuf *m=0,*mp; + + mp = top; + while (rest > 0) { +/* NCPSDEBUG("%d\n",rest);*/ + m = m_get(M_WAIT, MT_DATA); + if (rest > MINCLSIZE) { + MCLGET(m,M_WAIT); + mlen = ( (m->m_flags & M_EXT) == 0) ? MLEN : MCLBYTES; + } else { + mlen = MLEN; + } + m->m_len = 0/*min(mlen,rest)*/; + mp->m_next = m; + mp = m; + rest -= mlen; + } + return top; +} + +/* + * Routines to fill the request + */ +static caddr_t ncp_mchecksize(struct ncp_rq *rqp, int size); +#define NCP_RQADD(t) ((t*)(ncp_mchecksize(rqp,sizeof(t)))) + +caddr_t +ncp_mchecksize(struct ncp_rq *rqp, int size) { + caddr_t bpos1; + + if (size>MLEN) + panic("ncp_mchecksize\n"); + if (M_TRAILINGSPACE(rqp->mrq)<(size)) { + struct mbuf *m; + m = m_get(M_WAIT, MT_DATA); + m->m_len = 0; + rqp->bpos = mtod(m, caddr_t); + rqp->mrq->m_next = m; + rqp->mrq = m; + } + rqp->mrq->m_len += size; + bpos1 = rqp->bpos; + rqp->bpos += size; + return bpos1; +} + +void +ncp_rq_byte(struct ncp_rq *rqp,u_int8_t x) { + *NCP_RQADD(u_int8_t)=x; +} + +void +ncp_rq_word_hl(struct ncp_rq *rqp, u_int16_t x) { + setwbe(NCP_RQADD(u_int16_t), 0, x); +} + +void +ncp_rq_word_lh(struct ncp_rq *rqp, u_int16_t x) { + setwle(NCP_RQADD(u_int16_t), 0, x); +} + +void +ncp_rq_dword_lh(struct ncp_rq *rqp, u_int32_t x) { + setdle(NCP_RQADD(u_int32_t), 0, x); +} + +void +ncp_rq_pathstring(struct ncp_rq *rqp, int size, char *name, struct ncp_nlstables *nt) { + struct mbuf *m; + int cplen; + + ncp_rq_byte(rqp, size); + m = rqp->mrq; + cplen = min(size, M_TRAILINGSPACE(m)); + if (cplen) { + ncp_pathcopy(name, rqp->bpos, cplen, nt); + size -= cplen; + name += cplen; + m->m_len += cplen; + } + if (size) { + m_getm(m, size); + while (size > 0){ + m = m->m_next; + cplen = min(size, M_TRAILINGSPACE(m)); + ncp_pathcopy(name, mtod(m, caddr_t) + m->m_len, cplen, nt); + size -= cplen; + name += cplen; + m->m_len += cplen; + } + } + rqp->bpos = mtod(m,caddr_t) + m->m_len; + rqp->mrq = m; + return; +} + +int +ncp_rq_putanymem(struct ncp_rq *rqp, caddr_t source, int size, int type) { + struct mbuf *m; + int cplen, error; + + m = rqp->mrq; + cplen = min(size, M_TRAILINGSPACE(m)); + if (cplen) { + if (type==1) { + error = copyin(source, rqp->bpos, cplen); + if (error) return error; + } else + bcopy(source, rqp->bpos, cplen); + size -= cplen; + source += cplen; + m->m_len += cplen; + } + if (size) { + m_getm(m, size); + while (size > 0){ + m = m->m_next; + cplen = min(size, M_TRAILINGSPACE(m)); + if (type==1) { + error = copyin(source, mtod(m, caddr_t) + m->m_len, cplen); + if (error) return error; + } else + bcopy(source, mtod(m, caddr_t) + m->m_len, cplen); + size -= cplen; + source += cplen; + m->m_len += cplen; + } + } + rqp->bpos = mtod(m,caddr_t) + m->m_len; + rqp->mrq = m; + return 0; +} + +int +ncp_rq_mbuf(struct ncp_rq *rqp, struct mbuf *m, int size) { + + rqp->mrq->m_next = m; + m->m_next = NULL; + if (size != M_COPYALL) m->m_len = size; + rqp->bpos = mtod(m,caddr_t) + m->m_len; + rqp->mrq = m; + return 0; +} + +void +ncp_rq_pstring(struct ncp_rq *rqp, char *s) { + int len = strlen(s); + if (len > 255) { + nwfs_printf("string too long: %s\n", s); + len = 255; + } + ncp_rq_byte(rqp, len); + ncp_rq_mem(rqp, s, len); + return; +} + +void +ncp_rq_dbase_path(struct ncp_rq *rqp, u_int8_t vol_num, u_int32_t dir_base, + int namelen, u_char *path, struct ncp_nlstables *nt) +{ + int complen; + + ncp_rq_byte(rqp, vol_num); + ncp_rq_dword(rqp, dir_base); + ncp_rq_byte(rqp, 1); /* with dirbase */ + if (path != NULL && path[0]) { + if (namelen < 0) { + namelen = *path++; + ncp_rq_byte(rqp, namelen); + for(; namelen; namelen--) { + complen = *path++; + ncp_rq_byte(rqp, complen); + ncp_rq_mem(rqp, path, complen); + path += complen; + } + } else { + ncp_rq_byte(rqp, 1); /* 1 component */ + ncp_rq_pathstring(rqp, namelen, path, nt); + } + } else { + ncp_rq_byte(rqp, 0); + ncp_rq_byte(rqp, 0); + } +} +/* + * fetch reply routines + */ +#define ncp_mspaceleft (mtod(rqp->mrp,caddr_t)+rqp->mrp->m_len-rqp->bpos) + +u_int8_t +ncp_rp_byte(struct ncp_rq *rqp) { + if (rqp->mrp == NULL) return 0; + if (ncp_mspaceleft < 1) { + rqp->mrp = rqp->mrp->m_next; + if (rqp->mrp == NULL) return 0; + rqp->bpos = mtod(rqp->mrp, caddr_t); + } + rqp->bpos += 1; + return rqp->bpos[-1]; +} + +u_int16_t +ncp_rp_word_lh(struct ncp_rq *rqp) { + caddr_t prev = rqp->bpos; + u_int16_t t; + + if (rqp->mrp == NULL) return 0; + if (ncp_mspaceleft >= 2) { + rqp->bpos += 2; + return getwle(prev,0); + } + t = *((u_int8_t*)(rqp->bpos)); + rqp->mrp = rqp->mrp->m_next; + if (rqp->mrp == NULL) return 0; + ((u_int8_t *)&t)[1] = *((u_int8_t*)(rqp->bpos = mtod(rqp->mrp, caddr_t))); + rqp->bpos += 2; + return t; +} + +u_int16_t +ncp_rp_word_hl(struct ncp_rq *rqp) { + return (ntohs(ncp_rp_word_lh(rqp))); +} + +u_int32_t +ncp_rp_dword_hl(struct ncp_rq *rqp) { + int togo, rest; + caddr_t prev = rqp->bpos; + u_int32_t t; + + if (rqp->mrp == NULL) return 0; + rest = ncp_mspaceleft; + if (rest >= 4) { + rqp->bpos += 4; + return getdbe(prev,0); + } + togo = 0; + while (rest--) { + ((u_int8_t *)&t)[togo++] = *((u_int8_t*)(prev++)); + } + rqp->mrp = rqp->mrp->m_next; + if (rqp->mrp == NULL) return 0; + prev = mtod(rqp->mrp, caddr_t); + rqp->bpos = prev + 4 - togo; /* XXX possible low than togo bytes in next mbuf */ + while (togo < 4) { + ((u_int8_t *)&t)[togo++] = *((u_int8_t*)(prev++)); + } + return getdbe(&t,0); +} + +u_int32_t +ncp_rp_dword_lh(struct ncp_rq *rqp) { + int rest, togo; + caddr_t prev = rqp->bpos; + u_int32_t t; + + if (rqp->mrp == NULL) return 0; + rest = ncp_mspaceleft; + if (rest >= 4) { + rqp->bpos += 4; + return getdle(prev,0); + } + togo = 0; + while (rest--) { + ((u_int8_t *)&t)[togo++] = *((u_int8_t*)(prev++)); + } + rqp->mrp = rqp->mrp->m_next; + if (rqp->mrp == NULL) return 0; + prev = mtod(rqp->mrp, caddr_t); + rqp->bpos = prev + 4 - togo; /* XXX possible low than togo bytes in next mbuf */ + while (togo < 4) { + ((u_int8_t *)&t)[togo++] = *((u_int8_t*)(prev++)); + } + return getdle(&t,0); +} +void +ncp_rp_mem(struct ncp_rq *rqp,caddr_t target, int size) { + register struct mbuf *m=rqp->mrp; + register unsigned count; + + while (size > 0) { + if (m==0) { /* should be panic */ + printf("ncp_rp_mem: incomplete copy\n"); + return; + } + count = mtod(m,caddr_t)+m->m_len-rqp->bpos; + if (count == 0) { + m=m->m_next; + rqp->bpos=mtod(m,caddr_t); + continue; + } + count = min(count,size); + bcopy(rqp->bpos, target, count); + size -= count; + target += count; + rqp->bpos += count; + } + rqp->mrp=m; + return; +} + +int +ncp_rp_usermem(struct ncp_rq *rqp,caddr_t target, int size) { + register struct mbuf *m=rqp->mrp; + register unsigned count; + int error; + + while (size>0) { + if (m==0) { /* should be panic */ + printf("ncp_rp_mem: incomplete copy\n"); + return EFAULT; + } + count = mtod(m,caddr_t)+m->m_len-rqp->bpos; + if (count == 0) { + m=m->m_next; + rqp->bpos=mtod(m,caddr_t); + continue; + } + count = min(count,size); + error=copyout(rqp->bpos, target, count); + if (error) return error; + size -= count; + target += count; + rqp->bpos += count; + } + rqp->mrp=m; + return 0; +} + +struct mbuf* +ncp_rp_mbuf(struct ncp_rq *rqp, int size) { + register struct mbuf *m=rqp->mrp, *rm; + register unsigned count; + + rm = m_copym(m, rqp->bpos - mtod(m,caddr_t), size, M_WAIT); + while (size > 0) { + if (m == 0) { + printf("ncp_rp_mbuf: can't advance\n"); + return rm; + } + count = mtod(m,caddr_t)+ m->m_len - rqp->bpos; + if (count == 0) { + m = m->m_next; + rqp->bpos = mtod(m, caddr_t); + continue; + } + count = min(count, size); + size -= count; + rqp->bpos += count; + } + rqp->mrp=m; + return rm; +} + +int +nwfs_mbuftouio(mrep, uiop, siz, dpos) + struct mbuf **mrep; + register struct uio *uiop; + int siz; + caddr_t *dpos; +{ + register char *mbufcp, *uiocp; + register int xfer, left, len; + register struct mbuf *mp; + long uiosiz; + int error = 0; + + mp = *mrep; + if (!mp) return 0; + mbufcp = *dpos; + len = mtod(mp, caddr_t)+mp->m_len-mbufcp; + while (siz > 0) { + if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) + return (EFBIG); + left = uiop->uio_iov->iov_len; + uiocp = uiop->uio_iov->iov_base; + if (left > siz) + left = siz; + uiosiz = left; + while (left > 0) { + while (len == 0) { + mp = mp->m_next; + if (mp == NULL) + return (EBADRPC); + mbufcp = mtod(mp, caddr_t); + len = mp->m_len; + } + xfer = (left > len) ? len : left; +#ifdef notdef + /* Not Yet.. */ + if (uiop->uio_iov->iov_op != NULL) + (*(uiop->uio_iov->iov_op)) + (mbufcp, uiocp, xfer); + else +#endif + if (uiop->uio_segflg == UIO_SYSSPACE) + bcopy(mbufcp, uiocp, xfer); + else + copyout(mbufcp, uiocp, xfer); + left -= xfer; + len -= xfer; + mbufcp += xfer; + uiocp += xfer; + uiop->uio_offset += xfer; + uiop->uio_resid -= xfer; + } + if (uiop->uio_iov->iov_len <= siz) { + uiop->uio_iovcnt--; + uiop->uio_iov++; + } else { + uiop->uio_iov->iov_base += uiosiz; + uiop->uio_iov->iov_len -= uiosiz; + } + siz -= uiosiz; + } + *dpos = mbufcp; + *mrep = mp; + return (error); +} +/* + * copies a uio scatter/gather list to an mbuf chain. + * NOTE: can ony handle iovcnt == 1 + */ +int +nwfs_uiotombuf(uiop, mq, siz, bpos) + register struct uio *uiop; + struct mbuf **mq; + int siz; + caddr_t *bpos; +{ + register char *uiocp; + register struct mbuf *mp, *mp2; + register int xfer, left, mlen; + int uiosiz, clflg; + +#ifdef DIAGNOSTIC + if (uiop->uio_iovcnt != 1) + panic("nfsm_uiotombuf: iovcnt != 1"); +#endif + + if (siz > MLEN) /* or should it >= MCLBYTES ?? */ + clflg = 1; + else + clflg = 0; + mp = mp2 = *mq; + while (siz > 0) { + left = uiop->uio_iov->iov_len; + uiocp = uiop->uio_iov->iov_base; + if (left > siz) + left = siz; + uiosiz = left; + while (left > 0) { + mlen = M_TRAILINGSPACE(mp); + if (mlen == 0) { + MGET(mp, M_WAIT, MT_DATA); + if (clflg) + MCLGET(mp, M_WAIT); + mp->m_len = 0; + mp2->m_next = mp; + mp2 = mp; + mlen = M_TRAILINGSPACE(mp); + } + xfer = (left > mlen) ? mlen : left; +#ifdef notdef + /* Not Yet.. */ + if (uiop->uio_iov->iov_op != NULL) + (*(uiop->uio_iov->iov_op)) + (uiocp, mtod(mp, caddr_t)+mp->m_len, xfer); + else +#endif + if (uiop->uio_segflg == UIO_SYSSPACE) + bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer); + else + copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer); + mp->m_len += xfer; + left -= xfer; + uiocp += xfer; + uiop->uio_offset += xfer; + uiop->uio_resid -= xfer; + } + uiop->uio_iov->iov_base += uiosiz; + uiop->uio_iov->iov_len -= uiosiz; + siz -= uiosiz; + } + *bpos = mtod(mp, caddr_t)+mp->m_len; + *mq = mp; + return (0); +} diff --git a/sys/netncp/ncp_rq.h b/sys/netncp/ncp_rq.h new file mode 100644 index 0000000..259fed2 --- /dev/null +++ b/sys/netncp/ncp_rq.h @@ -0,0 +1,191 @@ +/* + * 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 _NCP_NCP_RQ_H_ +#define _NCP_NCP_RQ_H_ + +#include <machine/endian.h> + +#define getb(buf,ofs) (((const u_int8_t *)(buf))[ofs]) +#define setb(buf,ofs,val) (((u_int8_t*)(buf))[ofs])=val +#define getbw(buf,ofs) ((u_int16_t)(getb(buf,ofs))) + +#if (BYTE_ORDER == LITTLE_ENDIAN) + +#define getwle(buf,ofs) (*((u_int16_t*)(&((u_int8_t*)(buf))[ofs]))) +#define getdle(buf,ofs) (*((u_int32_t*)(&((u_int8_t*)(buf))[ofs]))) +#define getwbe(buf,ofs) (ntohs(getwle(buf,ofs))) +#define getdbe(buf,ofs) (ntohl(getdle(buf,ofs))) + +#define setwle(buf,ofs,val) getwle(buf,ofs)=val +#define setwbe(buf,ofs,val) getwle(buf,ofs)=htons(val) +#define setdle(buf,ofs,val) getdle(buf,ofs)=val +#define setdbe(buf,ofs,val) getdle(buf,ofs)=htonl(val) + +#define htoles(x) ((u_int16_t)(x)) +#define letohs(x) ((u_int16_t)(x)) +#define htolel(x) ((u_int32_t)(x)) +#define letohl(x) ((u_int32_t)(x)) + +#else +#error "Macros for Big-Endians are incomplete" +#define getwle(buf,ofs) ((u_int16_t)(getb(buf, ofs) | (getb(buf, ofs + 1) << 8))) +#define getdle(buf,ofs) ((u_int32_t)(getb(buf, ofs) | \ + (getb(buf, ofs + 1) << 8) | \ + (getb(buf, ofs + 2) << 16) | \ + (getb(buf, ofs + 3) << 24))) +#define getwbe(buf,ofs) (*((u_int16_t*)(&((u_int8_t*)(buf))[ofs]))) +#define getdbe(buf,ofs) (*((u_int32_t*)(&((u_int8_t*)(buf))[ofs]))) +/* +#define setwle(buf,ofs,val) getwle(buf,ofs)=val +#define setdle(buf,ofs,val) getdle(buf,ofs)=val +*/ +#define setwbe(buf,ofs,val) getwle(buf,ofs)=val +#define setdbe(buf,ofs,val) getdle(buf,ofs)=val +/* +#define htoles(x) ((u_int16_t)(x)) +#define letohs(x) ((u_int16_t)(x)) +#define htolel(x) ((u_int32_t)(x)) +#define letohl(x) ((u_int32_t)(x)) +*/ +#endif + + +#ifdef KERNEL +struct ncp_nlstables; +/* + * Structure to prepare ncp request and receive reply + */ +struct ncp_rq { + struct ncp_conn *conn; /* back link */ + struct mbuf *rq; + struct mbuf *mrq; + struct mbuf *rp; + struct mbuf *mrp; + caddr_t bpos; +/* int rqsize;*/ /* request size without ncp header */ + int rpsize; /* reply size minus ncp header */ + int cc; /* completion code */ + int cs; /* connection state */ + struct proc *p; /* proc that did rq */ + struct ucred *cred; /* user that did rq */ + int rexmit; +}; + +#define DECLARE_RQ struct ncp_rq rq;struct ncp_rq *rqp=&rq + + +int ncp_rq_head(struct ncp_rq *rqp,u_int32_t ptype, u_int8_t fn,struct proc *p, + struct ucred *cred); +int ncp_rq_done(struct ncp_rq *rqp); + +/* common case for normal request */ +#define ncp_rq_init(rqp,fn,p,c) ncp_rq_head((rqp),NCP_REQUEST,(fn),(p),(c)) +#define ncp_rq_close(rqp) ncp_rq_done((rqp)) + +#define NCP_RQ_HEAD(fn,p,c) ncp_rq_init(rqp,fn,p,c) +#define NCP_RQ_HEAD_S(fn,sfn,p,c) NCP_RQ_HEAD(fn,p,c);ncp_rq_word(rqp,0);ncp_rq_byte(rqp,(sfn)) +#define NCP_RQ_EXIT bad: ncp_rq_close(rqp) +#define NCP_RQ_EXIT_NB ncp_rq_close(rqp) +#define ncp_rq_word ncp_rq_word_lh +#define ncp_rq_dword ncp_rq_dword_lh + +/*void ncp_init_request(struct ncp_rq *rqp, int fn); +void ncp_close_request(struct ncp_rq *rqp);*/ +void ncp_rq_byte(struct ncp_rq *rqp, u_int8_t x); +void ncp_rq_word_hl(struct ncp_rq *rqp, u_int16_t x); +void ncp_rq_word_lh(struct ncp_rq *rqp, u_int16_t x); +void ncp_rq_dword_lh(struct ncp_rq *rqp, u_int32_t x); +static void ncp_rq_mem(struct ncp_rq *rqp, caddr_t source, int size); +static int ncp_rq_usermem(struct ncp_rq *rqp, caddr_t source, int size); +int ncp_rq_mbuf(struct ncp_rq *rqp, struct mbuf *m, int size); +int ncp_rq_putanymem(struct ncp_rq *rqp, caddr_t source, int size,int type); +void ncp_rq_pathstring(struct ncp_rq *rqp, int size, char *name, struct ncp_nlstables*); +void ncp_rq_dbase_path(struct ncp_rq *, u_int8_t vol_num, + u_int32_t dir_base, int namelen, u_char *name, struct ncp_nlstables *nt); +void ncp_rq_pstring(struct ncp_rq *rqp, char *s); + +u_int8_t ncp_rp_byte(struct ncp_rq *rqp); +u_int16_t ncp_rp_word_hl(struct ncp_rq *rqp); +u_int16_t ncp_rp_word_lh(struct ncp_rq *rqp); +u_int32_t ncp_rp_dword_hl(struct ncp_rq *rqp); +u_int32_t ncp_rp_dword_lh(struct ncp_rq *rqp); +void ncp_rp_mem(struct ncp_rq *rqp,caddr_t target, int size); +int ncp_rp_usermem(struct ncp_rq *rqp,caddr_t target, int size); +int nwfs_mbuftouio(struct mbuf **mrep, struct uio *uiop, int siz, caddr_t *dpos); +int nwfs_uiotombuf(struct uio *uiop, struct mbuf **mq, int siz, caddr_t *bpos); +struct mbuf* ncp_rp_mbuf(struct ncp_rq *rqp, int size); + +static void __inline ncp_rq_mem(struct ncp_rq *rqp, caddr_t source, int size) { + ncp_rq_putanymem(rqp,source,size,0); +} +static int __inline ncp_rq_usermem(struct ncp_rq *rqp, caddr_t source, int size) { + return ncp_rq_putanymem(rqp,source,size,1); +} +void ncp_sign_init(const char *logindata, char *sign_root); + +#else /* ifdef KERNEL */ + +#define DECLARE_RQ struct ncp_buf conn1, *conn=&conn1 + +#define ncp_add_byte(conn,x) (conn)->packet[(conn)->rqsize++]=x + +void ncp_init_request(struct ncp_buf *conn); +void ncp_init_request_s(struct ncp_buf *conn, int subfn); +void ncp_add_word_lh(struct ncp_buf *conn, u_int16_t x); +void ncp_add_dword_lh(struct ncp_buf *conn, u_int32_t x); +void ncp_add_word_hl(struct ncp_buf *conn, u_int16_t x); +void ncp_add_dword_hl(struct ncp_buf *conn, u_int32_t x); +void ncp_add_mem(struct ncp_buf *conn, const void *source, int size); +void ncp_add_mem_nls(struct ncp_buf *conn, const void *source, int size); +void ncp_add_pstring(struct ncp_buf *conn, const char *s); +void ncp_add_handle_path(struct ncp_buf *conn, nuint32 volNumber, nuint32 dirNumber, + int handleFlag, const char *path); + +#define ncp_reply_data(conn,offset) ((conn)->packet+offset) +#define ncp_reply_byte(conn,offset) (*(u_int8_t*)(ncp_reply_data(conn, offset))) + +u_int16_t ncp_reply_word_hl(struct ncp_buf *conn, int offset); +u_int16_t ncp_reply_word_lh(struct ncp_buf *conn, int offset); +u_int32_t ncp_reply_dword_hl(struct ncp_buf *conn, int offset); +u_int32_t ncp_reply_dword_lh(struct ncp_buf *conn, int offset); + +static __inline void +ConvertToNWfromDWORD(u_int32_t sfd, ncp_fh *fh) { + fh->val1 = (fh->val.val32 = sfd); + return; +} + +#endif /* ifdef KERNEL */ + +#endif /* _NCP_NCP_RQ_H_ */ diff --git a/sys/netncp/ncp_sock.c b/sys/netncp/ncp_sock.c new file mode 100644 index 0000000..bd3c4f0 --- /dev/null +++ b/sys/netncp/ncp_sock.c @@ -0,0 +1,442 @@ +/* + * 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$ + * + * Low level socket routines + */ + +#include "opt_inet.h" +#include "opt_ipx.h" + +#if !defined(INET) && !defined(IPX) +#error "NCP requeires either INET of IPX protocol family" +#endif + +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/protosw.h> +#include <sys/kernel.h> +#include <sys/poll.h> +#include <sys/uio.h> +#include <sys/file.h> +#include <sys/syslog.h> +#include <sys/mbuf.h> +#include <net/route.h> + +#ifdef IPX +#include <netipx/ipx.h> +#include <netipx/ipx_pcb.h> +#endif + +#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> + +#ifdef IPX +#define ipx_setnullnet(x) ((x).x_net.s_net[0]=0); ((x).x_net.s_net[1]=0); +#define ipx_setnullhost(x) ((x).x_host.s_host[0] = 0); \ + ((x).x_host.s_host[1] = 0); ((x).x_host.s_host[2] = 0); +#endif + +/*int ncp_poll(struct socket *so, int events);*/ +/*static int ncp_getsockname(struct socket *so, caddr_t asa, int *alen);*/ +static int ncp_soconnect(struct socket *so,struct sockaddr *target, struct proc *p); + + +/* This will need only if native IP used, or (unlikely) NCP will be + * implemented on the socket level + */ +static int +ncp_soconnect(struct socket *so,struct sockaddr *target, struct proc *p) { + int error,s; + + error = soconnect(so, (struct sockaddr*)target, p); + if (error) + return error; + /* + * Wait for the connection to complete. Cribbed from the + * connect system call but with the wait timing out so + * that interruptible mounts don't hang here for a long time. + */ + error = EIO; + s = splnet(); + while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { + (void) tsleep((caddr_t)&so->so_timeo, PSOCK, "ncpcon", 2 * hz); + if ((so->so_state & SS_ISCONNECTING) && + so->so_error == 0 /*&& rep &&*/) { + so->so_state &= ~SS_ISCONNECTING; + splx(s); + goto bad; + } + } + if (so->so_error) { + error = so->so_error; + so->so_error = 0; + splx(s); + goto bad; + } + splx(s); + error=0; +bad: + return error; +} +#ifdef notyet +static int +ncp_getsockname(struct socket *so, caddr_t asa, int *alen) { + struct sockaddr *sa; + int len=0, error; + + sa = 0; + error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, &sa); + if (error==0) { + if (sa) { + len = min(len, sa->sa_len); + bcopy(sa, (caddr_t)asa, (u_int)len); + } + *alen=len; + } + if (sa) + FREE(sa, M_SONAME); + return (error); +} +#endif +int ncp_sock_recv(struct socket *so, struct mbuf **mp, int *rlen) +{ + struct uio auio; + struct proc *p=curproc; /* XXX */ + int error,flags,len; + + auio.uio_resid = len = 1000000; + auio.uio_procp = p; + flags = MSG_DONTWAIT; + +/* error = so->so_proto->pr_usrreqs->pru_soreceive(so, 0, &auio, + (struct mbuf **)0, (struct mbuf **)0, &flags);*/ + error = so->so_proto->pr_usrreqs->pru_soreceive(so, 0, &auio, + mp, (struct mbuf **)0, &flags); + *rlen = len - auio.uio_resid; +/* if (!error) { + *rlen=iov.iov_len; + } else + *rlen=0;*/ +#ifdef NCP_SOCKET_DEBUG + if (error) + printf("ncp_recv: err=%d\n", error); +#endif + return (error); +} + +int +ncp_sock_send(struct socket *so, struct mbuf *top, struct ncp_rq *rqp) +{ + struct proc *p = curproc; /* XXX */ + struct sockaddr *to = 0; + struct ncp_conn *conn = rqp->conn; + struct mbuf *m; + int error, flags=0; + int sendwait; + + for(;;) { + m = m_copym(top, 0, M_COPYALL, M_WAIT); +/* NCPDDEBUG(m);*/ + error = so->so_proto->pr_usrreqs->pru_sosend(so, to, 0, m, 0, flags, p); + if (error == 0 || error == EINTR || error == ENETDOWN) + break; + if (rqp->rexmit == 0) break; + rqp->rexmit--; + tsleep(&sendwait, PWAIT, "ncprsn", conn->li.timeout * hz); + error = ncp_chkintr(conn, p); + if (error == EINTR) break; + } + if (error) { + log(LOG_INFO, "ncp_send: error %d for server %s", error, conn->li.server); + } + return error; +} + +int +ncp_poll(struct socket *so, int events){ + struct proc *p = curproc; + struct ucred *cred=NULL; + return so->so_proto->pr_usrreqs->pru_sopoll(so, events, cred, p); +} + +int +ncp_sock_rselect(struct socket *so,struct proc *p, struct timeval *tv, int events) +{ + struct timeval atv,rtv,ttv; + int s,timo,error=0; + + if (tv) { + atv=*tv; + if (itimerfix(&atv)) { + error = EINVAL; + goto done; + } + getmicrouptime(&rtv); + timevaladd(&atv, &rtv); + } + timo = 0; +retry: + p->p_flag |= P_SELECT; + error = ncp_poll(so, events); + if (error) { + error = 0; + goto done; + } + if (tv) { + getmicrouptime(&rtv); + if (timevalcmp(&rtv, &atv, >=)) + goto done; + ttv=atv; + timevalsub(&ttv, &rtv); + timo = tvtohz(&ttv); + } + s = splhigh(); + if ((p->p_flag & P_SELECT) == 0) { + splx(s); + goto retry; + } + p->p_flag &= ~P_SELECT; + error = tsleep((caddr_t)&selwait, PSOCK, "ncpslt", timo); + splx(s); +done: + p->p_flag &= ~P_SELECT; + if (error == ERESTART) { +/* printf("Signal: %x", CURSIG(p));*/ + error = 0; + } + return (error); +} + +#ifdef IPX +/* + * Connect to specified server via IPX + */ +int +ncp_sock_connect_ipx(struct ncp_conn *conn) { + struct sockaddr_ipx sipx; + struct ipxpcb *npcb; + struct proc *p = conn->procp; + int addrlen, error, count; + + sipx.sipx_port = htons(0); + + for (count = 0;;count++) { + if (count > (IPXPORT_WELLKNOWN-IPXPORT_RESERVED)*2) { + error = EADDRINUSE; + goto bad; + } + conn->ncp_so = conn->wdg_so = NULL; + checkbad(socreate(AF_IPX, &conn->ncp_so, SOCK_DGRAM, 0, p)); + if (conn->li.opt & NCP_OPT_WDOG) + checkbad(socreate(AF_IPX, &conn->wdg_so, SOCK_DGRAM,0,p)); + addrlen = sizeof(sipx); + sipx.sipx_family = AF_IPX; + ipx_setnullnet(sipx.sipx_addr); + ipx_setnullhost(sipx.sipx_addr); + sipx.sipx_len = addrlen; + error = sobind(conn->ncp_so, (struct sockaddr *)&sipx, p); + if (error == 0) { + if ((conn->li.opt & NCP_OPT_WDOG) == 0) + break; + sipx.sipx_addr = sotoipxpcb(conn->ncp_so)->ipxp_laddr; + sipx.sipx_port = htons(ntohs(sipx.sipx_port) + 1); + ipx_setnullnet(sipx.sipx_addr); + ipx_setnullhost(sipx.sipx_addr); + error = sobind(conn->wdg_so, (struct sockaddr *)&sipx, p); + } + if (!error) break; + if (error != EADDRINUSE) goto bad; + sipx.sipx_port = htons((ntohs(sipx.sipx_port)+4) & 0xfff8); + soclose(conn->ncp_so); + if (conn->wdg_so) + soclose(conn->wdg_so); + } + npcb = sotoipxpcb(conn->ncp_so); + npcb->ipxp_dpt = IPXPROTO_NCP; + /* IPXrouted must be running, i.e. route must be presented */ + conn->li.ipxaddr.sipx_len = sizeof(struct sockaddr_ipx); + checkbad(ncp_soconnect(conn->ncp_so, &conn->li.saddr, p)); + if (conn->wdg_so) { + sotoipxpcb(conn->wdg_so)->ipxp_laddr.x_net = npcb->ipxp_laddr.x_net; + sotoipxpcb(conn->wdg_so)->ipxp_laddr.x_host= npcb->ipxp_laddr.x_host; + } + if (!error) { + conn->flags |= NCPFL_SOCONN; + } +#ifdef NCPBURST + if (ncp_burst_enabled) { + checkbad(socreate(AF_IPX, &conn->bc_so, SOCK_DGRAM, 0, p)); + bzero(&sipx, sizeof(sipx)); + sipx.sipx_len = sizeof(sipx); + checkbad(sobind(conn->bc_so, (struct sockaddr *)&sipx, p)); + checkbad(ncp_soconnect(conn->bc_so, &conn->li.saddr, p)); + } +#endif + if (!error) { + conn->flags |= NCPFL_SOCONN; + ncp_sock_checksum(conn, 0); + } + return error; +bad: + ncp_sock_disconnect(conn); + return (error); +} + +int +ncp_sock_checksum(struct ncp_conn *conn, int enable) { + +#ifdef SO_IPX_CHECKSUM + if (enable) { + sotoipxpcb(conn->ncp_so)->ipxp_flags |= IPXP_CHECKSUM; + } else { + sotoipxpcb(conn->ncp_so)->ipxp_flags &= ~IPXP_CHECKSUM; + } +#endif + return 0; +} +#endif + +#ifdef INET +/* + * Connect to specified server via IP + */ +int +ncp_sock_connect_in(struct ncp_conn *conn) { + struct sockaddr_in sin; + struct proc *p=conn->procp; + int addrlen=sizeof(sin), error; + + conn->flags = 0; + bzero(&sin,addrlen); + conn->ncp_so = conn->wdg_so = NULL; + checkbad(socreate(AF_INET, &conn->ncp_so, SOCK_DGRAM, IPPROTO_UDP, p)); + sin.sin_family = AF_INET; + sin.sin_len = addrlen; + checkbad(sobind(conn->ncp_so, (struct sockaddr *)&sin, p)); + checkbad(ncp_soconnect(conn->ncp_so,(struct sockaddr*)&conn->li.addr, p)); + if (!error) + conn->flags |= NCPFL_SOCONN; + return error; +bad: + ncp_sock_disconnect(conn); + return (error); +} +#endif + + +/* + * Connection expected to be locked + */ +int +ncp_sock_disconnect(struct ncp_conn *conn) { + register struct socket *so; + conn->flags &= ~(NCPFL_SOCONN | NCPFL_ATTACHED | NCPFL_LOGGED); + if (conn->ncp_so) { + so = conn->ncp_so; + conn->ncp_so = (struct socket *)0; + soshutdown(so, 2); + soclose(so); + } + if (conn->wdg_so) { + so = conn->wdg_so; + conn->wdg_so = (struct socket *)0; + soshutdown(so, 2); + soclose(so); + } +#ifdef NCPBURST + if (conn->bc_so) { + so = conn->bc_so; + conn->bc_so = (struct socket *)NULL; + soshutdown(so, 2); + soclose(so); + } +#endif + return 0; +} + +#ifdef IPX +static void +ncp_watchdog(struct ncp_conn *conn) { + char *buf; + struct mbuf *m; + int error, len, flags; + struct socket *so; + struct sockaddr *sa; + struct uio auio; + + sa = NULL; + while (conn->wdg_so) { /* not a loop */ + so = conn->wdg_so; + auio.uio_resid = len = 1000000; + auio.uio_procp = curproc; + flags = MSG_DONTWAIT; + error = so->so_proto->pr_usrreqs->pru_soreceive(so, + (struct sockaddr**)&sa, &auio, &m, (struct mbuf**)0, &flags); + if (error) break; + len -= auio.uio_resid; + NCPSDEBUG("got watch dog %d\n",len); + if (len != 2) break; + buf = mtod(m, char*); + if (buf[1] != '?') break; + buf[1] = 'Y'; + error = so->so_proto->pr_usrreqs->pru_sosend(so, (struct sockaddr*)sa, 0, m, 0, 0, curproc); + NCPSDEBUG("send watch dog %d\n",error); + break; + } + if (sa) FREE(sa, M_SONAME); + return; +} +#endif /* IPX */ + +void +ncp_check_conn(struct ncp_conn *conn) { + int s; + + if (conn == NULL || !(conn->flags & NCPFL_ATTACHED)) + return; + s = splnet(); + ncp_check_rq(conn); + splx(s); +#ifdef IPX + ncp_watchdog(conn); +#endif +} diff --git a/sys/netncp/ncp_sock.h b/sys/netncp/ncp_sock.h new file mode 100644 index 0000000..37aa712 --- /dev/null +++ b/sys/netncp/ncp_sock.h @@ -0,0 +1,51 @@ +/* + * 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 _NCP_SOCK_H_ +#define _NCP_SOCK_H_ + +int ncp_sock_connect_ipx(struct ncp_conn *); +int ncp_sock_connect_in(struct ncp_conn *); +int ncp_sock_recv(struct socket *so, struct mbuf **mp, int *rlen); +int ncp_sock_send(struct socket *so, struct mbuf *data, struct ncp_rq *rqp); +int ncp_sock_disconnect(struct ncp_conn *conn); +int ncp_poll(struct socket *so, int events); +int ncp_sock_rselect(struct socket *so,struct proc *p, struct timeval *tv,int events); +int ncp_sock_checksum(struct ncp_conn *conn, int enable); + +void ncp_check_rq(struct ncp_conn *conn); +void ncp_check_conn(struct ncp_conn *conn); + +void ncp_check_wd(struct ncp_conn *conn); + +#endif /* _NCP_SOCK_H_ */ diff --git a/sys/netncp/ncp_subr.c b/sys/netncp/ncp_subr.c new file mode 100644 index 0000000..b26d638 --- /dev/null +++ b/sys/netncp/ncp_subr.c @@ -0,0 +1,255 @@ +/* + * 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/errno.h> +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <sys/mbuf.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_conn.h> +#include <netncp/ncp_sock.h> +#include <netncp/ncp_subr.h> +#include <netncp/ncp_rq.h> +#include <netncp/ncp_ncp.h> +#include <netncp/ncp_user.h> +#include <netncp/nwerror.h> + +int ncp_debuglevel = 0; + +struct callout_handle ncp_timer_handle; + +static void ncp_at_exit(struct proc *p); +static void ncp_timer(void *arg); + +/* + * duplicate string from user space. It should be very-very slow. + */ +char * +ncp_str_dup(char *s) { + char *p, bt; + int len = 0; + + for (p = s;;p++) { + if (copyin(p, &bt, 1)) return NULL; + len++; + if (bt == 0) break; + } + MALLOC(p, char*, len, M_NCPDATA, M_WAITOK); + copyin(s, p, len); + return p; +} + + +void +ncp_at_exit(struct proc *p) { + struct ncp_conn *ncp, *nncp; + + if (ncp_conn_putprochandles(p) == 0) return; + + ncp_conn_locklist(LK_EXCLUSIVE, p); + for (ncp = conn_list.slh_first; ncp; ncp = nncp) { + nncp = ncp->nc_next.sle_next; + if (ncp->ref_cnt != 0) continue; + if (ncp_conn_lock(ncp, p, p->p_ucred,NCPM_READ|NCPM_EXECUTE|NCPM_WRITE)) + continue; + if (ncp_disconnect(ncp) != 0) + ncp_conn_unlock(ncp,p); + } + ncp_conn_unlocklist(p); + return; +} + +int +ncp_init(void) { + ncp_conn_init(); + if (at_exit(ncp_at_exit)) { + NCPFATAL("can't register at_exit handler\n"); + return ENOMEM; + } + ncp_timer_handle = timeout(ncp_timer,NULL,NCP_TIMER_TICK); + return 0; +} + +void +ncp_done(void) { + struct ncp_conn *ncp, *nncp; + struct proc *p = curproc; + + untimeout(ncp_timer,NULL,ncp_timer_handle); + rm_at_exit(ncp_at_exit); + ncp_conn_locklist(LK_EXCLUSIVE, p); + for (ncp = conn_list.slh_first; ncp; ncp = nncp) { + nncp = ncp->nc_next.sle_next; + ncp->ref_cnt = 0; + if (ncp_conn_lock(ncp, p, p->p_ucred,NCPM_READ|NCPM_EXECUTE|NCPM_WRITE)) { + NCPFATAL("Can't lock connection !\n"); + continue; + } + if (ncp_disconnect(ncp) != 0) + ncp_conn_unlock(ncp,p); + } + ncp_conn_unlocklist(p); +} + + +/* tick every second and check for watch dog packets and lost connections */ +static void +ncp_timer(void *arg){ + struct ncp_conn *conn; + + if(ncp_conn_locklist(LK_SHARED | LK_NOWAIT, NULL) == 0) { + SLIST_FOREACH(conn, &conn_list, nc_next) + ncp_check_conn(conn); + ncp_conn_unlocklist(NULL); + } + ncp_timer_handle = timeout(ncp_timer,NULL,NCP_TIMER_TICK); +} + +int +ncp_get_bindery_object_id(struct ncp_conn *conn, + u_int16_t object_type, char *object_name, + struct ncp_bindery_object *target, + struct proc *p,struct ucred *cred) +{ + int error; + DECLARE_RQ; + + NCP_RQ_HEAD_S(23,53,p,cred); + ncp_rq_word_hl(rqp, object_type); + ncp_rq_pstring(rqp, object_name); + checkbad(ncp_request(conn,rqp)); + if (rqp->rpsize < 54) { + printf("ncp_rp_size %d < 54\n", rqp->rpsize); + error = EINVAL; + goto bad; + } + target->object_id = ncp_rp_dword_hl(rqp); + target->object_type = ncp_rp_word_hl(rqp); + ncp_rp_mem(rqp,(caddr_t)target->object_name, 48); + NCP_RQ_EXIT; + return error; +} + +int +ncp_read(struct ncp_conn *conn, ncp_fh *file, struct uio *uiop, struct ucred *cred) { + int error = 0, len = 0, retlen=0, tsiz, burstio; + DECLARE_RQ; + + tsiz = uiop->uio_resid; +#ifdef NCPBURST + burstio = (ncp_burst_enabled && tsiz > conn->buffer_size); +#else + burstio = 0; +#endif + + while (tsiz > 0) { + if (!burstio) { + len = min(4096 - (uiop->uio_offset % 4096), tsiz); + len = min(len, conn->buffer_size); + NCP_RQ_HEAD(72,uiop->uio_procp,cred); + ncp_rq_byte(rqp, 0); + ncp_rq_mem(rqp, (caddr_t)file, 6); + ncp_rq_dword(rqp, htonl(uiop->uio_offset)); + ncp_rq_word(rqp, htons(len)); + checkbad(ncp_request(conn,rqp)); + retlen = ncp_rp_word_hl(rqp); + if (uiop->uio_offset & 1) + ncp_rp_byte(rqp); + error = nwfs_mbuftouio(&rqp->mrp,uiop,retlen,&rqp->bpos); + NCP_RQ_EXIT; + } else { +#ifdef NCPBURST + error = ncp_burst_read(conn, file, tsiz, &len, &retlen, uiop, cred); +#endif + } + if (error) break; + tsiz -= retlen; + if (retlen < len) + break; + } + return (error); +} + +int +ncp_write(struct ncp_conn *conn, ncp_fh *file, struct uio *uiop, struct ucred *cred) +{ + int error = 0, len, tsiz, backup; + DECLARE_RQ; + + if (uiop->uio_iovcnt != 1) { + printf("%s: can't handle iovcnt>1 !!!\n", __FUNCTION__); + return EIO; + } + tsiz = uiop->uio_resid; + while (tsiz > 0) { + len = min(4096 - (uiop->uio_offset % 4096), tsiz); + len = min(len, conn->buffer_size); + if (len == 0) { + printf("gotcha!\n"); + } + /* rq head */ + NCP_RQ_HEAD(73,uiop->uio_procp,cred); + ncp_rq_byte(rqp, 0); + ncp_rq_mem(rqp, (caddr_t)file, 6); + ncp_rq_dword(rqp, htonl(uiop->uio_offset)); + ncp_rq_word_hl(rqp, len); + nwfs_uiotombuf(uiop,&rqp->mrq,len,&rqp->bpos); + checkbad(ncp_request(conn,rqp)); + if (len == 0) + break; + NCP_RQ_EXIT; + if (error) { + backup = len; + uiop->uio_iov->iov_base -= backup; + uiop->uio_iov->iov_len += backup; + uiop->uio_offset -= backup; + uiop->uio_resid += backup; + break; + } + tsiz -= len; + } + if (error) + uiop->uio_resid = tsiz; + switch (error) { + case NWE_INSUFFICIENT_SPACE: + error = ENOSPC; + break; + } + return (error); +} diff --git a/sys/netncp/ncp_subr.h b/sys/netncp/ncp_subr.h new file mode 100644 index 0000000..af3c784 --- /dev/null +++ b/sys/netncp/ncp_subr.h @@ -0,0 +1,130 @@ +/* + * 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 _NCP_SUBR_H_ +#define _NCP_SUBR_H_ + +#define NCP_TIMER_TICK 2*hz /* 1sec */ +#define NCP_SIGMASK(set) \ + (SIGISMEMBER(set, SIGINT) || SIGISMEMBER(set, SIGTERM) || \ + SIGISMEMBER(set, SIGHUP) || SIGISMEMBER(set, SIGKILL) || \ + SIGISMEMBER(set, SIGQUIT)) + + +#define NCP_PRINT(format, args...) printf("FATAL: %s: "format, __FUNCTION__ ,## args) +#define nwfs_printf NCP_PRINT +/* Maybe this should panic, but I dont like that */ +#define NCPFATAL NCP_PRINT + +/* socket debugging */ +#ifdef NCP_SOCKET_DEBUG +#define NCPSDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#else +#define NCPSDEBUG(format, args...) +#endif + +/* NCP calls debug */ +#ifdef NCP_NCP_DEBUG +#define NCPNDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#else +#define NCPNDEBUG(format, args...) +#endif + +/* NCP data dump */ +#ifdef NCP_DATA_DEBUG +#define NCPDDEBUG(m) m_dumpm(m) +#else +#define NCPDDEBUG(m) +#endif + +/* FS VOPS debug */ +#ifdef NWFS_VOPS_DEBUG +#define NCPVODEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#else +#define NCPVODEBUG(format, args...) +#endif + +/* FS VNOPS debug */ +#ifdef NWFS_VNOPS_DEBUG +#define NCPVNDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#else +#define NCPVNDEBUG(format, args...) +#endif + +#define checkbad(fn) {error=(fn);if(error) goto bad;} + +#define ncp_suser(cred) suser_xxx(cred, NULL, 0) + +#define ncp_isowner(conn,cred) ((cred)->cr_uid == (conn)->nc_owner->cr_uid) + +struct ncp_conn; + +struct nwmount; +struct vnode; +struct nwnode; +struct vattr; +struct uio; +struct ncp_nlstables; + +struct ncp_open_info { + u_int32_t origfh; + ncp_fh fh; + u_int8_t action; + struct nw_entry_info fattr; +}; + +extern int ncp_debuglevel; + +int ncp_init(void); +void ncp_done(void); +int ncp_chkintr(struct ncp_conn *conn, struct proc *p); +char*ncp_str_dup(char *s); + +/* ncp_crypt.c */ +void nw_keyhash(const u_char *key, const u_char *buf, int buflen, u_char *target); +void nw_encrypt(const u_char *fra, const u_char *buf, u_char *target); +void ncp_sign(const u_int32_t *state, const char *x, u_int32_t *ostate); + +/* ncp calls */ +int ncp_get_bindery_object_id(struct ncp_conn *conn, + u_int16_t object_type, char *object_name, + struct ncp_bindery_object *target, + struct proc *p,struct ucred *cred); +int ncp_login_object(struct ncp_conn *conn, unsigned char *username, + int login_type, unsigned char *password, + struct proc *p,struct ucred *cred); +int ncp_read(struct ncp_conn *conn, ncp_fh *file, struct uio *uiop, struct ucred *cred); +int ncp_write(struct ncp_conn *conn, ncp_fh *file, struct uio *uiop, struct ucred *cred); + + +#endif /* _NCP_SUBR_H_ */ diff --git a/sys/netncp/ncp_user.h b/sys/netncp/ncp_user.h new file mode 100644 index 0000000..4f430b0 --- /dev/null +++ b/sys/netncp/ncp_user.h @@ -0,0 +1,97 @@ +/* + * 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 _NCP_USER_H_ +#define _NCP_USER_H_ + +/* + * "ncp" interface to kernel, this can be done via syscalls but may eat + * a lot of them, so we select internal code, define req's and replays + * as necessary. Structure for call is simple: + * byte=NCP_CONN + * byte=NCP_CONN_SUBFN + * ....=data + */ +#define NCP_CONN 0xF5 /* change if that will occupied */ +#define NCP_CONN_READ 0x01 /* read from file handle */ +#define NCP_CONN_WRITE 0x02 /* write to file handle */ +#define NCP_CONN_SETFLAGS 0x03 /* word mask, word flags */ +#define NCP_CONN_LOGIN 0x04 /* bind login on handle */ +#define NCP_CONN_GETINFO 0x05 /* get information about connection */ +#define NCP_CONN_GETUSER 0x06 /* get user name for connection */ +#define NCP_CONN_CONN2REF 0x07 /* convert handle to reference */ +#define NCP_CONN_CONNCLOSE 0x08 /* release connection handle */ +#define NCP_CONN_FRAG 0x09 /* ncp fragmented request */ +#define NCP_CONN_DUP 0x0A /* get an additional handle */ +#define NCP_CONN_GETDATA 0x0B /* retrieve NCP_CD_* vals */ +#define NCP_CONN_SETDATA 0x0C /* store NCP_CD_* vals */ + +/* + * Internal connection data can be set by owner or superuser and retrieved + * only by superuser + */ +#define NCP_CD_NDSLOGINKEY 0x01 +#define NCP_CD_NDSPRIVATEKEY 0x02 +#define NCP_CD_NDSUFLAGS 0x03 + +/* user side structures to issue fragmented ncp calls */ +typedef struct { + char *fragAddress; + u_int32_t fragSize; +} NW_FRAGMENT; + + +struct ncp_rw { + ncp_fh nrw_fh; + char *nrw_base; + off_t nrw_offset; + int nrw_cnt; +}; + +struct ncp_conn_login { + char *username; + int objtype; + char *password; +}; + +struct ncp_conn_frag { + int cc; /* completion code */ + int cs; /* connection state */ + int fn; + int rqfcnt; + NW_FRAGMENT *rqf; + int rpfcnt; + NW_FRAGMENT *rpf; +}; + +#endif diff --git a/sys/netncp/nwerror.h b/sys/netncp/nwerror.h new file mode 100644 index 0000000..6a5b09e --- /dev/null +++ b/sys/netncp/nwerror.h @@ -0,0 +1,634 @@ +/* + * NetWare requestor error codes, they taken from NDK + * + * $FreeBSD$ + */ +#if !defined (_NWERROR_H_) +#define _NWERROR_H_ + +#ifndef SUCCESS +#define SUCCESS 0 +#endif + +#define SHELL_ERROR 0x8800 +#define VLM_ERROR 0x8800 +#define ALREADY_ATTACHED 0x8800 /* 0 - Attach attempted to server with valid, existing connection */ +#define INVALID_CONNECTION 0x8801 /* 1 - Request attempted with invalid or non-attached connection handle */ +#define DRIVE_IN_USE 0x8802 /* 2 - OS/2 only (NOT USED) */ +#define CANT_ADD_CDS 0x8803 /* 3 - Map drive attempted but unable to add new current directory structure */ +#define DRIVE_CANNOT_MAP 0x8803 +#define BAD_DRIVE_BASE 0x8804 /* 4 - Map drive attempted with invalid path specification */ +#define NET_READ_ERROR 0x8805 /* 5 - Attempt to receive from the selected transport failed */ +#define NET_RECV_ERROR 0x8805 /* 5 */ +#define UNKNOWN_NET_ERROR 0x8806 /* 6 - Network send attempted with an un-specific network error */ +#define SERVER_INVALID_SLOT 0x8807 /* 7 - Server request attempted with invalid server connection slot */ +#define BAD_SERVER_SLOT 0x8807 /* 7 */ +#define NO_SERVER_SLOTS 0x8808 /* 8 - Attach attempted to server with no connection slots available */ +#define NET_WRITE_ERROR 0x8809 /* 9 - Attempt to send on the selected transport failed */ +#define CONNECTION_IN_ERROR_STATE 0x8809 /* Client-32 */ +#define NET_SEND_ERROR 0x8809 /* 9 */ +#define SERVER_NO_ROUTE 0x880A /* 10 - Attempted to find route to server where no route exists */ +#define BAD_LOCAL_TARGET 0x880B /* 11 - OS/2 only */ +#define TOO_MANY_REQ_FRAGS 0x880C /* 12 - Attempted request with too many request fragments specified */ +#define CONNECT_LIST_OVERFLOW 0x880D /* 13 */ +#define BUFFER_OVERFLOW 0x880E /* 14 - Attempt to receive more data than the reply buffer had room for */ +#define MORE_DATA_ERROR 0x880E /* Client-32 */ +#define NO_CONN_TO_SERVER 0x880F /* 15 */ +#define NO_CONNECTION_TO_SERVER 0x880F /* 15 - Attempt to get connection for a server not connected */ +#define NO_ROUTER_FOUND 0x8810 /* 16 - OS/2 only */ +#define BAD_FUNC_ERROR 0x8811 /* 17 */ +#define INVALID_SHELL_CALL 0x8811 /* 17 - Attempted function call to non- existent or illegal function */ +#define SCAN_COMPLETE 0x8812 +#define LIP_RESIZE_ERROR 0x8812 /* Client-32 */ +#define UNSUPPORTED_NAME_FORMAT_TYPE 0x8813 +#define INVALID_DIR_HANDLE 0x8813 /* Client-32 */ +#define HANDLE_ALREADY_LICENSED 0x8814 +#define OUT_OF_CLIENT_MEMORY 0x8814 /* Client-32 */ +#define HANDLE_ALREADY_UNLICENSED 0x8815 +#define PATH_NOT_OURS 0x8815 /* Client-32 */ +#define INVALID_NCP_PACKET_LENGTH 0x8816 +#define PATH_IS_PRINT_DEVICE 0x8816 /* Client-32 */ +#define SETTING_UP_TIMEOUT 0x8817 +#define PATH_IS_EXCLUDED_DEVICE 0x8817 /* Client-32 */ +#define SETTING_SIGNALS 0x8818 +#define PATH_IS_INVALID 0x8818 /* Client-32 */ +#define SERVER_CONNECTION_LOST 0x8819 +#define NOT_SAME_DEVICE 0x8819 /* Client-32 */ +#define OUT_OF_HEAP_SPACE 0x881A +#define INVALID_SERVICE_REQUEST 0x881B +#define INVALID_SEARCH_HANDLE 0x881B /* Client-32 */ +#define INVALID_TASK_NUMBER 0x881C +#define INVALID_DEVICE_HANDLE 0x881C /* Client-32 */ +#define INVALID_MESSAGE_LENGTH 0x881D +#define INVALID_SEM_HANDLE 0x881D /* Client-32 */ +#define EA_SCAN_DONE 0x881E +#define INVALID_CFG_HANDLE 0x881E /* Client-32 */ +#define BAD_CONNECTION_NUMBER 0x881F +#define INVALID_MOD_HANDLE 0x881F /* Client-32 */ +#define ASYN_FIRST_PASS 0x8820 +#define INVALID_DEVICE_INDEX 0x8821 +#define INVALID_CONN_HANDLE 0x8822 +#define INVALID_QUEUE_ID 0x8823 +#define INVALID_PDEVICE_HANDLE 0x8824 +#define INVALID_JOB_HANDLE 0x8825 +#define INVALID_ELEMENT_ID 0x8826 +#define ALIAS_NOT_FOUND 0x8827 +#define RESOURCE_SUSPENDED 0x8828 +#define INVALID_QUEUE_SPECIFIED 0x8829 +#define DEVICE_ALREADY_OPEN 0x882A +#define JOB_ALREADY_OPEN 0x882B +#define QUEUE_NAME_ID_MISMATCH 0x882C +#define JOB_ALREADY_STARTED 0x882D +#define SPECT_DAA_TYPE_NOT_SUPPORTED 0x882E +#define INVALID_ENVIR_HANDLE 0x882F +#define NOT_SAME_CONNECTION 0x8830 /* 48 - Internal server request attempted accross different server connections */ +#define PRIMARY_CONNECTION_NOT_SET 0x8831 /* 49 - Attempt to retrieve default connection with no primary connection set */ +#define NO_PRIMARY_SET 0x8831 /* 49 */ +#define KEYWORD_NOT_FOUND 0x8832 /* Client-32 */ +#define PRINT_CAPTURE_NOT_IN_PROGRESS 0x8832 /* Client-32 */ +#define NO_CAPTURE_SET 0x8832 /* 50 */ +#define NO_CAPTURE_IN_PROGRESS 0x8832 /* 50 - Capture information requested on port with no capture in progress */ +#define BAD_BUFFER_LENGTH 0x8833 /* 51 */ +#define INVALID_BUFFER_LENGTH 0x8833 /* 51 - Used to indicate length which caller requested on a GetDNC or SetDNC was too large */ +#define NO_USER_NAME 0x8834 /* 52 */ +#define NO_NETWARE_PRINT_SPOOLER 0x8835 /* 53 - Capture requested without having the local print spooler installed */ +#define INVALID_PARAMETER 0x8836 /* 54 - Attempted function with an invalid function parameter specified */ +#define CONFIG_FILE_OPEN_FAILED 0x8837 /* 55 - OS/2 only */ +#define NO_CONFIG_FILE 0x8838 /* 56 - OS/2 only */ +#define CONFIG_FILE_READ_FAILED 0x8839 /* 57 - OS/2 only */ +#define CONFIG_LINE_TOO_LONG 0x883A /* 58 - OS/2 only */ +#define CONFIG_LINES_IGNORED 0x883B /* 59 - OS/2 only */ +#define NOT_MY_RESOURCE 0x883C /* 60 - Attempted request made with a parameter using foriegn resource */ +#define DAEMON_INSTALLED 0x883D /* 61 - OS/2 only */ +#define SPOOLER_INSTALLED 0x883E /* 62 - Attempted load of print spooler with print spooler already installed */ +#define CONN_TABLE_FULL 0x883F /* 63 */ +#define CONNECTION_TABLE_FULL 0x883F /* 63 - Attempted to allocate a connection handle with no more local connection table entries */ +#define CONFIG_SECTION_NOT_FOUND 0x8840 /* 64 - OS/2 only */ +#define BAD_TRAN_TYPE 0x8841 /* 65 */ +#define INVALID_TRANSPORT_TYPE 0x8841 /* 65 - Attempted function on a connection with an invalid transport selected */ +#define TDS_TAG_IN_USE 0x8842 /* 66 - OS/2 only */ +#define TDS_OUT_OF_MEMORY 0x8843 /* 67 - OS/2 only */ +#define TDS_INVALID_TAG 0x8844 /* 68 - Attempted TDS function with invalid tag */ +#define TDS_WRITE_TRUNCATED 0x8845 /* 69 - Attempted TDS write with buffer that exceeded buffer */ +#define NO_CONNECTION_TO_DS 0x8846 /* Client-32 */ +#define NO_DIRECTORY_SERVICE_CONNECTION 0x8846 /* 70 */ +#define SERVICE_BUSY 0x8846 /* 70 - Attempted request made to partially asynchronous function in busy state */ +#define NO_SERVER_ERROR 0x8847 /* 71 - Attempted connect failed to find any servers responding */ +#define BAD_VLM_ERROR 0x8848 /* 72 - Attempted function call to non-existant or not-loaded overlay */ +#define NETWORK_DRIVE_IN_USE 0x8849 /* 73 - Attempted map to network drive that was already mapped */ +#define LOCAL_DRIVE_IN_USE 0x884A /* 74 - Attempted map to local drive that was in use */ +#define NO_DRIVES_AVAILABLE 0x884B /* 75 - Attempted map to next available drive when none were available */ +#define DEVICE_NOT_REDIRECTED 0x884C /* 76 - The device is not redirected */ +#define NO_MORE_SFT_ENTRIES 0x884D /* 77 - Maximum number of files was reached */ +#define UNLOAD_ERROR 0x884E /* 78 - Attempted unload failed */ +#define IN_USE_ERROR 0x884F /* 79 - Attempted re-use of already in use connection entry */ +#define TOO_MANY_REP_FRAGS 0x8850 /* 80 - Attempted request with too many reply fragments specified */ +#define TABLE_FULL 0x8851 /* 81 - Attempted to add a name into the name table after it was full */ +#ifndef SOCKET_NOT_OPEN +#define SOCKET_NOT_OPEN 0x8852 /* 82 - Listen was posted on unopened socket */ +#endif +#define MEM_MGR_ERROR 0x8853 /* 83 - Attempted enhanced memory operation failed */ +#define SFT3_ERROR 0x8854 /* 84 - An SFT3 switch occured mid-transfer */ +#define PREFERRED_NOT_FOUND 0x8855 /* 85 - the preferred directory server was not established but another directory server was returned */ +#define DEVICE_NOT_RECOGNIZED 0x8856 /* 86 - used to determine if the device is not used by VISE so pass it on to the next redirector, if any. */ +#define BAD_NET_TYPE 0x8857 /* 87 - the network type (Bind/NDS) does not match the server version */ +#define ERROR_OPENING_FILE 0x8858 /* 88 - generic open failure error, invalid path, access denied, etc.. */ +#define NO_PREFERRED_SPECIFIED 0x8859 /* 89 - no preferred name specified */ +#define ERROR_OPENING_SOCKET 0x885A /* 90 - error opening a socket */ +#define REQUESTER_FAILURE 0x885A /* Client-32 */ +#define RESOURCE_ACCESS_DENIED 0x885B /* Client-32 */ +#define SIGNATURE_LEVEL_CONFLICT 0x8861 +#define NO_LOCK_FOUND 0x8862 /* OS/2 - process lock on conn handle failed, process ID not recognized */ +#define LOCK_TABLE_FULL 0x8863 /* OS/2 - process lock on conn handle failed, process lock table full */ +#define INVALID_MATCH_DATA 0x8864 +#define MATCH_FAILED 0x8865 +#define NO_MORE_ENTRIES 0x8866 +#define INSUFFICIENT_RESOURCES 0x8867 +#define STRING_TRANSLATION 0x8868 +#define STRING_TRANSLATION_NEEDED 0x8868 /* Client-32 */ +#define ACCESS_VIOLATION 0x8869 +#define NOT_AUTHENTICATED 0x886A +#define INVALID_LEVEL 0x886B +#define RESOURCE_LOCK_ERROR 0x886C +#define INVALID_NAME_FORMAT 0x886D +#define OBJECT_EXISTS 0x886E +#define OBJECT_NOT_FOUND 0x886F +#define UNSUPPORTED_TRAN_TYPE 0x8870 +#define INVALID_STRING_TYPE 0x8871 +#define INVALID_OWNER 0x8872 +#define UNSUPPORTED_AUTHENTICATOR 0x8873 +#define IO_PENDING 0x8874 +#define INVALID_DRIVE_NUM 0x8875 +#define SHELL_FAILURE 0x88FF +#define VLM_FAILURE 0x88FF + +#define SVC_ALREADY_REGISTERED 0x8880 /* Client-32 */ +#define SVC_REGISTRY_FULL 0x8881 /* Client-32 */ +#define SVC_NOT_REGISTERED 0x8882 /* Client-32 */ +#define OUT_OF_RESOURCES 0x8883 /* Client-32 */ +#define RESOLVE_SVC_FAILED 0x8884 /* Client-32 */ +#define CONNECT_FAILED 0x8885 /* Client-32 */ +#define PROTOCOL_NOT_BOUND 0x8886 /* Client-32 */ +#define AUTHENTICATION_FAILED 0x8887 /* Client-32 */ +#define INVALID_AUTHEN_HANDLE 0x8888 /* Client-32 */ +#define AUTHEN_HANDLE_ALREADY_EXISTS 0x8889 /* Client-32 */ + +#define DIFF_OBJECT_ALREADY_AUTHEN 0x8890 /* Client-32 */ +#define REQUEST_NOT_SERVICEABLE 0x8891 /* Client-32 */ +#define AUTO_RECONNECT_SO_REBUILD 0x8892 /* Client-32 */ +#define AUTO_RECONNECT_RETRY_REQUEST 0x8893 /* Client-32 */ +#define ASYNC_REQUEST_IN_USE 0x8894 /* Client-32 */ +#define ASYNC_REQUEST_CANCELED 0x8895 /* Client-32 */ +#define SESS_SVC_ALREADY_REGISTERED 0x8896 /* Client-32 */ +#define SESS_SVC_NOT_REGISTERED 0x8897 /* Client-32 */ +#define PREVIOUSLY_AUTHENTICATED 0x8899 /* Client-32 */ +#define RESOLVE_SVC_PARTIAL 0x889A /* Client-32 */ +#define NO_DEFAULT_SPECIFIED 0x889B /* Client-32 */ +#define HOOK_REQUEST_NOT_HANDLED 0x889C /* Client-32 */ +#define HOOK_REQUEST_BUSY 0x889D /* Client-32 */ +#define HOOK_REQUEST_QUEUED 0x889D /* Client-32 */ +#define AUTO_RECONNECT_SO_IGNORE 0x889E /* Client-32 */ +#define ASYNC_REQUEST_NOT_IN_USE 0x889F /* Client-32 */ +#define AUTO_RECONNECT_FAILURE 0x88A0 /* Client-32 */ +#define NET_ERROR_ABORT_APPLICATION 0x88A1 /* Client-32 */ +#define NET_ERROR_SUSPEND_APPLICATION 0x88A2 /* Client-32 */ +#define NET_ERROR_ABORTED_PROCESS_GROUP 0x88A3 /* Client-32 */ +#define NET_ERROR_PASSWORD_HAS_EXPIRED 0x88A5 /* Client-32 */ +#define NET_ERROR_NETWORK_INACTIVE 0x88A6 /* Client-32 */ +#define REPLY_TRUNCATED 0x88e6 /* 230 NLM */ + + +/* Server Errors */ + +#define ERR_INSUFFICIENT_SPACE 0x8901 /* 001 */ +#define ERR_NO_MORE_ENTRY 0x8914 /* 020 */ +#define NLM_INVALID_CONNECTION 0x890a /* 010 */ +#define ERR_BUFFER_TOO_SMALL 0x8977 /* 119 */ +#define ERR_VOLUME_FLAG_NOT_SET 0x8978 /* 120 the service requested, not avail. on the selected vol. */ +#define ERR_NO_ITEMS_FOUND 0x8979 /* 121 */ +#define ERR_CONN_ALREADY_TEMP 0x897a /* 122 */ +#define ERR_CONN_ALREADY_LOGGED_IN 0x897b /* 123 */ +#define ERR_CONN_NOT_AUTHENTICATED 0x897c /* 124 */ +#define ERR_CONN_NOT_LOGGED_IN 0x897d /* 125 */ +#define NCP_BOUNDARY_CHECK_FAILED 0x897e /* 126 */ +#define ERR_LOCK_WAITING 0x897f /* 127 */ +#define ERR_LOCK_FAIL 0x8980 /* 128 */ +#define FILE_IN_USE_ERROR 0x8980 /* 128 */ +#define NO_MORE_FILE_HANDLES 0x8981 /* 129 */ +#define NO_OPEN_PRIVILEGES 0x8982 /* 130 */ +#define IO_ERROR_NETWORK_DISK 0x8983 /* 131 */ +#define ERR_AUDITING_HARD_IO_ERROR 0x8983 /* 131 */ +#define NO_CREATE_PRIVILEGES 0x8984 /* 132 */ +#define ERR_AUDITING_NOT_SUPV 0x8984 /* 132 */ +#define NO_CREATE_DELETE_PRIVILEGES 0x8985 /* 133 */ +#define CREATE_FILE_EXISTS_READ_ONLY 0x8986 /* 134 */ +#define WILD_CARDS_IN_CREATE_FILE_NAME 0x8987 /* 135 */ +#define CREATE_FILENAME_ERROR 0x8987 /* 135 */ +#define INVALID_FILE_HANDLE 0x8988 /* 136 */ +#define NO_SEARCH_PRIVILEGES 0x8989 /* 137 */ +#define NO_DELETE_PRIVILEGES 0x898A /* 138 */ +#define NO_RENAME_PRIVILEGES 0x898B /* 139 */ +#define NO_MODIFY_PRIVILEGES 0x898C /* 140 */ +#define SOME_FILES_AFFECTED_IN_USE 0x898D /* 141 */ +#define NO_FILES_AFFECTED_IN_USE 0x898E /* 142 */ +#define SOME_FILES_AFFECTED_READ_ONLY 0x898F /* 143 */ +#define NO_FILES_AFFECTED_READ_ONLY 0x8990 /* 144 */ +#define SOME_FILES_RENAMED_NAME_EXISTS 0x8991 /* 145 */ +#define NO_FILES_RENAMED_NAME_EXISTS 0x8992 /* 146 */ +#define NO_READ_PRIVILEGES 0x8993 /* 147 */ +#define NO_WRITE_PRIVILEGES_OR_READONLY 0x8994 /* 148 */ +#define FILE_DETACHED 0x8995 /* 149 */ +#define SERVER_OUT_OF_MEMORY 0x8996 /* 150 */ +#define ERR_TARGET_NOT_A_SUBDIRECTORY 0x8996 /* 150 can be changed later (note written by server people). */ +#define NO_DISK_SPACE_FOR_SPOOL_FILE 0x8997 /* 151 */ +#define ERR_AUDITING_NOT_ENABLED 0x8997 /* 151 */ +#define VOLUME_DOES_NOT_EXIST 0x8998 /* 152 */ +#define DIRECTORY_FULL 0x8999 /* 153 */ +#define RENAMING_ACROSS_VOLUMES 0x899A /* 154 */ +#define BAD_DIRECTORY_HANDLE 0x899B /* 155 */ +#define INVALID_PATH 0x899C /* 156 */ +#define NO_MORE_TRUSTEES 0x899C /* 156 */ +#define NO_MORE_DIRECTORY_HANDLES 0x899D /* 157 */ +#define INVALID_FILENAME 0x899E /* 158 */ +#define DIRECTORY_ACTIVE 0x899F /* 159 */ +#define DIRECTORY_NOT_EMPTY 0x89A0 /* 160 */ +#define DIRECTORY_IO_ERROR 0x89A1 /* 161 */ +#define READ_FILE_WITH_RECORD_LOCKED 0x89A2 /* 162 */ +#define ERR_TRANSACTION_RESTARTED 0x89A3 /* 163 */ +#define ERR_RENAME_DIR_INVALID 0x89A4 /* 164 */ +#define ERR_INVALID_OPENCREATE_MODE 0x89A5 /* 165 */ +#define ERR_ALREADY_IN_USE 0x89A6 /* 166 */ +#define ERR_AUDITING_ACTIVE 0x89A6 /* 166 */ +#define ERR_INVALID_RESOURCE_TAG 0x89A7 /* 167 */ +#define ERR_ACCESS_DENIED 0x89A8 /* 168 */ +#define ERR_AUDITING_NO_RIGHTS 0x89A8 /* 168 */ +#define INVALID_DATA_STREAM 0x89BE /* 190 */ +#define INVALID_NAME_SPACE 0x89BF /* 191 */ +#define NO_ACCOUNTING_PRIVILEGES 0x89C0 /* 192 */ +#define LOGIN_DENIED_NO_ACCOUNT_BALANCE 0x89C1 /* 193 */ +#define LOGIN_DENIED_NO_CREDIT 0x89C2 /* 194 */ +#define ERR_AUDITING_RECORD_SIZE 0x89C2 /* 194 */ +#define ERR_TOO_MANY_HOLDS 0x89C3 /* 195 */ +#define ACCOUNTING_DISABLED 0x89C4 /* 196 */ +#define INTRUDER_DETECTION_LOCK 0x89C5 /* 197 */ +#define NO_CONSOLE_OPERATOR 0x89C6 /* 198 */ +#define NO_CONSOLE_PRIVILEGES 0x89C6 /* 198 */ +#define ERR_Q_IO_FAILURE 0x89D0 /* 208 */ +#define ERR_NO_QUEUE 0x89D1 /* 209 */ +#define ERR_NO_Q_SERVER 0x89D2 /* 210 */ +#define ERR_NO_Q_RIGHTS 0x89D3 /* 211 */ +#define ERR_Q_FULL 0x89D4 /* 212 */ +#define ERR_NO_Q_JOB 0x89D5 /* 213 */ +#define ERR_NO_Q_JOB_RIGHTS 0x89D6 /* 214 */ +#define ERR_Q_IN_SERVICE 0x89D7 /* 215 */ +#define PASSWORD_NOT_UNIQUE 0x89D7 /* 215 */ +#define ERR_Q_NOT_ACTIVE 0x89D8 /* 216 */ +#define PASSWORD_TOO_SHORT 0x89D8 /* 216 */ +#define ERR_Q_STN_NOT_SERVER 0x89D9 /* 217 */ +#define LOGIN_DENIED_NO_CONNECTION 0x89D9 /* 217 */ +#define ERR_MAXIMUM_LOGINS_EXCEEDED 0x89D9 /* 217 */ +#define ERR_Q_HALTED 0x89DA /* 218 */ +#define UNAUTHORIZED_LOGIN_TIME 0x89DA /* 218 */ +#define UNAUTHORIZED_LOGIN_STATION 0x89DB /* 219 */ +#define ERR_Q_MAX_SERVERS 0x89DB /* 219 */ +#define ACCOUNT_DISABLED 0x89DC /* 220 */ +#define PASSWORD_HAS_EXPIRED_NO_GRACE 0x89DE /* 222 */ +#define PASSWORD_HAS_EXPIRED 0x89DF /* 223 */ +#define E_NO_MORE_USERS 0x89E7 /* 231 */ +#define NOT_ITEM_PROPERTY 0x89E8 /* 232 */ +#define WRITE_PROPERTY_TO_GROUP 0x89E8 /* 232 */ +#define MEMBER_ALREADY_EXISTS 0x89E9 /* 233 */ +#define NO_SUCH_MEMBER 0x89EA /* 234 */ +#define NOT_GROUP_PROPERTY 0x89EB /* 235 */ +#define NO_SUCH_SEGMENT 0x89EC /* 236 */ +#define PROPERTY_ALREADY_EXISTS 0x89ED /* 237 */ +#define OBJECT_ALREADY_EXISTS 0x89EE /* 238 */ +#define INVALID_NAME 0x89EF /* 239 */ +#define WILD_CARD_NOT_ALLOWED 0x89F0 /* 240 */ +#define INVALID_BINDERY_SECURITY 0x89F1 /* 241 */ +#define NO_OBJECT_READ_PRIVILEGE 0x89F2 /* 242 */ +#define NO_OBJECT_RENAME_PRIVILEGE 0x89F3 /* 243 */ +#define NO_OBJECT_DELETE_PRIVILEGE 0x89F4 /* 244 */ +#define NO_OBJECT_CREATE_PRIVILEGE 0x89F5 /* 245 */ +#define NO_PROPERTY_DELETE_PRIVILEGE 0x89F6 /* 246 */ +#define NO_PROPERTY_CREATE_PRIVILEGE 0x89F7 /* 247 */ +#define NO_PROPERTY_WRITE_PRIVILEGE 0x89F8 /* 248 */ +#define NO_FREE_CONNECTION_SLOTS 0x89F9 /* 249 */ +#define NO_PROPERTY_READ_PRIVILEGE 0x89F9 /* 249 */ +#define NO_MORE_SERVER_SLOTS 0x89FA /* 250 */ +#define TEMP_REMAP_ERROR 0x89FA /* 250 */ +#define INVALID_PARAMETERS 0x89FB /* 251 */ +#define NO_SUCH_PROPERTY 0x89FB /* 251 */ +#define ERR_NCP_NOT_SUPPORTED 0x89FB /* 251 */ +#define INTERNET_PACKET_REQT_CANCELED 0x89FC /* 252 */ +#define UNKNOWN_FILE_SERVER 0x89FC /* 252 */ +#define MESSAGE_QUEUE_FULL 0x89FC /* 252 */ +#define NO_SUCH_OBJECT 0x89FC /* 252 */ +#define LOCK_COLLISION 0x89FD /* 253 */ +#define BAD_STATION_NUMBER 0x89FD /* 253 */ +#define INVALID_PACKET_LENGTH 0x89FD /* 253 */ +#define UNKNOWN_REQUEST 0x89FD /* 253 */ +#define BINDERY_LOCKED 0x89FE /* 254 */ +#define TRUSTEE_NOT_FOUND 0x89FE /* 254 */ +#define DIRECTORY_LOCKED 0x89FE /* 254 */ +#define INVALID_SEMAPHORE_NAME_LENGTH 0x89FE /* 254 */ +#define PACKET_NOT_DELIVERABLE 0x89FE /* 254 */ +#define SERVER_BINDERY_LOCKED 0x89FE /* 254 */ +#define SOCKET_TABLE_FULL 0x89FE /* 254 */ +#define SPOOL_DIRECTORY_ERROR 0x89FE /* 254 */ +#define SUPERVISOR_HAS_DISABLED_LOGIN 0x89FE /* 254 */ +#define TIMEOUT_FAILURE 0x89FE /* 254 */ +#define BAD_PRINTER_ERROR 0x89FF /* 255 */ +#define BAD_RECORD_OFFSET 0x89FF /* 255 */ +#define CLOSE_FCB_ERROR 0x89FF /* 255 */ +#define FILE_EXTENSION_ERROR 0x89FF /* 255 */ +#define FILE_NAME_ERROR 0x89FF /* 255 */ +#define HARDWARE_FAILURE 0x89FF /* 255 */ +#define INVALID_DRIVE_NUMBER 0x89FF /* 255 */ +#define DOS_INVALID_DRIVE 0x000F /* 255 */ +#define INVALID_INITIAL_SEMAPHORE_VALUE 0x89FF /* 255 */ +#define INVALID_SEMAPHORE_HANDLE 0x89FF /* 255 */ +#define IO_BOUND_ERROR 0x89FF /* 255 */ +#define NO_FILES_FOUND_ERROR 0x89FF /* 255 */ +#define NO_RESPONSE_FROM_SERVER 0x89FF /* 255 */ +#define NO_SUCH_OBJECT_OR_BAD_PASSWORD 0x89FF /* 255 */ +#define PATH_NOT_LOCATABLE 0x89FF /* 255 */ +#define QUEUE_FULL_ERROR 0x89FF /* 255 */ +#define REQUEST_NOT_OUTSTANDING 0x89FF /* 255 */ +#ifndef SOCKET_ALREADY_OPEN +#define SOCKET_ALREADY_OPEN 0x89FF /* 255 */ +#endif +#define LOCK_ERROR 0x89FF /* 255 */ +#ifndef FAILURE +#define FAILURE 0x89FF /* 255 Generic Failure */ +#endif + +/* #define NOT_SAME_LOCAL_DRIVE 0x89F6 */ +/* #define TARGET_DRIVE_NOT_LOCAL 0x89F7 */ +/* #define ALREADY_ATTACHED_TO_SERVER 0x89F8 */ /* 248 */ +/* #define NOT_ATTACHED_TO_SERVER 0x89F8 */ + +/**** Network errors ****/ +/* Decimal values at end of line are 32768 lower than actual */ + +#define NWE_ALREADY_ATTACHED 0x8800 /* 0 - Attach attempted to server with valid, existing connection */ +#define NWE_CONN_INVALID 0x8801 /* 1 - Request attempted with invalid or non-attached connection handle */ +#define NWE_DRIVE_IN_USE 0x8802 /* 2 - OS/2 only (NOT USED) */ +#define NWE_DRIVE_CANNOT_MAP 0x8803 /* 3 - Map drive attempted but unable to add new current directory structure */ +#define NWE_DRIVE_BAD_PATH 0x8804 /* 4 - Map drive attempted with invalid path specification */ +#define NWE_NET_RECEIVE 0x8805 /* 5 - Attempt to receive from the selected transport failed */ +#define NWE_NET_UNKNOWN 0x8806 /* 6 - Network send attempted with an un-specific network error */ +#define NWE_SERVER_BAD_SLOT 0x8807 /* 7 - Server request attempted with invalid server connection slot */ +#define NWE_SERVER_NO_SLOTS 0x8808 /* 8 - Attach attempted to server with no connection slots available */ +#define NWE_NET_SEND 0x8809 /* 9 - Attempt to send on the selected transport failed */ +#define NWE_SERVER_NO_ROUTE 0x880A /* 10 - Attempted to find route to server where no route exists */ +#define NWE_BAD_LOCAL_TARGET 0x880B /* 11 - OS/2 only */ +#define NWE_REQ_TOO_MANY_REQ_FRAGS 0x880C /* 12 - Attempted request with too many request fragments specified */ +#define NWE_CONN_LIST_OVERFLOW 0x880D /* 13 */ +#define NWE_BUFFER_OVERFLOW 0x880E /* 14 - Attempt to receive more data than the reply buffer had room for */ +#define NWE_SERVER_NO_CONN 0x880F /* 15 - Attempt to get connection for a server not connected */ +#define NWE_NO_ROUTER_FOUND 0x8810 /* 16 - OS/2 only */ +#define NWE_FUNCTION_INVALID 0x8811 /* 17 - Attempted function call to non- existent or illegal function */ +#define NWE_SCAN_COMPLETE 0x8812 +#define NWE_UNSUPPORTED_NAME_FORMAT_TYP 0x8813 +#define NWE_HANDLE_ALREADY_LICENSED 0x8814 +#define NWE_HANDLE_ALREADY_UNLICENSED 0x8815 +#define NWE_INVALID_NCP_PACKET_LENGTH 0x8816 +#define NWE_SETTING_UP_TIMEOUT 0x8817 +#define NWE_SETTING_SIGNALS 0x8818 +#define NWE_SERVER_CONNECTION_LOST 0x8819 +#define NWE_OUT_OF_HEAP_SPACE 0x881A +#define NWE_INVALID_SERVICE_REQUEST 0x881B +#define NWE_INVALID_TASK_NUMBER 0x881C +#define NWE_INVALID_MESSAGE_LENGTH 0x881D +#define NWE_EA_SCAN_DONE 0x881E +#define NWE_BAD_CONNECTION_NUMBER 0x881F +#define NWE_MULT_TREES_NOT_SUPPORTED 0x8820 /* 32 - Attempt to open a connection to a DS tree other than the default tree */ +#define NWE_CONN_NOT_SAME 0x8830 /* 48 - Internal server request attempted across different server connections */ +#define NWE_CONN_PRIMARY_NOT_SET 0x8831 /* 49 - Attempt to retrieve default connection with no primary connection set */ +#define NWE_PRN_CAPTURE_NOT_IN_PROGRESS 0x8832 /* 50 - Capture information requested on port with no capture in progress */ +#define NWE_BUFFER_INVALID_LEN 0x8833 /* 51 - Used to indicate length which caller requested on a GetDNC or SetDNC was too large */ +#define NWE_USER_NO_NAME 0x8834 /* 52 */ +#define NWE_PRN_NO_LOCAL_SPOOLER 0x8835 /* 53 - Capture requested without having the local print spooler installed */ +#define NWE_PARAM_INVALID 0x8836 /* 54 - Attempted function with an invalid function parameter specified */ +#define NWE_CFG_OPEN_FAILED 0x8837 /* 55 - OS/2 only */ +#define NWE_CFG_NO_FILE 0x8838 /* 56 - OS/2 only */ +#define NWE_CFG_READ_FAILED 0x8839 /* 57 - OS/2 only */ +#define NWE_CFG_LINE_TOO_LONG 0x883A /* 58 - OS/2 only */ +#define NWE_CFG_LINES_IGNORED 0x883B /* 59 - OS/2 only */ +#define NWE_RESOURCE_NOT_OWNED 0x883C /* 60 - Attempted request made with a parameter using foriegn resource */ +#define NWE_DAEMON_INSTALLED 0x883D /* 61 - OS/2 only */ +#define NWE_PRN_SPOOLER_INSTALLED 0x883E /* 62 - Attempted load of print spooler with print spooler already installed */ +#define NWE_CONN_TABLE_FULL 0x883F /* 63 - Attempted to allocate a connection handle with no more local connection table entries */ +#define NWE_CFG_SECTION_NOT_FOUND 0x8840 /* 64 - OS/2 only */ +#define NWE_TRAN_INVALID_TYPE 0x8841 /* 65 - Attempted function on a connection with an invalid transport selected */ +#define NWE_TDS_TAG_IN_USE 0x8842 /* 66 - OS/2 only */ +#define NWE_TDS_OUT_OF_MEMORY 0x8843 /* 67 - OS/2 only */ +#define NWE_TDS_INVALID_TAG 0x8844 /* 68 - Attempted TDS function with invalid tag */ +#define NWE_TDS_WRITE_TRUNCATED 0x8845 /* 69 - Attempted TDS write with buffer that exceeded buffer */ +#define NWE_DS_NO_CONN 0x8846 /* 70 */ +#define NWE_SERVICE_BUSY 0x8846 /* 70 - Attempted request made to partially asynchronous function in busy state */ +#define NWE_SERVER_NOT_FOUND 0x8847 /* 71 - Attempted connect failed to find any servers responding */ +#define NWE_VLM_INVALID 0x8848 /* 72 - Attempted function call to non-existant or not-loaded overlay */ +#define NWE_DRIVE_ALREADY_MAPPED 0x8849 /* 73 - Attempted map to network drive that was already mapped */ +#define NWE_DRIVE_LOCAL_IN_USE 0x884A /* 74 - Attempted map to local drive that was in use */ +#define NWE_DRIVE_NONE_AVAILABLE 0x884B /* 75 - Attempted map to next available drive when none were available */ +#define NWE_DEVICE_NOT_REDIRECTED 0x884C /* 76 - The device is not redirected */ +#define NWE_FILE_MAX_REACHED 0x884D /* 77 - Maximum number of files was reached */ +#define NWE_UNLOAD_FAILED 0x884E /* 78 - Attempted unload failed */ +#define NWE_CONN_IN_USE 0x884F /* 79 - Attempted re-use of already in use connection entry */ +#define NWE_REQ_TOO_MANY_REP_FRAGS 0x8850 /* 80 - Attempted request with too many reply fragments specified */ +#define NWE_NAME_TABLE_FULL 0x8851 /* 81 - Attempted to add a name into the name table after it was full */ +#define NWE_SOCKET_NOT_OPEN 0x8852 /* 82 - Listen was posted on unopened socket */ +#define NWE_MEMORY_MGR_ERROR 0x8853 /* 83 - Attempted enhanced memory operation failed */ +#define NWE_SFT3_ERROR 0x8854 /* 84 - An SFT3 switch occured mid-transfer */ +#define NWE_DS_PREFERRED_NOT_FOUND 0x8855 /* 85 - the preferred directory server was not established but another directory server was returned */ +#define NWE_DEVICE_NOT_RECOGNIZED 0x8856 /* 86 - used to determine if the device is not used by VISE so pass it on to the next redirector, if any. */ +#define NWE_NET_INVALID_TYPE 0x8857 /* 87 - the network type (Bind/NDS) does not match the server version */ +#define NWE_FILE_OPEN_FAILED 0x8858 /* 88 - generic open failure error, invalid path, access denied, etc.. */ +#define NWE_DS_PREFERRED_NOT_SPECIFIED 0x8859 /* 89 - no preferred name specified */ +#define NWE_SOCKET_OPEN_FAILED 0x885A /* 90 - error opening a socket */ +#define NWE_SIGNATURE_LEVEL_CONFLICT 0x8861 +#define NWE_NO_LOCK_FOUND 0x8862 /* OS/2 - process lock on conn handle failed, process ID not recognized */ +#define NWE_LOCK_TABLE_FULL 0x8863 /* OS/2 - process lock on conn handle failed, process lock table full */ +#define NWE_INVALID_MATCH_DATA 0x8864 +#define NWE_MATCH_FAILED 0x8865 +#define NWE_NO_MORE_ENTRIES 0x8866 +#define NWE_INSUFFICIENT_RESOURCES 0x8867 +#define NWE_STRING_TRANSLATION 0x8868 +#define NWE_ACCESS_VIOLATION 0x8869 +#define NWE_NOT_AUTHENTICATED 0x886A +#define NWE_INVALID_LEVEL 0x886B +#define NWE_RESOURCE_LOCK 0x886C +#define NWE_INVALID_NAME_FORMAT 0x886D +#define NWE_OBJECT_EXISTS 0x886E +#define NWE_OBJECT_NOT_FOUND 0x886F +#define NWE_UNSUPPORTED_TRAN_TYPE 0x8870 +#define NWE_INVALID_STRING_TYPE 0x8871 +#define NWE_INVALID_OWNER 0x8872 +#define NWE_UNSUPPORTED_AUTHENTICATOR 0x8873 +#define NWE_IO_PENDING 0x8874 +#define NWE_INVALID_DRIVE_NUMBER 0x8875 +#define NWE_REPLY_TRUNCATED 0x88e6 /* 230 NLM */ +#define NWE_REQUESTER_FAILURE 0x88FF + +/* Server Errors */ + +#define NWE_INSUFFICIENT_SPACE 0x8901 /* 001 */ +#define NWE_BUFFER_TOO_SMALL 0x8977 /* 119 */ +#define NWE_VOL_FLAG_NOT_SET 0x8978 /* 120 the service requested, not avail. on the selected vol. */ +#define NWE_NO_ITEMS_FOUND 0x8979 /* 121 */ +#define NWE_CONN_ALREADY_TEMP 0x897a /* 122 */ +#define NWE_CONN_ALREADY_LOGGED_IN 0x897b /* 123 */ +#define NWE_CONN_NOT_AUTHENTICATED 0x897c /* 124 */ +#define NWE_CONN_NOT_LOGGED_IN 0x897d /* 125 */ +#define NWE_NCP_BOUNDARY_CHECK_FAILED 0x897e /* 126 */ +#define NWE_LOCK_WAITING 0x897f /* 127 */ +#define NWE_LOCK_FAIL 0x8980 /* 128 */ +#define NWE_FILE_IN_USE 0x8980 /* 128 */ +#define NWE_FILE_NO_HANDLES 0x8981 /* 129 */ +#define NWE_FILE_NO_OPEN_PRIV 0x8982 /* 130 */ +#define NWE_DISK_IO_ERROR 0x8983 /* 131 */ +#define NWE_AUDITING_HARD_IO_ERROR 0x8983 /* 131 */ +#define NWE_FILE_NO_CREATE_PRIV 0x8984 /* 132 */ +#define NWE_AUDITING_NOT_SUPV 0x8984 /* 132 */ +#define NWE_FILE_NO_CREATE_DEL_PRIV 0x8985 /* 133 */ +#define NWE_FILE_EXISTS_READ_ONLY 0x8986 /* 134 */ +#define NWE_FILE_WILD_CARDS_IN_NAME 0x8987 /* 135 */ +#define NWE_FILE_INVALID_HANDLE 0x8988 /* 136 */ +#define NWE_FILE_NO_SRCH_PRIV 0x8989 /* 137 */ +#define NWE_FILE_NO_DEL_PRIV 0x898A /* 138 */ +#define NWE_FILE_NO_RENAME_PRIV 0x898B /* 139 */ +#define NWE_FILE_NO_MOD_PRIV 0x898C /* 140 */ +#define NWE_FILE_SOME_IN_USE 0x898D /* 141 */ +#define NWE_FILE_NONE_IN_USE 0x898E /* 142 */ +#define NWE_FILE_SOME_READ_ONLY 0x898F /* 143 */ +#define NWE_FILE_NONE_READ_ONLY 0x8990 /* 144 */ +#define NWE_FILE_SOME_RENAMED_EXIST 0x8991 /* 145 */ +#define NWE_FILE_NONE_RENAMED_EXIST 0x8992 /* 146 */ +#define NWE_FILE_NO_READ_PRIV 0x8993 /* 147 */ +#define NWE_FILE_NO_WRITE_PRIV 0x8994 /* 148 */ +#define NWE_FILE_READ_ONLY 0x8994 /* 148 */ +#define NWE_FILE_DETACHED 0x8995 /* 149 */ +#define NWE_SERVER_OUT_OF_MEMORY 0x8996 /* 150 */ +#define NWE_DIR_TARGET_INVALID 0x8996 /* 150 */ +#define NWE_DISK_NO_SPOOL_SPACE 0x8997 /* 151 */ +#define NWE_AUDITING_NOT_ENABLED 0x8997 /* 151 */ +#define NWE_VOL_INVALID 0x8998 /* 152 */ +#define NWE_DIR_FULL 0x8999 /* 153 */ +#define NWE_VOL_RENAMING_ACROSS 0x899A /* 154 */ +#define NWE_DIRHANDLE_INVALID 0x899B /* 155 */ +#define NWE_PATH_INVALID 0x899C /* 156 */ +#define NWE_TRUSTEES_NO_MORE 0x899C /* 156 */ +#define NWE_DIRHANDLE_NO_MORE 0x899D /* 157 */ +#define NWE_FILE_NAME_INVALID 0x899E /* 158 */ +#define NWE_DIR_ACTIVE 0x899F /* 159 */ +#define NWE_DIR_NOT_EMPTY 0x89A0 /* 160 */ +#define NWE_DIR_IO_ERROR 0x89A1 /* 161 */ +#define NWE_FILE_IO_LOCKED 0x89A2 /* 162 */ +#define NWE_TTS_RANSACTION_RESTARTED 0x89A3 /* 163 */ +#define NWE_TTS_TRANSACTION_RESTARTED 0x89A3 /* 163 */ +#define NWE_DIR_RENAME_INVALID 0x89A4 /* 164 */ +#define NWE_FILE_OPENCREAT_MODE_INVALID 0x89A5 /* 165 */ +#define NWE_ALREADY_IN_USE 0x89A6 /* 166 */ +#define NWE_AUDITING_ACTIVE 0x89A6 /* 166 */ +#define NWE_RESOURCE_TAG_INVALID 0x89A7 /* 167 */ +#define NWE_ACCESS_DENIED 0x89A8 /* 168 */ +#define NWE_AUDITING_NO_RIGHTS 0x89A8 /* 168 */ +#define NWE_DATA_STREAM_INVALID 0x89BE /* 190 */ +#define NWE_NAME_SPACE_INVALID 0x89BF /* 191 */ +#define NWE_ACCTING_NO_PRIV 0x89C0 /* 192 */ +#define NWE_ACCTING_NO_BALANCE 0x89C1 /* 193 */ +#define NWE_ACCTING_NO_CREDIT 0x89C2 /* 194 */ +#define NWE_AUDITING_RECORD_SIZE 0x89C2 /* 194 */ +#define NWE_ACCTING_TOO_MANY_HOLDS 0x89C3 /* 195 */ +#define NWE_ACCTING_DISABLED 0x89C4 /* 196 */ +#define NWE_LOGIN_LOCKOUT 0x89C5 /* 197 */ +#define NWE_CONSOLE_NO_PRIV 0x89C6 /* 198 */ +#define NWE_Q_IO_FAILURE 0x89D0 /* 208 */ +#define NWE_Q_NONE 0x89D1 /* 209 */ +#define NWE_Q_NO_SERVER 0x89D2 /* 210 */ +#define NWE_Q_NO_RIGHTS 0x89D3 /* 211 */ +#define NWE_Q_FULL 0x89D4 /* 212 */ +#define NWE_Q_NO_JOB 0x89D5 /* 213 */ +#define NWE_Q_NO_JOB_RIGHTS 0x89D6 /* 214 */ +#define NWE_PASSWORD_UNENCRYPTED 0x89D6 /* 214 */ +#define NWE_Q_IN_SERVICE 0x89D7 /* 215 */ +#define NWE_PASSWORD_NOT_UNIQUE 0x89D7 /* 215 */ +#define NWE_Q_NOT_ACTIVE 0x89D8 /* 216 */ +#define NWE_PASSWORD_TOO_SHORT 0x89D8 /* 216 */ +#define NWE_Q_STN_NOT_SERVER 0x89D9 /* 217 */ +#define NWE_LOGIN_NO_CONN 0x89D9 /* 217 */ +#define NWE_LOGIN_MAX_EXCEEDED 0x89D9 /* 217 */ +#define NWE_Q_HALTED 0x89DA /* 218 */ +#define NWE_LOGIN_UNAUTHORIZED_TIME 0x89DA /* 218 */ +#define NWE_LOGIN_UNAUTHORIZED_STATION 0x89DB /* 219 */ +#define NWE_Q_MAX_SERVERS 0x89DB /* 219 */ +#define NWE_ACCT_DISABLED 0x89DC /* 220 */ +#define NWE_PASSWORD_INVALID 0x89DE /* 222 */ +#define NWE_PASSWORD_EXPIRED 0x89DF /* 223 */ +#define NWE_LOGIN_NO_CONN_AVAIL 0x89E0 /* 224 */ +#define NWE_E_NO_MORE_USERS 0x89E7 /* 231 */ +#define NWE_BIND_NOT_ITEM_PROP 0x89E8 /* 232 */ +#define NWE_BIND_WRITE_TO_GROUP_PROP 0x89E8 /* 232 */ +#define NWE_BIND_MEMBER_ALREADY_EXISTS 0x89E9 /* 233 */ +#define NWE_BIND_NO_SUCH_MEMBER 0x89EA /* 234 */ +#define NWE_BIND_NOT_GROUP_PROP 0x89EB /* 235 */ +#define NWE_BIND_NO_SUCH_SEGMENT 0x89EC /* 236 */ +#define NWE_BIND_PROP_ALREADY_EXISTS 0x89ED /* 237 */ +#define NWE_BIND_OBJ_ALREADY_EXISTS 0x89EE /* 238 */ +#define NWE_BIND_NAME_INVALID 0x89EF /* 239 */ +#define NWE_BIND_WILDCARD_INVALID 0x89F0 /* 240 */ +#define NWE_BIND_SECURITY_INVALID 0x89F1 /* 241 */ +#define NWE_BIND_OBJ_NO_READ_PRIV 0x89F2 /* 242 */ +#define NWE_BIND_OBJ_NO_RENAME_PRIV 0x89F3 /* 243 */ +#define NWE_BIND_OBJ_NO_DELETE_PRIV 0x89F4 /* 244 */ +#define NWE_BIND_OBJ_NO_CREATE_PRIV 0x89F5 /* 245 */ +#define NWE_BIND_PROP_NO_DELETE_PRIV 0x89F6 /* 246 */ +#define NWE_BIND_PROP_NO_CREATE_PRIV 0x89F7 /* 247 */ +#define NWE_BIND_PROP_NO_WRITE_PRIV 0x89F8 /* 248 */ +#define NWE_BIND_PROP_NO_READ_PRIV 0x89F9 /* 249 */ +#define NWE_NO_FREE_CONN_SLOTS 0x89F9 /* 249 */ +#define NWE_NO_MORE_SERVER_SLOTS 0x89FA /* 250 */ +#define NWE_TEMP_REMAP_ERROR 0x89FA /* 250 */ +#define NWE_PARAMETERS_INVALID 0x89FB /* 251 */ +#define NWE_BIND_NO_SUCH_PROP 0x89FB /* 251 */ +#define NWE_NCP_NOT_SUPPORTED 0x89FB /* 251 */ +#define NWE_INET_PACKET_REQ_CANCELED 0x89FC /* 252 */ +#define NWE_SERVER_UNKNOWN 0x89FC /* 252 */ +#define NWE_MSG_Q_FULL 0x89FC /* 252 */ +#define NWE_BIND_NO_SUCH_OBJ 0x89FC /* 252 */ +#define NWE_LOCK_COLLISION 0x89FD /* 253 */ +#define NWE_CONN_NUM_INVALID 0x89FD /* 253 */ +#define NWE_PACKET_LEN_INVALID 0x89FD /* 253 */ +#define NWE_UNKNOWN_REQ 0x89FD /* 253 */ +#define NWE_BIND_LOCKED 0x89FE /* 254 */ +#define NWE_TRUSTEE_NOT_FOUND 0x89FE /* 254 */ +#define NWE_DIR_LOCKED 0x89FE /* 254 */ +#define NWE_SEM_INVALID_NAME_LEN 0x89FE /* 254 */ +#define NWE_PACKET_NOT_DELIVERABLE 0x89FE /* 254 */ +#define NWE_SOCKET_TABLE_FULL 0x89FE /* 254 */ +#define NWE_SPOOL_DIR_ERROR 0x89FE /* 254 */ +#define NWE_LOGIN_DISABLED_BY_SUPER 0x89FE /* 254 */ +#define NWE_TIMEOUT_FAILURE 0x89FE /* 254 */ +#define NWE_FILE_EXT 0x89FF /* 255 */ +#define NWE_FILE_NAME 0x89FF /* 255 */ +#define NWE_HARD_FAILURE 0x89FF /* 255 */ +#define NWE_FCB_CLOSE 0x89FF /* 255 */ +#define NWE_IO_BOUND 0x89FF /* 255 */ +#define NWE_BAD_SPOOL_PRINTER 0x89FF /* 255 */ +#define NWE_BAD_RECORD_OFFSET 0x89FF /* 255 */ +#define NWE_DRIVE_INVALID_NUM 0x89FF /* 255 */ +#define NWE_SEM_INVALID_INIT_VAL 0x89FF /* 255 */ +#define NWE_SEM_INVALID_HANDLE 0x89FF /* 255 */ +#define NWE_NO_FILES_FOUND_ERROR 0x89FF /* 255 */ +#define NWE_NO_RESPONSE_FROM_SERVER 0x89FF /* 255 */ +#define NWE_NO_OBJ_OR_BAD_PASSWORD 0x89FF /* 255 */ +#define NWE_PATH_NOT_LOCATABLE 0x89FF /* 255 */ +#define NWE_Q_FULL_ERROR 0x89FF /* 255 */ +#define NWE_REQ_NOT_OUTSTANDING 0x89FF /* 255 */ +#define NWE_SOCKET_ALREADY_OPEN 0x89FF /* 255 */ +#define NWE_LOCK_ERROR 0x89FF /* 255 */ +#define NWE_FAILURE 0x89FF /* 255 Generic Failure */ + +#endif /* !_NWERROR_H_ */ diff --git a/sys/nwfs/nwfs.h b/sys/nwfs/nwfs.h new file mode 100644 index 0000000..950d18a --- /dev/null +++ b/sys/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/nwfs/nwfs_io.c b/sys/nwfs/nwfs_io.c new file mode 100644 index 0000000..21339b3 --- /dev/null +++ b/sys/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/nwfs/nwfs_ioctl.c b/sys/nwfs/nwfs_ioctl.c new file mode 100644 index 0000000..6b4b510 --- /dev/null +++ b/sys/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/nwfs/nwfs_mount.h b/sys/nwfs/nwfs_mount.h new file mode 100644 index 0000000..56a3241 --- /dev/null +++ b/sys/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/nwfs/nwfs_node.c b/sys/nwfs/nwfs_node.c new file mode 100644 index 0000000..55e19b7 --- /dev/null +++ b/sys/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/nwfs/nwfs_node.h b/sys/nwfs/nwfs_node.h new file mode 100644 index 0000000..c88c4b4 --- /dev/null +++ b/sys/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/nwfs/nwfs_subr.c b/sys/nwfs/nwfs_subr.c new file mode 100644 index 0000000..d57d675 --- /dev/null +++ b/sys/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/nwfs/nwfs_subr.h b/sys/nwfs/nwfs_subr.h new file mode 100644 index 0000000..6112933 --- /dev/null +++ b/sys/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/nwfs/nwfs_vfsops.c b/sys/nwfs/nwfs_vfsops.c new file mode 100644 index 0000000..53ecf98 --- /dev/null +++ b/sys/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/nwfs/nwfs_vnops.c b/sys/nwfs/nwfs_vnops.c new file mode 100644 index 0000000..ab9f220 --- /dev/null +++ b/sys/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); +} |