diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index 3cec3f91e253..d8b2a3d52f8b 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -121,6 +121,12 @@ dev/syscons/scvidctl.c optional sc dev/syscons/scvtb.c optional sc dev/syscons/syscons.c optional sc dev/syscons/sysmouse.c optional sc +fs/smbfs/smbfs_io.c optional smbfs +fs/smbfs/smbfs_node.c optional smbfs +fs/smbfs/smbfs_smb.c optional smbfs +fs/smbfs/smbfs_subr.c optional smbfs +fs/smbfs/smbfs_vfsops.c optional smbfs +fs/smbfs/smbfs_vnops.c optional smbfs gnu/i386/fpemul/div_small.s optional gpl_math_emulate gnu/i386/fpemul/errors.c optional gpl_math_emulate gnu/i386/fpemul/fpu_arith.c optional gpl_math_emulate @@ -362,6 +368,7 @@ isa/vga_isa.c optional vga kern/imgact_aout.c standard kern/imgact_gzip.c optional gzip kern/link_aout.c standard +kern/md4c.c optional netsmb kern/subr_diskmbr.c standard libkern/divdi3.c standard libkern/moddi3.c standard @@ -369,3 +376,12 @@ libkern/qdivrem.c standard libkern/ucmpdi2.c standard libkern/udivdi3.c standard libkern/umoddi3.c standard +netsmb/smb_conn.c optional netsmb +netsmb/smb_crypt.c optional netsmb +netsmb/smb_dev.c optional netsmb +netsmb/smb_iod.c optional netsmb +netsmb/smb_rq.c optional netsmb +netsmb/smb_smb.c optional netsmb +netsmb/smb_subr.c optional netsmb +netsmb/smb_trantcp.c optional netsmb +netsmb/smb_usr.c optional netsmb diff --git a/sys/conf/options.i386 b/sys/conf/options.i386 index 65cebb7b1ad8..f1d7686a9a60 100644 --- a/sys/conf/options.i386 +++ b/sys/conf/options.i386 @@ -198,6 +198,13 @@ NDGBPORTS opt_dgb.h DEV_NPX opt_npx.h DEV_APM opt_apm.h +# SMB/CIFS requester +NETSMB +NETSMBCRYPTO + +# SMB/CIFS filesystem +SMBFS + # ------------------------------- # EOF # ------------------------------- diff --git a/sys/fs/smbfs/smbfs.h b/sys/fs/smbfs/smbfs.h new file mode 100644 index 000000000000..0b9d88bea36b --- /dev/null +++ b/sys/fs/smbfs/smbfs.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2000-2001, 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 _SMBFS_SMBFS_H_ +#define _SMBFS_SMBFS_H_ + +#define VT_SMBFS 24 + +#define SMBFS_VERMAJ 1 +#define SMBFS_VERMIN 1012 +#define SMBFS_VERSION (SMBFS_VERMAJ*100000 + SMBFS_VERMIN) +#define SMBFS_VFSNAME "smbfs" + +/* Values for flags */ +#define SMBFS_MOUNT_SOFT 0x0001 +#define SMBFS_MOUNT_INTR 0x0002 +#define SMBFS_MOUNT_STRONG 0x0004 +#define SMBFS_MOUNT_HAVE_NLS 0x0008 +#define SMBFS_MOUNT_NO_LONG 0x0010 + +#define SMBFS_MAXPATHCOMP 256 /* maximum number of path components */ + + +/* Layout of the mount control block for a netware file system. */ +struct smbfs_args { + int version; + int dev; + u_int flags; + char mount_point[MAXPATHLEN]; + u_char root_path[512+1]; + uid_t uid; + gid_t gid; + mode_t file_mode; + mode_t dir_mode; + int caseopt; +}; + +#ifdef _KERNEL + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_SMBFSMNT); +#endif + +#ifndef VI_LOCK +#define VI_LOCK(vp) smb_sl_lock(&(vp)->v_interlock) +#define VI_UNLOCK(vp) smb_sl_unlock(&(vp)->v_interlock) +#endif + +struct smbnode; +struct smb_share; +struct u_cred; +struct vop_ioctl_args; +struct buf; + +struct smbmount { + struct smbfs_args sm_args; + struct mount * sm_mp; + struct smbnode * sm_root; + struct ucred * sm_owner; + int sm_flags; + long sm_nextino; + struct smb_share * sm_share; +/* struct simplelock sm_npslock;*/ + struct smbnode * sm_npstack[SMBFS_MAXPATHCOMP]; + int sm_caseopt; + struct lock sm_hashlock; + LIST_HEAD(smbnode_hashhead, smbnode) *sm_hash; + u_long sm_hashlen; +}; + +#define VFSTOSMBFS(mp) ((struct smbmount *)((mp)->mnt_data)) +#define SMBFSTOVFS(smp) ((struct mount *)((smp)->sm_mp)) +#define VTOVFS(vp) ((vp)->v_mount) +#define VTOSMBFS(vp) (VFSTOSMBFS(VTOVFS(vp))) + +int smbfs_ioctl(struct vop_ioctl_args *ap); +int smbfs_doio(struct buf *bp, struct ucred *cr, struct proc *p); +int smbfs_vinvalbuf(struct vnode *vp, int flags, struct ucred *cred, + struct proc *p, int intrflg); +#endif /* KERNEL */ + +#endif /* _SMBFS_SMBFS_H_ */ diff --git a/sys/fs/smbfs/smbfs_io.c b/sys/fs/smbfs/smbfs_io.c new file mode 100644 index 000000000000..b275d7436783 --- /dev/null +++ b/sys/fs/smbfs/smbfs_io.c @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2000-2001, 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 +#include +#include /* defines plimit structure in proc struct */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if __FreeBSD_version < 400000 +#include +#endif +#include +#include +#include +#include +#include +/* +#include +*/ +#include +#include +#include + +#include +#include +#include + +/*#define SMBFS_RWGENERIC*/ + +extern int smbfs_pbuf_freecnt; + +static int smbfs_fastlookup = 1; + +extern struct linker_set sysctl_vfs_smbfs; + +SYSCTL_DECL(_vfs_smbfs); +SYSCTL_INT(_vfs_smbfs, OID_AUTO, fastlookup, CTLFLAG_RW, &smbfs_fastlookup, 0, ""); + + +#define DE_SIZE (sizeof(struct dirent)) + +static int +smbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred) +{ + struct dirent de; + struct componentname cn; + struct smb_cred scred; + struct smbfs_fctx *ctx; + struct vnode *newvp; + struct smbnode *np = VTOSMB(vp); + int error/*, *eofflag = ap->a_eofflag*/; + long offset, limit; + + np = VTOSMB(vp); + SMBVDEBUG("dirname='%s'\n", np->n_name); + smb_makescred(&scred, uio->uio_procp, cred); + offset = uio->uio_offset / DE_SIZE; /* offset in the directory */ + limit = uio->uio_resid / DE_SIZE; + if (uio->uio_resid < DE_SIZE || uio->uio_offset < 0) + return EINVAL; + while (limit && offset < 2) { + limit--; + bzero((caddr_t)&de, DE_SIZE); + de.d_reclen = DE_SIZE; + de.d_fileno = (offset == 0) ? np->n_ino : + (np->n_parent ? np->n_parent->n_ino : 2); + if (de.d_fileno == 0) + de.d_fileno = 0x7ffffffd + offset; + de.d_namlen = offset + 1; + de.d_name[0] = '.'; + de.d_name[1] = '.'; + de.d_name[offset + 1] = '\0'; + de.d_type = DT_DIR; + error = uiomove((caddr_t)&de, DE_SIZE, uio); + if (error) + return error; + offset++; + uio->uio_offset += DE_SIZE; + } + if (limit == 0) + return 0; + if (offset != np->n_dirofs || np->n_dirseq == NULL) { + SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs); + if (np->n_dirseq) { + smbfs_findclose(np->n_dirseq, &scred); + np->n_dirseq = NULL; + } + np->n_dirofs = 2; + error = smbfs_findopen(np, "*", 1, + SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, + &scred, &ctx); + if (error) { + SMBVDEBUG("can not open search, error = %d", error); + return error; + } + np->n_dirseq = ctx; + } else + ctx = np->n_dirseq; + while (np->n_dirofs < offset) { + error = smbfs_findnext(ctx, offset - np->n_dirofs++, &scred); + if (error) { + smbfs_findclose(np->n_dirseq, &scred); + np->n_dirseq = NULL; + return error == ENOENT ? 0 : error; + } + } + error = 0; + for (; limit; limit--, offset++) { + error = smbfs_findnext(ctx, limit, &scred); + if (error) + break; + np->n_dirofs++; + bzero((caddr_t)&de, DE_SIZE); + de.d_reclen = DE_SIZE; + de.d_fileno = ctx->f_attr.fa_ino; + de.d_type = (ctx->f_attr.fa_attr & SMB_FA_DIR) ? DT_DIR : DT_REG; + de.d_namlen = ctx->f_nmlen; + bcopy(ctx->f_name, de.d_name, de.d_namlen); + de.d_name[de.d_namlen] = '\0'; + if (smbfs_fastlookup) { + error = smbfs_nget(vp->v_mount, vp, ctx->f_name, + ctx->f_nmlen, &ctx->f_attr, &newvp); + if (!error) { + cn.cn_nameptr = de.d_name; + cn.cn_namelen = de.d_namlen; + cache_enter(vp, newvp, &cn); + vput(newvp); + } + } + error = uiomove((caddr_t)&de, DE_SIZE, uio); + if (error) + break; + } + if (error == ENOENT) + error = 0; + uio->uio_offset = offset * DE_SIZE; + return error; +} + +int +smbfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred) +{ + struct smbmount *smp = VFSTOSMBFS(vp->v_mount); + struct smbnode *np = VTOSMB(vp); + struct proc *p; + struct vattr vattr; + struct smb_cred scred; + int error, lks; + + if (vp->v_type != VREG && vp->v_type != VDIR) { + SMBFSERR("vn types other than VREG or VDIR are unsupported !\n"); + return EIO; + } + if (uiop->uio_resid == 0) + return 0; + if (uiop->uio_offset < 0) + return EINVAL; +/* if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize) + return EFBIG;*/ + p = uiop->uio_procp; + if (vp->v_type == VDIR) { + lks = LK_EXCLUSIVE;/*lockstatus(&vp->v_lock, p);*/ + if (lks == LK_SHARED) + vn_lock(vp, LK_UPGRADE | LK_RETRY, p); + error = smbfs_readvdir(vp, uiop, cred); + if (lks == LK_SHARED) + vn_lock(vp, LK_DOWNGRADE | LK_RETRY, p); + return error; + } + +/* biosize = SSTOCN(smp->sm_share)->sc_txmax;*/ + if (np->n_flag & NMODIFIED) { + smbfs_attr_cacheremove(vp); + error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) + return error; + np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; + } else { + error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) + return error; + if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) { + error = smbfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + if (error) + return error; + np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; + } + } + smb_makescred(&scred, p, cred); + return smb_read(smp->sm_share, np->n_fid, uiop, &scred); +} + +int +smbfs_writevnode(struct vnode *vp, struct uio *uiop, + struct ucred *cred, int ioflag) +{ + struct smbmount *smp = VTOSMBFS(vp); + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + struct proc *p; + int error = 0; + + if (vp->v_type != VREG) { + SMBERROR("vn types other than VREG unsupported !\n"); + return EIO; + } + SMBVDEBUG("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 > smp->nm_maxfilesize) + return (EFBIG);*/ + p = uiop->uio_procp; + if (ioflag & (IO_APPEND | IO_SYNC)) { + if (np->n_flag & NMODIFIED) { + smbfs_attr_cacheremove(vp); + error = smbfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + if (error) + return error; + } + if (ioflag & IO_APPEND) { +#if notyet + /* + * File size can be changed by another client + */ + smbfs_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; + } + smb_makescred(&scred, p, cred); + error = smb_write(smp->sm_share, np->n_fid, uiop, &scred); + SMBVDEBUG("after: ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); + if (!error) { + if (uiop->uio_offset > np->n_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 +smbfs_doio(struct buf *bp, struct ucred *cr, struct proc *p) +{ + struct vnode *vp = bp->b_vp; + struct smbmount *smp = VFSTOSMBFS(vp->v_mount); + struct smbnode *np = VTOSMB(vp); + struct uio uio, *uiop = &uio; + struct iovec io; + struct smb_cred scred; + int error = 0; + + uiop->uio_iov = &io; + uiop->uio_iovcnt = 1; + uiop->uio_segflg = UIO_SYSSPACE; + uiop->uio_procp = p; + + smb_makescred(&scred, p, cr); + + if (bp->b_iocmd == BIO_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 = smb_read(smp->sm_share, np->n_fid, uiop, &scred); + 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; + default: + printf("smbfs_doio: type %x unexpected\n",vp->v_type); + break; + }; + if (error) { + bp->b_error = error; + bp->b_ioflags |= BIO_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 = smb_write(smp->sm_share, np->n_fid, uiop, &scred); + 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 BIO_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_ioflags |= BIO_ERROR; + bp->b_error = error; + } + bp->b_dirtyoff = bp->b_dirtyend = 0; + } + } else { + bp->b_resid = 0; + bufdone(bp); + return 0; + } + } + bp->b_resid = uiop->uio_resid; + bufdone(bp); + return error; +} + +/* + * Vnode op for VM getpages. + * Wish wish .... get rid from multiple IO routines + */ +int +smbfs_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; +{ +#ifdef SMBFS_RWGENERIC + 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 smbmount *smp; + struct smbnode *np; + struct smb_cred scred; + vm_page_t *pages; + + vp = ap->a_vp; + p = curproc; /* XXX */ + cred = curproc->p_ucred; /* XXX */ + np = VTOSMB(vp); + smp = VFSTOSMBFS(vp->v_mount); + pages = ap->a_m; + count = ap->a_count; + + if (vp->v_object == NULL) { + printf("smbfs_getpages: called with non-merged cache vnode??\n"); + return VM_PAGER_ERROR; + } + smb_makescred(&scred, p, cred); + +#if __FreeBSD_version >= 400000 + bp = getpbuf(&smbfs_pbuf_freecnt); +#else + bp = getpbuf(); +#endif + 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 = smb_read(smp->sm_share, np->n_fid, &uio, &scred); + pmap_qremove(kva, npages); + +#if __FreeBSD_version >= 400000 + relpbuf(bp, &smbfs_pbuf_freecnt); +#else + relpbuf(bp); +#endif + + if (error && (uio.uio_resid == count)) { + printf("smbfs_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 /* SMBFS_RWGENERIC */ +} + +/* + * 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 +smbfs_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; + +#ifdef SMBFS_RWGENERIC + 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 smbmount *smp; + struct smbnode *np; + struct smb_cred scred; + vm_page_t *pages; + + p = curproc; /* XXX */ + cred = p->p_ucred; /* XXX */ +/* VOP_OPEN(vp, FWRITE, cred, p);*/ + np = VTOSMB(vp); + smp = VFSTOSMBFS(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; + } + +#if __FreeBSD_version >= 400000 + bp = getpbuf(&smbfs_pbuf_freecnt); +#else + bp = getpbuf(); +#endif + 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; + SMBVDEBUG("ofs=%d,resid=%d\n",(int)uio.uio_offset, uio.uio_resid); + + smb_makescred(&scred, p, cred); + error = smb_write(smp->sm_share, np->n_fid, &uio, &scred); +/* VOP_CLOSE(vp, FWRITE, cred, p);*/ + SMBVDEBUG("paged write done: %d\n", error); + + pmap_qremove(kva, npages); +#if __FreeBSD_version >= 400000 + relpbuf(bp, &smbfs_pbuf_freecnt); +#else + relpbuf(bp); +#endif + + 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 /* SMBFS_RWGENERIC */ +} + +/* + * Flush and invalidate all dirty buffers. If another process is already + * doing the flush, just wait for completion. + */ +int +smbfs_vinvalbuf(vp, flags, cred, p, intrflg) + struct vnode *vp; + int flags; + struct ucred *cred; + struct proc *p; + int intrflg; +{ + struct smbnode *np = VTOSMB(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, "smfsvinv", slptimeo); + error = smb_proc_intr(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/smbfs/smbfs_node.c b/sys/fs/smbfs/smbfs_node.c new file mode 100644 index 000000000000..0d98605c4a97 --- /dev/null +++ b/sys/fs/smbfs/smbfs_node.c @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2000-2001 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include +#include */ +#include + +#include +#include +#include + +#include +#include +#include + +#define SMBFS_NOHASH(smp, hval) (&(smp)->sm_hash[(hval) & (smp)->sm_hashlen]) +#define smbfs_hash_lock(smp, p) lockmgr(&smp->sm_hashlock, LK_EXCLUSIVE, NULL, p) +#define smbfs_hash_unlock(smp, p) lockmgr(&smp->sm_hashlock, LK_RELEASE, NULL, p) + + +extern vop_t **smbfs_vnodeop_p; + +MALLOC_DEFINE(M_SMBNODE, "SMBFS node", "SMBFS vnode private part"); +static MALLOC_DEFINE(M_SMBNODENAME, "SMBFS nname", "SMBFS node name"); + +int smbfs_hashprint(struct mount *mp); + +#if 0 +extern struct linker_set sysctl_vfs_smbfs; +#ifdef SYSCTL_DECL +SYSCTL_DECL(_vfs_smbfs); +#endif +SYSCTL_PROC(_vfs_smbfs, OID_AUTO, vnprint, CTLFLAG_WR|CTLTYPE_OPAQUE, + NULL, 0, smbfs_hashprint, "S,vnlist", "vnode hash"); +#endif + +#define FNV_32_PRIME ((u_int32_t) 0x01000193UL) +#define FNV1_32_INIT ((u_int32_t) 33554467UL) + +u_int32_t +smbfs_hash(const u_char *name, int nmlen) +{ + u_int32_t v; + + for (v = FNV1_32_INIT; nmlen; name++, nmlen--) { + v *= FNV_32_PRIME; + v ^= (u_int32_t)*name; + } + return v; +} + +int +smbfs_hashprint(struct mount *mp) +{ + struct smbmount *smp = VFSTOSMBFS(mp); + struct smbnode_hashhead *nhpp; + struct smbnode *np; + int i; + + for(i = 0; i <= smp->sm_hashlen; i++) { + nhpp = &smp->sm_hash[i]; + LIST_FOREACH(np, nhpp, n_hash) + vprint(NULL, SMBTOV(np)); + } + return 0; +} + +static char * +smbfs_name_alloc(const u_char *name, int nmlen) +{ + u_char *cp; + + nmlen++; +#ifdef SMBFS_NAME_DEBUG + cp = malloc(nmlen + 2 + sizeof(int), M_SMBNODENAME, M_WAITOK); + *(int*)cp = nmlen; + cp += sizeof(int); + cp[0] = 0xfc; + cp++; + bcopy(name, cp, nmlen - 1); + cp[nmlen] = 0xfe; +#else + cp = malloc(nmlen, M_SMBNODENAME, M_WAITOK); + bcopy(name, cp, nmlen - 1); +#endif + cp[nmlen - 1] = 0; + return cp; +} + +static void +smbfs_name_free(u_char *name) +{ +#ifdef SMBFS_NAME_DEBUG + int nmlen, slen; + u_char *cp; + + cp = name; + cp--; + if (*cp != 0xfc) { + printf("First byte of name entry '%s' corrupted\n", name); + Debugger("ditto"); + } + cp -= sizeof(int); + nmlen = *(int*)cp; + slen = strlen(name) + 1; + if (nmlen != slen) { + printf("Name length mismatch: was %d, now %d name '%s'\n", + nmlen, slen, name); + Debugger("ditto"); + } + if (name[nmlen] != 0xfe) { + printf("Last byte of name entry '%s' corrupted\n", name); + Debugger("ditto"); + } + free(cp, M_SMBNODENAME); +#else + free(name, M_SMBNODENAME); +#endif +} + +static int +smbfs_node_alloc(struct mount *mp, struct vnode *dvp, + const char *name, int nmlen, struct smbfattr *fap, struct vnode **vpp) +{ + struct proc *p = curproc; /* XXX */ + struct smbmount *smp = VFSTOSMBFS(mp); + struct smbnode_hashhead *nhpp; + struct smbnode *np, *np2, *dnp; + struct vnode *vp; + u_long hashval; + int error; + + *vpp = NULL; + if (smp->sm_root != NULL && dvp == NULL) { + SMBERROR("do not allocate root vnode twice!\n"); + return EINVAL; + } + if (nmlen == 2 && bcmp(name, "..", 2) == 0) { + if (dvp == NULL) + return EINVAL; + vp = VTOSMB(dvp)->n_parent->n_vnode; + error = vget(vp, LK_EXCLUSIVE, p); + if (error == 0) + *vpp = vp; + return error; + } else if (nmlen == 1 && name[0] == '.') { + SMBERROR("do not call me with dot!\n"); + return EINVAL; + } + dnp = dvp ? VTOSMB(dvp) : NULL; + if (dnp == NULL && dvp != NULL) { + vprint("smbfs_node_alloc: dead parent vnode", dvp); + return EINVAL; + } + hashval = smbfs_hash(name, nmlen); +retry: + smbfs_hash_lock(smp, p); +loop: + nhpp = SMBFS_NOHASH(smp, hashval); + LIST_FOREACH(np, nhpp, n_hash) { + vp = SMBTOV(np); + if (np->n_parent != dnp || + np->n_nmlen != nmlen || bcmp(name, np->n_name, nmlen) != 0) + continue; + VI_LOCK(vp); + smbfs_hash_unlock(smp, p); + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p) != 0) + goto retry; + *vpp = vp; + return 0; + } + smbfs_hash_unlock(smp, p); + /* + * If we don't have node attributes, then it is an explicit lookup + * for an existing vnode. + */ + if (fap == NULL) + return ENOENT; + + MALLOC(np, struct smbnode *, sizeof *np, M_SMBNODE, M_WAITOK); + error = getnewvnode(VT_SMBFS, mp, smbfs_vnodeop_p, &vp); + if (error) { + FREE(np, M_SMBNODE); + return error; + } + vp->v_type = fap->fa_attr & SMB_FA_DIR ? VDIR : VREG; + bzero(np, sizeof(*np)); + vp->v_data = np; + np->n_vnode = vp; + np->n_mount = VFSTOSMBFS(mp); + np->n_nmlen = nmlen; + np->n_name = smbfs_name_alloc(name, nmlen); + np->n_ino = fap->fa_ino; + + if (dvp) { + np->n_parent = dnp; + if (/*vp->v_type == VDIR &&*/ (dvp->v_flag & VROOT) == 0) { + vref(dvp); + np->n_flag |= NREFPARENT; + } + } else if (vp->v_type == VREG) + SMBERROR("new vnode '%s' born without parent ?\n", np->n_name); + + lockinit(&vp->v_lock, PINOD, "smbnode", 0, LK_CANRECURSE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + smbfs_hash_lock(smp, p); + LIST_FOREACH(np2, nhpp, n_hash) { + if (np2->n_parent != dnp || + np2->n_nmlen != nmlen || bcmp(name, np2->n_name, nmlen) != 0) + continue; + vput(vp); +/* smb_name_free(np->n_name); + FREE(np, M_SMBNODE);*/ + goto loop; + } + LIST_INSERT_HEAD(nhpp, np, n_hash); + smbfs_hash_unlock(smp, p); + *vpp = vp; + return 0; +} + +int +smbfs_nget(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, + struct smbfattr *fap, struct vnode **vpp) +{ + struct smbnode *np; + struct vnode *vp; + int error; + + *vpp = NULL; + error = smbfs_node_alloc(mp, dvp, name, nmlen, fap, &vp); + if (error) + return error; + np = VTOSMB(vp); + if (fap) + smbfs_attr_cacheenter(vp, fap); + *vpp = vp; + return 0; +} + +/* + * Free smbnode, and give vnode back to system + */ +int +smbfs_reclaim(ap) + struct vop_reclaim_args /* { + struct vnode *a_vp; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct proc *p = ap->a_p; + struct vnode *dvp; + struct smbnode *np = VTOSMB(vp); + struct smbmount *smp = VTOSMBFS(vp); + + SMBVDEBUG("%s,%d\n", np->n_name, vp->v_usecount); + + smbfs_hash_lock(smp, p); + + dvp = (np->n_parent && (np->n_flag & NREFPARENT)) ? + np->n_parent->n_vnode : NULL; + + if (np->n_hash.le_prev) + LIST_REMOVE(np, n_hash); + cache_purge(vp); + if (smp->sm_root == np) { + SMBVDEBUG("root vnode\n"); + smp->sm_root = NULL; + } + vp->v_data = NULL; + smbfs_hash_unlock(smp, p); + if (np->n_name) + smbfs_name_free(np->n_name); + FREE(np, M_SMBNODE); + if (dvp) { + VI_LOCK(dvp); + if (dvp->v_usecount >= 1) { + VI_UNLOCK(dvp); + vrele(dvp); + } else { + VI_UNLOCK(dvp); + SMBERROR("BUG: negative use count for parent!\n"); + } + } + return 0; +} + +int +smbfs_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 smbnode *np = VTOSMB(vp); + struct smb_cred scred; + int error; + + SMBVDEBUG("%s: %d\n", VTOSMB(vp)->n_name, vp->v_usecount); + if (np->n_opencount) { + error = smbfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + smb_makescred(&scred, p, cred); + error = smbfs_smb_close(np->n_mount->sm_share, np->n_fid, + &np->n_mtime, &scred); + np->n_opencount = 0; + } + VOP_UNLOCK(vp, 0, p); + return (0); +} +/* + * routines to maintain vnode attributes cache + * smbfs_attr_cacheenter: unpack np.i to vattr structure + */ +void +smbfs_attr_cacheenter(struct vnode *vp, struct smbfattr *fap) +{ + struct smbnode *np = VTOSMB(vp); + + if (vp->v_type == VREG) { + if (np->n_size != fap->fa_size) { + np->n_size = fap->fa_size; + vnode_pager_setsize(vp, np->n_size); + } + } else if (vp->v_type == VDIR) { + np->n_size = 16384; /* should be a better way ... */ + } else + return; + np->n_mtime = fap->fa_mtime; + np->n_dosattr = fap->fa_attr; + np->n_attrage = time_second; + return; +} + +int +smbfs_attr_cachelookup(struct vnode *vp, struct vattr *va) +{ + struct smbnode *np = VTOSMB(vp); + struct smbmount *smp = VTOSMBFS(vp); + int diff; + + diff = time_second - np->n_attrage; + if (diff > 2) /* XXX should be configurable */ + return ENOENT; + va->va_type = vp->v_type; /* vnode type (for create) */ + if (vp->v_type == VREG) { + va->va_mode = smp->sm_args.file_mode; /* files access mode and type */ + } else if (vp->v_type == VDIR) { + va->va_mode = smp->sm_args.dir_mode; /* files access mode and type */ + } else + return EINVAL; + va->va_size = np->n_size; + va->va_nlink = 1; /* number of references to file */ + va->va_uid = smp->sm_args.uid; /* owner user id */ + va->va_gid = smp->sm_args.gid; /* owner group id */ + va->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; + va->va_fileid = np->n_ino; /* file id */ + if (va->va_fileid == 0) + va->va_fileid = 2; + va->va_blocksize = SSTOVC(smp->sm_share)->vc_txmax; + va->va_mtime = np->n_mtime; + 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 */ + return 0; +} diff --git a/sys/fs/smbfs/smbfs_node.h b/sys/fs/smbfs/smbfs_node.h new file mode 100644 index 000000000000..1a5a643a2e0a --- /dev/null +++ b/sys/fs/smbfs/smbfs_node.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2000-2001, 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 _FS_SMBFS_NODE_H_ +#define _FS_SMBFS_NODE_H_ + +#define SMBFS_ROOT_INO 2 /* just like in UFS */ + +/* Bits for smbnode.n_flag */ +#define NFLUSHINPROG 0x0001 +#define NFLUSHWANT 0x0002 /* they should gone ... */ +#define NMODIFIED 0x0004 /* bogus, until async IO implemented */ +/*efine NNEW 0x0008*//* smb/vnode has been allocated */ +#define NREFPARENT 0x0010 /* node holds parent from recycling */ + +struct smbfs_fctx; + +struct smbnode { +#ifndef FB_CURRENT + struct lock n_lock; /* smbnode lock. (mbf) */ +#endif + int n_flag; + struct smbnode * n_parent; + struct vnode * n_vnode; + struct smbmount * n_mount; + time_t n_attrage; /* attributes cache time */ +/* time_t n_ctime;*/ + struct timespec n_mtime; /* modify time */ + struct timespec n_atime; /* last access time */ + u_quad_t n_size; + long n_ino; + int n_dosattr; + int n_opencount; + u_int16_t n_fid; /* file handle */ + int n_rwstate; /* granted access mode */ + u_char n_nmlen; + u_char * n_name; + struct smbfs_fctx * n_dirseq; /* ff context */ + long n_dirofs; /* last ff offset */ + struct lockf * n_lockf; /* Locking records of file */ + LIST_ENTRY(smbnode) n_hash; +}; + +#define VTOSMB(vp) ((struct smbnode *)(vp)->v_data) +#define SMBTOV(np) ((struct vnode *)(np)->n_vnode) + +struct vop_getpages_args; +struct vop_inactive_args; +struct vop_putpages_args; +struct vop_reclaim_args; +struct ucred; +struct uio; +struct smbfattr; + +int smbfs_inactive(struct vop_inactive_args *); +int smbfs_reclaim(struct vop_reclaim_args *); +int smbfs_nget(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, + struct smbfattr *fap, struct vnode **vpp); +u_int32_t smbfs_hash(const u_char *name, int nmlen); + +int smbfs_getpages(struct vop_getpages_args *); +int smbfs_putpages(struct vop_putpages_args *); +int smbfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred); +int smbfs_writevnode(struct vnode *vp, struct uio *uiop, struct ucred *cred, int ioflag); +void smbfs_attr_cacheenter(struct vnode *vp, struct smbfattr *fap); +int smbfs_attr_cachelookup(struct vnode *vp ,struct vattr *va); + +#define smbfs_attr_cacheremove(vp) VTOSMB(vp)->n_attrage = 0 + +#endif /* _FS_SMBFS_NODE_H_ */ diff --git a/sys/fs/smbfs/smbfs_smb.c b/sys/fs/smbfs/smbfs_smb.c new file mode 100644 index 000000000000..373d5c141a61 --- /dev/null +++ b/sys/fs/smbfs/smbfs_smb.c @@ -0,0 +1,1273 @@ +/* + * Copyright (c) 2000-2001 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_MD5_HASH +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +/* + * Lack of inode numbers leads us to the problem of generating them. + * Partially this problem can be solved by having a dir/file cache + * with inode numbers generated from the incremented by one counter. + * However this way will require too much kernel memory, gives all + * sorts of locking and consistency problems, not to mentinon counter overflows. + * So, I'm decided to use a hash function to generate pseudo random (and unique) + * inode numbers. + */ +static long +smbfs_getino(struct smbnode *dnp, const char *name, int nmlen) +{ +#ifdef USE_MD5_HASH + MD5_CTX md5; + u_int32_t state[4]; + long ino; + int i; + + MD5Init(&md5); + MD5Update(&md5, name, nmlen); + MD5Final((u_char *)state, &md5); + for (i = 0, ino = 0; i < 4; i++) + ino += state[i]; + return dnp->n_ino + ino; +#endif + u_int32_t ino; + + ino = dnp->n_ino + smbfs_hash(name, nmlen); + if (ino <= 2) + ino += 3; + return ino; +} + +static int +smbfs_smb_lockandx(struct smbnode *np, int op, u_int32_t pid, off_t start, off_t end, + struct smb_cred *scred) +{ + struct smb_share *ssp = np->n_mount->sm_share; + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + u_char ltype = 0; + int error; + + if (op == SMB_LOCK_SHARED) + ltype |= SMB_LOCKING_ANDX_SHARED_LOCK; + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); /* secondary command */ + mb_put_uint8(mbp, 0); /* MBZ */ + mb_put_uint16le(mbp, 0); + mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint8(mbp, ltype); /* locktype */ + mb_put_uint8(mbp, 0); /* oplocklevel - 0 seems is NO_OPLOCK */ + mb_put_uint32le(mbp, 0); /* timeout - break immediately */ + mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 1 : 0); + mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 0 : 1); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint16le(mbp, pid); + mb_put_uint32le(mbp, start); + mb_put_uint32le(mbp, end - start); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_lock(struct smbnode *np, int op, caddr_t id, + off_t start, off_t end, struct smb_cred *scred) +{ + struct smb_share *ssp = np->n_mount->sm_share; + + if (SMB_DIALECT(SSTOVC(ssp)) < SMB_DIALECT_LANMAN1_0) + /* + * TODO: use LOCK_BYTE_RANGE here. + */ + return EINVAL; + else + return smbfs_smb_lockandx(np, op, (u_int32_t)id, start, end, scred); +} + +int +smbfs_smb_statfs2(struct smb_share *ssp, struct statfs *sbp, + struct smb_cred *scred) +{ + struct smb_t2rq *t2p; + struct mbchain *mbp; + struct mdchain *mdp; + u_int16_t bsize; + u_int32_t units, bpu, funits; + int error; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FS_INFORMATION, + scred, &t2p); + if (error) + return error; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, SMB_INFO_ALLOCATION); + t2p->t2_maxpcount = 4; + t2p->t2_maxdcount = 4 * 4 + 2; + error = smb_t2_request(t2p); + if (error) { + smb_t2_done(t2p); + return error; + } + mdp = &t2p->t2_rdata; + md_get_uint32(mdp, NULL); /* fs id */ + md_get_uint32le(mdp, &bpu); + md_get_uint32le(mdp, &units); + md_get_uint32le(mdp, &funits); + md_get_uint16le(mdp, &bsize); + sbp->f_bsize = bpu * bsize; /* fundamental file system block size */ + sbp->f_blocks= units; /* total data blocks in file system */ + sbp->f_bfree = funits; /* free blocks in fs */ + sbp->f_bavail= funits; /* free blocks avail to non-superuser */ + sbp->f_files = 0xffff; /* total file nodes in file system */ + sbp->f_ffree = 0xffff; /* free file nodes in fs */ + smb_t2_done(t2p); + return 0; +} + +int +smbfs_smb_statfs(struct smb_share *ssp, struct statfs *sbp, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct mdchain *mdp; + u_int16_t units, bpu, bsize, funits; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_QUERY_INFORMATION_DISK, scred); + if (error) + return error; + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) { + smb_rq_done(rqp); + return error; + } + smb_rq_getreply(rqp, &mdp); + md_get_uint16le(mdp, &units); + md_get_uint16le(mdp, &bpu); + md_get_uint16le(mdp, &bsize); + md_get_uint16le(mdp, &funits); + sbp->f_bsize = bpu * bsize; /* fundamental file system block size */ + sbp->f_blocks= units; /* total data blocks in file system */ + sbp->f_bfree = funits; /* free blocks in fs */ + sbp->f_bavail= funits; /* free blocks avail to non-superuser */ + sbp->f_files = 0xffff; /* total file nodes in file system */ + sbp->f_ffree = 0xffff; /* free file nodes in fs */ + smb_rq_done(rqp); + return 0; +} + +int +smbfs_smb_setfsize(struct smbnode *np, int newsize, struct smb_cred *scred) +{ + struct smb_share *ssp = np->n_mount->sm_share; + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_WRITE, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, 0); + mb_put_uint32le(mbp, newsize); + mb_put_uint16le(mbp, 0); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_DATA); + mb_put_uint16le(mbp, 0); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return error; +} + + +/* + * Set DOS file attributes. mtime should be NULL for dialects above lm10 + */ +int +smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + u_long time; + int error, svtz; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred); + if (error) + return error; + svtz = SSTOVC(ssp)->vc_sopt.sv_tz; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, attr); + if (mtime) { + smb_time_local2server(mtime, svtz, &time); + } else + time = 0; + mb_put_uint32le(mbp, time); /* mtime */ + mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); + if (error) + break; + mb_put_uint8(mbp, SMB_DT_ASCII); + mb_put_uint8(mbp, 0); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBERROR("%d\n", error); + if (error) + break; + } while(0); + smb_rq_done(rqp); + return error; +} + +/* + * Note, win95 doesn't support this call. + */ +int +smbfs_smb_setptime2(struct smbnode *np, struct timespec *mtime, + struct timespec *atime, int attr, struct smb_cred *scred) +{ + struct smb_t2rq *t2p; + struct smb_share *ssp = np->n_mount->sm_share; + struct smb_vc *vcp = SSTOVC(ssp); + struct mbchain *mbp; + u_int16_t date, time; + int error, tzoff; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION, + scred, &t2p); + if (error) + return error; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, SMB_INFO_STANDARD); + mb_put_uint32le(mbp, 0); /* MBZ */ + error = smbfs_fullpath(mbp, vcp, np, NULL, 0); + if (error) { + smb_t2_done(t2p); + return error; + } + tzoff = vcp->vc_sopt.sv_tz; + mbp = &t2p->t2_tdata; + mb_init(mbp); + mb_put_uint32le(mbp, 0); /* creation time */ + if (atime) + smb_time_unix2dos(atime, tzoff, &date, &time, NULL); + else + time = date = 0; + mb_put_uint16le(mbp, date); + mb_put_uint16le(mbp, time); + if (mtime) + smb_time_unix2dos(mtime, tzoff, &date, &time, NULL); + else + time = date = 0; + mb_put_uint16le(mbp, date); + mb_put_uint16le(mbp, time); + mb_put_uint32le(mbp, 0); /* file size */ + mb_put_uint32le(mbp, 0); /* allocation unit size */ + mb_put_uint16le(mbp, attr); /* DOS attr */ + mb_put_uint32le(mbp, 0); /* EA size */ + t2p->t2_maxpcount = 5 * 2; + t2p->t2_maxdcount = vcp->vc_txmax; + error = smb_t2_request(t2p); + smb_t2_done(t2p); + return error; +} + +/* + * NT level. Specially for win9x + */ +int +smbfs_smb_setpattrNT(struct smbnode *np, u_short attr, struct timespec *mtime, + struct timespec *atime, struct smb_cred *scred) +{ + struct smb_t2rq *t2p; + struct smb_share *ssp = np->n_mount->sm_share; + struct smb_vc *vcp = SSTOVC(ssp); + struct mbchain *mbp; + int64_t tm; + int error, tzoff; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION, + scred, &t2p); + if (error) + return error; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFO); + mb_put_uint32le(mbp, 0); /* MBZ */ + error = smbfs_fullpath(mbp, vcp, np, NULL, 0); + if (error) { + smb_t2_done(t2p); + return error; + } + tzoff = vcp->vc_sopt.sv_tz; + mbp = &t2p->t2_tdata; + mb_init(mbp); + mb_put_int64le(mbp, 0); /* creation time */ + if (atime) { + smb_time_local2NT(atime, tzoff, &tm); + } else + tm = 0; + mb_put_int64le(mbp, tm); + if (mtime) { + smb_time_local2NT(mtime, tzoff, &tm); + } else + tm = 0; + mb_put_int64le(mbp, tm); + mb_put_int64le(mbp, tm); /* change time */ + mb_put_uint32le(mbp, attr); /* attr */ + t2p->t2_maxpcount = 24; + t2p->t2_maxdcount = 56; + error = smb_t2_request(t2p); + smb_t2_done(t2p); + return error; +} + +/* + * Set file atime and mtime. Doesn't supported by core dialect. + */ +int +smbfs_smb_setftime(struct smbnode *np, struct timespec *mtime, + struct timespec *atime, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + u_int16_t date, time; + int error, tzoff; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scred); + if (error) + return error; + tzoff = SSTOVC(ssp)->vc_sopt.sv_tz; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint32le(mbp, 0); /* creation time */ + + if (atime) + smb_time_unix2dos(atime, tzoff, &date, &time, NULL); + else + time = date = 0; + mb_put_uint16le(mbp, date); + mb_put_uint16le(mbp, time); + if (mtime) + smb_time_unix2dos(mtime, tzoff, &date, &time, NULL); + else + time = date = 0; + mb_put_uint16le(mbp, date); + mb_put_uint16le(mbp, time); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + return error; +} + +/* + * Set DOS file attributes. + * Looks like this call can be used only if CAP_NT_SMBS bit is on. + */ +int +smbfs_smb_setfattrNT(struct smbnode *np, u_int16_t attr, struct timespec *mtime, + struct timespec *atime, struct smb_cred *scred) +{ + struct smb_t2rq *t2p; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + int64_t tm; + int error, svtz; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION, + scred, &t2p); + if (error) + return error; + svtz = SSTOVC(ssp)->vc_sopt.sv_tz; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFO); + mb_put_uint32le(mbp, 0); + mbp = &t2p->t2_tdata; + mb_init(mbp); + mb_put_int64le(mbp, 0); /* creation time */ + if (atime) { + smb_time_local2NT(atime, svtz, &tm); + } else + tm = 0; + mb_put_int64le(mbp, tm); + if (mtime) { + smb_time_local2NT(mtime, svtz, &tm); + } else + tm = 0; + mb_put_int64le(mbp, tm); + mb_put_int64le(mbp, tm); /* change time */ + mb_put_uint16le(mbp, attr); + mb_put_uint32le(mbp, 0); /* padding */ + mb_put_uint16le(mbp, 0); + t2p->t2_maxpcount = 2; + t2p->t2_maxdcount = 0; + error = smb_t2_request(t2p); + smb_t2_done(t2p); + return error; +} + + +int +smbfs_smb_open(struct smbnode *np, int accmode, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc; + u_int16_t fid, wattr, grantedmode; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, accmode); + mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + if (md_get_uint8(mdp, &wc) != 0 || wc != 7) { + error = EBADRPC; + break; + } + md_get_uint16(mdp, &fid); + md_get_uint16le(mdp, &wattr); + md_get_uint32(mdp, NULL); /* mtime */ + md_get_uint32(mdp, NULL); /* fsize */ + md_get_uint16le(mdp, &grantedmode); + /* + * TODO: refresh attributes from this reply + */ + } while(0); + smb_rq_done(rqp); + if (error) + return error; + np->n_fid = fid; + np->n_rwstate = grantedmode; + return 0; +} + + +int +smbfs_smb_close(struct smb_share *ssp, u_int16_t fid, struct timespec *mtime, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + u_long time; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CLOSE, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); + if (mtime) { + smb_time_local2server(mtime, SSTOVC(ssp)->vc_sopt.sv_tz, &time); + } else + time = 0; + mb_put_uint32le(mbp, time); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = dnp->n_mount->sm_share; + struct mbchain *mbp; + struct mdchain *mdp; + struct timespec ctime; + u_int8_t wc; + u_int16_t fid; + u_long tm; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, SMB_FA_ARCHIVE); /* attributes */ + nanotime(&ctime); + smb_time_local2server(&ctime, SSTOVC(ssp)->vc_sopt.sv_tz, &tm); + mb_put_uint32le(mbp, tm); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, nmlen); + if (!error) { + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (!error) { + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc == 1) + md_get_uint16(mdp, &fid); + else + error = EBADRPC; + } + } + smb_rq_done(rqp); + if (error) + return error; + smbfs_smb_close(ssp, fid, &ctime, scred); + return error; +} + +int +smbfs_smb_delete(struct smbnode *np, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); + if (!error) { + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + } + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = src->n_mount->sm_share; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0); + if (error) + break; + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + } while(0); + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, u_int16_t flags, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = src->n_mount->sm_share; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_MOVE, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, SMB_TID_UNKNOWN); + mb_put_uint16le(mbp, 0x20); /* delete target file */ + mb_put_uint16le(mbp, flags); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0); + if (error) + break; + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + } while(0); + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = dnp->n_mount->sm_share; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE_DIRECTORY, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, len); + if (!error) { + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + } + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE_DIRECTORY, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); + if (!error) { + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + } + smb_rq_done(rqp); + return error; +} + +static int +smbfs_smb_search(struct smbfs_fctx *ctx) +{ + struct smb_vc *vcp = SSTOVC(ctx->f_ssp); + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc, bt; + u_int16_t ec, dlen, bc; + int maxent, error, iseof = 0; + + maxent = min(ctx->f_left, (vcp->vc_txmax - SMB_HDRLEN - 3) / SMB_DENTRYLEN); + if (ctx->f_rq) { + smb_rq_done(ctx->f_rq); + ctx->f_rq = NULL; + } + error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_SEARCH, ctx->f_scred, &rqp); + if (error) + return error; + ctx->f_rq = rqp; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, maxent); /* max entries to return */ + mb_put_uint16le(mbp, ctx->f_attrmask); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); /* buffer format */ + if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { + error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen); + if (error) + return error; + mb_put_uint8(mbp, SMB_DT_VARIABLE); + mb_put_uint16le(mbp, 0); /* context length */ + ctx->f_flags &= ~SMBFS_RDD_FINDFIRST; + } else { + mb_put_uint8(mbp, 0); /* file name length */ + mb_put_uint8(mbp, SMB_DT_VARIABLE); + mb_put_uint16le(mbp, SMB_SKEYLEN); + mb_put_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM); + } + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) { + if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnofiles) { + error = 0; + iseof = 1; + ctx->f_flags |= SMBFS_RDD_EOF; + } else + return error; + } + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 1) + return iseof ? ENOENT : EBADRPC; + md_get_uint16le(mdp, &ec); + if (ec == 0) + return ENOENT; + ctx->f_ecnt = ec; + md_get_uint16le(mdp, &bc); + if (bc < 3) + return EBADRPC; + bc -= 3; + md_get_uint8(mdp, &bt); + if (bt != SMB_DT_VARIABLE) + return EBADRPC; + md_get_uint16le(mdp, &dlen); + if (dlen != bc || dlen % SMB_DENTRYLEN != 0) + return EBADRPC; + return 0; +} + +static int +smbfs_findopenLM1(struct smbfs_fctx *ctx, struct smbnode *dnp, + const char *wildcard, int wclen, int attr, struct smb_cred *scred) +{ + ctx->f_attrmask = attr; + if (wildcard) { + if (wclen == 1 && wildcard[0] == '*') { + ctx->f_wildcard = "*.*"; + ctx->f_wclen = 3; + } else { + ctx->f_wildcard = wildcard; + ctx->f_wclen = wclen; + } + } else { + ctx->f_wildcard = NULL; + ctx->f_wclen = 0; + } + ctx->f_name = ctx->f_fname; + return 0; +} + +static int +smbfs_findnextLM1(struct smbfs_fctx *ctx, int limit) +{ + struct mdchain *mbp; + struct smb_rq *rqp; + char *cp; + u_int8_t battr; + u_int16_t date, time; + u_int32_t size; + int error; + + if (ctx->f_ecnt == 0) { + if (ctx->f_flags & SMBFS_RDD_EOF) + return ENOENT; + ctx->f_left = ctx->f_limit = limit; + error = smbfs_smb_search(ctx); + if (error) + return error; + } + rqp = ctx->f_rq; + smb_rq_getreply(rqp, &mbp); + md_get_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM); + md_get_uint8(mbp, &battr); + md_get_uint16le(mbp, &time); + md_get_uint16le(mbp, &date); + md_get_uint32le(mbp, &size); + cp = ctx->f_name; + md_get_mem(mbp, cp, sizeof(ctx->f_fname), MB_MSYSTEM); + cp[sizeof(ctx->f_fname) - 1] = 0; + cp += strlen(cp) - 1; + while (*cp == ' ' && cp >= ctx->f_name) + *cp-- = 0; + ctx->f_attr.fa_attr = battr; + smb_dos2unixtime(date, time, 0, rqp->sr_vc->vc_sopt.sv_tz, + &ctx->f_attr.fa_mtime); + ctx->f_attr.fa_size = size; + ctx->f_nmlen = strlen(ctx->f_name); + ctx->f_ecnt--; + ctx->f_left--; + return 0; +} + +static int +smbfs_findcloseLM1(struct smbfs_fctx *ctx) +{ + if (ctx->f_rq) + smb_rq_done(ctx->f_rq); + return 0; +} + +/* + * TRANS2_FIND_FIRST2/NEXT2, used for NT LM12 dialect + */ +static int +smbfs_smb_trans2find2(struct smbfs_fctx *ctx) +{ + struct smb_t2rq *t2p; + struct smb_vc *vcp = SSTOVC(ctx->f_ssp); + struct mbchain *mbp; + struct mdchain *mdp; + u_int16_t tw, flags; + int error; + + if (ctx->f_t2) { + smb_t2_done(ctx->f_t2); + ctx->f_t2 = NULL; + } + ctx->f_flags &= ~SMBFS_RDD_GOTRNAME; + flags = 8 | 2; /* | */ + if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) { + flags |= 1; /* close search after this request */ + ctx->f_flags |= SMBFS_RDD_NOCLOSE; + } + if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { + error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_FIRST2, + ctx->f_scred, &t2p); + if (error) + return error; + ctx->f_t2 = t2p; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, ctx->f_attrmask); + mb_put_uint16le(mbp, ctx->f_limit); + mb_put_uint16le(mbp, flags); + mb_put_uint16le(mbp, ctx->f_infolevel); + mb_put_uint32le(mbp, 0); + error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen); + if (error) + return error; + } else { + error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_NEXT2, + ctx->f_scred, &t2p); + if (error) + return error; + ctx->f_t2 = t2p; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, ctx->f_limit); + mb_put_uint16le(mbp, ctx->f_infolevel); + mb_put_uint32le(mbp, 0); /* resume key */ + mb_put_uint16le(mbp, flags); + if (ctx->f_rname) + mb_put_mem(mbp, ctx->f_rname, strlen(ctx->f_rname) + 1, MB_MSYSTEM); + else + mb_put_uint8(mbp, 0); /* resume file name */ +#if 0 + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 200 * 1000; /* 200ms */ + if (vcp->vc_flags & SMBC_WIN95) { + /* + * some implementations suggests to sleep here + * for 200ms, due to the bug in the Win95. + * I've didn't notice any problem, but put code + * for it. + */ + tsleep(&flags, PVFS, "fix95", tvtohz(&tv)); + } +#endif + } + t2p->t2_maxpcount = 5 * 2; + t2p->t2_maxdcount = vcp->vc_txmax; + error = smb_t2_request(t2p); + if (error) + return error; + mdp = &t2p->t2_rparam; + if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { + if ((error = md_get_uint16(mdp, &ctx->f_Sid)) != 0) + return error; + ctx->f_flags &= ~SMBFS_RDD_FINDFIRST; + } + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return error; + ctx->f_ecnt = tw; + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return error; + if (tw) + ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE; + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return error; + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return error; + if (ctx->f_ecnt == 0) + return ENOENT; + ctx->f_rnameofs = tw; + mdp = &t2p->t2_rdata; + if (mdp->md_top == NULL) { + printf("bug: ecnt = %d, but data is NULL (please report)\n", ctx->f_ecnt); + return ENOENT; + } + if (mdp->md_top->m_len == 0) { + printf("bug: ecnt = %d, but m_len = 0 and m_next = %p (please report)\n", ctx->f_ecnt,mbp->mb_top->m_next); + return ENOENT; + } + ctx->f_eofs = 0; + return 0; +} + +static int +smbfs_smb_findclose2(struct smbfs_fctx *ctx) +{ + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2, ctx->f_scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return error; +} + +static int +smbfs_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp, + const char *wildcard, int wclen, int attr, struct smb_cred *scred) +{ + ctx->f_name = malloc(SMB_MAXFNAMELEN, M_SMBFSDATA, M_WAITOK); + if (ctx->f_name == NULL) + return ENOMEM; + ctx->f_infolevel = SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_NTLM0_12 ? + SMB_INFO_STANDARD : SMB_FIND_FILE_DIRECTORY_INFO; + ctx->f_attrmask = attr; + ctx->f_wildcard = wildcard; + ctx->f_wclen = wclen; + return 0; +} + +static int +smbfs_findnextLM2(struct smbfs_fctx *ctx, int limit) +{ + struct mdchain *mbp; + struct smb_t2rq *t2p; + char *cp; + u_int8_t tb; + u_int16_t date, time, wattr; + u_int32_t size, next, dattr; + int64_t lint; + int error, svtz, cnt, fxsz, nmlen, recsz; + + if (ctx->f_ecnt == 0) { + if (ctx->f_flags & SMBFS_RDD_EOF) + return ENOENT; + ctx->f_left = ctx->f_limit = limit; + error = smbfs_smb_trans2find2(ctx); + if (error) + return error; + } + t2p = ctx->f_t2; + mbp = &t2p->t2_rdata; + svtz = SSTOVC(ctx->f_ssp)->vc_sopt.sv_tz; + switch (ctx->f_infolevel) { + case SMB_INFO_STANDARD: + next = 0; + fxsz = 0; + md_get_uint16le(mbp, &date); + md_get_uint16le(mbp, &time); /* creation time */ + md_get_uint16le(mbp, &date); + md_get_uint16le(mbp, &time); /* access time */ + smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_atime); + md_get_uint16le(mbp, &date); + md_get_uint16le(mbp, &time); /* access time */ + smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_mtime); + md_get_uint32le(mbp, &size); + ctx->f_attr.fa_size = size; + md_get_uint32(mbp, NULL); /* allocation size */ + md_get_uint16le(mbp, &wattr); + ctx->f_attr.fa_attr = wattr; + md_get_uint8(mbp, &tb); + size = nmlen = tb; + fxsz = 23; + recsz = next = 24 + nmlen; /* docs misses zero byte at end */ + break; + case SMB_FIND_FILE_DIRECTORY_INFO: + md_get_uint32le(mbp, &next); + md_get_uint32(mbp, NULL); /* file index */ + md_get_int64(mbp, NULL); /* creation time */ + md_get_int64le(mbp, &lint); + smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_atime); + md_get_int64le(mbp, &lint); + smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_mtime); + md_get_int64le(mbp, &lint); + smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_ctime); + md_get_int64le(mbp, &lint); /* file size */ + ctx->f_attr.fa_size = lint; + md_get_int64(mbp, NULL); /* real size (should use) */ + md_get_uint32(mbp, &dattr); /* EA */ + ctx->f_attr.fa_attr = dattr; + md_get_uint32le(mbp, &size); /* name len */ + fxsz = 64; + recsz = next ? next : fxsz + size; + break; + default: + SMBERROR("unexpected info level %d\n", ctx->f_infolevel); + return EINVAL; + } + nmlen = min(size, SMB_MAXFNAMELEN); + cp = ctx->f_name; + error = md_get_mem(mbp, cp, nmlen, MB_MSYSTEM); + if (error) + return error; + if (next) { + cnt = next - nmlen - fxsz; + if (cnt > 0) + md_get_mem(mbp, NULL, cnt, MB_MSYSTEM); + else if (cnt < 0) { + SMBERROR("out of sync\n"); + return EBADRPC; + } + } + if (nmlen && cp[nmlen - 1] == 0) + nmlen--; + if (nmlen == 0) + return EBADRPC; + + next = ctx->f_eofs + recsz; + if (ctx->f_rnameofs && (ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0 && + (ctx->f_rnameofs >= ctx->f_eofs && ctx->f_rnameofs < next)) { + /* + * Server needs a resume filename. + */ + if (ctx->f_rnamelen <= nmlen) { + if (ctx->f_rname) + free(ctx->f_rname, M_SMBFSDATA); + ctx->f_rname = malloc(nmlen + 1, M_SMBFSDATA, M_WAITOK); + ctx->f_rnamelen = nmlen; + } + bcopy(ctx->f_name, ctx->f_rname, nmlen); + ctx->f_rname[nmlen] = 0; + ctx->f_flags |= SMBFS_RDD_GOTRNAME; + } + ctx->f_nmlen = nmlen; + ctx->f_eofs = next; + ctx->f_ecnt--; + ctx->f_left--; + return 0; +} + +static int +smbfs_findcloseLM2(struct smbfs_fctx *ctx) +{ + if (ctx->f_name) + free(ctx->f_name, M_SMBFSDATA); + if (ctx->f_t2) + smb_t2_done(ctx->f_t2); + if ((ctx->f_flags & SMBFS_RDD_NOCLOSE) == 0) + smbfs_smb_findclose2(ctx); + return 0; +} + +int +smbfs_findopen(struct smbnode *dnp, const char *wildcard, int wclen, int attr, + struct smb_cred *scred, struct smbfs_fctx **ctxpp) +{ + struct smbfs_fctx *ctx; + int error; + + ctx = malloc(sizeof(*ctx), M_SMBFSDATA, M_WAITOK); + if (ctx == NULL) + return ENOMEM; + bzero(ctx, sizeof(*ctx)); + ctx->f_ssp = dnp->n_mount->sm_share; + ctx->f_dnp = dnp; + ctx->f_flags = SMBFS_RDD_FINDFIRST; + ctx->f_scred = scred; + if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 || + (dnp->n_mount->sm_args.flags & SMBFS_MOUNT_NO_LONG)) { + ctx->f_flags |= SMBFS_RDD_USESEARCH; + error = smbfs_findopenLM1(ctx, dnp, wildcard, wclen, attr, scred); + } else + error = smbfs_findopenLM2(ctx, dnp, wildcard, wclen, attr, scred); + if (error) + smbfs_findclose(ctx, scred); + else + *ctxpp = ctx; + return error; +} + +int +smbfs_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scred) +{ + int error; + + if (limit == 0) + limit = 1000000; + else if (limit > 1) + limit *= 4; /* imperical */ + ctx->f_scred = scred; + for (;;) { + if (ctx->f_flags & SMBFS_RDD_USESEARCH) { + error = smbfs_findnextLM1(ctx, limit); + } else + error = smbfs_findnextLM2(ctx, limit); + if (error) + return error; + if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') || + (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' && + ctx->f_name[1] == '.')) + continue; + break; + } + smbfs_fname_tolocal(SSTOVC(ctx->f_ssp), ctx->f_name, ctx->f_nmlen, + ctx->f_dnp->n_mount->sm_caseopt); + ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name, ctx->f_nmlen); + return 0; +} + +int +smbfs_findclose(struct smbfs_fctx *ctx, struct smb_cred *scred) +{ + ctx->f_scred = scred; + if (ctx->f_flags & SMBFS_RDD_USESEARCH) { + smbfs_findcloseLM1(ctx); + } else + smbfs_findcloseLM2(ctx); + if (ctx->f_rname) + free(ctx->f_rname, M_SMBFSDATA); + free(ctx, M_SMBFSDATA); + return 0; +} + +int +smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen, + struct smbfattr *fap, struct smb_cred *scred) +{ + struct smbfs_fctx *ctx; + int error; + + if (dnp == NULL || (dnp->n_ino == 2 && name == NULL)) { + bzero(fap, sizeof(*fap)); + fap->fa_attr = SMB_FA_DIR; + fap->fa_ino = 2; + return 0; + } + if (nmlen == 1 && name[0] == '.') { + error = smbfs_smb_lookup(dnp, NULL, 0, fap, scred); + return error; + } else if (nmlen == 2 && name[0] == '.' && name[1] == '.') { + error = smbfs_smb_lookup(dnp->n_parent, NULL, 0, fap, scred); + printf("%s: knows NOTHING about '..'\n", __FUNCTION__); + return error; + } + error = smbfs_findopen(dnp, name, nmlen, + SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scred, &ctx); + if (error) + return error; + ctx->f_flags |= SMBFS_RDD_FINDSINGLE; + error = smbfs_findnext(ctx, 1, scred); + if (error == 0) { + *fap = ctx->f_attr; + if (name == NULL) + fap->fa_ino = dnp->n_ino; + } + smbfs_findclose(ctx, scred); + return error; +} diff --git a/sys/fs/smbfs/smbfs_subr.c b/sys/fs/smbfs/smbfs_subr.c new file mode 100644 index 000000000000..8012c0ba47c9 --- /dev/null +++ b/sys/fs/smbfs/smbfs_subr.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2000-2001, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +MALLOC_DEFINE(M_SMBFSDATA, "SMBFS data", "SMBFS private data"); + +/* + * 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; + +void +smb_time_local2server(struct timespec *tsp, int tzoff, u_long *seconds) +{ + *seconds = tsp->tv_sec - tzoff * 60 /*- tz.tz_minuteswest * 60 - + (wall_cmos_clock ? adjkerntz : 0)*/; +} + +void +smb_time_server2local(u_long seconds, int tzoff, struct timespec *tsp) +{ + tsp->tv_sec = seconds + tzoff * 60; + /*+ tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0)*/; +} + +/* + * Number of seconds between 1970 and 1601 year + */ +int64_t DIFF1970TO1601 = 11644473600ULL; + +/* + * Time from server comes as UTC, so no need to use tz + */ +void +smb_time_NT2local(int64_t nsec, int tzoff, struct timespec *tsp) +{ + smb_time_server2local(nsec / 10000000 - DIFF1970TO1601, 0, tsp); +} + +void +smb_time_local2NT(struct timespec *tsp, int tzoff, int64_t *nsec) +{ + u_long seconds; + + smb_time_local2server(tsp, 0, &seconds); + *nsec = (((int64_t)(seconds) & ~1) + DIFF1970TO1601) * (int64_t)10000000; +} + +void +smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp, + u_int16_t *dtp, u_int8_t *dhp) +{ + u_long t, days, year, month, inc; + u_short *months; + + /* + * If the time from the last conversion is the same as now, then + * skip the computations and use the saved result. + */ + smb_time_local2server(tsp, tzoff, &t); + 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; + +void +smb_dos2unixtime(u_int dd, u_int dt, u_int dh, int tzoff, + struct timespec *tsp) +{ + u_long seconds; + u_long month; + u_long year; + u_long days; + u_short *months; + + if (dd == 0) { + 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; + } + smb_time_server2local(seconds + lastseconds, tzoff, tsp); + tsp->tv_nsec = (dh % 100) * 10000000; +} + +static int +smb_fphelp(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *np, + int caseopt) +{ + struct smbmount *smp= np->n_mount; + struct smbnode **npp = smp->sm_npstack; + int i, error = 0; + +/* simple_lock(&smp->sm_npslock);*/ + i = 0; + while (np->n_parent) { + if (i++ == SMBFS_MAXPATHCOMP) { +/* simple_unlock(&smp->sm_npslock);*/ + return ENAMETOOLONG; + } + *npp++ = np; + np = np->n_parent; + } +/* if (i == 0) + return smb_put_dmem(mbp, vcp, "\\", 2, caseopt);*/ + while (i--) { + np = *--npp; + error = mb_put_uint8(mbp, '\\'); + if (error) + break; + error = smb_put_dmem(mbp, vcp, np->n_name, np->n_nmlen, caseopt); + if (error) + break; + } +/* simple_unlock(&smp->sm_npslock);*/ + return error; +} + +int +smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, + const char *name, int nmlen) +{ + int caseopt = SMB_CS_NONE; + int error; + + if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0) + caseopt |= SMB_CS_UPPER; + if (dnp != NULL) { + error = smb_fphelp(mbp, vcp, dnp, caseopt); + if (error) + return error; + } + if (name) { + error = mb_put_uint8(mbp, '\\'); + if (error) + return error; + error = smb_put_dmem(mbp, vcp, name, nmlen, caseopt); + if (error) + return error; + } + error = mb_put_uint8(mbp, 0); + return error; +} + +int +smbfs_fname_tolocal(struct smb_vc *vcp, char *name, int nmlen, int caseopt) +{ +/* if (caseopt & SMB_CS_UPPER) + iconv_convmem(vcp->vc_toupper, name, name, nmlen); + else if (caseopt & SMB_CS_LOWER) + iconv_convmem(vcp->vc_tolower, name, name, nmlen);*/ + if (vcp->vc_tolocal) + iconv_convmem(vcp->vc_tolocal, name, name, nmlen); + return 0; +} diff --git a/sys/fs/smbfs/smbfs_subr.h b/sys/fs/smbfs/smbfs_subr.h new file mode 100644 index 000000000000..d41e9a8ace8a --- /dev/null +++ b/sys/fs/smbfs/smbfs_subr.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2000-2001, 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 _FS_SMBFS_SMBFS_SUBR_H_ +#define _FS_SMBFS_SMBFS_SUBR_H_ + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_SMBFSDATA); +#endif + +#define SMBFSERR(format, args...) printf("%s: "format, __FUNCTION__ ,## args) + +#ifdef SMB_VNODE_DEBUG +#define SMBVDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#else +#define SMBVDEBUG(format, args...) +#endif + +/* + * Possible lock commands + */ +#define SMB_LOCK_EXCL 0 +#define SMB_LOCK_SHARED 1 +#define SMB_LOCK_RELEASE 2 + +struct smbmount; +struct proc; +struct timespec; +struct ucred; +struct vattr; +struct vnode; +struct statfs; + +struct smbfattr { + int fa_attr; + int64_t fa_size; + struct timespec fa_atime; + struct timespec fa_ctime; + struct timespec fa_mtime; + long fa_ino; +}; + +/* + * Context to perform findfirst/findnext/findclose operations + */ +#define SMBFS_RDD_FINDFIRST 0x01 +#define SMBFS_RDD_EOF 0x02 +#define SMBFS_RDD_FINDSINGLE 0x04 +#define SMBFS_RDD_USESEARCH 0x08 +#define SMBFS_RDD_NOCLOSE 0x10 +#define SMBFS_RDD_GOTRNAME 0x1000 + +/* + * Search context supplied by server + */ +#define SMB_SKEYLEN 21 /* search context */ +#define SMB_DENTRYLEN (SMB_SKEYLEN + 22) /* entire entry */ + +struct smbfs_fctx { + /* + * Setable values + */ + int f_flags; /* SMBFS_RDD_ */ + /* + * Return values + */ + struct smbfattr f_attr; /* current attributes */ + char * f_name; /* current file name */ + int f_nmlen; /* name len */ + /* + * Internal variables + */ + int f_limit; /* maximum number of entries */ + int f_attrmask; /* SMB_FA_ */ + int f_wclen; + const char * f_wildcard; + struct smbnode* f_dnp; + struct smb_cred*f_scred; + struct smb_share *f_ssp; + union { + struct smb_rq * uf_rq; + struct smb_t2rq * uf_t2; + } f_urq; + int f_left; /* entries left */ + int f_ecnt; /* entries left in the current reponse */ + int f_eofs; /* entry offset in the parameter block */ + u_char f_skey[SMB_SKEYLEN]; /* server side search context */ + u_char f_fname[8 + 1 + 3 + 1]; /* common case for 8.3 filenames */ + u_int16_t f_Sid; + u_int16_t f_infolevel; + int f_rnamelen; + char * f_rname; /* resume name/key */ + int f_rnameofs; +}; + +#define f_rq f_urq.uf_rq +#define f_t2 f_urq.uf_t2 + +extern int smbfs_debuglevel; + + +/* + * smb level + */ +int smbfs_smb_lock(struct smbnode *np, int op, caddr_t id, + off_t start, off_t end, struct smb_cred *scred); +int smbfs_smb_statfs2(struct smb_share *ssp, struct statfs *sbp, + struct smb_cred *scred); +int smbfs_smb_statfs(struct smb_share *ssp, struct statfs *sbp, + struct smb_cred *scred); +int smbfs_smb_setfsize(struct smbnode *np, int newsize, struct smb_cred *scred); + +int smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, + struct timespec *mtime, struct smb_cred *scred); +int smbfs_smb_setptime2(struct smbnode *np, struct timespec *mtime, + struct timespec *atime, int attr, struct smb_cred *scred); +int smbfs_smb_setpattrNT(struct smbnode *np, u_int16_t attr, + struct timespec *mtime, struct timespec *atime, struct smb_cred *scred); + +int smbfs_smb_setftime(struct smbnode *np, struct timespec *mtime, + struct timespec *atime, struct smb_cred *scred); +int smbfs_smb_setfattrNT(struct smbnode *np, u_int16_t attr, + struct timespec *mtime, struct timespec *atime, struct smb_cred *scred); + +int smbfs_smb_open(struct smbnode *np, int accmode, struct smb_cred *scred); +int smbfs_smb_close(struct smb_share *ssp, u_int16_t fid, + struct timespec *mtime, struct smb_cred *scred); +int smbfs_smb_create(struct smbnode *dnp, const char *name, int len, + struct smb_cred *scred); +int smbfs_smb_delete(struct smbnode *np, struct smb_cred *scred); +int smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, struct smb_cred *scred); +int smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, u_int16_t flags, struct smb_cred *scred); +int smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len, + struct smb_cred *scred); +int smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scred); +int smbfs_findopen(struct smbnode *dnp, const char *wildcard, int wclen, + int attr, struct smb_cred *scred, struct smbfs_fctx **ctxpp); +int smbfs_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scred); +int smbfs_findclose(struct smbfs_fctx *ctx, struct smb_cred *scred); +int smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, + struct smbnode *dnp, const char *name, int nmlen); +int smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen, + struct smbfattr *fap, struct smb_cred *scred); + +int smbfs_fname_tolocal(struct smb_vc *vcp, char *name, int nmlen, int caseopt); + +void smb_time_local2server(struct timespec *tsp, int tzoff, u_long *seconds); +void smb_time_server2local(u_long seconds, int tzoff, struct timespec *tsp); +void smb_time_NT2local(int64_t nsec, int tzoff, struct timespec *tsp); +void smb_time_local2NT(struct timespec *tsp, int tzoff, int64_t *nsec); +void smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp, + u_int16_t *dtp, u_int8_t *dhp); +void smb_dos2unixtime (u_int dd, u_int dt, u_int dh, int tzoff, struct timespec *tsp); + +#endif /* !_FS_SMBFS_SMBFS_SUBR_H_ */ diff --git a/sys/fs/smbfs/smbfs_vfsops.c b/sys/fs/smbfs/smbfs_vfsops.c new file mode 100644 index 000000000000..c0f9ee1c06c5 --- /dev/null +++ b/sys/fs/smbfs/smbfs_vfsops.c @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2000-2001, 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_netsmb.h" +#ifndef NETSMB +#error "SMBFS requires option NETSMB" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include + +#include +#include +#include + +int smbfs_debuglevel = 0; + +static int smbfs_version = SMBFS_VERSION; + +#ifdef SMBFS_USEZONE +#include +#include +#include + +vm_zone_t smbfsmount_zone; +#endif + +SYSCTL_NODE(_vfs, OID_AUTO, smbfs, CTLFLAG_RW, 0, "SMB/CIFS file system"); +SYSCTL_INT(_vfs_smbfs, OID_AUTO, version, CTLFLAG_RD, &smbfs_version, 0, ""); +SYSCTL_INT(_vfs_smbfs, OID_AUTO, debuglevel, CTLFLAG_RW, &smbfs_debuglevel, 0, ""); + +static MALLOC_DEFINE(M_SMBFSHASH, "SMBFS hash", "SMBFS hash table"); + + +static int smbfs_mount(struct mount *, char *, caddr_t, + struct nameidata *, struct proc *); +static int smbfs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *); +static int smbfs_root(struct mount *, struct vnode **); +static int smbfs_start(struct mount *, int, struct proc *); +static int smbfs_statfs(struct mount *, struct statfs *, struct proc *); +static int smbfs_sync(struct mount *, int, struct ucred *, struct proc *); +static int smbfs_unmount(struct mount *, int, struct proc *); +static int smbfs_init(struct vfsconf *vfsp); +static int smbfs_uninit(struct vfsconf *vfsp); + +#if __FreeBSD_version < 400009 +static int smbfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp); +static int smbfs_fhtovp(struct mount *, struct fid *, + struct sockaddr *, struct vnode **, int *, + struct ucred **); +static int smbfs_vptofh(struct vnode *, struct fid *); +#endif + +static struct vfsops smbfs_vfsops = { + smbfs_mount, + smbfs_start, + smbfs_unmount, + smbfs_root, + smbfs_quotactl, + smbfs_statfs, + smbfs_sync, +#if __FreeBSD_version > 400008 + vfs_stdvget, + vfs_stdfhtovp, /* shouldn't happen */ + vfs_stdcheckexp, + vfs_stdvptofh, /* shouldn't happen */ +#else + smbfs_vget, + smbfs_fhtovp, + smbfs_vptofh, +#endif + smbfs_init, + smbfs_uninit, +#ifndef FB_RELENG3 + vfs_stdextattrctl, +#else +#define M_USE_RESERVE M_KERNEL + &sysctl___vfs_smbfs +#endif +}; + + +VFS_SET(smbfs_vfsops, smbfs, VFCF_NETWORK); + +MODULE_DEPEND(smbfs, netsmb, NSMB_VERSION, NSMB_VERSION, NSMB_VERSION); +MODULE_DEPEND(smbfs, libiconv, 1, 1, 1); + +int smbfs_pbuf_freecnt = -1; /* start out unlimited */ + +static int +smbfs_mount(struct mount *mp, char *path, caddr_t data, + struct nameidata *ndp, struct proc *p) +{ + struct smbfs_args args; /* will hold data from mount request */ + struct smbmount *smp = NULL; + struct smb_vc *vcp; + struct smb_share *ssp = NULL; + struct vnode *vp; + struct smb_cred scred; +#ifndef FB_CURRENT + size_t size; +#endif + int error; + char *pc, *pe; + + if (data == NULL) { + printf("missing data argument\n"); + return EINVAL; + } + if (mp->mnt_flag & MNT_UPDATE) { + printf("MNT_UPDATE not implemented"); + return EOPNOTSUPP; + } + error = copyin(data, (caddr_t)&args, sizeof(struct smbfs_args)); + if (error) + return error; + if (args.version != SMBFS_VERSION) { + printf("mount version mismatch: kernel=%d, mount=%d\n", + SMBFS_VERSION, args.version); + return EINVAL; + } + smb_makescred(&scred, p, p->p_ucred); + error = smb_dev2share(args.dev, SMBM_EXEC, &scred, &ssp); + if (error) { + printf("invalid device handle %d (%d)\n", args.dev, error); + return error; + } + vcp = SSTOVC(ssp); + smb_share_unlock(ssp, 0, p); + mp->mnt_stat.f_iosize = SSTOVC(ssp)->vc_txmax; + +#ifdef SMBFS_USEZONE + smp = zalloc(smbfsmount_zone); +#else + MALLOC(smp, struct smbmount*, sizeof(*smp), M_SMBFSDATA, M_USE_RESERVE); +#endif + if (smp == NULL) { + printf("could not alloc smbmount\n"); + error = ENOMEM; + goto bad; + } + bzero(smp, sizeof(*smp)); + mp->mnt_data = (qaddr_t)smp; + smp->sm_hash = hashinit(desiredvnodes, M_SMBFSHASH, &smp->sm_hashlen); + if (smp->sm_hash == NULL) + goto bad; + lockinit(&smp->sm_hashlock, PVFS, "smbfsh", 0, 0); + smp->sm_share = ssp; + smp->sm_root = NULL; + smp->sm_args = args; + smp->sm_caseopt = args.caseopt; + smp->sm_args.file_mode = (smp->sm_args.file_mode & + (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; + smp->sm_args.dir_mode = (smp->sm_args.dir_mode & + (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; + +/* simple_lock_init(&smp->sm_npslock);*/ +#ifndef FB_CURRENT + error = copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); + if (error) + goto bad; + bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); +#endif + pc = mp->mnt_stat.f_mntfromname; + pe = pc + sizeof(mp->mnt_stat.f_mntfromname); + bzero(pc, MNAMELEN); + *pc++ = '/'; + *pc++ = '/'; + pc=index(strncpy(pc, vcp->vc_username, pe - pc - 2), 0); + if (pc < pe-1) { + *(pc++) = '@'; + pc = index(strncpy(pc, vcp->vc_srvname, pe - pc - 2), 0); + if (pc < pe - 1) { + *(pc++) = '/'; + strncpy(pc, ssp->ss_name, pe - pc - 2); + } + } + /* protect against invalid mount points */ + smp->sm_args.mount_point[sizeof(smp->sm_args.mount_point) - 1] = '\0'; + vfs_getnewfsid(mp); + error = smbfs_root(mp, &vp); + if (error) + goto bad; + VOP_UNLOCK(vp, 0, p); + SMBVDEBUG("root.v_usecount = %d\n", vp->v_usecount); + +#ifdef DIAGNOSTICS + SMBERROR("mp=%p\n", mp); +#endif + return error; +bad: + if (smp) { + if (smp->sm_hash) + free(smp->sm_hash, M_SMBFSHASH); + lockdestroy(&smp->sm_hashlock); +#ifdef SMBFS_USEZONE + zfree(smbfsmount_zone, smp); +#else + free(smp, M_SMBFSDATA); +#endif + } + if (ssp) + smb_share_put(ssp, &scred); + return error; +} + +/* Unmount the filesystem described by mp. */ +static int +smbfs_unmount(struct mount *mp, int mntflags, struct proc *p) +{ + struct smbmount *smp = VFSTOSMBFS(mp); + struct vnode *vp; + struct smb_cred scred; + int error, flags; + + SMBVDEBUG("smbfs_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("smbfs_unmount: usecnt=%d\n", vp->v_usecount); + vput(vp); + return EBUSY; + } + error = vflush(mp, vp, flags); + if (error) { + vput(vp); + return error; + } + vput(vp); + vrele(vp); + vgone(vp); + smb_makescred(&scred, p, p->p_ucred); + smb_share_put(smp->sm_share, &scred); + mp->mnt_data = (qaddr_t)0; + + if (smp->sm_hash) + free(smp->sm_hash, M_SMBFSHASH); + lockdestroy(&smp->sm_hashlock); +#ifdef SMBFS_USEZONE + zfree(smbfsmount_zone, smp); +#else + free(smp, M_SMBFSDATA); +#endif + mp->mnt_flag &= ~MNT_LOCAL; + return error; +} + +/* + * Return locked root vnode of a filesystem + */ +static int +smbfs_root(struct mount *mp, struct vnode **vpp) +{ + struct smbmount *smp = VFSTOSMBFS(mp); + struct vnode *vp; + struct smbnode *np; + struct smbfattr fattr; + struct proc *p = curproc; + struct ucred *cred = p->p_ucred; + struct smb_cred scred; + int error; + + if (smp == NULL) { + SMBERROR("smp == NULL (bug in umount)\n"); + return EINVAL; + } + if (smp->sm_root) { + *vpp = SMBTOV(smp->sm_root); + return vget(*vpp, LK_EXCLUSIVE | LK_RETRY, p); + } + smb_makescred(&scred, p, cred); + error = smbfs_smb_lookup(NULL, NULL, 0, &fattr, &scred); + if (error) + return error; + error = smbfs_nget(mp, NULL, "TheRooT", 7, &fattr, &vp); + if (error) + return error; + vp->v_flag |= VROOT; + np = VTOSMB(vp); + smp->sm_root = np; + *vpp = vp; + return 0; +} + +/* + * Vfs start routine, a no-op. + */ +/* ARGSUSED */ +static int +smbfs_start(mp, flags, p) + struct mount *mp; + int flags; + struct proc *p; +{ + SMBVDEBUG("flags=%04x\n", flags); + return 0; +} + +/* + * Do operations associated with quotas, not supported + */ +/* ARGSUSED */ +static int +smbfs_quotactl(mp, cmd, uid, arg, p) + struct mount *mp; + int cmd; + uid_t uid; + caddr_t arg; + struct proc *p; +{ + SMBVDEBUG("return EOPNOTSUPP\n"); + return EOPNOTSUPP; +} + +/*ARGSUSED*/ +int +smbfs_init(struct vfsconf *vfsp) +{ +#ifndef SMP + int name[2]; + int olen, ncpu, plen, error; + + name[0] = CTL_HW; + name[1] = HW_NCPU; + error = kernel_sysctl(curproc, name, 2, &ncpu, &olen, NULL, 0, &plen); + if (error == 0 && ncpu > 1) + printf("warning: smbfs module compiled without SMP support."); +#endif + +#ifdef SMBFS_USEZONE + smbfsmount_zone = zinit("SMBFSMOUNT", sizeof(struct smbmount), 0, 0, 1); +#endif + smbfs_pbuf_freecnt = nswbuf / 2 + 1; + SMBVDEBUG("done.\n"); + return 0; +} + +/*ARGSUSED*/ +int +smbfs_uninit(struct vfsconf *vfsp) +{ + + SMBVDEBUG("done.\n"); + return 0; +} + +/* + * smbfs_statfs call + */ +int +smbfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p) +{ + struct smbmount *smp = VFSTOSMBFS(mp); + struct smbnode *np = smp->sm_root; + struct smb_share *ssp = smp->sm_share; + struct smb_cred scred; + int error = 0; + + if (np == NULL) + return EINVAL; + + sbp->f_iosize = SSTOVC(ssp)->vc_txmax; /* optimal transfer block size */ + sbp->f_spare2 = 0; /* placeholder */ + smb_makescred(&scred, p, p->p_ucred); + + if (SMB_DIALECT(SSTOVC(ssp)) >= SMB_DIALECT_LANMAN2_0) + error = smbfs_smb_statfs2(ssp, sbp, &scred); + else + error = smbfs_smb_statfs(ssp, sbp, &scred); + if (error) + return error; + 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 +smbfs_sync(mp, waitfor, cred, p) + struct mount *mp; + int waitfor; + struct ucred *cred; + struct proc *p; +{ + 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; +#ifndef FB_RELENG3 + if (VOP_ISLOCKED(vp, NULL) || TAILQ_EMPTY(&vp->v_dirtyblkhd) || +#else + if (VOP_ISLOCKED(vp) || TAILQ_EMPTY(&vp->v_dirtyblkhd) || +#endif + 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); +} + +#if __FreeBSD_version < 400009 +/* + * smbfs flat namespace lookup. Unsupported. + */ +/* ARGSUSED */ +static int smbfs_vget(mp, ino, vpp) + struct mount *mp; + ino_t ino; + struct vnode **vpp; +{ + return (EOPNOTSUPP); +} + +/* ARGSUSED */ +static int smbfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) + struct mount *mp; + struct fid *fhp; + struct sockaddr *nam; + struct vnode **vpp; + int *exflagsp; + struct ucred **credanonp; +{ + return (EINVAL); +} + +/* + * Vnode pointer to File handle, should never happen either + */ +/* ARGSUSED */ +static int +smbfs_vptofh(vp, fhp) + struct vnode *vp; + struct fid *fhp; +{ + return (EINVAL); +} + +#endif /* __FreeBSD_version < 400009 */ diff --git a/sys/fs/smbfs/smbfs_vnops.c b/sys/fs/smbfs/smbfs_vnops.c new file mode 100644 index 000000000000..1d0637e0dcc9 --- /dev/null +++ b/sys/fs/smbfs/smbfs_vnops.c @@ -0,0 +1,1340 @@ +/* + * Copyright (c) 2000-2001 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include +#include +#include + +#include +#include +#include + +/* + * Prototypes for SMBFS vnode operations + */ +static int smbfs_create(struct vop_create_args *); +static int smbfs_mknod(struct vop_mknod_args *); +static int smbfs_open(struct vop_open_args *); +static int smbfs_close(struct vop_close_args *); +static int smbfs_access(struct vop_access_args *); +static int smbfs_getattr(struct vop_getattr_args *); +static int smbfs_setattr(struct vop_setattr_args *); +static int smbfs_read(struct vop_read_args *); +static int smbfs_write(struct vop_write_args *); +static int smbfs_fsync(struct vop_fsync_args *); +static int smbfs_remove(struct vop_remove_args *); +static int smbfs_link(struct vop_link_args *); +static int smbfs_lookup(struct vop_lookup_args *); +static int smbfs_rename(struct vop_rename_args *); +static int smbfs_mkdir(struct vop_mkdir_args *); +static int smbfs_rmdir(struct vop_rmdir_args *); +static int smbfs_symlink(struct vop_symlink_args *); +static int smbfs_readdir(struct vop_readdir_args *); +static int smbfs_bmap(struct vop_bmap_args *); +static int smbfs_strategy(struct vop_strategy_args *); +static int smbfs_print(struct vop_print_args *); +static int smbfs_pathconf(struct vop_pathconf_args *ap); +static int smbfs_advlock(struct vop_advlock_args *); +#ifndef FB_RELENG3 +static int smbfs_getextattr(struct vop_getextattr_args *ap); +#endif + +vop_t **smbfs_vnodeop_p; +static struct vnodeopv_entry_desc smbfs_vnodeop_entries[] = { + { &vop_default_desc, (vop_t *) vop_defaultop }, + { &vop_access_desc, (vop_t *) smbfs_access }, + { &vop_advlock_desc, (vop_t *) smbfs_advlock }, + { &vop_bmap_desc, (vop_t *) smbfs_bmap }, + { &vop_close_desc, (vop_t *) smbfs_close }, + { &vop_create_desc, (vop_t *) smbfs_create }, + { &vop_fsync_desc, (vop_t *) smbfs_fsync }, + { &vop_getattr_desc, (vop_t *) smbfs_getattr }, + { &vop_getpages_desc, (vop_t *) smbfs_getpages }, + { &vop_inactive_desc, (vop_t *) smbfs_inactive }, + { &vop_ioctl_desc, (vop_t *) smbfs_ioctl }, + { &vop_islocked_desc, (vop_t *) vop_stdislocked }, + { &vop_link_desc, (vop_t *) smbfs_link }, + { &vop_lock_desc, (vop_t *) vop_stdlock }, + { &vop_lookup_desc, (vop_t *) smbfs_lookup }, + { &vop_mkdir_desc, (vop_t *) smbfs_mkdir }, + { &vop_mknod_desc, (vop_t *) smbfs_mknod }, + { &vop_open_desc, (vop_t *) smbfs_open }, + { &vop_pathconf_desc, (vop_t *) smbfs_pathconf }, + { &vop_print_desc, (vop_t *) smbfs_print }, + { &vop_putpages_desc, (vop_t *) smbfs_putpages }, + { &vop_read_desc, (vop_t *) smbfs_read }, + { &vop_readdir_desc, (vop_t *) smbfs_readdir }, + { &vop_reclaim_desc, (vop_t *) smbfs_reclaim }, + { &vop_remove_desc, (vop_t *) smbfs_remove }, + { &vop_rename_desc, (vop_t *) smbfs_rename }, + { &vop_rmdir_desc, (vop_t *) smbfs_rmdir }, + { &vop_setattr_desc, (vop_t *) smbfs_setattr }, + { &vop_strategy_desc, (vop_t *) smbfs_strategy }, + { &vop_symlink_desc, (vop_t *) smbfs_symlink }, + { &vop_unlock_desc, (vop_t *) vop_stdunlock }, + { &vop_write_desc, (vop_t *) smbfs_write }, +#ifndef FB_RELENG3 + { &vop_getextattr_desc, (vop_t *) smbfs_getextattr }, +/* { &vop_setextattr_desc, (vop_t *) smbfs_setextattr },*/ +#endif + { NULL, NULL } +}; + +static struct vnodeopv_desc smbfs_vnodeop_opv_desc = + { &smbfs_vnodeop_p, smbfs_vnodeop_entries }; + +VNODEOP_SET(smbfs_vnodeop_opv_desc); + +static int +smbfs_access(ap) + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct ucred *cred = ap->a_cred; + u_int mode = ap->a_mode; + struct smbmount *smp = VTOSMBFS(vp); + int error = 0; + + SMBVDEBUG("\n"); + if ((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 != smp->sm_args.uid) { + mode >>= 3; + if (!groupmember(smp->sm_args.gid, cred)) + mode >>= 3; + } + error = (((vp->v_type == VREG) ? smp->sm_args.file_mode : smp->sm_args.dir_mode) & mode) == mode ? 0 : EACCES; + return error; +} + +/* ARGSUSED */ +static int +smbfs_open(ap) + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + struct vattr vattr; + int mode = ap->a_mode; + int error, accmode; + + SMBVDEBUG("%s,%d\n", np->n_name, np->n_opencount); + if (vp->v_type != VREG && vp->v_type != VDIR) { + SMBFSERR("open eacces vtype=%d\n", vp->v_type); + return EACCES; + } + if (vp->v_type == VDIR) { + np->n_opencount++; + return 0; + } + if (np->n_flag & NMODIFIED) { + if ((error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) + return error; + smbfs_attr_cacheremove(vp); + error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p); + if (error) + return error; + np->n_mtime.tv_sec = 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.tv_sec != vattr.va_mtime.tv_sec) { + error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); + if (error == EINTR) + return error; + np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; + } + } + if (np->n_opencount) { + np->n_opencount++; + return 0; + } + accmode = SMB_AM_OPENREAD; + if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) + accmode = SMB_AM_OPENRW; + smb_makescred(&scred, ap->a_p, ap->a_cred); + error = smbfs_smb_open(np, accmode, &scred); + if (error) { + if (mode & FWRITE) + return EACCES; + accmode = SMB_AM_OPENREAD; + error = smbfs_smb_open(np, accmode, &scred); + } + if (!error) { + np->n_opencount++; + } + smbfs_attr_cacheremove(vp); + return error; +} + +static int +smbfs_closel(struct vop_close_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct proc *p = ap->a_p; + struct smb_cred scred; + struct vattr vattr; + int error; + + SMBVDEBUG("name=%s, pid=%d, c=%d\n",np->n_name, p->p_pid, np->n_opencount); + + smb_makescred(&scred, p, ap->a_cred); + + if (np->n_opencount == 0) { + SMBERROR("Negative opencount\n"); + return 0; + } + np->n_opencount--; + if (vp->v_type == VDIR) { + if (np->n_opencount) + return 0; + if (np->n_dirseq) { + smbfs_findclose(np->n_dirseq, &scred); + np->n_dirseq = NULL; + } + error = 0; + } else { + error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, p, 1); + if (np->n_opencount) + return error; + VOP_GETATTR(vp, &vattr, ap->a_cred, p); + error = smbfs_smb_close(np->n_mount->sm_share, np->n_fid, + &np->n_mtime, &scred); + } + smbfs_attr_cacheremove(vp); + return error; +} + +/* + * XXX: VOP_CLOSE() usually called without lock held which is suck. Here we + * do some heruistic to determine if vnode should be locked. + */ +static int +smbfs_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; +{ + struct vnode *vp = ap->a_vp; + struct proc *p = ap->a_p; + int error, dolock; + + VI_LOCK(vp); + dolock = (vp->v_flag & VXLOCK) == 0; + if (dolock) + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_INTERLOCK, p); + else + VI_UNLOCK(vp); + error = smbfs_closel(ap); + if (dolock) + VOP_UNLOCK(vp, 0, p); + return error; +} + +/* + * smbfs_getattr call from vfs. + */ +static int +smbfs_getattr(ap) + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct vattr *va=ap->a_vap; + struct smbfattr fattr; + struct smb_cred scred; + u_int32_t oldsize; + int error; + + SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_flag & VROOT) != 0); + error = smbfs_attr_cachelookup(vp, va); + if (!error) + return 0; + SMBVDEBUG("not in the cache\n"); + smb_makescred(&scred, ap->a_p, ap->a_cred); + oldsize = np->n_size; + error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred); + if (error) { + SMBVDEBUG("error %d\n", error); + return error; + } + smbfs_attr_cacheenter(vp, &fattr); + smbfs_attr_cachelookup(vp, va); + if (np->n_opencount) + np->n_size = oldsize; + return 0; +} + +static int +smbfs_setattr(ap) + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct vattr *vap = ap->a_vap; + struct timespec *mtime, *atime; + struct smb_cred scred; + struct smb_share *ssp = np->n_mount->sm_share; + struct smb_vc *vcp = SSTOVC(ssp); + u_quad_t tsize = 0; + int isreadonly, doclose, error = 0; + + SMBVDEBUG("\n"); + if (vap->va_flags != VNOVAL) + return EOPNOTSUPP; + isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY); + /* + * 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) && isreadonly) + return EROFS; + smb_makescred(&scred, ap->a_p, ap->a_cred); + if (vap->va_size != VNOVAL) { + switch (vp->v_type) { + case VDIR: + return EISDIR; + case VREG: + break; + default: + return EINVAL; + }; + if (isreadonly) + return EROFS; + doclose = 0; + vnode_pager_setsize(vp, (u_long)vap->va_size); + tsize = np->n_size; + np->n_size = vap->va_size; + if (np->n_opencount == 0) { + error = smbfs_smb_open(np, SMB_AM_OPENRW, &scred); + if (error == 0) + doclose = 1; + } + if (error == 0) + error = smbfs_smb_setfsize(np, vap->va_size, &scred); + if (doclose) + smbfs_smb_close(ssp, np->n_fid, NULL, &scred); + if (error) { + np->n_size = tsize; + vnode_pager_setsize(vp, (u_long)tsize); + return error; + } + } + mtime = atime = NULL; + if (vap->va_mtime.tv_sec != VNOVAL) + mtime = &vap->va_mtime; + if (vap->va_atime.tv_sec != VNOVAL) + atime = &vap->va_atime; + if (mtime != atime) { +#if 0 + if (mtime == NULL) + mtime = &np->n_mtime; + if (atime == NULL) + atime = &np->n_atime; +#endif + /* + * If file is opened, then we can use handle based calls. + * If not, use path based ones. + */ + if (np->n_opencount == 0) { + if (vcp->vc_flags & SMBV_WIN95) { + error = VOP_OPEN(vp, FWRITE, ap->a_cred, ap->a_p); + if (!error) { +/* error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); + VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p);*/ + if (mtime) + np->n_mtime = *mtime; + VOP_CLOSE(vp, FWRITE, ap->a_cred, ap->a_p); + } + } else if ((vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)) { + error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred); +/* error = smbfs_smb_setpattrNT(np, 0, mtime, atime, &scred);*/ + } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) { + error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred); + } else { + error = smbfs_smb_setpattr(np, 0, mtime, &scred); + } + } else { + if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { + error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); + } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) { + error = smbfs_smb_setftime(np, mtime, atime, &scred); + } else { + /* + * I have no idea how to handle this for core + * level servers. The possible solution is to + * update mtime after file is closed. + */ + SMBERROR("can't update times on an opened file\n"); + } + } + } + /* + * Invalidate attribute cache in case if server doesn't set + * required attributes. + */ + smbfs_attr_cacheremove(vp); /* invalidate cache */ + VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); + np->n_mtime.tv_sec = vap->va_mtime.tv_sec; + return error; +} +/* + * smbfs_read call. + */ +static int +smbfs_read(ap) + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + + SMBVDEBUG("\n"); + if (vp->v_type != VREG && vp->v_type != VDIR) + return EPERM; + return smbfs_readvnode(vp, uio, ap->a_cred); +} + +static int +smbfs_write(ap) + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + + SMBVDEBUG("%d,ofs=%d,sz=%d\n",vp->v_type, (int)uio->uio_offset, uio->uio_resid); + if (vp->v_type != VREG) + return (EPERM); + return smbfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag); +} +/* + * smbfs_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 +smbfs_create(ap) + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + struct vnode *dvp = ap->a_dvp; + struct vattr *vap = ap->a_vap; + struct vnode **vpp=ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + struct smbnode *dnp = VTOSMB(dvp); + struct vnode *vp; + struct vattr vattr; + struct smbfattr fattr; + struct smb_cred scred; + char *name = cnp->cn_nameptr; + int nmlen = cnp->cn_namelen; + int error; + + + SMBVDEBUG("\n"); + *vpp = NULL; + if (vap->va_type != VREG) + return EOPNOTSUPP; + if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc))) + return error; + smb_makescred(&scred, cnp->cn_proc, cnp->cn_cred); + + error = smbfs_smb_create(dnp, name, nmlen, &scred); + if (error) + return error; + error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred); + if (error) + return error; + error = smbfs_nget(VTOVFS(dvp), dvp, name, nmlen, &fattr, &vp); + if (error) + return error; + *vpp = vp; + if (cnp->cn_flags & MAKEENTRY) + cache_enter(dvp, vp, cnp); + return error; +} + +static int +smbfs_remove(ap) + struct vop_remove_args /* { + struct vnodeop_desc *a_desc; + struct vnode * a_dvp; + struct vnode * a_vp; + struct componentname * a_cnp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; +/* struct vnode *dvp = ap->a_dvp;*/ + struct componentname *cnp = ap->a_cnp; + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + int error; + + if (vp->v_type == VDIR || np->n_opencount || vp->v_usecount != 1) + return EPERM; + smb_makescred(&scred, cnp->cn_proc, cnp->cn_cred); + error = smbfs_smb_delete(np, &scred); + cache_purge(vp); + return error; +} + +/* + * smbfs_file rename call + */ +static int +smbfs_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; +{ + struct vnode *fvp = ap->a_fvp; + struct vnode *tvp = ap->a_tvp; + struct vnode *fdvp = ap->a_fdvp; + struct vnode *tdvp = ap->a_tdvp; + struct componentname *tcnp = ap->a_tcnp; +/* struct componentname *fcnp = ap->a_fcnp;*/ + struct smb_cred scred; + u_int16_t flags = 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; + } + flags = 0x10; /* verify all writes */ + if (fvp->v_type == VDIR) { + flags |= 2; + } else if (fvp->v_type == VREG) { + flags |= 1; + } else + return EINVAL; + smb_makescred(&scred, tcnp->cn_proc, tcnp->cn_cred); + /* + * It seems that Samba doesn't implement SMB_COM_MOVE call... + */ +#ifdef notnow + if (SMB_DIALECT(SSTOCN(smp->sm_share)) >= SMB_DIALECT_LANMAN1_0) { + error = smbfs_smb_move(VTOSMB(fvp), VTOSMB(tdvp), + tcnp->cn_nameptr, tcnp->cn_namelen, flags, &scred); + } else +#endif + { + /* + * We have to do the work atomicaly + */ + if (tvp && tvp != fvp) { + error = smbfs_smb_delete(VTOSMB(tvp), &scred); + if (error) + goto out; + } + error = smbfs_smb_rename(VTOSMB(fvp), VTOSMB(tdvp), + tcnp->cn_nameptr, tcnp->cn_namelen, &scred); + } + + 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); + smbfs_attr_cacheremove(fdvp); + smbfs_attr_cacheremove(tdvp); +#ifdef possible_mistake + vgone(fvp); + if (tvp) + vgone(tvp); +#endif + return error; +} + +/* + * somtime it will come true... + */ +static int +smbfs_link(ap) + struct vop_link_args /* { + struct vnode *a_tdvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; +{ + return EOPNOTSUPP; +} + +/* + * smbfs_symlink link create call. + * Sometime it will be functional... + */ +static int +smbfs_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; +{ + return EOPNOTSUPP; +} + +static int +smbfs_mknod(ap) + struct vop_mknod_args /* { + } */ *ap; +{ + return EOPNOTSUPP; +} + +static int +smbfs_mkdir(ap) + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + struct vnode *dvp = ap->a_dvp; +/* struct vattr *vap = ap->a_vap;*/ + struct vnode *vp; + struct componentname *cnp = ap->a_cnp; + struct smbnode *dnp = VTOSMB(dvp); + struct vattr vattr; + struct smb_cred scred; + struct smbfattr fattr; + char *name = cnp->cn_nameptr; + int len = cnp->cn_namelen; + int error; + + if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc))) { + return error; + } + if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))) + return EEXIST; + smb_makescred(&scred, cnp->cn_proc, cnp->cn_cred); + error = smbfs_smb_mkdir(dnp, name, len, &scred); + if (error) + return error; + error = smbfs_smb_lookup(dnp, name, len, &fattr, &scred); + if (error) + return error; + error = smbfs_nget(VTOVFS(dvp), dvp, name, len, &fattr, &vp); + if (error) + return error; + *ap->a_vpp = vp; + return 0; +} + +/* + * smbfs_remove directory call + */ +static int +smbfs_rmdir(ap) + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct vnode *dvp = ap->a_dvp; + struct componentname *cnp = ap->a_cnp; +/* struct smbmount *smp = VTOSMBFS(vp);*/ + struct smbnode *dnp = VTOSMB(dvp); + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + int error; + + if (dvp == vp) + return EINVAL; + + smb_makescred(&scred, cnp->cn_proc, cnp->cn_cred); + error = smbfs_smb_rmdir(np, &scred); + dnp->n_flag |= NMODIFIED; + smbfs_attr_cacheremove(dvp); +/* cache_purge(dvp);*/ + cache_purge(vp); + return error; +} + +/* + * smbfs_readdir call + */ +static int +smbfs_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; +{ + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + int error; + + if (vp->v_type != VDIR) + return (EPERM); +#ifdef notnow + if (ap->a_ncookies) { + printf("smbfs_readdir: no support for cookies now..."); + return (EOPNOTSUPP); + } +#endif + error = smbfs_readvnode(vp, uio, ap->a_cred); + return error; +} + +/* ARGSUSED */ +static int +smbfs_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 (smb_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_p, 1));*/ + return (0); +} + +static +int smbfs_print (ap) + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + + if (np == NULL) { + printf("no smbnode data\n"); + return (0); + } + printf("tag VT_SMBFS, name = %s, parent = %p, opencount = %d", + np->n_name, np->n_parent ? SMBTOV(np->n_parent) : NULL, + np->n_opencount); + lockmgr_printinfo(&vp->v_lock); + printf("\n"); + return (0); +} + +static int +smbfs_pathconf (ap) + struct vop_pathconf_args /* { + struct vnode *vp; + int name; + register_t *retval; + } */ *ap; +{ + struct smbmount *smp = VFSTOSMBFS(VTOVFS(ap->a_vp)); + struct smb_vc *vcp = SSTOVC(smp->sm_share); + register_t *retval = ap->a_retval; + int error = 0; + + switch (ap->a_name) { + case _PC_LINK_MAX: + *retval = 0; + break; + case _PC_NAME_MAX: + *retval = (vcp->vc_flags & SMBV_LONGNAMES) ? 255 : 12; + break; + case _PC_PATH_MAX: + *retval = 800; /* XXX: a correct one ? */ + break; + default: + error = EINVAL; + } + return error; +} + +static int +smbfs_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; + + SMBVDEBUG("\n"); + if (bp->b_flags & B_PHYS) + panic("smbfs physio"); + if (bp->b_flags & B_ASYNC) + p = (struct proc *)0; + else + p = curproc; /* XXX */ + if (bp->b_iocmd == BIO_READ) + cr = bp->b_rcred; + else + cr = bp->b_wcred; + + if ((bp->b_flags & B_ASYNC) == 0 ) + error = smbfs_doio(bp, cr, p); + return error; +} + +static int +smbfs_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; +{ + 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); +} + +int +smbfs_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; +{ + return EINVAL; +} + +static char smbfs_atl[] = "rhsvda"; +static int +smbfs_getextattr(struct vop_getextattr_args *ap) +/* { + IN struct vnode *a_vp; + IN char *a_name; + INOUT struct uio *a_uio; + IN struct ucred *a_cred; + IN struct proc *a_p; +}; +*/ +{ + struct vnode *vp = ap->a_vp; + struct proc *p = ap->a_p; + struct ucred *cred = ap->a_cred; + struct uio *uio = ap->a_uio; + const char *name = ap->a_name; + struct smbnode *np = VTOSMB(vp); + struct vattr vattr; + char buf[10]; + int i, attr, error; + + error = VOP_ACCESS(vp, VREAD, cred, p); + if (error) + return error; + error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) + return error; + if (strcmp(name, "dosattr") == 0) { + attr = np->n_dosattr; + for (i = 0; i < 6; i++, attr >>= 1) + buf[i] = (attr & 1) ? smbfs_atl[i] : '-'; + buf[i] = 0; + error = uiomove(buf, i, uio); + + } else + error = EINVAL; + return error; +} + +/* + * Since we expected to support F_GETLK (and SMB protocol has no such function), + * it is necessary to use lf_advlock(). It would be nice if this function had + * a callback mechanism because it will help to improve a level of consistency. + */ +int +smbfs_advlock(ap) + struct vop_advlock_args /* { + struct vnode *a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct flock *fl = ap->a_fl; + caddr_t id = (caddr_t)1 /* ap->a_id */; +/* int flags = ap->a_flags;*/ + struct proc *p = curproc; + struct smb_cred scred; + off_t start, end, size; + int error, lkop; + + if (vp->v_type == VDIR) { + /* + * SMB protocol have no support for directory locking. + * Although locks can be processed on local machine, I don't + * think that this is a good idea, because some programs + * can work wrong assuming directory is locked. So, we just + * return 'operation not supported + */ + return EOPNOTSUPP; + } + size = np->n_size; + switch (fl->l_whence) { + case SEEK_SET: + case SEEK_CUR: + start = fl->l_start; + break; + case SEEK_END: + start = fl->l_start + size; + default: + return EINVAL; + } + if (start < 0) + return EINVAL; + if (fl->l_len == 0) + end = -1; + else { + end = start + fl->l_len - 1; + if (end < start) + return EINVAL; + } + smb_makescred(&scred, p, p ? p->p_ucred : NULL); + switch (ap->a_op) { + case F_SETLK: + switch (fl->l_type) { + case F_WRLCK: + lkop = SMB_LOCK_EXCL; + break; + case F_RDLCK: + lkop = SMB_LOCK_SHARED; + break; + case F_UNLCK: + lkop = SMB_LOCK_RELEASE; + break; + default: + return EINVAL; + } + error = lf_advlock(ap, &np->n_lockf, size); + if (error) + break; + lkop = SMB_LOCK_EXCL; + error = smbfs_smb_lock(np, lkop, id, start, end, &scred); + if (error) { + ap->a_op = F_UNLCK; + lf_advlock(ap, &np->n_lockf, size); + } + break; + case F_UNLCK: + lf_advlock(ap, &np->n_lockf, size); + error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred); + break; + case F_GETLK: + error = lf_advlock(ap, &np->n_lockf, size); + break; + default: + return EINVAL; + } + return error; +} + +static int +smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop) +{ + static const char *badchars = "*/\[]:<>=;?"; + static const char *badchars83 = " +|,"; + const char *cp; + int i, error; + + if (nameiop == LOOKUP) + return 0; + error = ENOENT; + if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) { + /* + * Name should conform 8.3 format + */ + if (nmlen > 12) + return ENAMETOOLONG; + cp = index(name, '.'); + if (cp == NULL) + return error; + if (cp == name || (cp - name) > 8) + return error; + cp = index(cp + 1, '.'); + if (cp != NULL) + return error; + for (cp = name, i = 0; i < nmlen; i++, cp++) + if (index(badchars83, *cp) != NULL) + return error; + } + for (cp = name, i = 0; i < nmlen; i++, cp++) + if (index(badchars, *cp) != NULL) + return error; + return 0; +} + +#ifndef PDIRUNLOCK +#define PDIRUNLOCK 0 +#endif + +/* + * Things go even weird without fixed inode numbers... + */ +int +smbfs_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 proc *p = cnp->cn_proc; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vnode *vp; + struct smbmount *smp; + struct mount *mp = dvp->v_mount; + struct smbnode *dnp; + struct smbfattr fattr, *fap; + struct smb_cred scred; + char *name = cnp->cn_nameptr; + int flags = cnp->cn_flags; + int nameiop = cnp->cn_nameiop; + int nmlen = cnp->cn_namelen; + int lockparent, wantparent, error, islastcn, isdot; + + SMBVDEBUG("\n"); + cnp->cn_flags &= ~PDIRUNLOCK; + if (dvp->v_type != VDIR) + return ENOTDIR; + if ((flags & ISDOTDOT) && (dvp->v_flag & VROOT)) { + SMBFSERR("invalid '..'\n"); + return EIO; + } +#ifdef SMB_VNODE_DEBUG + { + char *cp, c; + + cp = name + nmlen; + c = *cp; + *cp = 0; + SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name, + VTOSMB(dvp)->n_name); + *cp = c; + } +#endif + islastcn = flags & ISLASTCN; + if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP)) + return EROFS; + if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, p)) != 0) + return error; + lockparent = flags & LOCKPARENT; + wantparent = flags & (LOCKPARENT|WANTPARENT); + smp = VFSTOSMBFS(mp); + dnp = VTOSMB(dvp); + isdot = (nmlen == 1 && name[0] == '.'); + + error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop); + + if (error) + return ENOENT; + + error = cache_lookup(dvp, vpp, cnp); + SMBVDEBUG("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; + SMBVDEBUG("cached '.'\n"); + } else if (flags & ISDOTDOT) { + VOP_UNLOCK(dvp, 0, p); /* unlock parent */ + cnp->cn_flags |= PDIRUNLOCK; + error = vget(vp, LK_EXCLUSIVE, p); + if (!error && lockparent && islastcn) { + error = vn_lock(dvp, LK_EXCLUSIVE, p); + if (error == 0) + cnp->cn_flags &= ~PDIRUNLOCK; + } + } else { + error = vget(vp, LK_EXCLUSIVE, p); + if (!lockparent || error || !islastcn) { + VOP_UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + } + } + if (!error) { + if (vpid == vp->v_id) { + if (!VOP_GETATTR(vp, &vattr, cnp->cn_cred, p) + /* && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) { + if (nameiop != LOOKUP && islastcn) + cnp->cn_flags |= SAVENAME; + SMBVDEBUG("use cached vnode\n"); + 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) { + cnp->cn_flags |= PDIRUNLOCK; + return (error); + } + cnp->cn_flags &= ~PDIRUNLOCK; + } + /* + * entry is not in the cache or has been expired + */ + error = 0; + *vpp = NULLVP; + smb_makescred(&scred, p, cnp->cn_cred); + fap = &fattr; + if (flags & ISDOTDOT) { + error = smbfs_smb_lookup(dnp->n_parent, NULL, 0, fap, &scred); + SMBVDEBUG("result of dotdot lookup: %d\n", error); + } else { + fap = &fattr; + error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred); +/* if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/ + SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error); + } + if (error && error != ENOENT) + return error; + if (error) { /* 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); + cnp->cn_flags |= PDIRUNLOCK; + } + return (EJUSTRETURN); + } + return ENOENT; + }/* else { + SMBVDEBUG("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, p); + if (error) + return error; + if (isdot) { + VREF(dvp); + *vpp = dvp; + return 0; + } + error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); + if (error) + return error; + *vpp = vp; + cnp->cn_flags |= SAVENAME; + if (!lockparent) { + VOP_UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + } + return 0; + } + if (nameiop == RENAME && islastcn && wantparent) { + error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, p); + if (error) + return error; + if (isdot) + return EISDIR; + error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); + if (error) + return error; + *vpp = vp; + cnp->cn_flags |= SAVENAME; + if (!lockparent) { + VOP_UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + } + return 0; + } + if (flags & ISDOTDOT) { + VOP_UNLOCK(dvp, 0, p); + error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp); + if (error) { + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); + return error; + } + if (lockparent && islastcn) { + error = vn_lock(dvp, LK_EXCLUSIVE, p); + if (error) { + cnp->cn_flags |= PDIRUNLOCK; + vput(vp); + return error; + } + } + *vpp = vp; + } else if (isdot) { + vref(dvp); + *vpp = dvp; + } else { + error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); + if (error) + return error; + *vpp = vp; + SMBVDEBUG("lookup: getnewvp!\n"); + if (!lockparent || !islastcn) { + VOP_UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + } + } + if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) { +/* VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/ + cache_enter(dvp, *vpp, cnp); + } + return 0; +} diff --git a/sys/kern/md4c.c b/sys/kern/md4c.c new file mode 100644 index 000000000000..e3a0bfa9c497 --- /dev/null +++ b/sys/kern/md4c.c @@ -0,0 +1,285 @@ +/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm + * $FreeBSD$ + */ + +/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD4 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD4 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + */ + +#include +#include +#include + +typedef unsigned char *POINTER; +typedef u_int16_t UINT2; +typedef u_int32_t UINT4; + +#define PROTO_LIST(list) list + +/* 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 + +static void MD4Transform PROTO_LIST ((UINT4 [4], const unsigned char [64])); +static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((UINT4 *, const unsigned char *, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* 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)); \ + } + +/* MD4 initialization. Begins an MD4 operation, writing a new context. + */ +void MD4Init (context) +MD4_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD4 block update operation. Continues an MD4 message-digest + operation, processing another message block, and updating the + context. + */ +void MD4Update (context, input, inputLen) +MD4_CTX *context; /* context */ +const unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + /* Transform as many times as possible. + */ + if (inputLen >= partLen) { + bcopy(input, &context->buffer[index], partLen); + MD4Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD4Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + bcopy(&input[i], &context->buffer[index], inputLen-i); +} + +/* MD4 padding. */ +void MD4Pad (context) +MD4_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. + */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD4Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD4Update (context, bits, 8); +} + +/* MD4 finalization. Ends an MD4 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD4Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD4_CTX *context; /* context */ +{ + /* Do padding */ + MD4Pad (context); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. + */ + bzero((POINTER)context, sizeof (*context)); +} + +/* MD4 basic transformation. Transforms state based on block. + */ +static void MD4Transform (state, block) +UINT4 state[4]; +const unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* 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 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + */ + bzero((POINTER)x, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* 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); +} diff --git a/sys/netsmb/netbios.h b/sys/netsmb/netbios.h new file mode 100644 index 000000000000..961f5be80c5e --- /dev/null +++ b/sys/netsmb/netbios.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2000-2001 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 _NETSMB_NETBIOS_H_ +#define _NETSMB_NETBIOS_H_ + +/* + * make this file dirty... + */ +#ifndef _NETINET_IN_H_ +#include +#endif + +#ifndef _NETIPX_IPX_H_ +#include +#endif + +#define AF_NETBIOS AF_NS /* XXX: should go to socket.h */ +#define PF_NETBIOS AF_NETBIOS + +#define NBPROTO_TCPSSN 1 /* NETBIOS session over TCP */ +#define NBPROTO_IPXSSN 11 /* NETBIOS over IPX */ + +#define NB_NAMELEN 16 +#define NB_ENCNAMELEN NB_NAMELEN * 2 +#define NB_MAXLABLEN 63 + +#define NB_MINSALEN (sizeof(struct sockaddr_nb)) + +/* + * name types + */ +#define NBT_WKSTA 0x00 +#define NBT_SERVER 0x20 + +/* + * Session packet types + */ +#define NB_SSN_MESSAGE 0x0 +#define NB_SSN_REQUEST 0x81 +#define NB_SSN_POSRESP 0x82 +#define NB_SSN_NEGRESP 0x83 +#define NB_SSN_RTGRESP 0x84 +#define NB_SSN_KEEPALIVE 0x85 + +/* + * resolver: Opcodes + */ +#define NBNS_OPCODE_QUERY 0x00 +#define NBNS_OPCODE_REGISTER 0x05 +#define NBNS_OPCODE_RELEASE 0x06 +#define NBNS_OPCODE_WACK 0x07 +#define NBNS_OPCODE_REFRESH 0x08 +#define NBNS_OPCODE_RESPONSE 0x10 /* or'ed with other opcodes */ + +/* + * resolver: NM_FLAGS + */ +#define NBNS_NMFLAG_BCAST 0x01 +#define NBNS_NMFLAG_RA 0x08 /* recursion available */ +#define NBNS_NMFLAG_RD 0x10 /* recursion desired */ +#define NBNS_NMFLAG_TC 0x20 /* truncation occured */ +#define NBNS_NMFLAG_AA 0x40 /* authoritative answer */ + +/* + * resolver: Question types + */ +#define NBNS_QUESTION_TYPE_NB 0x0020 +#define NBNS_QUESTION_TYPE_NBSTAT 0x0021 + +/* + * resolver: Question class + */ +#define NBNS_QUESTION_CLASS_IN 0x0001 + +/* + * resolver: Limits + */ +#define NBNS_MAXREDIRECTS 3 /* maximum number of accepted redirects */ +#define NBDG_MAXSIZE 576 /* maximum nbns datagram size */ + +/* + * NETBIOS addressing + */ +union nb_tran { + struct sockaddr_in x_in; + struct sockaddr_ipx x_ipx; +}; + +struct nb_name { + u_int nn_type; + u_char nn_name[NB_NAMELEN + 1]; + u_char * nn_scope; +}; + +/* + * Socket address + */ +struct sockaddr_nb { + u_char snb_len; + u_char snb_family; + union nb_tran snb_tran; /* transport */ + u_char snb_name[1 + NB_ENCNAMELEN + 1]; /* encoded */ +}; + +#define snb_addrin snb_tran.x_in + +#endif /* !_NETSMB_NETBIOS_H_ */ diff --git a/sys/netsmb/smb.h b/sys/netsmb/smb.h new file mode 100644 index 000000000000..4bd30969bbe6 --- /dev/null +++ b/sys/netsmb/smb.h @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2000-2001 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$ + */ + +/* + * Common definintions and structures for SMB/CIFS protocol + */ + +#ifndef _NETSMB_SMB_H_ +#define _NETSMB_SMB_H_ + +#define SMB_TCP_PORT 139 +/* + * SMB dialects that we have to deal with. + */ +enum smb_dialects { + SMB_DIALECT_NONE, + SMB_DIALECT_CORE, /* PC NETWORK PROGRAM 1.0, PCLAN1.0 */ + SMB_DIALECT_COREPLUS, /* MICROSOFT NETWORKS 1.03 */ + SMB_DIALECT_LANMAN1_0, /* MICROSOFT NETWORKS 3.0, LANMAN1.0 */ + SMB_DIALECT_LANMAN2_0, /* LM1.2X002, DOS LM1.2X002, Samba */ + SMB_DIALECT_LANMAN2_1, /* DOS LANMAN2.1, LANMAN2.1 */ + SMB_DIALECT_NTLM0_12 /* NT LM 0.12, Windows for Workgroups 3.1a, + * NT LANMAN 1.0 */ +}; + +/* + * Formats of data/string buffers + */ +#define SMB_DT_DATA 1 +#define SMB_DT_DIALECT 2 +#define SMB_DT_PATHNAME 3 +#define SMB_DT_ASCII 4 +#define SMB_DT_VARIABLE 5 + +/* + * SMB header + */ +#define SMB_SIGNATURE "\xFFSMB" +#define SMB_SIGLEN 4 +#define SMB_HDRMID(p) (*(u_short*)((u_char*)(p) + 30)) +#define SMB_HDRLEN 32 +/* + * bits in the smb_flags field + */ +#define SMB_FLAGS_CASELESS 0x08 +#define SMB_FLAGS_SERVER_RESP 0x80 /* indicates a response */ + +/* + * bits in the smb_flags2 field + */ +#define SMB_FLAGS2_KNOWS_LONG_NAMES 0x0001 +#define SMB_FLAGS2_KNOWS_EAS 0x0002 /* client know about EAs */ +#define SMB_FLAGS2_SECURITY_SIGNATURE 0x0004 /* check SMB integrity */ +#define SMB_FLAGS2_IS_LONG_NAME 0x0040 /* any path name is a long name */ +#define SMB_FLAGS2_EXT_SEC 0x0800 /* client aware of Extended + * Security negotiation */ +#define SMB_FLAGS2_DFS 0x1000 /* resolve paths in DFS */ +#define SMB_FLAGS2_PAGING_IO 0x2000 /* for exec */ +#define SMB_FLAGS2_ERR_STATUS 0x4000 /* 1 - status.status */ +#define SMB_FLAGS2_UNICODE 0x8000 /* use Unicode for all strings */ + +#define SMB_UID_UNKNOWN 0xffff +#define SMB_TID_UNKNOWN 0xffff + +/* + * Security mode bits + */ +#define SMB_SM_USER 0x01 /* server in the user security mode */ +#define SMB_SM_ENCRYPT 0x02 /* use challenge/responce */ + +/* + * NTLM capabilities + */ +#define SMB_CAP_RAW_MODE 0x0001 +#define SMB_CAP_MPX_MODE 0x0002 +#define SMB_CAP_UNICODE 0x0004 +#define SMB_CAP_LARGE_FILES 0x0008 /* 64 bit offsets supported */ +#define SMB_CAP_NT_SMBS 0x0010 +#define SMB_CAP_NT_FIND 0x0200 +#define SMB_CAP_EXT_SECURITY 0x80000000 + +/* + * File attributes + */ +#define SMB_FA_RDONLY 0x01 +#define SMB_FA_HIDDEN 0x02 +#define SMB_FA_SYSTEM 0x04 +#define SMB_FA_VOLUME 0x08 +#define SMB_FA_DIR 0x10 +#define SMB_FA_ARCHIVE 0x20 + +/* + * Extended file attributes + */ +#define SMB_EFA_RDONLY 0x0001 +#define SMB_EFA_HIDDEN 0x0002 +#define SMB_EFA_SYSTEM 0x0004 +#define SMB_EFA_ARCHIVE 0x0020 +#define SMB_EFA_NORMAL 0x0080 +#define SMB_EFA_TEMPORARY 0x0100 +#define SMB_EFA_COMPRESSED 0x0800 +#define SMB_EFA_POSIX_SEMANTICS 0x00100000 +#define SMB_EFA_BACKUP_SEMANTICS 0x02000000 +#define SMB_EFA_DELETE_ON_CLOSE 0x04000000 +#define SMB_EFA_SEQUENTIAL_SCAN 0x08000000 +#define SMB_EFA_RANDOM_ACCESS 0x10000000 +#define SMB_EFA_NO_BUFFERING 0x20000000 +#define SMB_EFA_WRITE_THROUGH 0x80000000 + +/* + * Access Mode Encoding + */ +#define SMB_AM_OPENREAD 0x0000 +#define SMB_AM_OPENWRITE 0x0001 +#define SMB_AM_OPENRW 0x0002 +#define SMB_AM_OPENEXEC 0x0003 +#define SMB_SM_COMPAT 0x0000 +#define SMB_SM_EXCLUSIVE 0x0010 +#define SMB_SM_DENYWRITE 0x0020 +#define SMB_SM_DENYREADEXEC 0x0030 +#define SMB_SM_DENYNONE 0x0040 + +/* + * SMB commands + */ +#define SMB_COM_CREATE_DIRECTORY 0x00 +#define SMB_COM_DELETE_DIRECTORY 0x01 +#define SMB_COM_OPEN 0x02 +#define SMB_COM_CREATE 0x03 +#define SMB_COM_CLOSE 0x04 +#define SMB_COM_FLUSH 0x05 +#define SMB_COM_DELETE 0x06 +#define SMB_COM_RENAME 0x07 +#define SMB_COM_QUERY_INFORMATION 0x08 +#define SMB_COM_SET_INFORMATION 0x09 +#define SMB_COM_READ 0x0A +#define SMB_COM_WRITE 0x0B +#define SMB_COM_LOCK_BYTE_RANGE 0x0C +#define SMB_COM_UNLOCK_BYTE_RANGE 0x0D +#define SMB_COM_CREATE_TEMPORARY 0x0E +#define SMB_COM_CREATE_NEW 0x0F +#define SMB_COM_CHECK_DIRECTORY 0x10 +#define SMB_COM_PROCESS_EXIT 0x11 +#define SMB_COM_SEEK 0x12 +#define SMB_COM_LOCK_AND_READ 0x13 +#define SMB_COM_WRITE_AND_UNLOCK 0x14 +#define SMB_COM_READ_RAW 0x1A +#define SMB_COM_READ_MPX 0x1B +#define SMB_COM_READ_MPX_SECONDARY 0x1C +#define SMB_COM_WRITE_RAW 0x1D +#define SMB_COM_WRITE_MPX 0x1E +#define SMB_COM_WRITE_COMPLETE 0x20 +#define SMB_COM_SET_INFORMATION2 0x22 +#define SMB_COM_QUERY_INFORMATION2 0x23 +#define SMB_COM_LOCKING_ANDX 0x24 +#define SMB_COM_TRANSACTION 0x25 +#define SMB_COM_TRANSACTION_SECONDARY 0x26 +#define SMB_COM_IOCTL 0x27 +#define SMB_COM_IOCTL_SECONDARY 0x28 +#define SMB_COM_COPY 0x29 +#define SMB_COM_MOVE 0x2A +#define SMB_COM_ECHO 0x2B +#define SMB_COM_WRITE_AND_CLOSE 0x2C +#define SMB_COM_OPEN_ANDX 0x2D +#define SMB_COM_READ_ANDX 0x2E +#define SMB_COM_WRITE_ANDX 0x2F +#define SMB_COM_CLOSE_AND_TREE_DISC 0x31 +#define SMB_COM_TRANSACTION2 0x32 +#define SMB_COM_TRANSACTION2_SECONDARY 0x33 +#define SMB_COM_FIND_CLOSE2 0x34 +#define SMB_COM_FIND_NOTIFY_CLOSE 0x35 +#define SMB_COM_TREE_CONNECT 0x70 +#define SMB_COM_TREE_DISCONNECT 0x71 +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SESSION_SETUP_ANDX 0x73 +#define SMB_COM_LOGOFF_ANDX 0x74 +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_QUERY_INFORMATION_DISK 0x80 +#define SMB_COM_SEARCH 0x81 +#define SMB_COM_FIND 0x82 +#define SMB_COM_FIND_UNIQUE 0x83 +#define SMB_COM_NT_TRANSACT 0xA0 +#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 +#define SMB_COM_NT_CREATE_ANDX 0xA2 +#define SMB_COM_NT_CANCEL 0xA4 +#define SMB_COM_OPEN_PRINT_FILE 0xC0 +#define SMB_COM_WRITE_PRINT_FILE 0xC1 +#define SMB_COM_CLOSE_PRINT_FILE 0xC2 +#define SMB_COM_GET_PRINT_QUEUE 0xC3 +#define SMB_COM_READ_BULK 0xD8 +#define SMB_COM_WRITE_BULK 0xD9 +#define SMB_COM_WRITE_BULK_DATA 0xDA + +/* + * TRANS2 commands + */ +#define SMB_TRANS2_OPEN2 0x00 +#define SMB_TRANS2_FIND_FIRST2 0x01 +#define SMB_TRANS2_FIND_NEXT2 0x02 +#define SMB_TRANS2_QUERY_FS_INFORMATION 0x03 +#define SMB_TRANS2_QUERY_PATH_INFORMATION 0x05 +#define SMB_TRANS2_SET_PATH_INFORMATION 0x06 +#define SMB_TRANS2_QUERY_FILE_INFORMATION 0x07 +#define SMB_TRANS2_SET_FILE_INFORMATION 0x08 +#define SMB_TRANS2_FSCTL 0x09 +#define SMB_TRANS2_IOCTL2 0x0A +#define SMB_TRANS2_FIND_NOTIFY_FIRST 0x0B +#define SMB_TRANS2_FIND_NOTIFY_NEXT 0x0C +#define SMB_TRANS2_CREATE_DIRECTORY 0x0D +#define SMB_TRANS2_SESSION_SETUP 0x0E +#define SMB_TRANS2_GET_DFS_REFERRAL 0x10 +#define SMB_TRANS2_REPORT_DFS_INCONSISTENCY 0x11 + +/* + * SMB_TRANS2_QUERY_FS_INFORMATION levels + */ +#define SMB_INFO_ALLOCATION 1 +#define SMB_INFO_VOLUME 2 +#define SMB_QUERY_FS_VOLUME_INFO 0x102 +#define SMB_QUERY_FS_SIZE_INFO 0x103 +#define SMB_QUERY_FS_DEVICE_INFO 0x104 +#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 + +/* + * SMB_TRANS2_FIND_FIRST2 information levels + */ +#define SMB_INFO_STANDARD 1 +#define SMB_INFO_QUERY_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_BOTH_DIRECTORY_INFO 0x104 + +/* + * Set PATH/FILE information levels + */ +#define SMB_SET_FILE_BASIC_INFO 0x101 + +/* + * LOCKING_ANDX LockType flags + */ +#define SMB_LOCKING_ANDX_SHARED_LOCK 0x01 +#define SMB_LOCKING_ANDX_OPLOCK_RELEASE 0x02 +#define SMB_LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 +#define SMB_LOCKING_ANDX_CANCEL_LOCK 0x08 +#define SMB_LOCKING_ANDX_LARGE_FILES 0x10 + +/* + * Some names length limitations. Some of them aren't declared by specs, + * but we need reasonable limits. + */ +#define SMB_MAXSRVNAMELEN 15 /* NetBIOS limit */ +#define SMB_MAXUSERNAMELEN 128 +#define SMB_MAXPASSWORDLEN 128 +#define SMB_MAXSHARENAMELEN 128 +#define SMB_MAXPKTLEN 0x1FFFF +#define SMB_MAXCHALLENGELEN 8 +#define SMB_MAXFNAMELEN 255 /* Keep in sync with MAXNAMLEN */ + +#define SMB_MAXRCN 3 /* number of reconnect attempts */ + +/* + * Error classes + */ +#define SMBSUCCESS 0x00 +#define ERRDOS 0x01 +#define ERRSRV 0x02 +#define ERRHRD 0x03 /* Error is an hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* + * Error codes for the ERRDOS class + */ +#define ERRbadfunc 1 /* Invalid function */ +#define ERRbadfile 2 /* File not found (last component) */ +#define ERRbadpath 3 /* Directory invalid */ +#define ERRnofids 4 /* Too many open files */ +#define ERRnoaccess 5 /* Access denied */ +#define ERRbadfid 6 /* Invalid file handle */ +#define ERRbadmcb 7 /* Memory control blocks destroyed (huh ?) */ +#define ERRnomem 8 /* Insufficient memory */ +#define ERRbadmem 9 /* Invalid memory block address */ +#define ERRbadenv 10 /* Invalid environment */ +#define ERRbadformat 11 /* Invalid format */ +#define ERRbadaccess 12 /* Invalid open mode */ +#define ERRbaddata 13 /* Invalid data */ +#define ERRbaddrive 15 /* Invalid drive specified */ +#define ERRremcd 16 /* An attempt to delete current directory */ +#define ERRdiffdevice 17 /* cross fs rename/move */ +#define ERRnofiles 18 /* no more files found in file search */ +#define ERRbadshare 32 /* Share mode can't be granted */ +#define ERRlock 33 /* A lock request conflicts with existing lock */ +#define ERRfilexists 80 /* The file named in the request already exists */ + +/* + * Error codes for the ERRSRV class + */ +#define ERRerror 1 /* Non-specific error code */ +#define ERRbadpw 2 /* Bad password */ +#define ERRaccess 4 /* The client doesn't have enough access rights */ +#define ERRinvnid 5 /* The Tid specified in a command is invalid */ +#define ERRinvnetname 6 /* Invalid server name in the tree connect */ +#define ERRinvdevice 7 /* Printer and not printer devices are mixed */ +#define ERRqfull 49 /* Print queue full */ +#define ERRqtoobig 50 /* Print queue full - no space */ +#define ERRinvpfid 52 /* Invalid print file FID */ +#define ERRsmbcmd 64 /* The server did not recognise the command */ +#define ERRsrverror 65 /* The server encountered and internal error */ +#define ERRfilespecs 67 /* The Fid and path name contains an invalid combination */ +#define ERRbadpermits 69 /* Access mode invalid */ +#define ERRsetattrmode 71 /* Attribute mode invalid */ +#define ERRpaused 81 /* Server is paused */ +#define ERRmsgoff 82 /* Not receiving messages */ +#define ERRnoroom 83 /* No room to buffer message */ +#define ERRrmuns 87 /* Too many remote user names */ +#define ERRtimeout 88 /* Operation timed out */ +#define ERRnoresource 89 /* No resources currently available for request */ +#define ERRtoomanyuids 90 /* Too many UIDs active on this session */ +#define ERRbaduid 91 /* The UID is not known in this session */ +#define ERRusempx 250 /* Temporarily unable to support Raw, use MPX mode */ +#define ERRusestd 251 /* Temporarily unable to support Raw, use stdandard r/w */ +#define ERRcontmpx 252 /* Continue in MPX mode */ +#define ERRnosupport 65535 /* Invalid function */ + +/* + * Error codes for the ERRHRD class + */ +#define ERRnowrite 19 /* write protected media */ +#define ERRbadunit 20 /* Unknown unit */ +#define ERRnotready 21 /* Drive not ready */ +#define ERRbadcmd 22 /* Unknown command */ +#define ERRdata 23 /* Data error (CRC) */ +#define ERRbadreq 24 /* Bad request structure length */ +#define ERRseek 25 /* Seek error */ +#define ERRbadmedia 26 /* Unknown media type */ +#define ERRbadsector 27 /* Sector not found */ +#define ERRnopaper 28 /* Printer out of paper */ +#define ERRwrite 29 /* Write fault */ +#define ERRread 30 /* Read fault */ +#define ERRgeneral 31 /* General failure */ +#define ERRbadshare 32 /* A open conflicts with an existing open */ +#define ERRlock 33 /* lock/unlock conflict */ +#define ERRwrongdisk 34 /* The wrong disk was found in a drive */ +#define ERRFCBunavail 35 /* No FCBs available */ +#define ERRsharebufexc 36 /* A sharing buffer has been exceeded */ + +/* + * RAP error codes (it seems that they returned not only by RAP) + */ +#define SMB_ERROR_ACCESS_DENIED 5 +#define SMB_ERROR_NETWORK_ACCESS_DENIED 65 +#define SMB_ERROR_MORE_DATA 234 + +typedef u_int16_t smbfh; + +#endif /* _NETSMB_SMB_H_ */ diff --git a/sys/netsmb/smb_conn.c b/sys/netsmb/smb_conn.c new file mode 100644 index 000000000000..54f5c0b82879 --- /dev/null +++ b/sys/netsmb/smb_conn.c @@ -0,0 +1,874 @@ +/* + * Copyright (c) 2000-2001 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 engine. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +static struct smb_connobj smb_vclist; +static int smb_vcnext = 1; /* next unique id for VC */ + +extern struct linker_set sysctl_net_smb; + +SYSCTL_NODE(_net, OID_AUTO, smb, CTLFLAG_RW, NULL, "SMB protocol"); + +MALLOC_DEFINE(M_SMBCONN, "SMB conn", "SMB connection"); + +static void smb_co_init(struct smb_connobj *cp, int level, char *objname, + struct proc *p); +static void smb_co_done(struct smb_connobj *cp); +static int smb_co_lockstatus(struct smb_connobj *cp, struct proc *p); + +static int smb_vc_disconnect(struct smb_vc *vcp); +static void smb_vc_free(struct smb_connobj *cp); +static void smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred); +static smb_co_free_t smb_share_free; +static smb_co_gone_t smb_share_gone; + +static int smb_sysctl_treedump(SYSCTL_HANDLER_ARGS); + +SYSCTL_PROC(_net_smb, OID_AUTO, treedump, CTLFLAG_RD | CTLTYPE_OPAQUE, + NULL, 0, smb_sysctl_treedump, "S,treedump", "Requester tree"); + +int +smb_sm_init(void) +{ + + smb_co_init(&smb_vclist, SMBL_SM, "smbsm", curproc); + smb_co_unlock(&smb_vclist, 0, curproc); + return 0; +} + +int +smb_sm_done(void) +{ + + /* XXX: hold the mutex */ + if (smb_vclist.co_usecount > 1) { + SMBERROR("%d connections still active\n", smb_vclist.co_usecount - 1); + return EBUSY; + } + smb_co_done(&smb_vclist); + return 0; +} + +static int +smb_sm_lockvclist(int flags, struct proc *p) +{ + + return smb_co_lock(&smb_vclist, flags | LK_CANRECURSE, p); +} + +static void +smb_sm_unlockvclist(struct proc *p) +{ + + smb_co_unlock(&smb_vclist, LK_RELEASE, p); +} + +static int +smb_sm_lookupint(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_vc **vcpp) +{ + struct proc *p = scred->scr_p; + struct smb_vc *vcp; + int exact = 1; + int error; + + vcspec->shspec = shspec; + error = ENOENT; + SMBCO_FOREACH((struct smb_connobj*)vcp, &smb_vclist) { + error = smb_vc_lock(vcp, LK_EXCLUSIVE, p); + if (error) + continue; + itry { + if ((vcp->obj.co_flags & SMBV_PRIVATE) || + !CONNADDREQ(vcp->vc_paddr, vcspec->sap) || + strcmp(vcp->vc_username, vcspec->username) != 0) + ithrow(1); + if (vcspec->owner != SMBM_ANY_OWNER) { + if (vcp->vc_uid != vcspec->owner) + ithrow(1); + } else + exact = 0; + if (vcspec->group != SMBM_ANY_GROUP) { + if (vcp->vc_grp != vcspec->group) + ithrow(1); + } else + exact = 0; + + if (vcspec->mode & SMBM_EXACT) { + if (!exact || + (vcspec->mode & SMBM_MASK) != vcp->vc_mode) + ithrow(1); + } + if (smb_vc_access(vcp, scred, vcspec->mode) != 0) + ithrow(1); + vcspec->ssp = NULL; + if (shspec) + ithrow(smb_vc_lookupshare(vcp, shspec, scred, &vcspec->ssp)); + error = 0; + break; + } icatch(error) { + smb_vc_unlock(vcp, 0, p); + } ifinally { + } iendtry; + if (error == 0) + break; + } + if (vcp) { + smb_vc_ref(vcp, p); + *vcpp = vcp; + } + return error; +} + +int +smb_sm_lookup(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_vc **vcpp) +{ + struct proc *p = scred->scr_p; + struct smb_vc *vcp; + struct smb_share *ssp = NULL; + int error; + + *vcpp = vcp = NULL; + + error = smb_sm_lockvclist(LK_EXCLUSIVE, p); + if (error) + return error; + error = smb_sm_lookupint(vcspec, shspec, scred, vcpp); + if (error == 0 || (vcspec->flags & SMBV_CREATE) == 0) { + smb_sm_unlockvclist(p); + return error; + } + error = smb_sm_lookupint(vcspec, NULL, scred, &vcp); + if (error) { + error = smb_vc_create(vcspec, scred, &vcp); + if (error) + goto out; + error = smb_vc_connect(vcp, scred); + if (error) + goto out; + } + if (shspec == NULL) + goto out; + error = smb_share_create(vcp, shspec, scred, &ssp); + if (error) + goto out; + error = smb_smb_treeconnect(ssp, scred); + if (error == 0) + vcspec->ssp = ssp; + else + smb_share_put(ssp, scred); +out: + smb_sm_unlockvclist(p); + if (error == 0) + *vcpp = vcp; + else if (vcp) + smb_vc_put(vcp, scred); + return error; +} + +/* + * Common code for connection object + */ +static void +smb_co_init(struct smb_connobj *cp, int level, char *objname, struct proc *p) +{ + SLIST_INIT(&cp->co_children); + smb_sl_init(&cp->co_interlock, objname); + lockinit(&cp->co_lock, PZERO, objname, 0, 0); + cp->co_level = level; + cp->co_usecount = 1; + KASSERT(smb_co_lock(cp, LK_EXCLUSIVE, p) == 0, ("smb_co_init: lock failed")); +} + +static void +smb_co_done(struct smb_connobj *cp) +{ + smb_sl_destroy(&cp->co_interlock); + lockdestroy(&cp->co_lock); +} + +static void +smb_co_gone(struct smb_connobj *cp, struct smb_cred *scred) +{ + struct smb_connobj *parent; + + if (cp->co_gone) + cp->co_gone(cp, scred); + parent = cp->co_parent; + if (parent) { + smb_co_lock(parent, LK_EXCLUSIVE, scred->scr_p); + SLIST_REMOVE(&parent->co_children, cp, smb_connobj, co_next); + smb_co_put(parent, scred); + } + if (cp->co_free) + cp->co_free(cp); +} + +void +smb_co_ref(struct smb_connobj *cp, struct proc *p) +{ + + SMB_CO_LOCK(cp); + cp->co_usecount++; + SMB_CO_UNLOCK(cp); +} + +void +smb_co_rele(struct smb_connobj *cp, struct smb_cred *scred) +{ + struct proc *p = scred->scr_p; + + SMB_CO_LOCK(cp); + if (cp->co_usecount > 1) { + cp->co_usecount--; + SMB_CO_UNLOCK(cp); + return; + } + if (cp->co_usecount == 0) { + SMBERROR("negative use_count for object %d", cp->co_level); + SMB_CO_UNLOCK(cp); + return; + } + cp->co_usecount--; + cp->co_flags |= SMBO_GONE; + + lockmgr(&cp->co_lock, LK_DRAIN | LK_INTERLOCK, &cp->co_interlock, p); + smb_co_gone(cp, scred); +} + +int +smb_co_get(struct smb_connobj *cp, int flags, struct smb_cred *scred) +{ + int error; + + if ((flags & LK_INTERLOCK) == 0) + SMB_CO_LOCK(cp); + cp->co_usecount++; + error = smb_co_lock(cp, flags | LK_INTERLOCK, scred->scr_p); + if (error) { + SMB_CO_LOCK(cp); + cp->co_usecount--; + SMB_CO_UNLOCK(cp); + return error; + } + return 0; +} + +void +smb_co_put(struct smb_connobj *cp, struct smb_cred *scred) +{ + struct proc *p = scred->scr_p; + int flags; + + flags = LK_RELEASE; + SMB_CO_LOCK(cp); + if (cp->co_usecount > 1) { + cp->co_usecount--; + } else if (cp->co_usecount == 1) { + cp->co_usecount--; + cp->co_flags |= SMBO_GONE; + flags = LK_DRAIN; + } else { + SMBERROR("negative usecount"); + } + lockmgr(&cp->co_lock, LK_RELEASE | LK_INTERLOCK, &cp->co_interlock, p); + if ((cp->co_flags & SMBO_GONE) == 0) + return; + lockmgr(&cp->co_lock, LK_DRAIN, NULL, p); + smb_co_gone(cp, scred); +} + +int +smb_co_lockstatus(struct smb_connobj *cp, struct proc *p) +{ + return lockstatus(&cp->co_lock, p); +} + +int +smb_co_lock(struct smb_connobj *cp, int flags, struct proc *p) +{ + + if (cp->co_flags & SMBO_GONE) + return EINVAL; + if ((flags & LK_TYPE_MASK) == 0) + flags |= LK_EXCLUSIVE; + if (smb_co_lockstatus(cp, p) == LK_EXCLUSIVE && + (flags & LK_CANRECURSE) == 0) { + SMBERROR("recursive lock for object %d\n", cp->co_level); + return 0; + } + return lockmgr(&cp->co_lock, flags, &cp->co_interlock, p); +} + +void +smb_co_unlock(struct smb_connobj *cp, int flags, struct proc *p) +{ + (void)lockmgr(&cp->co_lock, flags | LK_RELEASE, &cp->co_interlock, p); +} + +static void +smb_co_addchild(struct smb_connobj *parent, struct smb_connobj *child) +{ + KASSERT(smb_co_lockstatus(parent, curproc) == LK_EXCLUSIVE, ("smb_co_addchild: parent not locked")); + KASSERT(smb_co_lockstatus(child, curproc) == LK_EXCLUSIVE, ("smb_co_addchild: child not locked")); + + smb_co_ref(parent, curproc); + SLIST_INSERT_HEAD(&parent->co_children, child, co_next); + child->co_parent = parent; +} + +/* + * Session implementation + */ + +int +smb_vc_create(struct smb_vcspec *vcspec, + struct smb_cred *scred, struct smb_vc **vcpp) +{ + struct smb_vc *vcp; + struct proc *p = scred->scr_p; + struct ucred *cred = scred->scr_cred; + uid_t uid = vcspec->owner; + gid_t gid = vcspec->group; + uid_t realuid = cred->cr_uid; + char *domain = vcspec->domain; + int error, isroot; + + isroot = smb_suser(cred) == 0; + /* + * Only superuser can create VCs with different uid and gid + */ + if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot) + return EPERM; + if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot) + return EPERM; + + vcp = smb_zmalloc(sizeof(*vcp), M_SMBCONN, M_WAITOK); + smb_co_init(VCTOCP(vcp), SMBL_VC, "smb_vc", p); + vcp->obj.co_free = smb_vc_free; + vcp->obj.co_gone = smb_vc_gone; + vcp->vc_number = smb_vcnext++; + vcp->vc_timo = SMB_DEFRQTIMO; + vcp->vc_smbuid = SMB_UID_UNKNOWN; + vcp->vc_mode = vcspec->rights & SMBM_MASK; + vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE); + vcp->vc_tdesc = &smb_tran_nbtcp_desc; + + if (uid == SMBM_ANY_OWNER) + uid = realuid; + if (gid == SMBM_ANY_GROUP) + gid = cred->cr_groups[0]; + vcp->vc_uid = uid; + vcp->vc_grp = gid; + + smb_sl_init(&vcp->vc_stlock, "vcstlock"); + error = 0; + itry { + vcp->vc_paddr = dup_sockaddr(vcspec->sap, 1); + ierror(vcp->vc_paddr == NULL, ENOMEM); + + vcp->vc_laddr = dup_sockaddr(vcspec->lap, 1); + ierror(vcp->vc_laddr == NULL, ENOMEM); + + ierror((vcp->vc_pass = smb_strdup(vcspec->pass)) == NULL, ENOMEM); + + vcp->vc_domain = smb_strdup((domain && domain[0]) ? domain : "NODOMAIN"); + ierror(vcp->vc_domain == NULL, ENOMEM); + + ierror((vcp->vc_srvname = smb_strdup(vcspec->srvname)) == NULL, ENOMEM); + ierror((vcp->vc_username = smb_strdup(vcspec->username)) == NULL, ENOMEM); + + ithrow(iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower)); + ithrow(iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper)); + if (vcspec->servercs[0]) { + ithrow(iconv_open(vcspec->servercs, vcspec->localcs, + &vcp->vc_toserver)); + ithrow(iconv_open(vcspec->localcs, vcspec->servercs, + &vcp->vc_tolocal)); + } + + ithrow(smb_iod_create(vcp)); + *vcpp = vcp; + smb_co_addchild(&smb_vclist, VCTOCP(vcp)); + } icatch(error) { + smb_vc_put(vcp, scred); + } ifinally { + } iendtry; + return error; +} + +static void +smb_vc_free(struct smb_connobj *cp) +{ + struct smb_vc *vcp = CPTOVC(cp); + + if (vcp->vc_iod) + smb_iod_destroy(vcp->vc_iod); + SMB_STRFREE(vcp->vc_username); + SMB_STRFREE(vcp->vc_srvname); + SMB_STRFREE(vcp->vc_pass); + SMB_STRFREE(vcp->vc_domain); + if (vcp->vc_paddr) + free(vcp->vc_paddr, M_SONAME); + if (vcp->vc_laddr) + free(vcp->vc_laddr, M_SONAME); + if (vcp->vc_tolower) + iconv_close(vcp->vc_tolower); + if (vcp->vc_toupper) + iconv_close(vcp->vc_toupper); + if (vcp->vc_tolocal) + iconv_close(vcp->vc_tolocal); + if (vcp->vc_toserver) + iconv_close(vcp->vc_toserver); + smb_co_done(VCTOCP(vcp)); + smb_sl_destroy(&vcp->vc_stlock); + free(vcp, M_SMBCONN); +} + +/* + * Called when use count of VC dropped to zero. + * VC should be locked on enter with LK_DRAIN. + */ +static void +smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred) +{ + struct smb_vc *vcp = CPTOVC(cp); + + smb_vc_disconnect(vcp); +} + +void +smb_vc_ref(struct smb_vc *vcp, struct proc *p) +{ + smb_co_ref(VCTOCP(vcp), p); +} + +void +smb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred) +{ + smb_co_rele(VCTOCP(vcp), scred); +} + +int +smb_vc_get(struct smb_vc *vcp, int flags, struct smb_cred *scred) +{ + return smb_co_get(VCTOCP(vcp), flags, scred); +} + +void +smb_vc_put(struct smb_vc *vcp, struct smb_cred *scred) +{ + smb_co_put(VCTOCP(vcp), scred); +} + +int +smb_vc_lock(struct smb_vc *vcp, int flags, struct proc *p) +{ + return smb_co_lock(VCTOCP(vcp), flags, p); +} + +void +smb_vc_unlock(struct smb_vc *vcp, int flags, struct proc *p) +{ + smb_co_unlock(VCTOCP(vcp), flags, p); +} + +int +smb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode) +{ + struct ucred *cred = scred->scr_cred; + + if (smb_suser(cred) == 0 || cred->cr_uid == vcp->vc_uid) + return 0; + mode >>= 3; + if (!groupmember(vcp->vc_grp, cred)) + mode >>= 3; + return (vcp->vc_mode & mode) == mode ? 0 : EACCES; +} + +static int +smb_vc_cmpshare(struct smb_share *ssp, struct smb_sharespec *dp) +{ + int exact = 1; + + if (strcmp(ssp->ss_name, dp->name) != 0) + return 1; + if (dp->owner != SMBM_ANY_OWNER) { + if (ssp->ss_uid != dp->owner) + return 1; + } else + exact = 0; + if (dp->group != SMBM_ANY_GROUP) { + if (ssp->ss_grp != dp->group) + return 1; + } else + exact = 0; + + if (dp->mode & SMBM_EXACT) { + if (!exact) + return 1; + return (dp->mode & SMBM_MASK) == ssp->ss_mode ? 0 : 1; + } + if (smb_share_access(ssp, dp->scred, dp->mode) != 0) + return 1; + return 0; +} + +/* + * Lookup share in the given VC. Share referenced and locked on return. + * VC expected to be locked on entry and will be left locked on exit. + */ +int +smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *dp, + struct smb_cred *scred, struct smb_share **sspp) +{ + struct proc *p = scred->scr_p; + struct smb_share *ssp = NULL; + int error; + + *sspp = NULL; + dp->scred = scred; + SMBCO_FOREACH((struct smb_connobj*)ssp, VCTOCP(vcp)) { + error = smb_share_lock(ssp, LK_EXCLUSIVE, p); + if (error) + continue; + if (smb_vc_cmpshare(ssp, dp) == 0) + break; + smb_share_unlock(ssp, 0, p); + } + if (ssp) { + smb_share_ref(ssp, p); + *sspp = ssp; + error = 0; + } else + error = ENOENT; + return error; +} + +int +smb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred) +{ + + return smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL); +} + +/* + * Destroy VC to server, invalidate shares linked with it. + * Transport should be locked on entry. + */ +int +smb_vc_disconnect(struct smb_vc *vcp) +{ + + smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT | SMBIOD_EV_SYNC, NULL); + return 0; +} + +static char smb_emptypass[] = ""; + +const char * +smb_vc_getpass(struct smb_vc *vcp) +{ + if (vcp->vc_pass) + return vcp->vc_pass; + return smb_emptypass; +} + +static int +smb_vc_getinfo(struct smb_vc *vcp, struct smb_vc_info *vip) +{ + bzero(vip, sizeof(struct smb_vc_info)); + vip->itype = SMB_INFO_VC; + vip->usecount = vcp->obj.co_usecount; + vip->uid = vcp->vc_uid; + vip->gid = vcp->vc_grp; + vip->mode = vcp->vc_mode; + vip->flags = vcp->obj.co_flags; + vip->sopt = vcp->vc_sopt; + vip->iodstate = vcp->vc_iod->iod_state; + bzero(&vip->sopt.sv_skey, sizeof(vip->sopt.sv_skey)); + snprintf(vip->srvname, sizeof(vip->srvname), "%s", vcp->vc_srvname); + snprintf(vip->vcname, sizeof(vip->vcname), "%s", vcp->vc_username); + return 0; +} + +u_short +smb_vc_nextmid(struct smb_vc *vcp) +{ + u_short r; + + SMB_CO_LOCK(&vcp->obj); + r = vcp->vc_mid++; + SMB_CO_UNLOCK(&vcp->obj); + return r; +} + +/* + * Share implementation + */ +/* + * Allocate share structure and attach it to the given VC + * Connection expected to be locked on entry. Share will be returned + * in locked state. + */ +int +smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_share **sspp) +{ + struct smb_share *ssp; + struct proc *p = scred->scr_p; + struct ucred *cred = scred->scr_cred; + uid_t realuid = cred->cr_uid; + uid_t uid = shspec->owner; + gid_t gid = shspec->group; + int error, isroot; + + isroot = smb_suser(cred) == 0; + /* + * Only superuser can create shares with different uid and gid + */ + if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot) + return EPERM; + if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot) + return EPERM; + error = smb_vc_lookupshare(vcp, shspec, scred, &ssp); + if (!error) { + smb_share_put(ssp, scred); + return EEXIST; + } + if (uid == SMBM_ANY_OWNER) + uid = realuid; + if (gid == SMBM_ANY_GROUP) + gid = cred->cr_groups[0]; + ssp = smb_zmalloc(sizeof(*ssp), M_SMBCONN, M_WAITOK); + smb_co_init(SSTOCP(ssp), SMBL_SHARE, "smbss", p); + ssp->obj.co_free = smb_share_free; + ssp->obj.co_gone = smb_share_gone; + smb_sl_init(&ssp->ss_stlock, "ssstlock"); + ssp->ss_name = smb_strdup(shspec->name); + if (shspec->pass && shspec->pass[0]) + ssp->ss_pass = smb_strdup(shspec->pass); + ssp->ss_type = shspec->stype; + ssp->ss_tid = SMB_TID_UNKNOWN; + ssp->ss_uid = uid; + ssp->ss_grp = gid; + ssp->ss_mode = shspec->rights & SMBM_MASK; + smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp)); + *sspp = ssp; + return 0; +} + +static void +smb_share_free(struct smb_connobj *cp) +{ + struct smb_share *ssp = CPTOSS(cp); + + SMB_STRFREE(ssp->ss_name); + SMB_STRFREE(ssp->ss_pass); + smb_sl_destroy(&ssp->ss_stlock); + smb_co_done(SSTOCP(ssp)); + free(ssp, M_SMBCONN); +} + +static void +smb_share_gone(struct smb_connobj *cp, struct smb_cred *scred) +{ + struct smb_share *ssp = CPTOSS(cp); + + smb_smb_treedisconnect(ssp, scred); +} + +void +smb_share_ref(struct smb_share *ssp, struct proc *p) +{ + smb_co_ref(SSTOCP(ssp), p); +} + +void +smb_share_rele(struct smb_share *ssp, struct smb_cred *scred) +{ + smb_co_rele(SSTOCP(ssp), scred); +} + +int +smb_share_get(struct smb_share *ssp, int flags, struct smb_cred *scred) +{ + return smb_co_get(SSTOCP(ssp), flags, scred); +} + +void +smb_share_put(struct smb_share *ssp, struct smb_cred *scred) +{ + smb_co_put(SSTOCP(ssp), scred); +} + +int +smb_share_lock(struct smb_share *ssp, int flags, struct proc *p) +{ + return smb_co_lock(SSTOCP(ssp), flags, p); +} + +void +smb_share_unlock(struct smb_share *ssp, int flags, struct proc *p) +{ + smb_co_unlock(SSTOCP(ssp), flags, p); +} + +int +smb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode) +{ + struct ucred *cred = scred->scr_cred; + + if (smb_suser(cred) == 0 || cred->cr_uid == ssp->ss_uid) + return 0; + mode >>= 3; + if (!groupmember(ssp->ss_grp, cred)) + mode >>= 3; + return (ssp->ss_mode & mode) == mode ? 0 : EACCES; +} + +void +smb_share_invalidate(struct smb_share *ssp) +{ + ssp->ss_tid = SMB_TID_UNKNOWN; +} + +int +smb_share_valid(struct smb_share *ssp) +{ + return ssp->ss_tid != SMB_TID_UNKNOWN && + ssp->ss_vcgenid == SSTOVC(ssp)->vc_genid; +} + +const char* +smb_share_getpass(struct smb_share *ssp) +{ + struct smb_vc *vcp; + + if (ssp->ss_pass) + return ssp->ss_pass; + vcp = SSTOVC(ssp); + if (vcp->vc_pass) + return vcp->vc_pass; + return smb_emptypass; +} + +static int +smb_share_getinfo(struct smb_share *ssp, struct smb_share_info *sip) +{ + bzero(sip, sizeof(struct smb_share_info)); + sip->itype = SMB_INFO_SHARE; + sip->usecount = ssp->obj.co_usecount; + sip->tid = ssp->ss_tid; + sip->type= ssp->ss_type; + sip->uid = ssp->ss_uid; + sip->gid = ssp->ss_grp; + sip->mode= ssp->ss_mode; + sip->flags = ssp->obj.co_flags; + snprintf(sip->sname, sizeof(sip->sname), "%s", ssp->ss_name); + return 0; +} + +/* + * Dump an entire tree into sysctl call + */ +static int +smb_sysctl_treedump(SYSCTL_HANDLER_ARGS) +{ + struct proc *p = req->p; + struct smb_cred scred; + struct smb_vc *vcp; + struct smb_share *ssp; + struct smb_vc_info vci; + struct smb_share_info ssi; + int error, itype; + + smb_makescred(&scred, p, p->p_ucred); + error = smb_sm_lockvclist(LK_SHARED, p); + if (error) + return error; + SMBCO_FOREACH((struct smb_connobj*)vcp, &smb_vclist) { + error = smb_vc_lock(vcp, LK_SHARED, p); + if (error) + continue; + smb_vc_getinfo(vcp, &vci); + error = SYSCTL_OUT(req, &vci, sizeof(struct smb_vc_info)); + if (error) { + smb_vc_unlock(vcp, 0, p); + break; + } + SMBCO_FOREACH((struct smb_connobj*)ssp, VCTOCP(vcp)) { + error = smb_share_lock(ssp, LK_SHARED, p); + if (error) { + error = 0; + continue; + } + smb_share_getinfo(ssp, &ssi); + smb_share_unlock(ssp, 0, p); + error = SYSCTL_OUT(req, &ssi, sizeof(struct smb_share_info)); + if (error) + break; + } + smb_vc_unlock(vcp, 0, p); + if (error) + break; + } + if (!error) { + itype = SMB_INFO_NONE; + error = SYSCTL_OUT(req, &itype, sizeof(itype)); + } + smb_sm_unlockvclist(p); + return error; +} diff --git a/sys/netsmb/smb_conn.h b/sys/netsmb/smb_conn.h new file mode 100644 index 000000000000..3467f9a77ecb --- /dev/null +++ b/sys/netsmb/smb_conn.h @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2000-2001 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 _NETINET_IN_H_ +#include +#endif + +/* + * Two levels of connection hierarchy + */ +#define SMBL_SM 0 +#define SMBL_VC 1 +#define SMBL_SHARE 2 +#define SMBL_NUM 3 +#define SMBL_NONE (-1) + +#define SMB_CS_NONE 0x0000 +#define SMB_CS_UPPER 0x0001 /* convert passed string to upper case */ +#define SMB_CS_LOWER 0x0002 /* convert passed string to lower case */ + +/* + * Common object flags + */ +#define SMBO_GONE 0x1000000 + +/* + * access modes + */ +#define SMBM_READ 0400 /* read conn attrs.(like list shares) */ +#define SMBM_WRITE 0200 /* modify conn attrs */ +#define SMBM_EXEC 0100 /* can send SMB requests */ +#define SMBM_READGRP 0040 +#define SMBM_WRITEGRP 0020 +#define SMBM_EXECGRP 0010 +#define SMBM_READOTH 0004 +#define SMBM_WRITEOTH 0002 +#define SMBM_EXECOTH 0001 +#define SMBM_MASK 0777 +#define SMBM_EXACT 010000 /* check for specified mode exactly */ +#define SMBM_ALL (SMBM_READ | SMBM_WRITE | SMBM_EXEC) +#define SMBM_DEFAULT (SMBM_READ | SMBM_WRITE | SMBM_EXEC) +#define SMBM_ANY_OWNER ((uid_t)-1) +#define SMBM_ANY_GROUP ((gid_t)-1) + +/* + * VC flags + */ +#define SMBV_PERMANENT 0x0002 +#define SMBV_LONGNAMES 0x0004 /* connection is configured to use long names */ +#define SMBV_ENCRYPT 0x0008 /* server asked for encrypted password */ +#define SMBV_WIN95 0x0010 /* used to apply bugfixes for this OS */ +#define SMBV_PRIVATE 0x0020 /* connection can be used only by creator */ +#define SMBV_RECONNECTING 0x0040 /* conn is in the process of reconnection */ +#define SMBV_SINGLESHARE 0x0080 /* only one share connectin should be allowed */ +#define SMBV_CREATE 0x0100 /* lookup for create opeartion */ +/*#define SMBV_FAILED 0x0200*/ /* last reconnect attempt has failed */ + + +/* + * smb_share flags + */ +#define SMBS_PERMANENT 0x0001 +#define SMBS_RECONNECTING 0x0002 +#define SMBS_CONNECTED 0x0004 + +/* + * share types + */ +#define SMB_ST_DISK 0x0 /* A: */ +#define SMB_ST_PRINTER 0x1 /* LPT: */ +#define SMB_ST_PIPE 0x2 /* IPC */ +#define SMB_ST_COMM 0x3 /* COMM */ +#define SMB_ST_ANY 0x4 +#define SMB_ST_MAX 0x4 +#define SMB_ST_NONE 0xff /* not a part of protocol */ + +/* + * Negotiated protocol parameters + */ +struct smb_sopt { + int sv_proto; + int16_t sv_tz; /* offset in min relative to UTC */ + u_int32_t sv_maxtx; /* maximum transmit buf size */ + u_char sv_sm; /* security mode */ + u_int16_t sv_maxmux; /* max number of outstanding rq's */ + u_int16_t sv_maxvcs; /* max number of VCs */ + u_int16_t sv_rawmode; + u_int32_t sv_maxraw; /* maximum raw-buffer size */ + u_int32_t sv_skey; /* session key */ + u_int32_t sv_caps; /* capabilites SMB_CAP_ */ +}; + +/* + * network IO daemon states + */ +enum smbiod_state { + SMBIOD_ST_NOTCONN, /* no connect request was made */ + SMBIOD_ST_RECONNECT, /* a [re]connect attempt is in progress */ + SMBIOD_ST_TRANACTIVE, /* transport level is up */ + SMBIOD_ST_VCACTIVE, /* session established */ + SMBIOD_ST_DEAD /* connection broken, transport is down */ +}; + + +/* + * Info structures + */ +#define SMB_INFO_NONE 0 +#define SMB_INFO_VC 2 +#define SMB_INFO_SHARE 3 + +struct smb_vc_info { + int itype; + int usecount; + uid_t uid; /* user id of connection */ + gid_t gid; /* group of connection */ + mode_t mode; /* access mode */ + int flags; + enum smbiod_state iodstate; + struct smb_sopt sopt; + char srvname[SMB_MAXSRVNAMELEN]; + char vcname[128]; +}; + +struct smb_share_info { + int itype; + int usecount; + u_short tid; /* TID */ + int type; /* share type */ + uid_t uid; /* user id of connection */ + gid_t gid; /* group of connection */ + mode_t mode; /* access mode */ + int flags; + char sname[128]; +}; + +#ifdef _KERNEL + +#include +#include + +#define CONNADDREQ(a1,a2) ((a1)->sa_len == (a2)->sa_len && \ + bcmp(a1, a2, (a1)->sa_len) == 0) + +struct smb_vc; +struct smb_share; +struct smb_cred; +struct smb_rq; +struct mbdata; +struct smbioc_oshare; +struct smbioc_ossn; +struct uio; + +TAILQ_HEAD(smb_rqhead, smb_rq); + +#define SMB_DEFRQTIMO 5 + +#define SMB_DIALECT(vcp) ((vcp)->vc_sopt.sv_proto) + +struct smb_tran_desc; + +/* + * Connection object + */ +struct smb_connobj; + +typedef void smb_co_gone_t (struct smb_connobj *cp, struct smb_cred *scred); +typedef void smb_co_free_t (struct smb_connobj *cp); + +#define SMB_CO_LOCK(cp) smb_sl_lock(&(cp)->co_interlock) +#define SMB_CO_UNLOCK(cp) smb_sl_unlock(&(cp)->co_interlock) + +struct smb_connobj { + int co_level; /* SMBL_ */ + int co_flags; + struct lock co_lock; + struct smb_slock co_interlock; + int co_usecount; + struct smb_connobj * co_parent; + SLIST_HEAD(,smb_connobj)co_children; + SLIST_ENTRY(smb_connobj)co_next; + smb_co_gone_t * co_gone; + smb_co_free_t * co_free; +}; + +#define SMBCO_FOREACH(var, cp) SLIST_FOREACH((var), &(cp)->co_children, co_next) + +/* + * Virtual Circuit (session) to a server. + * This is the most (over)complicated part of SMB protocol. + * For the user security level (usl), each session with different remote + * user name has its own VC. + * It is unclear however, should share security level (ssl) allow additional + * VCs, because user name is not used and can be the same. On other hand, + * multiple VCs allows us to create separate sessions to server on a per + * user basis. + */ + +/* + * This lock protects vc_flags + */ +#define SMBC_ST_LOCK(vcp) smb_sl_lock(&(vcp)->vc_stlock) +#define SMBC_ST_UNLOCK(vcp) smb_sl_unlock(&(vcp)->vc_stlock) + + +struct smb_vc { + struct smb_connobj obj; + char * vc_srvname; + struct sockaddr*vc_paddr; /* server addr */ + struct sockaddr*vc_laddr; /* local addr, if any */ + char * vc_username; + char * vc_pass; /* password for usl case */ + char * vc_domain; /* workgroup/primary domain */ + + u_int vc_timo; /* default request timeout */ + int vc_maxvcs; /* maximum number of VC per connection */ + + void * vc_tolower; /* local charset */ + void * vc_toupper; /* local charset */ + void * vc_toserver; /* local charset to server one */ + void * vc_tolocal; /* server charset to local one */ + int vc_number; /* number of this VC from the client side */ + int vc_genid; + uid_t vc_uid; /* user id of connection */ + gid_t vc_grp; /* group of connection */ + mode_t vc_mode; /* access mode */ + struct tnode * vc_tnode; /* backing object */ + u_short vc_smbuid; /* unique vc id assigned by server */ + + u_char vc_hflags; /* or'ed with flags in the smb header */ + u_short vc_hflags2; /* or'ed with flags in the smb header */ + void * vc_tdata; /* transport control block */ + struct smb_tran_desc *vc_tdesc; + int vc_chlen; /* actual challenge length */ + u_char vc_ch[SMB_MAXCHALLENGELEN]; + u_short vc_mid; /* multiplex id */ + struct smb_sopt vc_sopt; /* server options */ + struct smb_cred*vc_scred; /* used in reconnect procedure */ + int vc_txmax; /* max tx/rx packet size */ + struct smbiod * vc_iod; + struct smb_slock vc_stlock; +}; + +#define vc_maxmux vc_sopt.sv_maxmux +#define vc_flags obj.co_flags + + +/* + * smb_share structure describes connection to the given SMB share (tree). + * Connection to share is always built on top of the VC. + */ + +/* + * This lock protects ss_flags + */ +#define SMBS_ST_LOCK(ssp) smb_sl_lock(&(ssp)->ss_stlock) +#define SMBS_ST_LOCKPTR(ssp) (&(ssp)->ss_stlock) +#define SMBS_ST_UNLOCK(ssp) smb_sl_unlock(&(ssp)->ss_stlock) + +struct smb_share { + struct smb_connobj obj; + char * ss_name; + u_short ss_tid; /* TID */ + int ss_type; /* share type */ + uid_t ss_uid; /* user id of connection */ + gid_t ss_grp; /* group of connection */ + mode_t ss_mode; /* access mode */ + int ss_vcgenid; + char * ss_pass; /* password to a share, can be null */ + struct smb_slock ss_stlock; + struct smb_cred *ss_cred; /* used in reconnect procedure */ +}; + +#define ss_flags obj.co_flags + +#define CPTOVC(cp) ((struct smb_vc*)(cp)) +#define VCTOCP(vcp) (&(vcp)->obj) +#define CPTOSS(cp) ((struct smb_share*)(cp)) +#define SSTOVC(ssp) CPTOVC(((ssp)->obj.co_parent)) +#define SSTOCP(ssp) (&(ssp)->obj) + +struct smb_vcspec { + char * srvname; + struct sockaddr*sap; + struct sockaddr*lap; + int flags; + char * username; + char * pass; + char * domain; + mode_t mode; + mode_t rights; + uid_t owner; + gid_t group; + char * localcs; + char * servercs; + struct smb_sharespec *shspec; + struct smb_share *ssp; /* returned */ + /* + * The rest is an internal data + */ + struct smb_cred *scred; +}; + +struct smb_sharespec { + char * name; + char * pass; + mode_t mode; + mode_t rights; + uid_t owner; + gid_t group; + int stype; + /* + * The rest is an internal data + */ + struct smb_cred *scred; +}; + +/* + * Session level functions + */ +int smb_sm_init(void); +int smb_sm_done(void); +int smb_sm_lookup(struct smb_vcspec *vcspec, + struct smb_sharespec *shspec, struct smb_cred *scred, + struct smb_vc **vcpp); + +/* + * Connection object + */ +void smb_co_ref(struct smb_connobj *cp, struct proc *p); +void smb_co_rele(struct smb_connobj *cp, struct smb_cred *scred); +int smb_co_get(struct smb_connobj *cp, int flags, struct smb_cred *scred); +void smb_co_put(struct smb_connobj *cp, struct smb_cred *scred); +int smb_co_lock(struct smb_connobj *cp, int flags, struct proc *p); +void smb_co_unlock(struct smb_connobj *cp, int flags, struct proc *p); + +/* + * session level functions + */ +int smb_vc_create(struct smb_vcspec *vcspec, + struct smb_cred *scred, struct smb_vc **vcpp); +int smb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred); +int smb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode); +int smb_vc_get(struct smb_vc *vcp, int flags, struct smb_cred *scred); +void smb_vc_put(struct smb_vc *vcp, struct smb_cred *scred); +void smb_vc_ref(struct smb_vc *vcp, struct proc *p); +void smb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred); +int smb_vc_lock(struct smb_vc *vcp, int flags, struct proc *p); +void smb_vc_unlock(struct smb_vc *vcp, int flags, struct proc *p); +int smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_share **sspp); +const char * smb_vc_getpass(struct smb_vc *vcp); +u_short smb_vc_nextmid(struct smb_vc *vcp); + +/* + * share level functions + */ +int smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_share **sspp); +int smb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode); +void smb_share_ref(struct smb_share *ssp, struct proc *p); +void smb_share_rele(struct smb_share *ssp, struct smb_cred *scred); +int smb_share_get(struct smb_share *ssp, int flags, struct smb_cred *scred); +void smb_share_put(struct smb_share *ssp, struct smb_cred *scred); +int smb_share_lock(struct smb_share *ssp, int flags, struct proc *p); +void smb_share_unlock(struct smb_share *ssp, int flags, struct proc *p); +void smb_share_invalidate(struct smb_share *ssp); +int smb_share_valid(struct smb_share *ssp); +const char * smb_share_getpass(struct smb_share *ssp); + +/* + * SMB protocol level functions + */ +int smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred); +int smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred); +int smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred); +int smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred); +int smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred); +int smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio, + struct smb_cred *scred); +int smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio, + struct smb_cred *scred); +int smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred); + +/* + * smbiod thread + */ + +#define SMBIOD_EV_NEWRQ 0x0001 +#define SMBIOD_EV_SHUTDOWN 0x0002 +#define SMBIOD_EV_CONNECT 0x0003 +#define SMBIOD_EV_DISCONNECT 0x0004 +#define SMBIOD_EV_TREECONNECT 0x0005 +#define SMBIOD_EV_MASK 0x00ff +#define SMBIOD_EV_SYNC 0x0100 +#define SMBIOD_EV_PROCESSING 0x0200 + +struct smbiod_event { + int ev_type; + int ev_error; + void * ev_ident; + STAILQ_ENTRY(smbiod_event) ev_link; +}; + +#define SMBIOD_SHUTDOWN 0x0001 + +struct smbiod { + int iod_id; + int iod_flags; + enum smbiod_state iod_state; + int iod_muxcnt; /* number of active outstanding requests */ + int iod_sleeptimo; + struct smb_vc * iod_vc; + struct smb_slock iod_rqlock; /* iod_rqlist, iod_muxwant */ + struct smb_rqhead iod_rqlist; /* list of outstanding requests */ + int iod_muxwant; + struct proc * iod_p; + struct smb_cred iod_scred; + struct smb_slock iod_evlock; /* iod_evlist */ + STAILQ_HEAD(,smbiod_event) iod_evlist; + struct timespec iod_lastrqsent; + struct timespec iod_pingtimo; +}; + +int smb_iod_init(void); +int smb_iod_done(void); +int smb_iod_create(struct smb_vc *vcp); +int smb_iod_destroy(struct smbiod *iod); +int smb_iod_request(struct smbiod *iod, int event, void *ident); +int smb_iod_addrq(struct smb_rq *rqp); +int smb_iod_waitrq(struct smb_rq *rqp); +int smb_iod_removerq(struct smb_rq *rqp); + +#endif /* _KERNEL */ diff --git a/sys/netsmb/smb_crypt.c b/sys/netsmb/smb_crypt.c new file mode 100644 index 000000000000..e72a385304f9 --- /dev/null +++ b/sys/netsmb/smb_crypt.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2000-2001, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "opt_netsmb.h" + +#ifdef NETSMBCRYPTO + +#include + +static u_char N8[] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + + +static void +smb_E(const u_char *key, u_char *data, u_char *dest) +{ + des_key_schedule *ksp; + u_char kk[8]; + + kk[0] = key[0] & 0xfe; + kk[1] = key[0] << 7 | (key[1] >> 1 & 0xfe); + kk[2] = key[1] << 6 | (key[2] >> 2 & 0xfe); + kk[3] = key[2] << 5 | (key[3] >> 3 & 0xfe); + kk[4] = key[3] << 4 | (key[4] >> 4 & 0xfe); + kk[5] = key[4] << 3 | (key[5] >> 5 & 0xfe); + kk[6] = key[5] << 2 | (key[6] >> 6 & 0xfe); + kk[7] = key[6] << 1; + ksp = malloc(sizeof(des_key_schedule), M_SMBTEMP, M_WAITOK); + des_set_key((C_Block*)kk, *ksp); + des_ecb_encrypt((C_Block*)data, (C_Block*)dest, *ksp, 1); + free(ksp, M_SMBTEMP); +} +#endif + + +int +smb_encrypt(const u_char *apwd, u_char *C8, u_char *RN) +{ +#ifdef NETSMBCRYPTO + u_char *p, *P14, *S21; + + p = malloc(14 + 21, M_SMBTEMP, M_WAITOK); + bzero(p, 14 + 21); + P14 = p; + S21 = p + 14; + bcopy(apwd, P14, min(14, strlen(apwd))); + /* + * S21 = concat(Ex(P14, N8), zeros(5)); + */ + smb_E(P14, N8, S21); + smb_E(P14 + 7, N8, S21 + 8); + + smb_E(S21, C8, RN); + smb_E(S21 + 7, C8, RN + 8); + smb_E(S21 + 14, C8, RN + 16); + free(p, M_SMBTEMP); + return 0; +#else + SMBERROR("password encryption is not available\n"); + bzero(RN, 24); + return EAUTH; +#endif +} + +int +smb_ntencrypt(const u_char *apwd, u_char *C8, u_char *RN) +{ +#ifdef NETSMBCRYPTO + u_char S21[21]; + u_int16_t *unipwd; + MD4_CTX *ctxp; + int len; + + len = strlen(apwd); + unipwd = malloc(len * sizeof(u_int16_t), M_SMBTEMP, M_WAITOK); + /* + * S21 = concat(MD4(U(apwd)), zeros(5)); + */ + smb_strtouni(unipwd, apwd); + ctxp = malloc(sizeof(MD4_CTX), M_SMBTEMP, M_WAITOK); + MD4Init(ctxp); + MD4Update(ctxp, (u_char*)unipwd, len * sizeof(u_int16_t)); + free(unipwd, M_SMBTEMP); + bzero(S21, 21); + MD4Final(S21, ctxp); + free(ctxp, M_SMBTEMP); + + smb_E(S21, C8, RN); + smb_E(S21 + 7, C8, RN + 8); + smb_E(S21 + 14, C8, RN + 16); + return 0; +#else + SMBERROR("password encryption is not available\n"); + bzero(RN, 24); + return EAUTH; +#endif +} + diff --git a/sys/netsmb/smb_dev.c b/sys/netsmb/smb_dev.c new file mode 100644 index 000000000000..fe00717d2c81 --- /dev/null +++ b/sys/netsmb/smb_dev.c @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2000-2001 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define SMB_GETDEV(dev) ((struct smb_dev*)(dev)->si_drv1) +#define SMB_CHECKMINOR(dev) do { \ + sdp = SMB_GETDEV(dev); \ + if (sdp == NULL) return ENXIO; \ + } while(0) + +static d_open_t nsmb_dev_open; +static d_close_t nsmb_dev_close; +static d_read_t nsmb_dev_read; +static d_write_t nsmb_dev_write; +static d_ioctl_t nsmb_dev_ioctl; +static d_poll_t nsmb_dev_poll; + +MODULE_DEPEND(netsmb, libiconv, 1, 1, 1); +MODULE_VERSION(netsmb, NSMB_VERSION); + +static int smb_version = NSMB_VERSION; + + +SYSCTL_DECL(_net_smb); +SYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, ""); + +static MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device"); + + +/* +int smb_dev_queue(struct smb_dev *ndp, struct smb_rq *rqp, int prio); +*/ + +static struct cdevsw nsmb_cdevsw = { + /* open */ nsmb_dev_open, + /* close */ nsmb_dev_close, + /* read */ nsmb_dev_read, + /* write */ nsmb_dev_write, + /* ioctl */ nsmb_dev_ioctl, + /* poll */ nsmb_dev_poll, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ NSMB_NAME, + /* maj */ NSMB_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, +#ifndef FB_CURRENT + /* bmaj */ -1 +#endif +}; + +static eventhandler_tag nsmb_dev_tag; + +static void +nsmb_dev_clone(void *arg, char *name, int namelen, dev_t *dev) +{ + int min; + + if (*dev != NODEV) + return; + if (dev_stdclone(name, NULL, NSMB_NAME, &min) != 1) + return; + *dev = make_dev(&nsmb_cdevsw, min, 0, 0, 0600, NSMB_NAME"%d", min); +} + +static int +nsmb_dev_open(dev_t dev, int oflags, int devtype, struct proc *p) +{ + struct smb_dev *sdp; + struct ucred *cred = p->p_ucred; + int s; + + sdp = SMB_GETDEV(dev); + if (sdp && (sdp->sd_flags & NSMBFL_OPEN)) + return EBUSY; + if (sdp == NULL) { + sdp = malloc(sizeof(*sdp), M_NSMBDEV, M_WAITOK); + dev->si_drv1 = (void*)sdp; + } + /* + * XXX: this is just crazy - make a device for an already passed device... + * someone should take care of it. + */ + if ((dev->si_flags & SI_NAMED) == 0) + make_dev(&nsmb_cdevsw, minor(dev), cred->cr_uid, cred->cr_gid, 0700, + NSMB_NAME"%d", dev2unit(dev)); + bzero(sdp, sizeof(*sdp)); +/* + STAILQ_INIT(&sdp->sd_rqlist); + STAILQ_INIT(&sdp->sd_rplist); + bzero(&sdp->sd_pollinfo, sizeof(struct selinfo)); +*/ + s = splimp(); + sdp->sd_level = -1; + sdp->sd_flags |= NSMBFL_OPEN; + splx(s); + return 0; +} + +static int +nsmb_dev_close(dev_t dev, int flag, int fmt, struct proc *p) +{ + struct smb_dev *sdp; + struct smb_vc *vcp; + struct smb_share *ssp; + struct smb_cred scred; + int s; + + SMB_CHECKMINOR(dev); + s = splimp(); + if ((sdp->sd_flags & NSMBFL_OPEN) == 0) { + splx(s); + return EBADF; + } + smb_makescred(&scred, p, NULL); + ssp = sdp->sd_share; + if (ssp != NULL) + smb_share_rele(ssp, &scred); + vcp = sdp->sd_vc; + if (vcp != NULL) + smb_vc_rele(vcp, &scred); +/* + smb_flushq(&sdp->sd_rqlist); + smb_flushq(&sdp->sd_rplist); +*/ +#if __FreeBSD_version > 400001 + dev->si_drv1 = NULL; + free(sdp, M_NSMBDEV); + destroy_dev(dev); +#else + sdp->sd_flags &= ~NSMBFL_OPEN; +#endif + splx(s); + return 0; +} + + +static int +nsmb_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct smb_dev *sdp; + struct smb_vc *vcp; + struct smb_share *ssp; + struct smb_cred scred; + int error = 0; + + SMB_CHECKMINOR(dev); + if ((sdp->sd_flags & NSMBFL_OPEN) == 0) + return EBADF; + + smb_makescred(&scred, p, NULL); + switch (cmd) { + case SMBIOC_OPENSESSION: + if (sdp->sd_vc) + return EISCONN; + error = smb_usr_opensession((struct smbioc_ossn*)data, + &scred, &vcp); + if (error) + break; + sdp->sd_vc = vcp; + smb_vc_unlock(vcp, 0, p); + sdp->sd_level = SMBL_VC; + break; + case SMBIOC_OPENSHARE: + if (sdp->sd_share) + return EISCONN; + if (sdp->sd_vc == NULL) + return ENOTCONN; + error = smb_usr_openshare(sdp->sd_vc, + (struct smbioc_oshare*)data, &scred, &ssp); + if (error) + break; + sdp->sd_share = ssp; + smb_share_unlock(ssp, 0, p); + sdp->sd_level = SMBL_SHARE; + break; + case SMBIOC_REQUEST: + if (sdp->sd_share == NULL) + return ENOTCONN; + error = smb_usr_simplerequest(sdp->sd_share, + (struct smbioc_rq*)data, &scred); + break; + case SMBIOC_T2RQ: + if (sdp->sd_share == NULL) + return ENOTCONN; + error = smb_usr_t2request(sdp->sd_share, + (struct smbioc_t2rq*)data, &scred); + break; + case SMBIOC_SETFLAGS: { + struct smbioc_flags *fl = (struct smbioc_flags*)data; + int on; + + if (fl->ioc_level == SMBL_VC) { + if (fl->ioc_mask & SMBV_PERMANENT) { + on = fl->ioc_flags & SMBV_PERMANENT; + if ((vcp = sdp->sd_vc) == NULL) + return ENOTCONN; + error = smb_vc_get(vcp, LK_EXCLUSIVE, &scred); + if (error) + break; + if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) { + vcp->obj.co_flags |= SMBV_PERMANENT; + smb_vc_ref(vcp, p); + } else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) { + vcp->obj.co_flags &= ~SMBV_PERMANENT; + smb_vc_rele(vcp, &scred); + } + smb_vc_put(vcp, &scred); + } else + error = EINVAL; + } else if (fl->ioc_level == SMBL_SHARE) { + if (fl->ioc_mask & SMBS_PERMANENT) { + on = fl->ioc_flags & SMBS_PERMANENT; + if ((ssp = sdp->sd_share) == NULL) + return ENOTCONN; + error = smb_share_get(ssp, LK_EXCLUSIVE, &scred); + if (error) + break; + if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) { + ssp->obj.co_flags |= SMBS_PERMANENT; + smb_share_ref(ssp, p); + } else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) { + ssp->obj.co_flags &= ~SMBS_PERMANENT; + smb_share_rele(ssp, &scred); + } + smb_share_put(ssp, &scred); + } else + error = EINVAL; + break; + } else + error = EINVAL; + break; + } + case SMBIOC_LOOKUP: + if (sdp->sd_vc || sdp->sd_share) + return EISCONN; + vcp = NULL; + ssp = NULL; + error = smb_usr_lookup((struct smbioc_lookup*)data, &scred, &vcp, &ssp); + if (error) + break; + if (vcp) { + sdp->sd_vc = vcp; + smb_vc_unlock(vcp, 0, p); + sdp->sd_level = SMBL_VC; + } + if (ssp) { + sdp->sd_share = ssp; + smb_share_unlock(ssp, 0, p); + sdp->sd_level = SMBL_SHARE; + } + break; + case SMBIOC_READ: case SMBIOC_WRITE: { + struct smbioc_rw *rwrq = (struct smbioc_rw*)data; + struct uio auio; + struct iovec iov; + + if ((ssp = sdp->sd_share) == NULL) + return ENOTCONN; + iov.iov_base = rwrq->ioc_base; + iov.iov_len = rwrq->ioc_cnt; + auio.uio_iov = &iov; + auio.uio_iovcnt = 1; + auio.uio_offset = rwrq->ioc_offset; + auio.uio_resid = rwrq->ioc_cnt; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE; + auio.uio_procp = p; + if (cmd == SMBIOC_READ) + error = smb_read(ssp, rwrq->ioc_fh, &auio, &scred); + else + error = smb_write(ssp, rwrq->ioc_fh, &auio, &scred); + rwrq->ioc_cnt -= auio.uio_resid; + break; + } + default: + error = ENODEV; + } + return error; +} + +static int +nsmb_dev_read(dev_t dev, struct uio *uio, int flag) +{ + return EACCES; +} + +static int +nsmb_dev_write(dev_t dev, struct uio *uio, int flag) +{ + return EACCES; +} + +static int +nsmb_dev_poll(dev_t dev, int events, struct proc *p) +{ + return ENODEV; +} + +static int +nsmb_dev_load(module_t mod, int cmd, void *arg) +{ + int error = 0; + + switch (cmd) { + case MOD_LOAD: + error = smb_sm_init(); + if (error) + break; + error = smb_iod_init(); + if (error) { + smb_sm_done(); + break; + } +#if __FreeBSD_version > 400001 + cdevsw_add(&nsmb_cdevsw); +#endif +#if __FreeBSD_version > 500000 + nsmb_dev_tag = EVENTHANDLER_REGISTER(dev_clone, nsmb_dev_clone, 0, 1000); +#endif + printf("netsmb_dev: loaded\n"); + break; + case MOD_UNLOAD: + smb_iod_done(); + error = smb_sm_done(); + error = 0; +#if __FreeBSD_version > 500000 + EVENTHANDLER_DEREGISTER(dev_clone, nsmb_dev_tag); +#endif +#if __FreeBSD_version > 400001 + cdevsw_remove(&nsmb_cdevsw); +#endif + printf("netsmb_dev: unloaded\n"); + break; + default: + error = EINVAL; + break; + } + return error; +} + +#if __FreeBSD_version > 400000 +DEV_MODULE (dev_netsmb, nsmb_dev_load, 0); +#else +CDEV_MODULE(dev_netsmb, NSMB_MAJOR, nsmb_cdevsw, nsmb_dev_load, 0); +#endif + + +/* + * Convert a file descriptor to appropriate smb_share pointer + */ +static struct file* +nsmb_getfp(struct filedesc* fdp, int fd, int flag) +{ + struct file* fp; + + if (((u_int)fd) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[fd]) == NULL || + (fp->f_flag & flag) == 0) + return (NULL); + return (fp); +} + +int +smb_dev2share(int fd, int mode, struct smb_cred *scred, + struct smb_share **sspp) +{ + struct file *fp; + struct vnode *vp; + struct smb_dev *sdp; + struct smb_share *ssp; + dev_t dev; + int error; + + if ((fp = nsmb_getfp(scred->scr_p->p_fd, fd, FREAD | FWRITE)) == NULL) + return EBADF; + vp = (struct vnode*)fp->f_data; + if (vp == NULL) + return EBADF; + dev = vn_todev(vp); + if (dev == NODEV) + return EBADF; + SMB_CHECKMINOR(dev); + ssp = sdp->sd_share; + if (ssp == NULL) + return ENOTCONN; + error = smb_share_get(ssp, LK_EXCLUSIVE, scred); + if (error) + return error; + *sspp = ssp; + return 0; +} + diff --git a/sys/netsmb/smb_dev.h b/sys/netsmb/smb_dev.h new file mode 100644 index 000000000000..52ab4369a94c --- /dev/null +++ b/sys/netsmb/smb_dev.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2000-2001 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 _NETSMB_DEV_H_ +#define _NETSMB_DEV_H_ + +#define NSMB_NAME "nsmb" +#define NSMB_MAJOR 144 + +#define NSMB_VERMAJ 1 +#define NSMB_VERMIN 3006 +#define NSMB_VERSION (NSMB_VERMAJ * 100000 + NSMB_VERMIN) + +#define NSMBFL_OPEN 0x0001 + +#define SMBVOPT_CREATE 0x0001 /* create object if necessary */ +#define SMBVOPT_PRIVATE 0x0002 /* connection should be private */ +#define SMBVOPT_SINGLESHARE 0x0004 /* keep only ane share at this VC */ +#define SMBVOPT_PERMANENT 0x0010 /* object will keep last reference */ + +#define SMBSOPT_CREATE 0x0001 /* create object if necessary */ +#define SMBSOPT_PERMANENT 0x0010 /* object will keep last reference */ + +/* + * SMBIOC_LOOKUP flags + */ +#define SMBLK_CREATE 0x0001 + +struct smbioc_ossn { + int ioc_opt; + int ioc_svlen; /* size of ioc_server address */ + struct sockaddr*ioc_server; + int ioc_lolen; /* size of ioc_local address */ + struct sockaddr*ioc_local; + char ioc_srvname[SMB_MAXSRVNAMELEN + 1]; + int ioc_timeout; + int ioc_retrycount; /* number of retries before giveup */ + char ioc_localcs[16];/* local charset */ + char ioc_servercs[16];/* server charset */ + char ioc_user[SMB_MAXUSERNAMELEN + 1]; + char ioc_workgroup[SMB_MAXUSERNAMELEN + 1]; + char ioc_password[SMB_MAXPASSWORDLEN + 1]; + uid_t ioc_owner; /* proposed owner */ + gid_t ioc_group; /* proposed group */ + mode_t ioc_mode; /* desired access mode */ + mode_t ioc_rights; /* SMBM_* */ +}; + +struct smbioc_oshare { + int ioc_opt; + int ioc_stype; /* share type */ + char ioc_share[SMB_MAXSHARENAMELEN + 1]; + char ioc_password[SMB_MAXPASSWORDLEN + 1]; + uid_t ioc_owner; /* proposed owner of share */ + gid_t ioc_group; /* proposed group of share */ + mode_t ioc_mode; /* desired access mode to share */ + mode_t ioc_rights; /* SMBM_* */ +}; + +struct smbioc_rq { + u_char ioc_cmd; + u_char ioc_twc; + void * ioc_twords; + u_short ioc_tbc; + void * ioc_tbytes; + int ioc_rpbufsz; + char * ioc_rpbuf; + u_char ioc_rwc; + u_short ioc_rbc; + u_int8_t ioc_errclass; + u_int16_t ioc_serror; + u_int32_t ioc_error; +}; + +struct smbioc_t2rq { + u_int16_t ioc_setup[3]; + int ioc_setupcnt; + char * ioc_name; + u_short ioc_tparamcnt; + void * ioc_tparam; + u_short ioc_tdatacnt; + void * ioc_tdata; + u_short ioc_rparamcnt; + void * ioc_rparam; + u_short ioc_rdatacnt; + void * ioc_rdata; +}; + +struct smbioc_flags { + int ioc_level; /* 0 - session, 1 - share */ + int ioc_mask; + int ioc_flags; +}; + +struct smbioc_lookup { + int ioc_level; + int ioc_flags; + struct smbioc_ossn ioc_ssn; + struct smbioc_oshare ioc_sh; +}; + +struct smbioc_rw { + smbfh ioc_fh; + char * ioc_base; + off_t ioc_offset; + int ioc_cnt; +}; + +/* + * Device IOCTLs + */ +#define SMBIOC_OPENSESSION _IOW('n', 100, struct smbioc_ossn) +#define SMBIOC_OPENSHARE _IOW('n', 101, struct smbioc_oshare) +#define SMBIOC_REQUEST _IOWR('n', 102, struct smbioc_rq) +#define SMBIOC_T2RQ _IOWR('n', 103, struct smbioc_t2rq) +#define SMBIOC_SETFLAGS _IOW('n', 104, struct smbioc_flags) +#define SMBIOC_LOOKUP _IOW('n', 106, struct smbioc_lookup) +#define SMBIOC_READ _IOWR('n', 107, struct smbioc_rw) +#define SMBIOC_WRITE _IOWR('n', 108, struct smbioc_rw) + +#ifdef _KERNEL + +#ifndef LK_SHARED +#include +#endif + +#define IF_QEMPTY(ifq) ((ifq)->ifq_len == 0) + +#define SMBST_CONNECTED 1 + +STAILQ_HEAD(smbrqh, smb_rq); + +struct smb_dev { + int sd_opened; + int sd_level; + struct smb_vc * sd_vc; /* reference to VC */ + struct smb_share *sd_share; /* reference to share if any */ + int sd_poll; + int sd_seq; +/* struct ifqueue sd_rdqueue; + struct ifqueue sd_wrqueue; + struct selinfo sd_pollinfo; + struct smbrqh sd_rqlist; + struct smbrqh sd_rplist; + struct ucred *sd_owner;*/ + int sd_flags; +}; + +struct smb_cred; +/* + * Compound user interface + */ +int smb_usr_lookup(struct smbioc_lookup *dp, struct smb_cred *scred, + struct smb_vc **vcpp, struct smb_share **sspp); +int smb_usr_opensession(struct smbioc_ossn *data, + struct smb_cred *scred, struct smb_vc **vcpp); +int smb_usr_openshare(struct smb_vc *vcp, struct smbioc_oshare *data, + struct smb_cred *scred, struct smb_share **sspp); +int smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *data, + struct smb_cred *scred); +int smb_usr_t2request(struct smb_share *ssp, struct smbioc_t2rq *data, + struct smb_cred *scred); +int smb_dev2share(int fd, int mode, struct smb_cred *scred, + struct smb_share **sspp); + + +#endif /* _KERNEL */ + +#endif /* _NETSMB_DEV_H_ */ diff --git a/sys/netsmb/smb_iod.c b/sys/netsmb/smb_iod.c new file mode 100644 index 000000000000..805330389d08 --- /dev/null +++ b/sys/netsmb/smb_iod.c @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2000-2001 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#define SMBIOD_SLEEP_TIMO 2 +#define SMBIOD_PING_TIMO 60 /* seconds */ + +#define SMB_IOD_EVLOCKPTR(iod) (&((iod)->iod_evlock)) +#define SMB_IOD_EVLOCK(iod) smb_sl_lock(&((iod)->iod_evlock)) +#define SMB_IOD_EVUNLOCK(iod) smb_sl_unlock(&((iod)->iod_evlock)) + +#define SMB_IOD_RQLOCKPTR(iod) (&((iod)->iod_rqlock)) +#define SMB_IOD_RQLOCK(iod) smb_sl_lock(&((iod)->iod_rqlock)) +#define SMB_IOD_RQUNLOCK(iod) smb_sl_unlock(&((iod)->iod_rqlock)) + +#define smb_iod_wakeup(iod) wakeup(&(iod)->iod_flags) + + +static MALLOC_DEFINE(M_SMBIOD, "SMBIOD", "SMB network io daemon"); + +static int smb_iod_next; + +static int smb_iod_sendall(struct smbiod *iod); +static int smb_iod_disconnect(struct smbiod *iod); +static void smb_iod_thread(void *); + +static __inline void +smb_iod_rqprocessed(struct smb_rq *rqp, int error) +{ + SMBRQ_SLOCK(rqp); + rqp->sr_lerror = error; + rqp->sr_rpgen++; + rqp->sr_state = SMBRQ_NOTIFIED; + wakeup(&rqp->sr_state); + SMBRQ_SUNLOCK(rqp); +} + +static void +smb_iod_invrq(struct smbiod *iod) +{ + struct smb_rq *rqp; + + /* + * Invalidate all outstanding requests for this connection + */ + SMB_IOD_RQLOCK(iod); + TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { + if (rqp->sr_flags & SMBR_INTERNAL) + SMBRQ_SUNLOCK(rqp); + rqp->sr_flags |= SMBR_RESTART; + smb_iod_rqprocessed(rqp, ENOTCONN); + } + SMB_IOD_RQUNLOCK(iod); +} + +static void +smb_iod_closetran(struct smbiod *iod) +{ + struct smb_vc *vcp = iod->iod_vc; + struct proc *p = iod->iod_p; + + if (vcp->vc_tdata == NULL) + return; + SMB_TRAN_DISCONNECT(vcp, p); + SMB_TRAN_DONE(vcp, p); + vcp->vc_tdata = NULL; +} + +static void +smb_iod_dead(struct smbiod *iod) +{ + iod->iod_state = SMBIOD_ST_DEAD; + smb_iod_closetran(iod); + smb_iod_invrq(iod); +} + +static int +smb_iod_connect(struct smbiod *iod) +{ + struct smb_vc *vcp = iod->iod_vc; + struct proc *p = iod->iod_p; + int error; + + SMBIODEBUG("%d\n", iod->iod_state); + switch(iod->iod_state) { + case SMBIOD_ST_VCACTIVE: + SMBERROR("called for already opened connection\n"); + return EISCONN; + case SMBIOD_ST_DEAD: + return ENOTCONN; /* XXX: last error code ? */ + default: + break; + } + vcp->vc_genid++; + error = 0; + itry { + ithrow(SMB_TRAN_CREATE(vcp, p)); + SMBIODEBUG("tcreate\n"); + if (vcp->vc_laddr) { + ithrow(SMB_TRAN_BIND(vcp, vcp->vc_laddr, p)); + } + SMBIODEBUG("tbind\n"); + ithrow(SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, p)); + SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, &iod->iod_flags); + iod->iod_state = SMBIOD_ST_TRANACTIVE; + SMBIODEBUG("tconnect\n"); +/* vcp->vc_mid = 0;*/ + ithrow(smb_smb_negotiate(vcp, &iod->iod_scred)); + SMBIODEBUG("snegotiate\n"); + ithrow(smb_smb_ssnsetup(vcp, &iod->iod_scred)); + iod->iod_state = SMBIOD_ST_VCACTIVE; + SMBIODEBUG("completed\n"); + smb_iod_invrq(iod); + } icatch(error) { + smb_iod_dead(iod); + } ifinally { + } iendtry; + return error; +} + +static int +smb_iod_disconnect(struct smbiod *iod) +{ + struct smb_vc *vcp = iod->iod_vc; + + SMBIODEBUG("\n"); + if (iod->iod_state == SMBIOD_ST_VCACTIVE) { + smb_smb_ssnclose(vcp, &iod->iod_scred); + iod->iod_state = SMBIOD_ST_TRANACTIVE; + } + vcp->vc_smbuid = SMB_UID_UNKNOWN; + smb_iod_closetran(iod); + iod->iod_state = SMBIOD_ST_NOTCONN; + return 0; +} + +static int +smb_iod_treeconnect(struct smbiod *iod, struct smb_share *ssp) +{ + int error; + + if (iod->iod_state != SMBIOD_ST_VCACTIVE) { + if (iod->iod_state != SMBIOD_ST_DEAD) + return ENOTCONN; + iod->iod_state = SMBIOD_ST_RECONNECT; + error = smb_iod_connect(iod); + if (error) + return error; + } + SMBIODEBUG("tree reconnect\n"); + SMBS_ST_LOCK(ssp); + ssp->ss_flags |= SMBS_RECONNECTING; + SMBS_ST_UNLOCK(ssp); + error = smb_smb_treeconnect(ssp, &iod->iod_scred); + SMBS_ST_LOCK(ssp); + ssp->ss_flags &= ~SMBS_RECONNECTING; + SMBS_ST_UNLOCK(ssp); + wakeup(&ssp->ss_vcgenid); + return error; +} + +static int +smb_iod_sendrq(struct smbiod *iod, struct smb_rq *rqp) +{ + struct proc *p = iod->iod_p; + struct smb_vc *vcp = iod->iod_vc; + struct smb_share *ssp = rqp->sr_share; + struct mbuf *m; + int error; + + SMBIODEBUG("iod_state = %d\n", iod->iod_state); + switch (iod->iod_state) { + case SMBIOD_ST_NOTCONN: + smb_iod_rqprocessed(rqp, ENOTCONN); + return 0; + case SMBIOD_ST_DEAD: + iod->iod_state = SMBIOD_ST_RECONNECT; + return 0; + case SMBIOD_ST_RECONNECT: + return 0; + default: + break; + } + if (rqp->sr_sendcnt == 0) { +#ifdef movedtoanotherplace + if (vcp->vc_maxmux != 0 && iod->iod_muxcnt >= vcp->vc_maxmux) + return 0; +#endif + *rqp->sr_rqtid = htoles(ssp ? ssp->ss_tid : SMB_TID_UNKNOWN); + *rqp->sr_rquid = htoles(vcp ? vcp->vc_smbuid : 0); + mb_fixhdr(&rqp->sr_rq); + } + if (rqp->sr_sendcnt++ > 5) { + rqp->sr_flags |= SMBR_RESTART; + smb_iod_rqprocessed(rqp, rqp->sr_lerror); + /* + * If all attempts to send a request failed, then + * something is seriously hosed. + */ + return ENOTCONN; + } + SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0); + m_dumpm(rqp->sr_rq.mb_top); + m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, M_WAIT); + error = rqp->sr_lerror = m ? SMB_TRAN_SEND(vcp, m, p) : ENOBUFS; + if (error == 0) { + getnanotime(&rqp->sr_timesent); + iod->iod_lastrqsent = rqp->sr_timesent; + rqp->sr_flags |= SMBR_SENT; + rqp->sr_state = SMBRQ_SENT; + return 0; + } + /* + * Check for fatal errors + */ + if (SMB_TRAN_FATAL(vcp, error)) { + /* + * No further attempts should be made + */ + return ENOTCONN; + } + if (smb_rq_intr(rqp)) + smb_iod_rqprocessed(rqp, EINTR); + return 0; +} + +/* + * Process incoming packets + */ +static int +smb_iod_recvall(struct smbiod *iod) +{ + struct smb_vc *vcp = iod->iod_vc; + struct proc *p = iod->iod_p; + struct smb_rq *rqp; + struct mbuf *m; + u_char *hp; + u_short mid; + int error; + + switch (iod->iod_state) { + case SMBIOD_ST_NOTCONN: + case SMBIOD_ST_DEAD: + case SMBIOD_ST_RECONNECT: + return 0; + default: + break; + } + for (;;) { + m = NULL; + error = SMB_TRAN_RECV(vcp, &m, p); + if (error == EWOULDBLOCK) + break; + if (SMB_TRAN_FATAL(vcp, error)) { + smb_iod_dead(iod); + break; + } + if (error) + break; + if (m == NULL) { + SMBERROR("tran return NULL without error\n"); + error = EPIPE; + continue; + } + m = m_pullup(m, SMB_HDRLEN); + if (m == NULL) + continue; /* wait for a good packet */ + /* + * Now we got an entire and possibly invalid SMB packet. + * Be careful while parsing it. + */ + m_dumpm(m); + hp = mtod(m, u_char*); + if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) { + m_freem(m); + continue; + } + mid = SMB_HDRMID(hp); + SMBSDEBUG("mid %04x\n", (u_int)mid); + SMB_IOD_RQLOCK(iod); + TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { + if (rqp->sr_mid != mid) + continue; + SMBRQ_SLOCK(rqp); + if (rqp->sr_rp.md_top == NULL) { + md_initm(&rqp->sr_rp, m); + } else { + if (rqp->sr_flags & SMBR_MULTIPACKET) { + md_append_record(&rqp->sr_rp, m); + } else { + SMBRQ_SUNLOCK(rqp); + SMBERROR("duplicate response %d (ignored)\n", mid); + break; + } + } + SMBRQ_SUNLOCK(rqp); + smb_iod_rqprocessed(rqp, 0); + break; + } + SMB_IOD_RQUNLOCK(iod); + if (rqp == NULL) { + SMBERROR("drop resp with mid %d\n", (u_int)mid); +/* smb_printrqlist(vcp);*/ + m_freem(m); + } + } + /* + * check for interrupts + */ + SMB_IOD_RQLOCK(iod); + TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { + if (smb_proc_intr(rqp->sr_cred->scr_p)) { + smb_iod_rqprocessed(rqp, EINTR); + } + } + SMB_IOD_RQUNLOCK(iod); + return 0; +} + +int +smb_iod_request(struct smbiod *iod, int event, void *ident) +{ + struct smbiod_event *evp; + int error; + + SMBIODEBUG("\n"); + evp = smb_zmalloc(sizeof(*evp), M_SMBIOD, M_WAITOK); + evp->ev_type = event; + evp->ev_ident = ident; + SMB_IOD_EVLOCK(iod); + STAILQ_INSERT_TAIL(&iod->iod_evlist, evp, ev_link); + if ((event & SMBIOD_EV_SYNC) == 0) { + SMB_IOD_EVUNLOCK(iod); + smb_iod_wakeup(iod); + return 0; + } + smb_iod_wakeup(iod); + msleep(evp, SMB_IOD_EVLOCKPTR(iod), PWAIT | PDROP, "90evw", 0); + error = evp->ev_error; + free(evp, M_SMBIOD); + return error; +} + +/* + * Place request in the queue. + * Request from smbiod have a high priority. + */ +int +smb_iod_addrq(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct smbiod *iod = vcp->vc_iod; + int error; + + SMBIODEBUG("\n"); + if (rqp->sr_cred->scr_p == iod->iod_p) { + rqp->sr_flags |= SMBR_INTERNAL; + SMB_IOD_RQLOCK(iod); + TAILQ_INSERT_HEAD(&iod->iod_rqlist, rqp, sr_link); + SMB_IOD_RQUNLOCK(iod); + for (;;) { + if (smb_iod_sendrq(iod, rqp) != 0) { + smb_iod_dead(iod); + break; + } + /* + * we don't need to lock state field here + */ + if (rqp->sr_state != SMBRQ_NOTSENT) + break; + tsleep(&iod->iod_flags, PWAIT, "90sndw", hz); + } + if (rqp->sr_lerror) + smb_iod_removerq(rqp); + return rqp->sr_lerror; + } + + switch (iod->iod_state) { + case SMBIOD_ST_NOTCONN: + return ENOTCONN; + case SMBIOD_ST_DEAD: + error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL); + if (error) + return error; + return EXDEV; + default: + break; + } + + SMB_IOD_RQLOCK(iod); + for (;;) { + if (vcp->vc_maxmux == 0) { + SMBERROR("maxmux == 0\n"); + break; + } + if (iod->iod_muxcnt < vcp->vc_maxmux) + break; + iod->iod_muxwant++; + msleep(&iod->iod_muxwant, SMB_IOD_RQLOCKPTR(iod), + PWAIT, "90mux", 0); + } + iod->iod_muxcnt++; + TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link); + SMB_IOD_RQUNLOCK(iod); + smb_iod_wakeup(iod); + return 0; +} + +int +smb_iod_removerq(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct smbiod *iod = vcp->vc_iod; + + SMBIODEBUG("\n"); + if (rqp->sr_flags & SMBR_INTERNAL) { + SMB_IOD_RQLOCK(iod); + TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link); + SMB_IOD_RQUNLOCK(iod); + return 0; + } + SMB_IOD_RQLOCK(iod); + while (rqp->sr_flags & SMBR_XLOCK) { + rqp->sr_flags |= SMBR_XLOCKWANT; + msleep(rqp, SMB_IOD_RQLOCKPTR(iod), PWAIT, "90xrm", 0); + } + TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link); + iod->iod_muxcnt--; + if (iod->iod_muxwant) { + iod->iod_muxwant--; + wakeup(&iod->iod_muxwant); + } + SMB_IOD_RQUNLOCK(iod); + return 0; +} + +int +smb_iod_waitrq(struct smb_rq *rqp) +{ + struct smbiod *iod = rqp->sr_vc->vc_iod; + int error; + + SMBIODEBUG("\n"); + if (rqp->sr_flags & SMBR_INTERNAL) { + for (;;) { + smb_iod_sendall(iod); + smb_iod_recvall(iod); + if (rqp->sr_rpgen != rqp->sr_rplast) + break; + tsleep(&iod->iod_flags, PWAIT, "90irq", hz); + } + smb_iod_removerq(rqp); + return rqp->sr_lerror; + + } + SMBRQ_SLOCK(rqp); + if (rqp->sr_rpgen == rqp->sr_rplast) + msleep(&rqp->sr_state, SMBRQ_SLOCKPTR(rqp), PWAIT, "90wrq", 0); + rqp->sr_rplast++; + SMBRQ_SUNLOCK(rqp); + error = rqp->sr_lerror; + if (rqp->sr_flags & SMBR_MULTIPACKET) { + /* + * If request should stay in the list, then reinsert it + * at the end of queue so other waiters have chance to concur + */ + SMB_IOD_RQLOCK(iod); + TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link); + TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link); + SMB_IOD_RQUNLOCK(iod); + } else + smb_iod_removerq(rqp); + return error; +} + + +static int +smb_iod_sendall(struct smbiod *iod) +{ + struct smb_vc *vcp = iod->iod_vc; + struct smb_rq *rqp; + struct timespec ts, tstimeout; + int herror; + + herror = 0; + /* + * Loop through the list of requests and send them if possible + */ + SMB_IOD_RQLOCK(iod); + TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { + switch (rqp->sr_state) { + case SMBRQ_NOTSENT: + rqp->sr_flags |= SMBR_XLOCK; + SMB_IOD_RQUNLOCK(iod); + herror = smb_iod_sendrq(iod, rqp); + SMB_IOD_RQLOCK(iod); + rqp->sr_flags &= ~SMBR_XLOCK; + if (rqp->sr_flags & SMBR_XLOCKWANT) { + rqp->sr_flags &= ~SMBR_XLOCKWANT; + wakeup(rqp); + } + break; + case SMBRQ_SENT: + SMB_TRAN_GETPARAM(vcp, SMBTP_TIMEOUT, &tstimeout); + timespecadd(&tstimeout, &tstimeout); + getnanotime(&ts); + timespecsub(&ts, &tstimeout); + if (timespeccmp(&ts, &rqp->sr_timesent, >)) { + smb_iod_rqprocessed(rqp, ETIMEDOUT); + } + break; + default: + } + if (herror) + break; + } + SMB_IOD_RQUNLOCK(iod); + if (herror == ENOTCONN) + smb_iod_dead(iod); + return 0; +} + +/* + * "main" function for smbiod daemon + */ +static __inline void +smb_iod_main(struct smbiod *iod) +{ +/* struct smb_vc *vcp = iod->iod_vc;*/ + struct smbiod_event *evp; +/* struct timespec tsnow;*/ + int error; + + SMBIODEBUG("\n"); + error = 0; + + /* + * Check all interesting events + */ + for (;;) { + SMB_IOD_EVLOCK(iod); + evp = STAILQ_FIRST(&iod->iod_evlist); + if (evp == NULL) { + SMB_IOD_EVUNLOCK(iod); + break; + } + STAILQ_REMOVE_HEAD(&iod->iod_evlist, ev_link); + evp->ev_type |= SMBIOD_EV_PROCESSING; + SMB_IOD_EVUNLOCK(iod); + switch (evp->ev_type & SMBIOD_EV_MASK) { + case SMBIOD_EV_CONNECT: + iod->iod_state = SMBIOD_ST_RECONNECT; + evp->ev_error = smb_iod_connect(iod); + break; + case SMBIOD_EV_DISCONNECT: + evp->ev_error = smb_iod_disconnect(iod); + break; + case SMBIOD_EV_TREECONNECT: + evp->ev_error = smb_iod_treeconnect(iod, evp->ev_ident); + break; + case SMBIOD_EV_SHUTDOWN: + iod->iod_flags |= SMBIOD_SHUTDOWN; + break; + case SMBIOD_EV_NEWRQ: + break; + } + if (evp->ev_type & SMBIOD_EV_SYNC) { + SMB_IOD_EVLOCK(iod); + wakeup(evp); + SMB_IOD_EVUNLOCK(iod); + } else + free(evp, M_SMBIOD); + } +#if 0 + if (iod->iod_state == SMBIOD_ST_VCACTIVE) { + getnanotime(&tsnow); + timespecsub(&tsnow, &iod->iod_pingtimo); + if (timespeccmp(&tsnow, &iod->iod_lastrqsent, >)) { + smb_smb_echo(vcp, &iod->iod_scred); + } + } +#endif + smb_iod_sendall(iod); + smb_iod_recvall(iod); + return; +} + +#ifndef FB_CURRENT +#define kthread_create_compat kthread_create2 +#else +#define kthread_create_compat kthread_create +#endif + + +void +smb_iod_thread(void *arg) +{ + struct smbiod *iod = arg; + + mtx_lock(&Giant); + smb_makescred(&iod->iod_scred, iod->iod_p, NULL); + while ((iod->iod_flags & SMBIOD_SHUTDOWN) == 0) { + smb_iod_main(iod); + SMBIODEBUG("going to sleep for %d ticks\n", iod->iod_sleeptimo); +/* mtx_unlock(&Giant, MTX_DEF);*/ + if (iod->iod_flags & SMBIOD_SHUTDOWN) + break; + tsleep(&iod->iod_flags, PWAIT, "90idle", iod->iod_sleeptimo); + } +/* mtx_lock(&Giant, MTX_DEF);*/ + kthread_exit(0); +} + +int +smb_iod_create(struct smb_vc *vcp) +{ + struct smbiod *iod; + int error; + + iod = smb_zmalloc(sizeof(*iod), M_SMBIOD, M_WAITOK); + iod->iod_id = smb_iod_next++; + iod->iod_state = SMBIOD_ST_NOTCONN; + iod->iod_vc = vcp; + iod->iod_sleeptimo = hz * SMBIOD_SLEEP_TIMO; + iod->iod_pingtimo.tv_sec = SMBIOD_PING_TIMO; + getnanotime(&iod->iod_lastrqsent); + vcp->vc_iod = iod; + smb_sl_init(&iod->iod_rqlock, "90rql"); + TAILQ_INIT(&iod->iod_rqlist); + smb_sl_init(&iod->iod_evlock, "90evl"); + STAILQ_INIT(&iod->iod_evlist); + error = kthread_create_compat(smb_iod_thread, iod, &iod->iod_p, + RFNOWAIT, "smbiod%d", iod->iod_id); + if (error) { + SMBERROR("can't start smbiod: %d", error); + free(iod, M_SMBIOD); + return error; + } + return 0; +} + +int +smb_iod_destroy(struct smbiod *iod) +{ + smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL); + mtx_destroy(&iod->iod_rqlock); + mtx_destroy(&iod->iod_evlock); + free(iod, M_SMBIOD); + return 0; +} + +int +smb_iod_init(void) +{ + return 0; +} + +int +smb_iod_done(void) +{ + return 0; +} + diff --git a/sys/netsmb/smb_rq.c b/sys/netsmb/smb_rq.c new file mode 100644 index 000000000000..9b105bc2b164 --- /dev/null +++ b/sys/netsmb/smb_rq.c @@ -0,0 +1,752 @@ +/* + * Copyright (c) 2000-2001, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +MALLOC_DEFINE(M_SMBRQ, "SMBRQ", "SMB request"); + +MODULE_DEPEND(netsmb, libmchain, 1, 1, 1); + +static int smb_rq_reply(struct smb_rq *rqp); +static int smb_rq_enqueue(struct smb_rq *rqp); +static int smb_rq_getenv(struct smb_connobj *layer, + struct smb_vc **vcpp, struct smb_share **sspp); +static int smb_rq_new(struct smb_rq *rqp, u_char cmd); +static int smb_t2_reply(struct smb_t2rq *t2p); + +int +smb_rq_alloc(struct smb_connobj *layer, u_char cmd, struct smb_cred *scred, + struct smb_rq **rqpp) +{ + struct smb_rq *rqp; + int error; + + MALLOC(rqp, struct smb_rq *, sizeof(*rqp), M_SMBRQ, M_WAITOK); + if (rqp == NULL) + return ENOMEM; + error = smb_rq_init(rqp, layer, cmd, scred); + rqp->sr_flags |= SMBR_ALLOCED; + if (error) { + smb_rq_done(rqp); + return error; + } + *rqpp = rqp; + return 0; +} + +static char tzero[12]; + +int +smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, u_char cmd, + struct smb_cred *scred) +{ + int error; + + bzero(rqp, sizeof(*rqp)); + smb_sl_init(&rqp->sr_slock, "srslock"); + error = smb_rq_getenv(layer, &rqp->sr_vc, &rqp->sr_share); + if (error) + return error; + error = smb_vc_access(rqp->sr_vc, scred, SMBM_EXEC); + if (error) + return error; + if (rqp->sr_share) { + error = smb_share_access(rqp->sr_share, scred, SMBM_EXEC); + if (error) + return error; + } + rqp->sr_cred = scred; + rqp->sr_mid = smb_vc_nextmid(rqp->sr_vc); + return smb_rq_new(rqp, cmd); +} + +static int +smb_rq_new(struct smb_rq *rqp, u_char cmd) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct mbchain *mbp = &rqp->sr_rq; + int error; + + rqp->sr_sendcnt = 0; + mb_done(mbp); + md_done(&rqp->sr_rp); + error = mb_init(mbp); + if (error) + return error; + mb_put_mem(mbp, SMB_SIGNATURE, SMB_SIGLEN, MB_MSYSTEM); + mb_put_uint8(mbp, cmd); + mb_put_uint32le(mbp, 0); /* DosError */ + mb_put_uint8(mbp, vcp->vc_hflags); + mb_put_uint16le(mbp, vcp->vc_hflags2); + mb_put_mem(mbp, tzero, 12, MB_MSYSTEM); + rqp->sr_rqtid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t)); + mb_put_uint16le(mbp, 1 /*scred->sc_p->p_pid & 0xffff*/); + rqp->sr_rquid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t)); + mb_put_uint16le(mbp, rqp->sr_mid); + return 0; +} + +void +smb_rq_done(struct smb_rq *rqp) +{ + mb_done(&rqp->sr_rq); + md_done(&rqp->sr_rp); + smb_sl_destroy(&rqp->sr_slock); + if (rqp->sr_flags & SMBR_ALLOCED) + free(rqp, M_SMBRQ); +} + +/* + * Simple request-reply exchange + */ +int +smb_rq_simple(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + int error = EINVAL, i; + + for (i = 0; i < SMB_MAXRCN; i++) { + rqp->sr_flags &= ~SMBR_RESTART; + rqp->sr_timo = vcp->vc_timo; + rqp->sr_state = SMBRQ_NOTSENT; + error = smb_rq_enqueue(rqp); + if (error) + return error; + error = smb_rq_reply(rqp); + if (error == 0) + break; + if ((rqp->sr_flags & (SMBR_RESTART | SMBR_NORESTART)) != SMBR_RESTART) + break; + } + return error; +} + +static int +smb_rq_enqueue(struct smb_rq *rqp) +{ + struct smb_share *ssp = rqp->sr_share; + int error; + + if (ssp == NULL || rqp->sr_cred == &rqp->sr_vc->vc_iod->iod_scred) { + return smb_iod_addrq(rqp); + } + for (;;) { + SMBS_ST_LOCK(ssp); + if (ssp->ss_flags & SMBS_RECONNECTING) { + msleep(&ssp->ss_vcgenid, SMBS_ST_LOCKPTR(ssp), + PWAIT | PDROP, "90trcn", hz); + if (smb_proc_intr(rqp->sr_cred->scr_p)) + return EINTR; + continue; + } + if (smb_share_valid(ssp) || (ssp->ss_flags & SMBS_CONNECTED) == 0) { + SMBS_ST_UNLOCK(ssp); + } else { + SMBS_ST_UNLOCK(ssp); + error = smb_iod_request(rqp->sr_vc->vc_iod, + SMBIOD_EV_TREECONNECT | SMBIOD_EV_SYNC, ssp); + if (error) + return error; + } + error = smb_iod_addrq(rqp); + if (error != EXDEV) + break; + } + return error; +} + +void +smb_rq_wstart(struct smb_rq *rqp) +{ + rqp->sr_wcount = mb_reserve(&rqp->sr_rq, sizeof(u_int8_t)); + rqp->sr_rq.mb_count = 0; +} + +void +smb_rq_wend(struct smb_rq *rqp) +{ + if (rqp->sr_wcount == NULL) { + SMBERROR("no wcount\n"); /* actually panic */ + return; + } + if (rqp->sr_rq.mb_count & 1) + SMBERROR("odd word count\n"); + *rqp->sr_wcount = rqp->sr_rq.mb_count / 2; +} + +void +smb_rq_bstart(struct smb_rq *rqp) +{ + rqp->sr_bcount = (u_short*)mb_reserve(&rqp->sr_rq, sizeof(u_short)); + rqp->sr_rq.mb_count = 0; +} + +void +smb_rq_bend(struct smb_rq *rqp) +{ + int bcnt; + + if (rqp->sr_bcount == NULL) { + SMBERROR("no bcount\n"); /* actually panic */ + return; + } + bcnt = rqp->sr_rq.mb_count; + if (bcnt > 0xffff) + SMBERROR("byte count too large (%d)\n", bcnt); + *rqp->sr_bcount = bcnt; +} + +int +smb_rq_intr(struct smb_rq *rqp) +{ + struct proc *p = rqp->sr_cred->scr_p; + + if (rqp->sr_flags & SMBR_INTR) + return EINTR; + return smb_proc_intr(p); +} + +int +smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp) +{ + *mbpp = &rqp->sr_rq; + return 0; +} + +int +smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp) +{ + *mbpp = &rqp->sr_rp; + return 0; +} + +static int +smb_rq_getenv(struct smb_connobj *layer, + struct smb_vc **vcpp, struct smb_share **sspp) +{ + struct smb_vc *vcp = NULL; + struct smb_share *ssp = NULL; + struct smb_connobj *cp; + int error = 0; + + switch (layer->co_level) { + case SMBL_VC: + vcp = CPTOVC(layer); + if (layer->co_parent == NULL) { + SMBERROR("zombie VC %s\n", vcp->vc_srvname); + error = EINVAL; + break; + } + break; + case SMBL_SHARE: + ssp = CPTOSS(layer); + cp = layer->co_parent; + if (cp == NULL) { + SMBERROR("zombie share %s\n", ssp->ss_name); + error = EINVAL; + break; + } + error = smb_rq_getenv(cp, &vcp, NULL); + if (error) + break; + break; + default: + SMBERROR("invalid layer %d passed\n", layer->co_level); + error = EINVAL; + } + if (vcpp) + *vcpp = vcp; + if (sspp) + *sspp = ssp; + return error; +} + +/* + * Wait for reply on the request + */ +static int +smb_rq_reply(struct smb_rq *rqp) +{ + struct mdchain *mdp = &rqp->sr_rp; + u_int32_t tdw; + u_int8_t tb; + int error, rperror = 0; + + error = smb_iod_waitrq(rqp); + if (error) + return error; + error = md_get_uint32(mdp, &tdw); + if (error) + return error; + error = md_get_uint8(mdp, &tb); + if (rqp->sr_vc->vc_hflags2 & SMB_FLAGS2_ERR_STATUS) { + error = md_get_uint32le(mdp, &rqp->sr_error); + } else { + error = md_get_uint8(mdp, &rqp->sr_errclass); + error = md_get_uint8(mdp, &tb); + error = md_get_uint16le(mdp, &rqp->sr_serror); + if (!error) + rperror = smb_maperror(rqp->sr_errclass, rqp->sr_serror); + } + error = md_get_uint8(mdp, &rqp->sr_rpflags); + error = md_get_uint16le(mdp, &rqp->sr_rpflags2); + + error = md_get_uint32(mdp, &tdw); + error = md_get_uint32(mdp, &tdw); + error = md_get_uint32(mdp, &tdw); + + error = md_get_uint16le(mdp, &rqp->sr_rptid); + error = md_get_uint16le(mdp, &rqp->sr_rppid); + error = md_get_uint16le(mdp, &rqp->sr_rpuid); + error = md_get_uint16le(mdp, &rqp->sr_rpmid); + + SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x, E: %d:%d\n", + rqp->sr_rpmid, rqp->sr_rppid, rqp->sr_rpuid, rqp->sr_rptid, + rqp->sr_errclass, rqp->sr_serror); + return error ? error : rperror; +} + + +#define ALIGN4(a) (((a) + 3) & ~3) + +/* + * TRANS2 request implementation + */ +int +smb_t2_alloc(struct smb_connobj *layer, u_short setup, struct smb_cred *scred, + struct smb_t2rq **t2pp) +{ + struct smb_t2rq *t2p; + int error; + + MALLOC(t2p, struct smb_t2rq *, sizeof(*t2p), M_SMBRQ, M_WAITOK); + if (t2p == NULL) + return ENOMEM; + error = smb_t2_init(t2p, layer, setup, scred); + t2p->t2_flags |= SMBT2_ALLOCED; + if (error) { + smb_t2_done(t2p); + return error; + } + *t2pp = t2p; + return 0; +} + +int +smb_t2_init(struct smb_t2rq *t2p, struct smb_connobj *source, u_short setup, + struct smb_cred *scred) +{ + int error; + + bzero(t2p, sizeof(*t2p)); + t2p->t2_source = source; + t2p->t2_setupcount = 1; + t2p->t2_setupdata = t2p->t2_setup; + t2p->t2_setup[0] = setup; + t2p->t2_fid = 0xffff; + t2p->t2_cred = scred; + error = smb_rq_getenv(source, &t2p->t2_vc, NULL); + if (error) + return error; + return 0; +} + +void +smb_t2_done(struct smb_t2rq *t2p) +{ + mb_done(&t2p->t2_tparam); + mb_done(&t2p->t2_tdata); + md_done(&t2p->t2_rparam); + md_done(&t2p->t2_rdata); + if (t2p->t2_flags & SMBT2_ALLOCED) + free(t2p, M_SMBRQ); +} + +static int +smb_t2_placedata(struct mbuf *mtop, u_int16_t offset, u_int16_t count, + struct mdchain *mdp) +{ + struct mbuf *m, *m0; + int len; + + m0 = m_split(mtop, offset, M_WAIT); + if (m0 == NULL) + return EBADRPC; + for(len = 0, m = m0; m->m_next; m = m->m_next) + len += m->m_len; + len += m->m_len; + m->m_len -= len - count; + if (mdp->md_top == NULL) { + md_initm(mdp, m0); + } else + m_cat(mdp->md_top, m0); + return 0; +} + +static int +smb_t2_reply(struct smb_t2rq *t2p) +{ + struct mdchain *mdp; + struct smb_rq *rqp = t2p->t2_rq; + int error, totpgot, totdgot; + u_int16_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp; + u_int16_t tmp, bc, dcount; + u_int8_t wc; + + error = smb_rq_reply(rqp); + if (error) + return error; + if ((t2p->t2_flags & SMBT2_ALLSENT) == 0) { + /* + * this is an interim response, ignore it. + */ + SMBRQ_SLOCK(rqp); + md_next_record(&rqp->sr_rp); + SMBRQ_SUNLOCK(rqp); + return 0; + } + /* + * Now we have to get all subseqent responses. The CIFS specification + * says that they can be misordered which is weird. + * TODO: timo + */ + totpgot = totdgot = 0; + totpcount = totdcount = 0xffff; + mdp = &rqp->sr_rp; + for (;;) { + m_dumpm(mdp->md_top); + if ((error = md_get_uint8(mdp, &wc)) != 0) + break; + if (wc < 10) { + error = ENOENT; + break; + } + if ((error = md_get_uint16le(mdp, &tmp)) != 0) + break; + if (totpcount > tmp) + totpcount = tmp; + md_get_uint16le(mdp, &tmp); + if (totdcount > tmp) + totdcount = tmp; + if ((error = md_get_uint16le(mdp, &tmp)) != 0 || /* reserved */ + (error = md_get_uint16le(mdp, &pcount)) != 0 || + (error = md_get_uint16le(mdp, &poff)) != 0 || + (error = md_get_uint16le(mdp, &pdisp)) != 0) + break; + if (pcount != 0 && pdisp != totpgot) { + SMBERROR("Can't handle misordered parameters %d:%d\n", + pdisp, totpgot); + error = EINVAL; + break; + } + if ((error = md_get_uint16le(mdp, &dcount)) != 0 || + (error = md_get_uint16le(mdp, &doff)) != 0 || + (error = md_get_uint16le(mdp, &ddisp)) != 0) + break; + if (dcount != 0 && ddisp != totdgot) { + SMBERROR("Can't handle misordered data\n"); + error = EINVAL; + break; + } + md_get_uint8(mdp, &wc); + md_get_uint8(mdp, NULL); + tmp = wc; + while (tmp--) + md_get_uint16(mdp, NULL); + if ((error = md_get_uint16le(mdp, &bc)) != 0) + break; +/* tmp = SMB_HDRLEN + 1 + 10 * 2 + 2 * wc + 2;*/ + if (dcount) { + error = smb_t2_placedata(mdp->md_top, doff, dcount, + &t2p->t2_rdata); + if (error) + break; + } + if (pcount) { + error = smb_t2_placedata(mdp->md_top, poff, pcount, + &t2p->t2_rparam); + if (error) + break; + } + totpgot += pcount; + totdgot += dcount; + if (totpgot >= totpcount && totdgot >= totdcount) { + error = 0; + t2p->t2_flags |= SMBT2_ALLRECV; + break; + } + /* + * We're done with this reply, look for the next one. + */ + SMBRQ_SLOCK(rqp); + md_next_record(&rqp->sr_rp); + SMBRQ_SUNLOCK(rqp); + error = smb_rq_reply(rqp); + if (error) + break; + } + return error; +} + +/* + * Perform a full round of TRANS2 request + */ +static int +smb_t2_request_int(struct smb_t2rq *t2p) +{ + struct smb_vc *vcp = t2p->t2_vc; + struct smb_cred *scred = t2p->t2_cred; + struct mbchain *mbp; + struct mdchain *mdp, mbparam, mbdata; + struct mbuf *m; + struct smb_rq *rqp; + int totpcount, leftpcount, totdcount, leftdcount, len, txmax, i; + int error, doff, poff, txdcount, txpcount, nmlen; + + m = t2p->t2_tparam.mb_top; + if (m) { + md_initm(&mbparam, m); /* do not free it! */ + totpcount = m_fixhdr(m); + if (totpcount > 0xffff) /* maxvalue for u_short */ + return EINVAL; + } else + totpcount = 0; + m = t2p->t2_tdata.mb_top; + if (m) { + md_initm(&mbdata, m); /* do not free it! */ + totdcount = m_fixhdr(m); + if (totdcount > 0xffff) + return EINVAL; + } else + totdcount = 0; + leftdcount = totdcount; + leftpcount = totpcount; + txmax = vcp->vc_txmax; + error = smb_rq_alloc(t2p->t2_source, t2p->t_name ? + SMB_COM_TRANSACTION : SMB_COM_TRANSACTION2, scred, &rqp); + if (error) + return error; + rqp->sr_flags |= SMBR_MULTIPACKET; + t2p->t2_rq = rqp; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, totpcount); + mb_put_uint16le(mbp, totdcount); + mb_put_uint16le(mbp, t2p->t2_maxpcount); + mb_put_uint16le(mbp, t2p->t2_maxdcount); + mb_put_uint8(mbp, t2p->t2_maxscount); + mb_put_uint8(mbp, 0); /* reserved */ + mb_put_uint16le(mbp, 0); /* flags */ + mb_put_uint32le(mbp, 0); /* Timeout */ + mb_put_uint16le(mbp, 0); /* reserved 2 */ + len = mb_fixhdr(mbp); + /* + * now we have known packet size as + * ALIGN4(len + 5 * 2 + setupcount * 2 + 2 + strlen(name) + 1), + * and need to decide which parts should go into the first request + */ + nmlen = t2p->t_name ? strlen(t2p->t_name) : 0; + len = ALIGN4(len + 5 * 2 + t2p->t2_setupcount * 2 + 2 + nmlen + 1); + if (len + leftpcount > txmax) { + txpcount = min(leftpcount, txmax - len); + poff = len; + txdcount = 0; + doff = 0; + } else { + txpcount = leftpcount; + poff = txpcount ? len : 0; + len = ALIGN4(len + txpcount); + txdcount = min(leftdcount, txmax - len); + doff = txdcount ? len : 0; + } + leftpcount -= txpcount; + leftdcount -= txdcount; + mb_put_uint16le(mbp, txpcount); + mb_put_uint16le(mbp, poff); + mb_put_uint16le(mbp, txdcount); + mb_put_uint16le(mbp, doff); + mb_put_uint8(mbp, t2p->t2_setupcount); + mb_put_uint8(mbp, 0); + for (i = 0; i < t2p->t2_setupcount; i++) + mb_put_uint16le(mbp, t2p->t2_setupdata[i]); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + /* TDUNICODE */ + if (t2p->t_name) + mb_put_mem(mbp, t2p->t_name, nmlen, MB_MSYSTEM); + mb_put_uint8(mbp, 0); /* terminating zero */ + len = mb_fixhdr(mbp); + if (txpcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbparam, txpcount, &m); + SMBSDEBUG("%d:%d:%d\n", error, txpcount, txmax); + if (error) + goto freerq; + mb_put_mbuf(mbp, m); + } + len = mb_fixhdr(mbp); + if (txdcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbdata, txdcount, &m); + if (error) + goto freerq; + mb_put_mbuf(mbp, m); + } + smb_rq_bend(rqp); /* incredible, but thats it... */ + error = smb_rq_enqueue(rqp); + if (error) + goto freerq; + if (leftpcount == 0 && leftdcount == 0) + t2p->t2_flags |= SMBT2_ALLSENT; + error = smb_t2_reply(t2p); + if (error) + goto bad; + while (leftpcount || leftdcount) { + error = smb_rq_new(rqp, t2p->t_name ? + SMB_COM_TRANSACTION_SECONDARY : SMB_COM_TRANSACTION2_SECONDARY); + if (error) + goto bad; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, totpcount); + mb_put_uint16le(mbp, totdcount); + len = mb_fixhdr(mbp); + /* + * now we have known packet size as + * ALIGN4(len + 7 * 2 + 2) for T2 request, and -2 for T one, + * and need to decide which parts should go into request + */ + len = ALIGN4(len + 6 * 2 + 2); + if (t2p->t_name == NULL) + len += 2; + if (len + leftpcount > txmax) { + txpcount = min(leftpcount, txmax - len); + poff = len; + txdcount = 0; + doff = 0; + } else { + txpcount = leftpcount; + poff = txpcount ? len : 0; + len = ALIGN4(len + txpcount); + txdcount = min(leftdcount, txmax - len); + doff = txdcount ? len : 0; + } + mb_put_uint16le(mbp, txpcount); + mb_put_uint16le(mbp, poff); + mb_put_uint16le(mbp, totpcount - leftpcount); + mb_put_uint16le(mbp, txdcount); + mb_put_uint16le(mbp, doff); + mb_put_uint16le(mbp, totdcount - leftdcount); + leftpcount -= txpcount; + leftdcount -= txdcount; + if (t2p->t_name == NULL) + mb_put_uint16le(mbp, t2p->t2_fid); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, 0); /* name */ + len = mb_fixhdr(mbp); + if (txpcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbparam, txpcount, &m); + if (error) + goto bad; + mb_put_mbuf(mbp, m); + } + len = mb_fixhdr(mbp); + if (txdcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbdata, txdcount, &m); + if (error) + goto bad; + mb_put_mbuf(mbp, m); + } + smb_rq_bend(rqp); + rqp->sr_state = SMBRQ_NOTSENT; + error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_NEWRQ, NULL); + if (error) + goto bad; + } /* while left params or data */ + t2p->t2_flags |= SMBT2_ALLSENT; + mdp = &t2p->t2_rdata; + if (mdp->md_top) { + m_fixhdr(mdp->md_top); + md_initm(mdp, mdp->md_top); + } + mdp = &t2p->t2_rparam; + if (mdp->md_top) { + m_fixhdr(mdp->md_top); + md_initm(mdp, mdp->md_top); + } +bad: + smb_iod_removerq(rqp); +freerq: + smb_rq_done(rqp); + if (error) { + if (rqp->sr_flags & SMBR_RESTART) + t2p->t2_flags |= SMBT2_RESTART; + md_done(&t2p->t2_rparam); + md_done(&t2p->t2_rdata); + } + return error; +} + +int +smb_t2_request(struct smb_t2rq *t2p) +{ + int error = EINVAL, i; + + for (i = 0; i < SMB_MAXRCN; i++) { + t2p->t2_flags &= ~SMBR_RESTART; + error = smb_t2_request_int(t2p); + if (error == 0) + break; + if ((t2p->t2_flags & (SMBT2_RESTART | SMBT2_NORESTART)) != SMBT2_RESTART) + break; + } + return error; +} diff --git a/sys/netsmb/smb_rq.h b/sys/netsmb/smb_rq.h new file mode 100644 index 000000000000..d37647b4c8d3 --- /dev/null +++ b/sys/netsmb/smb_rq.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2000-2001, 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 _NETSMB_SMB_RQ_H_ +#define _NETSMB_SMB_RQ_H_ + +#ifndef MB_MSYSTEM +#include +#endif + +#define SMBR_ALLOCED 0x0001 /* structure was malloced */ +#define SMBR_SENT 0x0002 /* request successfully transmitted */ +#define SMBR_REXMIT 0x0004 /* request should be retransmitted */ +#define SMBR_INTR 0x0008 /* request interrupted */ +#define SMBR_RESTART 0x0010 /* request should be repeated if possible */ +#define SMBR_NORESTART 0x0020 /* request is not restartable */ +#define SMBR_MULTIPACKET 0x0040 /* multiple packets can be sent and received */ +#define SMBR_INTERNAL 0x0080 /* request is internal to smbrqd */ +#define SMBR_XLOCK 0x0100 /* request locked and can't be moved */ +#define SMBR_XLOCKWANT 0x0200 /* waiter on XLOCK */ + +#define SMBT2_ALLSENT 0x0001 /* all data and params are sent */ +#define SMBT2_ALLRECV 0x0002 /* all data and params are received */ +#define SMBT2_ALLOCED 0x0004 +#define SMBT2_RESTART 0x0008 +#define SMBT2_NORESTART 0x0010 + +#define SMBRQ_SLOCK(rqp) smb_sl_lock(&(rqp)->sr_slock) +#define SMBRQ_SUNLOCK(rqp) smb_sl_unlock(&(rqp)->sr_slock) +#define SMBRQ_SLOCKPTR(rqp) (&(rqp)->sr_slock) + + +enum smbrq_state { + SMBRQ_NOTSENT, /* rq have data to send */ + SMBRQ_SENT, /* send procedure completed */ + SMBRQ_REPLYRECEIVED, + SMBRQ_NOTIFIED /* owner notified about completion */ +}; + +struct smb_vc; +struct smb_t2rq; + +struct smb_rq { + enum smbrq_state sr_state; + struct smb_vc * sr_vc; + struct smb_share* sr_share; + u_short sr_mid; + struct mbchain sr_rq; + u_int8_t sr_rqflags; + u_int16_t sr_rqflags2; + u_char * sr_wcount; + u_short * sr_bcount; + struct mdchain sr_rp; + int sr_rpgen; + int sr_rplast; + int sr_flags; /* SMBR_* */ + int sr_rpsize; + struct smb_cred * sr_cred; + int sr_timo; + int sr_rexmit; + int sr_sendcnt; + struct timespec sr_timesent; + int sr_lerror; + u_int16_t * sr_rqtid; + u_int16_t * sr_rquid; + u_int8_t sr_errclass; + u_int16_t sr_serror; + u_int32_t sr_error; + u_int8_t sr_rpflags; + u_int16_t sr_rpflags2; + u_int16_t sr_rptid; + u_int16_t sr_rppid; + u_int16_t sr_rpuid; + u_int16_t sr_rpmid; + struct smb_slock sr_slock; /* short term locks */ +/* struct smb_t2rq*sr_t2;*/ + TAILQ_ENTRY(smb_rq) sr_link; +}; + +struct smb_t2rq { + u_int16_t t2_setupcount; + u_int16_t * t2_setupdata; + u_int16_t t2_setup[2]; /* most of rqs has setupcount of 1 */ + u_int8_t t2_maxscount; /* max setup words to return */ + u_int16_t t2_maxpcount; /* max param bytes to return */ + u_int16_t t2_maxdcount; /* max data bytes to return */ + u_int16_t t2_fid; /* for T2 request */ + char * t_name; /* for T request, should be zero for T2 */ + int t2_flags; /* SMBT2_ */ + struct mbchain t2_tparam; /* parameters to transmit */ + struct mbchain t2_tdata; /* data to transmit */ + struct mdchain t2_rparam; /* received paramters */ + struct mdchain t2_rdata; /* received data */ + struct smb_cred*t2_cred; + struct smb_connobj *t2_source; + struct smb_rq * t2_rq; + struct smb_vc * t2_vc; +}; + +int smb_rq_alloc(struct smb_connobj *layer, u_char cmd, + struct smb_cred *scred, struct smb_rq **rqpp); +int smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, u_char cmd, + struct smb_cred *scred); +void smb_rq_done(struct smb_rq *rqp); +int smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp); +int smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp); +void smb_rq_wstart(struct smb_rq *rqp); +void smb_rq_wend(struct smb_rq *rqp); +void smb_rq_bstart(struct smb_rq *rqp); +void smb_rq_bend(struct smb_rq *rqp); +int smb_rq_intr(struct smb_rq *rqp); +int smb_rq_simple(struct smb_rq *rqp); + +int smb_t2_alloc(struct smb_connobj *layer, u_short setup, struct smb_cred *scred, + struct smb_t2rq **rqpp); +int smb_t2_init(struct smb_t2rq *rqp, struct smb_connobj *layer, u_short setup, + struct smb_cred *scred); +void smb_t2_done(struct smb_t2rq *t2p); +int smb_t2_request(struct smb_t2rq *t2p); + +#endif /* !_NETSMB_SMB_RQ_H_ */ diff --git a/sys/netsmb/smb_smb.c b/sys/netsmb/smb_smb.c new file mode 100644 index 000000000000..0f3179d38c56 --- /dev/null +++ b/sys/netsmb/smb_smb.c @@ -0,0 +1,660 @@ +/* + * Copyright (c) 2000-2001 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$ + */ +/* + * various SMB requests. Most of the routines merely packs data into mbufs. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +struct smb_dialect { + int d_id; + const char * d_name; +}; + +static struct smb_dialect smb_dialects[] = { + {SMB_DIALECT_CORE, "PC NETWORK PROGRAM 1.0"}, + {SMB_DIALECT_COREPLUS, "MICROSOFT NETWORKS 1.03"}, + {SMB_DIALECT_LANMAN1_0, "MICROSOFT NETWORKS 3.0"}, + {SMB_DIALECT_LANMAN1_0, "LANMAN1.0"}, + {SMB_DIALECT_LANMAN2_0, "LM1.2X002"}, + {SMB_DIALECT_LANMAN2_0, "Samba"}, + {SMB_DIALECT_NTLM0_12, "NT LANMAN 1.0"}, + {SMB_DIALECT_NTLM0_12, "NT LM 0.12"}, + {-1, NULL} +}; + +#define SMB_DIALECT_MAX (sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2) + +static int +smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name) +{ + if (scred->scr_p == vcp->vc_iod->iod_p) + return 0; + SMBERROR("wrong function called(%s)\n", name); + return EINVAL; +} + +int +smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_dialect *dp; + struct smb_sopt *sp = NULL; + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc, stime[8], sblen; + u_int16_t dindex, tw, tw1, swlen, bc; + int error, maxqsz; + + if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0) + return EINVAL; + vcp->vc_hflags = 0; + vcp->vc_hflags2 = 0; + vcp->obj.co_flags &= ~(SMBV_ENCRYPT); + sp = &vcp->vc_sopt; + bzero(sp, sizeof(struct smb_sopt)); + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + for(dp = smb_dialects; dp->d_id != -1; dp++) { + mb_put_uint8(mbp, SMB_DT_DIALECT); + smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE); + } + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + if (error) + goto bad; + smb_rq_getreply(rqp, &mdp); + do { + error = md_get_uint8(mdp, &wc); + if (error) + break; + error = md_get_uint16le(mdp, &dindex); + if (error) + break; + if (dindex > 7) { + SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex); + error = EBADRPC; + break; + } + dp = smb_dialects + dindex; + sp->sv_proto = dp->d_id; + SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc); + error = EBADRPC; + if (dp->d_id >= SMB_DIALECT_NTLM0_12) { + if (wc != 17) + break; + md_get_uint8(mdp, &sp->sv_sm); + md_get_uint16le(mdp, &sp->sv_maxmux); + md_get_uint16le(mdp, &sp->sv_maxvcs); + md_get_uint32le(mdp, &sp->sv_maxtx); + md_get_uint32le(mdp, &sp->sv_maxraw); + md_get_uint32le(mdp, &sp->sv_skey); + md_get_uint32le(mdp, &sp->sv_caps); + md_get_mem(mdp, stime, 8, MB_MSYSTEM); + md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz); + md_get_uint8(mdp, &sblen); + if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) { + if (sblen != SMB_MAXCHALLENGELEN) { + SMBERROR("Unexpected length of security blob (%d)\n", sblen); + break; + } + error = md_get_uint16(mdp, &bc); + if (error) + break; + if (sp->sv_caps & SMB_CAP_EXT_SECURITY) + md_get_mem(mdp, NULL, 16, MB_MSYSTEM); + error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM); + if (error) + break; + vcp->vc_chlen = sblen; + vcp->obj.co_flags |= SMBV_ENCRYPT; + } + vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; + if (dp->d_id == SMB_DIALECT_NTLM0_12 && + sp->sv_maxtx < 4096 && + (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) { + vcp->obj.co_flags |= SMBV_WIN95; + SMBSDEBUG("Win95 detected\n"); + } + } else if (dp->d_id > SMB_DIALECT_CORE) { + md_get_uint16le(mdp, &tw); + sp->sv_sm = tw; + md_get_uint16le(mdp, &tw); + sp->sv_maxtx = tw; + md_get_uint16le(mdp, &sp->sv_maxmux); + md_get_uint16le(mdp, &sp->sv_maxvcs); + md_get_uint16le(mdp, &tw); /* rawmode */ + md_get_uint32le(mdp, &sp->sv_skey); + if (wc == 13) { /* >= LANMAN1 */ + md_get_uint16(mdp, &tw); /* time */ + md_get_uint16(mdp, &tw1); /* date */ + md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz); + md_get_uint16le(mdp, &swlen); + if (swlen > SMB_MAXCHALLENGELEN) + break; + md_get_uint16(mdp, NULL); /* mbz */ + if (md_get_uint16(mdp, &bc) != 0) + break; + if (bc < swlen) + break; + if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) { + error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM); + if (error) + break; + vcp->vc_chlen = swlen; + vcp->obj.co_flags |= SMBV_ENCRYPT; + } + } + vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; + } else { /* an old CORE protocol */ + sp->sv_maxmux = 1; + } + error = 0; + } while (0); + if (error == 0) { + vcp->vc_maxvcs = sp->sv_maxvcs; + if (vcp->vc_maxvcs <= 1) { + if (vcp->vc_maxvcs == 0) + vcp->vc_maxvcs = 1; + } + if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff) + sp->sv_maxtx = 1024; + SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz); + vcp->vc_txmax = min(sp->sv_maxtx, maxqsz); + SMBSDEBUG("TZ = %d\n", sp->sv_tz); + SMBSDEBUG("CAPS = %x\n", sp->sv_caps); + SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux); + SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs); + SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw); + SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx); + } +bad: + smb_rq_done(rqp); + return error; +} + +int +smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; +/* u_int8_t wc; + u_int16_t tw, tw1;*/ + smb_uniptr unipp, ntencpass = NULL; + char *pp, *up, *pbuf, *encpass; + int error, plen, uniplen, ulen; + + vcp->vc_smbuid = SMB_UID_UNKNOWN; + + if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0) + return EINVAL; + + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp); + if (error) + return error; + pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK); + encpass = malloc(24, M_SMBTEMP, M_WAITOK); + if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { + iconv_convstr(vcp->vc_toupper, pbuf, smb_vc_getpass(vcp)); + iconv_convstr(vcp->vc_toserver, pbuf, pbuf); + if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { + uniplen = plen = 24; + smb_encrypt(pbuf, vcp->vc_ch, encpass); + ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK); + iconv_convstr(vcp->vc_toserver, pbuf, smb_vc_getpass(vcp)); + smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass); + pp = encpass; + unipp = ntencpass; + } else { + plen = strlen(pbuf) + 1; + pp = pbuf; + uniplen = plen * 2; + ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK); + smb_strtouni(ntencpass, smb_vc_getpass(vcp)); + plen--; + uniplen = 0/*-= 2*/; + unipp = ntencpass; + } + } else { + /* + * In the share security mode password will be used + * only in the tree authentication + */ + pp = ""; + plen = 1; + unipp = &smb_unieol; + uniplen = sizeof(smb_unieol); + } + smb_rq_wstart(rqp); + mbp = &rqp->sr_rq; + up = vcp->vc_username; + ulen = strlen(up) + 1; + mb_put_uint8(mbp, 0xff); + mb_put_uint8(mbp, 0); + mb_put_uint16le(mbp, 0); + mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx); + mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux); + mb_put_uint16le(mbp, vcp->vc_number); + mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey); + mb_put_uint16le(mbp, plen); + if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) { + mb_put_uint32le(mbp, 0); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_mem(mbp, pp, plen, MB_MSYSTEM); + smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); + } else { + mb_put_uint16le(mbp, uniplen); + mb_put_uint32le(mbp, 0); /* reserved */ + mb_put_uint32le(mbp, 0); /* my caps */ + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_mem(mbp, pp, plen, MB_MSYSTEM); + mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM); + smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* AccountName */ + smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE); /* PrimaryDomain */ + smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE); /* Client's OS */ + smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE); /* Client name */ + } + smb_rq_bend(rqp); + if (ntencpass) + free(ntencpass, M_SMBTEMP); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + if (error) { + if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess) + error = EAUTH; + goto bad; + } + vcp->vc_smbuid = rqp->sr_rpuid; +bad: + free(encpass, M_SMBTEMP); + free(pbuf, M_SMBTEMP); + smb_rq_done(rqp); + return error; +} + +int +smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + if (vcp->vc_smbuid == SMB_UID_UNKNOWN) + return 0; + + if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0) + return EINVAL; + + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp); + if (error) + return error; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); + mb_put_uint8(mbp, 0); + mb_put_uint16le(mbp, 0); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + return error; +} + +static char smb_any_share[] = "?????"; + +static char * +smb_share_typename(int stype) +{ + char *pp; + + switch (stype) { + case SMB_ST_DISK: + pp = "A:"; + break; + case SMB_ST_PRINTER: + pp = smb_any_share; /* can't use LPT: here... */ + break; + case SMB_ST_PIPE: + pp = "IPC"; + break; + case SMB_ST_COMM: + pp = "COMM"; + break; + case SMB_ST_ANY: + default: + pp = smb_any_share; + break; + } + return pp; +} + +int +smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) +{ + struct smb_vc *vcp; + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + char *pp, *pbuf, *encpass; + int error, plen, caseopt; + + ssp->ss_tid = SMB_TID_UNKNOWN; + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp); + if (error) + return error; + vcp = rqp->sr_vc; + caseopt = SMB_CS_NONE; + if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { + plen = 1; + pp = ""; + pbuf = NULL; + encpass = NULL; + } else { + pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK); + encpass = malloc(24, M_SMBTEMP, M_WAITOK); + iconv_convstr(vcp->vc_toupper, pbuf, smb_share_getpass(ssp)); + iconv_convstr(vcp->vc_toserver, pbuf, pbuf); + if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { + plen = 24; + smb_encrypt(pbuf, vcp->vc_ch, encpass); + pp = encpass; + } else { + plen = strlen(pbuf) + 1; + pp = pbuf; + } + } + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); + mb_put_uint8(mbp, 0); + mb_put_uint16le(mbp, 0); + mb_put_uint16le(mbp, 0); /* Flags */ + mb_put_uint16le(mbp, plen); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_mem(mbp, pp, plen, MB_MSYSTEM); + smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt); + pp = vcp->vc_srvname; + smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt); + smb_put_dmem(mbp, vcp, "\\", 1, caseopt); + pp = ssp->ss_name; + smb_put_dstring(mbp, vcp, pp, caseopt); + pp = smb_share_typename(ssp->ss_type); + smb_put_dstring(mbp, vcp, pp, caseopt); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + if (error) + goto bad; + ssp->ss_tid = rqp->sr_rptid; + ssp->ss_vcgenid = vcp->vc_genid; + ssp->ss_flags |= SMBS_CONNECTED; +bad: + if (encpass) + free(encpass, M_SMBTEMP); + if (pbuf) + free(pbuf, M_SMBTEMP); + smb_rq_done(rqp); + return error; +} + +int +smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + if (ssp->ss_tid == SMB_TID_UNKNOWN) + return 0; + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp); + if (error) + return error; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + ssp->ss_tid = SMB_TID_UNKNOWN; + return error; +} + +static __inline int +smb_smb_read(struct smb_share *ssp, u_int16_t fid, + int *len, int *rresid, struct uio *uio, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int16_t resid, bc; + u_int8_t wc; + int error, rlen, blksz; + + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp); + if (error) + return error; + + blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16; + rlen = *len = min(blksz, *len); + + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); + mb_put_uint16le(mbp, rlen); + mb_put_uint32le(mbp, uio->uio_offset); + mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff)); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + do { + error = smb_rq_simple(rqp); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 5) { + error = EBADRPC; + break; + } + md_get_uint16le(mdp, &resid); + md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); + md_get_uint16le(mdp, &bc); + md_get_uint8(mdp, NULL); /* ignore buffer type */ + md_get_uint16le(mdp, &resid); + if (resid == 0) { + *rresid = resid; + break; + } + error = md_get_uio(mdp, uio, resid); + if (error) + break; + *rresid = resid; + } while(0); + smb_rq_done(rqp); + return error; +} + +int +smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio, + struct smb_cred *scred) +{ + int tsize, len, resid; + int error = 0; + + tsize = uio->uio_resid; + while (tsize > 0) { + len = tsize; + error = smb_smb_read(ssp, fid, &len, &resid, uio, scred); + if (error) + break; + tsize -= resid; + if (resid < len) + break; + } + return error; +} + +static __inline int +smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, + struct uio *uio, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int16_t resid; + u_int8_t wc; + int error, blksz; + + blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16; + if (blksz > 0xffff) + blksz = 0xffff; + + resid = *len = min(blksz, *len); + + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); + mb_put_uint16le(mbp, resid); + mb_put_uint32le(mbp, uio->uio_offset); + mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff)); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_DATA); + mb_put_uint16le(mbp, resid); + do { + error = mb_put_uio(mbp, uio, resid); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 1) { + error = EBADRPC; + break; + } + md_get_uint16le(mdp, &resid); + *rresid = resid; + } while(0); + smb_rq_done(rqp); + return error; +} + +int +smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio, + struct smb_cred *scred) +{ + int error = 0, len, tsize, resid; + struct uio olduio; + + /* + * review: manage iov more precisely + */ + if (uio->uio_iovcnt != 1) { + SMBERROR("can't handle iovcnt > 1\n"); + return EIO; + } + tsize = uio->uio_resid; + olduio = *uio; + while (tsize > 0) { + len = tsize; + error = smb_smb_write(ssp, fid, &len, &resid, uio, scred); + if (error) + break; + if (resid < len) { + error = EIO; + break; + } + tsize -= resid; + } + if (error) { + *uio = olduio; + } + return error; +} + +int +smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp); + if (error) + return error; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, 1); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint32le(mbp, 0); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + return error; +} diff --git a/sys/netsmb/smb_subr.c b/sys/netsmb/smb_subr.c new file mode 100644 index 000000000000..8346629e4637 --- /dev/null +++ b/sys/netsmb/smb_subr.c @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2000-2001 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +MALLOC_DEFINE(M_SMBDATA, "SMBDATA", "Misc netsmb data"); +MALLOC_DEFINE(M_SMBSTR, "SMBSTR", "netsmb string data"); +MALLOC_DEFINE(M_SMBTEMP, "SMBTEMP", "Temp netsmb data"); + +smb_unichar smb_unieol = 0; + +void +smb_makescred(struct smb_cred *scred, struct proc *p, struct ucred *cred) +{ + if (p) { + scred->scr_p = p; + scred->scr_cred = cred ? cred : p->p_ucred; + } else { + scred->scr_p = NULL; + scred->scr_cred = cred ? cred : NULL; + } +} + +int +smb_proc_intr(struct proc *p) +{ +#if __FreeBSD_version < 400009 + + if (p && p->p_siglist && + (((p->p_siglist & ~p->p_sigmask) & ~p->p_sigignore) & SMB_SIGMASK)) + return EINTR; + return 0; +#else + 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) && SMB_SIGMASK(tmpset)) + return EINTR; + return 0; +#endif +} + +char * +smb_strdup(const char *s) +{ + char *p; + int len; + + len = s ? strlen(s) + 1 : 1; + p = malloc(len, M_SMBSTR, M_WAITOK); + if (s) + bcopy(s, p, len); + else + *p = 0; + return p; +} + +/* + * duplicate string from a user space. + */ +char * +smb_strdupin(char *s, int maxlen) +{ + char *p, bt; + int len = 0; + + for (p = s; ;p++) { + if (copyin(p, &bt, 1)) + return NULL; + len++; + if (maxlen && len > maxlen) + return NULL; + if (bt == 0) + break; + } + p = malloc(len, M_SMBSTR, M_WAITOK); + copyin(s, p, len); + return p; +} + +/* + * duplicate memory block from a user space. + */ +void * +smb_memdupin(void *umem, int len) +{ + char *p; + + if (len > 8 * 1024) + return NULL; + p = malloc(len, M_SMBSTR, M_WAITOK); + if (copyin(umem, p, len) == 0) + return p; + free(p, M_SMBSTR); + return NULL; +} + +/* + * duplicate memory block in the kernel space. + */ +void * +smb_memdup(const void *umem, int len) +{ + char *p; + + if (len > 8 * 1024) + return NULL; + p = malloc(len, M_SMBSTR, M_WAITOK); + if (p == NULL) + return NULL; + bcopy(umem, p, len); + return p; +} + +void +smb_strfree(char *s) +{ + free(s, M_SMBSTR); +} + +void +smb_memfree(void *s) +{ + free(s, M_SMBSTR); +} + +void * +smb_zmalloc(unsigned long size, struct malloc_type *type, int flags) +{ + + return malloc(size, type, flags | M_ZERO); +} + +void +smb_strtouni(u_int16_t *dst, const char *src) +{ + while (*src) { + *dst++ = htoles(*src++); + } + *dst = 0; +} + +#ifdef SMB_SOCKETDATA_DEBUG +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 + +int +smb_maperror(int eclass, int eno) +{ + if (eclass == 0 && eno == 0) + return 0; + switch (eclass) { + case ERRDOS: + switch (eno) { + case ERRbadfunc: + case ERRbadmcb: + case ERRbadenv: + case ERRbadformat: + case ERRrmuns: + return EINVAL; + case ERRbadfile: + case ERRbadpath: + case ERRremcd: + case 66: /* nt returns it when share not available */ + return ENOENT; + case ERRnofids: + return EMFILE; + case ERRnoaccess: + case ERRbadshare: + return EACCES; + case ERRbadfid: + return EBADF; + case ERRnomem: + return ENOMEM; /* actually remote no mem... */ + case ERRbadmem: + return EFAULT; + case ERRbadaccess: + return EACCES; + case ERRbaddata: + return E2BIG; + case ERRbaddrive: + case ERRnotready: /* nt */ + return ENXIO; + case ERRdiffdevice: + return EXDEV; + case ERRnofiles: + return 0; /* eeof ? */ + return ETXTBSY; + case ERRlock: + return EDEADLK; + case ERRfilexists: + return EEXIST; + case 123: /* dunno what is it, but samba maps as noent */ + return ENOENT; + case 145: /* samba */ + return ENOTEMPTY; + case 183: + return EEXIST; + } + break; + case ERRSRV: + switch (eno) { + case ERRerror: + return EINVAL; + case ERRbadpw: + return EAUTH; + case ERRaccess: + return EACCES; + case ERRinvnid: + return ENETRESET; + case ERRinvnetname: + SMBERROR("NetBIOS name is invalid\n"); + return EAUTH; + case 3: /* reserved and returned */ + return EIO; + case 2239: /* NT: account exists but disabled */ + return EPERM; + } + break; + case ERRHRD: + switch (eno) { + case ERRnowrite: + return EROFS; + case ERRbadunit: + return ENODEV; + case ERRnotready: + case ERRbadcmd: + case ERRdata: + return EIO; + case ERRbadreq: + return EBADRPC; + case ERRbadshare: + return ETXTBSY; + case ERRlock: + return EDEADLK; + } + break; + } + SMBERROR("Unmapped error %d:%d\n", eclass, eno); + return EBADRPC; +} + +static int +smb_copy_iconv(struct mbchain *mbp, c_caddr_t src, caddr_t dst, int len) +{ + int outlen = len; + + return iconv_conv((struct iconv_drv*)mbp->mb_udata, &src, &len, &dst, &outlen); +} + +int +smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, const char *src, + int size, int caseopt) +{ + struct iconv_drv *dp = vcp->vc_toserver; + + if (size == 0) + return 0; + if (dp == NULL) { + return mb_put_mem(mbp, src, size, MB_MSYSTEM); + } + mbp->mb_copy = smb_copy_iconv; + mbp->mb_udata = dp; + return mb_put_mem(mbp, src, size, MB_MCUSTOM); +} + +int +smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp, const char *src, + int caseopt) +{ + int error; + + error = smb_put_dmem(mbp, vcp, src, strlen(src), caseopt); + if (error) + return error; + return mb_put_uint8(mbp, 0); +} + +int +smb_put_asunistring(struct smb_rq *rqp, const char *src) +{ + struct mbchain *mbp = &rqp->sr_rq; + struct iconv_drv *dp = rqp->sr_vc->vc_toserver; + u_char c; + int error; + + while (*src) { + iconv_convmem(dp, &c, src++, 1); + error = mb_put_uint16le(mbp, c); + if (error) + return error; + } + return mb_put_uint16le(mbp, 0); +} diff --git a/sys/netsmb/smb_subr.h b/sys/netsmb/smb_subr.h new file mode 100644 index 000000000000..16ce212db399 --- /dev/null +++ b/sys/netsmb/smb_subr.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2000-2001, 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 _NETSMB_SMB_SUBR_H_ +#define _NETSMB_SMB_SUBR_H_ + +#ifndef _KERNEL +#error "This file shouldn't be included from userland programs" +#endif + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_SMBTEMP); +#endif + +#if __FreeBSD_version > 500000 +#define FB_CURRENT +#else +# if __FreeBSD_version > 400000 +# define FB_RELENG4 +# else +# error "Unsupported version of FreeBSD" +# endif +#endif + +#define SMBERROR(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#define SMBPANIC(format, args...) printf("%s: "format, __FUNCTION__ ,## args) + +#ifdef SMB_SOCKET_DEBUG +#define SMBSDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#else +#define SMBSDEBUG(format, args...) +#endif + +#ifdef SMB_IOD_DEBUG +#define SMBIODEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#else +#define SMBIODEBUG(format, args...) +#endif + +#ifdef SMB_SOCKETDATA_DEBUG +void m_dumpm(struct mbuf *m); +#else +#define m_dumpm(m) +#endif + +#if __FreeBSD_version > 400009 +#define SMB_SIGMASK(set) \ + (SIGISMEMBER(set, SIGINT) || SIGISMEMBER(set, SIGTERM) || \ + SIGISMEMBER(set, SIGHUP) || SIGISMEMBER(set, SIGKILL) || \ + SIGISMEMBER(set, SIGQUIT)) + +#define smb_suser(cred) suser_xxx(cred, NULL, 0) +#else +#define SMB_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \ + sigmask(SIGHUP)|sigmask(SIGQUIT)) + +#define smb_suser(cred) suser((cred), NULL) +#endif + +/* + * Compatibility wrappers for simple locks + */ +#if __FreeBSD_version < 500000 + +#include + +#define lockdestroy(lock) +#define smb_slock simplelock +#define smb_sl_init(mtx, desc) simple_lock_init(mtx) +#define smb_sl_destroy(mtx) +#define smb_sl_lock(mtx) simple_lock(mtx) +#define smb_sl_unlock(mtx) simple_unlock(mtx) +/* +#define mtx lock +#define mtx_init(mtx, desc, flags) lockinit(mtx, PWAIT, desc, 0, 0) +#define mtx_lock(mtx) lockmgr(mtx, LK_EXCLUSIVE, NULL, curproc) +#define mtx_unlock(mtx) lockmgr(mtx, LK_RELEASE, NULL, curproc) +#define mtx_destroy(mtx) +*/ +#else + +#include + +#define smb_slock mtx +#define smb_sl_init(mtx, desc) mtx_init(mtx, desc, MTX_DEF) +#define smb_sl_destroy(mtx) mtx_destroy(mtx) +#define smb_sl_lock(mtx) mtx_lock(mtx) +#define smb_sl_unlock(mtx) mtx_unlock(mtx) + +#endif + +#define SMB_STRFREE(p) do { if (p) smb_strfree(p); } while(0) + +/* + * The simple try/catch/finally interface. + * With GCC it is possible to allow more than one try/finally block per + * function, but we'll avoid it to maintain portability. + */ +#define itry { \ + __label__ _finlab, _catchlab; \ + int _tval; \ + +#define icatch(var) \ + goto _finlab; \ + (void)&&_catchlab; \ + _catchlab: \ + var = _tval; + +#define ifinally (void)&&_finlab; \ + _finlab: +#define iendtry } + +#define inocatch \ + goto _finlab; \ + (void)&&_catchlab; \ + _catchlab: \ + +#define ithrow(t) do { \ + if ((_tval = (int)(t)) != 0) \ + goto _catchlab; \ + } while (0) + +#define ierror(t,e) do { \ + if (t) { \ + _tval = e; \ + goto _catchlab; \ + } \ + } while (0) + +typedef u_int16_t smb_unichar; +typedef smb_unichar *smb_uniptr; + +/* + * Crediantials of user/process being processing in the connection procedures + */ +struct smb_cred { + struct proc * scr_p; + struct ucred * scr_cred; +}; + +extern smb_unichar smb_unieol; + +struct mbchain; +struct smb_vc; +struct smb_rq; + +void smb_makescred(struct smb_cred *scred, struct proc *p, struct ucred *cred); +int smb_proc_intr(struct proc *); +char *smb_strdup(const char *s); +void *smb_memdup(const void *umem, int len); +char *smb_strdupin(char *s, int maxlen); +void *smb_memdupin(void *umem, int len); +void smb_strtouni(u_int16_t *dst, const char *src); +void smb_strfree(char *s); +void smb_memfree(void *s); +void *smb_zmalloc(unsigned long size, struct malloc_type *type, int flags); + +int smb_encrypt(const u_char *apwd, u_char *C8, u_char *RN); +int smb_ntencrypt(const u_char *apwd, u_char *C8, u_char *RN); +int smb_maperror(int eclass, int eno); +int smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, + const char *src, int len, int caseopt); +int smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp, + const char *src, int caseopt); +int smb_put_string(struct smb_rq *rqp, const char *src); +int smb_put_asunistring(struct smb_rq *rqp, const char *src); + +#endif /* !_NETSMB_SMB_SUBR_H_ */ diff --git a/sys/netsmb/smb_tran.h b/sys/netsmb/smb_tran.h new file mode 100644 index 000000000000..7403d21240e0 --- /dev/null +++ b/sys/netsmb/smb_tran.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2000-2001, 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 _NETSMB_SMB_TRAN_H_ +#define _NETSMB_SMB_TRAN_H_ + +#include + +/* + * Known transports + */ +#define SMBT_NBTCP 1 + +/* + * Transport parameters + */ +#define SMBTP_SNDSZ 1 /* R - int */ +#define SMBTP_RCVSZ 2 /* R - int */ +#define SMBTP_TIMEOUT 3 /* RW - struct timespec */ +#define SMBTP_SELECTID 4 /* RW - (void *) */ + +struct smb_tran_ops; + +struct smb_tran_desc { + sa_family_t tr_type; + int (*tr_create)(struct smb_vc *vcp, struct proc *p); + int (*tr_done)(struct smb_vc *vcp, struct proc *p); + int (*tr_bind)(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p); + int (*tr_connect)(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p); + int (*tr_disconnect)(struct smb_vc *vcp, struct proc *p); + int (*tr_send)(struct smb_vc *vcp, struct mbuf *m0, struct proc *p); + int (*tr_recv)(struct smb_vc *vcp, struct mbuf **mpp, struct proc *p); + void (*tr_timo)(struct smb_vc *vcp); + void (*tr_intr)(struct smb_vc *vcp); + int (*tr_getparam)(struct smb_vc *vcp, int param, void *data); + int (*tr_setparam)(struct smb_vc *vcp, int param, void *data); + int (*tr_fatal)(struct smb_vc *vcp, int error); +#ifdef notyet + int (*tr_poll)(struct smb_vc *vcp, struct proc *p); + int (*tr_cmpaddr)(void *addr1, void *addr2); +#endif + LIST_ENTRY(smb_tran_desc) tr_link; +}; + +#define SMB_TRAN_CREATE(vcp,p) (vcp)->vc_tdesc->tr_create(vcp,p) +#define SMB_TRAN_DONE(vcp,p) (vcp)->vc_tdesc->tr_done(vcp,p) +#define SMB_TRAN_BIND(vcp,sap,p) (vcp)->vc_tdesc->tr_bind(vcp,sap,p) +#define SMB_TRAN_CONNECT(vcp,sap,p) (vcp)->vc_tdesc->tr_connect(vcp,sap,p) +#define SMB_TRAN_DISCONNECT(vcp,p) (vcp)->vc_tdesc->tr_disconnect(vcp,p) +#define SMB_TRAN_SEND(vcp,m0,p) (vcp)->vc_tdesc->tr_send(vcp,m0,p) +#define SMB_TRAN_RECV(vcp,m,p) (vcp)->vc_tdesc->tr_recv(vcp,m,p) +#define SMB_TRAN_TIMO(vcp) (vcp)->vc_tdesc->tr_timo(vcp) +#define SMB_TRAN_INTR(vcp) (vcp)->vc_tdesc->tr_intr(vcp) +#define SMB_TRAN_GETPARAM(vcp,par,data) (vcp)->vc_tdesc->tr_getparam(vcp, par, data) +#define SMB_TRAN_SETPARAM(vcp,par,data) (vcp)->vc_tdesc->tr_setparam(vcp, par, data) +#define SMB_TRAN_FATAL(vcp, error) (vcp)->vc_tdesc->tr_fatal(vcp, error) + +#endif /* _NETSMB_SMB_TRAN_H_ */ diff --git a/sys/netsmb/smb_trantcp.c b/sys/netsmb/smb_trantcp.c new file mode 100644 index 000000000000..c9d062287a45 --- /dev/null +++ b/sys/netsmb/smb_trantcp.c @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2000-2001 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#define M_NBDATA M_PCB + +static int smb_tcpsndbuf = 10 * 1024; +static int smb_tcprcvbuf = 10 * 1024; + +SYSCTL_DECL(_net_smb); +SYSCTL_INT(_net_smb, OID_AUTO, tcpsndbuf, CTLFLAG_RW, &smb_tcpsndbuf, 0, ""); +SYSCTL_INT(_net_smb, OID_AUTO, tcprcvbuf, CTLFLAG_RW, &smb_tcprcvbuf, 0, ""); + +#define nb_sosend(so,m,flags,p) (so)->so_proto->pr_usrreqs->pru_sosend( \ + so, NULL, 0, m, 0, flags, p) + +static int nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp, + u_int8_t *rpcodep, struct proc *p); +static int smb_nbst_disconnect(struct smb_vc *vcp, struct proc *p); + +static int +nb_setsockopt_int(struct socket *so, int level, int name, int val) +{ + struct sockopt sopt; + + bzero(&sopt, sizeof(sopt)); + sopt.sopt_level = level; + sopt.sopt_name = name; + sopt.sopt_val = &val; + sopt.sopt_valsize = sizeof(val); + return sosetopt(so, &sopt); +} + +static __inline int +nb_poll(struct nbpcb *nbp, int events, struct proc *p) +{ + return nbp->nbp_tso->so_proto->pr_usrreqs->pru_sopoll(nbp->nbp_tso, + events, NULL, p); +} + +static int +nbssn_rselect(struct nbpcb *nbp, struct timeval *tv, int events, struct proc *p) +{ + struct timeval atv, rtv, ttv; + int s, timo, error; + + 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 = nb_poll(nbp, events, p); + 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, "nbsel", timo); + splx(s); +done: + p->p_flag &= ~P_SELECT; + if (error == ERESTART) + return 0; + return error; +} + +static int +nb_intr(struct nbpcb *nbp, struct proc *p) +{ + return 0; +} + +static void +nb_upcall(struct socket *so, void *arg, int waitflag) +{ + struct nbpcb *nbp = arg; + + if (arg == NULL || nbp->nbp_selectid == NULL) + return; + wakeup(nbp->nbp_selectid); +} + +static int +nb_sethdr(struct mbuf *m, u_int8_t type, u_int32_t len) +{ + u_int32_t *p = mtod(m, u_int32_t *); + + *p = htonl((len & 0x1FFFF) | (type << 24)); + return 0; +} + +static int +nb_put_name(struct mbchain *mbp, struct sockaddr_nb *snb) +{ + int error; + u_char seglen, *cp; + + cp = snb->snb_name; + if (*cp == 0) + return EINVAL; + NBDEBUG("[%s]\n", cp); + for (;;) { + seglen = (*cp) + 1; + error = mb_put_mem(mbp, cp, seglen, MB_MSYSTEM); + if (error) + return error; + if (seglen == 1) + break; + cp += seglen; + } + return 0; +} + +static int +nb_connect_in(struct nbpcb *nbp, struct sockaddr_in *to, struct proc *p) +{ + struct socket *so; + int error, s; + + error = socreate(AF_INET, &so, SOCK_STREAM, IPPROTO_TCP, p); + if (error) + return error; + nbp->nbp_tso = so; + so->so_upcallarg = (caddr_t)nbp; + so->so_upcall = nb_upcall; + so->so_rcv.sb_flags |= SB_UPCALL; + so->so_rcv.sb_timeo = (5 * hz); + so->so_snd.sb_timeo = (5 * hz); + error = soreserve(so, nbp->nbp_sndbuf, nbp->nbp_rcvbuf); + if (error) + goto bad; + nb_setsockopt_int(so, SOL_SOCKET, SO_KEEPALIVE, 1); + nb_setsockopt_int(so, IPPROTO_TCP, TCP_NODELAY, 1); + so->so_rcv.sb_flags &= ~SB_NOINTR; + so->so_snd.sb_flags &= ~SB_NOINTR; + error = soconnect(so, (struct sockaddr*)to, p); + if (error) + goto bad; + s = splnet(); + while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { + tsleep(&so->so_timeo, PSOCK, "nbcon", 2 * hz); + if ((so->so_state & SS_ISCONNECTING) && so->so_error == 0 && + (error = nb_intr(nbp, p)) != 0) { + 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); + return 0; +bad: + smb_nbst_disconnect(nbp->nbp_vc, p); + return error; +} + +static int +nbssn_rq_request(struct nbpcb *nbp, struct proc *p) +{ + struct mbchain mb, *mbp = &mb; + struct mdchain md, *mdp = &md; + struct mbuf *m0; + struct timeval tv; + struct sockaddr_in sin; + u_short port; + u_int8_t rpcode; + int error, rplen; + + error = mb_init(mbp); + if (error) + return error; + mb_put_uint32le(mbp, 0); + nb_put_name(mbp, nbp->nbp_paddr); + nb_put_name(mbp, nbp->nbp_laddr); + nb_sethdr(mbp->mb_top, NB_SSN_REQUEST, mb_fixhdr(mbp) - 4); + error = nb_sosend(nbp->nbp_tso, mbp->mb_top, 0, p); + if (!error) { + nbp->nbp_state = NBST_RQSENT; + } + mb_detach(mbp); + mb_done(mbp); + if (error) + return error; + TIMESPEC_TO_TIMEVAL(&tv, &nbp->nbp_timo); + error = nbssn_rselect(nbp, &tv, POLLIN, p); + if (error == EWOULDBLOCK) { /* Timeout */ + NBDEBUG("initial request timeout\n"); + return ETIMEDOUT; + } + if (error) /* restart or interrupt */ + return error; + error = nbssn_recv(nbp, &m0, &rplen, &rpcode, p); + if (error) { + NBDEBUG("recv() error %d\n", error); + return error; + } + /* + * Process NETBIOS reply + */ + if (m0) + md_initm(mdp, m0); + error = 0; + do { + if (rpcode == NB_SSN_POSRESP) { + nbp->nbp_state = NBST_SESSION; + nbp->nbp_flags |= NBF_CONNECTED; + break; + } + if (rpcode != NB_SSN_RTGRESP) { + error = ECONNABORTED; + break; + } + if (rplen != 6) { + error = ECONNABORTED; + break; + } + md_get_mem(mdp, (caddr_t)&sin.sin_addr, 4, MB_MSYSTEM); + md_get_uint16(mdp, &port); + sin.sin_port = port; + nbp->nbp_state = NBST_RETARGET; + smb_nbst_disconnect(nbp->nbp_vc, p); + error = nb_connect_in(nbp, &sin, p); + if (!error) + error = nbssn_rq_request(nbp, p); + if (error) { + smb_nbst_disconnect(nbp->nbp_vc, p); + break; + } + } while(0); + if (m0) + md_done(mdp); + return error; +} + +static int +nbssn_recvhdr(struct nbpcb *nbp, int *lenp, + u_int8_t *rpcodep, int flags, struct proc *p) +{ + struct socket *so = nbp->nbp_tso; + struct uio auio; + struct iovec aio; + u_int32_t len; + int error; + + aio.iov_base = (caddr_t)&len; + aio.iov_len = sizeof(len); + auio.uio_iov = &aio; + auio.uio_iovcnt = 1; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_READ; + auio.uio_offset = 0; + auio.uio_resid = sizeof(len); + auio.uio_procp = p; + error = so->so_proto->pr_usrreqs->pru_soreceive + (so, (struct sockaddr **)NULL, &auio, + (struct mbuf **)NULL, (struct mbuf **)NULL, &flags); + if (error) + return error; + if (auio.uio_resid > 0) { + SMBSDEBUG("short reply\n"); + return EPIPE; + } + len = ntohl(len); + *rpcodep = (len >> 24) & 0xFF; + len &= 0x1ffff; + if (len > SMB_MAXPKTLEN) { + SMBERROR("packet too long (%d)\n", len); + return EFBIG; + } + *lenp = len; + return 0; +} + +static int +nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp, + u_int8_t *rpcodep, struct proc *p) +{ + struct socket *so = nbp->nbp_tso; + struct uio auio; + struct mbuf *m; + u_int8_t rpcode; + int len; + int error, rcvflg; + + if (so == NULL) + return ENOTCONN; + + if (mpp) + *mpp = NULL; + for(;;) { + m = NULL; + error = nbssn_recvhdr(nbp, &len, &rpcode, MSG_DONTWAIT, p); + if (so->so_state & + (SS_ISDISCONNECTING | SS_ISDISCONNECTED | SS_CANTRCVMORE)) { + nbp->nbp_state = NBST_CLOSED; + NBDEBUG("session closed by peer\n"); + return ECONNRESET; + } + if (error) + return error; + if (len == 0 && nbp->nbp_state != NBST_SESSION) + break; + if (rpcode == NB_SSN_KEEPALIVE) + continue; + bzero(&auio, sizeof(auio)); + auio.uio_resid = len; + auio.uio_procp = p; + do { + rcvflg = MSG_WAITALL; + error = so->so_proto->pr_usrreqs->pru_soreceive + (so, (struct sockaddr **)NULL, + &auio, &m, (struct mbuf **)NULL, &rcvflg); + } while (error == EWOULDBLOCK || error == EINTR || + error == ERESTART); + if (error) + break; + if (auio.uio_resid > 0) { + SMBERROR("packet is shorter than expected\n"); + error = EPIPE; + break; + } + if (nbp->nbp_state == NBST_SESSION && + rpcode == NB_SSN_MESSAGE) + break; + NBDEBUG("non-session packet %x\n", rpcode); + if (m) + m_freem(m); + } + if (error) { + if (m) + m_freem(m); + return error; + } + if (mpp) + *mpp = m; + else + m_freem(m); + *lenp = len; + *rpcodep = rpcode; + return 0; +} + +/* + * SMB transport interface + */ +static int +smb_nbst_create(struct smb_vc *vcp, struct proc *p) +{ + struct nbpcb *nbp; + + MALLOC(nbp, struct nbpcb *, sizeof *nbp, M_NBDATA, M_WAITOK); + bzero(nbp, sizeof *nbp); + nbp->nbp_timo.tv_sec = 15; /* XXX: sysctl ? */ + nbp->nbp_state = NBST_CLOSED; + nbp->nbp_vc = vcp; + nbp->nbp_sndbuf = smb_tcpsndbuf; + nbp->nbp_rcvbuf = smb_tcprcvbuf; + vcp->vc_tdata = nbp; + return 0; +} + +static int +smb_nbst_done(struct smb_vc *vcp, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + if (nbp == NULL) + return ENOTCONN; + smb_nbst_disconnect(vcp, p); + if (nbp->nbp_laddr) + free(nbp->nbp_laddr, M_SONAME); + if (nbp->nbp_paddr) + free(nbp->nbp_paddr, M_SONAME); + free(nbp, M_NBDATA); + return 0; +} + +static int +smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + struct sockaddr_nb *snb; + int error, slen; + + NBDEBUG("\n"); + error = EINVAL; + do { + if (nbp->nbp_flags & NBF_LOCADDR) + break; + /* + * It is possible to create NETBIOS name in the kernel, + * but nothing prevents us to do it in the user space. + */ + if (sap == NULL) + break; + slen = sap->sa_len; + if (slen < NB_MINSALEN) + break; + snb = (struct sockaddr_nb*)dup_sockaddr(sap, 1); + if (snb == NULL) { + error = ENOMEM; + break; + } + nbp->nbp_laddr = snb; + nbp->nbp_flags |= NBF_LOCADDR; + error = 0; + } while(0); + return error; +} + +static int +smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + struct sockaddr_in sin; + struct sockaddr_nb *snb; + struct timespec ts1, ts2; + int error, slen; + + NBDEBUG("\n"); + if (nbp->nbp_tso != NULL) + return EISCONN; + if (nbp->nbp_laddr == NULL) + return EINVAL; + slen = sap->sa_len; + if (slen < NB_MINSALEN) + return EINVAL; + if (nbp->nbp_paddr) { + free(nbp->nbp_paddr, M_SONAME); + nbp->nbp_paddr = NULL; + } + snb = (struct sockaddr_nb*)dup_sockaddr(sap, 1); + if (snb == NULL) + return ENOMEM; + nbp->nbp_paddr = snb; + sin = snb->snb_addrin; + getnanotime(&ts1); + error = nb_connect_in(nbp, &sin, p); + if (error) + return error; + getnanotime(&ts2); + timespecsub(&ts2, &ts1); + if (ts2.tv_sec == 0 && ts2.tv_sec == 0) + ts2.tv_sec = 1; + nbp->nbp_timo = ts2; + timespecadd(&nbp->nbp_timo, &ts2); + timespecadd(&nbp->nbp_timo, &ts2); + timespecadd(&nbp->nbp_timo, &ts2); /* * 4 */ + error = nbssn_rq_request(nbp, p); + if (error) + smb_nbst_disconnect(vcp, p); + return error; +} + +static int +smb_nbst_disconnect(struct smb_vc *vcp, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + struct socket *so; + + if (nbp == NULL || nbp->nbp_tso == NULL) + return ENOTCONN; + if ((so = nbp->nbp_tso) != NULL) { + nbp->nbp_flags &= ~NBF_CONNECTED; + nbp->nbp_tso = (struct socket *)NULL; + soshutdown(so, 2); + soclose(so); + } + if (nbp->nbp_state != NBST_RETARGET) { + nbp->nbp_state = NBST_CLOSED; + } + return 0; +} + +static int +smb_nbst_send(struct smb_vc *vcp, struct mbuf *m0, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + int error; + + if (nbp->nbp_state != NBST_SESSION) { + error = ENOTCONN; + goto abort; + } + M_PREPEND(m0, 4, M_WAITOK); + if (m0 == NULL) + return ENOBUFS; + nb_sethdr(m0, NB_SSN_MESSAGE, m_fixhdr(m0) - 4); + error = nb_sosend(nbp->nbp_tso, m0, 0, p); + return error; +abort: + if (m0) + m_freem(m0); + return error; +} + + +static int +smb_nbst_recv(struct smb_vc *vcp, struct mbuf **mpp, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + u_int8_t rpcode; + int error, rplen; + + nbp->nbp_flags |= NBF_RECVLOCK; + error = nbssn_recv(nbp, mpp, &rplen, &rpcode, p); + nbp->nbp_flags &= ~NBF_RECVLOCK; + return error; +} + +static void +smb_nbst_timo(struct smb_vc *vcp) +{ + return; +} + +static void +smb_nbst_intr(struct smb_vc *vcp) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + if (nbp == NULL || nbp->nbp_tso == NULL) + return; + sorwakeup(nbp->nbp_tso); + sowwakeup(nbp->nbp_tso); +} + +static int +smb_nbst_getparam(struct smb_vc *vcp, int param, void *data) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + switch (param) { + case SMBTP_SNDSZ: + *(int*)data = nbp->nbp_sndbuf; + break; + case SMBTP_RCVSZ: + *(int*)data = nbp->nbp_rcvbuf; + break; + case SMBTP_TIMEOUT: + *(struct timespec*)data = nbp->nbp_timo; + break; + default: + return EINVAL; + } + return 0; +} + +static int +smb_nbst_setparam(struct smb_vc *vcp, int param, void *data) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + switch (param) { + case SMBTP_SELECTID: + nbp->nbp_selectid = data; + break; + default: + return EINVAL; + } + return 0; +} + +/* + * Check for fatal errors + */ +static int +smb_nbst_fatal(struct smb_vc *vcp, int error) +{ + switch (error) { + case ENOTCONN: + case ENETRESET: + case ECONNABORTED: + return 1; + } + return 0; +} + + +struct smb_tran_desc smb_tran_nbtcp_desc = { + SMBT_NBTCP, + smb_nbst_create, smb_nbst_done, + smb_nbst_bind, smb_nbst_connect, smb_nbst_disconnect, + smb_nbst_send, smb_nbst_recv, + smb_nbst_timo, smb_nbst_intr, + smb_nbst_getparam, smb_nbst_setparam, + smb_nbst_fatal +}; + diff --git a/sys/netsmb/smb_trantcp.h b/sys/netsmb/smb_trantcp.h new file mode 100644 index 000000000000..6c625faa57c4 --- /dev/null +++ b/sys/netsmb/smb_trantcp.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2000-2001, 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 _NETSMB_SMB_TRANTCP_H_ +#define _NETSMB_SMB_TRANTCP_H_ + +#ifdef _KERNEL + +#ifdef NB_DEBUG +#define NBDEBUG(format, args...) printf("%s(%d): "format, \ + __FUNCTION__ , __LINE__ ,## args) +#else +#define NBDEBUG(format, args...) +#endif + +enum nbstate { + NBST_CLOSED, + NBST_RQSENT, + NBST_SESSION, + NBST_RETARGET, + NBST_REFUSED +}; + + +/* + * socket specific data + */ +struct nbpcb { + struct smb_vc * nbp_vc; + struct socket * nbp_tso; /* transport socket */ + struct sockaddr_nb *nbp_laddr; /* local address */ + struct sockaddr_nb *nbp_paddr; /* peer address */ + + int nbp_flags; +#define NBF_LOCADDR 0x0001 /* has local addr */ +#define NBF_CONNECTED 0x0002 +#define NBF_RECVLOCK 0x0004 + + enum nbstate nbp_state; + struct timespec nbp_timo; + int nbp_sndbuf; + int nbp_rcvbuf; + void * nbp_selectid; + +/* LIST_ENTRY(nbpcb) nbp_link;*/ +}; + +/* + * Nominal space allocated per a NETBIOS socket. + */ +#define NB_SNDQ (10 * 1024) +#define NB_RCVQ (20 * 1024) + +extern struct smb_tran_desc smb_tran_nbtcp_desc; + +#endif /* _KERNEL */ + +#endif /* !_NETSMB_SMB_TRANTCP_H_ */ diff --git a/sys/netsmb/smb_usr.c b/sys/netsmb/smb_usr.c new file mode 100644 index 000000000000..275917357a54 --- /dev/null +++ b/sys/netsmb/smb_usr.c @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2000-2001 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +/* + * helpers for nsmb device. Can be moved to the smb_dev.c file. + */ +static void smb_usr_vcspec_free(struct smb_vcspec *spec); + +static int +smb_usr_vc2spec(struct smbioc_ossn *dp, struct smb_vcspec *spec) +{ + int flags = 0; + + bzero(spec, sizeof(*spec)); + if (dp->ioc_user[0] == 0) + return EINVAL; + if (dp->ioc_server == NULL) + return EINVAL; + if (dp->ioc_localcs[0] == 0) { + SMBERROR("no local charset ?\n"); + return EINVAL; + } + + spec->sap = smb_memdupin(dp->ioc_server, dp->ioc_svlen); + if (spec->sap == NULL) + return ENOMEM; + if (dp->ioc_local) { + spec->lap = smb_memdupin(dp->ioc_local, dp->ioc_lolen); + if (spec->lap == NULL) { + smb_usr_vcspec_free(spec); + return ENOMEM; + } + } + spec->srvname = dp->ioc_srvname; + spec->pass = dp->ioc_password; + spec->domain = dp->ioc_workgroup; + spec->username = dp->ioc_user; + spec->mode = dp->ioc_mode; + spec->rights = dp->ioc_rights; + spec->owner = dp->ioc_owner; + spec->group = dp->ioc_group; + spec->localcs = dp->ioc_localcs; + spec->servercs = dp->ioc_servercs; + if (dp->ioc_opt & SMBVOPT_PRIVATE) + flags |= SMBV_PRIVATE; + if (dp->ioc_opt & SMBVOPT_SINGLESHARE) + flags |= SMBV_PRIVATE | SMBV_SINGLESHARE; + spec->flags = flags; + return 0; +} + +static void +smb_usr_vcspec_free(struct smb_vcspec *spec) +{ + if (spec->sap) + smb_memfree(spec->sap); + if (spec->lap) + smb_memfree(spec->lap); +} + +static int +smb_usr_share2spec(struct smbioc_oshare *dp, struct smb_sharespec *spec) +{ + bzero(spec, sizeof(*spec)); + spec->mode = dp->ioc_mode; + spec->rights = dp->ioc_rights; + spec->owner = dp->ioc_owner; + spec->group = dp->ioc_group; + spec->name = dp->ioc_share; + spec->stype = dp->ioc_stype; + spec->pass = dp->ioc_password; + return 0; +} + +int +smb_usr_lookup(struct smbioc_lookup *dp, struct smb_cred *scred, + struct smb_vc **vcpp, struct smb_share **sspp) +{ + struct smb_vc *vcp = NULL; + struct smb_vcspec vspec; + struct smb_sharespec sspec, *sspecp = NULL; + int error; + + if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE) + return EINVAL; + error = smb_usr_vc2spec(&dp->ioc_ssn, &vspec); + if (error) + return error; + if (dp->ioc_flags & SMBLK_CREATE) + vspec.flags |= SMBV_CREATE; + + if (dp->ioc_level >= SMBL_SHARE) { + error = smb_usr_share2spec(&dp->ioc_sh, &sspec); + if (error) + goto out; + sspecp = &sspec; + } + error = smb_sm_lookup(&vspec, sspecp, scred, &vcp); + if (error == 0) { + *vcpp = vcp; + *sspp = vspec.ssp; + } +out: + smb_usr_vcspec_free(&vspec); + return error; +} + +/* + * Connect to the resource specified by smbioc_ossn structure. + * It may either find an existing connection or try to establish a new one. + * If no errors occured smb_vc returned locked and referenced. + */ +int +smb_usr_opensession(struct smbioc_ossn *dp, struct smb_cred *scred, + struct smb_vc **vcpp) +{ + struct smb_vc *vcp = NULL; + struct smb_vcspec vspec; + int error; + + error = smb_usr_vc2spec(dp, &vspec); + if (error) + return error; + if (dp->ioc_opt & SMBVOPT_CREATE) + vspec.flags |= SMBV_CREATE; + + error = smb_sm_lookup(&vspec, NULL, scred, &vcp); + smb_usr_vcspec_free(&vspec); + return error; +} + +int +smb_usr_openshare(struct smb_vc *vcp, struct smbioc_oshare *dp, + struct smb_cred *scred, struct smb_share **sspp) +{ + struct smb_share *ssp; + struct smb_sharespec shspec; + int error; + + error = smb_usr_share2spec(dp, &shspec); + if (error) + return error; + error = smb_vc_lookupshare(vcp, &shspec, scred, &ssp); + if (error == 0) { + *sspp = ssp; + return 0; + } + if ((dp->ioc_opt & SMBSOPT_CREATE) == 0) + return error; + error = smb_share_create(vcp, &shspec, scred, &ssp); + if (error) + return error; + error = smb_smb_treeconnect(ssp, scred); + if (error) { + smb_share_put(ssp, scred); + } else + *sspp = ssp; + return error; +} + +int +smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *dp, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc; + u_int16_t bc; + int error; + + switch (dp->ioc_cmd) { + case SMB_COM_TRANSACTION2: + case SMB_COM_TRANSACTION2_SECONDARY: + case SMB_COM_CLOSE_AND_TREE_DISC: + case SMB_COM_TREE_CONNECT: + case SMB_COM_TREE_DISCONNECT: + case SMB_COM_NEGOTIATE: + case SMB_COM_SESSION_SETUP_ANDX: + case SMB_COM_LOGOFF_ANDX: + case SMB_COM_TREE_CONNECT_ANDX: + return EPERM; + } + error = smb_rq_init(rqp, SSTOCP(ssp), dp->ioc_cmd, scred); + if (error) + return error; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + error = mb_put_mem(mbp, dp->ioc_twords, dp->ioc_twc * 2, MB_MUSER); + if (error) + goto bad; + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + error = mb_put_mem(mbp, dp->ioc_tbytes, dp->ioc_tbc, MB_MUSER); + if (error) + goto bad; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) + goto bad; + mdp = &rqp->sr_rp; + md_get_uint8(mdp, &wc); + dp->ioc_rwc = wc; + wc *= 2; + if (wc > dp->ioc_rpbufsz) { + error = EBADRPC; + goto bad; + } + error = md_get_mem(mdp, dp->ioc_rpbuf, wc, MB_MUSER); + if (error) + goto bad; + md_get_uint16le(mdp, &bc); + if ((wc + bc) > dp->ioc_rpbufsz) { + error = EBADRPC; + goto bad; + } + dp->ioc_rbc = bc; + error = md_get_mem(mdp, dp->ioc_rpbuf + wc, bc, MB_MUSER); +bad: + dp->ioc_errclass = rqp->sr_errclass; + dp->ioc_serror = rqp->sr_serror; + dp->ioc_error = rqp->sr_error; + smb_rq_done(rqp); + return error; + +} + +static int +smb_cpdatain(struct mbchain *mbp, int len, caddr_t data) +{ + int error; + + if (len == 0) + return 0; + error = mb_init(mbp); + if (error) + return error; + return mb_put_mem(mbp, data, len, MB_MUSER); +} + +int +smb_usr_t2request(struct smb_share *ssp, struct smbioc_t2rq *dp, + struct smb_cred *scred) +{ + struct smb_t2rq t2, *t2p = &t2; + struct mdchain *mdp; + int error, len; + + if (dp->ioc_tparamcnt > 0xffff || dp->ioc_tdatacnt > 0xffff || + dp->ioc_setupcnt > 3) + return EINVAL; + error = smb_t2_init(t2p, SSTOCP(ssp), dp->ioc_setup[0], scred); + if (error) + return error; + len = t2p->t2_setupcount = dp->ioc_setupcnt; + if (len > 1) + t2p->t2_setupdata = dp->ioc_setup; + if (dp->ioc_name) { + t2p->t_name = smb_strdupin(dp->ioc_name, 128); + if (t2p->t_name == NULL) { + error = ENOMEM; + goto bad; + } + } + t2p->t2_maxscount = 0; + t2p->t2_maxpcount = dp->ioc_rparamcnt; + t2p->t2_maxdcount = dp->ioc_rdatacnt; + error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_tparamcnt, dp->ioc_tparam); + if (error) + goto bad; + error = smb_cpdatain(&t2p->t2_tdata, dp->ioc_tdatacnt, dp->ioc_tdata); + if (error) + goto bad; + error = smb_t2_request(t2p); + if (error) + goto bad; + mdp = &t2p->t2_rparam; + if (mdp->md_top) { + len = m_fixhdr(mdp->md_top); + if (len > dp->ioc_rparamcnt) { + error = EMSGSIZE; + goto bad; + } + dp->ioc_rparamcnt = len; + error = md_get_mem(mdp, dp->ioc_rparam, len, MB_MUSER); + if (error) + goto bad; + } else + dp->ioc_rparamcnt = 0; + mdp = &t2p->t2_rdata; + if (mdp->md_top) { + len = m_fixhdr(mdp->md_top); + if (len > dp->ioc_rdatacnt) { + error = EMSGSIZE; + goto bad; + } + dp->ioc_rdatacnt = len; + error = md_get_mem(mdp, dp->ioc_rdata, len, MB_MUSER); + } else + dp->ioc_rdatacnt = 0; +bad: + if (t2p->t_name) + smb_strfree(t2p->t_name); + smb_t2_done(t2p); + return error; +} diff --git a/sys/sys/md4.h b/sys/sys/md4.h new file mode 100644 index 000000000000..5a648c87f63a --- /dev/null +++ b/sys/sys/md4.h @@ -0,0 +1,47 @@ +/* MD4.H - header file for MD4C.C + * $FreeBSD$ + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD4 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD4 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + */ + +#ifndef _MD4_H_ +#define _MD4_H_ +/* MD4 context. */ +typedef struct MD4Context { + u_int32_t state[4]; /* state (ABCD) */ + u_int32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD4_CTX; + +#include + +__BEGIN_DECLS +void MD4Init(MD4_CTX *); +void MD4Update(MD4_CTX *, const unsigned char *, unsigned int); +void MD4Pad(MD4_CTX *); +void MD4Final(unsigned char [16], MD4_CTX *); +char * MD4End(MD4_CTX *, char *); +char * MD4File(const char *, char *); +char * MD4Data(const unsigned char *, unsigned int, char *); +__END_DECLS + +#endif /* _MD4_H_ */