/* * Create a regular file */ int ulfs_create(void *v) { struct vop_create_v3_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap = v; int error; struct vnode *dvp = ap->a_dvp; struct ulfs_lookup_results *ulr; /* XXX should handle this material another way */ ulr = &VTOI(dvp)->i_crap; ULFS_CHECK_CRAPCOUNTER(VTOI(dvp)); fstrans_start(dvp->v_mount, FSTRANS_SHARED); error = ulfs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode), dvp, ulr, ap->a_vpp, ap->a_cnp); if (error) { fstrans_done(dvp->v_mount); return (error); } fstrans_done(dvp->v_mount); VN_KNOTE(dvp, NOTE_WRITE); VOP_UNLOCK(*ap->a_vpp); return (0); }
/* * whiteout vnode call */ int ulfs_whiteout(void *v) { struct vop_whiteout_args /* { struct vnode *a_dvp; struct componentname *a_cnp; int a_flags; } */ *ap = v; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; int error; struct ulfsmount *ump = VFSTOULFS(dvp->v_mount); struct lfs *fs = ump->um_lfs; struct ulfs_lookup_results *ulr; /* XXX should handle this material another way */ ulr = &VTOI(dvp)->i_crap; ULFS_CHECK_CRAPCOUNTER(VTOI(dvp)); error = 0; switch (ap->a_flags) { case LOOKUP: /* 4.4 format directories support whiteout operations */ if (fs->um_maxsymlinklen > 0) return (0); return (EOPNOTSUPP); case CREATE: /* create a new directory whiteout */ fstrans_start(dvp->v_mount, FSTRANS_SHARED); #ifdef DIAGNOSTIC if (fs->um_maxsymlinklen <= 0) panic("ulfs_whiteout: old format filesystem"); #endif error = ulfs_direnter(dvp, ulr, NULL, cnp, ULFS_WINO, LFS_DT_WHT, NULL); break; case DELETE: /* remove an existing directory whiteout */ fstrans_start(dvp->v_mount, FSTRANS_SHARED); #ifdef DIAGNOSTIC if (fs->um_maxsymlinklen <= 0) panic("ulfs_whiteout: old format filesystem"); #endif cnp->cn_flags &= ~DOWHITEOUT; error = ulfs_dirremove(dvp, ulr, NULL, cnp->cn_flags, 0); break; default: panic("ulfs_whiteout: unknown op"); /* NOTREACHED */ } fstrans_done(dvp->v_mount); return (error); }
/* * symlink -- make a symbolic link */ int ulfs_symlink(void *v) { struct vop_symlink_v3_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap = v; struct vnode *vp, **vpp; struct inode *ip; int len, error; struct ulfs_lookup_results *ulr; vpp = ap->a_vpp; /* XXX should handle this material another way */ ulr = &VTOI(ap->a_dvp)->i_crap; ULFS_CHECK_CRAPCOUNTER(VTOI(ap->a_dvp)); fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED); error = ulfs_makeinode(LFS_IFLNK | ap->a_vap->va_mode, ap->a_dvp, ulr, vpp, ap->a_cnp); if (error) goto out; VN_KNOTE(ap->a_dvp, NOTE_WRITE); vp = *vpp; len = strlen(ap->a_target); ip = VTOI(vp); if (len < ip->i_lfs->um_maxsymlinklen) { memcpy((char *)SHORTLINK(ip), ap->a_target, len); ip->i_size = len; DIP_ASSIGN(ip, size, len); uvm_vnp_setsize(vp, ip->i_size); ip->i_flag |= IN_CHANGE | IN_UPDATE; if (vp->v_mount->mnt_flag & MNT_RELATIME) ip->i_flag |= IN_ACCESS; } else error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED | IO_JOURNALLOCKED, ap->a_cnp->cn_cred, NULL, NULL); VOP_UNLOCK(vp); if (error) vrele(vp); out: fstrans_done(ap->a_dvp->v_mount); return (error); }
int ulfs_remove(void *v) { struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap = v; struct vnode *vp, *dvp; struct inode *ip; struct mount *mp; int error; struct ulfs_lookup_results *ulr; vp = ap->a_vp; dvp = ap->a_dvp; ip = VTOI(vp); mp = dvp->v_mount; KASSERT(mp == vp->v_mount); /* XXX Not stable without lock. */ /* XXX should handle this material another way */ ulr = &VTOI(dvp)->i_crap; ULFS_CHECK_CRAPCOUNTER(VTOI(dvp)); fstrans_start(mp, FSTRANS_SHARED); if (vp->v_type == VDIR || (ip->i_flags & (IMMUTABLE | APPEND)) || (VTOI(dvp)->i_flags & APPEND)) error = EPERM; else { error = ulfs_dirremove(dvp, ulr, ip, ap->a_cnp->cn_flags, 0); } VN_KNOTE(vp, NOTE_DELETE); VN_KNOTE(dvp, NOTE_WRITE); if (dvp == vp) vrele(vp); else vput(vp); vput(dvp); fstrans_done(mp); return (error); }
int ulfs_rmdir(void *v) { struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap = v; struct vnode *vp, *dvp; struct componentname *cnp; struct inode *ip, *dp; int error; struct ulfs_lookup_results *ulr; vp = ap->a_vp; dvp = ap->a_dvp; cnp = ap->a_cnp; ip = VTOI(vp); dp = VTOI(dvp); /* XXX should handle this material another way */ ulr = &dp->i_crap; ULFS_CHECK_CRAPCOUNTER(dp); /* * No rmdir "." or of mounted directories please. */ if (dp == ip || vp->v_mountedhere != NULL) { if (dp == ip) vrele(dvp); else vput(dvp); vput(vp); return (EINVAL); } fstrans_start(dvp->v_mount, FSTRANS_SHARED); /* * Do not remove a directory that is in the process of being renamed. * Verify that the directory is empty (and valid). (Rmdir ".." won't * be valid since ".." will contain a reference to the current * directory and thus be non-empty.) */ error = 0; if (ip->i_nlink != 2 || !ulfs_dirempty(ip, dp->i_number, cnp->cn_cred)) { error = ENOTEMPTY; goto out; } if ((dp->i_flags & APPEND) || (ip->i_flags & (IMMUTABLE | APPEND))) { error = EPERM; goto out; } /* * Delete reference to directory before purging * inode. If we crash in between, the directory * will be reattached to lost+found, */ error = ulfs_dirremove(dvp, ulr, ip, cnp->cn_flags, 1); if (error) { goto out; } VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); cache_purge(dvp); /* * Truncate inode. The only stuff left in the directory is "." and * "..". The "." reference is inconsequential since we're quashing * it. */ dp->i_nlink--; DIP_ASSIGN(dp, nlink, dp->i_nlink); dp->i_flag |= IN_CHANGE; ip->i_nlink--; DIP_ASSIGN(ip, nlink, ip->i_nlink); ip->i_flag |= IN_CHANGE; error = lfs_truncate(vp, (off_t)0, IO_SYNC, cnp->cn_cred); cache_purge(vp); #ifdef LFS_DIRHASH if (ip->i_dirhash != NULL) ulfsdirhash_free(ip); #endif out: VN_KNOTE(vp, NOTE_DELETE); vput(vp); fstrans_done(dvp->v_mount); vput(dvp); return (error); }
int ulfs_mkdir(void *v) { struct vop_mkdir_v3_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap = v; struct vnode *dvp = ap->a_dvp, *tvp; struct vattr *vap = ap->a_vap; struct componentname *cnp = ap->a_cnp; struct inode *ip, *dp = VTOI(dvp); struct buf *bp; struct lfs_dirtemplate dirtemplate; struct lfs_direct *newdir; int error, dmode; struct ulfsmount *ump = dp->i_ump; struct lfs *fs = ump->um_lfs; int dirblksiz = fs->um_dirblksiz; struct ulfs_lookup_results *ulr; fstrans_start(dvp->v_mount, FSTRANS_SHARED); /* XXX should handle this material another way */ ulr = &dp->i_crap; ULFS_CHECK_CRAPCOUNTER(dp); if ((nlink_t)dp->i_nlink >= LINK_MAX) { error = EMLINK; goto out; } dmode = vap->va_mode & ACCESSPERMS; dmode |= LFS_IFDIR; /* * Must simulate part of ulfs_makeinode here to acquire the inode, * but not have it entered in the parent directory. The entry is * made later after writing "." and ".." entries. */ if ((error = lfs_valloc(dvp, dmode, cnp->cn_cred, ap->a_vpp)) != 0) goto out; tvp = *ap->a_vpp; ip = VTOI(tvp); ip->i_uid = kauth_cred_geteuid(cnp->cn_cred); DIP_ASSIGN(ip, uid, ip->i_uid); ip->i_gid = dp->i_gid; DIP_ASSIGN(ip, gid, ip->i_gid); #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) if ((error = lfs_chkiq(ip, 1, cnp->cn_cred, 0))) { lfs_vfree(tvp, ip->i_number, dmode); fstrans_done(dvp->v_mount); vput(tvp); return (error); } #endif ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; ip->i_mode = dmode; DIP_ASSIGN(ip, mode, dmode); tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */ ip->i_nlink = 2; DIP_ASSIGN(ip, nlink, 2); if (cnp->cn_flags & ISWHITEOUT) { ip->i_flags |= UF_OPAQUE; DIP_ASSIGN(ip, flags, ip->i_flags); } /* * Bump link count in parent directory to reflect work done below. * Should be done before reference is created so cleanup is * possible if we crash. */ dp->i_nlink++; DIP_ASSIGN(dp, nlink, dp->i_nlink); dp->i_flag |= IN_CHANGE; if ((error = lfs_update(dvp, NULL, NULL, UPDATE_DIROP)) != 0) goto bad; /* * Initialize directory with "." and ".." from static template. */ dirtemplate = mastertemplate; dirtemplate.dotdot_reclen = dirblksiz - dirtemplate.dot_reclen; dirtemplate.dot_ino = ulfs_rw32(ip->i_number, ULFS_MPNEEDSWAP(fs)); dirtemplate.dotdot_ino = ulfs_rw32(dp->i_number, ULFS_MPNEEDSWAP(fs)); dirtemplate.dot_reclen = ulfs_rw16(dirtemplate.dot_reclen, ULFS_MPNEEDSWAP(fs)); dirtemplate.dotdot_reclen = ulfs_rw16(dirtemplate.dotdot_reclen, ULFS_MPNEEDSWAP(fs)); if (fs->um_maxsymlinklen <= 0) { #if BYTE_ORDER == LITTLE_ENDIAN if (ULFS_MPNEEDSWAP(fs) == 0) #else if (ULFS_MPNEEDSWAP(fs) != 0) #endif { dirtemplate.dot_type = dirtemplate.dot_namlen; dirtemplate.dotdot_type = dirtemplate.dotdot_namlen; dirtemplate.dot_namlen = dirtemplate.dotdot_namlen = 0; } else dirtemplate.dot_type = dirtemplate.dotdot_type = 0; } if ((error = lfs_balloc(tvp, (off_t)0, dirblksiz, cnp->cn_cred, B_CLRBUF, &bp)) != 0) goto bad; ip->i_size = dirblksiz; DIP_ASSIGN(ip, size, dirblksiz); ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; uvm_vnp_setsize(tvp, ip->i_size); memcpy((void *)bp->b_data, (void *)&dirtemplate, sizeof dirtemplate); /* * Directory set up, now install it's entry in the parent directory. * We must write out the buffer containing the new directory body * before entering the new name in the parent. */ if ((error = VOP_BWRITE(bp->b_vp, bp)) != 0) goto bad; if ((error = lfs_update(tvp, NULL, NULL, UPDATE_DIROP)) != 0) { goto bad; } newdir = pool_cache_get(ulfs_direct_cache, PR_WAITOK); ulfs_makedirentry(ip, cnp, newdir); error = ulfs_direnter(dvp, ulr, tvp, newdir, cnp, bp); pool_cache_put(ulfs_direct_cache, newdir); bad: if (error == 0) { VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); VOP_UNLOCK(tvp); } else { dp->i_nlink--; DIP_ASSIGN(dp, nlink, dp->i_nlink); dp->i_flag |= IN_CHANGE; /* * No need to do an explicit lfs_truncate here, vrele will * do this for us because we set the link count to 0. */ ip->i_nlink = 0; DIP_ASSIGN(ip, nlink, 0); ip->i_flag |= IN_CHANGE; /* If IN_ADIROP, account for it */ lfs_unmark_vnode(tvp); vput(tvp); } out: fstrans_done(dvp->v_mount); return (error); }
/* * ulfs_link: create hard link. */ int ulfs_link(void *v) { struct vop_link_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap = v; struct vnode *dvp = ap->a_dvp; struct vnode *vp = ap->a_vp; struct componentname *cnp = ap->a_cnp; struct inode *ip; struct lfs_direct *newdir; int error; struct ulfs_lookup_results *ulr; KASSERT(dvp != vp); KASSERT(vp->v_type != VDIR); KASSERT(dvp->v_mount == vp->v_mount); /* XXX should handle this material another way */ ulr = &VTOI(dvp)->i_crap; ULFS_CHECK_CRAPCOUNTER(VTOI(dvp)); fstrans_start(dvp->v_mount, FSTRANS_SHARED); error = vn_lock(vp, LK_EXCLUSIVE); if (error) { VOP_ABORTOP(dvp, cnp); goto out2; } ip = VTOI(vp); if ((nlink_t)ip->i_nlink >= LINK_MAX) { VOP_ABORTOP(dvp, cnp); error = EMLINK; goto out1; } if (ip->i_flags & (IMMUTABLE | APPEND)) { VOP_ABORTOP(dvp, cnp); error = EPERM; goto out1; } ip->i_nlink++; DIP_ASSIGN(ip, nlink, ip->i_nlink); ip->i_flag |= IN_CHANGE; error = lfs_update(vp, NULL, NULL, UPDATE_DIROP); if (!error) { newdir = pool_cache_get(ulfs_direct_cache, PR_WAITOK); ulfs_makedirentry(ip, cnp, newdir); error = ulfs_direnter(dvp, ulr, vp, newdir, cnp, NULL); pool_cache_put(ulfs_direct_cache, newdir); } if (error) { ip->i_nlink--; DIP_ASSIGN(ip, nlink, ip->i_nlink); ip->i_flag |= IN_CHANGE; } out1: VOP_UNLOCK(vp); out2: VN_KNOTE(vp, NOTE_LINK); VN_KNOTE(dvp, NOTE_WRITE); vput(dvp); fstrans_done(dvp->v_mount); return (error); }
/* * ulfs_link: create hard link. */ int ulfs_link(void *v) { struct vop_link_v2_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap = v; struct vnode *dvp = ap->a_dvp; struct vnode *vp = ap->a_vp; struct componentname *cnp = ap->a_cnp; struct mount *mp = dvp->v_mount; struct inode *ip; int error; struct ulfs_lookup_results *ulr; KASSERT(dvp != vp); KASSERT(vp->v_type != VDIR); KASSERT(mp == vp->v_mount); /* XXX Not stable without lock. */ /* XXX should handle this material another way */ ulr = &VTOI(dvp)->i_crap; ULFS_CHECK_CRAPCOUNTER(VTOI(dvp)); fstrans_start(mp, FSTRANS_SHARED); error = vn_lock(vp, LK_EXCLUSIVE); if (error) { VOP_ABORTOP(dvp, cnp); goto out2; } ip = VTOI(vp); if ((nlink_t)ip->i_nlink >= LINK_MAX) { VOP_ABORTOP(dvp, cnp); error = EMLINK; goto out1; } if (ip->i_flags & (IMMUTABLE | APPEND)) { VOP_ABORTOP(dvp, cnp); error = EPERM; goto out1; } ip->i_nlink++; DIP_ASSIGN(ip, nlink, ip->i_nlink); ip->i_flag |= IN_CHANGE; error = lfs_update(vp, NULL, NULL, UPDATE_DIROP); if (!error) { error = ulfs_direnter(dvp, ulr, vp, cnp, ip->i_number, LFS_IFTODT(ip->i_mode), NULL); } if (error) { ip->i_nlink--; DIP_ASSIGN(ip, nlink, ip->i_nlink); ip->i_flag |= IN_CHANGE; } out1: VOP_UNLOCK(vp); out2: VN_KNOTE(vp, NOTE_LINK); VN_KNOTE(dvp, NOTE_WRITE); fstrans_done(mp); return (error); }