Import kernel part of SMB/CIFS requester.

Add smbfs(CIFS) filesystem.

Userland part will be in the ports tree for a while.

Obtained from:	smbfs-1.3.7-dev package.
This commit is contained in:
Boris Popov 2001-04-10 07:59:06 +00:00
parent 43d97995d8
commit 681a5bbef2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=75374
30 changed files with 11983 additions and 0 deletions

View File

@ -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

View File

@ -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
# -------------------------------

112
sys/fs/smbfs/smbfs.h Normal file
View File

@ -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_ */

672
sys/fs/smbfs/smbfs_io.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/resourcevar.h> /* defines plimit structure in proc struct */
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/dirent.h>
#include <sys/signalvar.h>
#include <sys/sysctl.h>
#include <vm/vm.h>
#if __FreeBSD_version < 400000
#include <vm/vm_prot.h>
#endif
#include <vm/vm_page.h>
#include <vm/vm_extern.h>
#include <vm/vm_object.h>
#include <vm/vm_pager.h>
#include <vm/vnode_pager.h>
/*
#include <sys/ioccom.h>
*/
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <fs/smbfs/smbfs.h>
#include <fs/smbfs/smbfs_node.h>
#include <fs/smbfs/smbfs_subr.h>
/*#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);
}

415
sys/fs/smbfs/smbfs_node.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
/*#include <vm/vm_page.h>
#include <vm/vm_object.h>*/
#include <sys/queue.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <fs/smbfs/smbfs.h>
#include <fs/smbfs/smbfs_node.h>
#include <fs/smbfs/smbfs_subr.h>
#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;
}

100
sys/fs/smbfs/smbfs_node.h Normal file
View File

@ -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_ */

1273
sys/fs/smbfs/smbfs_smb.c Normal file

File diff suppressed because it is too large Load Diff

326
sys/fs/smbfs/smbfs_subr.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <machine/clock.h>
#include <sys/time.h>
#include <sys/vnode.h>
#include <sys/sysctl.h>
#include <sys/iconv.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb_dev.h>
#include <fs/smbfs/smbfs.h>
#include <fs/smbfs/smbfs_node.h>
#include <fs/smbfs/smbfs_subr.h>
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;
}

187
sys/fs/smbfs/smbfs_subr.h Normal file
View File

@ -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_ */

513
sys/fs/smbfs/smbfs_vfsops.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/malloc.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_dev.h>
#include <fs/smbfs/smbfs.h>
#include <fs/smbfs/smbfs_node.h>
#include <fs/smbfs/smbfs_subr.h>
int smbfs_debuglevel = 0;
static int smbfs_version = SMBFS_VERSION;
#ifdef SMBFS_USEZONE
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_zone.h>
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 */

1340
sys/fs/smbfs/smbfs_vnops.c Normal file

File diff suppressed because it is too large Load Diff

285
sys/kern/md4c.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/md4.h>
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);
}

138
sys/netsmb/netbios.h Normal file
View File

@ -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 <netinet/in.h>
#endif
#ifndef _NETIPX_IPX_H_
#include <netipx/ipx.h>
#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_ */

388
sys/netsmb/smb.h Normal file
View File

@ -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_ */

874
sys/netsmb/smb_conn.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/sysctl.h>
#include <sys/socketvar.h>
#include <sys/iconv.h>
#include <netsmb/smb.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_tran.h>
#include <netsmb/smb_trantcp.h>
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;
}

464
sys/netsmb/smb_conn.h Normal file
View File

@ -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 <netinet/in.h>
#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 <sys/lock.h>
#include <netsmb/smb_subr.h>
#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 */

146
sys/netsmb/smb_crypt.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/md4.h>
#include <sys/iconv.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_dev.h>
#include "opt_netsmb.h"
#ifdef NETSMBCRYPTO
#include <crypto/des/des.h>
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
}

448
sys/netsmb/smb_dev.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/ioccom.h>
#include <sys/malloc.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/poll.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <net/if.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_dev.h>
#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;
}

199
sys/netsmb/smb_dev.h Normal file
View File

@ -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 <sys/lock.h>
#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_ */

709
sys/netsmb/smb_iod.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/unistd.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb_tran.h>
#include <netsmb/smb_trantcp.h>
#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;
}

752
sys/netsmb/smb_rq.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/mbuf.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_tran.h>
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;
}

151
sys/netsmb/smb_rq.h Normal file
View File

@ -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 <sys/mchain.h>
#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_ */

660
sys/netsmb/smb_smb.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/iconv.h>
#include <netsmb/smb.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_tran.h>
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;
}

359
sys/netsmb/smb_subr.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <sys/signalvar.h>
#include <sys/mbuf.h>
#include <sys/iconv.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb_subr.h>
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);
}

198
sys/netsmb/smb_subr.h Normal file
View File

@ -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 <sys/lock.h>
#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 <sys/mutex.h>
#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_ */

89
sys/netsmb/smb_tran.h Normal file
View File

@ -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 <sys/socket.h>
/*
* 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_ */

672
sys/netsmb/smb_trantcp.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/poll.h>
#include <sys/uio.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/mchain.h>
#include <netsmb/netbios.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_tran.h>
#include <netsmb/smb_trantcp.h>
#include <netsmb/smb_subr.h>
#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
};

88
sys/netsmb/smb_trantcp.h Normal file
View File

@ -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_ */

355
sys/netsmb/smb_usr.c Normal file
View File

@ -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 <sys/param.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/iconv.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_dev.h>
/*
* 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;
}

47
sys/sys/md4.h Normal file
View File

@ -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 <sys/cdefs.h>
__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_ */