/* * nwfs_ioctl(struct vnode *a_vp, u_long a_command, caddr_t a_data, * int a_fflag, struct ucred *a_cred) */ int nwfs_ioctl(struct vop_ioctl_args *ap) { int error; struct thread *td = curthread; /* XXX */ struct ucred *cred = ap->a_cred; struct vnode *vp = ap->a_vp; struct nwnode *np = VTONW(vp); struct nwmount *nmp = VTONWFS(vp); struct ncp_conn *conn = NWFSTOCONN(nmp); struct ncp_handle *hp; struct nw_entry_info *fap; void *data = ap->a_data; switch (ap->a_command) { case NWFSIOC_GETCONN: error = ncp_conn_lock(conn, td, cred, NCPM_READ); if (error) break; error = ncp_conn_gethandle(conn, td, &hp); ncp_conn_unlock(conn, td); if (error) break; *(int*)data = hp->nh_id; break; case NWFSIOC_GETEINFO: if ((error = VOP_EACCESS(vp, VEXEC, cred))) break; fap = data; error = ncp_obtain_info(nmp, np->n_fid.f_id, 0, NULL, fap, td, ap->a_cred); strcpy(fap->entryName, np->n_name); fap->nameLen = np->n_nmlen; break; case NWFSIOC_GETNS: if ((error = VOP_EACCESS(vp, VEXEC, cred))) break; *(int*)data = nmp->name_space; break; default: error = EINVAL; } return (error); }
/* * ffs_mount * * Called when mounting local physical media * * PARAMETERS: * mountroot * mp mount point structure * path NULL (flag for root mount!!!) * data <unused> * p process (user credentials check [statfs]) * * mount * mp mount point structure * path path to mount point * data pointer to argument struct in user space * p process (user credentials check) * * RETURNS: 0 Success * !0 error number (errno.h) * * LOCK STATE: * * ENTRY * mount point is locked * EXIT * mount point is locked * * NOTES: * A NULL path can be used for a flag since the mount * system call will fail with EFAULT in copyinstr in * nlookup() if it is a genuine NULL from the user. */ static int ffs_mount(struct mount *mp, /* mount struct pointer */ char *path, /* path to mount point */ caddr_t data, /* arguments to FS specific mount */ struct ucred *cred) /* process requesting mount */ { size_t size; int error; struct vnode *devvp; struct ufs_args args; struct ufsmount *ump = NULL; struct fs *fs; int flags, ronly = 0; mode_t accessmode; struct nlookupdata nd; struct vnode *rootvp; devvp = NULL; error = 0; /* * Use NULL path to flag a root mount */ if (path == NULL) { /* *** * Mounting root filesystem *** */ if ((error = bdevvp(rootdev, &rootvp))) { kprintf("ffs_mountroot: can't find rootvp\n"); return (error); } if( ( error = ffs_mountfs(rootvp, mp, M_FFSNODE)) != 0) { /* fs specific cleanup (if any)*/ goto error_1; } devvp = rootvp; goto dostatfs; /* success*/ } /* *** * Mounting non-root filesystem or updating a filesystem *** */ /* copy in user arguments*/ error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args)); if (error) goto error_1; /* can't get arguments*/ /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { ump = VFSTOUFS(mp); fs = ump->um_fs; devvp = ump->um_devvp; error = 0; ronly = fs->fs_ronly; /* MNT_RELOAD might change this */ if (ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { /* * Flush any dirty data. */ VFS_SYNC(mp, MNT_WAIT); /* * Check for and optionally get rid of files open * for writing. */ flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; if (mp->mnt_flag & MNT_SOFTDEP) { error = softdep_flushfiles(mp, flags); } else { error = ffs_flushfiles(mp, flags); } ronly = 1; } if (!error && (mp->mnt_flag & MNT_RELOAD)) { error = ffs_reload(mp, NULL); } if (error) { goto error_1; } if (ronly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ if (cred->cr_uid != 0) { vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); if ((error = VOP_EACCESS(devvp, VREAD | VWRITE, cred)) != 0) { vn_unlock(devvp); return (error); } vn_unlock(devvp); } fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) { fs->fs_flags |= FS_UNCLEAN; if (mp->mnt_flag & MNT_FORCE) { kprintf( "WARNING: %s was not properly dismounted\n", fs->fs_fsmnt); } else { kprintf( "WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n", fs->fs_fsmnt); error = EPERM; goto error_1; } } /* check to see if we need to start softdep */ if (fs->fs_flags & FS_DOSOFTDEP) { error = softdep_mount(devvp, mp, fs); if (error) goto error_1; } ronly = 0; } /* * Soft updates is incompatible with "async", * so if we are doing softupdates stop the user * from setting the async flag in an update. * Softdep_mount() clears it in an initial mount * or ro->rw remount. */ if (mp->mnt_flag & MNT_SOFTDEP) { mp->mnt_flag &= ~MNT_ASYNC; } /* if not updating name...*/ if (args.fspec == 0) { /* * Process export requests. Jumping to "success" * will return the vfs_export() error code. */ error = vfs_export(mp, &ump->um_export, &args.export); goto success; }
/* * mp - path - addr in user space of mount point (ie /usr or whatever) * data - addr in user space of mount params including the name of the block * special file to treat as a filesystem. */ static int msdosfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) { struct vnode *devvp; /* vnode for blk device to mount */ struct msdosfs_args args; /* will hold data from mount request */ /* msdosfs specific mount control block */ struct msdosfsmount *pmp = NULL; size_t size; int error, flags; mode_t accessmode; struct nlookupdata nd; error = copyin(data, (caddr_t)&args, sizeof(struct msdosfs_args)); if (error) return (error); if (args.magic != MSDOSFS_ARGSMAGIC) args.flags = 0; /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { pmp = VFSTOMSDOSFS(mp); error = 0; if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_flag & MNT_RDONLY)) { flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; error = vflush(mp, 0, flags); if (error == 0) { devvp = pmp->pm_devvp; VOP_OPEN(devvp, FREAD, FSCRED, NULL); VOP_CLOSE(devvp, FREAD|FWRITE); pmp->pm_flags |= MSDOSFSMNT_RONLY; } } if (!error && (mp->mnt_flag & MNT_RELOAD)) /* not yet implemented */ error = EOPNOTSUPP; if (error) return (error); if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ devvp = pmp->pm_devvp; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); if (cred->cr_uid != 0) { error = VOP_EACCESS(devvp, VREAD | VWRITE, cred); if (error) { vn_unlock(devvp); return (error); } } VOP_OPEN(devvp, FREAD|FWRITE, FSCRED, NULL); VOP_CLOSE(devvp, FREAD); vn_unlock(devvp); pmp->pm_flags &= ~MSDOSFSMNT_RONLY; } if (args.fspec == 0) { #ifdef __notyet__ /* doesn't work correctly with current mountd XXX */ if (args.flags & MSDOSFSMNT_MNTOPT) { pmp->pm_flags &= ~MSDOSFSMNT_MNTOPT; pmp->pm_flags |= args.flags & MSDOSFSMNT_MNTOPT; if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; } #endif /* * Process export requests. */ return (vfs_export(mp, &pmp->pm_export, &args.export)); } }
/* * VFS Operations. * * mount system call * * Parameters: * data: this is actually a (struct ext2_args *) */ static int ext2_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) { struct vnode *devvp; struct ext2_args args; struct ext2mount *ump = NULL; struct ext2_sb_info *fs; size_t size; int error, flags; mode_t accessmode; struct nlookupdata nd; if ((error = copyin(data, (caddr_t)&args, sizeof (struct ext2_args))) != 0) return (error); /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { ump = VFSTOEXT2(mp); fs = ump->um_e2fs; devvp = ump->um_devvp; error = 0; if (fs->s_rd_only == 0 && (mp->mnt_flag & MNT_RDONLY)) { flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; if (vfs_busy(mp, LK_NOWAIT)) return (EBUSY); error = ext2_flushfiles(mp, flags); vfs_unbusy(mp); if (!error && fs->s_wasvalid) { fs->s_es->s_state |= EXT2_VALID_FS; ext2_sbupdate(ump, MNT_WAIT); } fs->s_rd_only = 1; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); VOP_OPEN(devvp, FREAD, FSCRED, NULL); VOP_CLOSE(devvp, FREAD|FWRITE); vn_unlock(devvp); } if (!error && (mp->mnt_flag & MNT_RELOAD)) error = ext2_reload(mp, cred); if (error) return (error); if (ext2_check_sb_compat(fs->s_es, devvp->v_rdev, (mp->mnt_kern_flag & MNTK_WANTRDWR) == 0) != 0) return (EPERM); if (fs->s_rd_only && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ if (cred->cr_uid != 0) { vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_EACCESS(devvp, VREAD | VWRITE, cred); if (error) { vn_unlock(devvp); return (error); } vn_unlock(devvp); } if ((fs->s_es->s_state & EXT2_VALID_FS) == 0 || (fs->s_es->s_state & EXT2_ERROR_FS)) { if (mp->mnt_flag & MNT_FORCE) { kprintf( "WARNING: %s was not properly dismounted\n", fs->fs_fsmnt); } else { kprintf( "WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n", fs->fs_fsmnt); return (EPERM); } } fs->s_es->s_state &= ~EXT2_VALID_FS; ext2_sbupdate(ump, MNT_WAIT); fs->s_rd_only = 0; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); VOP_OPEN(devvp, FREAD|FWRITE, FSCRED, NULL); VOP_CLOSE(devvp, FREAD); vn_unlock(devvp); } if (args.fspec == NULL) { /* * Process export requests. */ return (vfs_export(mp, &ump->um_export, &args.export)); } }
/* * How to keep the brain busy ... * Currently lookup routine can make two lookup for vnode. This can be * avoided by reorg the code. * * nwfs_lookup(struct vnode *a_dvp, struct vnode **a_vpp, * struct componentname *a_cnp) */ int nwfs_lookup(struct vop_old_lookup_args *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; int lockparent, wantparent, error = 0, notfound; struct thread *td = cnp->cn_td; 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 & CNP_ISDOTDOT) && (dvp->v_flag & VROOT)) { kprintf("nwfs_lookup: invalid '..'\n"); return EIO; } NCPVNDEBUG("%d '%s' in '%s' id=d\n", nameiop, _name, VTONW(dvp)->n_name/*, VTONW(dvp)->n_name*/); if ((mp->mnt_flag & MNT_RDONLY) && nameiop != NAMEI_LOOKUP) return (EROFS); if ((error = VOP_EACCESS(dvp, VEXEC, cnp->cn_cred))) return (error); lockparent = flags & CNP_LOCKPARENT; wantparent = flags & (CNP_LOCKPARENT | CNP_WANTPARENT); nmp = VFSTONWFS(mp); dnp = VTONW(dvp); /* kprintf("dvp %d:%d:%d\n", (int)mp, (int)dvp->v_flag & VROOT, (int)flags & CNP_ISDOTDOT); */ error = ncp_pathcheck(cnp->cn_nameptr, cnp->cn_namelen, &nmp->m.nls, (nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) && (nmp->m.nls.opt & NWHP_NOSTRICT) == 0); if (error) return ENOENT; error = 0; *vpp = NULLVP; fap = NULL; if (flags & CNP_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 == NAMEI_CREATE || nameiop == NAMEI_RENAME) && wantparent) { if (!lockparent) vn_unlock(dvp); return (EJUSTRETURN); } return ENOENT; }/* else { NCPVNDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum); }*/ /* handle DELETE case ... */ if (nameiop == NAMEI_DELETE) { /* delete last component */ error = VOP_EACCESS(dvp, VWRITE, cnp->cn_cred); 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; if (!lockparent) vn_unlock(dvp); return (0); } if (nameiop == NAMEI_RENAME && wantparent) { error = VOP_EACCESS(dvp, VWRITE, cnp->cn_cred); 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; if (!lockparent) vn_unlock(dvp); return (0); } if (flags & CNP_ISDOTDOT) { vn_unlock(dvp); /* race to get the inode */ error = nwfs_nget(mp, fid, NULL, NULL, &vp); if (error) { vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); return (error); } if (lockparent) { error = vn_lock(dvp, LK_EXCLUSIVE | LK_FAILRECLAIM); if (error) { 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) vn_unlock(dvp); } #if 0 /* XXX MOVE TO NREMOVE */ if ((cnp->cn_flags & CNP_MAKEENTRY)) { VTONW(*vpp)->n_ctime = VTONW(*vpp)->n_vattr.va_ctime.tv_sec; /* XXX */ } #endif return (0); }
/* * When we search a directory the blocks containing directory entries are * read and examined. The directory entries contain information that would * normally be in the inode of a unix filesystem. This means that some of * a directory's contents may also be in memory resident denodes (sort of * an inode). This can cause problems if we are searching while some other * process is modifying a directory. To prevent one process from accessing * incompletely modified directory information we depend upon being the * sole owner of a directory block. bread/brelse provide this service. * This being the case, when a process modifies a directory it must first * acquire the disk block that contains the directory entry to be modified. * Then update the disk block and the denode, and then write the disk block * out to disk. This way disk blocks containing directory entries and in * memory denode's will be in synch. * * msdosfs_lookup(struct vnode *a_dvp, struct vnode **a_vpp, * struct componentname *a_cnp) */ int msdosfs_lookup(struct vop_old_lookup_args *ap) { struct mbnambuf nb; struct vnode *vdp = ap->a_dvp; struct vnode **vpp = ap->a_vpp; struct componentname *cnp = ap->a_cnp; daddr_t bn; int error; int lockparent; int wantparent; int slotcount; int slotoffset = 0; int frcn; u_long cluster; int blkoff; int diroff; int blsize; int isadir; /* ~0 if found direntry is a directory */ u_long scn; /* starting cluster number */ struct vnode *pdp; struct denode *dp; struct denode *tdp; struct msdosfsmount *pmp; struct buf *bp = NULL; struct direntry *dep = NULL; u_char dosfilename[12]; int flags = cnp->cn_flags; int nameiop = cnp->cn_nameiop; int unlen; int wincnt = 1; int chksum = -1; int olddos = 1; cnp->cn_flags &= ~CNP_PDIRUNLOCK; #ifdef MSDOSFS_DEBUG kprintf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr); #endif dp = VTODE(vdp); pmp = dp->de_pmp; *vpp = NULL; lockparent = flags & CNP_LOCKPARENT; wantparent = flags & (CNP_LOCKPARENT | CNP_WANTPARENT); #ifdef MSDOSFS_DEBUG kprintf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n", vdp, dp, dp->de_Attributes); #endif /* * If they are going after the . or .. entry in the root directory, * they won't find it. DOS filesystems don't have them in the root * directory. So, we fake it. deget() is in on this scam too. */ if ((vdp->v_flag & VROOT) && cnp->cn_nameptr[0] == '.' && (cnp->cn_namelen == 1 || (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) { isadir = ATTR_DIRECTORY; scn = MSDOSFSROOT; #ifdef MSDOSFS_DEBUG kprintf("msdosfs_lookup(): looking for . or .. in root directory\n"); #endif cluster = MSDOSFSROOT; blkoff = MSDOSFSROOT_OFS; goto foundroot; } switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename, cnp->cn_namelen, 0, pmp)) { case 0: return (EINVAL); case 1: break; case 2: wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, cnp->cn_namelen, pmp) + 1; break; case 3: olddos = 0; wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, cnp->cn_namelen, pmp) + 1; break; } if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) { wincnt = 1; olddos = 1; } unlen = winLenFixup(cnp->cn_nameptr, cnp->cn_namelen); /* * Suppress search for slots unless creating * file and at end of pathname, in which case * we watch for a place to put the new file in * case it doesn't already exist. */ slotcount = wincnt; if (nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) slotcount = 0; #ifdef MSDOSFS_DEBUG kprintf("msdosfs_lookup(): dos version of filename %s, length %ld\n", dosfilename, cnp->cn_namelen); #endif /* * Search the directory pointed at by vdp for the name pointed at * by cnp->cn_nameptr. */ tdp = NULL; mbnambuf_init(&nb); /* * The outer loop ranges over the clusters that make up the * directory. Note that the root directory is different from all * other directories. It has a fixed number of blocks that are not * part of the pool of allocatable clusters. So, we treat it a * little differently. The root directory starts at "cluster" 0. */ diroff = 0; for (frcn = 0;; frcn++) { error = pcbmap(dp, frcn, &bn, &cluster, &blsize); if (error) { if (error == E2BIG) break; return (error); } error = bread(pmp->pm_devvp, de_bntodoff(pmp, bn), blsize, &bp); if (error) { brelse(bp); return (error); } for (blkoff = 0; blkoff < blsize; blkoff += sizeof(struct direntry), diroff += sizeof(struct direntry)) { dep = (struct direntry *)(bp->b_data + blkoff); /* * If the slot is empty and we are still looking * for an empty then remember this one. If the * slot is not empty then check to see if it * matches what we are looking for. If the slot * has never been filled with anything, then the * remainder of the directory has never been used, * so there is no point in searching it. */ if (dep->deName[0] == SLOT_EMPTY || dep->deName[0] == SLOT_DELETED) { /* * Drop memory of previous long matches */ chksum = -1; mbnambuf_init(&nb); if (slotcount < wincnt) { slotcount++; slotoffset = diroff; } if (dep->deName[0] == SLOT_EMPTY) { brelse(bp); goto notfound; } } else { /* * If there wasn't enough space for our winentries, * forget about the empty space */ if (slotcount < wincnt) slotcount = 0; /* * Check for Win95 long filename entry */ if (dep->deAttributes == ATTR_WIN95) { if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) continue; chksum = win2unixfn(&nb, (struct winentry *)dep, chksum, pmp); continue; } chksum = winChkName(&nb, (const u_char *)cnp->cn_nameptr, unlen, chksum, pmp); if (chksum == -2) { chksum = -1; continue; } /* * Ignore volume labels (anywhere, not just * the root directory). */ if (dep->deAttributes & ATTR_VOLUME) { chksum = -1; continue; } /* * Check for a checksum or name match */ if (chksum != winChksum(dep->deName) && (!olddos || bcmp(dosfilename, dep->deName, 11))) { chksum = -1; continue; } #ifdef MSDOSFS_DEBUG kprintf("msdosfs_lookup(): match blkoff %d, diroff %d\n", blkoff, diroff); #endif /* * Remember where this directory * entry came from for whoever did * this lookup. */ dp->de_fndoffset = diroff; dp->de_fndcnt = wincnt - 1; goto found; } } /* for (blkoff = 0; .... */ /* * Release the buffer holding the directory cluster just * searched. */ brelse(bp); } /* for (frcn = 0; ; frcn++) */ notfound: /* * We hold no disk buffers at this point. */ /* * Fixup the slot description to point to the place where * we might put the new DOS direntry (putting the Win95 * long name entries before that) */ if (!slotcount) { slotcount = 1; slotoffset = diroff; } if (wincnt > slotcount) slotoffset += sizeof(struct direntry) * (wincnt - slotcount); /* * If we get here we didn't find the entry we were looking for. But * that's ok if we are creating or renaming and are at the end of * the pathname and the directory hasn't been removed. */ #ifdef MSDOSFS_DEBUG kprintf("msdosfs_lookup(): op %d, refcnt %ld\n", nameiop, dp->de_refcnt); kprintf(" slotcount %d, slotoffset %d\n", slotcount, slotoffset); #endif if ((nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) && dp->de_refcnt > 0) { /* * Access for write is interpreted as allowing * creation of files in the directory. */ error = VOP_EACCESS(vdp, VWRITE, cnp->cn_cred); if (error) return (error); /* * Return an indication of where the new directory * entry should be put. */ dp->de_fndoffset = slotoffset; dp->de_fndcnt = wincnt - 1; /* * We return with the directory locked, so that * the parameters we set up above will still be * valid if we actually decide to do a direnter(). * We return ni_vp == NULL to indicate that the entry * does not currently exist; we leave a pointer to * the (locked) directory inode in ndp->ni_dvp. * The pathname buffer is saved so that the name * can be obtained later. * * NB - if the directory is unlocked, then this * information cannot be used. */ if (!lockparent) { vn_unlock(vdp); cnp->cn_flags |= CNP_PDIRUNLOCK; } return (EJUSTRETURN); } return (ENOENT); found: /* * NOTE: We still have the buffer with matched directory entry at * this point. */ isadir = dep->deAttributes & ATTR_DIRECTORY; scn = getushort(dep->deStartCluster); if (FAT32(pmp)) { scn |= getushort(dep->deHighClust) << 16; if (scn == pmp->pm_rootdirblk) { /* * There should actually be 0 here. * Just ignore the error. */ scn = MSDOSFSROOT; } } if (isadir) { cluster = scn; if (cluster == MSDOSFSROOT) blkoff = MSDOSFSROOT_OFS; else blkoff = 0; } else if (cluster == MSDOSFSROOT) blkoff = diroff; /* * Now release buf to allow deget to read the entry again. * Reserving it here and giving it to deget could result * in a deadlock. */ brelse(bp); bp = NULL; foundroot: /* * If we entered at foundroot, then we are looking for the . or .. * entry of the filesystems root directory. isadir and scn were * setup before jumping here. And, bp is already null. */ if (FAT32(pmp) && scn == MSDOSFSROOT) scn = pmp->pm_rootdirblk; /* * If deleting, and at end of pathname, return * parameters which can be used to remove file. * If the wantparent flag isn't set, we return only * the directory (in ndp->ni_dvp), otherwise we go * on and lock the inode, being careful with ".". */ if (nameiop == NAMEI_DELETE) { /* * Don't allow deleting the root. */ if (blkoff == MSDOSFSROOT_OFS) return EROFS; /* really? XXX */ /* * Write access to directory required to delete files. */ error = VOP_EACCESS(vdp, VWRITE, cnp->cn_cred); if (error) return (error); /* * Return pointer to current entry in dp->i_offset. * Save directory inode pointer in ndp->ni_dvp for dirremove(). */ if (dp->de_StartCluster == scn && isadir) { /* "." */ vref(vdp); *vpp = vdp; return (0); } error = deget(pmp, cluster, blkoff, &tdp); if (error) return (error); *vpp = DETOV(tdp); if (!lockparent) { vn_unlock(vdp); cnp->cn_flags |= CNP_PDIRUNLOCK; } return (0); } /* * If rewriting (RENAME), return the inode and the * information required to rewrite the present directory * Must get inode of directory entry to verify it's a * regular file, or empty directory. */ if (nameiop == NAMEI_RENAME && wantparent) { if (blkoff == MSDOSFSROOT_OFS) return EROFS; /* really? XXX */ error = VOP_EACCESS(vdp, VWRITE, cnp->cn_cred); if (error) return (error); /* * Careful about locking second inode. * This can only occur if the target is ".". */ if (dp->de_StartCluster == scn && isadir) return (EISDIR); if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) return (error); *vpp = DETOV(tdp); if (!lockparent) { vn_unlock(vdp); cnp->cn_flags |= CNP_PDIRUNLOCK; } return (0); } /* * Step through the translation in the name. We do not `vput' the * directory because we may need it again if a symbolic link * is relative to the current directory. Instead we save it * unlocked as "pdp". We must get the target inode before unlocking * the directory to insure that the inode will not be removed * before we get it. We prevent deadlock by always fetching * inodes from the root, moving down the directory tree. Thus * when following backward pointers ".." we must unlock the * parent directory before getting the requested directory. * There is a potential race condition here if both the current * and parent directories are removed before the VFS_VGET for the * inode associated with ".." returns. We hope that this occurs * infrequently since we cannot avoid this race condition without * implementing a sophisticated deadlock detection algorithm. * Note also that this simple deadlock detection scheme will not * work if the file system has any hard links other than ".." * that point backwards in the directory structure. */ pdp = vdp; if (flags & CNP_ISDOTDOT) { vn_unlock(pdp); cnp->cn_flags |= CNP_PDIRUNLOCK; error = deget(pmp, cluster, blkoff, &tdp); if (error) { vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY); cnp->cn_flags &= ~CNP_PDIRUNLOCK; return (error); } if (lockparent) { error = vn_lock(pdp, LK_EXCLUSIVE | LK_FAILRECLAIM); if (error) { vput(DETOV(tdp)); return (error); } cnp->cn_flags &= ~CNP_PDIRUNLOCK; } *vpp = DETOV(tdp); } else if (dp->de_StartCluster == scn && isadir) { vref(vdp); /* we want ourself, ie "." */ *vpp = vdp; } else { if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) return (error); if (!lockparent) { vn_unlock(pdp); cnp->cn_flags |= CNP_PDIRUNLOCK; } *vpp = DETOV(tdp); } return (0); }
/* * Convert a component of a pathname into a pointer to a locked inode. * This is a very central and rather complicated routine. * If the file system is not maintained in a strict tree hierarchy, * this can result in a deadlock situation (see comments in code below). * * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending * on whether the name is to be looked up, created, renamed, or deleted. * When CREATE, RENAME, or DELETE is specified, information usable in * creating, renaming, or deleting a directory entry may be calculated. * If flag has LOCKPARENT or'ed into it and the target of the pathname * exists, lookup returns both the target and its parent directory locked. * When creating or renaming and LOCKPARENT is specified, the target may * not be ".". When deleting and LOCKPARENT is specified, the target may * be "."., but the caller must check to ensure it does an vrele and vput * instead of two vputs. * * Overall outline of ufs_lookup: * * search for name in directory, to found or notfound * notfound: * if creating, return locked directory, leaving info on available slots * else return error * found: * if at end of path and deleting, return information to allow delete * if at end of path and rewriting (RENAME and LOCKPARENT), lock target * inode and return info to allow rewrite * if not at end, add name to cache; if at end and neither creating * nor deleting, add name to cache * * ext2_lookup(struct vnode *a_dvp, struct vnode **a_vpp, * struct componentname *a_cnp) */ int ext2_lookup(struct vop_old_lookup_args *ap) { struct vnode *vdp; /* vnode for directory being searched */ struct inode *dp; /* inode for directory being searched */ struct buf *bp; /* a buffer of directory entries */ struct ext2_dir_entry_2 *ep; /* the current directory entry */ int entryoffsetinblock; /* offset of ep in bp's buffer */ enum {NONE, COMPACT, FOUND} slotstatus; doff_t slotoffset; /* offset of area with free space */ int slotsize; /* size of area at slotoffset */ int slotfreespace; /* amount of space free in slot */ int slotneeded; /* size of the entry we're seeking */ int numdirpasses; /* strategy for directory search */ doff_t endsearch; /* offset to end directory search */ doff_t prevoff; /* prev entry dp->i_offset */ struct vnode *pdp; /* saved dp during symlink work */ struct vnode *tdp; /* returned by VFS_VGET */ doff_t enduseful; /* pointer past last used dir slot */ u_long bmask; /* block offset mask */ int lockparent; /* 1 => lockparent flag is set */ int wantparent; /* 1 => wantparent or lockparent flag */ int namlen, error; struct vnode **vpp = ap->a_vpp; struct componentname *cnp = ap->a_cnp; struct ucred *cred = cnp->cn_cred; int flags = cnp->cn_flags; int nameiop = cnp->cn_nameiop; int DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->s_blocksize; bp = NULL; slotoffset = -1; *vpp = NULL; vdp = ap->a_dvp; dp = VTOI(vdp); lockparent = flags & CNP_LOCKPARENT; wantparent = flags & (CNP_LOCKPARENT|CNP_WANTPARENT); /* * We now have a segment name to search for, and a directory to search. */ /* * Suppress search for slots unless creating * file and at end of pathname, in which case * we watch for a place to put the new file in * case it doesn't already exist. */ slotstatus = FOUND; slotfreespace = slotsize = slotneeded = 0; if (nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) { slotstatus = NONE; slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen); /* was slotneeded = (sizeof(struct direct) - MAXNAMLEN + cnp->cn_namelen + 3) &~ 3; */ } /* * If there is cached information on a previous search of * this directory, pick up where we last left off. * We cache only lookups as these are the most common * and have the greatest payoff. Caching CREATE has little * benefit as it usually must search the entire directory * to determine that the entry does not exist. Caching the * location of the last DELETE or RENAME has not reduced * profiling time and hence has been removed in the interest * of simplicity. */ bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; if (nameiop != NAMEI_LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) { entryoffsetinblock = 0; dp->i_offset = 0; numdirpasses = 1; } else { dp->i_offset = dp->i_diroff; if ((entryoffsetinblock = dp->i_offset & bmask) && (error = EXT2_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))) return (error); numdirpasses = 2; } prevoff = dp->i_offset; endsearch = roundup(dp->i_size, DIRBLKSIZ); enduseful = 0; searchloop: while (dp->i_offset < endsearch) { /* * If necessary, get the next directory block. */ if ((dp->i_offset & bmask) == 0) { if (bp != NULL) brelse(bp); if ((error = EXT2_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)) != 0) return (error); entryoffsetinblock = 0; } /* * If still looking for a slot, and at a DIRBLKSIZE * boundary, have to start looking for free space again. */ if (slotstatus == NONE && (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { slotoffset = -1; slotfreespace = 0; } /* * Get pointer to next entry. * Full validation checks are slow, so we only check * enough to insure forward progress through the * directory. Complete checks can be run by patching * "dirchk" to be true. */ ep = (struct ext2_dir_entry_2 *) ((char *)bp->b_data + entryoffsetinblock); if (ep->rec_len == 0 || (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) { int i; ext2_dirbad(dp, dp->i_offset, "mangled entry"); i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); dp->i_offset += i; entryoffsetinblock += i; continue; } /* * If an appropriate sized slot has not yet been found, * check to see if one is available. Also accumulate space * in the current block so that we can determine if * compaction is viable. */ if (slotstatus != FOUND) { int size = ep->rec_len; if (ep->inode != 0) size -= EXT2_DIR_REC_LEN(ep->name_len); if (size > 0) { if (size >= slotneeded) { slotstatus = FOUND; slotoffset = dp->i_offset; slotsize = ep->rec_len; } else if (slotstatus == NONE) { slotfreespace += size; if (slotoffset == -1) slotoffset = dp->i_offset; if (slotfreespace >= slotneeded) { slotstatus = COMPACT; slotsize = dp->i_offset + ep->rec_len - slotoffset; } } } } /* * Check for a name match. */ if (ep->inode) { namlen = ep->name_len; if (namlen == cnp->cn_namelen && !bcmp(cnp->cn_nameptr, ep->name, (unsigned)namlen)) { /* * Save directory entry's inode number and * reclen in ndp->ni_ufs area, and release * directory buffer. */ dp->i_ino = ep->inode; dp->i_reclen = ep->rec_len; goto found; } } prevoff = dp->i_offset; dp->i_offset += ep->rec_len; entryoffsetinblock += ep->rec_len; if (ep->inode) enduseful = dp->i_offset; } /* notfound: */ /* * If we started in the middle of the directory and failed * to find our target, we must check the beginning as well. */ if (numdirpasses == 2) { numdirpasses--; dp->i_offset = 0; endsearch = dp->i_diroff; goto searchloop; } if (bp != NULL) brelse(bp); /* * If creating, and at end of pathname and current * directory has not been removed, then can consider * allowing file to be created. */ if ((nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) && dp->i_nlink != 0) { /* * Access for write is interpreted as allowing * creation of files in the directory. */ if ((error = VOP_EACCESS(vdp, VWRITE, cred)) != 0) return (error); /* * Return an indication of where the new directory * entry should be put. If we didn't find a slot, * then set dp->i_count to 0 indicating * that the new slot belongs at the end of the * directory. If we found a slot, then the new entry * can be put in the range from dp->i_offset to * dp->i_offset + dp->i_count. */ if (slotstatus == NONE) { dp->i_offset = roundup(dp->i_size, DIRBLKSIZ); dp->i_count = 0; enduseful = dp->i_offset; } else { dp->i_offset = slotoffset; dp->i_count = slotsize; if (enduseful < slotoffset + slotsize) enduseful = slotoffset + slotsize; } dp->i_endoff = roundup(enduseful, DIRBLKSIZ); dp->i_flag |= IN_CHANGE | IN_UPDATE; /* * We return with the directory locked, so that * the parameters we set up above will still be * valid if we actually decide to do a direnter(). * We return ni_vp == NULL to indicate that the entry * does not currently exist; we leave a pointer to * the (locked) directory inode in ndp->ni_dvp. * The pathname buffer is saved so that the name * can be obtained later. * * NB - if the directory is unlocked, then this * information cannot be used. */ if (!lockparent) vn_unlock(vdp); return (EJUSTRETURN); } return (ENOENT); found: /* * Check that directory length properly reflects presence * of this entry. */ if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->name_len) > dp->i_size) { ext2_dirbad(dp, dp->i_offset, "i_size too small"); dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->name_len); dp->i_flag |= IN_CHANGE | IN_UPDATE; } brelse(bp); /* * Found component in pathname. * If the final component of path name, save information * in the cache as to where the entry was found. */ if (nameiop == NAMEI_LOOKUP) dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1); /* * If deleting, and at end of pathname, return * parameters which can be used to remove file. * If the wantparent flag isn't set, we return only * the directory (in ndp->ni_dvp), otherwise we go * on and lock the inode, being careful with ".". */ if (nameiop == NAMEI_DELETE) { /* * Write access to directory required to delete files. */ if ((error = VOP_EACCESS(vdp, VWRITE, cred)) != 0) return (error); /* * Return pointer to current entry in dp->i_offset, * and distance past previous entry (if there * is a previous entry in this block) in dp->i_count. * Save directory inode pointer in ndp->ni_dvp for dirremove(). */ if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) dp->i_count = 0; else dp->i_count = dp->i_offset - prevoff; if (dp->i_number == dp->i_ino) { vref(vdp); *vpp = vdp; return (0); } if ((error = VFS_VGET(vdp->v_mount, NULL, dp->i_ino, &tdp)) != 0) return (error); /* * If directory is "sticky", then user must own * the directory, or the file in it, else she * may not delete it (unless she's root). This * implements append-only directories. */ if ((dp->i_mode & ISVTX) && cred->cr_uid != 0 && cred->cr_uid != dp->i_uid && VTOI(tdp)->i_uid != cred->cr_uid) { vput(tdp); return (EPERM); } *vpp = tdp; if (!lockparent) vn_unlock(vdp); return (0); } /* * If rewriting (RENAME), return the inode and the * information required to rewrite the present directory * Must get inode of directory entry to verify it's a * regular file, or empty directory. */ if (nameiop == NAMEI_RENAME && wantparent) { if ((error = VOP_EACCESS(vdp, VWRITE, cred)) != 0) return (error); /* * Careful about locking second inode. * This can only occur if the target is ".". */ if (dp->i_number == dp->i_ino) return (EISDIR); if ((error = VFS_VGET(vdp->v_mount, NULL, dp->i_ino, &tdp)) != 0) return (error); *vpp = tdp; if (!lockparent) vn_unlock(vdp); return (0); } /* * Step through the translation in the name. We do not `vput' the * directory because we may need it again if a symbolic link * is relative to the current directory. Instead we save it * unlocked as "pdp". We must get the target inode before unlocking * the directory to insure that the inode will not be removed * before we get it. We prevent deadlock by always fetching * inodes from the root, moving down the directory tree. Thus * when following backward pointers ".." we must unlock the * parent directory before getting the requested directory. * There is a potential race condition here if both the current * and parent directories are removed before the VFS_VGET for the * inode associated with ".." returns. We hope that this occurs * infrequently since we cannot avoid this race condition without * implementing a sophisticated deadlock detection algorithm. * Note also that this simple deadlock detection scheme will not * work if the file system has any hard links other than ".." * that point backwards in the directory structure. */ pdp = vdp; if (flags & CNP_ISDOTDOT) { vn_unlock(pdp); /* race to get the inode */ if ((error = VFS_VGET(vdp->v_mount, NULL, dp->i_ino, &tdp)) != 0) { vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY); return (error); } if (lockparent && (error = vn_lock(pdp, LK_EXCLUSIVE))) { vput(tdp); return (error); } *vpp = tdp; } else if (dp->i_number == dp->i_ino) { vref(vdp); /* we want ourself, ie "." */ *vpp = vdp; } else { if ((error = VFS_VGET(vdp->v_mount, NULL, dp->i_ino, &tdp)) != 0) return (error); if (!lockparent) vn_unlock(pdp); *vpp = tdp; } return (0); }
/* * hpfs_lookup(struct vnode *a_dvp, struct vnode **a_vpp, * struct componentname *a_cnp) */ int hpfs_lookup(struct vop_old_lookup_args *ap) { struct vnode *dvp = ap->a_dvp; 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 & CNP_LOCKPARENT; #if HPFS_DEBUG int wantparent = flags & (CNP_LOCKPARENT | CNP_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 != NAMEI_CREATE && nameiop != NAMEI_DELETE && nameiop != NAMEI_LOOKUP) { kprintf("hpfs_lookup: LOOKUP, DELETE and CREATE are only supported\n"); return (EOPNOTSUPP); } error = VOP_EACCESS(dvp, VEXEC, cred); 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 & CNP_ISDOTDOT) ) { dprintf(("hpfs_lookup(0x%x,...): .. faked (0x%x)\n", dhp->h_no, dhp->h_fn.fn_parent)); VOP__UNLOCK(dvp, 0); error = VFS_VGET(hpmp->hpm_mp, NULL, dhp->h_fn.fn_parent, ap->a_vpp); if (error) { VOP__LOCK(dvp, 0); return(error); } if (lockparent && (error = VOP__LOCK(dvp, 0))) { vput( *(ap->a_vpp) ); return (error); } return (error); } 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 && (nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME)) { if(!lockparent) VOP__UNLOCK(dvp, 0); return (EJUSTRETURN); } return (error); } dprintf(("hpfs_lookup: fnode: 0x%x, CPID: 0x%x\n", dep->de_fnode, dep->de_cpid)); if (nameiop == NAMEI_DELETE) { error = VOP_EACCESS(dvp, VWRITE, cred); 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, NULL, dep->de_fnode, ap->a_vpp); if (error) { kprintf("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) VOP__UNLOCK(dvp, 0); } return (error); }
/* * XXXXX do we need hpfsnode locking inside? * * hpfs_setattr(struct vnode *a_vp, struct vattr *a_vap, struct ucred *a_cred) */ static int hpfs_setattr(struct vop_setattr_args *ap) { struct vnode *vp = ap->a_vp; struct hpfsnode *hp = VTOHP(vp); struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; 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_rmajor != 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) { kprintf("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) { kprintf("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) { kprintf("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 = priv_check_cred(cred, PRIV_VFS_SETATTR, 0)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || (error = VOP_EACCESS(vp, VWRITE, cred)))) 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: kprintf("hpfs_setattr: WRONG v_type\n"); return (EINVAL); } if (vap->va_size < hp->h_fn.fn_size) { #if defined(__DragonFly__) error = vtruncbuf(vp, vap->va_size, DEV_BSIZE); if (error) return (error); #else /* defined(__NetBSD__) */ #error Need alternation for vtruncbuf() #endif error = hpfs_truncate(hp, vap->va_size); if (error) return (error); } else if (vap->va_size > hp->h_fn.fn_size) { #if defined(__DragonFly__) vnode_pager_setsize(vp, vap->va_size); #endif error = hpfs_extend(hp, vap->va_size); if (error) return (error); } } return (0); }