#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ext2fs_lookup.c,v 1.95 2024/09/08 09:36:52 rillig Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/buf.h>
#include <sys/file.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/kmem.h>
#include <sys/malloc.h>
#include <sys/dirent.h>
#include <sys/kauth.h>
#include <sys/proc.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/ext2fs/ext2fs_extern.h>
#include <ufs/ext2fs/ext2fs_dir.h>
#include <ufs/ext2fs/ext2fs.h>
#include <ufs/ext2fs/ext2fs_htree.h>
#include <miscfs/genfs/genfs.h>
extern int dirchk;
static void ext2fs_dirconv2ffs(struct m_ext2fs *fs,
struct ext2fs_direct *e2dir,
struct dirent *ffsdir);
static int ext2fs_dirbadentry(struct vnode *dp,
struct ext2fs_direct *de,
int entryoffsetinblock);
static void
ext2fs_dirconv2ffs(struct m_ext2fs *fs, struct ext2fs_direct *e2dir, struct dirent *ffsdir)
{
memset(ffsdir, 0, sizeof(struct dirent));
ffsdir->d_fileno = fs2h32(e2dir->e2d_ino);
ffsdir->d_namlen = e2dir->e2d_namlen;
if (EXT2F_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_FTYPE))
ffsdir->d_type = ext2dt2dt(e2dir->e2d_type);
else
ffsdir->d_type = DT_UNKNOWN;
#ifdef DIAGNOSTIC
#if MAXNAMLEN < E2FS_MAXNAMLEN
if (e2dir->e2d_namlen > MAXNAMLEN)
panic("ext2fs: e2dir->e2d_namlen");
#endif
#endif
strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen);
ffsdir->d_reclen = _DIRENT_SIZE(ffsdir);
}
static int
ext2fs_is_dot_entry(struct componentname *cnp)
{
return cnp->cn_namelen <= 2 && cnp->cn_nameptr[0] == '.' &&
(cnp->cn_nameptr[1] == '.' || cnp->cn_nameptr[1] == '\0');
}
int
ext2fs_readdir(void *v)
{
struct vop_readdir_args
*ap = v;
struct uio *uio = ap->a_uio;
int error;
size_t e2fs_count, readcnt;
struct vnode *vp = ap->a_vp;
struct m_ext2fs *fs = VTOI(vp)->i_e2fs;
struct ext2fs_direct *dp;
struct dirent *dstd;
struct uio auio;
struct iovec aiov;
void *dirbuf;
off_t off = uio->uio_offset;
off_t *cookies = NULL;
int nc = 0, ncookies = 0;
int e2d_reclen;
if (vp->v_type != VDIR)
return ENOTDIR;
e2fs_count = uio->uio_resid;
e2fs_count -= (uio->uio_offset + e2fs_count) & (fs->e2fs_bsize -1);
if (e2fs_count <= 0)
return EINVAL;
auio = *uio;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
aiov.iov_len = e2fs_count;
auio.uio_resid = e2fs_count;
UIO_SETUP_SYSSPACE(&auio);
dirbuf = kmem_alloc(e2fs_count, KM_SLEEP);
dstd = kmem_zalloc(sizeof(struct dirent), KM_SLEEP);
if (ap->a_ncookies) {
nc = e2fs_count / _DIRENT_MINSIZE((struct dirent *)0);
ncookies = nc;
cookies = malloc(sizeof (off_t) * ncookies, M_TEMP, M_WAITOK);
*ap->a_cookies = cookies;
}
aiov.iov_base = dirbuf;
error = UFS_BUFRD(ap->a_vp, &auio, 0, ap->a_cred);
if (error == 0) {
readcnt = e2fs_count - auio.uio_resid;
for (dp = (struct ext2fs_direct *)dirbuf;
(char *)dp < (char *)dirbuf + readcnt;) {
e2d_reclen = fs2h16(dp->e2d_reclen);
if (e2d_reclen == 0) {
error = EIO;
break;
}
ext2fs_dirconv2ffs(fs, dp, dstd);
if(dstd->d_reclen > uio->uio_resid) {
break;
}
error = uiomove(dstd, dstd->d_reclen, uio);
if (error != 0) {
break;
}
off = off + e2d_reclen;
if (cookies != NULL) {
*cookies++ = off;
if (--ncookies <= 0){
break;
}
}
dp = (struct ext2fs_direct *) ((char *)dp + e2d_reclen);
}
uio->uio_offset = off;
}
kmem_free(dirbuf, e2fs_count);
kmem_free(dstd, sizeof(*dstd));
*ap->a_eofflag = ext2fs_size(VTOI(ap->a_vp)) <= uio->uio_offset;
if (ap->a_ncookies) {
if (error) {
free(*ap->a_cookies, M_TEMP);
*ap->a_ncookies = 0;
*ap->a_cookies = NULL;
} else
*ap->a_ncookies = nc - ncookies;
}
return error;
}
int
ext2fs_lookup(void *v)
{
struct vop_lookup_v2_args
*ap = v;
struct vnode *vdp = ap->a_dvp;
struct inode *dp = VTOI(vdp);
struct buf *bp;
struct ext2fs_direct *ep;
int entryoffsetinblock;
enum ext2fs_slotstatus slotstatus;
doff_t slotoffset;
int slotsize;
int slotfreespace;
int slotneeded;
int numdirpasses;
doff_t endsearch;
doff_t prevoff;
struct vnode *tdp;
doff_t enduseful;
u_long bmask;
int namlen, error;
struct vnode **vpp = ap->a_vpp;
struct componentname *cnp = ap->a_cnp;
kauth_cred_t cred = cnp->cn_cred;
int flags;
int nameiop = cnp->cn_nameiop;
struct ufsmount *ump = dp->i_ump;
int dirblksiz = ump->um_dirblksiz;
ino_t foundino;
struct ufs_lookup_results *results;
flags = cnp->cn_flags;
bp = NULL;
slotoffset = -1;
*vpp = NULL;
if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0)
return error;
if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
return EROFS;
if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen,
cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) {
return *vpp == NULLVP ? ENOENT : 0;
}
if (VOP_ISLOCKED(vdp) != LK_EXCLUSIVE)
return ENOLCK;
results = &dp->i_crap;
dp->i_crapcounter++;
slotstatus = FOUND;
slotfreespace = slotsize = slotneeded = 0;
if ((nameiop == CREATE || nameiop == RENAME) &&
(flags & ISLASTCN)) {
slotstatus = NONE;
slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen);
}
bmask = vdp->v_mount->mnt_stat.f_iosize - 1;
if (nameiop != LOOKUP || results->ulr_diroff == 0 ||
results->ulr_diroff >= ext2fs_size(dp)) {
entryoffsetinblock = 0;
results->ulr_offset = 0;
numdirpasses = 1;
} else {
results->ulr_offset = results->ulr_diroff;
if ((entryoffsetinblock = results->ulr_offset & bmask) &&
(error = ext2fs_blkatoff(vdp, (off_t)results->ulr_offset, NULL, &bp)))
return error;
numdirpasses = 2;
namecache_count_2passes();
}
prevoff = results->ulr_offset;
endsearch = roundup(ext2fs_size(dp), dirblksiz);
enduseful = 0;
if (!ext2fs_is_dot_entry(cnp) && ext2fs_htree_has_idx(dp)) {
doff_t i_offset;
struct ext2fs_searchslot ss;
numdirpasses = 1;
entryoffsetinblock = 0;
int htree_lookup_ret = ext2fs_htree_lookup(dp, cnp->cn_nameptr,
cnp->cn_namelen, &bp, &entryoffsetinblock, &i_offset,
&prevoff, &enduseful, &ss);
switch (htree_lookup_ret) {
case 0:
ep = (void *)((char *)bp->b_data + (i_offset & bmask));
foundino = ep->e2d_ino;
goto found;
case ENOENT:
i_offset = roundup2(dp->i_size, dp->i_e2fs->e2fs_bsize);
goto notfound;
default:
break;
}
}
searchloop:
while (results->ulr_offset < endsearch) {
preempt_point();
if ((results->ulr_offset & bmask) == 0) {
if (bp != NULL)
brelse(bp, 0);
error = ext2fs_blkatoff(vdp, (off_t)results->ulr_offset,
NULL, &bp);
if (error != 0)
return error;
entryoffsetinblock = 0;
}
if (slotstatus == NONE &&
(entryoffsetinblock & (dirblksiz - 1)) == 0) {
slotoffset = -1;
slotfreespace = 0;
}
KASSERT(bp != NULL);
ep = (struct ext2fs_direct *)
((char *)bp->b_data + entryoffsetinblock);
if (ep->e2d_reclen == 0 ||
(dirchk &&
ext2fs_dirbadentry(vdp, ep, entryoffsetinblock))) {
int i;
ufs_dirbad(dp, results->ulr_offset, "mangled entry");
i = dirblksiz - (entryoffsetinblock & (dirblksiz - 1));
results->ulr_offset += i;
entryoffsetinblock += i;
continue;
}
if (slotstatus != FOUND) {
int size = fs2h16(ep->e2d_reclen);
if (ep->e2d_ino != 0)
size -= EXT2FS_DIRSIZ(ep->e2d_namlen);
if (size > 0) {
if (size >= slotneeded) {
slotstatus = FOUND;
slotoffset = results->ulr_offset;
slotsize = fs2h16(ep->e2d_reclen);
} else if (slotstatus == NONE) {
slotfreespace += size;
if (slotoffset == -1)
slotoffset = results->ulr_offset;
if (slotfreespace >= slotneeded) {
slotstatus = COMPACT;
slotsize = results->ulr_offset +
fs2h16(ep->e2d_reclen) -
slotoffset;
}
}
}
}
if (ep->e2d_ino) {
namlen = ep->e2d_namlen;
if (namlen == cnp->cn_namelen &&
!memcmp(cnp->cn_nameptr, ep->e2d_name,
(unsigned)namlen)) {
foundino = fs2h32(ep->e2d_ino);
results->ulr_reclen = fs2h16(ep->e2d_reclen);
goto found;
}
}
prevoff = results->ulr_offset;
results->ulr_offset += fs2h16(ep->e2d_reclen);
entryoffsetinblock += fs2h16(ep->e2d_reclen);
if (ep->e2d_ino)
enduseful = results->ulr_offset;
}
notfound:
if (numdirpasses == 2) {
numdirpasses--;
results->ulr_offset = 0;
endsearch = results->ulr_diroff;
goto searchloop;
}
if (bp != NULL)
brelse(bp, 0);
if ((nameiop == CREATE || nameiop == RENAME) &&
(flags & ISLASTCN) && dp->i_e2fs_nlink != 0) {
error = VOP_ACCESS(vdp, VWRITE, cred);
if (error)
return error;
if (slotstatus == NONE) {
results->ulr_offset = roundup(ext2fs_size(dp), dirblksiz);
results->ulr_count = 0;
enduseful = results->ulr_offset;
} else {
results->ulr_offset = slotoffset;
results->ulr_count = slotsize;
if (enduseful < slotoffset + slotsize)
enduseful = slotoffset + slotsize;
}
results->ulr_endoff = roundup(enduseful, dirblksiz);
#if 0
dp->i_flag |= IN_CHANGE | IN_UPDATE;
#endif
return EJUSTRETURN;
}
if (nameiop != CREATE) {
cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen,
cnp->cn_flags);
}
return ENOENT;
found:
if (numdirpasses == 2)
namecache_count_pass2();
if (results->ulr_offset + EXT2FS_DIRSIZ(ep->e2d_namlen) > ext2fs_size(dp)) {
ufs_dirbad(dp, results->ulr_offset, "i_size too small");
error = ext2fs_setsize(dp,
results->ulr_offset + EXT2FS_DIRSIZ(ep->e2d_namlen));
if (error) {
brelse(bp, 0);
return error;
}
dp->i_flag |= IN_CHANGE | IN_UPDATE;
uvm_vnp_setsize(vdp, ext2fs_size(dp));
}
brelse(bp, 0);
if ((flags & ISLASTCN) && nameiop == LOOKUP)
results->ulr_diroff = results->ulr_offset &~ (dirblksiz - 1);
if (nameiop == DELETE && (flags & ISLASTCN)) {
if ((results->ulr_offset & (dirblksiz - 1)) == 0)
results->ulr_count = 0;
else
results->ulr_count = results->ulr_offset - prevoff;
if (dp->i_number == foundino) {
vref(vdp);
tdp = vdp;
} else {
error = vcache_get(vdp->v_mount,
&foundino, sizeof(foundino), &tdp);
if (error)
return error;
}
if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0) {
vrele(tdp);
return error;
}
if (dp->i_e2fs_mode & ISVTX) {
error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE,
tdp, vdp, genfs_can_sticky(vdp, cred, dp->i_uid,
VTOI(tdp)->i_uid));
if (error) {
vrele(tdp);
return EPERM;
}
}
*vpp = tdp;
return 0;
}
if (nameiop == RENAME && (flags & ISLASTCN)) {
error = VOP_ACCESS(vdp, VWRITE, cred);
if (error)
return error;
if (dp->i_number == foundino)
return EISDIR;
error = vcache_get(vdp->v_mount,
&foundino, sizeof(foundino), &tdp);
if (error)
return error;
*vpp = tdp;
return 0;
}
if (dp->i_number == foundino) {
vref(vdp);
*vpp = vdp;
} else {
error = vcache_get(vdp->v_mount,
&foundino, sizeof(foundino), &tdp);
if (error)
return error;
*vpp = tdp;
}
cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags);
return 0;
}
static void
ext2fs_accumulatespace (struct ext2fs_searchslot *ssp, struct ext2fs_direct *ep,
doff_t *offp)
{
int size = ep->e2d_reclen;
if (ep->e2d_ino != 0)
size -= EXT2_DIR_REC_LEN(ep->e2d_namlen);
if (size <= 0)
return;
if (size >= ssp->slotneeded) {
ssp->slotstatus = FOUND;
ssp->slotoffset = *offp;
ssp->slotsize = ep->e2d_reclen;
return;
}
if (ssp->slotstatus != NONE)
return;
ssp->slotfreespace += size;
if (ssp->slotoffset == -1)
ssp->slotoffset = *offp;
if (ssp->slotfreespace >= ssp->slotneeded) {
ssp->slotstatus = COMPACT;
ssp->slotsize = *offp + ep->e2d_reclen - ssp->slotoffset;
}
}
int
ext2fs_search_dirblock(struct inode *ip, void *data, int *foundp,
const char *name, int namelen, int *entryoffsetinblockp,
doff_t *offp, doff_t *prevoffp, doff_t *endusefulp,
struct ext2fs_searchslot *ssp)
{
struct vnode *vdp = ITOV(ip);
struct ext2fs_direct *ep, *top;
uint32_t bsize = ip->i_e2fs->e2fs_bsize;
int offset = *entryoffsetinblockp;
int namlen;
ep = (void *)((char *)data + offset);
top = (void *)((char *)data + bsize - EXT2_DIR_REC_LEN(0));
while (ep < top) {
if (ep->e2d_reclen == 0 ||
(dirchk && ext2fs_dirbadentry(vdp, ep, offset))) {
int i;
ufs_dirbad(ip, *offp, "mangled entry");
i = bsize - (offset & (bsize - 1));
*offp += i;
offset += i;
continue;
}
if (ssp->slotstatus != FOUND)
ext2fs_accumulatespace(ssp, ep, offp);
if (ep->e2d_ino) {
namlen = ep->e2d_namlen;
if (namlen == namelen &&
!memcmp(name, ep->e2d_name, (unsigned)namlen)) {
*foundp = 1;
return 0;
}
}
*prevoffp = *offp;
*offp += ep->e2d_reclen;
offset += ep->e2d_reclen;
*entryoffsetinblockp = offset;
if (ep->e2d_ino)
*endusefulp = *offp;
ep = (void *)((char *)data + offset);
}
return 0;
}
static int
ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de,
int entryoffsetinblock)
{
struct ufsmount *ump = VFSTOUFS(dp->v_mount);
int dirblksiz = ump->um_dirblksiz;
const char *error_msg = NULL;
int reclen = fs2h16(de->e2d_reclen);
int namlen = de->e2d_namlen;
if (reclen < EXT2FS_DIRSIZ(1))
error_msg = "rec_len is smaller than minimal";
else if (reclen % 4 != 0)
error_msg = "rec_len % 4 != 0";
else if (namlen > EXT2FS_MAXNAMLEN)
error_msg = "namlen > EXT2FS_MAXNAMLEN";
else if (reclen < EXT2FS_DIRSIZ(namlen))
error_msg = "reclen is too small for name_len";
else if (entryoffsetinblock + reclen > dirblksiz)
error_msg = "directory entry across blocks";
else if (fs2h32(de->e2d_ino) >
VTOI(dp)->i_e2fs->e2fs.e2fs_icount)
error_msg = "inode out of bounds";
if (error_msg != NULL) {
printf( "bad directory entry: %s\n"
"offset=%d, inode=%lu, rec_len=%d, name_len=%d \n",
error_msg, entryoffsetinblock,
(unsigned long) fs2h32(de->e2d_ino),
reclen, namlen);
panic("ext2fs_dirbadentry");
}
return error_msg == NULL ? 0 : 1;
}
int
ext2fs_direnter(struct inode *ip, struct vnode *dvp,
const struct ufs_lookup_results *ulr, struct componentname *cnp)
{
struct inode *dp;
struct ext2fs_direct newdir;
struct iovec aiov;
struct uio auio;
int error;
struct ufsmount *ump = VFSTOUFS(dvp->v_mount);
int dirblksiz = ump->um_dirblksiz;
size_t newentrysize;
dp = VTOI(dvp);
newdir.e2d_ino = h2fs32(ip->i_number);
newdir.e2d_namlen = cnp->cn_namelen;
if (EXT2F_HAS_INCOMPAT_FEATURE(ip->i_e2fs, EXT2F_INCOMPAT_FTYPE)) {
newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
} else {
newdir.e2d_type = 0;
}
memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1);
newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen);
if (ext2fs_htree_has_idx(dp)) {
error = ext2fs_htree_add_entry(dvp, &newdir, cnp, newentrysize);
if (error) {
dp->i_e2fs_flags &= ~EXT2_INDEX;
dp->i_flag |= IN_CHANGE | IN_UPDATE;
}
return error;
}
if (ulr->ulr_count == 0) {
if (ulr->ulr_offset & (dirblksiz - 1))
panic("ext2fs_direnter: newblk");
auio.uio_offset = ulr->ulr_offset;
newdir.e2d_reclen = h2fs16(dirblksiz);
auio.uio_resid = newentrysize;
aiov.iov_len = newentrysize;
aiov.iov_base = (void *)&newdir;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_rw = UIO_WRITE;
UIO_SETUP_SYSSPACE(&auio);
error = ext2fs_bufwr(dvp, &auio, IO_SYNC, cnp->cn_cred);
if (dirblksiz > dvp->v_mount->mnt_stat.f_bsize)
panic("ext2fs_direnter: frag size");
else if (!error) {
error = ext2fs_setsize(dp,
roundup(ext2fs_size(dp), dirblksiz));
if (error)
return error;
dp->i_flag |= IN_CHANGE;
uvm_vnp_setsize(dvp, ext2fs_size(dp));
}
return error;
}
error = ext2fs_add_entry(dvp, &newdir, ulr, newentrysize);
if (!error && ulr->ulr_endoff && ulr->ulr_endoff < ext2fs_size(dp))
error = ext2fs_truncate(dvp, (off_t)ulr->ulr_endoff, IO_SYNC,
cnp->cn_cred);
return error;
}
int
ext2fs_add_entry(struct vnode* dvp, struct ext2fs_direct *entry,
const struct ufs_lookup_results *ulr, size_t newentrysize)
{
struct ext2fs_direct *ep, *nep;
struct inode *dp;
struct buf *bp;
u_int dsize;
int error, loc, spacefree;
char *dirbuf;
dp = VTOI(dvp);
if ((error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset, &dirbuf, &bp)) != 0)
return error;
ep = (struct ext2fs_direct *)dirbuf;
dsize = EXT2FS_DIRSIZ(ep->e2d_namlen);
spacefree = fs2h16(ep->e2d_reclen) - dsize;
for (loc = fs2h16(ep->e2d_reclen); loc < ulr->ulr_count;) {
nep = (struct ext2fs_direct *)(dirbuf + loc);
if (ep->e2d_ino) {
ep->e2d_reclen = h2fs16(dsize);
ep = (struct ext2fs_direct *)((char *)ep + dsize);
} else {
spacefree += dsize;
}
dsize = EXT2FS_DIRSIZ(nep->e2d_namlen);
spacefree += fs2h16(nep->e2d_reclen) - dsize;
loc += fs2h16(nep->e2d_reclen);
memcpy((void *)ep, (void *)nep, dsize);
}
if (ep->e2d_ino == 0) {
#ifdef DIAGNOSTIC
if (spacefree + dsize < newentrysize)
panic("ext2fs_direnter: compact1");
#endif
entry->e2d_reclen = h2fs16(spacefree + dsize);
} else {
#ifdef DIAGNOSTIC
if (spacefree < newentrysize) {
printf("ext2fs_direnter: compact2 %u %u",
(u_int)spacefree, (u_int)newentrysize);
panic("ext2fs_direnter: compact2");
}
#endif
entry->e2d_reclen = h2fs16(spacefree);
ep->e2d_reclen = h2fs16(dsize);
ep = (struct ext2fs_direct *)((char *)ep + dsize);
}
memcpy(ep, entry, (u_int)newentrysize);
error = VOP_BWRITE(bp->b_vp, bp);
dp->i_flag |= IN_CHANGE | IN_UPDATE;
return error;
}
int
ext2fs_dirremove(struct vnode *dvp, const struct ufs_lookup_results *ulr,
struct componentname *cnp)
{
struct inode *dp;
struct ext2fs_direct *ep;
struct buf *bp;
int error;
dp = VTOI(dvp);
if (ulr->ulr_count == 0) {
error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset,
(void *)&ep, &bp);
if (error != 0)
return error;
ep->e2d_ino = 0;
error = VOP_BWRITE(bp->b_vp, bp);
dp->i_flag |= IN_CHANGE | IN_UPDATE;
return error;
}
error = ext2fs_blkatoff(dvp, (off_t)(ulr->ulr_offset - ulr->ulr_count),
(void *)&ep, &bp);
if (error != 0)
return error;
ep->e2d_reclen = h2fs16(fs2h16(ep->e2d_reclen) + ulr->ulr_reclen);
error = VOP_BWRITE(bp->b_vp, bp);
dp->i_flag |= IN_CHANGE | IN_UPDATE;
return error;
}
int
ext2fs_dirrewrite(struct inode *dp, const struct ufs_lookup_results *ulr,
struct inode *ip, struct componentname *cnp)
{
struct buf *bp;
struct ext2fs_direct *ep;
struct vnode *vdp = ITOV(dp);
int error;
error = ext2fs_blkatoff(vdp, (off_t)ulr->ulr_offset, (void *)&ep, &bp);
if (error != 0)
return error;
ep->e2d_ino = h2fs32(ip->i_number);
if (EXT2F_HAS_INCOMPAT_FEATURE(dp->i_e2fs, EXT2F_INCOMPAT_FTYPE)) {
ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
} else {
ep->e2d_type = 0;
}
error = VOP_BWRITE(bp->b_vp, bp);
dp->i_flag |= IN_CHANGE | IN_UPDATE;
return error;
}
int
ext2fs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred)
{
off_t off;
struct ext2fs_direct dbuf;
struct ext2fs_direct *dp = &dbuf;
int error, namlen;
size_t count;
#define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2)
for (off = 0; off < ext2fs_size(ip); off += fs2h16(dp->e2d_reclen)) {
error = ufs_bufio(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ,
off, IO_NODELOCKED, cred, &count, NULL);
if (error || count != 0)
return 0;
if (dp->e2d_reclen == 0)
return 0;
if (dp->e2d_ino == 0)
continue;
namlen = dp->e2d_namlen;
if (namlen > 2)
return 0;
if (dp->e2d_name[0] != '.')
return 0;
if (namlen == 1)
continue;
if (dp->e2d_name[1] == '.' && fs2h32(dp->e2d_ino) == parentino)
continue;
return 0;
}
return 1;
}