[BACK]Return to msdosfs_vnops.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / fs / msdosfs

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/sys/fs/msdosfs/msdosfs_vnops.c between version 1.30 and 1.61.4.3

version 1.30, 2006/07/23 22:06:10 version 1.61.4.3, 2010/07/03 01:19:50
Line 60  __KERNEL_RCSID(0, "$NetBSD$");
Line 60  __KERNEL_RCSID(0, "$NetBSD$");
 #include <sys/buf.h>  #include <sys/buf.h>
 #include <sys/proc.h>  #include <sys/proc.h>
 #include <sys/mount.h>  #include <sys/mount.h>
   #include <sys/fstrans.h>
 #include <sys/vnode.h>  #include <sys/vnode.h>
 #include <sys/signalvar.h>  #include <sys/signalvar.h>
 #include <sys/malloc.h>  #include <sys/malloc.h>
Line 103  __KERNEL_RCSID(0, "$NetBSD$");
Line 104  __KERNEL_RCSID(0, "$NetBSD$");
  * only if the SAVESTART bit in cn_flags is clear on success.   * only if the SAVESTART bit in cn_flags is clear on success.
  */   */
 int  int
 msdosfs_create(v)  msdosfs_create(void *v)
         void *v;  
 {  {
         struct vop_create_args /* {          struct vop_create_args /* {
                 struct vnode *a_dvp;                  struct vnode *a_dvp;
Line 122  msdosfs_create(v)
Line 122  msdosfs_create(v)
         printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);          printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
 #endif  #endif
   
           fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
         /*          /*
          * If this is the root directory and there is no space left we           * 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           * can't do anything.  This is because the root directory can not
Line 160  msdosfs_create(v)
Line 161  msdosfs_create(v)
                 goto bad;                  goto bad;
         if ((cnp->cn_flags & SAVESTART) == 0)          if ((cnp->cn_flags & SAVESTART) == 0)
                 PNBUF_PUT(cnp->cn_pnbuf);                  PNBUF_PUT(cnp->cn_pnbuf);
           fstrans_done(ap->a_dvp->v_mount);
         VN_KNOTE(ap->a_dvp, NOTE_WRITE);          VN_KNOTE(ap->a_dvp, NOTE_WRITE);
         vput(ap->a_dvp);          vput(ap->a_dvp);
         *ap->a_vpp = DETOV(dep);          *ap->a_vpp = DETOV(dep);
         return (0);          return (0);
   
 bad:  bad:
           fstrans_done(ap->a_dvp->v_mount);
         PNBUF_PUT(cnp->cn_pnbuf);          PNBUF_PUT(cnp->cn_pnbuf);
         vput(ap->a_dvp);          vput(ap->a_dvp);
         return (error);          return (error);
 }  }
   
 int  int
 msdosfs_mknod(v)  msdosfs_close(void *v)
         void *v;  
 {  
         struct vop_mknod_args /* {  
                 struct vnode *a_dvp;  
                 struct vnode **a_vpp;  
                 struct componentname *a_cnp;  
                 struct vattr *a_vap;  
         } */ *ap = v;  
   
         PNBUF_PUT(ap->a_cnp->cn_pnbuf);  
         vput(ap->a_dvp);  
         return (EINVAL);  
 }  
   
 int  
 msdosfs_open(v)  
         void *v;  
 {  
 #if 0  
         struct vop_open_args /* {  
                 struct vnode *a_vp;  
                 int a_mode;  
                 kauth_cred_t a_cred;  
                 struct lwp *a_l;  
         } */ *ap;  
 #endif  
   
         return (0);  
 }  
   
 int  
 msdosfs_close(v)  
         void *v;  
 {  {
         struct vop_close_args /* {          struct vop_close_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
                 int a_fflag;                  int a_fflag;
                 kauth_cred_t a_cred;                  kauth_cred_t a_cred;
                 struct lwp *a_l;  
         } */ *ap = v;          } */ *ap = v;
         struct vnode *vp = ap->a_vp;          struct vnode *vp = ap->a_vp;
         struct denode *dep = VTODE(vp);          struct denode *dep = VTODE(vp);
   
         simple_lock(&vp->v_interlock);          fstrans_start(vp->v_mount, FSTRANS_SHARED);
           mutex_enter(vp->v_interlock);
         if (vp->v_usecount > 1)          if (vp->v_usecount > 1)
                 DETIMES(dep, NULL, NULL, NULL, dep->de_pmp->pm_gmtoff);                  DETIMES(dep, NULL, NULL, NULL, dep->de_pmp->pm_gmtoff);
         simple_unlock(&vp->v_interlock);          mutex_exit(vp->v_interlock);
           fstrans_done(vp->v_mount);
         return (0);          return (0);
 }  }
   
 int  static int
 msdosfs_access(v)  msdosfs_check_possible(struct vnode *vp, struct denode *dep, mode_t mode)
         void *v;  
 {  {
         struct vop_access_args /* {  
                 struct vnode *a_vp;  
                 int a_mode;  
                 kauth_cred_t a_cred;  
                 struct lwp *a_l;  
         } */ *ap = v;  
         struct vnode *vp = ap->a_vp;  
         struct denode *dep = VTODE(vp);  
         struct msdosfsmount *pmp = dep->de_pmp;  
         mode_t mode = ap->a_mode;  
   
         /*          /*
          * Disallow write attempts on read-only file systems;           * Disallow write attempts on read-only file systems;
Line 255  msdosfs_access(v)
Line 215  msdosfs_access(v)
                 }                  }
         }          }
   
           return 0;
   }
   
   static int
   msdosfs_check_permitted(struct vnode *vp, struct denode *dep, mode_t mode,
       kauth_cred_t cred)
   {
           struct msdosfsmount *pmp = dep->de_pmp;
           mode_t file_mode;
   
         if ((dep->de_Attributes & ATTR_READONLY) == 0)          if ((dep->de_Attributes & ATTR_READONLY) == 0)
                 mode = S_IRWXU|S_IRWXG|S_IRWXO;                  file_mode = S_IRWXU|S_IRWXG|S_IRWXO;
         else          else
                 mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;                  file_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
         return (vaccess(ap->a_vp->v_type,  
             mode & (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask),          return genfs_can_access(vp->v_type,
             pmp->pm_uid, pmp->pm_gid, ap->a_mode, ap->a_cred));              file_mode & (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask),
               pmp->pm_uid, pmp->pm_gid, mode, cred);
 }  }
   
 int  int
 msdosfs_getattr(v)  msdosfs_access(void *v)
         void *v;  {
           struct vop_access_args /* {
                   struct vnode *a_vp;
                   int a_mode;
                   kauth_cred_t a_cred;
           } */ *ap = v;
           struct vnode *vp = ap->a_vp;
           struct denode *dep = VTODE(vp);
           int error;
   
           error = msdosfs_check_possible(vp, dep, ap->a_mode);
           if (error)
                   return error;
   
           error = msdosfs_check_permitted(vp, dep, ap->a_mode, ap->a_cred);
   
           return error;
   }
   
   int
   msdosfs_getattr(void *v)
 {  {
         struct vop_getattr_args /* {          struct vop_getattr_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
                 struct vattr *a_vap;                  struct vattr *a_vap;
                 kauth_cred_t a_cred;                  kauth_cred_t a_cred;
                 struct lwp *a_l;  
         } */ *ap = v;          } */ *ap = v;
         struct denode *dep = VTODE(ap->a_vp);          struct denode *dep = VTODE(ap->a_vp);
         struct msdosfsmount *pmp = dep->de_pmp;          struct msdosfsmount *pmp = dep->de_pmp;
Line 281  msdosfs_getattr(v)
Line 271  msdosfs_getattr(v)
         u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);          u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
         ino_t fileid;          ino_t fileid;
   
           fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
         DETIMES(dep, NULL, NULL, NULL, pmp->pm_gmtoff);          DETIMES(dep, NULL, NULL, NULL, pmp->pm_gmtoff);
         vap->va_fsid = dep->de_dev;          vap->va_fsid = dep->de_dev;
         /*          /*
Line 329  msdosfs_getattr(v)
Line 320  msdosfs_getattr(v)
         vap->va_bytes =          vap->va_bytes =
             (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;              (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;
         vap->va_type = ap->a_vp->v_type;          vap->va_type = ap->a_vp->v_type;
           fstrans_done(ap->a_vp->v_mount);
         return (0);          return (0);
 }  }
   
 int  int
 msdosfs_setattr(v)  msdosfs_setattr(void *v)
         void *v;  
 {  {
         struct vop_setattr_args /* {          struct vop_setattr_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
                 struct vattr *a_vap;                  struct vattr *a_vap;
                 kauth_cred_t a_cred;                  kauth_cred_t a_cred;
                 struct lwp *a_l;  
         } */ *ap = v;          } */ *ap = v;
         int error = 0, de_changed = 0;          int error = 0, de_changed = 0;
         struct denode *dep = VTODE(ap->a_vp);          struct denode *dep = VTODE(ap->a_vp);
Line 350  msdosfs_setattr(v)
Line 340  msdosfs_setattr(v)
         kauth_cred_t cred = ap->a_cred;          kauth_cred_t cred = ap->a_cred;
   
 #ifdef MSDOSFS_DEBUG  #ifdef MSDOSFS_DEBUG
         printf("msdosfs_setattr(): vp %p, vap %p, cred %p, p %p\n",          printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
             ap->a_vp, vap, cred, ap->a_l);              ap->a_vp, vap, cred);
 #endif  #endif
         /*          /*
          * Note we silently ignore uid or gid changes.           * Note we silently ignore uid or gid changes.
Line 364  msdosfs_setattr(v)
Line 354  msdosfs_setattr(v)
             (vap->va_gid != VNOVAL && vap->va_gid != pmp->pm_gid)) {              (vap->va_gid != VNOVAL && vap->va_gid != pmp->pm_gid)) {
 #ifdef MSDOSFS_DEBUG  #ifdef MSDOSFS_DEBUG
                 printf("msdosfs_setattr(): returning EINVAL\n");                  printf("msdosfs_setattr(): returning EINVAL\n");
                 printf("    va_type %d, va_nlink %x, va_fsid %lx, va_fileid %llx\n",                  printf("    va_type %d, va_nlink %x, va_fsid %"PRIx64", va_fileid %llx\n",
                     vap->va_type, vap->va_nlink, vap->va_fsid,                      vap->va_type, vap->va_nlink, vap->va_fsid,
                     (unsigned long long)vap->va_fileid);                      (unsigned long long)vap->va_fileid);
                 printf("    va_blocksize %lx, va_rdev %x, va_bytes %qx, va_gen %lx\n",                  printf("    va_blocksize %lx, va_rdev %"PRIx64", va_bytes %"PRIx64", va_gen %lx\n",
                     vap->va_blocksize, vap->va_rdev, (long long)vap->va_bytes, vap->va_gen);                      vap->va_blocksize, vap->va_rdev, (long long)vap->va_bytes, vap->va_gen);
 #endif  #endif
                 return (EINVAL);                  return (EINVAL);
Line 378  msdosfs_setattr(v)
Line 368  msdosfs_setattr(v)
         if (ap->a_vp->v_type == VDIR)          if (ap->a_vp->v_type == VDIR)
                 return 0;                  return 0;
   
           fstrans_start(vp->v_mount, FSTRANS_SHARED);
         if (vap->va_size != VNOVAL) {          if (vap->va_size != VNOVAL) {
                 if (vp->v_mount->mnt_flag & MNT_RDONLY)                  if (vp->v_mount->mnt_flag & MNT_RDONLY) {
                         return (EROFS);                          error = EROFS;
                 error = detrunc(dep, (u_long)vap->va_size, 0, cred, ap->a_l);                          goto bad;
                   }
                   error = detrunc(dep, (u_long)vap->va_size, 0, cred);
                 if (error)                  if (error)
                         return (error);                          goto bad;
                 de_changed = 1;                  de_changed = 1;
         }          }
         if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {          if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
                 if (vp->v_mount->mnt_flag & MNT_RDONLY)                  if (vp->v_mount->mnt_flag & MNT_RDONLY) {
                         return (EROFS);                          error = EROFS;
                 if (kauth_cred_geteuid(cred) != pmp->pm_uid &&                          goto bad;
                     (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,                  }
                     &ap->a_l->l_acflag)) &&                  error = genfs_can_chtimes(ap->a_vp, vap->va_vaflags,
                     ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||                      pmp->pm_uid, cred);
                     (error = VOP_ACCESS(ap->a_vp, VWRITE, cred, ap->a_l))))                  if (error)
                         return (error);                          goto bad;
                 if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&                  if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
                     vap->va_atime.tv_sec != VNOVAL)                      vap->va_atime.tv_sec != VNOVAL)
                         unix2dostime(&vap->va_atime, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL);                          unix2dostime(&vap->va_atime, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL);
Line 410  msdosfs_setattr(v)
Line 403  msdosfs_setattr(v)
          * attribute.           * attribute.
          */           */
         if (vap->va_mode != (mode_t)VNOVAL) {          if (vap->va_mode != (mode_t)VNOVAL) {
                 if (vp->v_mount->mnt_flag & MNT_RDONLY)                  if (vp->v_mount->mnt_flag & MNT_RDONLY) {
                         return (EROFS);                          error = EROFS;
                           goto bad;
                   }
                 if (kauth_cred_geteuid(cred) != pmp->pm_uid &&                  if (kauth_cred_geteuid(cred) != pmp->pm_uid &&
                     (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,                      (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
                     &ap->a_l->l_acflag)))                      NULL)))
                         return (error);                          goto bad;
                 /* We ignore the read and execute bits. */                  /* We ignore the read and execute bits. */
                 if (vap->va_mode & S_IWUSR)                  if (vap->va_mode & S_IWUSR)
                         dep->de_Attributes &= ~ATTR_READONLY;                          dep->de_Attributes &= ~ATTR_READONLY;
Line 428  msdosfs_setattr(v)
Line 423  msdosfs_setattr(v)
          * Allow the `archived' bit to be toggled.           * Allow the `archived' bit to be toggled.
          */           */
         if (vap->va_flags != VNOVAL) {          if (vap->va_flags != VNOVAL) {
                 if (vp->v_mount->mnt_flag & MNT_RDONLY)                  if (vp->v_mount->mnt_flag & MNT_RDONLY) {
                         return (EROFS);                          error = EROFS;
                           goto bad;
                   }
                 if (kauth_cred_geteuid(cred) != pmp->pm_uid &&                  if (kauth_cred_geteuid(cred) != pmp->pm_uid &&
                     (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,                      (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
                     &ap->a_l->l_acflag)))                      NULL)))
                         return (error);                          goto bad;
                 if (vap->va_flags & SF_ARCHIVED)                  if (vap->va_flags & SF_ARCHIVED)
                         dep->de_Attributes &= ~ATTR_ARCHIVE;                          dep->de_Attributes &= ~ATTR_ARCHIVE;
                 else                  else
Line 444  msdosfs_setattr(v)
Line 441  msdosfs_setattr(v)
   
         if (de_changed) {          if (de_changed) {
                 VN_KNOTE(vp, NOTE_ATTRIB);                  VN_KNOTE(vp, NOTE_ATTRIB);
                 return (deupdat(dep, 1));                  error = deupdat(dep, 1);
         } else                  if (error)
                 return (0);                          goto bad;
           }
   
   bad:
           fstrans_done(vp->v_mount);
           return error;
 }  }
   
 int  int
 msdosfs_read(v)  msdosfs_read(void *v)
         void *v;  
 {  {
         struct vop_read_args /* {          struct vop_read_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
Line 459  msdosfs_read(v)
Line 460  msdosfs_read(v)
                 int a_ioflag;                  int a_ioflag;
                 kauth_cred_t a_cred;                  kauth_cred_t a_cred;
         } */ *ap = v;          } */ *ap = v;
         int error = 0, flags;          int error = 0;
         int64_t diff;          int64_t diff;
         int blsize;          int blsize;
         long n;          long n;
         long on;          long on;
         daddr_t lbn;          daddr_t lbn;
         void *win;  
         vsize_t bytelen;          vsize_t bytelen;
         struct buf *bp;          struct buf *bp;
         struct vnode *vp = ap->a_vp;          struct vnode *vp = ap->a_vp;
Line 484  msdosfs_read(v)
Line 484  msdosfs_read(v)
         if (uio->uio_offset >= dep->de_FileSize)          if (uio->uio_offset >= dep->de_FileSize)
                 return (0);                  return (0);
   
           fstrans_start(vp->v_mount, FSTRANS_SHARED);
         if (vp->v_type == VREG) {          if (vp->v_type == VREG) {
                 const int advice = IO_ADV_DECODE(ap->a_ioflag);                  const int advice = IO_ADV_DECODE(ap->a_ioflag);
   
Line 493  msdosfs_read(v)
Line 494  msdosfs_read(v)
   
                         if (bytelen == 0)                          if (bytelen == 0)
                                 break;                                  break;
                         win = ubc_alloc(&vp->v_uobj, uio->uio_offset,                          error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice,
                                         &bytelen, advice, UBC_READ);                              UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp));
                         error = uiomove(win, bytelen, uio);  
                         flags = UBC_WANT_UNMAP(vp) ? UBC_UNMAP : 0;  
                         ubc_release(win, flags);  
                         if (error)                          if (error)
                                 break;                                  break;
                 }                  }
Line 517  msdosfs_read(v)
Line 515  msdosfs_read(v)
                 if (diff < n)                  if (diff < n)
                         n = (long) diff;                          n = (long) diff;
   
                 /* convert cluster # to block # */                  /* convert cluster # to sector # */
                 error = pcbmap(dep, lbn, &lbn, 0, &blsize);                  error = pcbmap(dep, lbn, &lbn, 0, &blsize);
                 if (error)                  if (error)
                         return (error);                          goto bad;
   
                 /*                  /*
                  * If we are operating on a directory file then be sure to                   * If we are operating on a directory file then be sure to
                  * do i/o with the vnode for the filesystem instead of the                   * do i/o with the vnode for the filesystem instead of the
                  * vnode for the directory.                   * vnode for the directory.
                  */                   */
                 error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp);                  error = bread(pmp->pm_devvp, de_bn2kb(pmp, lbn), blsize,
                       NOCRED, 0, &bp);
                 n = MIN(n, pmp->pm_bpcluster - bp->b_resid);                  n = MIN(n, pmp->pm_bpcluster - bp->b_resid);
                 if (error) {                  if (error) {
                         brelse(bp);                          brelse(bp, 0);
                         return (error);                          goto bad;
                 }                  }
                 error = uiomove(bp->b_data + on, (int) n, uio);                  error = uiomove((char *)bp->b_data + on, (int) n, uio);
                 brelse(bp);                  brelse(bp, 0);
         } while (error == 0 && uio->uio_resid > 0 && n != 0);          } while (error == 0 && uio->uio_resid > 0 && n != 0);
   
 out:  out:
         if ((ap->a_ioflag & IO_SYNC) == IO_SYNC)          if ((ap->a_ioflag & IO_SYNC) == IO_SYNC)
                 error = deupdat(dep, 1);                  error = deupdat(dep, 1);
   bad:
           fstrans_done(vp->v_mount);
         return (error);          return (error);
 }  }
   
Line 547  out:
Line 548  out:
  * Write data to a file or directory.   * Write data to a file or directory.
  */   */
 int  int
 msdosfs_write(v)  msdosfs_write(void *v)
         void *v;  
 {  {
         struct vop_write_args /* {          struct vop_write_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
Line 556  msdosfs_write(v)
Line 556  msdosfs_write(v)
                 int a_ioflag;                  int a_ioflag;
                 kauth_cred_t a_cred;                  kauth_cred_t a_cred;
         } */ *ap = v;          } */ *ap = v;
         int resid, flags, extended = 0;          int resid, extended = 0;
         int error = 0;          int error = 0;
         int ioflag = ap->a_ioflag;          int ioflag = ap->a_ioflag;
         u_long osize;          u_long osize;
         u_long count;          u_long count;
         void *win;  
         vsize_t bytelen;          vsize_t bytelen;
         off_t oldoff;          off_t oldoff;
         struct uio *uio = ap->a_uio;          struct uio *uio = ap->a_uio;
         struct proc *p = curproc;  
         struct vnode *vp = ap->a_vp;          struct vnode *vp = ap->a_vp;
         struct denode *dep = VTODE(vp);          struct denode *dep = VTODE(vp);
         struct msdosfsmount *pmp = dep->de_pmp;          struct msdosfsmount *pmp = dep->de_pmp;
         kauth_cred_t cred = ap->a_cred;          kauth_cred_t cred = ap->a_cred;
         boolean_t async;          bool async;
   
 #ifdef MSDOSFS_DEBUG  #ifdef MSDOSFS_DEBUG
         printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",          printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
Line 600  msdosfs_write(v)
Line 598  msdosfs_write(v)
         if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX)          if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX)
                 return (EFBIG);                  return (EFBIG);
   
         /*          fstrans_start(vp->v_mount, FSTRANS_SHARED);
          * If they've exceeded their filesize limit, tell them about it.  
          */  
         if (((uio->uio_offset + uio->uio_resid) >  
             p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) {  
                 psignal(p, SIGXFSZ);  
                 return (EFBIG);  
         }  
   
         /*          /*
          * If the offset we are starting the write at is beyond the end of           * 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           * the file, then they've done a seek.  Unix filesystems allow
Line 616  msdosfs_write(v)
Line 606  msdosfs_write(v)
          * with zeroed blocks.           * with zeroed blocks.
          */           */
         if (uio->uio_offset > dep->de_FileSize) {          if (uio->uio_offset > dep->de_FileSize) {
                 if ((error = deextend(dep, uio->uio_offset, cred)) != 0)                  if ((error = deextend(dep, uio->uio_offset, cred)) != 0) {
                           fstrans_done(vp->v_mount);
                         return (error);                          return (error);
                   }
         }          }
   
         /*          /*
Line 634  msdosfs_write(v)
Line 626  msdosfs_write(v)
         if (uio->uio_offset + resid > osize) {          if (uio->uio_offset + resid > osize) {
                 count = de_clcount(pmp, uio->uio_offset + resid) -                  count = de_clcount(pmp, uio->uio_offset + resid) -
                         de_clcount(pmp, osize);                          de_clcount(pmp, osize);
                 if ((error = extendfile(dep, count, NULL, NULL, 0)) &&                  if ((error = extendfile(dep, count, NULL, NULL, 0)))
                     (error != ENOSPC || (ioflag & IO_UNIT)))  
                         goto errexit;                          goto errexit;
         }  
   
         if (dep->de_FileSize < uio->uio_offset + resid) {  
                 dep->de_FileSize = uio->uio_offset + resid;                  dep->de_FileSize = uio->uio_offset + resid;
                 uvm_vnp_setsize(vp, dep->de_FileSize);                  /* hint uvm to not read in extended part */
                   uvm_vnp_setwritesize(vp, dep->de_FileSize);
                 extended = 1;                  extended = 1;
         }          }
   
Line 649  msdosfs_write(v)
Line 639  msdosfs_write(v)
                 oldoff = uio->uio_offset;                  oldoff = uio->uio_offset;
                 bytelen = uio->uio_resid;                  bytelen = uio->uio_resid;
   
                 win = ubc_alloc(&vp->v_uobj, oldoff, &bytelen, UVM_ADV_NORMAL,                  error = ubc_uiomove(&vp->v_uobj, uio, bytelen,
                     UBC_WRITE);                      IO_ADV_DECODE(ioflag), UBC_WRITE | UBC_UNMAP_FLAG(vp));
                 error = uiomove(win, bytelen, uio);                  if (error)
                 flags = UBC_WANT_UNMAP(vp) ? UBC_UNMAP : 0;  
                 ubc_release(win, flags);  
                 if (error) {  
                         break;                          break;
                 }  
   
                 /*                  /*
                  * flush what we just wrote if necessary.                   * flush what we just wrote if necessary.
Line 664  msdosfs_write(v)
Line 650  msdosfs_write(v)
                  */                   */
   
                 if (!async && oldoff >> 16 != uio->uio_offset >> 16) {                  if (!async && oldoff >> 16 != uio->uio_offset >> 16) {
                         simple_lock(&vp->v_interlock);                          mutex_enter(vp->v_interlock);
                         error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16,                          error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16,
                             (uio->uio_offset >> 16) << 16, PGO_CLEANIT);                              (uio->uio_offset >> 16) << 16, PGO_CLEANIT);
                 }                  }
         } while (error == 0 && uio->uio_resid > 0);          } while (error == 0 && uio->uio_resid > 0);
   
           /* set final size */
           uvm_vnp_setsize(vp, dep->de_FileSize);
         if (error == 0 && ioflag & IO_SYNC) {          if (error == 0 && ioflag & IO_SYNC) {
                 simple_lock(&vp->v_interlock);                  mutex_enter(vp->v_interlock);
                 error = VOP_PUTPAGES(vp, trunc_page(oldoff),                  error = VOP_PUTPAGES(vp, trunc_page(oldoff),
                     round_page(oldoff + bytelen), PGO_CLEANIT | PGO_SYNCIO);                      round_page(oldoff + bytelen), PGO_CLEANIT | PGO_SYNCIO);
         }          }
Line 684  errexit:
Line 673  errexit:
         if (resid > uio->uio_resid)          if (resid > uio->uio_resid)
                 VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));                  VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
         if (error) {          if (error) {
                 detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL);                  detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
                 uio->uio_offset -= resid - uio->uio_resid;                  uio->uio_offset -= resid - uio->uio_resid;
                 uio->uio_resid = resid;                  uio->uio_resid = resid;
         } else if ((ioflag & IO_SYNC) == IO_SYNC)          } else if ((ioflag & IO_SYNC) == IO_SYNC)
                 error = deupdat(dep, 1);                  error = deupdat(dep, 1);
           fstrans_done(vp->v_mount);
         KASSERT(vp->v_size == dep->de_FileSize);          KASSERT(vp->v_size == dep->de_FileSize);
         return (error);          return (error);
 }  }
Line 732  msdosfs_update(struct vnode *vp, const s
Line 722  msdosfs_update(struct vnode *vp, const s
  * could just do a sync if they try an fsync on a directory file.   * could just do a sync if they try an fsync on a directory file.
  */   */
 int  int
 msdosfs_remove(v)  msdosfs_remove(void *v)
         void *v;  
 {  {
         struct vop_remove_args /* {          struct vop_remove_args /* {
                 struct vnode *a_dvp;                  struct vnode *a_dvp;
Line 744  msdosfs_remove(v)
Line 733  msdosfs_remove(v)
         struct denode *ddep = VTODE(ap->a_dvp);          struct denode *ddep = VTODE(ap->a_dvp);
         int error;          int error;
   
           fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
         if (ap->a_vp->v_type == VDIR)          if (ap->a_vp->v_type == VDIR)
                 error = EPERM;                  error = EPERM;
         else          else
Line 760  msdosfs_remove(v)
Line 750  msdosfs_remove(v)
                 vput(ap->a_vp); /* causes msdosfs_inactive() to be called                  vput(ap->a_vp); /* causes msdosfs_inactive() to be called
                                  * via vrele() */                                   * via vrele() */
         vput(ap->a_dvp);          vput(ap->a_dvp);
           fstrans_done(ap->a_dvp->v_mount);
         return (error);          return (error);
 }  }
   
 /*  /*
  * DOS filesystems don't know what links are. But since we already called  
  * msdosfs_lookup() with create and lockparent, the parent is locked so we  
  * have to free it before we return the error.  
  */  
 int  
 msdosfs_link(v)  
         void *v;  
 {  
         struct vop_link_args /* {  
                 struct vnode *a_dvp;  
                 struct vnode *a_vp;  
                 struct componentname *a_cnp;  
         } */ *ap = v;  
   
         VOP_ABORTOP(ap->a_dvp, ap->a_cnp);  
         vput(ap->a_dvp);  
         return (EOPNOTSUPP);  
 }  
   
 /*  
  * Renames on files require moving the denode to a new hash queue since the   * 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   * 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".   * in. Unless it is a rename in place.  For example "mv a b".
Line 835  msdosfs_link(v)
Line 806  msdosfs_link(v)
  * I'm not sure how the memory containing the pathnames pointed at by the   * I'm not sure how the memory containing the pathnames pointed at by the
  * componentname structures is freed, there may be some memory bleeding   * componentname structures is freed, there may be some memory bleeding
  * for each rename done.   * for each rename done.
    *
    * --More-- Notes:
    * This routine needs help.  badly.
  */   */
 int  int
 msdosfs_rename(v)  msdosfs_rename(void *v)
         void *v;  
 {  {
         struct vop_rename_args /* {          struct vop_rename_args /* {
                 struct vnode *a_fdvp;                  struct vnode *a_fdvp;
Line 854  msdosfs_rename(v)
Line 827  msdosfs_rename(v)
         struct vnode *fdvp = ap->a_fdvp;          struct vnode *fdvp = ap->a_fdvp;
         struct componentname *tcnp = ap->a_tcnp;          struct componentname *tcnp = ap->a_tcnp;
         struct componentname *fcnp = ap->a_fcnp;          struct componentname *fcnp = ap->a_fcnp;
         struct lwp *l = tcnp->cn_lwp;  
         struct denode *ip, *xp, *dp, *zp;          struct denode *ip, *xp, *dp, *zp;
         u_char toname[11], oldname[11];          u_char toname[11], oldname[11];
         u_long from_diroffset, to_diroffset;          u_long from_diroffset, to_diroffset;
Line 903  abortit:
Line 875  abortit:
                 goto abortit;                  goto abortit;
         }          }
   
         /* */          /*
            * XXX: This can deadlock since we hold tdvp/tvp locked.
            * But I'm not going to fix it now.
            */
         if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)          if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
                 goto abortit;                  goto abortit;
         dp = VTODE(fdvp);          dp = VTODE(fdvp);
Line 924  abortit:
Line 899  abortit:
                     (fcnp->cn_flags & ISDOTDOT) ||                      (fcnp->cn_flags & ISDOTDOT) ||
                     (tcnp->cn_flags & ISDOTDOT) ||                      (tcnp->cn_flags & ISDOTDOT) ||
                     (ip->de_flag & DE_RENAME)) {                      (ip->de_flag & DE_RENAME)) {
                         VOP_UNLOCK(fvp, 0);                          VOP_UNLOCK(fvp);
                         error = EINVAL;                          error = EINVAL;
                         goto abortit;                          goto abortit;
                 }                  }
Line 933  abortit:
Line 908  abortit:
         }          }
         VN_KNOTE(fdvp, NOTE_WRITE);             /* XXXLUKEM/XXX: right place? */          VN_KNOTE(fdvp, NOTE_WRITE);             /* XXXLUKEM/XXX: right place? */
   
           fstrans_start(fdvp->v_mount, FSTRANS_SHARED);
         /*          /*
          * When the target exists, both the directory           * When the target exists, both the directory
          * and target vnodes are returned locked.           * and target vnodes are returned locked.
Line 955  abortit:
Line 931  abortit:
          * to namei, as the parent directory is unlocked by the           * to namei, as the parent directory is unlocked by the
          * call to doscheckpath().           * call to doscheckpath().
          */           */
         error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, l);          error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
         VOP_UNLOCK(fvp, 0);          VOP_UNLOCK(fvp);
         if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)          if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
                 newparent = 1;                  newparent = 1;
   
           /*
            * XXX: We can do this here because rename uses SAVEFART and
            * therefore fdvp has at least two references (one doesn't
            * belong to us, though, and that's evil).  We'll get
            * another "extra" reference when we do relookup(), so we
            * need to compensate.  We should *NOT* be doing this, but
            * it works, so whatever.
            */
         vrele(fdvp);          vrele(fdvp);
   
         if (doingdirectory && newparent) {          if (doingdirectory && newparent) {
                 if (error)      /* write access check above */                  if (error)      /* write access check above */
                         goto bad;                          goto tdvpbad;
                 if (xp != NULL)                  if (xp != NULL)
                         vput(tvp);                          vput(tvp);
                   tvp = NULL;
                 /*                  /*
                  * doscheckpath() vput()'s dp,                   * doscheckpath() vput()'s dp,
                  * so we have to do a relookup afterwards                   * so we have to do a relookup afterwards
Line 973  abortit:
Line 960  abortit:
                         goto out;                          goto out;
                 if ((tcnp->cn_flags & SAVESTART) == 0)                  if ((tcnp->cn_flags & SAVESTART) == 0)
                         panic("msdosfs_rename: lost to startdir");                          panic("msdosfs_rename: lost to startdir");
                 if ((error = relookup(tdvp, &tvp, tcnp)) != 0)                  vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
                   if ((error = relookup(tdvp, &tvp, tcnp)) != 0) {
                           VOP_UNLOCK(tdvp);
                         goto out;                          goto out;
                   }
                   /*
                    * XXX: SAVESTART causes us to get a reference, but
                    * that's released already above in doscheckpath()
                    */
                 dp = VTODE(tdvp);                  dp = VTODE(tdvp);
                 xp = tvp ? VTODE(tvp) : NULL;                  xp = tvp ? VTODE(tvp) : NULL;
         }          }
Line 988  abortit:
Line 982  abortit:
                 if (xp->de_Attributes & ATTR_DIRECTORY) {                  if (xp->de_Attributes & ATTR_DIRECTORY) {
                         if (!dosdirempty(xp)) {                          if (!dosdirempty(xp)) {
                                 error = ENOTEMPTY;                                  error = ENOTEMPTY;
                                 goto bad;                                  goto tdvpbad;
                         }                          }
                         if (!doingdirectory) {                          if (!doingdirectory) {
                                 error = ENOTDIR;                                  error = ENOTDIR;
                                 goto bad;                                  goto tdvpbad;
                         }                          }
                 } else if (doingdirectory) {                  } else if (doingdirectory) {
                         error = EISDIR;                          error = EISDIR;
                         goto bad;                          goto tdvpbad;
                 }                  }
                 if ((error = removede(dp, xp)) != 0)                  if ((error = removede(dp, xp)) != 0)
                         goto bad;                          goto tdvpbad;
                 VN_KNOTE(tdvp, NOTE_WRITE);                  VN_KNOTE(tdvp, NOTE_WRITE);
                 VN_KNOTE(tvp, NOTE_DELETE);                  VN_KNOTE(tvp, NOTE_DELETE);
                 cache_purge(tvp);                  cache_purge(tvp);
                 vput(tvp);                  vput(tvp);
                   tvp = NULL;
                 xp = NULL;                  xp = NULL;
         }          }
   
Line 1012  abortit:
Line 1007  abortit:
          * into the denode and directory entry for the destination           * into the denode and directory entry for the destination
          * file/directory.           * file/directory.
          */           */
         if ((error = uniqdosname(VTODE(tdvp), tcnp, toname)) != 0)          if ((error = uniqdosname(VTODE(tdvp), tcnp, toname)) != 0) {
                   fstrans_done(fdvp->v_mount);
                 goto abortit;                  goto abortit;
           }
   
         /*          /*
          * Since from wasn't locked at various places above,           * Since from wasn't locked at various places above,
Line 1023  abortit:
Line 1020  abortit:
         fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;          fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
         if ((fcnp->cn_flags & SAVESTART) == 0)          if ((fcnp->cn_flags & SAVESTART) == 0)
                 panic("msdosfs_rename: lost from startdir");                  panic("msdosfs_rename: lost from startdir");
         if (!newparent)          VOP_UNLOCK(tdvp);
                 VOP_UNLOCK(tdvp, 0);          vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
         (void) relookup(fdvp, &fvp, fcnp);          if ((error = relookup(fdvp, &fvp, fcnp))) {
                   VOP_UNLOCK(fdvp);
                   vrele(ap->a_fvp);
                   vrele(tdvp);
                   fstrans_done(fdvp->v_mount);
                   return (error);
           }
         if (fvp == NULL) {          if (fvp == NULL) {
                 /*                  /*
                  * From name has disappeared.                   * From name has disappeared.
                  */                   */
                 if (doingdirectory)                  if (doingdirectory)
                         panic("rename: lost dir entry");                          panic("rename: lost dir entry");
                   vput(fdvp);
                 vrele(ap->a_fvp);                  vrele(ap->a_fvp);
                 if (newparent)  
                         VOP_UNLOCK(tdvp, 0);  
                 vrele(tdvp);                  vrele(tdvp);
                   fstrans_done(fdvp->v_mount);
                 return 0;                  return 0;
         }          }
         fdvp_dorele = 1;          fdvp_dorele = 1;
           VOP_UNLOCK(fdvp);
         xp = VTODE(fvp);          xp = VTODE(fvp);
         zp = VTODE(fdvp);          zp = VTODE(fdvp);
         from_diroffset = zp->de_fndoffset;          from_diroffset = zp->de_fndoffset;
Line 1055  abortit:
Line 1059  abortit:
                 if (doingdirectory)                  if (doingdirectory)
                         panic("rename: lost dir entry");                          panic("rename: lost dir entry");
                 vrele(ap->a_fvp);                  vrele(ap->a_fvp);
                 VOP_UNLOCK(fvp, 0);  
                 if (newparent)  
                         VOP_UNLOCK(fdvp, 0);  
                 xp = NULL;                  xp = NULL;
         } else {          } else {
                 vrele(fvp);                  vrele(fvp);
Line 1078  abortit:
Line 1079  abortit:
                 error = createde(ip, dp, (struct denode **)0, tcnp);                  error = createde(ip, dp, (struct denode **)0, tcnp);
                 if (error) {                  if (error) {
                         memcpy(ip->de_Name, oldname, 11);                          memcpy(ip->de_Name, oldname, 11);
                         if (newparent)                          VOP_UNLOCK(fvp);
                                 VOP_UNLOCK(fdvp, 0);  
                         VOP_UNLOCK(fvp, 0);  
                         goto bad;                          goto bad;
                 }                  }
                 ip->de_refcnt++;                  ip->de_refcnt++;
                 zp->de_fndoffset = from_diroffset;                  zp->de_fndoffset = from_diroffset;
                 if ((error = removede(zp, ip)) != 0) {                  if ((error = removede(zp, ip)) != 0) {
                         /* XXX should really panic here, fs is corrupt */                          /* XXX should really panic here, fs is corrupt */
                         if (newparent)                          VOP_UNLOCK(fvp);
                                 VOP_UNLOCK(fdvp, 0);  
                         VOP_UNLOCK(fvp, 0);  
                         goto bad;                          goto bad;
                 }                  }
                 cache_purge(fvp);                  cache_purge(fvp);
Line 1098  abortit:
Line 1095  abortit:
                                        &ip->de_dirclust, 0);                                         &ip->de_dirclust, 0);
                         if (error) {                          if (error) {
                                 /* XXX should really panic here, fs is corrupt */                                  /* XXX should really panic here, fs is corrupt */
                                 if (newparent)                                  VOP_UNLOCK(fvp);
                                         VOP_UNLOCK(fdvp, 0);  
                                 VOP_UNLOCK(fvp, 0);  
                                 goto bad;                                  goto bad;
                         }                          }
                         ip->de_diroffset = to_diroffset;                          ip->de_diroffset = to_diroffset;
Line 1108  abortit:
Line 1103  abortit:
                                 ip->de_diroffset &= pmp->pm_crbomask;                                  ip->de_diroffset &= pmp->pm_crbomask;
                 }                  }
                 reinsert(ip);                  reinsert(ip);
                 if (newparent)  
                         VOP_UNLOCK(fdvp, 0);  
         }          }
   
         /*          /*
Line 1123  abortit:
Line 1116  abortit:
                         panic("msdosfs_rename: updating .. in root directory?");                          panic("msdosfs_rename: updating .. in root directory?");
                 } else                  } else
                         bn = cntobn(pmp, cn);                          bn = cntobn(pmp, cn);
                 error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,                  error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
                               NOCRED, &bp);                      pmp->pm_bpcluster, NOCRED, B_MODIFY, &bp);
                 if (error) {                  if (error) {
                         /* XXX should really panic here, fs is corrupt */                          /* XXX should really panic here, fs is corrupt */
                         brelse(bp);                          brelse(bp, 0);
                         VOP_UNLOCK(fvp, 0);                          VOP_UNLOCK(fvp);
                         goto bad;                          goto bad;
                 }                  }
                 dotdotp = (struct direntry *)bp->b_data + 1;                  dotdotp = (struct direntry *)bp->b_data + 1;
Line 1141  abortit:
Line 1134  abortit:
                 }                  }
                 if ((error = bwrite(bp)) != 0) {                  if ((error = bwrite(bp)) != 0) {
                         /* XXX should really panic here, fs is corrupt */                          /* XXX should really panic here, fs is corrupt */
                         VOP_UNLOCK(fvp, 0);                          VOP_UNLOCK(fvp);
                         goto bad;                          goto bad;
                 }                  }
         }          }
   
         VN_KNOTE(fvp, NOTE_RENAME);          VN_KNOTE(fvp, NOTE_RENAME);
         VOP_UNLOCK(fvp, 0);          VOP_UNLOCK(fvp);
 bad:  bad:
         if (xp)          if (tvp)
                 vput(tvp);                  vput(tvp);
         vput(tdvp);          vrele(tdvp);
 out:  out:
         ip->de_flag &= ~DE_RENAME;          ip->de_flag &= ~DE_RENAME;
         if (fdvp_dorele)          if (fdvp_dorele)
                 vrele(fdvp);                  vrele(fdvp);
         vrele(fvp);          vrele(fvp);
           fstrans_done(fdvp->v_mount);
         return (error);          return (error);
   
           /* XXX: uuuh */
   tdvpbad:
           VOP_UNLOCK(tdvp);
           goto bad;
 }  }
   
 static const struct {  static const struct {
Line 1188  static const struct {
Line 1186  static const struct {
 };  };
   
 int  int
 msdosfs_mkdir(v)  msdosfs_mkdir(void *v)
         void *v;  
 {  {
         struct vop_mkdir_args /* {          struct vop_mkdir_args /* {
                 struct vnode *a_dvp;                  struct vnode *a_dvp;
Line 1204  msdosfs_mkdir(v)
Line 1201  msdosfs_mkdir(v)
         int error;          int error;
         int bn;          int bn;
         u_long newcluster, pcl;          u_long newcluster, pcl;
           daddr_t lbn;
         struct direntry *denp;          struct direntry *denp;
         struct msdosfsmount *pmp = pdep->de_pmp;          struct msdosfsmount *pmp = pdep->de_pmp;
         struct buf *bp;          struct buf *bp;
         int async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;          int async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
   
           fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
         /*          /*
          * If this is the root directory and there is no space left we           * 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           * can't do anything.  This is because the root directory can not
Line 1238  msdosfs_mkdir(v)
Line 1237  msdosfs_mkdir(v)
          * directory to be pointing at if there were a crash.           * directory to be pointing at if there were a crash.
          */           */
         bn = cntobn(pmp, newcluster);          bn = cntobn(pmp, newcluster);
           lbn = de_bn2kb(pmp, bn);
         /* always succeeds */          /* always succeeds */
         bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0);          bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
         memset(bp->b_data, 0, pmp->pm_bpcluster);          memset(bp->b_data, 0, pmp->pm_bpcluster);
         memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);          memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
         denp = (struct direntry *)bp->b_data;          denp = (struct direntry *)bp->b_data;
Line 1297  msdosfs_mkdir(v)
Line 1297  msdosfs_mkdir(v)
         VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK);          VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
         vput(ap->a_dvp);          vput(ap->a_dvp);
         *ap->a_vpp = DETOV(dep);          *ap->a_vpp = DETOV(dep);
           fstrans_done(ap->a_dvp->v_mount);
         return (0);          return (0);
   
 bad:  bad:
Line 1304  bad:
Line 1305  bad:
 bad2:  bad2:
         PNBUF_PUT(cnp->cn_pnbuf);          PNBUF_PUT(cnp->cn_pnbuf);
         vput(ap->a_dvp);          vput(ap->a_dvp);
           fstrans_done(ap->a_dvp->v_mount);
         return (error);          return (error);
 }  }
   
 int  int
 msdosfs_rmdir(v)  msdosfs_rmdir(void *v)
         void *v;  
 {  {
         struct vop_rmdir_args /* {          struct vop_rmdir_args /* {
                 struct vnode *a_dvp;                  struct vnode *a_dvp;
Line 1332  msdosfs_rmdir(v)
Line 1333  msdosfs_rmdir(v)
                 vput(vp);                  vput(vp);
                 return (EINVAL);                  return (EINVAL);
         }          }
           fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
         /*          /*
          * Verify the directory is empty (and valid).           * Verify the directory is empty (and valid).
          * (Rmdir ".." won't be valid since           * (Rmdir ".." won't be valid since
Line 1366  msdosfs_rmdir(v)
Line 1368  msdosfs_rmdir(v)
         /*          /*
          * Truncate the directory that is being deleted.           * Truncate the directory that is being deleted.
          */           */
         error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred, cnp->cn_lwp);          error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
         cache_purge(vp);          cache_purge(vp);
 out:  out:
         VN_KNOTE(vp, NOTE_DELETE);          VN_KNOTE(vp, NOTE_DELETE);
         if (dvp)          if (dvp)
                 vput(dvp);                  vput(dvp);
         vput(vp);          vput(vp);
           fstrans_done(ap->a_dvp->v_mount);
         return (error);          return (error);
 }  }
   
 /*  
  * DOS filesystems don't know what symlinks are.  
  */  
 int  
 msdosfs_symlink(v)  
         void *v;  
 {  
         struct vop_symlink_args /* {  
                 struct vnode *a_dvp;  
                 struct vnode **a_vpp;  
                 struct componentname *a_cnp;  
                 struct vattr *a_vap;  
                 char *a_target;  
         } */ *ap = v;  
   
         VOP_ABORTOP(ap->a_dvp, ap->a_cnp);  
         vput(ap->a_dvp);  
         return (EOPNOTSUPP);  
 }  
   
 int  int
 msdosfs_readdir(v)  msdosfs_readdir(void *v)
         void *v;  
 {  {
         struct vop_readdir_args /* {          struct vop_readdir_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
Line 1424  msdosfs_readdir(v)
Line 1406  msdosfs_readdir(v)
         struct denode *dep = VTODE(ap->a_vp);          struct denode *dep = VTODE(ap->a_vp);
         struct msdosfsmount *pmp = dep->de_pmp;          struct msdosfsmount *pmp = dep->de_pmp;
         struct direntry *dentp;          struct direntry *dentp;
         struct dirent dirbuf;          struct dirent *dirbuf;
         struct uio *uio = ap->a_uio;          struct uio *uio = ap->a_uio;
         off_t *cookies = NULL;          off_t *cookies = NULL;
         int ncookies = 0, nc = 0;          int ncookies = 0, nc = 0;
Line 1446  msdosfs_readdir(v)
Line 1428  msdosfs_readdir(v)
                 return (ENOTDIR);                  return (ENOTDIR);
   
         /*          /*
          * To be safe, initialize dirbuf  
          */  
         memset(dirbuf.d_name, 0, sizeof(dirbuf.d_name));  
   
         /*  
          * If the user buffer is smaller than the size of one dos directory           * 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           * entry or the file offset is not a multiple of the size of a
          * directory entry, then we fail the read.           * directory entry, then we fail the read.
Line 1463  msdosfs_readdir(v)
Line 1440  msdosfs_readdir(v)
         lost = uio->uio_resid - count;          lost = uio->uio_resid - count;
         uio->uio_resid = count;          uio->uio_resid = count;
         uio_off = uio->uio_offset;          uio_off = uio->uio_offset;
   
           fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
   
           /* Allocate a temporary dirent buffer. */
           dirbuf = malloc(sizeof(struct dirent), M_MSDOSFSTMP, M_WAITOK | M_ZERO);
   
         if (ap->a_ncookies) {          if (ap->a_ncookies) {
                 nc = uio->uio_resid / 16;                  nc = uio->uio_resid / _DIRENT_MINSIZE((struct dirent *)0);
                 cookies = malloc(nc * sizeof (off_t), M_TEMP, M_WAITOK);                  cookies = malloc(nc * sizeof (off_t), M_TEMP, M_WAITOK);
                 *ap->a_cookies = cookies;                  *ap->a_cookies = cookies;
         }          }
Line 1482  msdosfs_readdir(v)
Line 1464  msdosfs_readdir(v)
         if (dep->de_StartCluster == MSDOSFSROOT          if (dep->de_StartCluster == MSDOSFSROOT
             || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {              || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
 #if 0  #if 0
                 printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",                  printf("msdosfs_readdir(): going after . or .. in root dir, "
                     offset);                      "offset %" PRIu64 "\n", offset);
 #endif  #endif
                 bias = 2 * sizeof(struct direntry);                  bias = 2 * sizeof(struct direntry);
                 if (offset < bias) {                  if (offset < bias) {
                         for (n = (int)offset / sizeof(struct direntry);                          for (n = (int)offset / sizeof(struct direntry);
                              n < 2; n++) {                               n < 2; n++) {
                                 if (FAT32(pmp))                                  if (FAT32(pmp))
                                         dirbuf.d_fileno = cntobn(pmp,                                          dirbuf->d_fileno = cntobn(pmp,
                                              (ino_t)pmp->pm_rootdirblk)                                               (ino_t)pmp->pm_rootdirblk)
                                              * dirsperblk;                                               * dirsperblk;
                                 else                                  else
                                         dirbuf.d_fileno = 1;                                          dirbuf->d_fileno = 1;
                                 dirbuf.d_type = DT_DIR;                                  dirbuf->d_type = DT_DIR;
                                 switch (n) {                                  switch (n) {
                                 case 0:                                  case 0:
                                         dirbuf.d_namlen = 1;                                          dirbuf->d_namlen = 1;
                                         strlcpy(dirbuf.d_name, ".",                                          strlcpy(dirbuf->d_name, ".",
                                             sizeof(dirbuf.d_name));                                              sizeof(dirbuf->d_name));
                                         break;                                          break;
                                 case 1:                                  case 1:
                                         dirbuf.d_namlen = 2;                                          dirbuf->d_namlen = 2;
                                         strlcpy(dirbuf.d_name, "..",                                          strlcpy(dirbuf->d_name, "..",
                                             sizeof(dirbuf.d_name));                                              sizeof(dirbuf->d_name));
                                         break;                                          break;
                                 }                                  }
                                 dirbuf.d_reclen = _DIRENT_SIZE(&dirbuf);                                  dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
                                 if (uio->uio_resid < dirbuf.d_reclen)                                  if (uio->uio_resid < dirbuf->d_reclen)
                                         goto out;                                          goto out;
                                 error = uiomove(&dirbuf,                                  error = uiomove(dirbuf, dirbuf->d_reclen, uio);
                                                 dirbuf.d_reclen, uio);  
                                 if (error)                                  if (error)
                                         goto out;                                          goto out;
                                 offset += sizeof(struct direntry);                                  offset += sizeof(struct direntry);
Line 1537  msdosfs_readdir(v)
Line 1518  msdosfs_readdir(v)
                 n = MIN(n, diff);                  n = MIN(n, diff);
                 if ((error = pcbmap(dep, lbn, &bn, &cn, &blsize)) != 0)                  if ((error = pcbmap(dep, lbn, &bn, &cn, &blsize)) != 0)
                         break;                          break;
                 error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);                  error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
                       NOCRED, 0, &bp);
                 if (error) {                  if (error) {
                         brelse(bp);                          brelse(bp, 0);
                         return (error);                          goto bad;
                 }                  }
                 n = MIN(n, blsize - bp->b_resid);                  n = MIN(n, blsize - bp->b_resid);
   
Line 1548  msdosfs_readdir(v)
Line 1530  msdosfs_readdir(v)
                  * Convert from dos directory entries to fs-independent                   * Convert from dos directory entries to fs-independent
                  * directory entries.                   * directory entries.
                  */                   */
                 for (dentp = (struct direntry *)(bp->b_data + on);                  for (dentp = (struct direntry *)((char *)bp->b_data + on);
                      (char *)dentp < bp->b_data + on + n;                       (char *)dentp < (char *)bp->b_data + on + n;
                      dentp++, offset += sizeof(struct direntry)) {                       dentp++, offset += sizeof(struct direntry)) {
 #if 0  #if 0
   
Line 1560  msdosfs_readdir(v)
Line 1542  msdosfs_readdir(v)
                          * If this is an unused entry, we can stop.                           * If this is an unused entry, we can stop.
                          */                           */
                         if (dentp->deName[0] == SLOT_EMPTY) {                          if (dentp->deName[0] == SLOT_EMPTY) {
                                 brelse(bp);                                  brelse(bp, 0);
                                 goto out;                                  goto out;
                         }                          }
                         /*                          /*
Line 1577  msdosfs_readdir(v)
Line 1559  msdosfs_readdir(v)
                         if (dentp->deAttributes == ATTR_WIN95) {                          if (dentp->deAttributes == ATTR_WIN95) {
                                 if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)                                  if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
                                         continue;                                          continue;
                                 chksum = win2unixfn((struct winentry *)dentp, &dirbuf, chksum);                                  chksum = win2unixfn((struct winentry *)dentp,
                                       dirbuf, chksum);
                                 continue;                                  continue;
                         }                          }
   
Line 1607  msdosfs_readdir(v)
Line 1590  msdosfs_readdir(v)
                                                 fileno = 1;                                                  fileno = 1;
                                 else                                  else
                                         fileno = cntobn(pmp, fileno) * dirsperblk;                                          fileno = cntobn(pmp, fileno) * dirsperblk;
                                 dirbuf.d_fileno = fileno;                                  dirbuf->d_fileno = fileno;
                                 dirbuf.d_type = DT_DIR;                                  dirbuf->d_type = DT_DIR;
                         } else {                          } else {
                                 dirbuf.d_fileno = offset / sizeof(struct direntry);                                  dirbuf->d_fileno =
                                 dirbuf.d_type = DT_REG;                                      offset / sizeof(struct direntry);
                                   dirbuf->d_type = DT_REG;
                         }                          }
                         if (chksum != winChksum(dentp->deName))                          if (chksum != winChksum(dentp->deName))
                                 dirbuf.d_namlen = dos2unixfn(dentp->deName,                                  dirbuf->d_namlen = dos2unixfn(dentp->deName,
                                     (u_char *)dirbuf.d_name,                                      (u_char *)dirbuf->d_name,
                                     pmp->pm_flags & MSDOSFSMNT_SHORTNAME);                                      pmp->pm_flags & MSDOSFSMNT_SHORTNAME);
                         else                          else
                                 dirbuf.d_name[dirbuf.d_namlen] = 0;                                  dirbuf->d_name[dirbuf->d_namlen] = 0;
                         chksum = -1;                          chksum = -1;
                         dirbuf.d_reclen = _DIRENT_SIZE(&dirbuf);                          dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
                         if (uio->uio_resid < dirbuf.d_reclen) {                          if (uio->uio_resid < dirbuf->d_reclen) {
                                 brelse(bp);                                  brelse(bp, 0);
                                 goto out;                                  goto out;
                         }                          }
                         error = uiomove(&dirbuf,                          error = uiomove(dirbuf, dirbuf->d_reclen, uio);
                                         dirbuf.d_reclen, uio);  
                         if (error) {                          if (error) {
                                 brelse(bp);                                  brelse(bp, 0);
                                 goto out;                                  goto out;
                         }                          }
                         uio_off = offset + sizeof(struct direntry);                          uio_off = offset + sizeof(struct direntry);
Line 1636  msdosfs_readdir(v)
Line 1619  msdosfs_readdir(v)
                                 *cookies++ = offset + sizeof(struct direntry);                                  *cookies++ = offset + sizeof(struct direntry);
                                 ncookies++;                                  ncookies++;
                                 if (ncookies >= nc) {                                  if (ncookies >= nc) {
                                         brelse(bp);                                          brelse(bp, 0);
                                         goto out;                                          goto out;
                                 }                                  }
                         }                          }
                 }                  }
                 brelse(bp);                  brelse(bp, 0);
         }          }
   
 out:  out:
Line 1660  out:
Line 1643  out:
                 } else                  } else
                         *ap->a_ncookies = ncookies;                          *ap->a_ncookies = ncookies;
         }          }
         return (error);  
 }  
   
 /*  bad:
  * DOS filesystems don't know what symlinks are.          free(dirbuf, M_MSDOSFSTMP);
  */          fstrans_done(ap->a_vp->v_mount);
 int          return (error);
 msdosfs_readlink(v)  
         void *v;  
 {  
 #if 0  
         struct vop_readlink_args /* {  
                 struct vnode *a_vp;  
                 struct uio *a_uio;  
                 kauth_cred_t a_cred;  
         } */ *ap;  
 #endif  
   
         return (EINVAL);  
 }  }
   
 /*  /*
Line 1689  msdosfs_readlink(v)
Line 1658  msdosfs_readlink(v)
  * bnp - address of where to return the filesystem relative block number   * bnp - address of where to return the filesystem relative block number
  */   */
 int  int
 msdosfs_bmap(v)  msdosfs_bmap(void *v)
         void *v;  
 {  {
         struct vop_bmap_args /* {          struct vop_bmap_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
Line 1700  msdosfs_bmap(v)
Line 1668  msdosfs_bmap(v)
                 int *a_runp;                  int *a_runp;
         } */ *ap = v;          } */ *ap = v;
         struct denode *dep = VTODE(ap->a_vp);          struct denode *dep = VTODE(ap->a_vp);
           int run, maxrun;
           daddr_t runbn;
           int status;
   
         if (ap->a_vpp != NULL)          if (ap->a_vpp != NULL)
                 *ap->a_vpp = dep->de_devvp;                  *ap->a_vpp = dep->de_devvp;
         if (ap->a_bnp == NULL)          if (ap->a_bnp == NULL)
                 return (0);                  return (0);
         if (ap->a_runp) {          status = pcbmap(dep, ap->a_bn, ap->a_bnp, 0, 0);
                 /*  
                  * Sequential clusters should be counted here.          /*
                  */           * From FreeBSD:
                 *ap->a_runp = 0;           * A little kludgy, but we loop calling pcbmap until we
            * reach the end of the contiguous piece, or reach MAXPHYS.
            * Since it reduces disk I/Os, the "wasted" CPU is put to
            * good use (4 to 5 fold sequential read I/O improvement on USB
            * drives).
            */
           if (ap->a_runp != NULL) {
                   /* taken from ufs_bmap */
                   maxrun = ulmin(MAXPHYS / dep->de_pmp->pm_bpcluster - 1,
                                  dep->de_pmp->pm_maxcluster - ap->a_bn);
                   for (run = 1; run <= maxrun; run++) {
                           if (pcbmap(dep, ap->a_bn + run, &runbn, NULL, NULL)
                               != 0 || runbn !=
                                       *ap->a_bnp + de_cn2bn(dep->de_pmp, run))
                                   break;
                   }
                   *ap->a_runp = run - 1;
         }          }
         return (pcbmap(dep, ap->a_bn, ap->a_bnp, 0, 0));  
           /*
            * We need to scale *ap->a_bnp by sector_size/DEV_BSIZE
            */
           *ap->a_bnp = de_bn2kb(dep->de_pmp, *ap->a_bnp);
           return status;
 }  }
   
 int  int
 msdosfs_strategy(v)  msdosfs_strategy(void *v)
         void *v;  
 {  {
         struct vop_strategy_args /* {          struct vop_strategy_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
Line 1742  msdosfs_strategy(v)
Line 1733  msdosfs_strategy(v)
                         bp->b_blkno = -1;                          bp->b_blkno = -1;
                 if (bp->b_blkno == -1)                  if (bp->b_blkno == -1)
                         clrbuf(bp);                          clrbuf(bp);
                   else
                           bp->b_blkno = de_bn2kb(dep->de_pmp, bp->b_blkno);
         }          }
         if (bp->b_blkno == -1) {          if (bp->b_blkno == -1) {
                 biodone(bp);                  biodone(bp);
Line 1758  msdosfs_strategy(v)
Line 1751  msdosfs_strategy(v)
 }  }
   
 int  int
 msdosfs_print(v)  msdosfs_print(void *v)
         void *v;  
 {  {
         struct vop_print_args /* {          struct vop_print_args /* {
                 struct vnode *vp;                  struct vnode *vp;
Line 1769  msdosfs_print(v)
Line 1761  msdosfs_print(v)
         printf(          printf(
             "tag VT_MSDOSFS, startcluster %ld, dircluster %ld, diroffset %ld ",              "tag VT_MSDOSFS, startcluster %ld, dircluster %ld, diroffset %ld ",
             dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);              dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
         printf(" dev %d, %d ", major(dep->de_dev), minor(dep->de_dev));          printf(" dev %llu, %llu ", (unsigned long long)major(dep->de_dev),
         lockmgr_printinfo(&ap->a_vp->v_lock);              (unsigned long long)minor(dep->de_dev));
         printf("\n");          printf("\n");
         return (0);          return (0);
 }  }
   
 int  int
 msdosfs_advlock(v)  msdosfs_advlock(void *v)
         void *v;  
 {  {
         struct vop_advlock_args /* {          struct vop_advlock_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
Line 1792  msdosfs_advlock(v)
Line 1783  msdosfs_advlock(v)
 }  }
   
 int  int
 msdosfs_pathconf(v)  msdosfs_pathconf(void *v)
         void *v;  
 {  {
         struct vop_pathconf_args /* {          struct vop_pathconf_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
Line 1830  msdosfs_pathconf(v)
Line 1820  msdosfs_pathconf(v)
 }  }
   
 int  int
 msdosfs_fsync(v)  msdosfs_fsync(void *v)
         void *v;  
 {  {
         struct vop_fsync_args /* {          struct vop_fsync_args /* {
                 struct vnode *a_vp;                  struct vnode *a_vp;
Line 1839  msdosfs_fsync(v)
Line 1828  msdosfs_fsync(v)
                 int a_flags;                  int a_flags;
                 off_t offlo;                  off_t offlo;
                 off_t offhi;                  off_t offhi;
                 struct lwp *a_l;  
         } */ *ap = v;          } */ *ap = v;
         struct vnode *vp = ap->a_vp;          struct vnode *vp = ap->a_vp;
         int wait;          int wait;
         int error;          int error;
   
           fstrans_start(vp->v_mount, FSTRANS_LAZY);
         wait = (ap->a_flags & FSYNC_WAIT) != 0;          wait = (ap->a_flags & FSYNC_WAIT) != 0;
         vflushbuf(vp, wait);          vflushbuf(vp, wait);
         if ((ap->a_flags & FSYNC_DATAONLY) != 0)          if ((ap->a_flags & FSYNC_DATAONLY) != 0)
Line 1858  msdosfs_fsync(v)
Line 1847  msdosfs_fsync(v)
   
                 int l = 0;                  int l = 0;
                 error = VOP_IOCTL(devvp, DIOCCACHESYNC, &l, FWRITE,                  error = VOP_IOCTL(devvp, DIOCCACHESYNC, &l, FWRITE,
                                           ap->a_l->l_cred, ap->a_l);                                            curlwp->l_cred);
         }          }
           fstrans_done(vp->v_mount);
   
         return (error);          return (error);
 }  }
Line 1906  const struct vnodeopv_entry_desc msdosfs
Line 1896  const struct vnodeopv_entry_desc msdosfs
         { &vop_default_desc, vn_default_error },          { &vop_default_desc, vn_default_error },
         { &vop_lookup_desc, msdosfs_lookup },           /* lookup */          { &vop_lookup_desc, msdosfs_lookup },           /* lookup */
         { &vop_create_desc, msdosfs_create },           /* create */          { &vop_create_desc, msdosfs_create },           /* create */
         { &vop_mknod_desc, msdosfs_mknod },             /* mknod */          { &vop_mknod_desc, genfs_eopnotsupp },          /* mknod */
         { &vop_open_desc, msdosfs_open },               /* open */          { &vop_open_desc, genfs_nullop },               /* open */
         { &vop_close_desc, msdosfs_close },             /* close */          { &vop_close_desc, msdosfs_close },             /* close */
         { &vop_access_desc, msdosfs_access },           /* access */          { &vop_access_desc, msdosfs_access },           /* access */
         { &vop_getattr_desc, msdosfs_getattr },         /* getattr */          { &vop_getattr_desc, msdosfs_getattr },         /* getattr */
         { &vop_setattr_desc, msdosfs_setattr },         /* setattr */          { &vop_setattr_desc, msdosfs_setattr },         /* setattr */
         { &vop_read_desc, msdosfs_read },               /* read */          { &vop_read_desc, msdosfs_read },               /* read */
         { &vop_write_desc, msdosfs_write },             /* write */          { &vop_write_desc, msdosfs_write },             /* write */
         { &vop_lease_desc, msdosfs_lease_check },       /* lease */  
         { &vop_fcntl_desc, genfs_fcntl },               /* fcntl */          { &vop_fcntl_desc, genfs_fcntl },               /* fcntl */
         { &vop_ioctl_desc, msdosfs_ioctl },             /* ioctl */          { &vop_ioctl_desc, msdosfs_ioctl },             /* ioctl */
         { &vop_poll_desc, msdosfs_poll },               /* poll */          { &vop_poll_desc, msdosfs_poll },               /* poll */
Line 1924  const struct vnodeopv_entry_desc msdosfs
Line 1913  const struct vnodeopv_entry_desc msdosfs
         { &vop_fsync_desc, msdosfs_fsync },             /* fsync */          { &vop_fsync_desc, msdosfs_fsync },             /* fsync */
         { &vop_seek_desc, msdosfs_seek },               /* seek */          { &vop_seek_desc, msdosfs_seek },               /* seek */
         { &vop_remove_desc, msdosfs_remove },           /* remove */          { &vop_remove_desc, msdosfs_remove },           /* remove */
         { &vop_link_desc, msdosfs_link },               /* link */          { &vop_link_desc, genfs_eopnotsupp },           /* link */
         { &vop_rename_desc, msdosfs_rename },           /* rename */          { &vop_rename_desc, msdosfs_rename },           /* rename */
         { &vop_mkdir_desc, msdosfs_mkdir },             /* mkdir */          { &vop_mkdir_desc, msdosfs_mkdir },             /* mkdir */
         { &vop_rmdir_desc, msdosfs_rmdir },             /* rmdir */          { &vop_rmdir_desc, msdosfs_rmdir },             /* rmdir */
         { &vop_symlink_desc, msdosfs_symlink },         /* symlink */          { &vop_symlink_desc, genfs_eopnotsupp },        /* symlink */
         { &vop_readdir_desc, msdosfs_readdir },         /* readdir */          { &vop_readdir_desc, msdosfs_readdir },         /* readdir */
         { &vop_readlink_desc, msdosfs_readlink },       /* readlink */          { &vop_readlink_desc, genfs_einval },           /* readlink */
         { &vop_abortop_desc, msdosfs_abortop },         /* abortop */          { &vop_abortop_desc, msdosfs_abortop },         /* abortop */
         { &vop_inactive_desc, msdosfs_inactive },       /* inactive */          { &vop_inactive_desc, msdosfs_inactive },       /* inactive */
         { &vop_reclaim_desc, msdosfs_reclaim },         /* reclaim */          { &vop_reclaim_desc, msdosfs_reclaim },         /* reclaim */

Legend:
Removed from v.1.30  
changed lines
  Added in v.1.61.4.3

CVSweb <webmaster@jp.NetBSD.org>