From a414f03f5d87ade219aa2e4bcd0830eceaee6bd3 Mon Sep 17 00:00:00 2001 From: bp Date: Tue, 10 Apr 2001 07:59:06 +0000 Subject: 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. --- sys/fs/smbfs/smbfs.h | 112 ++++ sys/fs/smbfs/smbfs_io.c | 672 ++++++++++++++++++++++ sys/fs/smbfs/smbfs_node.c | 415 ++++++++++++++ sys/fs/smbfs/smbfs_node.h | 100 ++++ sys/fs/smbfs/smbfs_smb.c | 1273 ++++++++++++++++++++++++++++++++++++++++ sys/fs/smbfs/smbfs_subr.c | 326 +++++++++++ sys/fs/smbfs/smbfs_subr.h | 187 ++++++ sys/fs/smbfs/smbfs_vfsops.c | 513 +++++++++++++++++ sys/fs/smbfs/smbfs_vnops.c | 1340 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 4938 insertions(+) create mode 100644 sys/fs/smbfs/smbfs.h create mode 100644 sys/fs/smbfs/smbfs_io.c create mode 100644 sys/fs/smbfs/smbfs_node.c create mode 100644 sys/fs/smbfs/smbfs_node.h create mode 100644 sys/fs/smbfs/smbfs_smb.c create mode 100644 sys/fs/smbfs/smbfs_subr.c create mode 100644 sys/fs/smbfs/smbfs_subr.h create mode 100644 sys/fs/smbfs/smbfs_vfsops.c create mode 100644 sys/fs/smbfs/smbfs_vnops.c (limited to 'sys/fs') diff --git a/sys/fs/smbfs/smbfs.h b/sys/fs/smbfs/smbfs.h new file mode 100644 index 0000000..0b9d88b --- /dev/null +++ b/sys/fs/smbfs/smbfs.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _SMBFS_SMBFS_H_ +#define _SMBFS_SMBFS_H_ + +#define VT_SMBFS 24 + +#define SMBFS_VERMAJ 1 +#define SMBFS_VERMIN 1012 +#define SMBFS_VERSION (SMBFS_VERMAJ*100000 + SMBFS_VERMIN) +#define SMBFS_VFSNAME "smbfs" + +/* Values for flags */ +#define SMBFS_MOUNT_SOFT 0x0001 +#define SMBFS_MOUNT_INTR 0x0002 +#define SMBFS_MOUNT_STRONG 0x0004 +#define SMBFS_MOUNT_HAVE_NLS 0x0008 +#define SMBFS_MOUNT_NO_LONG 0x0010 + +#define SMBFS_MAXPATHCOMP 256 /* maximum number of path components */ + + +/* Layout of the mount control block for a netware file system. */ +struct smbfs_args { + int version; + int dev; + u_int flags; + char mount_point[MAXPATHLEN]; + u_char root_path[512+1]; + uid_t uid; + gid_t gid; + mode_t file_mode; + mode_t dir_mode; + int caseopt; +}; + +#ifdef _KERNEL + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_SMBFSMNT); +#endif + +#ifndef VI_LOCK +#define VI_LOCK(vp) smb_sl_lock(&(vp)->v_interlock) +#define VI_UNLOCK(vp) smb_sl_unlock(&(vp)->v_interlock) +#endif + +struct smbnode; +struct smb_share; +struct u_cred; +struct vop_ioctl_args; +struct buf; + +struct smbmount { + struct smbfs_args sm_args; + struct mount * sm_mp; + struct smbnode * sm_root; + struct ucred * sm_owner; + int sm_flags; + long sm_nextino; + struct smb_share * sm_share; +/* struct simplelock sm_npslock;*/ + struct smbnode * sm_npstack[SMBFS_MAXPATHCOMP]; + int sm_caseopt; + struct lock sm_hashlock; + LIST_HEAD(smbnode_hashhead, smbnode) *sm_hash; + u_long sm_hashlen; +}; + +#define VFSTOSMBFS(mp) ((struct smbmount *)((mp)->mnt_data)) +#define SMBFSTOVFS(smp) ((struct mount *)((smp)->sm_mp)) +#define VTOVFS(vp) ((vp)->v_mount) +#define VTOSMBFS(vp) (VFSTOSMBFS(VTOVFS(vp))) + +int smbfs_ioctl(struct vop_ioctl_args *ap); +int smbfs_doio(struct buf *bp, struct ucred *cr, struct proc *p); +int smbfs_vinvalbuf(struct vnode *vp, int flags, struct ucred *cred, + struct proc *p, int intrflg); +#endif /* KERNEL */ + +#endif /* _SMBFS_SMBFS_H_ */ diff --git a/sys/fs/smbfs/smbfs_io.c b/sys/fs/smbfs/smbfs_io.c new file mode 100644 index 0000000..b275d74 --- /dev/null +++ b/sys/fs/smbfs/smbfs_io.c @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ +#include +#include +#include /* defines plimit structure in proc struct */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if __FreeBSD_version < 400000 +#include +#endif +#include +#include +#include +#include +#include +/* +#include +*/ +#include +#include +#include + +#include +#include +#include + +/*#define SMBFS_RWGENERIC*/ + +extern int smbfs_pbuf_freecnt; + +static int smbfs_fastlookup = 1; + +extern struct linker_set sysctl_vfs_smbfs; + +SYSCTL_DECL(_vfs_smbfs); +SYSCTL_INT(_vfs_smbfs, OID_AUTO, fastlookup, CTLFLAG_RW, &smbfs_fastlookup, 0, ""); + + +#define DE_SIZE (sizeof(struct dirent)) + +static int +smbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred) +{ + struct dirent de; + struct componentname cn; + struct smb_cred scred; + struct smbfs_fctx *ctx; + struct vnode *newvp; + struct smbnode *np = VTOSMB(vp); + int error/*, *eofflag = ap->a_eofflag*/; + long offset, limit; + + np = VTOSMB(vp); + SMBVDEBUG("dirname='%s'\n", np->n_name); + smb_makescred(&scred, uio->uio_procp, cred); + offset = uio->uio_offset / DE_SIZE; /* offset in the directory */ + limit = uio->uio_resid / DE_SIZE; + if (uio->uio_resid < DE_SIZE || uio->uio_offset < 0) + return EINVAL; + while (limit && offset < 2) { + limit--; + bzero((caddr_t)&de, DE_SIZE); + de.d_reclen = DE_SIZE; + de.d_fileno = (offset == 0) ? np->n_ino : + (np->n_parent ? np->n_parent->n_ino : 2); + if (de.d_fileno == 0) + de.d_fileno = 0x7ffffffd + offset; + de.d_namlen = offset + 1; + de.d_name[0] = '.'; + de.d_name[1] = '.'; + de.d_name[offset + 1] = '\0'; + de.d_type = DT_DIR; + error = uiomove((caddr_t)&de, DE_SIZE, uio); + if (error) + return error; + offset++; + uio->uio_offset += DE_SIZE; + } + if (limit == 0) + return 0; + if (offset != np->n_dirofs || np->n_dirseq == NULL) { + SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs); + if (np->n_dirseq) { + smbfs_findclose(np->n_dirseq, &scred); + np->n_dirseq = NULL; + } + np->n_dirofs = 2; + error = smbfs_findopen(np, "*", 1, + SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, + &scred, &ctx); + if (error) { + SMBVDEBUG("can not open search, error = %d", error); + return error; + } + np->n_dirseq = ctx; + } else + ctx = np->n_dirseq; + while (np->n_dirofs < offset) { + error = smbfs_findnext(ctx, offset - np->n_dirofs++, &scred); + if (error) { + smbfs_findclose(np->n_dirseq, &scred); + np->n_dirseq = NULL; + return error == ENOENT ? 0 : error; + } + } + error = 0; + for (; limit; limit--, offset++) { + error = smbfs_findnext(ctx, limit, &scred); + if (error) + break; + np->n_dirofs++; + bzero((caddr_t)&de, DE_SIZE); + de.d_reclen = DE_SIZE; + de.d_fileno = ctx->f_attr.fa_ino; + de.d_type = (ctx->f_attr.fa_attr & SMB_FA_DIR) ? DT_DIR : DT_REG; + de.d_namlen = ctx->f_nmlen; + bcopy(ctx->f_name, de.d_name, de.d_namlen); + de.d_name[de.d_namlen] = '\0'; + if (smbfs_fastlookup) { + error = smbfs_nget(vp->v_mount, vp, ctx->f_name, + ctx->f_nmlen, &ctx->f_attr, &newvp); + if (!error) { + cn.cn_nameptr = de.d_name; + cn.cn_namelen = de.d_namlen; + cache_enter(vp, newvp, &cn); + vput(newvp); + } + } + error = uiomove((caddr_t)&de, DE_SIZE, uio); + if (error) + break; + } + if (error == ENOENT) + error = 0; + uio->uio_offset = offset * DE_SIZE; + return error; +} + +int +smbfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred) +{ + struct smbmount *smp = VFSTOSMBFS(vp->v_mount); + struct smbnode *np = VTOSMB(vp); + struct proc *p; + struct vattr vattr; + struct smb_cred scred; + int error, lks; + + if (vp->v_type != VREG && vp->v_type != VDIR) { + SMBFSERR("vn types other than VREG or VDIR are unsupported !\n"); + return EIO; + } + if (uiop->uio_resid == 0) + return 0; + if (uiop->uio_offset < 0) + return EINVAL; +/* if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize) + return EFBIG;*/ + p = uiop->uio_procp; + if (vp->v_type == VDIR) { + lks = LK_EXCLUSIVE;/*lockstatus(&vp->v_lock, p);*/ + if (lks == LK_SHARED) + vn_lock(vp, LK_UPGRADE | LK_RETRY, p); + error = smbfs_readvdir(vp, uiop, cred); + if (lks == LK_SHARED) + vn_lock(vp, LK_DOWNGRADE | LK_RETRY, p); + return error; + } + +/* biosize = SSTOCN(smp->sm_share)->sc_txmax;*/ + if (np->n_flag & NMODIFIED) { + smbfs_attr_cacheremove(vp); + error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) + return error; + np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; + } else { + error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) + return error; + if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) { + error = smbfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + if (error) + return error; + np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; + } + } + smb_makescred(&scred, p, cred); + return smb_read(smp->sm_share, np->n_fid, uiop, &scred); +} + +int +smbfs_writevnode(struct vnode *vp, struct uio *uiop, + struct ucred *cred, int ioflag) +{ + struct smbmount *smp = VTOSMBFS(vp); + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + struct proc *p; + int error = 0; + + if (vp->v_type != VREG) { + SMBERROR("vn types other than VREG unsupported !\n"); + return EIO; + } + SMBVDEBUG("ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); + if (uiop->uio_offset < 0) + return EINVAL; +/* if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize) + return (EFBIG);*/ + p = uiop->uio_procp; + if (ioflag & (IO_APPEND | IO_SYNC)) { + if (np->n_flag & NMODIFIED) { + smbfs_attr_cacheremove(vp); + error = smbfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + if (error) + return error; + } + if (ioflag & IO_APPEND) { +#if notyet + /* + * File size can be changed by another client + */ + smbfs_attr_cacheremove(vp); + error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) return (error); +#endif + uiop->uio_offset = np->n_size; + } + } + if (uiop->uio_resid == 0) + return 0; + if (p && uiop->uio_offset + uiop->uio_resid > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) { + psignal(p, SIGXFSZ); + return EFBIG; + } + smb_makescred(&scred, p, cred); + error = smb_write(smp->sm_share, np->n_fid, uiop, &scred); + SMBVDEBUG("after: ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); + if (!error) { + if (uiop->uio_offset > np->n_size) { + np->n_size = uiop->uio_offset; + vnode_pager_setsize(vp, np->n_size); + } + } + return error; +} + +/* + * Do an I/O operation to/from a cache block. + */ +int +smbfs_doio(struct buf *bp, struct ucred *cr, struct proc *p) +{ + struct vnode *vp = bp->b_vp; + struct smbmount *smp = VFSTOSMBFS(vp->v_mount); + struct smbnode *np = VTOSMB(vp); + struct uio uio, *uiop = &uio; + struct iovec io; + struct smb_cred scred; + int error = 0; + + uiop->uio_iov = &io; + uiop->uio_iovcnt = 1; + uiop->uio_segflg = UIO_SYSSPACE; + uiop->uio_procp = p; + + smb_makescred(&scred, p, cr); + + if (bp->b_iocmd == BIO_READ) { + io.iov_len = uiop->uio_resid = bp->b_bcount; + io.iov_base = bp->b_data; + uiop->uio_rw = UIO_READ; + switch (vp->v_type) { + case VREG: + uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE; + error = smb_read(smp->sm_share, np->n_fid, uiop, &scred); + if (error) + break; + if (uiop->uio_resid) { + int left = uiop->uio_resid; + int nread = bp->b_bcount - left; + if (left > 0) + bzero((char *)bp->b_data + nread, left); + } + break; + default: + printf("smbfs_doio: type %x unexpected\n",vp->v_type); + break; + }; + if (error) { + bp->b_error = error; + bp->b_ioflags |= BIO_ERROR; + } + } else { /* write */ + if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size) + bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE); + + if (bp->b_dirtyend > bp->b_dirtyoff) { + io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff; + uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; + io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; + uiop->uio_rw = UIO_WRITE; + bp->b_flags |= B_WRITEINPROG; + error = smb_write(smp->sm_share, np->n_fid, uiop, &scred); + bp->b_flags &= ~B_WRITEINPROG; + + /* + * For an interrupted write, the buffer is still valid + * and the write hasn't been pushed to the server yet, + * so we can't set BIO_ERROR and report the interruption + * by setting B_EINTR. For the B_ASYNC case, B_EINTR + * is not relevant, so the rpc attempt is essentially + * a noop. For the case of a V3 write rpc not being + * committed to stable storage, the block is still + * dirty and requires either a commit rpc or another + * write rpc with iomode == NFSV3WRITE_FILESYNC before + * the block is reused. This is indicated by setting + * the B_DELWRI and B_NEEDCOMMIT flags. + */ + if (error == EINTR + || (!error && (bp->b_flags & B_NEEDCOMMIT))) { + int s; + + s = splbio(); + bp->b_flags &= ~(B_INVAL|B_NOCACHE); + if ((bp->b_flags & B_ASYNC) == 0) + bp->b_flags |= B_EINTR; + if ((bp->b_flags & B_PAGING) == 0) { + bdirty(bp); + bp->b_flags &= ~B_DONE; + } + if ((bp->b_flags & B_ASYNC) == 0) + bp->b_flags |= B_EINTR; + splx(s); + } else { + if (error) { + bp->b_ioflags |= BIO_ERROR; + bp->b_error = error; + } + bp->b_dirtyoff = bp->b_dirtyend = 0; + } + } else { + bp->b_resid = 0; + bufdone(bp); + return 0; + } + } + bp->b_resid = uiop->uio_resid; + bufdone(bp); + return error; +} + +/* + * Vnode op for VM getpages. + * Wish wish .... get rid from multiple IO routines + */ +int +smbfs_getpages(ap) + struct vop_getpages_args /* { + struct vnode *a_vp; + vm_page_t *a_m; + int a_count; + int a_reqpage; + vm_ooffset_t a_offset; + } */ *ap; +{ +#ifdef SMBFS_RWGENERIC + return vnode_pager_generic_getpages(ap->a_vp, ap->a_m, ap->a_count, + ap->a_reqpage); +#else + int i, error, nextoff, size, toff, npages, count; + struct uio uio; + struct iovec iov; + vm_offset_t kva; + struct buf *bp; + struct vnode *vp; + struct proc *p; + struct ucred *cred; + struct smbmount *smp; + struct smbnode *np; + struct smb_cred scred; + vm_page_t *pages; + + vp = ap->a_vp; + p = curproc; /* XXX */ + cred = curproc->p_ucred; /* XXX */ + np = VTOSMB(vp); + smp = VFSTOSMBFS(vp->v_mount); + pages = ap->a_m; + count = ap->a_count; + + if (vp->v_object == NULL) { + printf("smbfs_getpages: called with non-merged cache vnode??\n"); + return VM_PAGER_ERROR; + } + smb_makescred(&scred, p, cred); + +#if __FreeBSD_version >= 400000 + bp = getpbuf(&smbfs_pbuf_freecnt); +#else + bp = getpbuf(); +#endif + npages = btoc(count); + kva = (vm_offset_t) bp->b_data; + pmap_qenter(kva, pages, npages); + + iov.iov_base = (caddr_t) kva; + iov.iov_len = count; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = IDX_TO_OFF(pages[0]->pindex); + uio.uio_resid = count; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_procp = p; + + error = smb_read(smp->sm_share, np->n_fid, &uio, &scred); + pmap_qremove(kva, npages); + +#if __FreeBSD_version >= 400000 + relpbuf(bp, &smbfs_pbuf_freecnt); +#else + relpbuf(bp); +#endif + + if (error && (uio.uio_resid == count)) { + printf("smbfs_getpages: error %d\n",error); + for (i = 0; i < npages; i++) { + if (ap->a_reqpage != i) + vnode_pager_freepage(pages[i]); + } + return VM_PAGER_ERROR; + } + + size = count - uio.uio_resid; + + for (i = 0, toff = 0; i < npages; i++, toff = nextoff) { + vm_page_t m; + nextoff = toff + PAGE_SIZE; + m = pages[i]; + + m->flags &= ~PG_ZERO; + + if (nextoff <= size) { + m->valid = VM_PAGE_BITS_ALL; + m->dirty = 0; + } else { + int nvalid = ((size + DEV_BSIZE - 1) - toff) & ~(DEV_BSIZE - 1); + vm_page_set_validclean(m, 0, nvalid); + } + + if (i != ap->a_reqpage) { + /* + * Whether or not to leave the page activated is up in + * the air, but we should put the page on a page queue + * somewhere (it already is in the object). Result: + * It appears that emperical results show that + * deactivating pages is best. + */ + + /* + * Just in case someone was asking for this page we + * now tell them that it is ok to use. + */ + if (!error) { + if (m->flags & PG_WANTED) + vm_page_activate(m); + else + vm_page_deactivate(m); + vm_page_wakeup(m); + } else { + vnode_pager_freepage(m); + } + } + } + return 0; +#endif /* SMBFS_RWGENERIC */ +} + +/* + * Vnode op for VM putpages. + * possible bug: all IO done in sync mode + * Note that vop_close always invalidate pages before close, so it's + * not necessary to open vnode. + */ +int +smbfs_putpages(ap) + struct vop_putpages_args /* { + struct vnode *a_vp; + vm_page_t *a_m; + int a_count; + int a_sync; + int *a_rtvals; + vm_ooffset_t a_offset; + } */ *ap; +{ + int error; + struct vnode *vp = ap->a_vp; + struct proc *p; + struct ucred *cred; + +#ifdef SMBFS_RWGENERIC + p = curproc; /* XXX */ + cred = p->p_ucred; /* XXX */ + VOP_OPEN(vp, FWRITE, cred, p); + error = vnode_pager_generic_putpages(ap->a_vp, ap->a_m, ap->a_count, + ap->a_sync, ap->a_rtvals); + VOP_CLOSE(vp, FWRITE, cred, p); + return error; +#else + struct uio uio; + struct iovec iov; + vm_offset_t kva; + struct buf *bp; + int i, npages, count; + int *rtvals; + struct smbmount *smp; + struct smbnode *np; + struct smb_cred scred; + vm_page_t *pages; + + p = curproc; /* XXX */ + cred = p->p_ucred; /* XXX */ +/* VOP_OPEN(vp, FWRITE, cred, p);*/ + np = VTOSMB(vp); + smp = VFSTOSMBFS(vp->v_mount); + pages = ap->a_m; + count = ap->a_count; + rtvals = ap->a_rtvals; + npages = btoc(count); + + for (i = 0; i < npages; i++) { + rtvals[i] = VM_PAGER_AGAIN; + } + +#if __FreeBSD_version >= 400000 + bp = getpbuf(&smbfs_pbuf_freecnt); +#else + bp = getpbuf(); +#endif + kva = (vm_offset_t) bp->b_data; + pmap_qenter(kva, pages, npages); + + iov.iov_base = (caddr_t) kva; + iov.iov_len = count; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = IDX_TO_OFF(pages[0]->pindex); + uio.uio_resid = count; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_WRITE; + uio.uio_procp = p; + SMBVDEBUG("ofs=%d,resid=%d\n",(int)uio.uio_offset, uio.uio_resid); + + smb_makescred(&scred, p, cred); + error = smb_write(smp->sm_share, np->n_fid, &uio, &scred); +/* VOP_CLOSE(vp, FWRITE, cred, p);*/ + SMBVDEBUG("paged write done: %d\n", error); + + pmap_qremove(kva, npages); +#if __FreeBSD_version >= 400000 + relpbuf(bp, &smbfs_pbuf_freecnt); +#else + relpbuf(bp); +#endif + + if (!error) { + int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE; + for (i = 0; i < nwritten; i++) { + rtvals[i] = VM_PAGER_OK; + pages[i]->dirty = 0; + } + } + return rtvals[0]; +#endif /* SMBFS_RWGENERIC */ +} + +/* + * Flush and invalidate all dirty buffers. If another process is already + * doing the flush, just wait for completion. + */ +int +smbfs_vinvalbuf(vp, flags, cred, p, intrflg) + struct vnode *vp; + int flags; + struct ucred *cred; + struct proc *p; + int intrflg; +{ + struct smbnode *np = VTOSMB(vp); + int error = 0, slpflag, slptimeo; + + if (vp->v_flag & VXLOCK) + return 0; + if (intrflg) { + slpflag = PCATCH; + slptimeo = 2 * hz; + } else { + slpflag = 0; + slptimeo = 0; + } + while (np->n_flag & NFLUSHINPROG) { + np->n_flag |= NFLUSHWANT; + error = tsleep((caddr_t)&np->n_flag, PRIBIO + 2, "smfsvinv", slptimeo); + error = smb_proc_intr(p); + if (error == EINTR && intrflg) + return EINTR; + } + np->n_flag |= NFLUSHINPROG; + error = vinvalbuf(vp, flags, cred, p, slpflag, 0); + while (error) { + if (intrflg && (error == ERESTART || error == EINTR)) { + np->n_flag &= ~NFLUSHINPROG; + if (np->n_flag & NFLUSHWANT) { + np->n_flag &= ~NFLUSHWANT; + wakeup((caddr_t)&np->n_flag); + } + return EINTR; + } + error = vinvalbuf(vp, flags, cred, p, slpflag, 0); + } + np->n_flag &= ~(NMODIFIED | NFLUSHINPROG); + if (np->n_flag & NFLUSHWANT) { + np->n_flag &= ~NFLUSHWANT; + wakeup((caddr_t)&np->n_flag); + } + return (error); +} diff --git a/sys/fs/smbfs/smbfs_node.c b/sys/fs/smbfs/smbfs_node.c new file mode 100644 index 0000000..0d98605 --- /dev/null +++ b/sys/fs/smbfs/smbfs_node.c @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include +#include */ +#include + +#include +#include +#include + +#include +#include +#include + +#define SMBFS_NOHASH(smp, hval) (&(smp)->sm_hash[(hval) & (smp)->sm_hashlen]) +#define smbfs_hash_lock(smp, p) lockmgr(&smp->sm_hashlock, LK_EXCLUSIVE, NULL, p) +#define smbfs_hash_unlock(smp, p) lockmgr(&smp->sm_hashlock, LK_RELEASE, NULL, p) + + +extern vop_t **smbfs_vnodeop_p; + +MALLOC_DEFINE(M_SMBNODE, "SMBFS node", "SMBFS vnode private part"); +static MALLOC_DEFINE(M_SMBNODENAME, "SMBFS nname", "SMBFS node name"); + +int smbfs_hashprint(struct mount *mp); + +#if 0 +extern struct linker_set sysctl_vfs_smbfs; +#ifdef SYSCTL_DECL +SYSCTL_DECL(_vfs_smbfs); +#endif +SYSCTL_PROC(_vfs_smbfs, OID_AUTO, vnprint, CTLFLAG_WR|CTLTYPE_OPAQUE, + NULL, 0, smbfs_hashprint, "S,vnlist", "vnode hash"); +#endif + +#define FNV_32_PRIME ((u_int32_t) 0x01000193UL) +#define FNV1_32_INIT ((u_int32_t) 33554467UL) + +u_int32_t +smbfs_hash(const u_char *name, int nmlen) +{ + u_int32_t v; + + for (v = FNV1_32_INIT; nmlen; name++, nmlen--) { + v *= FNV_32_PRIME; + v ^= (u_int32_t)*name; + } + return v; +} + +int +smbfs_hashprint(struct mount *mp) +{ + struct smbmount *smp = VFSTOSMBFS(mp); + struct smbnode_hashhead *nhpp; + struct smbnode *np; + int i; + + for(i = 0; i <= smp->sm_hashlen; i++) { + nhpp = &smp->sm_hash[i]; + LIST_FOREACH(np, nhpp, n_hash) + vprint(NULL, SMBTOV(np)); + } + return 0; +} + +static char * +smbfs_name_alloc(const u_char *name, int nmlen) +{ + u_char *cp; + + nmlen++; +#ifdef SMBFS_NAME_DEBUG + cp = malloc(nmlen + 2 + sizeof(int), M_SMBNODENAME, M_WAITOK); + *(int*)cp = nmlen; + cp += sizeof(int); + cp[0] = 0xfc; + cp++; + bcopy(name, cp, nmlen - 1); + cp[nmlen] = 0xfe; +#else + cp = malloc(nmlen, M_SMBNODENAME, M_WAITOK); + bcopy(name, cp, nmlen - 1); +#endif + cp[nmlen - 1] = 0; + return cp; +} + +static void +smbfs_name_free(u_char *name) +{ +#ifdef SMBFS_NAME_DEBUG + int nmlen, slen; + u_char *cp; + + cp = name; + cp--; + if (*cp != 0xfc) { + printf("First byte of name entry '%s' corrupted\n", name); + Debugger("ditto"); + } + cp -= sizeof(int); + nmlen = *(int*)cp; + slen = strlen(name) + 1; + if (nmlen != slen) { + printf("Name length mismatch: was %d, now %d name '%s'\n", + nmlen, slen, name); + Debugger("ditto"); + } + if (name[nmlen] != 0xfe) { + printf("Last byte of name entry '%s' corrupted\n", name); + Debugger("ditto"); + } + free(cp, M_SMBNODENAME); +#else + free(name, M_SMBNODENAME); +#endif +} + +static int +smbfs_node_alloc(struct mount *mp, struct vnode *dvp, + const char *name, int nmlen, struct smbfattr *fap, struct vnode **vpp) +{ + struct proc *p = curproc; /* XXX */ + struct smbmount *smp = VFSTOSMBFS(mp); + struct smbnode_hashhead *nhpp; + struct smbnode *np, *np2, *dnp; + struct vnode *vp; + u_long hashval; + int error; + + *vpp = NULL; + if (smp->sm_root != NULL && dvp == NULL) { + SMBERROR("do not allocate root vnode twice!\n"); + return EINVAL; + } + if (nmlen == 2 && bcmp(name, "..", 2) == 0) { + if (dvp == NULL) + return EINVAL; + vp = VTOSMB(dvp)->n_parent->n_vnode; + error = vget(vp, LK_EXCLUSIVE, p); + if (error == 0) + *vpp = vp; + return error; + } else if (nmlen == 1 && name[0] == '.') { + SMBERROR("do not call me with dot!\n"); + return EINVAL; + } + dnp = dvp ? VTOSMB(dvp) : NULL; + if (dnp == NULL && dvp != NULL) { + vprint("smbfs_node_alloc: dead parent vnode", dvp); + return EINVAL; + } + hashval = smbfs_hash(name, nmlen); +retry: + smbfs_hash_lock(smp, p); +loop: + nhpp = SMBFS_NOHASH(smp, hashval); + LIST_FOREACH(np, nhpp, n_hash) { + vp = SMBTOV(np); + if (np->n_parent != dnp || + np->n_nmlen != nmlen || bcmp(name, np->n_name, nmlen) != 0) + continue; + VI_LOCK(vp); + smbfs_hash_unlock(smp, p); + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p) != 0) + goto retry; + *vpp = vp; + return 0; + } + smbfs_hash_unlock(smp, p); + /* + * If we don't have node attributes, then it is an explicit lookup + * for an existing vnode. + */ + if (fap == NULL) + return ENOENT; + + MALLOC(np, struct smbnode *, sizeof *np, M_SMBNODE, M_WAITOK); + error = getnewvnode(VT_SMBFS, mp, smbfs_vnodeop_p, &vp); + if (error) { + FREE(np, M_SMBNODE); + return error; + } + vp->v_type = fap->fa_attr & SMB_FA_DIR ? VDIR : VREG; + bzero(np, sizeof(*np)); + vp->v_data = np; + np->n_vnode = vp; + np->n_mount = VFSTOSMBFS(mp); + np->n_nmlen = nmlen; + np->n_name = smbfs_name_alloc(name, nmlen); + np->n_ino = fap->fa_ino; + + if (dvp) { + np->n_parent = dnp; + if (/*vp->v_type == VDIR &&*/ (dvp->v_flag & VROOT) == 0) { + vref(dvp); + np->n_flag |= NREFPARENT; + } + } else if (vp->v_type == VREG) + SMBERROR("new vnode '%s' born without parent ?\n", np->n_name); + + lockinit(&vp->v_lock, PINOD, "smbnode", 0, LK_CANRECURSE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + smbfs_hash_lock(smp, p); + LIST_FOREACH(np2, nhpp, n_hash) { + if (np2->n_parent != dnp || + np2->n_nmlen != nmlen || bcmp(name, np2->n_name, nmlen) != 0) + continue; + vput(vp); +/* smb_name_free(np->n_name); + FREE(np, M_SMBNODE);*/ + goto loop; + } + LIST_INSERT_HEAD(nhpp, np, n_hash); + smbfs_hash_unlock(smp, p); + *vpp = vp; + return 0; +} + +int +smbfs_nget(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, + struct smbfattr *fap, struct vnode **vpp) +{ + struct smbnode *np; + struct vnode *vp; + int error; + + *vpp = NULL; + error = smbfs_node_alloc(mp, dvp, name, nmlen, fap, &vp); + if (error) + return error; + np = VTOSMB(vp); + if (fap) + smbfs_attr_cacheenter(vp, fap); + *vpp = vp; + return 0; +} + +/* + * Free smbnode, and give vnode back to system + */ +int +smbfs_reclaim(ap) + struct vop_reclaim_args /* { + struct vnode *a_vp; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct proc *p = ap->a_p; + struct vnode *dvp; + struct smbnode *np = VTOSMB(vp); + struct smbmount *smp = VTOSMBFS(vp); + + SMBVDEBUG("%s,%d\n", np->n_name, vp->v_usecount); + + smbfs_hash_lock(smp, p); + + dvp = (np->n_parent && (np->n_flag & NREFPARENT)) ? + np->n_parent->n_vnode : NULL; + + if (np->n_hash.le_prev) + LIST_REMOVE(np, n_hash); + cache_purge(vp); + if (smp->sm_root == np) { + SMBVDEBUG("root vnode\n"); + smp->sm_root = NULL; + } + vp->v_data = NULL; + smbfs_hash_unlock(smp, p); + if (np->n_name) + smbfs_name_free(np->n_name); + FREE(np, M_SMBNODE); + if (dvp) { + VI_LOCK(dvp); + if (dvp->v_usecount >= 1) { + VI_UNLOCK(dvp); + vrele(dvp); + } else { + VI_UNLOCK(dvp); + SMBERROR("BUG: negative use count for parent!\n"); + } + } + return 0; +} + +int +smbfs_inactive(ap) + struct vop_inactive_args /* { + struct vnode *a_vp; + struct proc *a_p; + } */ *ap; +{ + struct proc *p = ap->a_p; + struct ucred *cred = p->p_ucred; + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + int error; + + SMBVDEBUG("%s: %d\n", VTOSMB(vp)->n_name, vp->v_usecount); + if (np->n_opencount) { + error = smbfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + smb_makescred(&scred, p, cred); + error = smbfs_smb_close(np->n_mount->sm_share, np->n_fid, + &np->n_mtime, &scred); + np->n_opencount = 0; + } + VOP_UNLOCK(vp, 0, p); + return (0); +} +/* + * routines to maintain vnode attributes cache + * smbfs_attr_cacheenter: unpack np.i to vattr structure + */ +void +smbfs_attr_cacheenter(struct vnode *vp, struct smbfattr *fap) +{ + struct smbnode *np = VTOSMB(vp); + + if (vp->v_type == VREG) { + if (np->n_size != fap->fa_size) { + np->n_size = fap->fa_size; + vnode_pager_setsize(vp, np->n_size); + } + } else if (vp->v_type == VDIR) { + np->n_size = 16384; /* should be a better way ... */ + } else + return; + np->n_mtime = fap->fa_mtime; + np->n_dosattr = fap->fa_attr; + np->n_attrage = time_second; + return; +} + +int +smbfs_attr_cachelookup(struct vnode *vp, struct vattr *va) +{ + struct smbnode *np = VTOSMB(vp); + struct smbmount *smp = VTOSMBFS(vp); + int diff; + + diff = time_second - np->n_attrage; + if (diff > 2) /* XXX should be configurable */ + return ENOENT; + va->va_type = vp->v_type; /* vnode type (for create) */ + if (vp->v_type == VREG) { + va->va_mode = smp->sm_args.file_mode; /* files access mode and type */ + } else if (vp->v_type == VDIR) { + va->va_mode = smp->sm_args.dir_mode; /* files access mode and type */ + } else + return EINVAL; + va->va_size = np->n_size; + va->va_nlink = 1; /* number of references to file */ + va->va_uid = smp->sm_args.uid; /* owner user id */ + va->va_gid = smp->sm_args.gid; /* owner group id */ + va->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; + va->va_fileid = np->n_ino; /* file id */ + if (va->va_fileid == 0) + va->va_fileid = 2; + va->va_blocksize = SSTOVC(smp->sm_share)->vc_txmax; + va->va_mtime = np->n_mtime; + va->va_atime = va->va_ctime = va->va_mtime; /* time file changed */ + va->va_gen = VNOVAL; /* generation number of file */ + va->va_flags = 0; /* flags defined for file */ + va->va_rdev = VNOVAL; /* device the special file represents */ + va->va_bytes = va->va_size; /* bytes of disk space held by file */ + va->va_filerev = 0; /* file modification number */ + va->va_vaflags = 0; /* operations flags */ + return 0; +} diff --git a/sys/fs/smbfs/smbfs_node.h b/sys/fs/smbfs/smbfs_node.h new file mode 100644 index 0000000..1a5a643 --- /dev/null +++ b/sys/fs/smbfs/smbfs_node.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _FS_SMBFS_NODE_H_ +#define _FS_SMBFS_NODE_H_ + +#define SMBFS_ROOT_INO 2 /* just like in UFS */ + +/* Bits for smbnode.n_flag */ +#define NFLUSHINPROG 0x0001 +#define NFLUSHWANT 0x0002 /* they should gone ... */ +#define NMODIFIED 0x0004 /* bogus, until async IO implemented */ +/*efine NNEW 0x0008*//* smb/vnode has been allocated */ +#define NREFPARENT 0x0010 /* node holds parent from recycling */ + +struct smbfs_fctx; + +struct smbnode { +#ifndef FB_CURRENT + struct lock n_lock; /* smbnode lock. (mbf) */ +#endif + int n_flag; + struct smbnode * n_parent; + struct vnode * n_vnode; + struct smbmount * n_mount; + time_t n_attrage; /* attributes cache time */ +/* time_t n_ctime;*/ + struct timespec n_mtime; /* modify time */ + struct timespec n_atime; /* last access time */ + u_quad_t n_size; + long n_ino; + int n_dosattr; + int n_opencount; + u_int16_t n_fid; /* file handle */ + int n_rwstate; /* granted access mode */ + u_char n_nmlen; + u_char * n_name; + struct smbfs_fctx * n_dirseq; /* ff context */ + long n_dirofs; /* last ff offset */ + struct lockf * n_lockf; /* Locking records of file */ + LIST_ENTRY(smbnode) n_hash; +}; + +#define VTOSMB(vp) ((struct smbnode *)(vp)->v_data) +#define SMBTOV(np) ((struct vnode *)(np)->n_vnode) + +struct vop_getpages_args; +struct vop_inactive_args; +struct vop_putpages_args; +struct vop_reclaim_args; +struct ucred; +struct uio; +struct smbfattr; + +int smbfs_inactive(struct vop_inactive_args *); +int smbfs_reclaim(struct vop_reclaim_args *); +int smbfs_nget(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, + struct smbfattr *fap, struct vnode **vpp); +u_int32_t smbfs_hash(const u_char *name, int nmlen); + +int smbfs_getpages(struct vop_getpages_args *); +int smbfs_putpages(struct vop_putpages_args *); +int smbfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred); +int smbfs_writevnode(struct vnode *vp, struct uio *uiop, struct ucred *cred, int ioflag); +void smbfs_attr_cacheenter(struct vnode *vp, struct smbfattr *fap); +int smbfs_attr_cachelookup(struct vnode *vp ,struct vattr *va); + +#define smbfs_attr_cacheremove(vp) VTOSMB(vp)->n_attrage = 0 + +#endif /* _FS_SMBFS_NODE_H_ */ diff --git a/sys/fs/smbfs/smbfs_smb.c b/sys/fs/smbfs/smbfs_smb.c new file mode 100644 index 0000000..373d5c1 --- /dev/null +++ b/sys/fs/smbfs/smbfs_smb.c @@ -0,0 +1,1273 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_MD5_HASH +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +/* + * Lack of inode numbers leads us to the problem of generating them. + * Partially this problem can be solved by having a dir/file cache + * with inode numbers generated from the incremented by one counter. + * However this way will require too much kernel memory, gives all + * sorts of locking and consistency problems, not to mentinon counter overflows. + * So, I'm decided to use a hash function to generate pseudo random (and unique) + * inode numbers. + */ +static long +smbfs_getino(struct smbnode *dnp, const char *name, int nmlen) +{ +#ifdef USE_MD5_HASH + MD5_CTX md5; + u_int32_t state[4]; + long ino; + int i; + + MD5Init(&md5); + MD5Update(&md5, name, nmlen); + MD5Final((u_char *)state, &md5); + for (i = 0, ino = 0; i < 4; i++) + ino += state[i]; + return dnp->n_ino + ino; +#endif + u_int32_t ino; + + ino = dnp->n_ino + smbfs_hash(name, nmlen); + if (ino <= 2) + ino += 3; + return ino; +} + +static int +smbfs_smb_lockandx(struct smbnode *np, int op, u_int32_t pid, off_t start, off_t end, + struct smb_cred *scred) +{ + struct smb_share *ssp = np->n_mount->sm_share; + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + u_char ltype = 0; + int error; + + if (op == SMB_LOCK_SHARED) + ltype |= SMB_LOCKING_ANDX_SHARED_LOCK; + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); /* secondary command */ + mb_put_uint8(mbp, 0); /* MBZ */ + mb_put_uint16le(mbp, 0); + mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint8(mbp, ltype); /* locktype */ + mb_put_uint8(mbp, 0); /* oplocklevel - 0 seems is NO_OPLOCK */ + mb_put_uint32le(mbp, 0); /* timeout - break immediately */ + mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 1 : 0); + mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 0 : 1); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint16le(mbp, pid); + mb_put_uint32le(mbp, start); + mb_put_uint32le(mbp, end - start); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_lock(struct smbnode *np, int op, caddr_t id, + off_t start, off_t end, struct smb_cred *scred) +{ + struct smb_share *ssp = np->n_mount->sm_share; + + if (SMB_DIALECT(SSTOVC(ssp)) < SMB_DIALECT_LANMAN1_0) + /* + * TODO: use LOCK_BYTE_RANGE here. + */ + return EINVAL; + else + return smbfs_smb_lockandx(np, op, (u_int32_t)id, start, end, scred); +} + +int +smbfs_smb_statfs2(struct smb_share *ssp, struct statfs *sbp, + struct smb_cred *scred) +{ + struct smb_t2rq *t2p; + struct mbchain *mbp; + struct mdchain *mdp; + u_int16_t bsize; + u_int32_t units, bpu, funits; + int error; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FS_INFORMATION, + scred, &t2p); + if (error) + return error; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, SMB_INFO_ALLOCATION); + t2p->t2_maxpcount = 4; + t2p->t2_maxdcount = 4 * 4 + 2; + error = smb_t2_request(t2p); + if (error) { + smb_t2_done(t2p); + return error; + } + mdp = &t2p->t2_rdata; + md_get_uint32(mdp, NULL); /* fs id */ + md_get_uint32le(mdp, &bpu); + md_get_uint32le(mdp, &units); + md_get_uint32le(mdp, &funits); + md_get_uint16le(mdp, &bsize); + sbp->f_bsize = bpu * bsize; /* fundamental file system block size */ + sbp->f_blocks= units; /* total data blocks in file system */ + sbp->f_bfree = funits; /* free blocks in fs */ + sbp->f_bavail= funits; /* free blocks avail to non-superuser */ + sbp->f_files = 0xffff; /* total file nodes in file system */ + sbp->f_ffree = 0xffff; /* free file nodes in fs */ + smb_t2_done(t2p); + return 0; +} + +int +smbfs_smb_statfs(struct smb_share *ssp, struct statfs *sbp, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct mdchain *mdp; + u_int16_t units, bpu, bsize, funits; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_QUERY_INFORMATION_DISK, scred); + if (error) + return error; + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) { + smb_rq_done(rqp); + return error; + } + smb_rq_getreply(rqp, &mdp); + md_get_uint16le(mdp, &units); + md_get_uint16le(mdp, &bpu); + md_get_uint16le(mdp, &bsize); + md_get_uint16le(mdp, &funits); + sbp->f_bsize = bpu * bsize; /* fundamental file system block size */ + sbp->f_blocks= units; /* total data blocks in file system */ + sbp->f_bfree = funits; /* free blocks in fs */ + sbp->f_bavail= funits; /* free blocks avail to non-superuser */ + sbp->f_files = 0xffff; /* total file nodes in file system */ + sbp->f_ffree = 0xffff; /* free file nodes in fs */ + smb_rq_done(rqp); + return 0; +} + +int +smbfs_smb_setfsize(struct smbnode *np, int newsize, struct smb_cred *scred) +{ + struct smb_share *ssp = np->n_mount->sm_share; + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_WRITE, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, 0); + mb_put_uint32le(mbp, newsize); + mb_put_uint16le(mbp, 0); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_DATA); + mb_put_uint16le(mbp, 0); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return error; +} + + +/* + * Set DOS file attributes. mtime should be NULL for dialects above lm10 + */ +int +smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + u_long time; + int error, svtz; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred); + if (error) + return error; + svtz = SSTOVC(ssp)->vc_sopt.sv_tz; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, attr); + if (mtime) { + smb_time_local2server(mtime, svtz, &time); + } else + time = 0; + mb_put_uint32le(mbp, time); /* mtime */ + mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); + if (error) + break; + mb_put_uint8(mbp, SMB_DT_ASCII); + mb_put_uint8(mbp, 0); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBERROR("%d\n", error); + if (error) + break; + } while(0); + smb_rq_done(rqp); + return error; +} + +/* + * Note, win95 doesn't support this call. + */ +int +smbfs_smb_setptime2(struct smbnode *np, struct timespec *mtime, + struct timespec *atime, int attr, struct smb_cred *scred) +{ + struct smb_t2rq *t2p; + struct smb_share *ssp = np->n_mount->sm_share; + struct smb_vc *vcp = SSTOVC(ssp); + struct mbchain *mbp; + u_int16_t date, time; + int error, tzoff; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION, + scred, &t2p); + if (error) + return error; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, SMB_INFO_STANDARD); + mb_put_uint32le(mbp, 0); /* MBZ */ + error = smbfs_fullpath(mbp, vcp, np, NULL, 0); + if (error) { + smb_t2_done(t2p); + return error; + } + tzoff = vcp->vc_sopt.sv_tz; + mbp = &t2p->t2_tdata; + mb_init(mbp); + mb_put_uint32le(mbp, 0); /* creation time */ + if (atime) + smb_time_unix2dos(atime, tzoff, &date, &time, NULL); + else + time = date = 0; + mb_put_uint16le(mbp, date); + mb_put_uint16le(mbp, time); + if (mtime) + smb_time_unix2dos(mtime, tzoff, &date, &time, NULL); + else + time = date = 0; + mb_put_uint16le(mbp, date); + mb_put_uint16le(mbp, time); + mb_put_uint32le(mbp, 0); /* file size */ + mb_put_uint32le(mbp, 0); /* allocation unit size */ + mb_put_uint16le(mbp, attr); /* DOS attr */ + mb_put_uint32le(mbp, 0); /* EA size */ + t2p->t2_maxpcount = 5 * 2; + t2p->t2_maxdcount = vcp->vc_txmax; + error = smb_t2_request(t2p); + smb_t2_done(t2p); + return error; +} + +/* + * NT level. Specially for win9x + */ +int +smbfs_smb_setpattrNT(struct smbnode *np, u_short attr, struct timespec *mtime, + struct timespec *atime, struct smb_cred *scred) +{ + struct smb_t2rq *t2p; + struct smb_share *ssp = np->n_mount->sm_share; + struct smb_vc *vcp = SSTOVC(ssp); + struct mbchain *mbp; + int64_t tm; + int error, tzoff; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION, + scred, &t2p); + if (error) + return error; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFO); + mb_put_uint32le(mbp, 0); /* MBZ */ + error = smbfs_fullpath(mbp, vcp, np, NULL, 0); + if (error) { + smb_t2_done(t2p); + return error; + } + tzoff = vcp->vc_sopt.sv_tz; + mbp = &t2p->t2_tdata; + mb_init(mbp); + mb_put_int64le(mbp, 0); /* creation time */ + if (atime) { + smb_time_local2NT(atime, tzoff, &tm); + } else + tm = 0; + mb_put_int64le(mbp, tm); + if (mtime) { + smb_time_local2NT(mtime, tzoff, &tm); + } else + tm = 0; + mb_put_int64le(mbp, tm); + mb_put_int64le(mbp, tm); /* change time */ + mb_put_uint32le(mbp, attr); /* attr */ + t2p->t2_maxpcount = 24; + t2p->t2_maxdcount = 56; + error = smb_t2_request(t2p); + smb_t2_done(t2p); + return error; +} + +/* + * Set file atime and mtime. Doesn't supported by core dialect. + */ +int +smbfs_smb_setftime(struct smbnode *np, struct timespec *mtime, + struct timespec *atime, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + u_int16_t date, time; + int error, tzoff; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scred); + if (error) + return error; + tzoff = SSTOVC(ssp)->vc_sopt.sv_tz; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint32le(mbp, 0); /* creation time */ + + if (atime) + smb_time_unix2dos(atime, tzoff, &date, &time, NULL); + else + time = date = 0; + mb_put_uint16le(mbp, date); + mb_put_uint16le(mbp, time); + if (mtime) + smb_time_unix2dos(mtime, tzoff, &date, &time, NULL); + else + time = date = 0; + mb_put_uint16le(mbp, date); + mb_put_uint16le(mbp, time); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + return error; +} + +/* + * Set DOS file attributes. + * Looks like this call can be used only if CAP_NT_SMBS bit is on. + */ +int +smbfs_smb_setfattrNT(struct smbnode *np, u_int16_t attr, struct timespec *mtime, + struct timespec *atime, struct smb_cred *scred) +{ + struct smb_t2rq *t2p; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + int64_t tm; + int error, svtz; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION, + scred, &t2p); + if (error) + return error; + svtz = SSTOVC(ssp)->vc_sopt.sv_tz; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFO); + mb_put_uint32le(mbp, 0); + mbp = &t2p->t2_tdata; + mb_init(mbp); + mb_put_int64le(mbp, 0); /* creation time */ + if (atime) { + smb_time_local2NT(atime, svtz, &tm); + } else + tm = 0; + mb_put_int64le(mbp, tm); + if (mtime) { + smb_time_local2NT(mtime, svtz, &tm); + } else + tm = 0; + mb_put_int64le(mbp, tm); + mb_put_int64le(mbp, tm); /* change time */ + mb_put_uint16le(mbp, attr); + mb_put_uint32le(mbp, 0); /* padding */ + mb_put_uint16le(mbp, 0); + t2p->t2_maxpcount = 2; + t2p->t2_maxdcount = 0; + error = smb_t2_request(t2p); + smb_t2_done(t2p); + return error; +} + + +int +smbfs_smb_open(struct smbnode *np, int accmode, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc; + u_int16_t fid, wattr, grantedmode; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, accmode); + mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + if (md_get_uint8(mdp, &wc) != 0 || wc != 7) { + error = EBADRPC; + break; + } + md_get_uint16(mdp, &fid); + md_get_uint16le(mdp, &wattr); + md_get_uint32(mdp, NULL); /* mtime */ + md_get_uint32(mdp, NULL); /* fsize */ + md_get_uint16le(mdp, &grantedmode); + /* + * TODO: refresh attributes from this reply + */ + } while(0); + smb_rq_done(rqp); + if (error) + return error; + np->n_fid = fid; + np->n_rwstate = grantedmode; + return 0; +} + + +int +smbfs_smb_close(struct smb_share *ssp, u_int16_t fid, struct timespec *mtime, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + u_long time; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CLOSE, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); + if (mtime) { + smb_time_local2server(mtime, SSTOVC(ssp)->vc_sopt.sv_tz, &time); + } else + time = 0; + mb_put_uint32le(mbp, time); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = dnp->n_mount->sm_share; + struct mbchain *mbp; + struct mdchain *mdp; + struct timespec ctime; + u_int8_t wc; + u_int16_t fid; + u_long tm; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, SMB_FA_ARCHIVE); /* attributes */ + nanotime(&ctime); + smb_time_local2server(&ctime, SSTOVC(ssp)->vc_sopt.sv_tz, &tm); + mb_put_uint32le(mbp, tm); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, nmlen); + if (!error) { + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (!error) { + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc == 1) + md_get_uint16(mdp, &fid); + else + error = EBADRPC; + } + } + smb_rq_done(rqp); + if (error) + return error; + smbfs_smb_close(ssp, fid, &ctime, scred); + return error; +} + +int +smbfs_smb_delete(struct smbnode *np, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); + if (!error) { + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + } + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = src->n_mount->sm_share; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0); + if (error) + break; + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + } while(0); + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, u_int16_t flags, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = src->n_mount->sm_share; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_MOVE, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, SMB_TID_UNKNOWN); + mb_put_uint16le(mbp, 0x20); /* delete target file */ + mb_put_uint16le(mbp, flags); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0); + if (error) + break; + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + } while(0); + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = dnp->n_mount->sm_share; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE_DIRECTORY, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, len); + if (!error) { + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + } + smb_rq_done(rqp); + return error; +} + +int +smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = np->n_mount->sm_share; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE_DIRECTORY, scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); + if (!error) { + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + } + smb_rq_done(rqp); + return error; +} + +static int +smbfs_smb_search(struct smbfs_fctx *ctx) +{ + struct smb_vc *vcp = SSTOVC(ctx->f_ssp); + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc, bt; + u_int16_t ec, dlen, bc; + int maxent, error, iseof = 0; + + maxent = min(ctx->f_left, (vcp->vc_txmax - SMB_HDRLEN - 3) / SMB_DENTRYLEN); + if (ctx->f_rq) { + smb_rq_done(ctx->f_rq); + ctx->f_rq = NULL; + } + error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_SEARCH, ctx->f_scred, &rqp); + if (error) + return error; + ctx->f_rq = rqp; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, maxent); /* max entries to return */ + mb_put_uint16le(mbp, ctx->f_attrmask); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_ASCII); /* buffer format */ + if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { + error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen); + if (error) + return error; + mb_put_uint8(mbp, SMB_DT_VARIABLE); + mb_put_uint16le(mbp, 0); /* context length */ + ctx->f_flags &= ~SMBFS_RDD_FINDFIRST; + } else { + mb_put_uint8(mbp, 0); /* file name length */ + mb_put_uint8(mbp, SMB_DT_VARIABLE); + mb_put_uint16le(mbp, SMB_SKEYLEN); + mb_put_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM); + } + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) { + if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnofiles) { + error = 0; + iseof = 1; + ctx->f_flags |= SMBFS_RDD_EOF; + } else + return error; + } + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 1) + return iseof ? ENOENT : EBADRPC; + md_get_uint16le(mdp, &ec); + if (ec == 0) + return ENOENT; + ctx->f_ecnt = ec; + md_get_uint16le(mdp, &bc); + if (bc < 3) + return EBADRPC; + bc -= 3; + md_get_uint8(mdp, &bt); + if (bt != SMB_DT_VARIABLE) + return EBADRPC; + md_get_uint16le(mdp, &dlen); + if (dlen != bc || dlen % SMB_DENTRYLEN != 0) + return EBADRPC; + return 0; +} + +static int +smbfs_findopenLM1(struct smbfs_fctx *ctx, struct smbnode *dnp, + const char *wildcard, int wclen, int attr, struct smb_cred *scred) +{ + ctx->f_attrmask = attr; + if (wildcard) { + if (wclen == 1 && wildcard[0] == '*') { + ctx->f_wildcard = "*.*"; + ctx->f_wclen = 3; + } else { + ctx->f_wildcard = wildcard; + ctx->f_wclen = wclen; + } + } else { + ctx->f_wildcard = NULL; + ctx->f_wclen = 0; + } + ctx->f_name = ctx->f_fname; + return 0; +} + +static int +smbfs_findnextLM1(struct smbfs_fctx *ctx, int limit) +{ + struct mdchain *mbp; + struct smb_rq *rqp; + char *cp; + u_int8_t battr; + u_int16_t date, time; + u_int32_t size; + int error; + + if (ctx->f_ecnt == 0) { + if (ctx->f_flags & SMBFS_RDD_EOF) + return ENOENT; + ctx->f_left = ctx->f_limit = limit; + error = smbfs_smb_search(ctx); + if (error) + return error; + } + rqp = ctx->f_rq; + smb_rq_getreply(rqp, &mbp); + md_get_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM); + md_get_uint8(mbp, &battr); + md_get_uint16le(mbp, &time); + md_get_uint16le(mbp, &date); + md_get_uint32le(mbp, &size); + cp = ctx->f_name; + md_get_mem(mbp, cp, sizeof(ctx->f_fname), MB_MSYSTEM); + cp[sizeof(ctx->f_fname) - 1] = 0; + cp += strlen(cp) - 1; + while (*cp == ' ' && cp >= ctx->f_name) + *cp-- = 0; + ctx->f_attr.fa_attr = battr; + smb_dos2unixtime(date, time, 0, rqp->sr_vc->vc_sopt.sv_tz, + &ctx->f_attr.fa_mtime); + ctx->f_attr.fa_size = size; + ctx->f_nmlen = strlen(ctx->f_name); + ctx->f_ecnt--; + ctx->f_left--; + return 0; +} + +static int +smbfs_findcloseLM1(struct smbfs_fctx *ctx) +{ + if (ctx->f_rq) + smb_rq_done(ctx->f_rq); + return 0; +} + +/* + * TRANS2_FIND_FIRST2/NEXT2, used for NT LM12 dialect + */ +static int +smbfs_smb_trans2find2(struct smbfs_fctx *ctx) +{ + struct smb_t2rq *t2p; + struct smb_vc *vcp = SSTOVC(ctx->f_ssp); + struct mbchain *mbp; + struct mdchain *mdp; + u_int16_t tw, flags; + int error; + + if (ctx->f_t2) { + smb_t2_done(ctx->f_t2); + ctx->f_t2 = NULL; + } + ctx->f_flags &= ~SMBFS_RDD_GOTRNAME; + flags = 8 | 2; /* | */ + if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) { + flags |= 1; /* close search after this request */ + ctx->f_flags |= SMBFS_RDD_NOCLOSE; + } + if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { + error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_FIRST2, + ctx->f_scred, &t2p); + if (error) + return error; + ctx->f_t2 = t2p; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, ctx->f_attrmask); + mb_put_uint16le(mbp, ctx->f_limit); + mb_put_uint16le(mbp, flags); + mb_put_uint16le(mbp, ctx->f_infolevel); + mb_put_uint32le(mbp, 0); + error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen); + if (error) + return error; + } else { + error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_NEXT2, + ctx->f_scred, &t2p); + if (error) + return error; + ctx->f_t2 = t2p; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, ctx->f_limit); + mb_put_uint16le(mbp, ctx->f_infolevel); + mb_put_uint32le(mbp, 0); /* resume key */ + mb_put_uint16le(mbp, flags); + if (ctx->f_rname) + mb_put_mem(mbp, ctx->f_rname, strlen(ctx->f_rname) + 1, MB_MSYSTEM); + else + mb_put_uint8(mbp, 0); /* resume file name */ +#if 0 + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 200 * 1000; /* 200ms */ + if (vcp->vc_flags & SMBC_WIN95) { + /* + * some implementations suggests to sleep here + * for 200ms, due to the bug in the Win95. + * I've didn't notice any problem, but put code + * for it. + */ + tsleep(&flags, PVFS, "fix95", tvtohz(&tv)); + } +#endif + } + t2p->t2_maxpcount = 5 * 2; + t2p->t2_maxdcount = vcp->vc_txmax; + error = smb_t2_request(t2p); + if (error) + return error; + mdp = &t2p->t2_rparam; + if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { + if ((error = md_get_uint16(mdp, &ctx->f_Sid)) != 0) + return error; + ctx->f_flags &= ~SMBFS_RDD_FINDFIRST; + } + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return error; + ctx->f_ecnt = tw; + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return error; + if (tw) + ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE; + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return error; + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return error; + if (ctx->f_ecnt == 0) + return ENOENT; + ctx->f_rnameofs = tw; + mdp = &t2p->t2_rdata; + if (mdp->md_top == NULL) { + printf("bug: ecnt = %d, but data is NULL (please report)\n", ctx->f_ecnt); + return ENOENT; + } + if (mdp->md_top->m_len == 0) { + printf("bug: ecnt = %d, but m_len = 0 and m_next = %p (please report)\n", ctx->f_ecnt,mbp->mb_top->m_next); + return ENOENT; + } + ctx->f_eofs = 0; + return 0; +} + +static int +smbfs_smb_findclose2(struct smbfs_fctx *ctx) +{ + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2, ctx->f_scred); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return error; +} + +static int +smbfs_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp, + const char *wildcard, int wclen, int attr, struct smb_cred *scred) +{ + ctx->f_name = malloc(SMB_MAXFNAMELEN, M_SMBFSDATA, M_WAITOK); + if (ctx->f_name == NULL) + return ENOMEM; + ctx->f_infolevel = SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_NTLM0_12 ? + SMB_INFO_STANDARD : SMB_FIND_FILE_DIRECTORY_INFO; + ctx->f_attrmask = attr; + ctx->f_wildcard = wildcard; + ctx->f_wclen = wclen; + return 0; +} + +static int +smbfs_findnextLM2(struct smbfs_fctx *ctx, int limit) +{ + struct mdchain *mbp; + struct smb_t2rq *t2p; + char *cp; + u_int8_t tb; + u_int16_t date, time, wattr; + u_int32_t size, next, dattr; + int64_t lint; + int error, svtz, cnt, fxsz, nmlen, recsz; + + if (ctx->f_ecnt == 0) { + if (ctx->f_flags & SMBFS_RDD_EOF) + return ENOENT; + ctx->f_left = ctx->f_limit = limit; + error = smbfs_smb_trans2find2(ctx); + if (error) + return error; + } + t2p = ctx->f_t2; + mbp = &t2p->t2_rdata; + svtz = SSTOVC(ctx->f_ssp)->vc_sopt.sv_tz; + switch (ctx->f_infolevel) { + case SMB_INFO_STANDARD: + next = 0; + fxsz = 0; + md_get_uint16le(mbp, &date); + md_get_uint16le(mbp, &time); /* creation time */ + md_get_uint16le(mbp, &date); + md_get_uint16le(mbp, &time); /* access time */ + smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_atime); + md_get_uint16le(mbp, &date); + md_get_uint16le(mbp, &time); /* access time */ + smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_mtime); + md_get_uint32le(mbp, &size); + ctx->f_attr.fa_size = size; + md_get_uint32(mbp, NULL); /* allocation size */ + md_get_uint16le(mbp, &wattr); + ctx->f_attr.fa_attr = wattr; + md_get_uint8(mbp, &tb); + size = nmlen = tb; + fxsz = 23; + recsz = next = 24 + nmlen; /* docs misses zero byte at end */ + break; + case SMB_FIND_FILE_DIRECTORY_INFO: + md_get_uint32le(mbp, &next); + md_get_uint32(mbp, NULL); /* file index */ + md_get_int64(mbp, NULL); /* creation time */ + md_get_int64le(mbp, &lint); + smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_atime); + md_get_int64le(mbp, &lint); + smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_mtime); + md_get_int64le(mbp, &lint); + smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_ctime); + md_get_int64le(mbp, &lint); /* file size */ + ctx->f_attr.fa_size = lint; + md_get_int64(mbp, NULL); /* real size (should use) */ + md_get_uint32(mbp, &dattr); /* EA */ + ctx->f_attr.fa_attr = dattr; + md_get_uint32le(mbp, &size); /* name len */ + fxsz = 64; + recsz = next ? next : fxsz + size; + break; + default: + SMBERROR("unexpected info level %d\n", ctx->f_infolevel); + return EINVAL; + } + nmlen = min(size, SMB_MAXFNAMELEN); + cp = ctx->f_name; + error = md_get_mem(mbp, cp, nmlen, MB_MSYSTEM); + if (error) + return error; + if (next) { + cnt = next - nmlen - fxsz; + if (cnt > 0) + md_get_mem(mbp, NULL, cnt, MB_MSYSTEM); + else if (cnt < 0) { + SMBERROR("out of sync\n"); + return EBADRPC; + } + } + if (nmlen && cp[nmlen - 1] == 0) + nmlen--; + if (nmlen == 0) + return EBADRPC; + + next = ctx->f_eofs + recsz; + if (ctx->f_rnameofs && (ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0 && + (ctx->f_rnameofs >= ctx->f_eofs && ctx->f_rnameofs < next)) { + /* + * Server needs a resume filename. + */ + if (ctx->f_rnamelen <= nmlen) { + if (ctx->f_rname) + free(ctx->f_rname, M_SMBFSDATA); + ctx->f_rname = malloc(nmlen + 1, M_SMBFSDATA, M_WAITOK); + ctx->f_rnamelen = nmlen; + } + bcopy(ctx->f_name, ctx->f_rname, nmlen); + ctx->f_rname[nmlen] = 0; + ctx->f_flags |= SMBFS_RDD_GOTRNAME; + } + ctx->f_nmlen = nmlen; + ctx->f_eofs = next; + ctx->f_ecnt--; + ctx->f_left--; + return 0; +} + +static int +smbfs_findcloseLM2(struct smbfs_fctx *ctx) +{ + if (ctx->f_name) + free(ctx->f_name, M_SMBFSDATA); + if (ctx->f_t2) + smb_t2_done(ctx->f_t2); + if ((ctx->f_flags & SMBFS_RDD_NOCLOSE) == 0) + smbfs_smb_findclose2(ctx); + return 0; +} + +int +smbfs_findopen(struct smbnode *dnp, const char *wildcard, int wclen, int attr, + struct smb_cred *scred, struct smbfs_fctx **ctxpp) +{ + struct smbfs_fctx *ctx; + int error; + + ctx = malloc(sizeof(*ctx), M_SMBFSDATA, M_WAITOK); + if (ctx == NULL) + return ENOMEM; + bzero(ctx, sizeof(*ctx)); + ctx->f_ssp = dnp->n_mount->sm_share; + ctx->f_dnp = dnp; + ctx->f_flags = SMBFS_RDD_FINDFIRST; + ctx->f_scred = scred; + if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 || + (dnp->n_mount->sm_args.flags & SMBFS_MOUNT_NO_LONG)) { + ctx->f_flags |= SMBFS_RDD_USESEARCH; + error = smbfs_findopenLM1(ctx, dnp, wildcard, wclen, attr, scred); + } else + error = smbfs_findopenLM2(ctx, dnp, wildcard, wclen, attr, scred); + if (error) + smbfs_findclose(ctx, scred); + else + *ctxpp = ctx; + return error; +} + +int +smbfs_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scred) +{ + int error; + + if (limit == 0) + limit = 1000000; + else if (limit > 1) + limit *= 4; /* imperical */ + ctx->f_scred = scred; + for (;;) { + if (ctx->f_flags & SMBFS_RDD_USESEARCH) { + error = smbfs_findnextLM1(ctx, limit); + } else + error = smbfs_findnextLM2(ctx, limit); + if (error) + return error; + if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') || + (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' && + ctx->f_name[1] == '.')) + continue; + break; + } + smbfs_fname_tolocal(SSTOVC(ctx->f_ssp), ctx->f_name, ctx->f_nmlen, + ctx->f_dnp->n_mount->sm_caseopt); + ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name, ctx->f_nmlen); + return 0; +} + +int +smbfs_findclose(struct smbfs_fctx *ctx, struct smb_cred *scred) +{ + ctx->f_scred = scred; + if (ctx->f_flags & SMBFS_RDD_USESEARCH) { + smbfs_findcloseLM1(ctx); + } else + smbfs_findcloseLM2(ctx); + if (ctx->f_rname) + free(ctx->f_rname, M_SMBFSDATA); + free(ctx, M_SMBFSDATA); + return 0; +} + +int +smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen, + struct smbfattr *fap, struct smb_cred *scred) +{ + struct smbfs_fctx *ctx; + int error; + + if (dnp == NULL || (dnp->n_ino == 2 && name == NULL)) { + bzero(fap, sizeof(*fap)); + fap->fa_attr = SMB_FA_DIR; + fap->fa_ino = 2; + return 0; + } + if (nmlen == 1 && name[0] == '.') { + error = smbfs_smb_lookup(dnp, NULL, 0, fap, scred); + return error; + } else if (nmlen == 2 && name[0] == '.' && name[1] == '.') { + error = smbfs_smb_lookup(dnp->n_parent, NULL, 0, fap, scred); + printf("%s: knows NOTHING about '..'\n", __FUNCTION__); + return error; + } + error = smbfs_findopen(dnp, name, nmlen, + SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scred, &ctx); + if (error) + return error; + ctx->f_flags |= SMBFS_RDD_FINDSINGLE; + error = smbfs_findnext(ctx, 1, scred); + if (error == 0) { + *fap = ctx->f_attr; + if (name == NULL) + fap->fa_ino = dnp->n_ino; + } + smbfs_findclose(ctx, scred); + return error; +} diff --git a/sys/fs/smbfs/smbfs_subr.c b/sys/fs/smbfs/smbfs_subr.c new file mode 100644 index 0000000..8012c0b --- /dev/null +++ b/sys/fs/smbfs/smbfs_subr.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +MALLOC_DEFINE(M_SMBFSDATA, "SMBFS data", "SMBFS private data"); + +/* + * Time & date conversion routines taken from msdosfs. Although leap + * year calculation is bogus, it's sufficient before 2100 :) + */ +/* + * This is the format of the contents of the deTime field in the direntry + * structure. + * We don't use bitfields because we don't know how compilers for + * arbitrary machines will lay them out. + */ +#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ +#define DT_2SECONDS_SHIFT 0 +#define DT_MINUTES_MASK 0x7E0 /* minutes */ +#define DT_MINUTES_SHIFT 5 +#define DT_HOURS_MASK 0xF800 /* hours */ +#define DT_HOURS_SHIFT 11 + +/* + * This is the format of the contents of the deDate field in the direntry + * structure. + */ +#define DD_DAY_MASK 0x1F /* day of month */ +#define DD_DAY_SHIFT 0 +#define DD_MONTH_MASK 0x1E0 /* month */ +#define DD_MONTH_SHIFT 5 +#define DD_YEAR_MASK 0xFE00 /* year - 1980 */ +#define DD_YEAR_SHIFT 9 +/* + * Total number of days that have passed for each month in a regular year. + */ +static u_short regyear[] = { + 31, 59, 90, 120, 151, 181, + 212, 243, 273, 304, 334, 365 +}; + +/* + * Total number of days that have passed for each month in a leap year. + */ +static u_short leapyear[] = { + 31, 60, 91, 121, 152, 182, + 213, 244, 274, 305, 335, 366 +}; + +/* + * Variables used to remember parts of the last time conversion. Maybe we + * can avoid a full conversion. + */ +static u_long lasttime; +static u_long lastday; +static u_short lastddate; +static u_short lastdtime; + +void +smb_time_local2server(struct timespec *tsp, int tzoff, u_long *seconds) +{ + *seconds = tsp->tv_sec - tzoff * 60 /*- tz.tz_minuteswest * 60 - + (wall_cmos_clock ? adjkerntz : 0)*/; +} + +void +smb_time_server2local(u_long seconds, int tzoff, struct timespec *tsp) +{ + tsp->tv_sec = seconds + tzoff * 60; + /*+ tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0)*/; +} + +/* + * Number of seconds between 1970 and 1601 year + */ +int64_t DIFF1970TO1601 = 11644473600ULL; + +/* + * Time from server comes as UTC, so no need to use tz + */ +void +smb_time_NT2local(int64_t nsec, int tzoff, struct timespec *tsp) +{ + smb_time_server2local(nsec / 10000000 - DIFF1970TO1601, 0, tsp); +} + +void +smb_time_local2NT(struct timespec *tsp, int tzoff, int64_t *nsec) +{ + u_long seconds; + + smb_time_local2server(tsp, 0, &seconds); + *nsec = (((int64_t)(seconds) & ~1) + DIFF1970TO1601) * (int64_t)10000000; +} + +void +smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp, + u_int16_t *dtp, u_int8_t *dhp) +{ + u_long t, days, year, month, inc; + u_short *months; + + /* + * If the time from the last conversion is the same as now, then + * skip the computations and use the saved result. + */ + smb_time_local2server(tsp, tzoff, &t); + t &= ~1; + if (lasttime != t) { + lasttime = t; + lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) + + (((t / 60) % 60) << DT_MINUTES_SHIFT) + + (((t / 3600) % 24) << DT_HOURS_SHIFT); + + /* + * If the number of days since 1970 is the same as the last + * time we did the computation then skip all this leap year + * and month stuff. + */ + days = t / (24 * 60 * 60); + if (days != lastday) { + lastday = days; + for (year = 1970;; year++) { + inc = year & 0x03 ? 365 : 366; + if (days < inc) + break; + days -= inc; + } + months = year & 0x03 ? regyear : leapyear; + for (month = 0; days >= months[month]; month++) + ; + if (month > 0) + days -= months[month - 1]; + lastddate = ((days + 1) << DD_DAY_SHIFT) + + ((month + 1) << DD_MONTH_SHIFT); + /* + * Remember dos's idea of time is relative to 1980. + * unix's is relative to 1970. If somehow we get a + * time before 1980 then don't give totally crazy + * results. + */ + if (year > 1980) + lastddate += (year - 1980) << DD_YEAR_SHIFT; + } + } + if (dtp) + *dtp = lastdtime; + if (dhp) + *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; + + *ddp = lastddate; +} + +/* + * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that + * interval there were 8 regular years and 2 leap years. + */ +#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) + +static u_short lastdosdate; +static u_long lastseconds; + +void +smb_dos2unixtime(u_int dd, u_int dt, u_int dh, int tzoff, + struct timespec *tsp) +{ + u_long seconds; + u_long month; + u_long year; + u_long days; + u_short *months; + + if (dd == 0) { + tsp->tv_sec = 0; + tsp->tv_nsec = 0; + return; + } + seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) + + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 + + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 + + dh / 100; + /* + * If the year, month, and day from the last conversion are the + * same then use the saved value. + */ + if (lastdosdate != dd) { + lastdosdate = dd; + days = 0; + year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; + days = year * 365; + days += year / 4 + 1; /* add in leap days */ + if ((year & 0x03) == 0) + days--; /* if year is a leap year */ + months = year & 0x03 ? regyear : leapyear; + month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; + if (month < 1 || month > 12) { + month = 1; + } + if (month > 1) + days += months[month - 2]; + days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; + lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; + } + smb_time_server2local(seconds + lastseconds, tzoff, tsp); + tsp->tv_nsec = (dh % 100) * 10000000; +} + +static int +smb_fphelp(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *np, + int caseopt) +{ + struct smbmount *smp= np->n_mount; + struct smbnode **npp = smp->sm_npstack; + int i, error = 0; + +/* simple_lock(&smp->sm_npslock);*/ + i = 0; + while (np->n_parent) { + if (i++ == SMBFS_MAXPATHCOMP) { +/* simple_unlock(&smp->sm_npslock);*/ + return ENAMETOOLONG; + } + *npp++ = np; + np = np->n_parent; + } +/* if (i == 0) + return smb_put_dmem(mbp, vcp, "\\", 2, caseopt);*/ + while (i--) { + np = *--npp; + error = mb_put_uint8(mbp, '\\'); + if (error) + break; + error = smb_put_dmem(mbp, vcp, np->n_name, np->n_nmlen, caseopt); + if (error) + break; + } +/* simple_unlock(&smp->sm_npslock);*/ + return error; +} + +int +smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, + const char *name, int nmlen) +{ + int caseopt = SMB_CS_NONE; + int error; + + if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0) + caseopt |= SMB_CS_UPPER; + if (dnp != NULL) { + error = smb_fphelp(mbp, vcp, dnp, caseopt); + if (error) + return error; + } + if (name) { + error = mb_put_uint8(mbp, '\\'); + if (error) + return error; + error = smb_put_dmem(mbp, vcp, name, nmlen, caseopt); + if (error) + return error; + } + error = mb_put_uint8(mbp, 0); + return error; +} + +int +smbfs_fname_tolocal(struct smb_vc *vcp, char *name, int nmlen, int caseopt) +{ +/* if (caseopt & SMB_CS_UPPER) + iconv_convmem(vcp->vc_toupper, name, name, nmlen); + else if (caseopt & SMB_CS_LOWER) + iconv_convmem(vcp->vc_tolower, name, name, nmlen);*/ + if (vcp->vc_tolocal) + iconv_convmem(vcp->vc_tolocal, name, name, nmlen); + return 0; +} diff --git a/sys/fs/smbfs/smbfs_subr.h b/sys/fs/smbfs/smbfs_subr.h new file mode 100644 index 0000000..d41e9a8 --- /dev/null +++ b/sys/fs/smbfs/smbfs_subr.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _FS_SMBFS_SMBFS_SUBR_H_ +#define _FS_SMBFS_SMBFS_SUBR_H_ + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_SMBFSDATA); +#endif + +#define SMBFSERR(format, args...) printf("%s: "format, __FUNCTION__ ,## args) + +#ifdef SMB_VNODE_DEBUG +#define SMBVDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) +#else +#define SMBVDEBUG(format, args...) +#endif + +/* + * Possible lock commands + */ +#define SMB_LOCK_EXCL 0 +#define SMB_LOCK_SHARED 1 +#define SMB_LOCK_RELEASE 2 + +struct smbmount; +struct proc; +struct timespec; +struct ucred; +struct vattr; +struct vnode; +struct statfs; + +struct smbfattr { + int fa_attr; + int64_t fa_size; + struct timespec fa_atime; + struct timespec fa_ctime; + struct timespec fa_mtime; + long fa_ino; +}; + +/* + * Context to perform findfirst/findnext/findclose operations + */ +#define SMBFS_RDD_FINDFIRST 0x01 +#define SMBFS_RDD_EOF 0x02 +#define SMBFS_RDD_FINDSINGLE 0x04 +#define SMBFS_RDD_USESEARCH 0x08 +#define SMBFS_RDD_NOCLOSE 0x10 +#define SMBFS_RDD_GOTRNAME 0x1000 + +/* + * Search context supplied by server + */ +#define SMB_SKEYLEN 21 /* search context */ +#define SMB_DENTRYLEN (SMB_SKEYLEN + 22) /* entire entry */ + +struct smbfs_fctx { + /* + * Setable values + */ + int f_flags; /* SMBFS_RDD_ */ + /* + * Return values + */ + struct smbfattr f_attr; /* current attributes */ + char * f_name; /* current file name */ + int f_nmlen; /* name len */ + /* + * Internal variables + */ + int f_limit; /* maximum number of entries */ + int f_attrmask; /* SMB_FA_ */ + int f_wclen; + const char * f_wildcard; + struct smbnode* f_dnp; + struct smb_cred*f_scred; + struct smb_share *f_ssp; + union { + struct smb_rq * uf_rq; + struct smb_t2rq * uf_t2; + } f_urq; + int f_left; /* entries left */ + int f_ecnt; /* entries left in the current reponse */ + int f_eofs; /* entry offset in the parameter block */ + u_char f_skey[SMB_SKEYLEN]; /* server side search context */ + u_char f_fname[8 + 1 + 3 + 1]; /* common case for 8.3 filenames */ + u_int16_t f_Sid; + u_int16_t f_infolevel; + int f_rnamelen; + char * f_rname; /* resume name/key */ + int f_rnameofs; +}; + +#define f_rq f_urq.uf_rq +#define f_t2 f_urq.uf_t2 + +extern int smbfs_debuglevel; + + +/* + * smb level + */ +int smbfs_smb_lock(struct smbnode *np, int op, caddr_t id, + off_t start, off_t end, struct smb_cred *scred); +int smbfs_smb_statfs2(struct smb_share *ssp, struct statfs *sbp, + struct smb_cred *scred); +int smbfs_smb_statfs(struct smb_share *ssp, struct statfs *sbp, + struct smb_cred *scred); +int smbfs_smb_setfsize(struct smbnode *np, int newsize, struct smb_cred *scred); + +int smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, + struct timespec *mtime, struct smb_cred *scred); +int smbfs_smb_setptime2(struct smbnode *np, struct timespec *mtime, + struct timespec *atime, int attr, struct smb_cred *scred); +int smbfs_smb_setpattrNT(struct smbnode *np, u_int16_t attr, + struct timespec *mtime, struct timespec *atime, struct smb_cred *scred); + +int smbfs_smb_setftime(struct smbnode *np, struct timespec *mtime, + struct timespec *atime, struct smb_cred *scred); +int smbfs_smb_setfattrNT(struct smbnode *np, u_int16_t attr, + struct timespec *mtime, struct timespec *atime, struct smb_cred *scred); + +int smbfs_smb_open(struct smbnode *np, int accmode, struct smb_cred *scred); +int smbfs_smb_close(struct smb_share *ssp, u_int16_t fid, + struct timespec *mtime, struct smb_cred *scred); +int smbfs_smb_create(struct smbnode *dnp, const char *name, int len, + struct smb_cred *scred); +int smbfs_smb_delete(struct smbnode *np, struct smb_cred *scred); +int smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, struct smb_cred *scred); +int smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, u_int16_t flags, struct smb_cred *scred); +int smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len, + struct smb_cred *scred); +int smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scred); +int smbfs_findopen(struct smbnode *dnp, const char *wildcard, int wclen, + int attr, struct smb_cred *scred, struct smbfs_fctx **ctxpp); +int smbfs_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scred); +int smbfs_findclose(struct smbfs_fctx *ctx, struct smb_cred *scred); +int smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, + struct smbnode *dnp, const char *name, int nmlen); +int smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen, + struct smbfattr *fap, struct smb_cred *scred); + +int smbfs_fname_tolocal(struct smb_vc *vcp, char *name, int nmlen, int caseopt); + +void smb_time_local2server(struct timespec *tsp, int tzoff, u_long *seconds); +void smb_time_server2local(u_long seconds, int tzoff, struct timespec *tsp); +void smb_time_NT2local(int64_t nsec, int tzoff, struct timespec *tsp); +void smb_time_local2NT(struct timespec *tsp, int tzoff, int64_t *nsec); +void smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp, + u_int16_t *dtp, u_int8_t *dhp); +void smb_dos2unixtime (u_int dd, u_int dt, u_int dh, int tzoff, struct timespec *tsp); + +#endif /* !_FS_SMBFS_SMBFS_SUBR_H_ */ diff --git a/sys/fs/smbfs/smbfs_vfsops.c b/sys/fs/smbfs/smbfs_vfsops.c new file mode 100644 index 0000000..c0f9ee1 --- /dev/null +++ b/sys/fs/smbfs/smbfs_vfsops.c @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include "opt_netsmb.h" +#ifndef NETSMB +#error "SMBFS requires option NETSMB" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include + +#include +#include +#include + +int smbfs_debuglevel = 0; + +static int smbfs_version = SMBFS_VERSION; + +#ifdef SMBFS_USEZONE +#include +#include +#include + +vm_zone_t smbfsmount_zone; +#endif + +SYSCTL_NODE(_vfs, OID_AUTO, smbfs, CTLFLAG_RW, 0, "SMB/CIFS file system"); +SYSCTL_INT(_vfs_smbfs, OID_AUTO, version, CTLFLAG_RD, &smbfs_version, 0, ""); +SYSCTL_INT(_vfs_smbfs, OID_AUTO, debuglevel, CTLFLAG_RW, &smbfs_debuglevel, 0, ""); + +static MALLOC_DEFINE(M_SMBFSHASH, "SMBFS hash", "SMBFS hash table"); + + +static int smbfs_mount(struct mount *, char *, caddr_t, + struct nameidata *, struct proc *); +static int smbfs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *); +static int smbfs_root(struct mount *, struct vnode **); +static int smbfs_start(struct mount *, int, struct proc *); +static int smbfs_statfs(struct mount *, struct statfs *, struct proc *); +static int smbfs_sync(struct mount *, int, struct ucred *, struct proc *); +static int smbfs_unmount(struct mount *, int, struct proc *); +static int smbfs_init(struct vfsconf *vfsp); +static int smbfs_uninit(struct vfsconf *vfsp); + +#if __FreeBSD_version < 400009 +static int smbfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp); +static int smbfs_fhtovp(struct mount *, struct fid *, + struct sockaddr *, struct vnode **, int *, + struct ucred **); +static int smbfs_vptofh(struct vnode *, struct fid *); +#endif + +static struct vfsops smbfs_vfsops = { + smbfs_mount, + smbfs_start, + smbfs_unmount, + smbfs_root, + smbfs_quotactl, + smbfs_statfs, + smbfs_sync, +#if __FreeBSD_version > 400008 + vfs_stdvget, + vfs_stdfhtovp, /* shouldn't happen */ + vfs_stdcheckexp, + vfs_stdvptofh, /* shouldn't happen */ +#else + smbfs_vget, + smbfs_fhtovp, + smbfs_vptofh, +#endif + smbfs_init, + smbfs_uninit, +#ifndef FB_RELENG3 + vfs_stdextattrctl, +#else +#define M_USE_RESERVE M_KERNEL + &sysctl___vfs_smbfs +#endif +}; + + +VFS_SET(smbfs_vfsops, smbfs, VFCF_NETWORK); + +MODULE_DEPEND(smbfs, netsmb, NSMB_VERSION, NSMB_VERSION, NSMB_VERSION); +MODULE_DEPEND(smbfs, libiconv, 1, 1, 1); + +int smbfs_pbuf_freecnt = -1; /* start out unlimited */ + +static int +smbfs_mount(struct mount *mp, char *path, caddr_t data, + struct nameidata *ndp, struct proc *p) +{ + struct smbfs_args args; /* will hold data from mount request */ + struct smbmount *smp = NULL; + struct smb_vc *vcp; + struct smb_share *ssp = NULL; + struct vnode *vp; + struct smb_cred scred; +#ifndef FB_CURRENT + size_t size; +#endif + int error; + char *pc, *pe; + + if (data == NULL) { + printf("missing data argument\n"); + return EINVAL; + } + if (mp->mnt_flag & MNT_UPDATE) { + printf("MNT_UPDATE not implemented"); + return EOPNOTSUPP; + } + error = copyin(data, (caddr_t)&args, sizeof(struct smbfs_args)); + if (error) + return error; + if (args.version != SMBFS_VERSION) { + printf("mount version mismatch: kernel=%d, mount=%d\n", + SMBFS_VERSION, args.version); + return EINVAL; + } + smb_makescred(&scred, p, p->p_ucred); + error = smb_dev2share(args.dev, SMBM_EXEC, &scred, &ssp); + if (error) { + printf("invalid device handle %d (%d)\n", args.dev, error); + return error; + } + vcp = SSTOVC(ssp); + smb_share_unlock(ssp, 0, p); + mp->mnt_stat.f_iosize = SSTOVC(ssp)->vc_txmax; + +#ifdef SMBFS_USEZONE + smp = zalloc(smbfsmount_zone); +#else + MALLOC(smp, struct smbmount*, sizeof(*smp), M_SMBFSDATA, M_USE_RESERVE); +#endif + if (smp == NULL) { + printf("could not alloc smbmount\n"); + error = ENOMEM; + goto bad; + } + bzero(smp, sizeof(*smp)); + mp->mnt_data = (qaddr_t)smp; + smp->sm_hash = hashinit(desiredvnodes, M_SMBFSHASH, &smp->sm_hashlen); + if (smp->sm_hash == NULL) + goto bad; + lockinit(&smp->sm_hashlock, PVFS, "smbfsh", 0, 0); + smp->sm_share = ssp; + smp->sm_root = NULL; + smp->sm_args = args; + smp->sm_caseopt = args.caseopt; + smp->sm_args.file_mode = (smp->sm_args.file_mode & + (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; + smp->sm_args.dir_mode = (smp->sm_args.dir_mode & + (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; + +/* simple_lock_init(&smp->sm_npslock);*/ +#ifndef FB_CURRENT + error = copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); + if (error) + goto bad; + bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); +#endif + pc = mp->mnt_stat.f_mntfromname; + pe = pc + sizeof(mp->mnt_stat.f_mntfromname); + bzero(pc, MNAMELEN); + *pc++ = '/'; + *pc++ = '/'; + pc=index(strncpy(pc, vcp->vc_username, pe - pc - 2), 0); + if (pc < pe-1) { + *(pc++) = '@'; + pc = index(strncpy(pc, vcp->vc_srvname, pe - pc - 2), 0); + if (pc < pe - 1) { + *(pc++) = '/'; + strncpy(pc, ssp->ss_name, pe - pc - 2); + } + } + /* protect against invalid mount points */ + smp->sm_args.mount_point[sizeof(smp->sm_args.mount_point) - 1] = '\0'; + vfs_getnewfsid(mp); + error = smbfs_root(mp, &vp); + if (error) + goto bad; + VOP_UNLOCK(vp, 0, p); + SMBVDEBUG("root.v_usecount = %d\n", vp->v_usecount); + +#ifdef DIAGNOSTICS + SMBERROR("mp=%p\n", mp); +#endif + return error; +bad: + if (smp) { + if (smp->sm_hash) + free(smp->sm_hash, M_SMBFSHASH); + lockdestroy(&smp->sm_hashlock); +#ifdef SMBFS_USEZONE + zfree(smbfsmount_zone, smp); +#else + free(smp, M_SMBFSDATA); +#endif + } + if (ssp) + smb_share_put(ssp, &scred); + return error; +} + +/* Unmount the filesystem described by mp. */ +static int +smbfs_unmount(struct mount *mp, int mntflags, struct proc *p) +{ + struct smbmount *smp = VFSTOSMBFS(mp); + struct vnode *vp; + struct smb_cred scred; + int error, flags; + + SMBVDEBUG("smbfs_unmount: flags=%04x\n", mntflags); + flags = 0; + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + error = VFS_ROOT(mp, &vp); + if (error) + return (error); + if (vp->v_usecount > 2) { + printf("smbfs_unmount: usecnt=%d\n", vp->v_usecount); + vput(vp); + return EBUSY; + } + error = vflush(mp, vp, flags); + if (error) { + vput(vp); + return error; + } + vput(vp); + vrele(vp); + vgone(vp); + smb_makescred(&scred, p, p->p_ucred); + smb_share_put(smp->sm_share, &scred); + mp->mnt_data = (qaddr_t)0; + + if (smp->sm_hash) + free(smp->sm_hash, M_SMBFSHASH); + lockdestroy(&smp->sm_hashlock); +#ifdef SMBFS_USEZONE + zfree(smbfsmount_zone, smp); +#else + free(smp, M_SMBFSDATA); +#endif + mp->mnt_flag &= ~MNT_LOCAL; + return error; +} + +/* + * Return locked root vnode of a filesystem + */ +static int +smbfs_root(struct mount *mp, struct vnode **vpp) +{ + struct smbmount *smp = VFSTOSMBFS(mp); + struct vnode *vp; + struct smbnode *np; + struct smbfattr fattr; + struct proc *p = curproc; + struct ucred *cred = p->p_ucred; + struct smb_cred scred; + int error; + + if (smp == NULL) { + SMBERROR("smp == NULL (bug in umount)\n"); + return EINVAL; + } + if (smp->sm_root) { + *vpp = SMBTOV(smp->sm_root); + return vget(*vpp, LK_EXCLUSIVE | LK_RETRY, p); + } + smb_makescred(&scred, p, cred); + error = smbfs_smb_lookup(NULL, NULL, 0, &fattr, &scred); + if (error) + return error; + error = smbfs_nget(mp, NULL, "TheRooT", 7, &fattr, &vp); + if (error) + return error; + vp->v_flag |= VROOT; + np = VTOSMB(vp); + smp->sm_root = np; + *vpp = vp; + return 0; +} + +/* + * Vfs start routine, a no-op. + */ +/* ARGSUSED */ +static int +smbfs_start(mp, flags, p) + struct mount *mp; + int flags; + struct proc *p; +{ + SMBVDEBUG("flags=%04x\n", flags); + return 0; +} + +/* + * Do operations associated with quotas, not supported + */ +/* ARGSUSED */ +static int +smbfs_quotactl(mp, cmd, uid, arg, p) + struct mount *mp; + int cmd; + uid_t uid; + caddr_t arg; + struct proc *p; +{ + SMBVDEBUG("return EOPNOTSUPP\n"); + return EOPNOTSUPP; +} + +/*ARGSUSED*/ +int +smbfs_init(struct vfsconf *vfsp) +{ +#ifndef SMP + int name[2]; + int olen, ncpu, plen, error; + + name[0] = CTL_HW; + name[1] = HW_NCPU; + error = kernel_sysctl(curproc, name, 2, &ncpu, &olen, NULL, 0, &plen); + if (error == 0 && ncpu > 1) + printf("warning: smbfs module compiled without SMP support."); +#endif + +#ifdef SMBFS_USEZONE + smbfsmount_zone = zinit("SMBFSMOUNT", sizeof(struct smbmount), 0, 0, 1); +#endif + smbfs_pbuf_freecnt = nswbuf / 2 + 1; + SMBVDEBUG("done.\n"); + return 0; +} + +/*ARGSUSED*/ +int +smbfs_uninit(struct vfsconf *vfsp) +{ + + SMBVDEBUG("done.\n"); + return 0; +} + +/* + * smbfs_statfs call + */ +int +smbfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p) +{ + struct smbmount *smp = VFSTOSMBFS(mp); + struct smbnode *np = smp->sm_root; + struct smb_share *ssp = smp->sm_share; + struct smb_cred scred; + int error = 0; + + if (np == NULL) + return EINVAL; + + sbp->f_iosize = SSTOVC(ssp)->vc_txmax; /* optimal transfer block size */ + sbp->f_spare2 = 0; /* placeholder */ + smb_makescred(&scred, p, p->p_ucred); + + if (SMB_DIALECT(SSTOVC(ssp)) >= SMB_DIALECT_LANMAN2_0) + error = smbfs_smb_statfs2(ssp, sbp, &scred); + else + error = smbfs_smb_statfs(ssp, sbp, &scred); + if (error) + return error; + sbp->f_flags = 0; /* copy of mount exported flags */ + if (sbp != &mp->mnt_stat) { + sbp->f_fsid = mp->mnt_stat.f_fsid; /* file system id */ + sbp->f_owner = mp->mnt_stat.f_owner; /* user that mounted the filesystem */ + sbp->f_type = mp->mnt_vfc->vfc_typenum; /* type of filesystem */ + bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); + bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); + } + strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); + return 0; +} + +/* + * Flush out the buffer cache + */ +/* ARGSUSED */ +static int +smbfs_sync(mp, waitfor, cred, p) + struct mount *mp; + int waitfor; + struct ucred *cred; + struct proc *p; +{ + struct vnode *vp; + int error, allerror = 0; + /* + * Force stale buffer cache information to be flushed. + */ +loop: + for (vp = mp->mnt_vnodelist.lh_first; + vp != NULL; + vp = vp->v_mntvnodes.le_next) { + /* + * If the vnode that we are about to sync is no longer + * associated with this mount point, start over. + */ + if (vp->v_mount != mp) + goto loop; +#ifndef FB_RELENG3 + if (VOP_ISLOCKED(vp, NULL) || TAILQ_EMPTY(&vp->v_dirtyblkhd) || +#else + if (VOP_ISLOCKED(vp) || TAILQ_EMPTY(&vp->v_dirtyblkhd) || +#endif + waitfor == MNT_LAZY) + continue; + if (vget(vp, LK_EXCLUSIVE, p)) + goto loop; + error = VOP_FSYNC(vp, cred, waitfor, p); + if (error) + allerror = error; + vput(vp); + } + return (allerror); +} + +#if __FreeBSD_version < 400009 +/* + * smbfs flat namespace lookup. Unsupported. + */ +/* ARGSUSED */ +static int smbfs_vget(mp, ino, vpp) + struct mount *mp; + ino_t ino; + struct vnode **vpp; +{ + return (EOPNOTSUPP); +} + +/* ARGSUSED */ +static int smbfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) + struct mount *mp; + struct fid *fhp; + struct sockaddr *nam; + struct vnode **vpp; + int *exflagsp; + struct ucred **credanonp; +{ + return (EINVAL); +} + +/* + * Vnode pointer to File handle, should never happen either + */ +/* ARGSUSED */ +static int +smbfs_vptofh(vp, fhp) + struct vnode *vp; + struct fid *fhp; +{ + return (EINVAL); +} + +#endif /* __FreeBSD_version < 400009 */ diff --git a/sys/fs/smbfs/smbfs_vnops.c b/sys/fs/smbfs/smbfs_vnops.c new file mode 100644 index 0000000..1d0637e --- /dev/null +++ b/sys/fs/smbfs/smbfs_vnops.c @@ -0,0 +1,1340 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include +#include +#include + +#include +#include +#include + +/* + * Prototypes for SMBFS vnode operations + */ +static int smbfs_create(struct vop_create_args *); +static int smbfs_mknod(struct vop_mknod_args *); +static int smbfs_open(struct vop_open_args *); +static int smbfs_close(struct vop_close_args *); +static int smbfs_access(struct vop_access_args *); +static int smbfs_getattr(struct vop_getattr_args *); +static int smbfs_setattr(struct vop_setattr_args *); +static int smbfs_read(struct vop_read_args *); +static int smbfs_write(struct vop_write_args *); +static int smbfs_fsync(struct vop_fsync_args *); +static int smbfs_remove(struct vop_remove_args *); +static int smbfs_link(struct vop_link_args *); +static int smbfs_lookup(struct vop_lookup_args *); +static int smbfs_rename(struct vop_rename_args *); +static int smbfs_mkdir(struct vop_mkdir_args *); +static int smbfs_rmdir(struct vop_rmdir_args *); +static int smbfs_symlink(struct vop_symlink_args *); +static int smbfs_readdir(struct vop_readdir_args *); +static int smbfs_bmap(struct vop_bmap_args *); +static int smbfs_strategy(struct vop_strategy_args *); +static int smbfs_print(struct vop_print_args *); +static int smbfs_pathconf(struct vop_pathconf_args *ap); +static int smbfs_advlock(struct vop_advlock_args *); +#ifndef FB_RELENG3 +static int smbfs_getextattr(struct vop_getextattr_args *ap); +#endif + +vop_t **smbfs_vnodeop_p; +static struct vnodeopv_entry_desc smbfs_vnodeop_entries[] = { + { &vop_default_desc, (vop_t *) vop_defaultop }, + { &vop_access_desc, (vop_t *) smbfs_access }, + { &vop_advlock_desc, (vop_t *) smbfs_advlock }, + { &vop_bmap_desc, (vop_t *) smbfs_bmap }, + { &vop_close_desc, (vop_t *) smbfs_close }, + { &vop_create_desc, (vop_t *) smbfs_create }, + { &vop_fsync_desc, (vop_t *) smbfs_fsync }, + { &vop_getattr_desc, (vop_t *) smbfs_getattr }, + { &vop_getpages_desc, (vop_t *) smbfs_getpages }, + { &vop_inactive_desc, (vop_t *) smbfs_inactive }, + { &vop_ioctl_desc, (vop_t *) smbfs_ioctl }, + { &vop_islocked_desc, (vop_t *) vop_stdislocked }, + { &vop_link_desc, (vop_t *) smbfs_link }, + { &vop_lock_desc, (vop_t *) vop_stdlock }, + { &vop_lookup_desc, (vop_t *) smbfs_lookup }, + { &vop_mkdir_desc, (vop_t *) smbfs_mkdir }, + { &vop_mknod_desc, (vop_t *) smbfs_mknod }, + { &vop_open_desc, (vop_t *) smbfs_open }, + { &vop_pathconf_desc, (vop_t *) smbfs_pathconf }, + { &vop_print_desc, (vop_t *) smbfs_print }, + { &vop_putpages_desc, (vop_t *) smbfs_putpages }, + { &vop_read_desc, (vop_t *) smbfs_read }, + { &vop_readdir_desc, (vop_t *) smbfs_readdir }, + { &vop_reclaim_desc, (vop_t *) smbfs_reclaim }, + { &vop_remove_desc, (vop_t *) smbfs_remove }, + { &vop_rename_desc, (vop_t *) smbfs_rename }, + { &vop_rmdir_desc, (vop_t *) smbfs_rmdir }, + { &vop_setattr_desc, (vop_t *) smbfs_setattr }, + { &vop_strategy_desc, (vop_t *) smbfs_strategy }, + { &vop_symlink_desc, (vop_t *) smbfs_symlink }, + { &vop_unlock_desc, (vop_t *) vop_stdunlock }, + { &vop_write_desc, (vop_t *) smbfs_write }, +#ifndef FB_RELENG3 + { &vop_getextattr_desc, (vop_t *) smbfs_getextattr }, +/* { &vop_setextattr_desc, (vop_t *) smbfs_setextattr },*/ +#endif + { NULL, NULL } +}; + +static struct vnodeopv_desc smbfs_vnodeop_opv_desc = + { &smbfs_vnodeop_p, smbfs_vnodeop_entries }; + +VNODEOP_SET(smbfs_vnodeop_opv_desc); + +static int +smbfs_access(ap) + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct ucred *cred = ap->a_cred; + u_int mode = ap->a_mode; + struct smbmount *smp = VTOSMBFS(vp); + int error = 0; + + SMBVDEBUG("\n"); + if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { + switch (vp->v_type) { + case VREG: case VDIR: case VLNK: + return EROFS; + default: + break; + } + } + if (cred->cr_uid == 0) + return 0; + if (cred->cr_uid != smp->sm_args.uid) { + mode >>= 3; + if (!groupmember(smp->sm_args.gid, cred)) + mode >>= 3; + } + error = (((vp->v_type == VREG) ? smp->sm_args.file_mode : smp->sm_args.dir_mode) & mode) == mode ? 0 : EACCES; + return error; +} + +/* ARGSUSED */ +static int +smbfs_open(ap) + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + struct vattr vattr; + int mode = ap->a_mode; + int error, accmode; + + SMBVDEBUG("%s,%d\n", np->n_name, np->n_opencount); + if (vp->v_type != VREG && vp->v_type != VDIR) { + SMBFSERR("open eacces vtype=%d\n", vp->v_type); + return EACCES; + } + if (vp->v_type == VDIR) { + np->n_opencount++; + return 0; + } + if (np->n_flag & NMODIFIED) { + if ((error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) + return error; + smbfs_attr_cacheremove(vp); + error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p); + if (error) + return error; + np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; + } else { + error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p); + if (error) + return error; + if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) { + error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); + if (error == EINTR) + return error; + np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; + } + } + if (np->n_opencount) { + np->n_opencount++; + return 0; + } + accmode = SMB_AM_OPENREAD; + if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) + accmode = SMB_AM_OPENRW; + smb_makescred(&scred, ap->a_p, ap->a_cred); + error = smbfs_smb_open(np, accmode, &scred); + if (error) { + if (mode & FWRITE) + return EACCES; + accmode = SMB_AM_OPENREAD; + error = smbfs_smb_open(np, accmode, &scred); + } + if (!error) { + np->n_opencount++; + } + smbfs_attr_cacheremove(vp); + return error; +} + +static int +smbfs_closel(struct vop_close_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct proc *p = ap->a_p; + struct smb_cred scred; + struct vattr vattr; + int error; + + SMBVDEBUG("name=%s, pid=%d, c=%d\n",np->n_name, p->p_pid, np->n_opencount); + + smb_makescred(&scred, p, ap->a_cred); + + if (np->n_opencount == 0) { + SMBERROR("Negative opencount\n"); + return 0; + } + np->n_opencount--; + if (vp->v_type == VDIR) { + if (np->n_opencount) + return 0; + if (np->n_dirseq) { + smbfs_findclose(np->n_dirseq, &scred); + np->n_dirseq = NULL; + } + error = 0; + } else { + error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, p, 1); + if (np->n_opencount) + return error; + VOP_GETATTR(vp, &vattr, ap->a_cred, p); + error = smbfs_smb_close(np->n_mount->sm_share, np->n_fid, + &np->n_mtime, &scred); + } + smbfs_attr_cacheremove(vp); + return error; +} + +/* + * XXX: VOP_CLOSE() usually called without lock held which is suck. Here we + * do some heruistic to determine if vnode should be locked. + */ +static int +smbfs_close(ap) + struct vop_close_args /* { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct proc *p = ap->a_p; + int error, dolock; + + VI_LOCK(vp); + dolock = (vp->v_flag & VXLOCK) == 0; + if (dolock) + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_INTERLOCK, p); + else + VI_UNLOCK(vp); + error = smbfs_closel(ap); + if (dolock) + VOP_UNLOCK(vp, 0, p); + return error; +} + +/* + * smbfs_getattr call from vfs. + */ +static int +smbfs_getattr(ap) + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct vattr *va=ap->a_vap; + struct smbfattr fattr; + struct smb_cred scred; + u_int32_t oldsize; + int error; + + SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_flag & VROOT) != 0); + error = smbfs_attr_cachelookup(vp, va); + if (!error) + return 0; + SMBVDEBUG("not in the cache\n"); + smb_makescred(&scred, ap->a_p, ap->a_cred); + oldsize = np->n_size; + error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred); + if (error) { + SMBVDEBUG("error %d\n", error); + return error; + } + smbfs_attr_cacheenter(vp, &fattr); + smbfs_attr_cachelookup(vp, va); + if (np->n_opencount) + np->n_size = oldsize; + return 0; +} + +static int +smbfs_setattr(ap) + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct vattr *vap = ap->a_vap; + struct timespec *mtime, *atime; + struct smb_cred scred; + struct smb_share *ssp = np->n_mount->sm_share; + struct smb_vc *vcp = SSTOVC(ssp); + u_quad_t tsize = 0; + int isreadonly, doclose, error = 0; + + SMBVDEBUG("\n"); + if (vap->va_flags != VNOVAL) + return EOPNOTSUPP; + isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY); + /* + * Disallow write attempts if the filesystem is mounted read-only. + */ + if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || + vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || + vap->va_mode != (mode_t)VNOVAL) && isreadonly) + return EROFS; + smb_makescred(&scred, ap->a_p, ap->a_cred); + if (vap->va_size != VNOVAL) { + switch (vp->v_type) { + case VDIR: + return EISDIR; + case VREG: + break; + default: + return EINVAL; + }; + if (isreadonly) + return EROFS; + doclose = 0; + vnode_pager_setsize(vp, (u_long)vap->va_size); + tsize = np->n_size; + np->n_size = vap->va_size; + if (np->n_opencount == 0) { + error = smbfs_smb_open(np, SMB_AM_OPENRW, &scred); + if (error == 0) + doclose = 1; + } + if (error == 0) + error = smbfs_smb_setfsize(np, vap->va_size, &scred); + if (doclose) + smbfs_smb_close(ssp, np->n_fid, NULL, &scred); + if (error) { + np->n_size = tsize; + vnode_pager_setsize(vp, (u_long)tsize); + return error; + } + } + mtime = atime = NULL; + if (vap->va_mtime.tv_sec != VNOVAL) + mtime = &vap->va_mtime; + if (vap->va_atime.tv_sec != VNOVAL) + atime = &vap->va_atime; + if (mtime != atime) { +#if 0 + if (mtime == NULL) + mtime = &np->n_mtime; + if (atime == NULL) + atime = &np->n_atime; +#endif + /* + * If file is opened, then we can use handle based calls. + * If not, use path based ones. + */ + if (np->n_opencount == 0) { + if (vcp->vc_flags & SMBV_WIN95) { + error = VOP_OPEN(vp, FWRITE, ap->a_cred, ap->a_p); + if (!error) { +/* error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); + VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p);*/ + if (mtime) + np->n_mtime = *mtime; + VOP_CLOSE(vp, FWRITE, ap->a_cred, ap->a_p); + } + } else if ((vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)) { + error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred); +/* error = smbfs_smb_setpattrNT(np, 0, mtime, atime, &scred);*/ + } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) { + error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred); + } else { + error = smbfs_smb_setpattr(np, 0, mtime, &scred); + } + } else { + if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { + error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); + } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) { + error = smbfs_smb_setftime(np, mtime, atime, &scred); + } else { + /* + * I have no idea how to handle this for core + * level servers. The possible solution is to + * update mtime after file is closed. + */ + SMBERROR("can't update times on an opened file\n"); + } + } + } + /* + * Invalidate attribute cache in case if server doesn't set + * required attributes. + */ + smbfs_attr_cacheremove(vp); /* invalidate cache */ + VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); + np->n_mtime.tv_sec = vap->va_mtime.tv_sec; + return error; +} +/* + * smbfs_read call. + */ +static int +smbfs_read(ap) + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + + SMBVDEBUG("\n"); + if (vp->v_type != VREG && vp->v_type != VDIR) + return EPERM; + return smbfs_readvnode(vp, uio, ap->a_cred); +} + +static int +smbfs_write(ap) + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + + SMBVDEBUG("%d,ofs=%d,sz=%d\n",vp->v_type, (int)uio->uio_offset, uio->uio_resid); + if (vp->v_type != VREG) + return (EPERM); + return smbfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag); +} +/* + * smbfs_create call + * Create a regular file. On entry the directory to contain the file being + * created is locked. We must release before we return. We must also free + * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or + * only if the SAVESTART bit in cn_flags is clear on success. + */ +static int +smbfs_create(ap) + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + struct vnode *dvp = ap->a_dvp; + struct vattr *vap = ap->a_vap; + struct vnode **vpp=ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + struct smbnode *dnp = VTOSMB(dvp); + struct vnode *vp; + struct vattr vattr; + struct smbfattr fattr; + struct smb_cred scred; + char *name = cnp->cn_nameptr; + int nmlen = cnp->cn_namelen; + int error; + + + SMBVDEBUG("\n"); + *vpp = NULL; + if (vap->va_type != VREG) + return EOPNOTSUPP; + if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc))) + return error; + smb_makescred(&scred, cnp->cn_proc, cnp->cn_cred); + + error = smbfs_smb_create(dnp, name, nmlen, &scred); + if (error) + return error; + error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred); + if (error) + return error; + error = smbfs_nget(VTOVFS(dvp), dvp, name, nmlen, &fattr, &vp); + if (error) + return error; + *vpp = vp; + if (cnp->cn_flags & MAKEENTRY) + cache_enter(dvp, vp, cnp); + return error; +} + +static int +smbfs_remove(ap) + struct vop_remove_args /* { + struct vnodeop_desc *a_desc; + struct vnode * a_dvp; + struct vnode * a_vp; + struct componentname * a_cnp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; +/* struct vnode *dvp = ap->a_dvp;*/ + struct componentname *cnp = ap->a_cnp; + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + int error; + + if (vp->v_type == VDIR || np->n_opencount || vp->v_usecount != 1) + return EPERM; + smb_makescred(&scred, cnp->cn_proc, cnp->cn_cred); + error = smbfs_smb_delete(np, &scred); + cache_purge(vp); + return error; +} + +/* + * smbfs_file rename call + */ +static int +smbfs_rename(ap) + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap; +{ + struct vnode *fvp = ap->a_fvp; + struct vnode *tvp = ap->a_tvp; + struct vnode *fdvp = ap->a_fdvp; + struct vnode *tdvp = ap->a_tdvp; + struct componentname *tcnp = ap->a_tcnp; +/* struct componentname *fcnp = ap->a_fcnp;*/ + struct smb_cred scred; + u_int16_t flags = 6; + int error=0; + + /* Check for cross-device rename */ + if ((fvp->v_mount != tdvp->v_mount) || + (tvp && (fvp->v_mount != tvp->v_mount))) { + error = EXDEV; + goto out; + } + + if (tvp && tvp->v_usecount > 1) { + error = EBUSY; + goto out; + } + flags = 0x10; /* verify all writes */ + if (fvp->v_type == VDIR) { + flags |= 2; + } else if (fvp->v_type == VREG) { + flags |= 1; + } else + return EINVAL; + smb_makescred(&scred, tcnp->cn_proc, tcnp->cn_cred); + /* + * It seems that Samba doesn't implement SMB_COM_MOVE call... + */ +#ifdef notnow + if (SMB_DIALECT(SSTOCN(smp->sm_share)) >= SMB_DIALECT_LANMAN1_0) { + error = smbfs_smb_move(VTOSMB(fvp), VTOSMB(tdvp), + tcnp->cn_nameptr, tcnp->cn_namelen, flags, &scred); + } else +#endif + { + /* + * We have to do the work atomicaly + */ + if (tvp && tvp != fvp) { + error = smbfs_smb_delete(VTOSMB(tvp), &scred); + if (error) + goto out; + } + error = smbfs_smb_rename(VTOSMB(fvp), VTOSMB(tdvp), + tcnp->cn_nameptr, tcnp->cn_namelen, &scred); + } + + if (fvp->v_type == VDIR) { + if (tvp != NULL && tvp->v_type == VDIR) + cache_purge(tdvp); + cache_purge(fdvp); + } +out: + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + vrele(fdvp); + vrele(fvp); + smbfs_attr_cacheremove(fdvp); + smbfs_attr_cacheremove(tdvp); +#ifdef possible_mistake + vgone(fvp); + if (tvp) + vgone(tvp); +#endif + return error; +} + +/* + * somtime it will come true... + */ +static int +smbfs_link(ap) + struct vop_link_args /* { + struct vnode *a_tdvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; +{ + return EOPNOTSUPP; +} + +/* + * smbfs_symlink link create call. + * Sometime it will be functional... + */ +static int +smbfs_symlink(ap) + struct vop_symlink_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + char *a_target; + } */ *ap; +{ + return EOPNOTSUPP; +} + +static int +smbfs_mknod(ap) + struct vop_mknod_args /* { + } */ *ap; +{ + return EOPNOTSUPP; +} + +static int +smbfs_mkdir(ap) + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + struct vnode *dvp = ap->a_dvp; +/* struct vattr *vap = ap->a_vap;*/ + struct vnode *vp; + struct componentname *cnp = ap->a_cnp; + struct smbnode *dnp = VTOSMB(dvp); + struct vattr vattr; + struct smb_cred scred; + struct smbfattr fattr; + char *name = cnp->cn_nameptr; + int len = cnp->cn_namelen; + int error; + + if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc))) { + return error; + } + if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))) + return EEXIST; + smb_makescred(&scred, cnp->cn_proc, cnp->cn_cred); + error = smbfs_smb_mkdir(dnp, name, len, &scred); + if (error) + return error; + error = smbfs_smb_lookup(dnp, name, len, &fattr, &scred); + if (error) + return error; + error = smbfs_nget(VTOVFS(dvp), dvp, name, len, &fattr, &vp); + if (error) + return error; + *ap->a_vpp = vp; + return 0; +} + +/* + * smbfs_remove directory call + */ +static int +smbfs_rmdir(ap) + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct vnode *dvp = ap->a_dvp; + struct componentname *cnp = ap->a_cnp; +/* struct smbmount *smp = VTOSMBFS(vp);*/ + struct smbnode *dnp = VTOSMB(dvp); + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + int error; + + if (dvp == vp) + return EINVAL; + + smb_makescred(&scred, cnp->cn_proc, cnp->cn_cred); + error = smbfs_smb_rmdir(np, &scred); + dnp->n_flag |= NMODIFIED; + smbfs_attr_cacheremove(dvp); +/* cache_purge(dvp);*/ + cache_purge(vp); + return error; +} + +/* + * smbfs_readdir call + */ +static int +smbfs_readdir(ap) + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + int *a_eofflag; + u_long *a_cookies; + int a_ncookies; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + int error; + + if (vp->v_type != VDIR) + return (EPERM); +#ifdef notnow + if (ap->a_ncookies) { + printf("smbfs_readdir: no support for cookies now..."); + return (EOPNOTSUPP); + } +#endif + error = smbfs_readvnode(vp, uio, ap->a_cred); + return error; +} + +/* ARGSUSED */ +static int +smbfs_fsync(ap) + struct vop_fsync_args /* { + struct vnodeop_desc *a_desc; + struct vnode * a_vp; + struct ucred * a_cred; + int a_waitfor; + struct proc * a_p; + } */ *ap; +{ +/* return (smb_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_p, 1));*/ + return (0); +} + +static +int smbfs_print (ap) + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + + if (np == NULL) { + printf("no smbnode data\n"); + return (0); + } + printf("tag VT_SMBFS, name = %s, parent = %p, opencount = %d", + np->n_name, np->n_parent ? SMBTOV(np->n_parent) : NULL, + np->n_opencount); + lockmgr_printinfo(&vp->v_lock); + printf("\n"); + return (0); +} + +static int +smbfs_pathconf (ap) + struct vop_pathconf_args /* { + struct vnode *vp; + int name; + register_t *retval; + } */ *ap; +{ + struct smbmount *smp = VFSTOSMBFS(VTOVFS(ap->a_vp)); + struct smb_vc *vcp = SSTOVC(smp->sm_share); + register_t *retval = ap->a_retval; + int error = 0; + + switch (ap->a_name) { + case _PC_LINK_MAX: + *retval = 0; + break; + case _PC_NAME_MAX: + *retval = (vcp->vc_flags & SMBV_LONGNAMES) ? 255 : 12; + break; + case _PC_PATH_MAX: + *retval = 800; /* XXX: a correct one ? */ + break; + default: + error = EINVAL; + } + return error; +} + +static int +smbfs_strategy (ap) + struct vop_strategy_args /* { + struct buf *a_bp + } */ *ap; +{ + struct buf *bp=ap->a_bp; + struct ucred *cr; + struct proc *p; + int error = 0; + + SMBVDEBUG("\n"); + if (bp->b_flags & B_PHYS) + panic("smbfs physio"); + if (bp->b_flags & B_ASYNC) + p = (struct proc *)0; + else + p = curproc; /* XXX */ + if (bp->b_iocmd == BIO_READ) + cr = bp->b_rcred; + else + cr = bp->b_wcred; + + if ((bp->b_flags & B_ASYNC) == 0 ) + error = smbfs_doio(bp, cr, p); + return error; +} + +static int +smbfs_bmap(ap) + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + int *a_runb; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + + if (ap->a_vpp != NULL) + *ap->a_vpp = vp; + if (ap->a_bnp != NULL) + *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize); + if (ap->a_runp != NULL) + *ap->a_runp = 0; + if (ap->a_runb != NULL) + *ap->a_runb = 0; + return (0); +} + +int +smbfs_ioctl(ap) + struct vop_ioctl_args /* { + struct vnode *a_vp; + u_long a_command; + caddr_t a_data; + int fflag; + struct ucred *cred; + struct proc *p; + } */ *ap; +{ + return EINVAL; +} + +static char smbfs_atl[] = "rhsvda"; +static int +smbfs_getextattr(struct vop_getextattr_args *ap) +/* { + IN struct vnode *a_vp; + IN char *a_name; + INOUT struct uio *a_uio; + IN struct ucred *a_cred; + IN struct proc *a_p; +}; +*/ +{ + struct vnode *vp = ap->a_vp; + struct proc *p = ap->a_p; + struct ucred *cred = ap->a_cred; + struct uio *uio = ap->a_uio; + const char *name = ap->a_name; + struct smbnode *np = VTOSMB(vp); + struct vattr vattr; + char buf[10]; + int i, attr, error; + + error = VOP_ACCESS(vp, VREAD, cred, p); + if (error) + return error; + error = VOP_GETATTR(vp, &vattr, cred, p); + if (error) + return error; + if (strcmp(name, "dosattr") == 0) { + attr = np->n_dosattr; + for (i = 0; i < 6; i++, attr >>= 1) + buf[i] = (attr & 1) ? smbfs_atl[i] : '-'; + buf[i] = 0; + error = uiomove(buf, i, uio); + + } else + error = EINVAL; + return error; +} + +/* + * Since we expected to support F_GETLK (and SMB protocol has no such function), + * it is necessary to use lf_advlock(). It would be nice if this function had + * a callback mechanism because it will help to improve a level of consistency. + */ +int +smbfs_advlock(ap) + struct vop_advlock_args /* { + struct vnode *a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct smbnode *np = VTOSMB(vp); + struct flock *fl = ap->a_fl; + caddr_t id = (caddr_t)1 /* ap->a_id */; +/* int flags = ap->a_flags;*/ + struct proc *p = curproc; + struct smb_cred scred; + off_t start, end, size; + int error, lkop; + + if (vp->v_type == VDIR) { + /* + * SMB protocol have no support for directory locking. + * Although locks can be processed on local machine, I don't + * think that this is a good idea, because some programs + * can work wrong assuming directory is locked. So, we just + * return 'operation not supported + */ + return EOPNOTSUPP; + } + size = np->n_size; + switch (fl->l_whence) { + case SEEK_SET: + case SEEK_CUR: + start = fl->l_start; + break; + case SEEK_END: + start = fl->l_start + size; + default: + return EINVAL; + } + if (start < 0) + return EINVAL; + if (fl->l_len == 0) + end = -1; + else { + end = start + fl->l_len - 1; + if (end < start) + return EINVAL; + } + smb_makescred(&scred, p, p ? p->p_ucred : NULL); + switch (ap->a_op) { + case F_SETLK: + switch (fl->l_type) { + case F_WRLCK: + lkop = SMB_LOCK_EXCL; + break; + case F_RDLCK: + lkop = SMB_LOCK_SHARED; + break; + case F_UNLCK: + lkop = SMB_LOCK_RELEASE; + break; + default: + return EINVAL; + } + error = lf_advlock(ap, &np->n_lockf, size); + if (error) + break; + lkop = SMB_LOCK_EXCL; + error = smbfs_smb_lock(np, lkop, id, start, end, &scred); + if (error) { + ap->a_op = F_UNLCK; + lf_advlock(ap, &np->n_lockf, size); + } + break; + case F_UNLCK: + lf_advlock(ap, &np->n_lockf, size); + error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred); + break; + case F_GETLK: + error = lf_advlock(ap, &np->n_lockf, size); + break; + default: + return EINVAL; + } + return error; +} + +static int +smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop) +{ + static const char *badchars = "*/\[]:<>=;?"; + static const char *badchars83 = " +|,"; + const char *cp; + int i, error; + + if (nameiop == LOOKUP) + return 0; + error = ENOENT; + if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) { + /* + * Name should conform 8.3 format + */ + if (nmlen > 12) + return ENAMETOOLONG; + cp = index(name, '.'); + if (cp == NULL) + return error; + if (cp == name || (cp - name) > 8) + return error; + cp = index(cp + 1, '.'); + if (cp != NULL) + return error; + for (cp = name, i = 0; i < nmlen; i++, cp++) + if (index(badchars83, *cp) != NULL) + return error; + } + for (cp = name, i = 0; i < nmlen; i++, cp++) + if (index(badchars, *cp) != NULL) + return error; + return 0; +} + +#ifndef PDIRUNLOCK +#define PDIRUNLOCK 0 +#endif + +/* + * Things go even weird without fixed inode numbers... + */ +int +smbfs_lookup(ap) + struct vop_lookup_args /* { + struct vnodeop_desc *a_desc; + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap; +{ + struct componentname *cnp = ap->a_cnp; + struct proc *p = cnp->cn_proc; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vnode *vp; + struct smbmount *smp; + struct mount *mp = dvp->v_mount; + struct smbnode *dnp; + struct smbfattr fattr, *fap; + struct smb_cred scred; + char *name = cnp->cn_nameptr; + int flags = cnp->cn_flags; + int nameiop = cnp->cn_nameiop; + int nmlen = cnp->cn_namelen; + int lockparent, wantparent, error, islastcn, isdot; + + SMBVDEBUG("\n"); + cnp->cn_flags &= ~PDIRUNLOCK; + if (dvp->v_type != VDIR) + return ENOTDIR; + if ((flags & ISDOTDOT) && (dvp->v_flag & VROOT)) { + SMBFSERR("invalid '..'\n"); + return EIO; + } +#ifdef SMB_VNODE_DEBUG + { + char *cp, c; + + cp = name + nmlen; + c = *cp; + *cp = 0; + SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name, + VTOSMB(dvp)->n_name); + *cp = c; + } +#endif + islastcn = flags & ISLASTCN; + if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP)) + return EROFS; + if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, p)) != 0) + return error; + lockparent = flags & LOCKPARENT; + wantparent = flags & (LOCKPARENT|WANTPARENT); + smp = VFSTOSMBFS(mp); + dnp = VTOSMB(dvp); + isdot = (nmlen == 1 && name[0] == '.'); + + error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop); + + if (error) + return ENOENT; + + error = cache_lookup(dvp, vpp, cnp); + SMBVDEBUG("cache_lookup returned %d\n", error); + if (error > 0) + return error; + if (error) { /* name was found */ + struct vattr vattr; + int vpid; + + vp = *vpp; + vpid = vp->v_id; + if (dvp == vp) { /* lookup on current */ + vref(vp); + error = 0; + SMBVDEBUG("cached '.'\n"); + } else if (flags & ISDOTDOT) { + VOP_UNLOCK(dvp, 0, p); /* unlock parent */ + cnp->cn_flags |= PDIRUNLOCK; + error = vget(vp, LK_EXCLUSIVE, p); + if (!error && lockparent && islastcn) { + error = vn_lock(dvp, LK_EXCLUSIVE, p); + if (error == 0) + cnp->cn_flags &= ~PDIRUNLOCK; + } + } else { + error = vget(vp, LK_EXCLUSIVE, p); + if (!lockparent || error || !islastcn) { + VOP_UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + } + } + if (!error) { + if (vpid == vp->v_id) { + if (!VOP_GETATTR(vp, &vattr, cnp->cn_cred, p) + /* && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) { + if (nameiop != LOOKUP && islastcn) + cnp->cn_flags |= SAVENAME; + SMBVDEBUG("use cached vnode\n"); + return (0); + } + cache_purge(vp); + } + vput(vp); + if (lockparent && dvp != vp && islastcn) + VOP_UNLOCK(dvp, 0, p); + } + error = vn_lock(dvp, LK_EXCLUSIVE, p); + *vpp = NULLVP; + if (error) { + cnp->cn_flags |= PDIRUNLOCK; + return (error); + } + cnp->cn_flags &= ~PDIRUNLOCK; + } + /* + * entry is not in the cache or has been expired + */ + error = 0; + *vpp = NULLVP; + smb_makescred(&scred, p, cnp->cn_cred); + fap = &fattr; + if (flags & ISDOTDOT) { + error = smbfs_smb_lookup(dnp->n_parent, NULL, 0, fap, &scred); + SMBVDEBUG("result of dotdot lookup: %d\n", error); + } else { + fap = &fattr; + error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred); +/* if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/ + SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error); + } + if (error && error != ENOENT) + return error; + if (error) { /* entry not found */ + /* + * Handle RENAME or CREATE case... + */ + if ((nameiop == CREATE || nameiop == RENAME) && wantparent && islastcn) { + cnp->cn_flags |= SAVENAME; + if (!lockparent) { + VOP_UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + } + return (EJUSTRETURN); + } + return ENOENT; + }/* else { + SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum); + }*/ + /* + * handle DELETE case ... + */ + if (nameiop == DELETE && islastcn) { /* delete last component */ + error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, p); + if (error) + return error; + if (isdot) { + VREF(dvp); + *vpp = dvp; + return 0; + } + error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); + if (error) + return error; + *vpp = vp; + cnp->cn_flags |= SAVENAME; + if (!lockparent) { + VOP_UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + } + return 0; + } + if (nameiop == RENAME && islastcn && wantparent) { + error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, p); + if (error) + return error; + if (isdot) + return EISDIR; + error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); + if (error) + return error; + *vpp = vp; + cnp->cn_flags |= SAVENAME; + if (!lockparent) { + VOP_UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + } + return 0; + } + if (flags & ISDOTDOT) { + VOP_UNLOCK(dvp, 0, p); + error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp); + if (error) { + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); + return error; + } + if (lockparent && islastcn) { + error = vn_lock(dvp, LK_EXCLUSIVE, p); + if (error) { + cnp->cn_flags |= PDIRUNLOCK; + vput(vp); + return error; + } + } + *vpp = vp; + } else if (isdot) { + vref(dvp); + *vpp = dvp; + } else { + error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); + if (error) + return error; + *vpp = vp; + SMBVDEBUG("lookup: getnewvp!\n"); + if (!lockparent || !islastcn) { + VOP_UNLOCK(dvp, 0, p); + cnp->cn_flags |= PDIRUNLOCK; + } + } + if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) { +/* VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/ + cache_enter(dvp, *vpp, cnp); + } + return 0; +} -- cgit v1.1