diff --git a/sys/fs/hpfs/hpfs_vnops.c b/sys/fs/hpfs/hpfs_vnops.c index 38f54e643b21..7df5b497ce2f 100644 --- a/sys/fs/hpfs/hpfs_vnops.c +++ b/sys/fs/hpfs/hpfs_vnops.c @@ -1,1258 +1,1259 @@ /*- * Copyright (c) 1998, 1999 Semen Ustimenko (semenu@FreeBSD.org) * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for pathconf(2) constants */ #include #include #include #include static int hpfs_de_uiomove(struct hpfsmount *, struct hpfsdirent *, struct uio *); static int hpfs_ioctl(struct vop_ioctl_args *ap); static int hpfs_read(struct vop_read_args *); static int hpfs_write(struct vop_write_args *ap); static int hpfs_getattr(struct vop_getattr_args *ap); static int hpfs_setattr(struct vop_setattr_args *ap); static int hpfs_inactive(struct vop_inactive_args *ap); static int hpfs_print(struct vop_print_args *ap); static int hpfs_reclaim(struct vop_reclaim_args *ap); static int hpfs_strategy(struct vop_strategy_args *ap); static int hpfs_access(struct vop_access_args *ap); static int hpfs_open(struct vop_open_args *ap); static int hpfs_close(struct vop_close_args *ap); static int hpfs_readdir(struct vop_readdir_args *ap); static int hpfs_lookup(struct vop_lookup_args *ap); static int hpfs_create(struct vop_create_args *); static int hpfs_remove(struct vop_remove_args *); static int hpfs_bmap(struct vop_bmap_args *ap); static int hpfs_fsync(struct vop_fsync_args *ap); static int hpfs_pathconf(struct vop_pathconf_args *ap); static int hpfs_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct thread *a_td; } */ *ap; { /* * Flush our dirty buffers. */ vop_stdfsync(ap); /* * Write out the on-disc version of the vnode. */ return hpfs_update(VTOHP(ap->a_vp)); } static int hpfs_ioctl ( struct vop_ioctl_args /* { struct vnode *a_vp; u_long a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct thread *a_td; } */ *ap) { register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(vp); int error; printf("hpfs_ioctl(0x%x, 0x%lx, 0x%p, 0x%x): ", hp->h_no, ap->a_command, ap->a_data, ap->a_fflag); switch (ap->a_command) { case HPFSIOCGEANUM: { u_long eanum; u_long passed; struct ea *eap; eanum = 0; if (hp->h_fn.fn_ealen > 0) { eap = (struct ea *)&(hp->h_fn.fn_int); passed = 0; while (passed < hp->h_fn.fn_ealen) { printf("EAname: %s\n", EA_NAME(eap)); eanum++; passed += sizeof(struct ea) + eap->ea_namelen + 1 + eap->ea_vallen; eap = (struct ea *)((caddr_t)hp->h_fn.fn_int + passed); } error = 0; } else { error = ENOENT; } printf("%lu eas\n", eanum); *(u_long *)ap->a_data = eanum; break; } case HPFSIOCGEASZ: { u_long eanum; u_long passed; struct ea *eap; printf("EA%ld\n", *(u_long *)ap->a_data); eanum = 0; if (hp->h_fn.fn_ealen > 0) { eap = (struct ea *)&(hp->h_fn.fn_int); passed = 0; error = ENOENT; while (passed < hp->h_fn.fn_ealen) { printf("EAname: %s\n", EA_NAME(eap)); if (eanum == *(u_long *)ap->a_data) { *(u_long *)ap->a_data = eap->ea_namelen + 1 + eap->ea_vallen; error = 0; break; } eanum++; passed += sizeof(struct ea) + eap->ea_namelen + 1 + eap->ea_vallen; eap = (struct ea *)((caddr_t)hp->h_fn.fn_int + passed); } } else { error = ENOENT; } break; } case HPFSIOCRDEA: { u_long eanum; u_long passed; struct hpfs_rdea *rdeap; struct ea *eap; rdeap = (struct hpfs_rdea *)ap->a_data; printf("EA%ld\n", rdeap->ea_no); eanum = 0; if (hp->h_fn.fn_ealen > 0) { eap = (struct ea *)&(hp->h_fn.fn_int); passed = 0; error = ENOENT; while (passed < hp->h_fn.fn_ealen) { printf("EAname: %s\n", EA_NAME(eap)); if (eanum == rdeap->ea_no) { rdeap->ea_sz = eap->ea_namelen + 1 + eap->ea_vallen; copyout(EA_NAME(eap),rdeap->ea_data, rdeap->ea_sz); error = 0; break; } eanum++; passed += sizeof(struct ea) + eap->ea_namelen + 1 + eap->ea_vallen; eap = (struct ea *)((caddr_t)hp->h_fn.fn_int + passed); } } else { error = ENOENT; } break; } default: error = ENOTTY; break; } return (error); } /* * Map file offset to disk offset. */ int hpfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { register struct hpfsnode *hp = VTOHP(ap->a_vp); daddr_t blkno; int error; if (ap->a_vpp != NULL) *ap->a_vpp = hp->h_devvp; if (ap->a_runb != NULL) *ap->a_runb = 0; if (ap->a_bnp == NULL) return (0); dprintf(("hpfs_bmap(0x%x, 0x%x): ",hp->h_no, ap->a_bn)); error = hpfs_hpbmap (hp, ap->a_bn, &blkno, ap->a_runp); *ap->a_bnp = blkno; return (error); } static int hpfs_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(vp); struct uio *uio = ap->a_uio; struct buf *bp; u_int xfersz, toread; u_int off; daddr_t lbn, bn; int resid; int runl; int error = 0; resid = min (uio->uio_resid, hp->h_fn.fn_size - uio->uio_offset); dprintf(("hpfs_read(0x%x, off: %d resid: %d, segflg: %d): [resid: 0x%x]\n",hp->h_no,(u_int32_t)uio->uio_offset,uio->uio_resid,uio->uio_segflg, resid)); while (resid) { lbn = uio->uio_offset >> DEV_BSHIFT; off = uio->uio_offset & (DEV_BSIZE - 1); dprintf(("hpfs_read: resid: 0x%x lbn: 0x%x off: 0x%x\n", uio->uio_resid, lbn, off)); error = hpfs_hpbmap(hp, lbn, &bn, &runl); if (error) return (error); toread = min(off + resid, min(DFLTPHYS, (runl+1)*DEV_BSIZE)); xfersz = (toread + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1); dprintf(("hpfs_read: bn: 0x%x (0x%x) toread: 0x%x (0x%x)\n", bn, runl, toread, xfersz)); if (toread == 0) break; error = bread(hp->h_devvp, bn, xfersz, NOCRED, &bp); if (error) { brelse(bp); break; } error = uiomove(bp->b_data + off, toread - off, uio); if(error) { brelse(bp); break; } brelse(bp); resid -= toread; } dprintf(("hpfs_read: successful\n")); return (error); } static int hpfs_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(vp); struct uio *uio = ap->a_uio; struct buf *bp; u_int xfersz, towrite; u_int off; daddr_t lbn, bn; int runl; int error = 0; dprintf(("hpfs_write(0x%x, off: %d resid: %d, segflg: %d):\n",hp->h_no,(u_int32_t)uio->uio_offset,uio->uio_resid,uio->uio_segflg)); if (ap->a_ioflag & IO_APPEND) { dprintf(("hpfs_write: APPEND mode\n")); uio->uio_offset = hp->h_fn.fn_size; } if (uio->uio_offset + uio->uio_resid > hp->h_fn.fn_size) { error = hpfs_extend (hp, uio->uio_offset + uio->uio_resid); if (error) { printf("hpfs_write: hpfs_extend FAILED %d\n", error); return (error); } } while (uio->uio_resid) { lbn = uio->uio_offset >> DEV_BSHIFT; off = uio->uio_offset & (DEV_BSIZE - 1); dprintf(("hpfs_write: resid: 0x%x lbn: 0x%x off: 0x%x\n", uio->uio_resid, lbn, off)); error = hpfs_hpbmap(hp, lbn, &bn, &runl); if (error) return (error); towrite = min(off + uio->uio_resid, min(DFLTPHYS, (runl+1)*DEV_BSIZE)); xfersz = (towrite + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1); dprintf(("hpfs_write: bn: 0x%x (0x%x) towrite: 0x%x (0x%x)\n", bn, runl, towrite, xfersz)); if ((off == 0) && (towrite == xfersz)) { bp = getblk(hp->h_devvp, bn, xfersz, 0, 0, 0); clrbuf(bp); } else { error = bread(hp->h_devvp, bn, xfersz, NOCRED, &bp); if (error) { brelse(bp); return (error); } } error = uiomove(bp->b_data + off, towrite - off, uio); if(error) { brelse(bp); return (error); } if (ap->a_ioflag & IO_SYNC) bwrite(bp); else bawrite(bp); } dprintf(("hpfs_write: successful\n")); return (0); } /* * XXXXX do we need hpfsnode locking inside? */ static int hpfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct thread *a_td; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(vp); register struct vattr *vap = ap->a_vap; int error; dprintf(("hpfs_getattr(0x%x):\n", hp->h_no)); vap->va_fsid = dev2udev(hp->h_dev); vap->va_fileid = hp->h_no; vap->va_mode = hp->h_mode; vap->va_nlink = 1; vap->va_uid = hp->h_uid; vap->va_gid = hp->h_gid; vap->va_rdev = 0; /* XXX UNODEV ? */ vap->va_size = hp->h_fn.fn_size; vap->va_bytes = ((hp->h_fn.fn_size + DEV_BSIZE-1) & ~(DEV_BSIZE-1)) + DEV_BSIZE; if (!(hp->h_flag & H_PARVALID)) { error = hpfs_validateparent(hp); if (error) return (error); } vap->va_atime = hpfstimetounix(hp->h_atime); vap->va_mtime = hpfstimetounix(hp->h_mtime); vap->va_ctime = hpfstimetounix(hp->h_ctime); vap->va_flags = 0; vap->va_gen = 0; vap->va_blocksize = DEV_BSIZE; vap->va_type = vp->v_type; vap->va_filerev = 0; return (0); } /* * XXXXX do we need hpfsnode locking inside? */ static int hpfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct vnode *vp = ap->a_vp; struct hpfsnode *hp = VTOHP(vp); struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; struct thread *td = ap->a_td; int error; dprintf(("hpfs_setattr(0x%x):\n", hp->h_no)); /* * Check for unsettable attributes. */ if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { dprintf(("hpfs_setattr: changing nonsettable attr\n")); return (EINVAL); } /* Can't change flags XXX Could be implemented */ if (vap->va_flags != VNOVAL) { printf("hpfs_setattr: FLAGS CANNOT BE SET\n"); return (EINVAL); } /* Can't change uid/gid XXX Could be implemented */ if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { printf("hpfs_setattr: UID/GID CANNOT BE SET\n"); return (EINVAL); } /* Can't change mode XXX Could be implemented */ if (vap->va_mode != (mode_t)VNOVAL) { printf("hpfs_setattr: MODE CANNOT BE SET\n"); return (EINVAL); } /* Update times */ if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != hp->h_uid && (error = suser_cred(cred, PRISON_ROOT)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || (error = VOP_ACCESS(vp, VWRITE, cred, td)))) return (error); if (vap->va_atime.tv_sec != VNOVAL) hp->h_atime = vap->va_atime.tv_sec; if (vap->va_mtime.tv_sec != VNOVAL) hp->h_mtime = vap->va_mtime.tv_sec; hp->h_flag |= H_PARCHANGE; } if (vap->va_size != VNOVAL) { switch (vp->v_type) { case VDIR: return (EISDIR); case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; default: printf("hpfs_setattr: WRONG v_type\n"); return (EINVAL); } if (vap->va_size < hp->h_fn.fn_size) { error = vtruncbuf(vp, cred, td, vap->va_size, DEV_BSIZE); if (error) return (error); error = hpfs_truncate(hp, vap->va_size); if (error) return (error); } else if (vap->va_size > hp->h_fn.fn_size) { vnode_pager_setsize(vp, vap->va_size); error = hpfs_extend(hp, vap->va_size); if (error) return (error); } } return (0); } /* * Last reference to a node. If necessary, write or delete it. */ int hpfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(vp); int error; dprintf(("hpfs_inactive(0x%x): \n", hp->h_no)); if (hp->h_flag & H_CHANGE) { dprintf(("hpfs_inactive: node changed, update\n")); error = hpfs_update (hp); if (error) return (error); } if (hp->h_flag & H_PARCHANGE) { dprintf(("hpfs_inactive: parent node changed, update\n")); error = hpfs_updateparent (hp); if (error) return (error); } if (prtactive && vrefcnt(vp) != 0) vprint("hpfs_inactive: pushing active", vp); if (hp->h_flag & H_INVAL) { VOP_UNLOCK(vp,0,ap->a_td); vrecycle(vp, NULL, ap->a_td); return (0); } VOP_UNLOCK(vp,0,ap->a_td); return (0); } /* * Reclaim an inode so that it can be used for other purposes. */ int hpfs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(vp); dprintf(("hpfs_reclaim(0x%x0): \n", hp->h_no)); hpfs_hphashrem(hp); /* Purge old data structures associated with the inode. */ if (hp->h_devvp) { vrele(hp->h_devvp); hp->h_devvp = NULL; } mtx_destroy(&hp->h_interlock); vp->v_data = NULL; FREE(hp, M_HPFSNO); return (0); } static int hpfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(vp); printf("\tino 0x%x\n", hp->h_no); return (0); } /* * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. * * In order to be able to swap to a file, the hpfs_hpbmap operation may not * deadlock on memory. See hpfs_bmap() for details. XXXXXXX (not impl) */ int hpfs_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { register struct buf *bp = ap->a_bp; register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(ap->a_vp); daddr_t blkno; int error; KASSERT(ap->a_vp == ap->a_bp->b_vp, ("%s(%p != %p)", __func__, ap->a_vp, ap->a_bp->b_vp)); dprintf(("hpfs_strategy(): \n")); if (vp->v_type == VBLK || vp->v_type == VCHR) panic("hpfs_strategy: spec"); if (bp->b_blkno == bp->b_lblkno) { error = hpfs_hpbmap (hp, bp->b_lblkno, &blkno, NULL); bp->b_blkno = blkno; if (error) { printf("hpfs_strategy: hpfs_bpbmap FAILED %d\n", error); bp->b_error = error; bp->b_ioflags |= BIO_ERROR; biodone(&bp->b_io); return (error); } if ((long)bp->b_blkno == -1) vfs_bio_clrbuf(bp); } if ((long)bp->b_blkno == -1) { biodone(&bp->b_io); return (0); } bp->b_dev = hp->h_devvp->v_rdev; + bp->b_offset = dbtob(bp->b_blkno); VOP_SPECSTRATEGY(hp->h_devvp, bp); return (0); } /* * XXXXX do we need hpfsnode locking inside? */ int hpfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct vnode *vp = ap->a_vp; struct hpfsnode *hp = VTOHP(vp); mode_t mode = ap->a_mode; dprintf(("hpfs_access(0x%x):\n", hp->h_no)); /* * Disallow write attempts on read-only filesystems; * unless the file is a socket, fifo, or a block or * character device resident on the filesystem. */ if (mode & VWRITE) { switch ((int)vp->v_type) { case VDIR: case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; } } return (vaccess(vp->v_type, hp->h_mode, hp->h_uid, hp->h_gid, ap->a_mode, ap->a_cred, NULL)); } /* * Open called. * * Nothing to do. */ /* ARGSUSED */ static int hpfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct thread *a_td; } */ *ap; { #if HPFS_DEBUG register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(vp); printf("hpfs_open(0x%x):\n",hp->h_no); #endif /* * Files marked append-only must be opened for appending. */ return (0); } /* * Close called. * * Update the times on the inode. */ /* ARGSUSED */ static int hpfs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct thread *a_td; } */ *ap; { #if HPFS_DEBUG register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(vp); printf("hpfs_close: %d\n",hp->h_no); #endif return (0); } static int hpfs_de_uiomove ( struct hpfsmount *hpmp, struct hpfsdirent *dep, struct uio *uio) { struct dirent cde; int i, error; dprintf(("[no: 0x%x, size: %d, name: %2d:%.*s, flag: 0x%x] ", dep->de_fnode, dep->de_size, dep->de_namelen, dep->de_namelen, dep->de_name, dep->de_flag)); /*strncpy(cde.d_name, dep->de_name, dep->de_namelen);*/ for (i=0; ide_namelen; i++) cde.d_name[i] = hpfs_d2u(hpmp, dep->de_name[i]); cde.d_name[dep->de_namelen] = '\0'; cde.d_namlen = dep->de_namelen; cde.d_fileno = dep->de_fnode; cde.d_type = (dep->de_flag & DE_DIR) ? DT_DIR : DT_REG; cde.d_reclen = sizeof(struct dirent); error = uiomove((char *)&cde, sizeof(struct dirent), uio); if (error) return (error); dprintf(("[0x%x] ", uio->uio_resid)); return (error); } static struct dirent hpfs_de_dot = { 0, sizeof(struct dirent), DT_DIR, 1, "." }; static struct dirent hpfs_de_dotdot = { 0, sizeof(struct dirent), DT_DIR, 2, ".." }; int hpfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_ncookies; u_int **cookies; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct hpfsnode *hp = VTOHP(vp); struct hpfsmount *hpmp = hp->h_hpmp; struct uio *uio = ap->a_uio; int ncookies = 0, i, num, cnum; int error = 0; off_t off; struct buf *bp; struct dirblk *dp; struct hpfsdirent *dep; lsn_t olsn; lsn_t lsn; int level; dprintf(("hpfs_readdir(0x%x, 0x%x, 0x%x): ",hp->h_no,(u_int32_t)uio->uio_offset,uio->uio_resid)); off = uio->uio_offset; if( uio->uio_offset < sizeof(struct dirent) ) { dprintf((". faked, ")); hpfs_de_dot.d_fileno = hp->h_no; error = uiomove((char *)&hpfs_de_dot,sizeof(struct dirent),uio); if(error) { return (error); } ncookies ++; } if( uio->uio_offset < 2 * sizeof(struct dirent) ) { dprintf((".. faked, ")); hpfs_de_dotdot.d_fileno = hp->h_fn.fn_parent; error = uiomove((char *)&hpfs_de_dotdot, sizeof(struct dirent), uio); if(error) { return (error); } ncookies ++; } num = uio->uio_offset / sizeof(struct dirent) - 2; cnum = 0; lsn = ((alleaf_t *)hp->h_fn.fn_abd)->al_lsn; olsn = 0; level = 1; dive: dprintf(("[dive 0x%x] ", lsn)); error = bread(hp->h_devvp, lsn, D_BSIZE, NOCRED, &bp); if (error) { brelse(bp); return (error); } dp = (struct dirblk *) bp->b_data; if (dp->d_magic != D_MAGIC) { printf("hpfs_readdir: MAGIC DOESN'T MATCH\n"); brelse(bp); return (EINVAL); } dep = D_DIRENT(dp); if (olsn) { dprintf(("[restore 0x%x] ", olsn)); while(!(dep->de_flag & DE_END) ) { if((dep->de_flag & DE_DOWN) && (olsn == DE_DOWNLSN(dep))) break; dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen); } if((dep->de_flag & DE_DOWN) && (olsn == DE_DOWNLSN(dep))) { if (dep->de_flag & DE_END) goto blockdone; if (!(dep->de_flag & DE_SPECIAL)) { if (num <= cnum) { if (uio->uio_resid < sizeof(struct dirent)) { brelse(bp); dprintf(("[resid] ")); goto readdone; } error = hpfs_de_uiomove(hpmp, dep, uio); if (error) { brelse (bp); return (error); } ncookies++; if (uio->uio_resid < sizeof(struct dirent)) { brelse(bp); dprintf(("[resid] ")); goto readdone; } } cnum++; } dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen); } else { printf("hpfs_readdir: ERROR! oLSN not found\n"); brelse(bp); return (EINVAL); } } olsn = 0; while(!(dep->de_flag & DE_END)) { if(dep->de_flag & DE_DOWN) { lsn = DE_DOWNLSN(dep); brelse(bp); level++; goto dive; } if (!(dep->de_flag & DE_SPECIAL)) { if (num <= cnum) { if (uio->uio_resid < sizeof(struct dirent)) { brelse(bp); dprintf(("[resid] ")); goto readdone; } error = hpfs_de_uiomove(hpmp, dep, uio); if (error) { brelse (bp); return (error); } ncookies++; if (uio->uio_resid < sizeof(struct dirent)) { brelse(bp); dprintf(("[resid] ")); goto readdone; } } cnum++; } dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen); } if(dep->de_flag & DE_DOWN) { dprintf(("[enddive] ")); lsn = DE_DOWNLSN(dep); brelse(bp); level++; goto dive; } blockdone: dprintf(("[EOB] ")); olsn = lsn; lsn = dp->d_parent; brelse(bp); level--; dprintf(("[level %d] ", level)); if (level > 0) goto dive; /* undive really */ if (ap->a_eofflag) { dprintf(("[EOF] ")); *ap->a_eofflag = 1; } readdone: dprintf(("[readdone]\n")); if (!error && ap->a_ncookies != NULL) { struct dirent* dpStart; struct dirent* dp; u_long *cookies; u_long *cookiep; dprintf(("%d cookies, ",ncookies)); if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) panic("hpfs_readdir: unexpected uio from NFS server"); dpStart = (struct dirent *) ((caddr_t)uio->uio_iov->iov_base - (uio->uio_offset - off)); MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK); for (dp = dpStart, cookiep = cookies, i=0; i < ncookies; dp = (struct dirent *)((caddr_t) dp + dp->d_reclen), i++) { off += dp->d_reclen; *cookiep++ = (u_int) off; } *ap->a_ncookies = ncookies; *ap->a_cookies = cookies; } return (0); } int hpfs_lookup(ap) struct vop_lookup_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { register struct vnode *dvp = ap->a_dvp; register struct hpfsnode *dhp = VTOHP(dvp); struct hpfsmount *hpmp = dhp->h_hpmp; struct componentname *cnp = ap->a_cnp; struct ucred *cred = cnp->cn_cred; int error; int nameiop = cnp->cn_nameiop; int flags = cnp->cn_flags; int lockparent = flags & LOCKPARENT; #if HPFS_DEBUG int wantparent = flags & (LOCKPARENT|WANTPARENT); #endif dprintf(("hpfs_lookup(0x%x, %s, %ld, %d, %d): \n", dhp->h_no, cnp->cn_nameptr, cnp->cn_namelen, lockparent, wantparent)); if (nameiop != CREATE && nameiop != DELETE && nameiop != LOOKUP) { printf("hpfs_lookup: LOOKUP, DELETE and CREATE are only supported\n"); return (EOPNOTSUPP); } error = VOP_ACCESS(dvp, VEXEC, cred, cnp->cn_thread); if(error) return (error); if( (cnp->cn_namelen == 1) && !strncmp(cnp->cn_nameptr,".",1) ) { dprintf(("hpfs_lookup(0x%x,...): . faked\n",dhp->h_no)); VREF(dvp); *ap->a_vpp = dvp; return (0); } else if( (cnp->cn_namelen == 2) && !strncmp(cnp->cn_nameptr,"..",2) && (flags & ISDOTDOT) ) { dprintf(("hpfs_lookup(0x%x,...): .. faked (0x%x)\n", dhp->h_no, dhp->h_fn.fn_parent)); if (VFS_VGET(hpmp->hpm_mp, dhp->h_fn.fn_parent, LK_NOWAIT | LK_EXCLUSIVE, ap->a_vpp)) { VOP_UNLOCK(dvp,0,cnp->cn_thread); error = VFS_VGET(hpmp->hpm_mp, dhp->h_fn.fn_parent, LK_EXCLUSIVE, ap->a_vpp); VOP_LOCK(dvp, 0, cnp->cn_thread); if(error) return(error); } if (!lockparent || !(flags & ISLASTCN)) VOP_UNLOCK(dvp,0,cnp->cn_thread); return (0); } else { struct buf *bp; struct hpfsdirent *dep; struct hpfsnode *hp; error = hpfs_genlookupbyname(dhp, cnp->cn_nameptr, cnp->cn_namelen, &bp, &dep); if (error) { if ((error == ENOENT) && (flags & ISLASTCN) && (nameiop == CREATE || nameiop == RENAME)) { if(!lockparent) VOP_UNLOCK(dvp, 0, cnp->cn_thread); cnp->cn_flags |= SAVENAME; return (EJUSTRETURN); } return (error); } dprintf(("hpfs_lookup: fnode: 0x%x, CPID: 0x%x\n", dep->de_fnode, dep->de_cpid)); if (nameiop == DELETE && (flags & ISLASTCN)) { error = VOP_ACCESS(dvp, VWRITE, cred, cnp->cn_thread); if (error) { brelse(bp); return (error); } } if (dhp->h_no == dep->de_fnode) { brelse(bp); VREF(dvp); *ap->a_vpp = dvp; return (0); } error = VFS_VGET(hpmp->hpm_mp, dep->de_fnode, LK_EXCLUSIVE, ap->a_vpp); if (error) { printf("hpfs_lookup: VFS_VGET FAILED %d\n", error); brelse(bp); return(error); } hp = VTOHP(*ap->a_vpp); hp->h_mtime = dep->de_mtime; hp->h_ctime = dep->de_ctime; hp->h_atime = dep->de_atime; bcopy(dep->de_name, hp->h_name, dep->de_namelen); hp->h_name[dep->de_namelen] = '\0'; hp->h_namelen = dep->de_namelen; hp->h_flag |= H_PARVALID; brelse(bp); if(!lockparent || !(flags & ISLASTCN)) VOP_UNLOCK(dvp, 0, cnp->cn_thread); if ((flags & MAKEENTRY) && (!(flags & ISLASTCN) || (nameiop != DELETE && nameiop != CREATE))) cache_enter(dvp, *ap->a_vpp, cnp); } return (error); } int hpfs_remove(ap) struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int error; dprintf(("hpfs_remove(0x%x, %s, %ld): \n", VTOHP(ap->a_vp)->h_no, ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen)); if (ap->a_vp->v_type == VDIR) return (EPERM); error = hpfs_removefnode (ap->a_dvp, ap->a_vp, ap->a_cnp); return (error); } int hpfs_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { int error; dprintf(("hpfs_create(0x%x, %s, %ld): \n", VTOHP(ap->a_dvp)->h_no, ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen)); if (!(ap->a_cnp->cn_flags & HASBUF)) panic ("hpfs_create: no name\n"); error = hpfs_makefnode (ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap); return (error); } /* * Return POSIX pathconf information applicable to NTFS filesystem */ int hpfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; register_t *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = 1; return (0); case _PC_NAME_MAX: *ap->a_retval = HPFS_MAXFILENAME; return (0); case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_NO_TRUNC: *ap->a_retval = 0; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Global vfs data structures */ vop_t **hpfs_vnodeop_p; struct vnodeopv_entry_desc hpfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vop_defaultop }, { &vop_getattr_desc, (vop_t *)hpfs_getattr }, { &vop_setattr_desc, (vop_t *)hpfs_setattr }, { &vop_inactive_desc, (vop_t *)hpfs_inactive }, { &vop_reclaim_desc, (vop_t *)hpfs_reclaim }, { &vop_print_desc, (vop_t *)hpfs_print }, { &vop_create_desc, (vop_t *)hpfs_create }, { &vop_remove_desc, (vop_t *)hpfs_remove }, { &vop_cachedlookup_desc, (vop_t *)hpfs_lookup }, { &vop_lookup_desc, (vop_t *)vfs_cache_lookup }, { &vop_access_desc, (vop_t *)hpfs_access }, { &vop_close_desc, (vop_t *)hpfs_close }, { &vop_open_desc, (vop_t *)hpfs_open }, { &vop_readdir_desc, (vop_t *)hpfs_readdir }, { &vop_fsync_desc, (vop_t *)hpfs_fsync }, { &vop_bmap_desc, (vop_t *)hpfs_bmap }, { &vop_strategy_desc, (vop_t *)hpfs_strategy }, { &vop_read_desc, (vop_t *)hpfs_read }, { &vop_write_desc, (vop_t *)hpfs_write }, { &vop_ioctl_desc, (vop_t *)hpfs_ioctl }, { &vop_pathconf_desc, (vop_t *)hpfs_pathconf }, { NULL, NULL } }; static struct vnodeopv_desc hpfs_vnodeop_opv_desc = { &hpfs_vnodeop_p, hpfs_vnodeop_entries }; VNODEOP_SET(hpfs_vnodeop_opv_desc); diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c index a4c60cc9df37..c483e443e605 100644 --- a/sys/fs/msdosfs/msdosfs_vnops.c +++ b/sys/fs/msdosfs/msdosfs_vnops.c @@ -1,1872 +1,1873 @@ /* $FreeBSD$ */ /* $NetBSD: msdosfs_vnops.c,v 1.68 1998/02/10 14:10:04 mrg Exp $ */ /*- * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * * 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 TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /* * Written by Paul Popelka (paulp@uts.amdahl.com) * * You can do anything you want with this software, just don't say you wrote * it, and don't remove this notice. * * This software is provided "as is". * * The author supplies this software to be publicly redistributed on the * understanding that the author is not responsible for the correct * functioning of this software in any circumstances and is not liable for * any damages caused by this software. * * October 1992 */ #include #include #include #include /* defines plimit structure in proc struct */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DOS_FILESIZE_MAX 0xffffffff /* * Prototypes for MSDOSFS vnode operations */ static int msdosfs_create(struct vop_create_args *); static int msdosfs_mknod(struct vop_mknod_args *); static int msdosfs_close(struct vop_close_args *); static int msdosfs_access(struct vop_access_args *); static int msdosfs_getattr(struct vop_getattr_args *); static int msdosfs_setattr(struct vop_setattr_args *); static int msdosfs_read(struct vop_read_args *); static int msdosfs_write(struct vop_write_args *); static int msdosfs_fsync(struct vop_fsync_args *); static int msdosfs_remove(struct vop_remove_args *); static int msdosfs_link(struct vop_link_args *); static int msdosfs_rename(struct vop_rename_args *); static int msdosfs_mkdir(struct vop_mkdir_args *); static int msdosfs_rmdir(struct vop_rmdir_args *); static int msdosfs_symlink(struct vop_symlink_args *); static int msdosfs_readdir(struct vop_readdir_args *); static int msdosfs_bmap(struct vop_bmap_args *); static int msdosfs_strategy(struct vop_strategy_args *); static int msdosfs_print(struct vop_print_args *); static int msdosfs_pathconf(struct vop_pathconf_args *ap); /* * Some general notes: * * In the ufs filesystem the inodes, superblocks, and indirect blocks are * read/written using the vnode for the filesystem. Blocks that represent * the contents of a file are read/written using the vnode for the file * (including directories when they are read/written as files). This * presents problems for the dos filesystem because data that should be in * an inode (if dos had them) resides in the directory itself. Since we * must update directory entries without the benefit of having the vnode * for the directory we must use the vnode for the filesystem. This means * that when a directory is actually read/written (via read, write, or * readdir, or seek) we must use the vnode for the filesystem instead of * the vnode for the directory as would happen in ufs. This is to insure we * retreive the correct block from the buffer cache since the hash value is * based upon the vnode address and the desired block number. */ /* * 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 msdosfs_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct denode ndirent; struct denode *dep; struct denode *pdep = VTODE(ap->a_dvp); struct timespec ts; int error; #ifdef MSDOSFS_DEBUG printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap); #endif /* * If this is the root directory and there is no space left we * can't do anything. This is because the root directory can not * change size. */ if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndoffset >= pdep->de_FileSize) { error = ENOSPC; goto bad; } /* * Create a directory entry for the file, then call createde() to * have it installed. NOTE: DOS files are always executable. We * use the absence of the owner write bit to make the file * readonly. */ #ifdef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("msdosfs_create: no name"); #endif bzero(&ndirent, sizeof(ndirent)); error = uniqdosname(pdep, cnp, ndirent.de_Name); if (error) goto bad; ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ? ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; ndirent.de_LowerCase = 0; ndirent.de_StartCluster = 0; ndirent.de_FileSize = 0; ndirent.de_dev = pdep->de_dev; ndirent.de_devvp = pdep->de_devvp; ndirent.de_pmp = pdep->de_pmp; ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; getnanotime(&ts); DETIMES(&ndirent, &ts, &ts, &ts); error = createde(&ndirent, pdep, &dep, cnp); if (error) goto bad; *ap->a_vpp = DETOV(dep); return (0); bad: return (error); } static int msdosfs_mknod(ap) struct vop_mknod_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { return (EINVAL); } static int msdosfs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(vp); struct timespec ts; VI_LOCK(vp); if (vp->v_usecount > 1) { getnanotime(&ts); DETIMES(dep, &ts, &ts, &ts); } VI_UNLOCK(vp); return 0; } static int msdosfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; mode_t file_mode, mode = ap->a_mode; file_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); file_mode &= (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask); /* * Disallow write attempts on read-only filesystems; * unless the file is a socket, fifo, or a block or * character device resident on the filesystem. */ if (mode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; default: break; } } return (vaccess(vp->v_type, file_mode, pmp->pm_uid, pmp->pm_gid, ap->a_mode, ap->a_cred, NULL)); } static int msdosfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct vattr *vap = ap->a_vap; mode_t mode; struct timespec ts; u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry); u_long fileid; getnanotime(&ts); DETIMES(dep, &ts, &ts, &ts); vap->va_fsid = dev2udev(dep->de_dev); /* * The following computation of the fileid must be the same as that * used in msdosfs_readdir() to compute d_fileno. If not, pwd * doesn't work. */ if (dep->de_Attributes & ATTR_DIRECTORY) { fileid = cntobn(pmp, dep->de_StartCluster) * dirsperblk; if (dep->de_StartCluster == MSDOSFSROOT) fileid = 1; } else { fileid = cntobn(pmp, dep->de_dirclust) * dirsperblk; if (dep->de_dirclust == MSDOSFSROOT) fileid = roottobn(pmp, 0) * dirsperblk; fileid += dep->de_diroffset / sizeof(struct direntry); } vap->va_fileid = fileid; if ((dep->de_Attributes & ATTR_READONLY) == 0) mode = S_IRWXU|S_IRWXG|S_IRWXO; else mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; vap->va_mode = mode & (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask); vap->va_uid = pmp->pm_uid; vap->va_gid = pmp->pm_gid; vap->va_nlink = 1; vap->va_rdev = 0; vap->va_size = dep->de_FileSize; dos2unixtime(dep->de_MDate, dep->de_MTime, 0, &vap->va_mtime); if (pmp->pm_flags & MSDOSFSMNT_LONGNAME) { dos2unixtime(dep->de_ADate, 0, 0, &vap->va_atime); dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CHun, &vap->va_ctime); } else { vap->va_atime = vap->va_mtime; vap->va_ctime = vap->va_mtime; } vap->va_flags = 0; if ((dep->de_Attributes & ATTR_ARCHIVE) == 0) vap->va_flags |= SF_ARCHIVED; vap->va_gen = 0; vap->va_blocksize = pmp->pm_bpcluster; vap->va_bytes = (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask; vap->va_type = ap->a_vp->v_type; vap->va_filerev = dep->de_modrev; return (0); } static int msdosfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; int error = 0; #ifdef MSDOSFS_DEBUG printf("msdosfs_setattr(): vp %p, vap %p, cred %p, p %p\n", ap->a_vp, vap, cred, ap->a_td); #endif /* * Check for unsettable attributes. */ if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { #ifdef MSDOSFS_DEBUG printf("msdosfs_setattr(): returning EINVAL\n"); printf(" va_type %d, va_nlink %x, va_fsid %lx, va_fileid %lx\n", vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid); printf(" va_blocksize %lx, va_rdev %x, va_bytes %qx, va_gen %lx\n", vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen); printf(" va_uid %x, va_gid %x\n", vap->va_uid, vap->va_gid); #endif return (EINVAL); } if (vap->va_flags != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != pmp->pm_uid && (error = suser_cred(cred, PRISON_ROOT))) return (error); /* * We are very inconsistent about handling unsupported * attributes. We ignored the access time and the * read and execute bits. We were strict for the other * attributes. * * Here we are strict, stricter than ufs in not allowing * users to attempt to set SF_SETTABLE bits or anyone to * set unsupported bits. However, we ignore attempts to * set ATTR_ARCHIVE for directories `cp -pr' from a more * sensible filesystem attempts it a lot. */ if (suser_cred(cred, PRISON_ROOT)) { if (vap->va_flags & SF_SETTABLE) return EPERM; } if (vap->va_flags & ~SF_ARCHIVED) return EOPNOTSUPP; if (vap->va_flags & SF_ARCHIVED) dep->de_Attributes &= ~ATTR_ARCHIVE; else if (!(dep->de_Attributes & ATTR_DIRECTORY)) dep->de_Attributes |= ATTR_ARCHIVE; dep->de_flag |= DE_MODIFIED; } if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { uid_t uid; gid_t gid; if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); uid = vap->va_uid; if (uid == (uid_t)VNOVAL) uid = pmp->pm_uid; gid = vap->va_gid; if (gid == (gid_t)VNOVAL) gid = pmp->pm_gid; if ((cred->cr_uid != pmp->pm_uid || uid != pmp->pm_uid || (gid != pmp->pm_gid && !groupmember(gid, cred))) && (error = suser_cred(cred, PRISON_ROOT))) return error; if (uid != pmp->pm_uid || gid != pmp->pm_gid) return EINVAL; } if (vap->va_size != VNOVAL) { /* * Disallow write attempts on read-only filesystems; * unless the file is a socket, fifo, or a block or * character device resident on the filesystem. */ switch (vp->v_type) { case VDIR: return (EISDIR); /* NOT REACHED */ case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; default: break; } error = detrunc(dep, vap->va_size, 0, cred, ap->a_td); if (error) return error; } if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != pmp->pm_uid && (error = suser_cred(cred, PRISON_ROOT)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || (error = VOP_ACCESS(ap->a_vp, VWRITE, cred, ap->a_td)))) return (error); if (vp->v_type != VDIR) { if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 && vap->va_atime.tv_sec != VNOVAL) { dep->de_flag &= ~DE_ACCESS; unix2dostime(&vap->va_atime, &dep->de_ADate, NULL, NULL); } if (vap->va_mtime.tv_sec != VNOVAL) { dep->de_flag &= ~DE_UPDATE; unix2dostime(&vap->va_mtime, &dep->de_MDate, &dep->de_MTime, NULL); } dep->de_Attributes |= ATTR_ARCHIVE; dep->de_flag |= DE_MODIFIED; } } /* * DOS files only have the ability to have their writability * attribute set, so we use the owner write bit to set the readonly * attribute. */ if (vap->va_mode != (mode_t)VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != pmp->pm_uid && (error = suser_cred(cred, PRISON_ROOT))) return (error); if (vp->v_type != VDIR) { /* We ignore the read and execute bits. */ if (vap->va_mode & VWRITE) dep->de_Attributes &= ~ATTR_READONLY; else dep->de_Attributes |= ATTR_READONLY; dep->de_Attributes |= ATTR_ARCHIVE; dep->de_flag |= DE_MODIFIED; } } return (deupdat(dep, 1)); } static int msdosfs_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error = 0; int blsize; int isadir; int orig_resid; u_int n; u_long diff; u_long on; daddr_t lbn; daddr_t rablock; int rasize; int seqcount; struct buf *bp; struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(vp); struct msdosfsmount *pmp = dep->de_pmp; struct uio *uio = ap->a_uio; if (uio->uio_offset < 0) return (EINVAL); if ((uoff_t)uio->uio_offset > DOS_FILESIZE_MAX) return (0); /* * If they didn't ask for any data, then we are done. */ orig_resid = uio->uio_resid; if (orig_resid <= 0) return (0); seqcount = ap->a_ioflag >> IO_SEQSHIFT; isadir = dep->de_Attributes & ATTR_DIRECTORY; do { if (uio->uio_offset >= dep->de_FileSize) break; lbn = de_cluster(pmp, uio->uio_offset); /* * If we are operating on a directory file then be sure to * do i/o with the vnode for the filesystem instead of the * vnode for the directory. */ if (isadir) { /* convert cluster # to block # */ error = pcbmap(dep, lbn, &lbn, 0, &blsize); if (error == E2BIG) { error = EINVAL; break; } else if (error) break; error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp); } else { blsize = pmp->pm_bpcluster; rablock = lbn + 1; if (seqcount > 1 && de_cn2off(pmp, rablock) < dep->de_FileSize) { rasize = pmp->pm_bpcluster; error = breadn(vp, lbn, blsize, &rablock, &rasize, 1, NOCRED, &bp); } else { error = bread(vp, lbn, blsize, NOCRED, &bp); } } if (error) { brelse(bp); break; } on = uio->uio_offset & pmp->pm_crbomask; diff = pmp->pm_bpcluster - on; n = diff > uio->uio_resid ? uio->uio_resid : diff; diff = dep->de_FileSize - uio->uio_offset; if (diff < n) n = diff; diff = blsize - bp->b_resid; if (diff < n) n = diff; error = uiomove(bp->b_data + on, (int) n, uio); brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); if (!isadir && (error == 0 || uio->uio_resid != orig_resid) && (vp->v_mount->mnt_flag & MNT_NOATIME) == 0) dep->de_flag |= DE_ACCESS; return (error); } /* * Write data to a file or directory. */ static int msdosfs_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int n; int croffset; int resid; u_long osize; int error = 0; u_long count; daddr_t bn, lastcn; struct buf *bp; int ioflag = ap->a_ioflag; struct uio *uio = ap->a_uio; struct thread *td = uio->uio_td; struct vnode *vp = ap->a_vp; struct vnode *thisvp; struct denode *dep = VTODE(vp); struct msdosfsmount *pmp = dep->de_pmp; struct ucred *cred = ap->a_cred; #ifdef MSDOSFS_DEBUG printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n", vp, uio, ioflag, cred); printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n", dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); #endif switch (vp->v_type) { case VREG: if (ioflag & IO_APPEND) uio->uio_offset = dep->de_FileSize; thisvp = vp; break; case VDIR: return EISDIR; default: panic("msdosfs_write(): bad file type"); } if (uio->uio_offset < 0) return (EFBIG); if (uio->uio_resid == 0) return (0); /* * If they've exceeded their filesize limit, tell them about it. */ if (td && ((uoff_t)uio->uio_offset + uio->uio_resid > td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { PROC_LOCK(td->td_proc); psignal(td->td_proc, SIGXFSZ); PROC_UNLOCK(td->td_proc); return (EFBIG); } if ((uoff_t)uio->uio_offset + uio->uio_resid > DOS_FILESIZE_MAX) return (EFBIG); /* * If the offset we are starting the write at is beyond the end of * the file, then they've done a seek. Unix filesystems allow * files with holes in them, DOS doesn't so we must fill the hole * with zeroed blocks. */ if (uio->uio_offset > dep->de_FileSize) { error = deextend(dep, uio->uio_offset, cred); if (error) return (error); } /* * Remember some values in case the write fails. */ resid = uio->uio_resid; osize = dep->de_FileSize; /* * If we write beyond the end of the file, extend it to its ultimate * size ahead of the time to hopefully get a contiguous area. */ if (uio->uio_offset + resid > osize) { count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize); error = extendfile(dep, count, NULL, NULL, 0); if (error && (error != ENOSPC || (ioflag & IO_UNIT))) goto errexit; lastcn = dep->de_fc[FC_LASTFC].fc_frcn; } else lastcn = de_clcount(pmp, osize) - 1; do { if (de_cluster(pmp, uio->uio_offset) > lastcn) { error = ENOSPC; break; } croffset = uio->uio_offset & pmp->pm_crbomask; n = min(uio->uio_resid, pmp->pm_bpcluster - croffset); if (uio->uio_offset + n > dep->de_FileSize) { dep->de_FileSize = uio->uio_offset + n; /* The object size needs to be set before buffer is allocated */ vnode_pager_setsize(vp, dep->de_FileSize); } bn = de_cluster(pmp, uio->uio_offset); if ((uio->uio_offset & pmp->pm_crbomask) == 0 && (de_cluster(pmp, uio->uio_offset + uio->uio_resid) > de_cluster(pmp, uio->uio_offset) || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) { /* * If either the whole cluster gets written, * or we write the cluster from its start beyond EOF, * then no need to read data from disk. */ bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0, 0); clrbuf(bp); /* * Do the bmap now, since pcbmap needs buffers * for the fat table. (see msdosfs_strategy) */ if (bp->b_blkno == bp->b_lblkno) { error = pcbmap(dep, bp->b_lblkno, &bn, 0, 0); if (error) bp->b_blkno = -1; else bp->b_blkno = bn; } if (bp->b_blkno == -1) { brelse(bp); if (!error) error = EIO; /* XXX */ break; } } else { /* * The block we need to write into exists, so read it in. */ error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp); if (error) { brelse(bp); break; } } /* * Should these vnode_pager_* functions be done on dir * files? */ /* * Copy the data from user space into the buf header. */ error = uiomove(bp->b_data + croffset, n, uio); if (error) { brelse(bp); break; } /* * If they want this synchronous then write it and wait for * it. Otherwise, if on a cluster boundary write it * asynchronously so we can move on to the next block * without delay. Otherwise do a delayed write because we * may want to write somemore into the block later. */ if (ioflag & IO_SYNC) (void) bwrite(bp); else if (n + croffset == pmp->pm_bpcluster) bawrite(bp); else bdwrite(bp); dep->de_flag |= DE_UPDATE; } while (error == 0 && uio->uio_resid > 0); /* * If the write failed and they want us to, truncate the file back * to the size it was before the write was attempted. */ errexit: if (error) { if (ioflag & IO_UNIT) { detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } else { detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL); if (uio->uio_resid != resid) error = 0; } } else if (ioflag & IO_SYNC) error = deupdat(dep, 1); return (error); } /* * Flush the blocks of a file to disk. * * This function is worthless for vnodes that represent directories. Maybe we * could just do a sync if they try an fsync on a directory file. */ static int msdosfs_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct thread *a_td; } */ *ap; { /* * Flush our dirty buffers. */ vop_stdfsync(ap); return (deupdat(VTODE(ap->a_vp), ap->a_waitfor == MNT_WAIT)); } static int msdosfs_remove(ap) struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); struct denode *ddep = VTODE(ap->a_dvp); int error; if (ap->a_vp->v_type == VDIR) error = EPERM; else error = removede(ddep, dep); #ifdef MSDOSFS_DEBUG printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep, ap->a_vp->v_usecount); #endif return (error); } /* * DOS filesystems don't know what links are. */ static int msdosfs_link(ap) struct vop_link_args /* { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { return (EOPNOTSUPP); } /* * Renames on files require moving the denode to a new hash queue since the * denode's location is used to compute which hash queue to put the file * in. Unless it is a rename in place. For example "mv a b". * * What follows is the basic algorithm: * * if (file move) { * if (dest file exists) { * remove dest file * } * if (dest and src in same directory) { * rewrite name in existing directory slot * } else { * write new entry in dest directory * update offset and dirclust in denode * move denode to new hash chain * clear old directory entry * } * } else { * directory move * if (dest directory exists) { * if (dest is not empty) { * return ENOTEMPTY * } * remove dest directory * } * if (dest and src in same directory) { * rewrite name in existing entry * } else { * be sure dest is not a child of src directory * write entry in dest directory * update "." and ".." in moved directory * clear old directory entry for moved directory * } * } * * On entry: * source's parent directory is unlocked * source file or directory is unlocked * destination's parent directory is locked * destination file or directory is locked if it exists * * On exit: * all denodes should be released */ static int msdosfs_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 *tdvp = ap->a_tdvp; struct vnode *fvp = ap->a_fvp; struct vnode *fdvp = ap->a_fdvp; struct vnode *tvp = ap->a_tvp; struct componentname *tcnp = ap->a_tcnp; struct componentname *fcnp = ap->a_fcnp; struct thread *td = fcnp->cn_thread; struct denode *ip, *xp, *dp, *zp; u_char toname[11], oldname[11]; u_long from_diroffset, to_diroffset; u_char to_count; int doingdirectory = 0, newparent = 0; int error; u_long cn; daddr_t bn; struct denode *fddep; /* from file's parent directory */ struct msdosfsmount *pmp; struct direntry *dotdotp; struct buf *bp; fddep = VTODE(ap->a_fdvp); pmp = fddep->de_pmp; pmp = VFSTOMSDOSFS(fdvp->v_mount); #ifdef DIAGNOSTIC if ((tcnp->cn_flags & HASBUF) == 0 || (fcnp->cn_flags & HASBUF) == 0) panic("msdosfs_rename: no name"); #endif /* * Check for cross-device rename. */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; abortit: if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fdvp); vrele(fvp); return (error); } /* * If source and dest are the same, do nothing. */ if (tvp == fvp) { error = 0; goto abortit; } error = vn_lock(fvp, LK_EXCLUSIVE, td); if (error) goto abortit; dp = VTODE(fdvp); ip = VTODE(fvp); /* * Be sure we are not renaming ".", "..", or an alias of ".". This * leads to a crippled directory tree. It's pretty tough to do a * "ls" or "pwd" with the "." directory entry missing, and "cd .." * doesn't work if the ".." entry is missing. */ if (ip->de_Attributes & ATTR_DIRECTORY) { /* * Avoid ".", "..", and aliases of "." for obvious reasons. */ if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || dp == ip || (fcnp->cn_flags & ISDOTDOT) || (tcnp->cn_flags & ISDOTDOT) || (ip->de_flag & DE_RENAME)) { VOP_UNLOCK(fvp, 0, td); error = EINVAL; goto abortit; } ip->de_flag |= DE_RENAME; doingdirectory++; } /* * When the target exists, both the directory * and target vnodes are returned locked. */ dp = VTODE(tdvp); xp = tvp ? VTODE(tvp) : NULL; /* * Remember direntry place to use for destination */ to_diroffset = dp->de_fndoffset; to_count = dp->de_fndcnt; /* * If ".." must be changed (ie the directory gets a new * parent) then the source directory must not be in the * directory heirarchy above the target, as this would * orphan everything below the source directory. Also * the user must have write permission in the source so * as to be able to change "..". We must repeat the call * to namei, as the parent directory is unlocked by the * call to doscheckpath(). */ error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread); VOP_UNLOCK(fvp, 0, td); if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster) newparent = 1; if (doingdirectory && newparent) { if (error) /* write access check above */ goto bad; if (xp != NULL) vput(tvp); /* * doscheckpath() vput()'s dp, * so we have to do a relookup afterwards */ error = doscheckpath(ip, dp); if (error) goto out; if ((tcnp->cn_flags & SAVESTART) == 0) panic("msdosfs_rename: lost to startdir"); error = relookup(tdvp, &tvp, tcnp); if (error) goto out; dp = VTODE(tdvp); xp = tvp ? VTODE(tvp) : NULL; } if (xp != NULL) { /* * Target must be empty if a directory and have no links * to it. Also, ensure source and target are compatible * (both directories, or both not directories). */ if (xp->de_Attributes & ATTR_DIRECTORY) { if (!dosdirempty(xp)) { error = ENOTEMPTY; goto bad; } if (!doingdirectory) { error = ENOTDIR; goto bad; } cache_purge(tdvp); } else if (doingdirectory) { error = EISDIR; goto bad; } error = removede(dp, xp); if (error) goto bad; vput(tvp); xp = NULL; } /* * Convert the filename in tcnp into a dos filename. We copy this * into the denode and directory entry for the destination * file/directory. */ error = uniqdosname(VTODE(tdvp), tcnp, toname); if (error) goto abortit; /* * Since from wasn't locked at various places above, * have to do a relookup here. */ fcnp->cn_flags &= ~MODMASK; fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; if ((fcnp->cn_flags & SAVESTART) == 0) panic("msdosfs_rename: lost from startdir"); if (!newparent) VOP_UNLOCK(tdvp, 0, td); if (relookup(fdvp, &fvp, fcnp) == 0) vrele(fdvp); if (fvp == NULL) { /* * From name has disappeared. */ if (doingdirectory) panic("rename: lost dir entry"); vrele(ap->a_fvp); if (newparent) VOP_UNLOCK(tdvp, 0, td); vrele(tdvp); return 0; } xp = VTODE(fvp); zp = VTODE(fdvp); from_diroffset = zp->de_fndoffset; /* * Ensure that the directory entry still exists and has not * changed till now. If the source is a file the entry may * have been unlinked or renamed. In either case there is * no further work to be done. If the source is a directory * then it cannot have been rmdir'ed or renamed; this is * prohibited by the DE_RENAME flag. */ if (xp != ip) { if (doingdirectory) panic("rename: lost dir entry"); vrele(ap->a_fvp); VOP_UNLOCK(fvp, 0, td); if (newparent) VOP_UNLOCK(fdvp, 0, td); xp = NULL; } else { vrele(fvp); xp = NULL; /* * First write a new entry in the destination * directory and mark the entry in the source directory * as deleted. Then move the denode to the correct hash * chain for its new location in the filesystem. And, if * we moved a directory, then update its .. entry to point * to the new parent directory. */ bcopy(ip->de_Name, oldname, 11); bcopy(toname, ip->de_Name, 11); /* update denode */ dp->de_fndoffset = to_diroffset; dp->de_fndcnt = to_count; error = createde(ip, dp, (struct denode **)0, tcnp); if (error) { bcopy(oldname, ip->de_Name, 11); if (newparent) VOP_UNLOCK(fdvp, 0, td); VOP_UNLOCK(fvp, 0, td); goto bad; } ip->de_refcnt++; zp->de_fndoffset = from_diroffset; error = removede(zp, ip); if (error) { /* XXX should really panic here, fs is corrupt */ if (newparent) VOP_UNLOCK(fdvp, 0, td); VOP_UNLOCK(fvp, 0, td); goto bad; } if (!doingdirectory) { error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0, &ip->de_dirclust, 0); if (error) { /* XXX should really panic here, fs is corrupt */ if (newparent) VOP_UNLOCK(fdvp, 0, td); VOP_UNLOCK(fvp, 0, td); goto bad; } if (ip->de_dirclust == MSDOSFSROOT) ip->de_diroffset = to_diroffset; else ip->de_diroffset = to_diroffset & pmp->pm_crbomask; } reinsert(ip); if (newparent) VOP_UNLOCK(fdvp, 0, td); } /* * If we moved a directory to a new parent directory, then we must * fixup the ".." entry in the moved directory. */ if (doingdirectory && newparent) { cn = ip->de_StartCluster; if (cn == MSDOSFSROOT) { /* this should never happen */ panic("msdosfs_rename(): updating .. in root directory?"); } else bn = cntobn(pmp, cn); error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); if (error) { /* XXX should really panic here, fs is corrupt */ brelse(bp); VOP_UNLOCK(fvp, 0, td); goto bad; } dotdotp = (struct direntry *)bp->b_data + 1; putushort(dotdotp->deStartCluster, dp->de_StartCluster); if (FAT32(pmp)) putushort(dotdotp->deHighClust, dp->de_StartCluster >> 16); error = bwrite(bp); if (error) { /* XXX should really panic here, fs is corrupt */ VOP_UNLOCK(fvp, 0, td); goto bad; } } VOP_UNLOCK(fvp, 0, td); bad: if (xp) vput(tvp); vput(tdvp); out: ip->de_flag &= ~DE_RENAME; vrele(fdvp); vrele(fvp); return (error); } static struct { struct direntry dot; struct direntry dotdot; } dosdirtemplate = { { ". ", " ", /* the . entry */ ATTR_DIRECTORY, /* file attribute */ 0, /* reserved */ 0, { 0, 0 }, { 0, 0 }, /* create time & date */ { 0, 0 }, /* access date */ { 0, 0 }, /* high bits of start cluster */ { 210, 4 }, { 210, 4 }, /* modify time & date */ { 0, 0 }, /* startcluster */ { 0, 0, 0, 0 } /* filesize */ }, { ".. ", " ", /* the .. entry */ ATTR_DIRECTORY, /* file attribute */ 0, /* reserved */ 0, { 0, 0 }, { 0, 0 }, /* create time & date */ { 0, 0 }, /* access date */ { 0, 0 }, /* high bits of start cluster */ { 210, 4 }, { 210, 4 }, /* modify time & date */ { 0, 0 }, /* startcluster */ { 0, 0, 0, 0 } /* filesize */ } }; static int msdosfs_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struvt vnode **a_vpp; struvt componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct denode *dep; struct denode *pdep = VTODE(ap->a_dvp); struct direntry *denp; struct msdosfsmount *pmp = pdep->de_pmp; struct buf *bp; u_long newcluster, pcl; int bn; int error; struct denode ndirent; struct timespec ts; /* * If this is the root directory and there is no space left we * can't do anything. This is because the root directory can not * change size. */ if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndoffset >= pdep->de_FileSize) { error = ENOSPC; goto bad2; } /* * Allocate a cluster to hold the about to be created directory. */ error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL); if (error) goto bad2; bzero(&ndirent, sizeof(ndirent)); ndirent.de_pmp = pmp; ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; getnanotime(&ts); DETIMES(&ndirent, &ts, &ts, &ts); /* * Now fill the cluster with the "." and ".." entries. And write * the cluster to disk. This way it is there for the parent * directory to be pointing at if there were a crash. */ bn = cntobn(pmp, newcluster); /* always succeeds */ bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, 0); bzero(bp->b_data, pmp->pm_bpcluster); bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate); denp = (struct direntry *)bp->b_data; putushort(denp[0].deStartCluster, newcluster); putushort(denp[0].deCDate, ndirent.de_CDate); putushort(denp[0].deCTime, ndirent.de_CTime); denp[0].deCHundredth = ndirent.de_CHun; putushort(denp[0].deADate, ndirent.de_ADate); putushort(denp[0].deMDate, ndirent.de_MDate); putushort(denp[0].deMTime, ndirent.de_MTime); pcl = pdep->de_StartCluster; if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) pcl = 0; putushort(denp[1].deStartCluster, pcl); putushort(denp[1].deCDate, ndirent.de_CDate); putushort(denp[1].deCTime, ndirent.de_CTime); denp[1].deCHundredth = ndirent.de_CHun; putushort(denp[1].deADate, ndirent.de_ADate); putushort(denp[1].deMDate, ndirent.de_MDate); putushort(denp[1].deMTime, ndirent.de_MTime); if (FAT32(pmp)) { putushort(denp[0].deHighClust, newcluster >> 16); putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16); } error = bwrite(bp); if (error) goto bad; /* * Now build up a directory entry pointing to the newly allocated * cluster. This will be written to an empty slot in the parent * directory. */ #ifdef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("msdosfs_mkdir: no name"); #endif error = uniqdosname(pdep, cnp, ndirent.de_Name); if (error) goto bad; ndirent.de_Attributes = ATTR_DIRECTORY; ndirent.de_LowerCase = 0; ndirent.de_StartCluster = newcluster; ndirent.de_FileSize = 0; ndirent.de_dev = pdep->de_dev; ndirent.de_devvp = pdep->de_devvp; error = createde(&ndirent, pdep, &dep, cnp); if (error) goto bad; *ap->a_vpp = DETOV(dep); return (0); bad: clusterfree(pmp, newcluster, NULL); bad2: return (error); } static int msdosfs_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 denode *ip, *dp; struct thread *td = cnp->cn_thread; int error; ip = VTODE(vp); dp = VTODE(dvp); /* * Verify the directory is empty (and valid). * (Rmdir ".." won't be valid since * ".." will contain a reference to * the current directory and thus be * non-empty.) */ error = 0; if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) { error = ENOTEMPTY; goto out; } /* * Delete the entry from the directory. For dos filesystems this * gets rid of the directory entry on disk, the in memory copy * still exists but the de_refcnt is <= 0. This prevents it from * being found by deget(). When the vput() on dep is done we give * up access and eventually msdosfs_reclaim() will be called which * will remove it from the denode cache. */ error = removede(dp, ip); if (error) goto out; /* * This is where we decrement the link count in the parent * directory. Since dos filesystems don't do this we just purge * the name cache. */ cache_purge(dvp); VOP_UNLOCK(dvp, 0, td); /* * Truncate the directory that is being deleted. */ error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred, td); cache_purge(vp); vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); out: return (error); } /* * DOS filesystems don't know what symlinks are. */ static int msdosfs_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 msdosfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; int *a_ncookies; u_long **a_cookies; } */ *ap; { int error = 0; int diff; long n; int blsize; long on; u_long cn; u_long fileno; u_long dirsperblk; long bias = 0; daddr_t bn, lbn; struct buf *bp; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct direntry *dentp; struct dirent dirbuf; struct uio *uio = ap->a_uio; u_long *cookies = NULL; int ncookies = 0; off_t offset, off; int chksum = -1; #ifdef MSDOSFS_DEBUG printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n", ap->a_vp, uio, ap->a_cred, ap->a_eofflag); #endif /* * msdosfs_readdir() won't operate properly on regular files since * it does i/o only with the the filesystem vnode, and hence can * retrieve the wrong block from the buffer cache for a plain file. * So, fail attempts to readdir() on a plain file. */ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) return (ENOTDIR); /* * To be safe, initialize dirbuf */ bzero(dirbuf.d_name, sizeof(dirbuf.d_name)); /* * If the user buffer is smaller than the size of one dos directory * entry or the file offset is not a multiple of the size of a * directory entry, then we fail the read. */ off = offset = uio->uio_offset; if (uio->uio_resid < sizeof(struct direntry) || (offset & (sizeof(struct direntry) - 1))) return (EINVAL); if (ap->a_ncookies) { ncookies = uio->uio_resid / 16; MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK); *ap->a_cookies = cookies; *ap->a_ncookies = ncookies; } dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry); /* * If they are reading from the root directory then, we simulate * the . and .. entries since these don't exist in the root * directory. We also set the offset bias to make up for having to * simulate these entries. By this I mean that at file offset 64 we * read the first entry in the root directory that lives on disk. */ if (dep->de_StartCluster == MSDOSFSROOT || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) { #if 0 printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n", offset); #endif bias = 2 * sizeof(struct direntry); if (offset < bias) { for (n = (int)offset / sizeof(struct direntry); n < 2; n++) { if (FAT32(pmp)) dirbuf.d_fileno = cntobn(pmp, pmp->pm_rootdirblk) * dirsperblk; else dirbuf.d_fileno = 1; dirbuf.d_type = DT_DIR; switch (n) { case 0: dirbuf.d_namlen = 1; strcpy(dirbuf.d_name, "."); break; case 1: dirbuf.d_namlen = 2; strcpy(dirbuf.d_name, ".."); break; } dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf); if (uio->uio_resid < dirbuf.d_reclen) goto out; error = uiomove(&dirbuf, dirbuf.d_reclen, uio); if (error) goto out; offset += sizeof(struct direntry); off = offset; if (cookies) { *cookies++ = offset; if (--ncookies <= 0) goto out; } } } } mbnambuf_init(); off = offset; while (uio->uio_resid > 0) { lbn = de_cluster(pmp, offset - bias); on = (offset - bias) & pmp->pm_crbomask; n = min(pmp->pm_bpcluster - on, uio->uio_resid); diff = dep->de_FileSize - (offset - bias); if (diff <= 0) break; n = min(n, diff); error = pcbmap(dep, lbn, &bn, &cn, &blsize); if (error) break; error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); if (error) { brelse(bp); return (error); } n = min(n, blsize - bp->b_resid); if (n == 0) { brelse(bp); return (EIO); } /* * Convert from dos directory entries to fs-independent * directory entries. */ for (dentp = (struct direntry *)(bp->b_data + on); (char *)dentp < bp->b_data + on + n; dentp++, offset += sizeof(struct direntry)) { #if 0 printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n", dentp, prev, crnt, dentp->deName[0], dentp->deAttributes); #endif /* * If this is an unused entry, we can stop. */ if (dentp->deName[0] == SLOT_EMPTY) { brelse(bp); goto out; } /* * Skip deleted entries. */ if (dentp->deName[0] == SLOT_DELETED) { chksum = -1; mbnambuf_init(); continue; } /* * Handle Win95 long directory entries */ if (dentp->deAttributes == ATTR_WIN95) { if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) continue; chksum = win2unixfn((struct winentry *)dentp, chksum, pmp); continue; } /* * Skip volume labels */ if (dentp->deAttributes & ATTR_VOLUME) { chksum = -1; mbnambuf_init(); continue; } /* * This computation of d_fileno must match * the computation of va_fileid in * msdosfs_getattr. */ if (dentp->deAttributes & ATTR_DIRECTORY) { fileno = getushort(dentp->deStartCluster); if (FAT32(pmp)) fileno |= getushort(dentp->deHighClust) << 16; /* if this is the root directory */ if (fileno == MSDOSFSROOT) if (FAT32(pmp)) fileno = cntobn(pmp, pmp->pm_rootdirblk) * dirsperblk; else fileno = 1; else fileno = cntobn(pmp, fileno) * dirsperblk; dirbuf.d_fileno = fileno; dirbuf.d_type = DT_DIR; } else { dirbuf.d_fileno = offset / sizeof(struct direntry); dirbuf.d_type = DT_REG; } if (chksum != winChksum(dentp->deName)) { dirbuf.d_namlen = dos2unixfn(dentp->deName, (u_char *)dirbuf.d_name, dentp->deLowerCase | ((pmp->pm_flags & MSDOSFSMNT_SHORTNAME) ? (LCASE_BASE | LCASE_EXT) : 0), pmp); mbnambuf_init(); } else mbnambuf_flush(&dirbuf); chksum = -1; dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf); if (uio->uio_resid < dirbuf.d_reclen) { brelse(bp); goto out; } error = uiomove(&dirbuf, dirbuf.d_reclen, uio); if (error) { brelse(bp); goto out; } if (cookies) { *cookies++ = offset + sizeof(struct direntry); if (--ncookies <= 0) { brelse(bp); goto out; } } off = offset + sizeof(struct direntry); } brelse(bp); } out: /* Subtract unused cookies */ if (ap->a_ncookies) *ap->a_ncookies -= ncookies; uio->uio_offset = off; /* * Set the eofflag (NFS uses it) */ if (ap->a_eofflag) { if (dep->de_FileSize - (offset - bias) <= 0) *ap->a_eofflag = 1; else *ap->a_eofflag = 0; } return (error); } /* * vp - address of vnode file the file * bn - which cluster we are interested in mapping to a filesystem block number. * vpp - returns the vnode for the block special file holding the filesystem * containing the file of interest * bnp - address of where to return the filesystem relative block number */ static int msdosfs_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 denode *dep = VTODE(ap->a_vp); daddr_t blkno; int error; if (ap->a_vpp != NULL) *ap->a_vpp = dep->de_devvp; if (ap->a_bnp == NULL) return (0); if (ap->a_runp) { /* * Sequential clusters should be counted here. */ *ap->a_runp = 0; } if (ap->a_runb) { *ap->a_runb = 0; } error = pcbmap(dep, ap->a_bn, &blkno, 0, 0); *ap->a_bnp = blkno; return (error); } static int msdosfs_strategy(ap) struct vop_strategy_args /* { struct vnode *a_vp; struct buf *a_bp; } */ *ap; { struct buf *bp = ap->a_bp; struct denode *dep = VTODE(bp->b_vp); struct vnode *vp; int error = 0; daddr_t blkno; KASSERT(ap->a_vp == ap->a_bp->b_vp, ("%s(%p != %p)", __func__, ap->a_vp, ap->a_bp->b_vp)); if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR) panic("msdosfs_strategy: spec"); /* * If we don't already know the filesystem relative block number * then get it using pcbmap(). If pcbmap() returns the block * number as -1 then we've got a hole in the file. DOS filesystems * don't allow files with holes, so we shouldn't ever see this. */ if (bp->b_blkno == bp->b_lblkno) { error = pcbmap(dep, bp->b_lblkno, &blkno, 0, 0); bp->b_blkno = blkno; if (error) { bp->b_error = error; bp->b_ioflags |= BIO_ERROR; bufdone(bp); return (error); } if ((long)bp->b_blkno == -1) vfs_bio_clrbuf(bp); } if (bp->b_blkno == -1) { bufdone(bp); return (0); } /* * Read/write the block from/to the disk that contains the desired * file block. */ vp = dep->de_devvp; bp->b_dev = vp->v_rdev; + bp->b_offset = dbtob(bp->b_blkno); VOP_SPECSTRATEGY(vp, bp); return (0); } static int msdosfs_print(ap) struct vop_print_args /* { struct vnode *vp; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); printf("\tstartcluster %lu, dircluster %lu, diroffset %lu, ", dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset); printf("on dev (%d, %d)\n", major(dep->de_dev), minor(dep->de_dev)); return (0); } static int msdosfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp; switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = 1; return (0); case _PC_NAME_MAX: *ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12; return (0); case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_NO_TRUNC: *ap->a_retval = 0; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* Global vfs data structures for msdosfs */ vop_t **msdosfs_vnodeop_p; static struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) vop_defaultop }, { &vop_access_desc, (vop_t *) msdosfs_access }, { &vop_bmap_desc, (vop_t *) msdosfs_bmap }, { &vop_cachedlookup_desc, (vop_t *) msdosfs_lookup }, { &vop_close_desc, (vop_t *) msdosfs_close }, { &vop_create_desc, (vop_t *) msdosfs_create }, { &vop_fsync_desc, (vop_t *) msdosfs_fsync }, { &vop_getattr_desc, (vop_t *) msdosfs_getattr }, { &vop_inactive_desc, (vop_t *) msdosfs_inactive }, { &vop_link_desc, (vop_t *) msdosfs_link }, { &vop_lookup_desc, (vop_t *) vfs_cache_lookup }, { &vop_mkdir_desc, (vop_t *) msdosfs_mkdir }, { &vop_mknod_desc, (vop_t *) msdosfs_mknod }, { &vop_pathconf_desc, (vop_t *) msdosfs_pathconf }, { &vop_print_desc, (vop_t *) msdosfs_print }, { &vop_read_desc, (vop_t *) msdosfs_read }, { &vop_readdir_desc, (vop_t *) msdosfs_readdir }, { &vop_reclaim_desc, (vop_t *) msdosfs_reclaim }, { &vop_remove_desc, (vop_t *) msdosfs_remove }, { &vop_rename_desc, (vop_t *) msdosfs_rename }, { &vop_rmdir_desc, (vop_t *) msdosfs_rmdir }, { &vop_setattr_desc, (vop_t *) msdosfs_setattr }, { &vop_strategy_desc, (vop_t *) msdosfs_strategy }, { &vop_symlink_desc, (vop_t *) msdosfs_symlink }, { &vop_write_desc, (vop_t *) msdosfs_write }, { NULL, NULL } }; static struct vnodeopv_desc msdosfs_vnodeop_opv_desc = { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries }; VNODEOP_SET(msdosfs_vnodeop_opv_desc); diff --git a/sys/fs/nwfs/nwfs_vnops.c b/sys/fs/nwfs/nwfs_vnops.c index ab8a4f24fd11..4215e633d4d9 100644 --- a/sys/fs/nwfs/nwfs_vnops.c +++ b/sys/fs/nwfs/nwfs_vnops.c @@ -1,1026 +1,1025 @@ /* * Copyright (c) 1999, 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 NWFS vnode operations */ static int nwfs_create(struct vop_create_args *); static int nwfs_mknod(struct vop_mknod_args *); static int nwfs_open(struct vop_open_args *); static int nwfs_close(struct vop_close_args *); static int nwfs_access(struct vop_access_args *); static int nwfs_getattr(struct vop_getattr_args *); static int nwfs_setattr(struct vop_setattr_args *); static int nwfs_read(struct vop_read_args *); static int nwfs_write(struct vop_write_args *); static int nwfs_fsync(struct vop_fsync_args *); static int nwfs_remove(struct vop_remove_args *); static int nwfs_link(struct vop_link_args *); static int nwfs_lookup(struct vop_lookup_args *); static int nwfs_rename(struct vop_rename_args *); static int nwfs_mkdir(struct vop_mkdir_args *); static int nwfs_rmdir(struct vop_rmdir_args *); static int nwfs_symlink(struct vop_symlink_args *); static int nwfs_readdir(struct vop_readdir_args *); static int nwfs_strategy(struct vop_strategy_args *); static int nwfs_print(struct vop_print_args *); static int nwfs_pathconf(struct vop_pathconf_args *ap); /* Global vfs data structures for nwfs */ vop_t **nwfs_vnodeop_p; static struct vnodeopv_entry_desc nwfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) vop_defaultop }, { &vop_access_desc, (vop_t *) nwfs_access }, { &vop_open_desc, (vop_t *) nwfs_open }, { &vop_close_desc, (vop_t *) nwfs_close }, { &vop_create_desc, (vop_t *) nwfs_create }, { &vop_fsync_desc, (vop_t *) nwfs_fsync }, { &vop_getattr_desc, (vop_t *) nwfs_getattr }, { &vop_getpages_desc, (vop_t *) nwfs_getpages }, { &vop_putpages_desc, (vop_t *) nwfs_putpages }, { &vop_ioctl_desc, (vop_t *) nwfs_ioctl }, { &vop_inactive_desc, (vop_t *) nwfs_inactive }, { &vop_link_desc, (vop_t *) nwfs_link }, { &vop_lookup_desc, (vop_t *) nwfs_lookup }, { &vop_mkdir_desc, (vop_t *) nwfs_mkdir }, { &vop_mknod_desc, (vop_t *) nwfs_mknod }, { &vop_pathconf_desc, (vop_t *) nwfs_pathconf }, { &vop_print_desc, (vop_t *) nwfs_print }, { &vop_read_desc, (vop_t *) nwfs_read }, { &vop_readdir_desc, (vop_t *) nwfs_readdir }, { &vop_reclaim_desc, (vop_t *) nwfs_reclaim }, { &vop_remove_desc, (vop_t *) nwfs_remove }, { &vop_rename_desc, (vop_t *) nwfs_rename }, { &vop_rmdir_desc, (vop_t *) nwfs_rmdir }, { &vop_setattr_desc, (vop_t *) nwfs_setattr }, { &vop_strategy_desc, (vop_t *) nwfs_strategy }, { &vop_symlink_desc, (vop_t *) nwfs_symlink }, { &vop_write_desc, (vop_t *) nwfs_write }, { NULL, NULL } }; static struct vnodeopv_desc nwfs_vnodeop_opv_desc = { &nwfs_vnodeop_p, nwfs_vnodeop_entries }; VNODEOP_SET(nwfs_vnodeop_opv_desc); /* * nwfs_access vnode op */ static int nwfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct thread *td; } */ *ap; { struct vnode *vp = ap->a_vp; mode_t mpmode; struct nwmount *nmp = VTONWFS(vp); NCPVNDEBUG("\n"); if ((ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { switch (vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); default: break; } } mpmode = vp->v_type == VREG ? nmp->m.file_mode : nmp->m.dir_mode; return (vaccess(vp->v_type, mpmode, nmp->m.uid, nmp->m.gid, ap->a_mode, ap->a_cred, NULL)); } /* * nwfs_open vnode op */ /* ARGSUSED */ static int nwfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct thread *td; } */ *ap; { struct vnode *vp = ap->a_vp; int mode = ap->a_mode; struct nwnode *np = VTONW(vp); struct ncp_open_info no; struct nwmount *nmp = VTONWFS(vp); struct vattr vattr; int error, nwm; NCPVNDEBUG("%s,%d\n", np->n_name, np->opened); if (vp->v_type != VREG && vp->v_type != VDIR) { NCPFATAL("open vtype = %d\n", vp->v_type); return (EACCES); } if (vp->v_type == VDIR) return 0; /* nothing to do now */ if (np->n_flag & NMODIFIED) { if ((error = nwfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_td, 1)) == EINTR) return (error); np->n_atime = 0; error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td); if (error) return (error); np->n_mtime = vattr.va_mtime.tv_sec; } else { error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td); if (error) return (error); if (np->n_mtime != vattr.va_mtime.tv_sec) { if ((error = nwfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_td, 1)) == EINTR) return (error); np->n_mtime = vattr.va_mtime.tv_sec; } } if (np->opened) { np->opened++; return 0; } nwm = AR_READ; if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) nwm |= AR_WRITE; error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0, nwm, &no, ap->a_td, ap->a_cred); if (error) { if (mode & FWRITE) return EACCES; nwm = AR_READ; error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0, nwm, &no, ap->a_td, ap->a_cred); } if (!error) { np->opened++; np->n_fh = no.fh; np->n_origfh = no.origfh; } np->n_atime = 0; return (error); } static int nwfs_close(ap) struct vop_close_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct thread *td; } */ *ap; { struct vnode *vp = ap->a_vp; struct nwnode *np = VTONW(vp); int error; NCPVNDEBUG("name=%s,pid=%d,c=%d\n", np->n_name, ap->a_td->td_proc->p_pid, np->opened); if (vp->v_type == VDIR) return 0; /* nothing to do now */ error = 0; mtx_lock(&vp->v_interlock); if (np->opened == 0) { mtx_unlock(&vp->v_interlock); return 0; } mtx_unlock(&vp->v_interlock); error = nwfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_td, 1); mtx_lock(&vp->v_interlock); if (np->opened == 0) { mtx_unlock(&vp->v_interlock); return 0; } if (--np->opened == 0) { mtx_unlock(&vp->v_interlock); error = ncp_close_file(NWFSTOCONN(VTONWFS(vp)), &np->n_fh, ap->a_td, ap->a_cred); } else mtx_unlock(&vp->v_interlock); np->n_atime = 0; return (error); } /* * nwfs_getattr call from vfs. */ static int nwfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct thread *td; } */ *ap; { struct vnode *vp = ap->a_vp; struct nwnode *np = VTONW(vp); struct vattr *va=ap->a_vap; struct nwmount *nmp = VTONWFS(vp); struct nw_entry_info fattr; int error; u_int32_t oldsize; NCPVNDEBUG("%lx:%d: '%s' %d\n", (long)vp, nmp->n_volume, np->n_name, (vp->v_vflag & VV_ROOT) != 0); error = nwfs_attr_cachelookup(vp, va); if (!error) return 0; NCPVNDEBUG("not in cache\n"); oldsize = np->n_size; if (np->n_flag & NVOLUME) { error = ncp_obtain_info(nmp, np->n_fid.f_id, 0, NULL, &fattr, ap->a_td, ap->a_cred); } else { error = ncp_obtain_info(nmp, np->n_fid.f_parent, np->n_nmlen, np->n_name, &fattr, ap->a_td, ap->a_cred); } if (error) { NCPVNDEBUG("error %d\n", error); return error; } nwfs_attr_cacheenter(vp, &fattr); *va = np->n_vattr; if (np->opened) np->n_size = oldsize; return (0); } /* * nwfs_setattr call from vfs. */ static int nwfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct thread *td; } */ *ap; { struct vnode *vp = ap->a_vp; struct nwnode *np = VTONW(vp); struct vattr *vap = ap->a_vap; u_quad_t tsize=0; int error = 0; NCPVNDEBUG("\n"); if (vap->va_flags != VNOVAL) return (EOPNOTSUPP); /* * Disallow write attempts if the filesystem is mounted read-only. */ if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) &&(vp->v_mount->mnt_flag & MNT_RDONLY)) return (EROFS); if (vap->va_size != VNOVAL) { switch (vp->v_type) { case VDIR: return (EISDIR); case VREG: /* * Disallow write attempts if the filesystem is * mounted read-only. */ if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); vnode_pager_setsize(vp, (u_long)vap->va_size); tsize = np->n_size; np->n_size = vap->va_size; break; default: return EINVAL; }; } error = ncp_setattr(vp, vap, ap->a_cred, ap->a_td); if (error && vap->va_size != VNOVAL) { np->n_size = tsize; vnode_pager_setsize(vp, (u_long)tsize); } np->n_atime = 0; /* invalidate cache */ VOP_GETATTR(vp, vap, ap->a_cred, ap->a_td); np->n_mtime = vap->va_mtime.tv_sec; return (0); } /* * nwfs_read call. */ static int nwfs_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { struct vnode *vp = ap->a_vp; struct uio *uio=ap->a_uio; int error; NCPVNDEBUG("nwfs_read:\n"); if (vp->v_type != VREG && vp->v_type != VDIR) return (EPERM); error = nwfs_readvnode(vp, uio, ap->a_cred); return error; } static int nwfs_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; int error; NCPVNDEBUG("%d,ofs=%d,sz=%d\n", vp->v_type, (int)uio->uio_offset, uio->uio_resid); if (vp->v_type != VREG) return (EPERM); error = nwfs_writevnode(vp, uio, ap->a_cred, ap->a_ioflag); return(error); } /* * nwfs_create call * Create a regular file. On entry the directory to contain the file being * created is locked. We must release before we return. We must also free * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or * only if the SAVESTART bit in cn_flags is clear on success. */ static int nwfs_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { 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 vnode *vp = (struct vnode *)0; int error = 0, fmode; struct vattr vattr; struct nwnode *np; struct ncp_open_info no; struct nwmount *nmp=VTONWFS(dvp); ncpfid fid; NCPVNDEBUG("\n"); *vpp = NULL; if (vap->va_type == VSOCK) return (EOPNOTSUPP); if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_thread))) { return (error); } fmode = AR_READ | AR_WRITE; /* if (vap->va_vaflags & VA_EXCLUSIVE) fmode |= AR_DENY_READ | AR_DENY_WRITE;*/ error = ncp_open_create_file_or_subdir(nmp, dvp, cnp->cn_namelen, cnp->cn_nameptr, OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, 0, fmode, &no, cnp->cn_thread, cnp->cn_cred); if (!error) { error = ncp_close_file(NWFSTOCONN(nmp), &no.fh, cnp->cn_thread, cnp->cn_cred); fid.f_parent = VTONW(dvp)->n_fid.f_id; fid.f_id = no.fattr.dirEntNum; error = nwfs_nget(VTOVFS(dvp), fid, &no.fattr, dvp, &vp); if (!error) { np = VTONW(vp); np->opened = 0; *vpp = vp; } if (cnp->cn_flags & MAKEENTRY) cache_enter(dvp, vp, cnp); } return (error); } /* * nwfs_remove call. It isn't possible to emulate UFS behaivour because * NetWare doesn't allow delete/rename operations on an opened file. */ static int nwfs_remove(ap) struct vop_remove_args /* { struct vnodeop_desc *a_desc; struct vnode * a_dvp; struct vnode * a_vp; struct componentname * a_cnp; } */ *ap; { struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; struct nwnode *np = VTONW(vp); struct nwmount *nmp = VTONWFS(vp); int error; if (vp->v_type == VDIR || np->opened || vrefcnt(vp) != 1) return EPERM; cache_purge(vp); error = ncp_DeleteNSEntry(nmp, VTONW(dvp)->n_fid.f_id, cnp->cn_namelen, cnp->cn_nameptr, cnp->cn_thread, cnp->cn_cred); if (error == 0) np->n_flag |= NSHOULDFREE; else if (error == 0x899c) error = EACCES; return (error); } /* * nwfs_file rename call */ static int nwfs_rename(ap) struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap; { 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 nwmount *nmp=VTONWFS(fvp); u_int16_t oldtype = 6; int error=0; /* Check for cross-device rename */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; goto out; } if (tvp && vrefcnt(tvp) > 1) { error = EBUSY; goto out; } if (fvp->v_type == VDIR) { oldtype |= NW_TYPE_SUBDIR; } else if (fvp->v_type == VREG) { oldtype |= NW_TYPE_FILE; } else { error = EINVAL; goto out; } if (tvp && tvp != fvp) { error = ncp_DeleteNSEntry(nmp, VTONW(tdvp)->n_fid.f_id, tcnp->cn_namelen, tcnp->cn_nameptr, tcnp->cn_thread, tcnp->cn_cred); if (error == 0x899c) error = EACCES; if (error) goto out_cacherem; } error = ncp_nsrename(NWFSTOCONN(nmp), nmp->n_volume, nmp->name_space, oldtype, &nmp->m.nls, VTONW(fdvp)->n_fid.f_id, fcnp->cn_nameptr, fcnp->cn_namelen, VTONW(tdvp)->n_fid.f_id, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_thread, tcnp->cn_cred); if (error == 0x8992) error = EEXIST; if (fvp->v_type == VDIR) { if (tvp != NULL && tvp->v_type == VDIR) cache_purge(tdvp); cache_purge(fdvp); } out_cacherem: nwfs_attr_cacheremove(fdvp); nwfs_attr_cacheremove(tdvp); nwfs_attr_cacheremove(fvp); out: if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fdvp); vrele(fvp); if (tvp) nwfs_attr_cacheremove(tvp); /* * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry. */ if (error == ENOENT) error = 0; return (error); } /* * nwfs hard link create call * Netware filesystems don't know what links are. */ static int nwfs_link(ap) struct vop_link_args /* { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { return EOPNOTSUPP; } /* * nwfs_symlink link create call * Netware filesystems don't know what symlinks are. */ static int nwfs_symlink(ap) struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap; { return (EOPNOTSUPP); } static int nwfs_mknod(ap) struct vop_mknod_args /* { } */ *ap; { return (EOPNOTSUPP); } /* * nwfs_mkdir call */ static int nwfs_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct vnode *dvp = ap->a_dvp; /* struct vattr *vap = ap->a_vap;*/ struct componentname *cnp = ap->a_cnp; int len=cnp->cn_namelen; struct ncp_open_info no; struct nwnode *np; struct vnode *newvp = (struct vnode *)0; ncpfid fid; int error = 0; struct vattr vattr; char *name=cnp->cn_nameptr; if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_thread))) { return (error); } if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))) { return EEXIST; } if (ncp_open_create_file_or_subdir(VTONWFS(dvp), dvp, cnp->cn_namelen, cnp->cn_nameptr, OC_MODE_CREATE, aDIR, 0xffff, &no, cnp->cn_thread, cnp->cn_cred) != 0) { error = EACCES; } else { error = 0; } if (!error) { fid.f_parent = VTONW(dvp)->n_fid.f_id; fid.f_id = no.fattr.dirEntNum; error = nwfs_nget(VTOVFS(dvp), fid, &no.fattr, dvp, &newvp); if (!error) { np = VTONW(newvp); newvp->v_type = VDIR; *ap->a_vpp = newvp; } } return (error); } /* * nwfs_remove directory call */ static int nwfs_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; struct nwnode *np = VTONW(vp); struct nwmount *nmp = VTONWFS(vp); struct nwnode *dnp = VTONW(dvp); int error = EIO; if (dvp == vp) return EINVAL; error = ncp_DeleteNSEntry(nmp, dnp->n_fid.f_id, cnp->cn_namelen, cnp->cn_nameptr, cnp->cn_thread, cnp->cn_cred); if (error == 0) np->n_flag |= NSHOULDFREE; else if (error == NWE_DIR_NOT_EMPTY) error = ENOTEMPTY; dnp->n_flag |= NMODIFIED; nwfs_attr_cacheremove(dvp); cache_purge(dvp); cache_purge(vp); return (error); } /* * nwfs_readdir call */ static int nwfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap; { struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; int error; if (vp->v_type != VDIR) return (EPERM); if (ap->a_ncookies) { printf("nwfs_readdir: no support for cookies now..."); return (EOPNOTSUPP); } error = nwfs_readvnode(vp, uio, ap->a_cred); return error; } /* ARGSUSED */ static int nwfs_fsync(ap) struct vop_fsync_args /* { struct vnodeop_desc *a_desc; struct vnode * a_vp; struct ucred * a_cred; int a_waitfor; struct thread *a_td; } */ *ap; { /* return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_td, 1));*/ return (0); } /* ARGSUSED */ static int nwfs_print (ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; struct nwnode *np = VTONW(vp); printf("\tnwfs node: name = '%s', fid = %d, pfid = %d\n", np->n_name, np->n_fid.f_id, np->n_fid.f_parent); return (0); } static int nwfs_pathconf (ap) struct vop_pathconf_args /* { struct vnode *vp; int name; register_t *retval; } */ *ap; { int name=ap->a_name, error=0; register_t *retval=ap->a_retval; switch(name){ case _PC_LINK_MAX: *retval=0; break; case _PC_NAME_MAX: *retval=NCP_MAX_FILENAME; /* XXX from nwfsnode */ break; case _PC_PATH_MAX: *retval=NCP_MAXPATHLEN; /* XXX from nwfsnode */ break; default: error=EINVAL; } return(error); } static int nwfs_strategy (ap) struct vop_strategy_args /* { struct buf *a_bp } */ *ap; { struct buf *bp=ap->a_bp; struct ucred *cr; struct thread *td; int error = 0; KASSERT(ap->a_vp == ap->a_bp->b_vp, ("%s(%p != %p)", __func__, ap->a_vp, ap->a_bp->b_vp)); NCPVNDEBUG("\n"); - KASSERT(!(bp->b_flags & B_PHYS), ("nwfs physio")); if (bp->b_flags & B_ASYNC) td = (struct thread *)0; else td = curthread; /* XXX */ if (bp->b_iocmd == BIO_READ) cr = bp->b_rcred; else cr = bp->b_wcred; /* * If the op is asynchronous and an i/o daemon is waiting * queue the request, wake it up and wait for completion * otherwise just do it ourselves. */ if ((bp->b_flags & B_ASYNC) == 0 ) error = nwfs_doio(bp, cr, td); return (error); } /* * How to keep the brain busy ... * Currently lookup routine can make two lookup for vnode. This can be * avoided by reorg the code. */ int nwfs_lookup(ap) struct vop_lookup_args /* { struct vnodeop_desc *a_desc; struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct vnode *dvp = ap->a_dvp; struct vnode **vpp = ap->a_vpp; int flags = cnp->cn_flags; struct vnode *vp; struct nwmount *nmp; struct mount *mp = dvp->v_mount; struct nwnode *dnp, *npp; struct nw_entry_info fattr, *fap; ncpfid fid; int nameiop=cnp->cn_nameiop, islastcn; int lockparent, wantparent, error = 0, notfound; struct thread *td = cnp->cn_thread; char _name[cnp->cn_namelen+1]; bcopy(cnp->cn_nameptr, _name, cnp->cn_namelen); _name[cnp->cn_namelen]=0; if (dvp->v_type != VDIR) return (ENOTDIR); if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT)) { printf("nwfs_lookup: invalid '..'\n"); return EIO; } NCPVNDEBUG("%d '%s' in '%s' id=d\n", nameiop, _name, VTONW(dvp)->n_name/*, VTONW(dvp)->n_name*/); islastcn = flags & ISLASTCN; if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP)) return (EROFS); if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td))) return (error); lockparent = flags & LOCKPARENT; wantparent = flags & (LOCKPARENT|WANTPARENT); nmp = VFSTONWFS(mp); dnp = VTONW(dvp); /* printf("dvp %d:%d:%d\n", (int)mp, (int)dvp->v_vflag & VV_ROOT, (int)flags & ISDOTDOT); */ error = ncp_pathcheck(cnp->cn_nameptr, cnp->cn_namelen, &nmp->m.nls, (nameiop == CREATE || nameiop == RENAME) && (nmp->m.nls.opt & NWHP_NOSTRICT) == 0); if (error) return ENOENT; error = cache_lookup(dvp, vpp, cnp); NCPVNDEBUG("cache_lookup returned %d\n", error); if (error > 0) return error; if (error) { /* name was found */ struct vattr vattr; int vpid; vp = *vpp; vpid = vp->v_id; if (dvp == vp) { /* lookup on current */ vref(vp); error = 0; NCPVNDEBUG("cached '.'"); } else if (flags & ISDOTDOT) { VOP_UNLOCK(dvp, 0, td); /* unlock parent */ error = vget(vp, LK_EXCLUSIVE, td); if (!error && lockparent && islastcn) error = vn_lock(dvp, LK_EXCLUSIVE, td); } else { error = vget(vp, LK_EXCLUSIVE, td); if (!lockparent || error || !islastcn) VOP_UNLOCK(dvp, 0, td); } if (!error) { if (vpid == vp->v_id) { if (!VOP_GETATTR(vp, &vattr, cnp->cn_cred, td) && vattr.va_ctime.tv_sec == VTONW(vp)->n_ctime) { if (nameiop != LOOKUP && islastcn) cnp->cn_flags |= SAVENAME; NCPVNDEBUG("use cached vnode"); return (0); } cache_purge(vp); } vput(vp); if (lockparent && dvp != vp && islastcn) VOP_UNLOCK(dvp, 0, td); } error = vn_lock(dvp, LK_EXCLUSIVE, td); *vpp = NULLVP; if (error) return (error); } /* not in cache, so ... */ error = 0; *vpp = NULLVP; fap = NULL; if (flags & ISDOTDOT) { if (NWCMPF(&dnp->n_parent, &nmp->n_rootent)) { fid = nmp->n_rootent; fap = NULL; notfound = 0; } else { error = nwfs_lookupnp(nmp, dnp->n_parent, td, &npp); if (error) { return error; } fid = dnp->n_parent; fap = &fattr; /*np = *npp;*/ notfound = ncp_obtain_info(nmp, npp->n_dosfid, 0, NULL, fap, td, cnp->cn_cred); } } else { fap = &fattr; notfound = ncp_lookup(dvp, cnp->cn_namelen, cnp->cn_nameptr, fap, td, cnp->cn_cred); fid.f_id = fap->dirEntNum; if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { fid.f_parent = dnp->n_fid.f_parent; } else fid.f_parent = dnp->n_fid.f_id; NCPVNDEBUG("call to ncp_lookup returned=%d\n", notfound); } if (notfound && notfound < 0x80 ) return (notfound); /* hard error */ if (notfound) { /* entry not found */ /* Handle RENAME or CREATE case... */ if ((nameiop == CREATE || nameiop == RENAME) && wantparent && islastcn) { cnp->cn_flags |= SAVENAME; if (!lockparent) VOP_UNLOCK(dvp, 0, td); return (EJUSTRETURN); } return ENOENT; }/* else { NCPVNDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum); }*/ /* handle DELETE case ... */ if (nameiop == DELETE && islastcn) { /* delete last component */ error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, cnp->cn_thread); if (error) return (error); if (NWCMPF(&dnp->n_fid, &fid)) { /* we found ourselfs */ VREF(dvp); *vpp = dvp; return 0; } error = nwfs_nget(mp, fid, fap, dvp, &vp); if (error) return (error); *vpp = vp; cnp->cn_flags |= SAVENAME; /* I free it later */ if (!lockparent) VOP_UNLOCK(dvp, 0, td); return (0); } if (nameiop == RENAME && islastcn && wantparent) { error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, cnp->cn_thread); if (error) return (error); if (NWCMPF(&dnp->n_fid, &fid)) return EISDIR; error = nwfs_nget(mp, fid, fap, dvp, &vp); if (error) return (error); *vpp = vp; cnp->cn_flags |= SAVENAME; if (!lockparent) VOP_UNLOCK(dvp, 0, td); return (0); } if (flags & ISDOTDOT) { VOP_UNLOCK(dvp, 0, td); /* race to get the inode */ error = nwfs_nget(mp, fid, NULL, NULL, &vp); if (error) { vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); return (error); } if (lockparent && islastcn && (error = vn_lock(dvp, LK_EXCLUSIVE, td))) { vput(vp); return (error); } *vpp = vp; } else if (NWCMPF(&dnp->n_fid, &fid)) { vref(dvp); *vpp = dvp; } else { error = nwfs_nget(mp, fid, fap, dvp, &vp); if (error) return (error); *vpp = vp; NCPVNDEBUG("lookup: getnewvp!\n"); if (!lockparent || !islastcn) VOP_UNLOCK(dvp, 0, td); } if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) { VTONW(*vpp)->n_ctime = VTONW(*vpp)->n_vattr.va_ctime.tv_sec; cache_enter(dvp, *vpp, cnp); } return (0); } diff --git a/sys/fs/smbfs/smbfs_vnops.c b/sys/fs/smbfs/smbfs_vnops.c index 383f565e2d2b..f2e0f1e4d557 100644 --- a/sys/fs/smbfs/smbfs_vnops.c +++ b/sys/fs/smbfs/smbfs_vnops.c @@ -1,1297 +1,1296 @@ /* * 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_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 *); static int smbfs_getextattr(struct vop_getextattr_args *ap); 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_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_link_desc, (vop_t *) smbfs_link }, { &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_write_desc, (vop_t *) smbfs_write }, { &vop_getextattr_desc, (vop_t *) smbfs_getextattr }, /* { &vop_setextattr_desc, (vop_t *) smbfs_setextattr },*/ { 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 thread *a_td; } */ *ap; { struct vnode *vp = ap->a_vp; mode_t mode = ap->a_mode; mode_t mpmode; struct smbmount *smp = VTOSMBFS(vp); 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; } } mpmode = vp->v_type == VREG ? smp->sm_args.file_mode : smp->sm_args.dir_mode; return (vaccess(vp->v_type, mpmode, smp->sm_args.uid, smp->sm_args.gid, ap->a_mode, ap->a_cred, NULL)); } /* ARGSUSED */ static int smbfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct thread *a_td; } */ *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_flag & NOPEN) != 0); 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_flag |= NOPEN; return 0; } if (np->n_flag & NMODIFIED) { if ((error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_td, 1)) == EINTR) return error; smbfs_attr_cacheremove(vp); error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td); 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_td); 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_td, 1); if (error == EINTR) return error; np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; } } if ((np->n_flag & NOPEN) != 0) return 0; /* * Use DENYNONE to give unixy semantics of permitting * everything not forbidden by permissions. Ie denial * is up to server with clients/openers needing to use * advisory locks for further control. */ accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD; if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) accmode = SMB_SM_DENYNONE|SMB_AM_OPENRW; smb_makescred(&scred, ap->a_td, ap->a_cred); error = smbfs_smb_open(np, accmode, &scred); if (error) { if (mode & FWRITE) return EACCES; else if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD; error = smbfs_smb_open(np, accmode, &scred); } } if (error == 0) np->n_flag |= NOPEN; 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 thread *a_td; } */ *ap; { struct vnode *vp = ap->a_vp; struct thread *td = ap->a_td; int dolock; VI_LOCK(vp); dolock = (vp->v_iflag & VI_XLOCK) == 0; if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_INTERLOCK, td); else VI_UNLOCK(vp); /* Nothing. */ if (dolock) VOP_UNLOCK(vp, 0, td); return 0; } /* * 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 thread *a_td; } */ *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_vflag & VV_ROOT) != 0); error = smbfs_attr_cachelookup(vp, va); if (!error) return 0; SMBVDEBUG("not in the cache\n"); smb_makescred(&scred, ap->a_td, 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_flag & NOPEN) 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 thread *a_td; } */ *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_td, 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_flag & NOPEN) == 0) { error = smbfs_smb_open(np, SMB_SM_DENYNONE|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 (ap->a_cred->cr_uid != VTOSMBFS(vp)->sm_args.uid && (error = suser_cred(ap->a_cred, PRISON_ROOT)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || (error = VOP_ACCESS(vp, VWRITE, ap->a_cred, ap->a_td)))) return (error); #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_flag & NOPEN) == 0) { if (vcp->vc_flags & SMBV_WIN95) { error = VOP_OPEN(vp, FWRITE, ap->a_cred, ap->a_td, -1); if (!error) { /* error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td);*/ if (mtime) np->n_mtime = *mtime; VOP_CLOSE(vp, FWRITE, ap->a_cred, ap->a_td); } } 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_td); 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_thread))) return error; smb_makescred(&scred, cnp->cn_thread, 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_flag & NOPEN) != 0 || vrefcnt(vp) != 1) return EPERM; smb_makescred(&scred, cnp->cn_thread, 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 && vrefcnt(tvp) > 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 { error = EINVAL; goto out; } smb_makescred(&scred, tcnp->cn_thread, 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_cacherem; } 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_cacherem: smbfs_attr_cacheremove(fdvp); smbfs_attr_cacheremove(tdvp); out: if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fdvp); vrele(fvp); #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_thread))) { return error; } if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))) return EEXIST; smb_makescred(&scred, cnp->cn_thread, 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_thread, 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 thread * a_td; } */ *ap; { /* return (smb_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_td, 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("\tname = %s, parent = %p, open = %d\n", np->n_name, np->n_parent ? np->n_parent : NULL, (np->n_flag & NOPEN) != 0); 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_hflags2 & SMB_FLAGS2_KNOWS_LONG_NAMES) ? 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 thread *td; int error = 0; KASSERT(ap->a_vp == ap->a_bp->b_vp, ("%s(%p != %p)", __func__, ap->a_vp, ap->a_bp->b_vp)); SMBVDEBUG("\n"); - KASSERT(!(bp->b_flags & B_PHYS), ("smbfs physio")); if (bp->b_flags & B_ASYNC) td = (struct thread *)0; else td = curthread; /* 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, td); return error; } 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 thread *td; } */ *ap; { return ENOTTY; } 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 thread *a_td; }; */ { struct vnode *vp = ap->a_vp; struct thread *td = ap->a_td; 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, td); if (error) return error; error = VOP_GETATTR(vp, &vattr, cred, td); 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 thread *td = curthread; struct smb_cred scred; u_quad_t size; off_t start, end, oadd; 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: if (size > OFF_MAX || (fl->l_start > 0 && size > OFF_MAX - fl->l_start)) return EOVERFLOW; start = size + fl->l_start; break; default: return EINVAL; } if (start < 0) return EINVAL; if (fl->l_len < 0) { if (start == 0) return EINVAL; end = start - 1; start += fl->l_len; if (start < 0) return EINVAL; } else if (fl->l_len == 0) end = -1; else { oadd = fl->l_len - 1; if (oadd > OFF_MAX - start) return EOVERFLOW; end = start + oadd; } smb_makescred(&scred, td, td->td_ucred); 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 thread *td = cnp->cn_thread; 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_vflag & VV_ROOT)) { 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, td)) != 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; mp_fixme("Unlocked v_id access."); 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, td); /* unlock parent */ cnp->cn_flags |= PDIRUNLOCK; error = vget(vp, LK_EXCLUSIVE, td); if (!error && lockparent && islastcn) { error = vn_lock(dvp, LK_EXCLUSIVE, td); if (error == 0) cnp->cn_flags &= ~PDIRUNLOCK; } } else { error = vget(vp, LK_EXCLUSIVE, td); if (!lockparent || error || !islastcn) { VOP_UNLOCK(dvp, 0, td); cnp->cn_flags |= PDIRUNLOCK; } } if (!error) { if (vpid == vp->v_id) { if (!VOP_GETATTR(vp, &vattr, cnp->cn_cred, td) /* && 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, td); } error = vn_lock(dvp, LK_EXCLUSIVE, td); *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, td, cnp->cn_cred); fap = &fattr; if (flags & ISDOTDOT) { error = smbfs_smb_lookup(VTOSMB(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) { error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); if (error) return error; cnp->cn_flags |= SAVENAME; if (!lockparent) { VOP_UNLOCK(dvp, 0, td); 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, td); 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, td); cnp->cn_flags |= PDIRUNLOCK; } return 0; } if (nameiop == RENAME && islastcn && wantparent) { error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); 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, td); cnp->cn_flags |= PDIRUNLOCK; } return 0; } if (flags & ISDOTDOT) { VOP_UNLOCK(dvp, 0, td); error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp); if (error) { vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); return error; } if (lockparent && islastcn) { error = vn_lock(dvp, LK_EXCLUSIVE, td); 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, td); cnp->cn_flags |= PDIRUNLOCK; } } if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) { /* VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/ cache_enter(dvp, *vpp, cnp); } return 0; } diff --git a/sys/fs/specfs/spec_vnops.c b/sys/fs/specfs/spec_vnops.c index 8b5d2a54d3a0..bde0786de720 100644 --- a/sys/fs/specfs/spec_vnops.c +++ b/sys/fs/specfs/spec_vnops.c @@ -1,853 +1,852 @@ /* * Copyright (c) 1989, 1993, 1995 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)spec_vnops.c 8.14 (Berkeley) 5/21/95 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int spec_advlock(struct vop_advlock_args *); static int spec_close(struct vop_close_args *); static int spec_freeblks(struct vop_freeblks_args *); static int spec_fsync(struct vop_fsync_args *); static int spec_getpages(struct vop_getpages_args *); static int spec_ioctl(struct vop_ioctl_args *); static int spec_kqfilter(struct vop_kqfilter_args *); static int spec_open(struct vop_open_args *); static int spec_poll(struct vop_poll_args *); static int spec_print(struct vop_print_args *); static int spec_read(struct vop_read_args *); static int spec_specstrategy(struct vop_specstrategy_args *); static int spec_write(struct vop_write_args *); vop_t **spec_vnodeop_p; static struct vnodeopv_entry_desc spec_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) vop_defaultop }, { &vop_access_desc, (vop_t *) vop_ebadf }, { &vop_advlock_desc, (vop_t *) spec_advlock }, { &vop_bmap_desc, (vop_t *) vop_panic }, { &vop_close_desc, (vop_t *) spec_close }, { &vop_create_desc, (vop_t *) vop_panic }, { &vop_freeblks_desc, (vop_t *) spec_freeblks }, { &vop_fsync_desc, (vop_t *) spec_fsync }, { &vop_getpages_desc, (vop_t *) spec_getpages }, { &vop_getwritemount_desc, (vop_t *) vop_stdgetwritemount }, { &vop_ioctl_desc, (vop_t *) spec_ioctl }, { &vop_kqfilter_desc, (vop_t *) spec_kqfilter }, { &vop_lease_desc, (vop_t *) vop_null }, { &vop_link_desc, (vop_t *) vop_panic }, { &vop_mkdir_desc, (vop_t *) vop_panic }, { &vop_mknod_desc, (vop_t *) vop_panic }, { &vop_open_desc, (vop_t *) spec_open }, { &vop_pathconf_desc, (vop_t *) vop_stdpathconf }, { &vop_poll_desc, (vop_t *) spec_poll }, { &vop_print_desc, (vop_t *) spec_print }, { &vop_read_desc, (vop_t *) spec_read }, { &vop_readdir_desc, (vop_t *) vop_panic }, { &vop_readlink_desc, (vop_t *) vop_panic }, { &vop_reallocblks_desc, (vop_t *) vop_panic }, { &vop_reclaim_desc, (vop_t *) vop_null }, { &vop_remove_desc, (vop_t *) vop_panic }, { &vop_rename_desc, (vop_t *) vop_panic }, { &vop_rmdir_desc, (vop_t *) vop_panic }, { &vop_setattr_desc, (vop_t *) vop_ebadf }, { &vop_specstrategy_desc, (vop_t *) spec_specstrategy }, { &vop_strategy_desc, (vop_t *) vop_panic }, { &vop_symlink_desc, (vop_t *) vop_panic }, { &vop_write_desc, (vop_t *) spec_write }, { NULL, NULL } }; static struct vnodeopv_desc spec_vnodeop_opv_desc = { &spec_vnodeop_p, spec_vnodeop_entries }; VNODEOP_SET(spec_vnodeop_opv_desc); int spec_vnoperate(ap) struct vop_generic_args /* { struct vnodeop_desc *a_desc; } */ *ap; { return (VOCALL(spec_vnodeop_p, ap->a_desc->vdesc_offset, ap)); } /* * Open a special file. */ /* ARGSUSED */ static int spec_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct thread *td = ap->a_td; struct vnode *vp = ap->a_vp; dev_t dev = vp->v_rdev; int error; struct cdevsw *dsw; const char *cp; if (vp->v_type == VBLK) return (ENXIO); /* Don't allow open if fs is mounted -nodev. */ if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV)) return (ENXIO); dsw = devsw(dev); if (dsw == NULL || dsw->d_open == NULL) return (ENXIO); /* Make this field valid before any I/O in d_open. */ if (dev->si_iosize_max == 0) dev->si_iosize_max = DFLTPHYS; /* * XXX: Disks get special billing here, but it is mostly wrong. * XXX: Disk partitions can overlap and the real checks should * XXX: take this into account, and consequently they need to * XXX: live in the disk slice code. Some checks do. */ if (vn_isdisk(vp, NULL) && ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) { /* * Never allow opens for write if the disk is mounted R/W. */ if (vp->v_rdev->si_mountpoint != NULL && !(vp->v_rdev->si_mountpoint->mnt_flag & MNT_RDONLY)) return (EBUSY); /* * When running in secure mode, do not allow opens * for writing if the disk is mounted. */ error = securelevel_ge(td->td_ucred, 1); if (error && vfs_mountedon(vp)) return (error); /* * When running in very secure mode, do not allow * opens for writing of any disks. */ error = securelevel_ge(td->td_ucred, 2); if (error) return (error); } /* XXX: Special casing of ttys for deadfs. Probably redundant. */ if (dsw->d_flags & D_TTY) vp->v_vflag |= VV_ISTTY; VOP_UNLOCK(vp, 0, td); if(dsw->d_flags & D_NOGIANT) { DROP_GIANT(); if (dsw->d_fdopen != NULL) error = dsw->d_fdopen(dev, ap->a_mode, td, ap->a_fdidx); else error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td); PICKUP_GIANT(); } else if (dsw->d_fdopen != NULL) error = dsw->d_fdopen(dev, ap->a_mode, td, ap->a_fdidx); else error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (error) return (error); if (dsw->d_flags & D_TTY) { if (dev->si_tty) { struct tty *tp; tp = dev->si_tty; if (!tp->t_stop) { printf("Warning:%s: no t_stop, using nottystop\n", devtoname(dev)); tp->t_stop = nottystop; } } } if (vn_isdisk(vp, NULL)) { if (!dev->si_bsize_phys) dev->si_bsize_phys = DEV_BSIZE; } if ((dsw->d_flags & D_DISK) == 0) { cp = devtoname(dev); if (*cp == '#' && (dsw->d_flags & D_NAGGED) == 0) { printf("WARNING: driver %s should register devices with make_dev() (dev_t = \"%s\")\n", dsw->d_name, cp); dsw->d_flags |= D_NAGGED; } } return (error); } /* * Vnode op for read */ /* ARGSUSED */ static int spec_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; struct thread *td; struct uio *uio; dev_t dev; int error, resid; struct cdevsw *dsw; vp = ap->a_vp; dev = vp->v_rdev; uio = ap->a_uio; td = uio->uio_td; resid = uio->uio_resid; if (resid == 0) return (0); dsw = devsw(dev); VOP_UNLOCK(vp, 0, td); if (dsw->d_flags & D_NOGIANT) { DROP_GIANT(); error = dsw->d_read(dev, uio, ap->a_ioflag); PICKUP_GIANT(); } else error = dsw->d_read(dev, uio, ap->a_ioflag); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (uio->uio_resid != resid || (error == 0 && resid != 0)) vfs_timestamp(&dev->si_atime); return (error); } /* * Vnode op for write */ /* ARGSUSED */ static int spec_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; struct thread *td; struct uio *uio; dev_t dev; int error, resid; struct cdevsw *dsw; vp = ap->a_vp; dev = vp->v_rdev; dsw = devsw(dev); uio = ap->a_uio; td = uio->uio_td; resid = uio->uio_resid; VOP_UNLOCK(vp, 0, td); if (dsw->d_flags & D_NOGIANT) { DROP_GIANT(); error = dsw->d_write(dev, uio, ap->a_ioflag); PICKUP_GIANT(); } else error = dsw->d_write(dev, uio, ap->a_ioflag); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (uio->uio_resid != resid || (error == 0 && resid != 0)) { vfs_timestamp(&dev->si_ctime); dev->si_mtime = dev->si_ctime; } return (error); } /* * Device ioctl operation. */ /* ARGSUSED */ static int spec_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; u_long a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct thread *a_td; } */ *ap; { dev_t dev; int error; struct cdevsw *dsw; dev = ap->a_vp->v_rdev; dsw = devsw(dev); if (dsw->d_flags & D_NOGIANT) { DROP_GIANT(); error = dsw->d_ioctl(dev, ap->a_command, ap->a_data, ap->a_fflag, ap->a_td); PICKUP_GIANT(); } else error = dsw->d_ioctl(dev, ap->a_command, ap->a_data, ap->a_fflag, ap->a_td); if (error == ENOIOCTL) error = ENOTTY; return (error); } /* ARGSUSED */ static int spec_poll(ap) struct vop_poll_args /* { struct vnode *a_vp; int a_events; struct ucred *a_cred; struct thread *a_td; } */ *ap; { dev_t dev; struct cdevsw *dsw; int error; dev = ap->a_vp->v_rdev; dsw = devsw(dev); if (dsw->d_flags & D_NOGIANT) { DROP_GIANT(); error = dsw->d_poll(dev, ap->a_events, ap->a_td); PICKUP_GIANT(); } else error = dsw->d_poll(dev, ap->a_events, ap->a_td); return(error); } /* ARGSUSED */ static int spec_kqfilter(ap) struct vop_kqfilter_args /* { struct vnode *a_vp; struct knote *a_kn; } */ *ap; { dev_t dev; struct cdevsw *dsw; int error; dev = ap->a_vp->v_rdev; dsw = devsw(dev); if (dsw->d_flags & D_NOGIANT) { DROP_GIANT(); error = dsw->d_kqfilter(dev, ap->a_kn); PICKUP_GIANT(); } else error = dsw->d_kqfilter(dev, ap->a_kn); return (error); } /* * Synch buffers associated with a block device */ /* ARGSUSED */ static int spec_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct thread *a_td; } */ *ap; { if (!vn_isdisk(ap->a_vp, NULL)) return (0); return (vop_stdfsync(ap)); } /* * Mutex to use when delaying niced I/O bound processes in spec_strategy(). */ static struct mtx strategy_mtx; static void strategy_init(void) { mtx_init(&strategy_mtx, "strategy", NULL, MTX_DEF); } SYSINIT(strategy, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, strategy_init, NULL) static int doslowdown = 0; SYSCTL_INT(_debug, OID_AUTO, doslowdown, CTLFLAG_RW, &doslowdown, 0, ""); /* * Just call the device strategy routine */ static int spec_xstrategy(struct vnode *vp, struct buf *bp) { struct mount *mp; int error; struct cdevsw *dsw; struct thread *td = curthread; KASSERT(bp->b_iocmd == BIO_READ || bp->b_iocmd == BIO_WRITE || bp->b_iocmd == BIO_DELETE, ("Wrong b_iocmd buf=%p cmd=%d", bp, bp->b_iocmd)); /* * Slow down disk requests for niced processes. */ if (doslowdown && td && td->td_ksegrp->kg_nice > 0) { mtx_lock(&strategy_mtx); msleep(&strategy_mtx, &strategy_mtx, PPAUSE | PCATCH | PDROP, "ioslow", td->td_ksegrp->kg_nice); } if (bp->b_iocmd == BIO_WRITE) { if ((bp->b_flags & B_VALIDSUSPWRT) == 0 && bp->b_vp != NULL && bp->b_vp->v_mount != NULL && (bp->b_vp->v_mount->mnt_kern_flag & MNTK_SUSPENDED) != 0) panic("spec_strategy: bad I/O"); bp->b_flags &= ~B_VALIDSUSPWRT; if (LIST_FIRST(&bp->b_dep) != NULL) buf_start(bp); mp_fixme("This should require the vnode lock."); if ((vp->v_vflag & VV_COPYONWRITE) && vp->v_rdev->si_copyonwrite && (error = (*vp->v_rdev->si_copyonwrite)(vp, bp)) != 0 && error != EOPNOTSUPP) { bp->b_io.bio_error = error; bp->b_io.bio_flags |= BIO_ERROR; biodone(&bp->b_io); return (0); } } /* * Collect statistics on synchronous and asynchronous read * and write counts for disks that have associated filesystems. */ if (vn_isdisk(vp, NULL) && (mp = vp->v_rdev->si_mountpoint) != NULL) { if (bp->b_iocmd == BIO_WRITE) { if (bp->b_lock.lk_lockholder == LK_KERNPROC) mp->mnt_stat.f_asyncwrites++; else mp->mnt_stat.f_syncwrites++; } else { if (bp->b_lock.lk_lockholder == LK_KERNPROC) mp->mnt_stat.f_asyncreads++; else mp->mnt_stat.f_syncreads++; } } if (devsw(bp->b_dev) == NULL) { bp->b_io.bio_error = ENXIO; bp->b_io.bio_flags |= BIO_ERROR; biodone(&bp->b_io); return (0); } dsw = devsw(bp->b_dev); KASSERT(dsw->d_strategy != NULL, ("No strategy on dev %s responsible for buffer %p\n", devtoname(bp->b_dev), bp)); if (dsw->d_flags & D_NOGIANT) { /* XXX: notyet DROP_GIANT(); */ DEV_STRATEGY(bp); /* XXX: notyet PICKUP_GIANT(); */ } else DEV_STRATEGY(bp); return (0); } static int spec_specstrategy(ap) struct vop_specstrategy_args /* { struct vnode *a_vp; struct buf *a_bp; } */ *ap; { KASSERT(ap->a_vp->v_rdev == ap->a_bp->b_dev, ("%s, dev %s != %s", __func__, devtoname(ap->a_vp->v_rdev), devtoname(ap->a_bp->b_dev))); return spec_xstrategy(ap->a_vp, ap->a_bp); } static int spec_freeblks(ap) struct vop_freeblks_args /* { struct vnode *a_vp; daddr_t a_addr; daddr_t a_length; } */ *ap; { struct buf *bp; /* * XXX: This assumes that strategy does the deed right away. * XXX: this may not be TRTTD. */ if ((ap->a_vp->v_rdev->si_flags & SI_CANDELETE) == 0) return (0); bp = geteblk(ap->a_length); bp->b_iocmd = BIO_DELETE; bp->b_dev = ap->a_vp->v_rdev; bp->b_blkno = ap->a_addr; bp->b_offset = dbtob(ap->a_addr); bp->b_bcount = ap->a_length; BUF_KERNPROC(bp); DEV_STRATEGY(bp); return (0); } /* * Device close routine */ /* ARGSUSED */ static int spec_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct vnode *vp = ap->a_vp, *oldvp; struct thread *td = ap->a_td; dev_t dev = vp->v_rdev; struct cdevsw *dsw; int error; /* * Hack: a tty device that is a controlling terminal * has a reference from the session structure. * We cannot easily tell that a character device is * a controlling terminal, unless it is the closing * process' controlling terminal. In that case, * if the reference count is 2 (this last descriptor * plus the session), release the reference from the session. */ /* * This needs to be rewritten to take the vp interlock into * consideration. */ dsw = devsw(dev); oldvp = NULL; sx_xlock(&proctree_lock); if (td && vp == td->td_proc->p_session->s_ttyvp) { SESS_LOCK(td->td_proc->p_session); VI_LOCK(vp); if (vcount(vp) == 2 && (vp->v_iflag & VI_XLOCK) == 0) { td->td_proc->p_session->s_ttyvp = NULL; oldvp = vp; } VI_UNLOCK(vp); SESS_UNLOCK(td->td_proc->p_session); } sx_xunlock(&proctree_lock); if (oldvp != NULL) vrele(oldvp); /* * We do not want to really close the device if it * is still in use unless we are trying to close it * forcibly. Since every use (buffer, vnode, swap, cmap) * holds a reference to the vnode, and because we mark * any other vnodes that alias this device, when the * sum of the reference counts on all the aliased * vnodes descends to one, we are on last close. */ VI_LOCK(vp); if (vp->v_iflag & VI_XLOCK) { /* Forced close. */ } else if (dsw->d_flags & D_TRACKCLOSE) { /* Keep device updated on status. */ } else if (vcount(vp) > 1) { VI_UNLOCK(vp); return (0); } VI_UNLOCK(vp); if (dsw->d_flags & D_NOGIANT) { DROP_GIANT(); error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td); PICKUP_GIANT(); } else error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td); return (error); } /* * Print out the contents of a special device vnode. */ static int spec_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("\tdev %s\n", devtoname(ap->a_vp->v_rdev)); return (0); } /* * Special device advisory byte-level locks. */ /* ARGSUSED */ static int spec_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; { return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL); } static int spec_getpages(ap) struct vop_getpages_args *ap; { vm_offset_t kva; int error; int i, pcount, size, s; daddr_t blkno; struct buf *bp; vm_page_t m; vm_ooffset_t offset; int toff, nextoff, nread; struct vnode *vp = ap->a_vp; int blksiz; int gotreqpage; GIANT_REQUIRED; error = 0; pcount = round_page(ap->a_count) / PAGE_SIZE; /* * Calculate the offset of the transfer and do a sanity check. * FreeBSD currently only supports an 8 TB range due to b_blkno * being in DEV_BSIZE ( usually 512 ) byte chunks on call to * VOP_STRATEGY. XXX */ offset = IDX_TO_OFF(ap->a_m[0]->pindex) + ap->a_offset; blkno = btodb(offset); /* * Round up physical size for real devices. We cannot round using * v_mount's block size data because v_mount has nothing to do with * the device. i.e. it's usually '/dev'. We need the physical block * size for the device itself. * * We can't use v_rdev->si_mountpoint because it only exists when the * block device is mounted. However, we can use v_rdev. */ if (vn_isdisk(vp, NULL)) blksiz = vp->v_rdev->si_bsize_phys; else blksiz = DEV_BSIZE; size = (ap->a_count + blksiz - 1) & ~(blksiz - 1); bp = getpbuf(NULL); kva = (vm_offset_t)bp->b_data; /* * Map the pages to be read into the kva. */ pmap_qenter(kva, ap->a_m, pcount); /* Build a minimal buffer header. */ bp->b_iocmd = BIO_READ; bp->b_iodone = bdone; - /* B_PHYS is not set, but it is nice to fill this in. */ KASSERT(bp->b_rcred == NOCRED, ("leaking read ucred")); KASSERT(bp->b_wcred == NOCRED, ("leaking write ucred")); bp->b_rcred = crhold(curthread->td_ucred); bp->b_wcred = crhold(curthread->td_ucred); bp->b_blkno = blkno; bp->b_lblkno = blkno; pbgetvp(ap->a_vp, bp); bp->b_bcount = size; bp->b_bufsize = size; bp->b_resid = 0; bp->b_runningbufspace = bp->b_bufsize; runningbufspace += bp->b_runningbufspace; cnt.v_vnodein++; cnt.v_vnodepgsin += pcount; /* Do the input. */ spec_xstrategy(bp->b_vp, bp); s = splbio(); bwait(bp, PVM, "spread"); splx(s); if ((bp->b_ioflags & BIO_ERROR) != 0) { if (bp->b_error) error = bp->b_error; else error = EIO; } nread = size - bp->b_resid; if (nread < ap->a_count) { bzero((caddr_t)kva + nread, ap->a_count - nread); } pmap_qremove(kva, pcount); gotreqpage = 0; /* * While the page is busy, its object field is immutable. */ VM_OBJECT_LOCK(ap->a_m[ap->a_reqpage]->object); vm_page_lock_queues(); for (i = 0, toff = 0; i < pcount; i++, toff = nextoff) { nextoff = toff + PAGE_SIZE; m = ap->a_m[i]; m->flags &= ~PG_ZERO; if (nextoff <= nread) { m->valid = VM_PAGE_BITS_ALL; vm_page_undirty(m); } else if (toff < nread) { /* * Since this is a VM request, we have to supply the * unaligned offset to allow vm_page_set_validclean() * to zero sub-DEV_BSIZE'd portions of the page. */ vm_page_set_validclean(m, 0, nread - toff); } else { m->valid = 0; vm_page_undirty(m); } if (i != ap->a_reqpage) { /* * Just in case someone was asking for this page we * now tell them that it is ok to use. */ if (!error || (m->valid == VM_PAGE_BITS_ALL)) { if (m->valid) { if (m->flags & PG_WANTED) { vm_page_activate(m); } else { vm_page_deactivate(m); } vm_page_wakeup(m); } else { vm_page_free(m); } } else { vm_page_free(m); } } else if (m->valid) { gotreqpage = 1; /* * Since this is a VM request, we need to make the * entire page presentable by zeroing invalid sections. */ if (m->valid != VM_PAGE_BITS_ALL) vm_page_zero_invalid(m, FALSE); } } vm_page_unlock_queues(); if (!gotreqpage) { m = ap->a_m[ap->a_reqpage]; printf( "spec_getpages:(%s) I/O read failure: (error=%d) bp %p vp %p\n", devtoname(bp->b_dev), error, bp, bp->b_vp); printf( " size: %d, resid: %ld, a_count: %d, valid: 0x%lx\n", size, bp->b_resid, ap->a_count, (u_long)m->valid); printf( " nread: %d, reqpage: %d, pindex: %lu, pcount: %d\n", nread, ap->a_reqpage, (u_long)m->pindex, pcount); VM_OBJECT_UNLOCK(m->object); /* * Free the buffer header back to the swap buffer pool. */ relpbuf(bp, NULL); return VM_PAGER_ERROR; } VM_OBJECT_UNLOCK(ap->a_m[ap->a_reqpage]->object); /* * Free the buffer header back to the swap buffer pool. */ relpbuf(bp, NULL); return VM_PAGER_OK; } diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c index 59245594273d..1a2ae4396198 100644 --- a/sys/fs/udf/udf_vnops.c +++ b/sys/fs/udf/udf_vnops.c @@ -1,1204 +1,1205 @@ /*- * Copyright (c) 2001, 2002 Scott Long * 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. * * 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$ */ /* udf_vnops.c */ /* Take care of the vnode side of things */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int udf_access(struct vop_access_args *); static int udf_getattr(struct vop_getattr_args *); static int udf_ioctl(struct vop_ioctl_args *); static int udf_pathconf(struct vop_pathconf_args *); static int udf_read(struct vop_read_args *); static int udf_readdir(struct vop_readdir_args *); static int udf_readlink(struct vop_readlink_args *ap); static int udf_strategy(struct vop_strategy_args *); static int udf_bmap(struct vop_bmap_args *); static int udf_lookup(struct vop_cachedlookup_args *); static int udf_reclaim(struct vop_reclaim_args *); static void udf_dumpblock(void *, int) __unused; static int udf_readatoffset(struct udf_node *, int *, int, struct buf **, uint8_t **); static int udf_bmap_internal(struct udf_node *, uint32_t, daddr_t *, uint32_t *); vop_t **udf_vnodeop_p; static struct vnodeopv_entry_desc udf_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) vop_defaultop }, { &vop_access_desc, (vop_t *) udf_access }, { &vop_bmap_desc, (vop_t *) udf_bmap }, { &vop_cachedlookup_desc, (vop_t *) udf_lookup }, { &vop_getattr_desc, (vop_t *) udf_getattr }, { &vop_ioctl_desc, (vop_t *) udf_ioctl }, { &vop_lookup_desc, (vop_t *) vfs_cache_lookup }, { &vop_pathconf_desc, (vop_t *) udf_pathconf }, { &vop_read_desc, (vop_t *) udf_read }, { &vop_readdir_desc, (vop_t *) udf_readdir }, { &vop_readlink_desc, (vop_t *) udf_readlink }, { &vop_reclaim_desc, (vop_t *) udf_reclaim }, { &vop_strategy_desc, (vop_t *) udf_strategy }, { NULL, NULL } }; static struct vnodeopv_desc udf_vnodeop_opv_desc = { &udf_vnodeop_p, udf_vnodeop_entries }; VNODEOP_SET(udf_vnodeop_opv_desc); MALLOC_DEFINE(M_UDFFID, "UDF FID", "UDF FileId structure"); MALLOC_DEFINE(M_UDFDS, "UDF DS", "UDF Dirstream structure"); #define UDF_INVALID_BMAP -1 /* Look up a udf_node based on the ino_t passed in and return it's vnode */ int udf_hashlookup(struct udf_mnt *udfmp, ino_t id, int flags, struct vnode **vpp) { struct udf_node *node; struct udf_hash_lh *lh; int error; *vpp = NULL; loop: mtx_lock(&udfmp->hash_mtx); lh = &udfmp->hashtbl[id % udfmp->hashsz]; if (lh == NULL) return (ENOENT); LIST_FOREACH(node, lh, le) { if (node->hash_id == id) { VI_LOCK(node->i_vnode); mtx_unlock(&udfmp->hash_mtx); error = vget(node->i_vnode, flags | LK_INTERLOCK, curthread); if (error == ENOENT) goto loop; if (error) return (error); *vpp = node->i_vnode; return (0); } } mtx_unlock(&udfmp->hash_mtx); return (0); } int udf_hashins(struct udf_node *node) { struct udf_mnt *udfmp; struct udf_hash_lh *lh; udfmp = node->udfmp; vn_lock(node->i_vnode, LK_EXCLUSIVE | LK_RETRY, curthread); mtx_lock(&udfmp->hash_mtx); lh = &udfmp->hashtbl[node->hash_id % udfmp->hashsz]; if (lh == NULL) LIST_INIT(lh); LIST_INSERT_HEAD(lh, node, le); mtx_unlock(&udfmp->hash_mtx); return (0); } int udf_hashrem(struct udf_node *node) { struct udf_mnt *udfmp; struct udf_hash_lh *lh; udfmp = node->udfmp; mtx_lock(&udfmp->hash_mtx); lh = &udfmp->hashtbl[node->hash_id % udfmp->hashsz]; if (lh == NULL) panic("hash entry is NULL, node->hash_id= %d\n", node->hash_id); LIST_REMOVE(node, le); mtx_unlock(&udfmp->hash_mtx); return (0); } int udf_allocv(struct mount *mp, struct vnode **vpp, struct thread *td) { int error; struct vnode *vp; error = getnewvnode("udf", mp, udf_vnodeop_p, &vp); if (error) { printf("udf_allocv: failed to allocate new vnode\n"); return (error); } *vpp = vp; return (0); } /* Convert file entry permission (5 bits per owner/group/user) to a mode_t */ static mode_t udf_permtomode(struct udf_node *node) { uint32_t perm; uint32_t flags; mode_t mode; perm = node->fentry->perm; flags = node->fentry->icbtag.flags; mode = perm & UDF_FENTRY_PERM_USER_MASK; mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK) >> 2); mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4); mode |= ((flags & UDF_ICB_TAG_FLAGS_STICKY) << 4); mode |= ((flags & UDF_ICB_TAG_FLAGS_SETGID) << 6); mode |= ((flags & UDF_ICB_TAG_FLAGS_SETUID) << 8); return (mode); } static int udf_access(struct vop_access_args *a) { struct vnode *vp; struct udf_node *node; mode_t a_mode, mode; vp = a->a_vp; node = VTON(vp); a_mode = a->a_mode; if (a_mode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: return (EROFS); /* NOT REACHED */ default: break; } } mode = udf_permtomode(node); return (vaccess(vp->v_type, mode, node->fentry->uid, node->fentry->gid, a_mode, a->a_cred, NULL)); } static int mon_lens[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; static int udf_isaleapyear(int year) { int i; i = (year % 4) ? 0 : 1; i &= (year % 100) ? 1 : 0; i |= (year % 400) ? 0 : 1; return i; } /* * XXX This is just a rough hack. Daylight savings isn't calculated and tv_nsec * is ignored. * Timezone calculation compliments of Julian Elischer . */ static void udf_timetotimespec(struct timestamp *time, struct timespec *t) { int i, lpyear, daysinyear; union { uint16_t u_tz_offset; int16_t s_tz_offset; } tz; t->tv_nsec = 0; /* DirectCD seems to like using bogus year values */ if (time->year < 1970) { t->tv_sec = 0; return; } /* Calculate the time and day */ t->tv_sec = time->second; t->tv_sec += time->minute * 60; t->tv_sec += time->hour * 3600; t->tv_sec += time->day * 3600 * 24; /* Calclulate the month */ lpyear = udf_isaleapyear(time->year); for (i = 1; i < time->month; i++) t->tv_sec += mon_lens[lpyear][i] * 3600 * 24; /* Speed up the calculation */ if (time->year > 1979) t->tv_sec += 315532800; if (time->year > 1989) t->tv_sec += 315619200; if (time->year > 1999) t->tv_sec += 315532800; for (i = 2000; i < time->year; i++) { daysinyear = udf_isaleapyear(i) + 365 ; t->tv_sec += daysinyear * 3600 * 24; } /* * Calculate the time zone. The timezone is 12 bit signed 2's * compliment, so we gotta do some extra magic to handle it right. */ tz.u_tz_offset = time->type_tz; tz.u_tz_offset &= 0x0fff; if (tz.u_tz_offset & 0x0800) tz.u_tz_offset |= 0xf000; /* extend the sign to 16 bits */ if ((time->type_tz & 0x1000) && (tz.s_tz_offset != -2047)) t->tv_sec -= tz.s_tz_offset * 60; return; } static int udf_getattr(struct vop_getattr_args *a) { struct vnode *vp; struct udf_node *node; struct vattr *vap; struct file_entry *fentry; struct timespec ts; ts.tv_sec = 0; vp = a->a_vp; vap = a->a_vap; node = VTON(vp); fentry = node->fentry; vap->va_fsid = dev2udev(node->i_dev); vap->va_fileid = node->hash_id; vap->va_mode = udf_permtomode(node); vap->va_nlink = fentry->link_cnt; /* * XXX The spec says that -1 is valid for uid/gid and indicates an * invalid uid/gid. How should this be represented? */ vap->va_uid = (fentry->uid == -1) ? 0 : fentry->uid; vap->va_gid = (fentry->gid == -1) ? 0 : fentry->gid; udf_timetotimespec(&fentry->atime, &vap->va_atime); udf_timetotimespec(&fentry->mtime, &vap->va_mtime); vap->va_ctime = vap->va_mtime; /* XXX Stored as an Extended Attribute */ vap->va_rdev = 0; /* XXX */ if (vp->v_type & VDIR) { /* * Directories that are recorded within their ICB will show * as having 0 blocks recorded. Since tradition dictates * that directories consume at least one logical block, * make it appear so. */ if (fentry->logblks_rec != 0) { vap->va_size = fentry->logblks_rec * node->udfmp->bsize; } else { vap->va_size = node->udfmp->bsize; } } else { vap->va_size = fentry->inf_len; } vap->va_flags = 0; vap->va_gen = 1; vap->va_blocksize = node->udfmp->bsize; vap->va_bytes = fentry->inf_len; vap->va_type = vp->v_type; vap->va_filerev = 0; /* XXX */ return (0); } /* * File specific ioctls. DeCSS candidate? */ static int udf_ioctl(struct vop_ioctl_args *a) { printf("%s called\n", __FUNCTION__); return (ENOTTY); } /* * I'm not sure that this has much value in a read-only filesystem, but * cd9660 has it too. */ static int udf_pathconf(struct vop_pathconf_args *a) { switch (a->a_name) { case _PC_LINK_MAX: *a->a_retval = 65535; return (0); case _PC_NAME_MAX: *a->a_retval = NAME_MAX; return (0); case _PC_PATH_MAX: *a->a_retval = PATH_MAX; return (0); case _PC_NO_TRUNC: *a->a_retval = 1; return (0); default: return (EINVAL); } } static int udf_read(struct vop_read_args *a) { struct vnode *vp = a->a_vp; struct uio *uio = a->a_uio; struct udf_node *node = VTON(vp); struct buf *bp; uint8_t *data; int error = 0; int size, fsize, offset; if (uio->uio_offset < 0) return (EINVAL); fsize = node->fentry->inf_len; while (uio->uio_offset < fsize && uio->uio_resid > 0) { offset = uio->uio_offset; size = uio->uio_resid; error = udf_readatoffset(node, &size, offset, &bp, &data); if (error) return (error); error = uiomove(data, size, uio); if (bp != NULL) brelse(bp); if (error) break; }; return (error); } /* Convienience routine to dump a block in hex */ static void udf_dumpblock(void *data, int len) { int i, j; for (i = 0; i < len; i++) { printf("\noffset= %d: ", i); for (j = 0; j < 8; j++) { if (i + j == len) break; printf("0x%02x ", (uint8_t)((uint8_t*)(data))[i + j]); } i += j - 1; } printf("\n"); } /* * Call the OSTA routines to translate the name from a CS0 dstring to a * 16-bit Unicode String. Hooks need to be placed in here to translate from * Unicode to the encoding that the kernel/user expects. For now, compact * the encoding to 8 bits if possible. Return the length of the translated * string. * XXX This horribly pessimizes the 8bit case */ static int udf_transname(char *cs0string, char *destname, int len) { unicode_t *transname; int i, unilen = 0; /* allocate a buffer big enough to hold an 8->16 bit expansion */ transname = uma_zalloc(udf_zone_trans, M_WAITOK); if ((unilen = udf_UncompressUnicode(len, cs0string, transname)) == -1) { printf("udf: Unicode translation failed\n"); uma_zfree(udf_zone_trans, transname); return 0; } /* At this point, the name is in 16-bit Unicode. Compact it down * to 8-bit */ for (i = 0; i < unilen ; i++) { if (transname[i] & 0xff00) { destname[i] = '.'; /* Fudge the 16bit chars */ } else { destname[i] = transname[i] & 0xff; } } destname[unilen] = 0; uma_zfree(udf_zone_trans, transname); return unilen; } /* * Compare a CS0 dstring with a name passed in from the VFS layer. Return * 0 on a successful match, nonzero therwise. Unicode work may need to be done * here also. */ static int udf_cmpname(char *cs0string, char *cmpname, int cs0len, int cmplen) { char *transname; int error = 0; /* This is overkill, but not worth creating a new zone */ transname = uma_zalloc(udf_zone_trans, M_WAITOK); cs0len = udf_transname(cs0string, transname, cs0len); /* Easy check. If they aren't the same length, they aren't equal */ if ((cs0len == 0) || (cs0len != cmplen)) error = -1; else error = bcmp(transname, cmpname, cmplen); uma_zfree(udf_zone_trans, transname); return (error); } struct udf_uiodir { struct dirent *dirent; u_long *cookies; int ncookies; int acookies; int eofflag; }; static int udf_uiodir(struct udf_uiodir *uiodir, int de_size, struct uio *uio, long cookie) { if (uiodir->cookies != NULL) { if (++uiodir->acookies > uiodir->ncookies) { uiodir->eofflag = 0; return (-1); } *uiodir->cookies++ = cookie; } if (uio->uio_resid < de_size) { uiodir->eofflag = 0; return (-1); } return (uiomove(uiodir->dirent, de_size, uio)); } static struct udf_dirstream * udf_opendir(struct udf_node *node, int offset, int fsize, struct udf_mnt *udfmp) { struct udf_dirstream *ds; ds = uma_zalloc(udf_zone_ds, M_WAITOK | M_ZERO); ds->node = node; ds->offset = offset; ds->udfmp = udfmp; ds->fsize = fsize; return (ds); } static struct fileid_desc * udf_getfid(struct udf_dirstream *ds) { struct fileid_desc *fid; int error, frag_size = 0, total_fid_size; /* End of directory? */ if (ds->offset + ds->off >= ds->fsize) { ds->error = 0; return (NULL); } /* Grab the first extent of the directory */ if (ds->off == 0) { ds->size = 0; error = udf_readatoffset(ds->node, &ds->size, ds->offset, &ds->bp, &ds->data); if (error) { ds->error = error; return (NULL); } } /* * Clean up from a previous fragmented FID. * XXX Is this the right place for this? */ if (ds->fid_fragment && ds->buf != NULL) { ds->fid_fragment = 0; FREE(ds->buf, M_UDFFID); } fid = (struct fileid_desc*)&ds->data[ds->off]; /* * Check to see if the fid is fragmented. The first test * ensures that we don't wander off the end of the buffer * looking for the l_iu and l_fi fields. */ if (ds->off + UDF_FID_SIZE > ds->size || ds->off + fid->l_iu + fid->l_fi + UDF_FID_SIZE > ds->size) { /* Copy what we have of the fid into a buffer */ frag_size = ds->size - ds->off; if (frag_size >= ds->udfmp->bsize) { printf("udf: invalid FID fragment\n"); ds->error = EINVAL; return (NULL); } /* * File ID descriptors can only be at most one * logical sector in size. */ MALLOC(ds->buf, uint8_t*, ds->udfmp->bsize, M_UDFFID, M_WAITOK | M_ZERO); bcopy(fid, ds->buf, frag_size); /* Reduce all of the casting magic */ fid = (struct fileid_desc*)ds->buf; if (ds->bp != NULL) brelse(ds->bp); /* Fetch the next allocation */ ds->offset += ds->size; ds->size = 0; error = udf_readatoffset(ds->node, &ds->size, ds->offset, &ds->bp, &ds->data); if (error) { ds->error = error; return (NULL); } /* * If the fragment was so small that we didn't get * the l_iu and l_fi fields, copy those in. */ if (frag_size < UDF_FID_SIZE) bcopy(ds->data, &ds->buf[frag_size], UDF_FID_SIZE - frag_size); /* * Now that we have enough of the fid to work with, * copy in the rest of the fid from the new * allocation. */ total_fid_size = UDF_FID_SIZE + fid->l_iu + fid->l_fi; if (total_fid_size > ds->udfmp->bsize) { printf("udf: invalid FID\n"); ds->error = EIO; return (NULL); } bcopy(ds->data, &ds->buf[frag_size], total_fid_size - frag_size); ds->fid_fragment = 1; } else { total_fid_size = fid->l_iu + fid->l_fi + UDF_FID_SIZE; } /* * Update the offset. Align on a 4 byte boundary because the * UDF spec says so. */ ds->this_off = ds->off; if (!ds->fid_fragment) { ds->off += (total_fid_size + 3) & ~0x03; } else { ds->off = (total_fid_size - frag_size + 3) & ~0x03; } return (fid); } static void udf_closedir(struct udf_dirstream *ds) { if (ds->bp != NULL) brelse(ds->bp); if (ds->fid_fragment && ds->buf != NULL) FREE(ds->buf, M_UDFFID); uma_zfree(udf_zone_ds, ds); } static int udf_readdir(struct vop_readdir_args *a) { struct vnode *vp; struct uio *uio; struct dirent dir; struct udf_node *node; struct fileid_desc *fid; struct udf_uiodir uiodir; struct udf_dirstream *ds; u_long *cookies = NULL; int ncookies; int error = 0; vp = a->a_vp; uio = a->a_uio; node = VTON(vp); uiodir.eofflag = 1; if (a->a_ncookies != NULL) { /* * Guess how many entries are needed. If we run out, this * function will be called again and thing will pick up were * it left off. */ ncookies = uio->uio_resid / 8; MALLOC(cookies, u_long *, sizeof(u_long) * ncookies, M_TEMP, M_WAITOK); if (cookies == NULL) return (ENOMEM); uiodir.ncookies = ncookies; uiodir.cookies = cookies; uiodir.acookies = 0; } else { uiodir.cookies = NULL; } /* * Iterate through the file id descriptors. Give the parent dir * entry special attention. */ ds = udf_opendir(node, uio->uio_offset, node->fentry->inf_len, node->udfmp); while ((fid = udf_getfid(ds)) != NULL) { /* XXX Should we return an error on a bad fid? */ if (udf_checktag(&fid->tag, TAGID_FID)) { printf("Invalid FID tag\n"); udf_dumpblock(fid, UDF_FID_SIZE); error = EIO; break; } /* Is this a deleted file? */ if (fid->file_char & UDF_FILE_CHAR_DEL) continue; if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) { /* Do up the '.' and '..' entries. Dummy values are * used for the cookies since the offset here is * usually zero, and NFS doesn't like that value */ dir.d_fileno = node->hash_id; dir.d_type = DT_DIR; dir.d_name[0] = '.'; dir.d_namlen = 1; dir.d_reclen = GENERIC_DIRSIZ(&dir); uiodir.dirent = &dir; error = udf_uiodir(&uiodir, dir.d_reclen, uio, 1); if (error) break; dir.d_fileno = udf_getid(&fid->icb); dir.d_type = DT_DIR; dir.d_name[0] = '.'; dir.d_name[1] = '.'; dir.d_namlen = 2; dir.d_reclen = GENERIC_DIRSIZ(&dir); uiodir.dirent = &dir; error = udf_uiodir(&uiodir, dir.d_reclen, uio, 2); } else { dir.d_namlen = udf_transname(&fid->data[fid->l_iu], &dir.d_name[0], fid->l_fi); dir.d_fileno = udf_getid(&fid->icb); dir.d_type = (fid->file_char & UDF_FILE_CHAR_DIR) ? DT_DIR : DT_UNKNOWN; dir.d_reclen = GENERIC_DIRSIZ(&dir); uiodir.dirent = &dir; error = udf_uiodir(&uiodir, dir.d_reclen, uio, ds->this_off); } if (error) { printf("uiomove returned %d\n", error); break; } } /* tell the calling layer whether we need to be called again */ *a->a_eofflag = uiodir.eofflag; uio->uio_offset = ds->offset + ds->off; if (!error) error = ds->error; udf_closedir(ds); if (a->a_ncookies != NULL) { if (error) FREE(cookies, M_TEMP); else { *a->a_ncookies = uiodir.acookies; *a->a_cookies = cookies; } } return (error); } /* Are there any implementations out there that do soft-links? */ static int udf_readlink(struct vop_readlink_args *ap) { printf("%s called\n", __FUNCTION__); return (EOPNOTSUPP); } static int udf_strategy(struct vop_strategy_args *a) { struct buf *bp; struct vnode *vp; struct udf_node *node; int maxsize; bp = a->a_bp; vp = bp->b_vp; node = VTON(vp); KASSERT(a->a_vp == a->a_bp->b_vp, ("%s(%p != %p)", __func__, a->a_vp, a->a_bp->b_vp)); /* cd9660 has this test reversed, but it seems more logical this way */ if (bp->b_blkno != bp->b_lblkno) { /* * Files that are embedded in the fentry don't translate well * to a block number. Reject. */ if (udf_bmap_internal(node, bp->b_lblkno * node->udfmp->bsize, &bp->b_lblkno, &maxsize)) { clrbuf(bp); bp->b_blkno = -1; } } if ((long)bp->b_blkno == -1) { bufdone(bp); return (0); } vp = node->i_devvp; bp->b_dev = vp->v_rdev; + bp->b_offset = dbtob(bp->b_blkno); VOP_SPECSTRATEGY(vp, bp); return (0); } static int udf_bmap(struct vop_bmap_args *a) { struct udf_node *node; uint32_t max_size; daddr_t lsector; int error; node = VTON(a->a_vp); if (a->a_vpp != NULL) *a->a_vpp = node->i_devvp; if (a->a_bnp == NULL) return (0); if (a->a_runb) *a->a_runb = 0; error = udf_bmap_internal(node, a->a_bn * node->udfmp->bsize, &lsector, &max_size); if (error) return (error); /* Translate logical to physical sector number */ *a->a_bnp = lsector << (node->udfmp->bshift - DEV_BSHIFT); /* Punt on read-ahead for now */ if (a->a_runp) *a->a_runp = 0; return (0); } /* * The all powerful VOP_LOOKUP(). */ static int udf_lookup(struct vop_cachedlookup_args *a) { struct vnode *dvp; struct vnode *tdp = NULL; struct vnode **vpp = a->a_vpp; struct udf_node *node; struct udf_mnt *udfmp; struct fileid_desc *fid = NULL; struct udf_dirstream *ds; struct thread *td; u_long nameiop; u_long flags; char *nameptr; long namelen; ino_t id = 0; int offset, error = 0; int numdirpasses, fsize; dvp = a->a_dvp; node = VTON(dvp); udfmp = node->udfmp; nameiop = a->a_cnp->cn_nameiop; flags = a->a_cnp->cn_flags; nameptr = a->a_cnp->cn_nameptr; namelen = a->a_cnp->cn_namelen; fsize = node->fentry->inf_len; td = a->a_cnp->cn_thread; /* * If this is a LOOKUP and we've already partially searched through * the directory, pick up where we left off and flag that the * directory may need to be searched twice. For a full description, * see /sys/isofs/cd9660/cd9660_lookup.c:cd9660_lookup() */ if (nameiop != LOOKUP || node->diroff == 0 || node->diroff > fsize) { offset = 0; numdirpasses = 1; } else { offset = node->diroff; numdirpasses = 2; nchstats.ncs_2passes++; } lookloop: ds = udf_opendir(node, offset, fsize, udfmp); while ((fid = udf_getfid(ds)) != NULL) { /* XXX Should we return an error on a bad fid? */ if (udf_checktag(&fid->tag, TAGID_FID)) { printf("udf_lookup: Invalid tag\n"); error = EIO; break; } /* Is this a deleted file? */ if (fid->file_char & UDF_FILE_CHAR_DEL) continue; if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) { if (flags & ISDOTDOT) { id = udf_getid(&fid->icb); break; } } else { if (!(udf_cmpname(&fid->data[fid->l_iu], nameptr, fid->l_fi, namelen))) { id = udf_getid(&fid->icb); break; } } } if (!error) error = ds->error; /* XXX Bail out here? */ if (error) { udf_closedir(ds); return (error); } /* Did we have a match? */ if (id) { error = udf_vget(udfmp->im_mountp, id, LK_EXCLUSIVE, &tdp); if (!error) { /* * Remember where this entry was if it's the final * component. */ if ((flags & ISLASTCN) && nameiop == LOOKUP) node->diroff = ds->offset + ds->off; if (numdirpasses == 2) nchstats.ncs_pass2++; if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) { a->a_cnp->cn_flags |= PDIRUNLOCK; VOP_UNLOCK(dvp, 0, td); } *vpp = tdp; /* Put this entry in the cache */ if (flags & MAKEENTRY) cache_enter(dvp, *vpp, a->a_cnp); } } else { /* Name wasn't found on this pass. Do another pass? */ if (numdirpasses == 2) { numdirpasses--; offset = 0; udf_closedir(ds); goto lookloop; } /* Enter name into cache as non-existant */ if (flags & MAKEENTRY) cache_enter(dvp, *vpp, a->a_cnp); if ((flags & ISLASTCN) && (nameiop == CREATE || nameiop == RENAME)) { error = EROFS; } else { error = ENOENT; } } udf_closedir(ds); return (error); } static int udf_reclaim(struct vop_reclaim_args *a) { struct vnode *vp; struct udf_node *unode; vp = a->a_vp; unode = VTON(vp); if (unode != NULL) { udf_hashrem(unode); if (unode->i_devvp) { vrele(unode->i_devvp); unode->i_devvp = 0; } if (unode->fentry != NULL) FREE(unode->fentry, M_UDFFENTRY); uma_zfree(udf_zone_node, unode); vp->v_data = NULL; } return (0); } /* * Read the block and then set the data pointer to correspond with the * offset passed in. Only read in at most 'size' bytes, and then set 'size' * to the number of bytes pointed to. If 'size' is zero, try to read in a * whole extent. * XXX 'size' is limited to the logical block size for now due to problems * with udf_read() */ static int udf_readatoffset(struct udf_node *node, int *size, int offset, struct buf **bp, uint8_t **data) { struct udf_mnt *udfmp; struct file_entry *fentry = NULL; struct buf *bp1; uint32_t max_size; daddr_t sector; int error; udfmp = node->udfmp; error = udf_bmap_internal(node, offset, §or, &max_size); if (error == UDF_INVALID_BMAP) { /* * This error means that the file *data* is stored in the * allocation descriptor field of the file entry. */ fentry = node->fentry; *data = &fentry->data[fentry->l_ea]; *size = fentry->l_ad; *bp = NULL; return (0); } else if (error != 0) { return (error); } /* Adjust the size so that it is within range */ if (*size == 0 || *size > max_size) *size = max_size; *size = min(*size, MAXBSIZE); if ((error = udf_readlblks(udfmp, sector, *size, bp))) { printf("warning: udf_readlblks returned error %d\n", error); return (error); } bp1 = *bp; *data = (uint8_t *)&bp1->b_data[offset % udfmp->bsize]; return (0); } /* * Translate a file offset into a logical block and then into a physical * block. */ static int udf_bmap_internal(struct udf_node *node, uint32_t offset, daddr_t *sector, uint32_t *max_size) { struct udf_mnt *udfmp; struct file_entry *fentry; void *icb; struct icb_tag *tag; uint32_t icblen = 0; daddr_t lsector; int ad_offset, ad_num = 0; int i, p_offset; udfmp = node->udfmp; fentry = node->fentry; tag = &fentry->icbtag; switch (tag->strat_type) { case 4: break; case 4096: printf("Cannot deal with strategy4096 yet!\n"); return (ENODEV); default: printf("Unknown strategy type %d\n", tag->strat_type); return (ENODEV); } switch (tag->flags & 0x7) { case 0: /* * The allocation descriptor field is filled with short_ad's. * If the offset is beyond the current extent, look for the * next extent. */ do { offset -= icblen; ad_offset = sizeof(struct short_ad) * ad_num; if (ad_offset > fentry->l_ad) { printf("File offset out of bounds\n"); return (EINVAL); } icb = GETICB(long_ad, fentry, fentry->l_ea + ad_offset); icblen = GETICBLEN(short_ad, icb); ad_num++; } while(offset >= icblen); lsector = (offset >> udfmp->bshift) + ((struct short_ad *)(icb))->pos; *max_size = GETICBLEN(short_ad, icb); break; case 1: /* * The allocation descriptor field is filled with long_ad's * If the offset is beyond the current extent, look for the * next extent. */ do { offset -= icblen; ad_offset = sizeof(struct long_ad) * ad_num; if (ad_offset > fentry->l_ad) { printf("File offset out of bounds\n"); return (EINVAL); } icb = GETICB(long_ad, fentry, fentry->l_ea + ad_offset); icblen = GETICBLEN(long_ad, icb); ad_num++; } while(offset >= icblen); lsector = (offset >> udfmp->bshift) + ((struct long_ad *)(icb))->loc.lb_num; *max_size = GETICBLEN(long_ad, icb); break; case 3: /* * This type means that the file *data* is stored in the * allocation descriptor field of the file entry. */ *max_size = 0; *sector = node->hash_id + udfmp->part_start; return (UDF_INVALID_BMAP); case 2: /* DirectCD does not use extended_ad's */ default: printf("Unsupported allocation descriptor %d\n", tag->flags & 0x7); return (ENODEV); } *sector = lsector + udfmp->part_start; /* * Check the sparing table. Each entry represents the beginning of * a packet. */ if (udfmp->s_table != NULL) { for (i = 0; i< udfmp->s_table_entries; i++) { p_offset = lsector - udfmp->s_table->entries[i].org; if ((p_offset < udfmp->p_sectors) && (p_offset >= 0)) { *sector = udfmp->s_table->entries[i].map + p_offset; break; } } } return (0); }