version 1.47.2.8, 2005/01/24 08:36:05 |
version 1.99.2.2, 2009/03/03 18:34:40 |
|
|
#include <sys/cdefs.h> |
#include <sys/cdefs.h> |
__KERNEL_RCSID(0, "$NetBSD$"); |
__KERNEL_RCSID(0, "$NetBSD$"); |
|
|
|
#ifdef _KERNEL_OPT |
|
#include "opt_ffs.h" |
|
#include "fs_ffs.h" |
|
#endif |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
#include <sys/namei.h> |
#include <sys/namei.h> |
Line 48 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 53 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/mount.h> |
#include <sys/mount.h> |
#include <sys/vnode.h> |
#include <sys/vnode.h> |
#include <sys/kernel.h> |
#include <sys/kernel.h> |
|
#include <sys/kauth.h> |
|
#include <sys/wapbl.h> |
|
#include <sys/fstrans.h> |
|
#include <sys/proc.h> |
|
#include <sys/kmem.h> |
|
|
#include <ufs/ufs/inode.h> |
#include <ufs/ufs/inode.h> |
#include <ufs/ufs/dir.h> |
#include <ufs/ufs/dir.h> |
Line 57 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 67 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <ufs/ufs/ufsmount.h> |
#include <ufs/ufs/ufsmount.h> |
#include <ufs/ufs/ufs_extern.h> |
#include <ufs/ufs/ufs_extern.h> |
#include <ufs/ufs/ufs_bswap.h> |
#include <ufs/ufs/ufs_bswap.h> |
|
#include <ufs/ufs/ufs_wapbl.h> |
|
|
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
int dirchk = 1; |
int dirchk = 1; |
|
|
int dirchk = 0; |
int dirchk = 0; |
#endif |
#endif |
|
|
#define FSFMT(vp) (((vp)->v_mount->mnt_iflag & IMNT_DTYPE) == 0) |
#define FSFMT(vp) (((vp)->v_mount->mnt_iflag & IMNT_DTYPE) == 0) |
|
|
/* |
/* |
* Convert a component of a pathname into a pointer to a locked inode. |
* Convert a component of a pathname into a pointer to a locked inode. |
|
|
* nor deleting, add name to cache |
* nor deleting, add name to cache |
*/ |
*/ |
int |
int |
ufs_lookup(v) |
ufs_lookup(void *v) |
void *v; |
|
{ |
{ |
struct vop_lookup_args /* { |
struct vop_lookup_args /* { |
struct vnode *a_dvp; |
struct vnode *a_dvp; |
|
|
struct vnode *tdp; /* returned by VFS_VGET */ |
struct vnode *tdp; /* returned by VFS_VGET */ |
doff_t enduseful; /* pointer past last used dir slot */ |
doff_t enduseful; /* pointer past last used dir slot */ |
u_long bmask; /* block offset mask */ |
u_long bmask; /* block offset mask */ |
int lockparent; /* 1 => lockparent flag is set */ |
|
int wantparent; /* 1 => wantparent or lockparent flag */ |
|
int namlen, error; |
int namlen, error; |
struct vnode **vpp = ap->a_vpp; |
struct vnode **vpp = ap->a_vpp; |
struct componentname *cnp = ap->a_cnp; |
struct componentname *cnp = ap->a_cnp; |
struct ucred *cred = cnp->cn_cred; |
kauth_cred_t cred = cnp->cn_cred; |
int flags; |
int flags; |
int nameiop = cnp->cn_nameiop; |
int nameiop = cnp->cn_nameiop; |
struct ufsmount *ump = dp->i_ump; |
struct ufsmount *ump = dp->i_ump; |
|
|
int dirblksiz = ump->um_dirblksiz; |
int dirblksiz = ump->um_dirblksiz; |
ino_t foundino; |
ino_t foundino; |
|
|
cnp->cn_flags &= ~PDIRUNLOCK; |
|
flags = cnp->cn_flags; |
flags = cnp->cn_flags; |
|
|
bp = NULL; |
bp = NULL; |
slotoffset = -1; |
slotoffset = -1; |
*vpp = NULL; |
*vpp = NULL; |
lockparent = flags & LOCKPARENT; |
|
wantparent = flags & (LOCKPARENT|WANTPARENT); |
|
endsearch = 0; /* silence compiler warning */ |
endsearch = 0; /* silence compiler warning */ |
/* |
/* |
* Check accessiblity of directory. |
* Check accessiblity of directory. |
*/ |
*/ |
if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_lwp)) != 0) |
if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0) |
return (error); |
return (error); |
|
|
if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && |
if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && |
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) |
(nameiop == DELETE || nameiop == RENAME)) |
return (EROFS); |
return (EROFS); |
|
|
/* |
/* |
|
|
* check the name cache to see if the directory/name pair |
* check the name cache to see if the directory/name pair |
* we are looking for is known already. |
* we are looking for is known already. |
*/ |
*/ |
if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) |
if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) { |
return (error); |
return (error); |
|
} |
|
|
|
fstrans_start(vdp->v_mount, FSTRANS_SHARED); |
|
|
/* |
/* |
* Suppress search for slots unless creating |
* Suppress search for slots unless creating |
|
|
} else { |
} else { |
dp->i_offset = dp->i_diroff; |
dp->i_offset = dp->i_diroff; |
if ((entryoffsetinblock = dp->i_offset & bmask) && |
if ((entryoffsetinblock = dp->i_offset & bmask) && |
(error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))) |
(error = ufs_blkatoff(vdp, (off_t)dp->i_offset, |
return (error); |
NULL, &bp, false))) |
|
goto out; |
numdirpasses = 2; |
numdirpasses = 2; |
nchstats.ncs_2passes++; |
nchstats.ncs_2passes++; |
} |
} |
|
|
searchloop: |
searchloop: |
while (dp->i_offset < endsearch) { |
while (dp->i_offset < endsearch) { |
if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD) |
if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD) |
preempt(1); |
preempt(); |
/* |
/* |
* If necessary, get the next directory block. |
* If necessary, get the next directory block. |
*/ |
*/ |
if ((dp->i_offset & bmask) == 0) { |
if ((dp->i_offset & bmask) == 0) { |
if (bp != NULL) |
if (bp != NULL) |
brelse(bp); |
brelse(bp, 0); |
error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, |
error = ufs_blkatoff(vdp, (off_t)dp->i_offset, NULL, |
&bp); |
&bp, false); |
if (error) |
if (error) |
return (error); |
goto out; |
entryoffsetinblock = 0; |
entryoffsetinblock = 0; |
} |
} |
/* |
/* |
* If still looking for a slot, and at a DIRBLKSIZE |
* If still looking for a slot, and at a DIRBLKSIZ |
* boundary, have to start looking for free space again. |
* boundary, have to start looking for free space again. |
*/ |
*/ |
if (slotstatus == NONE && |
if (slotstatus == NONE && |
|
|
* directory. Complete checks can be run by patching |
* directory. Complete checks can be run by patching |
* "dirchk" to be true. |
* "dirchk" to be true. |
*/ |
*/ |
|
KASSERT(bp != NULL); |
ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock); |
ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock); |
if (ep->d_reclen == 0 || |
if (ep->d_reclen == 0 || |
(dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock))) { |
(dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock))) { |
|
|
else |
else |
namlen = ep->d_namlen; |
namlen = ep->d_namlen; |
#else |
#else |
if (FSFMT(vdp) && needswap != 0) |
if (FSFMT(vdp) && needswap != 0) |
namlen = ep->d_type; |
namlen = ep->d_type; |
else |
else |
namlen = ep->d_namlen; |
namlen = ep->d_namlen; |
|
|
goto searchloop; |
goto searchloop; |
} |
} |
if (bp != NULL) |
if (bp != NULL) |
brelse(bp); |
brelse(bp, 0); |
/* |
/* |
* If creating, and at end of pathname and current |
* If creating, and at end of pathname and current |
* directory has not been removed, then can consider |
* directory has not been removed, then can consider |
|
|
(nameiop == DELETE && |
(nameiop == DELETE && |
(ap->a_cnp->cn_flags & DOWHITEOUT) && |
(ap->a_cnp->cn_flags & DOWHITEOUT) && |
(ap->a_cnp->cn_flags & ISWHITEOUT))) && |
(ap->a_cnp->cn_flags & ISWHITEOUT))) && |
(flags & ISLASTCN) && dp->i_ffs_effnlink != 0) { |
(flags & ISLASTCN) && dp->i_nlink != 0) { |
/* |
/* |
* Access for write is interpreted as allowing |
* Access for write is interpreted as allowing |
* creation of files in the directory. |
* creation of files in the directory. |
*/ |
*/ |
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp); |
error = VOP_ACCESS(vdp, VWRITE, cred); |
if (error) |
if (error) |
return (error); |
goto out; |
/* |
/* |
* Return an indication of where the new directory |
* Return an indication of where the new directory |
* entry should be put. If we didn't find a slot, |
* entry should be put. If we didn't find a slot, |
|
|
* information cannot be used. |
* information cannot be used. |
*/ |
*/ |
cnp->cn_flags |= SAVENAME; |
cnp->cn_flags |= SAVENAME; |
if (!lockparent) { |
error = EJUSTRETURN; |
VOP_UNLOCK(vdp, 0); |
goto out; |
cnp->cn_flags |= PDIRUNLOCK; |
|
} |
|
return (EJUSTRETURN); |
|
} |
} |
/* |
/* |
* Insert name into cache (as non-existent) if appropriate. |
* Insert name into cache (as non-existent) if appropriate. |
*/ |
*/ |
if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) |
if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) |
cache_enter(vdp, *vpp, cnp); |
cache_enter(vdp, *vpp, cnp); |
return (ENOENT); |
error = ENOENT; |
|
goto out; |
|
|
found: |
found: |
if (numdirpasses == 2) |
if (numdirpasses == 2) |
|
|
dp->i_size = dp->i_offset + DIRSIZ(FSFMT(vdp), ep, needswap); |
dp->i_size = dp->i_offset + DIRSIZ(FSFMT(vdp), ep, needswap); |
DIP_ASSIGN(dp, size, dp->i_size); |
DIP_ASSIGN(dp, size, dp->i_size); |
dp->i_flag |= IN_CHANGE | IN_UPDATE; |
dp->i_flag |= IN_CHANGE | IN_UPDATE; |
|
UFS_WAPBL_UPDATE(vdp, NULL, NULL, UPDATE_DIROP); |
} |
} |
brelse(bp); |
brelse(bp, 0); |
|
|
/* |
/* |
* Found component in pathname. |
* Found component in pathname. |
|
|
/* |
/* |
* If deleting, and at end of pathname, return |
* If deleting, and at end of pathname, return |
* parameters which can be used to remove file. |
* parameters which can be used to remove file. |
* If the wantparent flag isn't set, we return only |
* Lock the inode, being careful with ".". |
* the directory (in ndp->ni_dvp), otherwise we go |
|
* on and lock the inode, being careful with ".". |
|
*/ |
*/ |
if (nameiop == DELETE && (flags & ISLASTCN)) { |
if (nameiop == DELETE && (flags & ISLASTCN)) { |
/* |
/* |
* Write access to directory required to delete files. |
* Write access to directory required to delete files. |
*/ |
*/ |
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp); |
error = VOP_ACCESS(vdp, VWRITE, cred); |
if (error) |
if (error) |
return (error); |
goto out; |
/* |
/* |
* Return pointer to current entry in dp->i_offset, |
* Return pointer to current entry in dp->i_offset, |
* and distance past previous entry (if there |
* and distance past previous entry (if there |
|
|
if (dp->i_number == foundino) { |
if (dp->i_number == foundino) { |
VREF(vdp); |
VREF(vdp); |
*vpp = vdp; |
*vpp = vdp; |
return (0); |
error = 0; |
|
goto out; |
} |
} |
if (flags & ISDOTDOT) |
if (flags & ISDOTDOT) |
VOP_UNLOCK(vdp, 0); /* race to get the inode */ |
VOP_UNLOCK(vdp, 0); /* race to get the inode */ |
|
|
if (flags & ISDOTDOT) |
if (flags & ISDOTDOT) |
vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY); |
vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY); |
if (error) |
if (error) |
return (error); |
goto out; |
/* |
/* |
* If directory is "sticky", then user must own |
* If directory is "sticky", then user must own |
* the directory, or the file in it, else she |
* the directory, or the file in it, else she |
|
|
* implements append-only directories. |
* implements append-only directories. |
*/ |
*/ |
if ((dp->i_mode & ISVTX) && |
if ((dp->i_mode & ISVTX) && |
cred->cr_uid != 0 && |
kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, |
cred->cr_uid != dp->i_uid && |
NULL) != 0 && |
VTOI(tdp)->i_uid != cred->cr_uid) { |
kauth_cred_geteuid(cred) != dp->i_uid && |
|
VTOI(tdp)->i_uid != kauth_cred_geteuid(cred)) { |
vput(tdp); |
vput(tdp); |
return (EPERM); |
error = EPERM; |
|
goto out; |
} |
} |
*vpp = tdp; |
*vpp = tdp; |
if (!lockparent) { |
error = 0; |
VOP_UNLOCK(vdp, 0); |
goto out; |
cnp->cn_flags |= PDIRUNLOCK; |
|
} |
|
return (0); |
|
} |
} |
|
|
/* |
/* |
|
|
* Must get inode of directory entry to verify it's a |
* Must get inode of directory entry to verify it's a |
* regular file, or empty directory. |
* regular file, or empty directory. |
*/ |
*/ |
if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { |
if (nameiop == RENAME && (flags & ISLASTCN)) { |
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp); |
error = VOP_ACCESS(vdp, VWRITE, cred); |
if (error) |
if (error) |
return (error); |
goto out; |
/* |
/* |
* Careful about locking second inode. |
* Careful about locking second inode. |
* This can only occur if the target is ".". |
* This can only occur if the target is ".". |
*/ |
*/ |
if (dp->i_number == foundino) |
if (dp->i_number == foundino) { |
return (EISDIR); |
error = EISDIR; |
|
goto out; |
|
} |
if (flags & ISDOTDOT) |
if (flags & ISDOTDOT) |
VOP_UNLOCK(vdp, 0); /* race to get the inode */ |
VOP_UNLOCK(vdp, 0); /* race to get the inode */ |
error = VFS_VGET(vdp->v_mount, foundino, &tdp); |
error = VFS_VGET(vdp->v_mount, foundino, &tdp); |
if (flags & ISDOTDOT) |
if (flags & ISDOTDOT) |
vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY); |
vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY); |
if (error) |
if (error) |
return (error); |
goto out; |
*vpp = tdp; |
*vpp = tdp; |
cnp->cn_flags |= SAVENAME; |
cnp->cn_flags |= SAVENAME; |
if (!lockparent) { |
error = 0; |
VOP_UNLOCK(vdp, 0); |
goto out; |
cnp->cn_flags |= PDIRUNLOCK; |
|
} |
|
return (0); |
|
} |
} |
|
|
/* |
/* |
|
|
pdp = vdp; |
pdp = vdp; |
if (flags & ISDOTDOT) { |
if (flags & ISDOTDOT) { |
VOP_UNLOCK(pdp, 0); /* race to get the inode */ |
VOP_UNLOCK(pdp, 0); /* race to get the inode */ |
cnp->cn_flags |= PDIRUNLOCK; |
|
error = VFS_VGET(vdp->v_mount, foundino, &tdp); |
error = VFS_VGET(vdp->v_mount, foundino, &tdp); |
|
vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY); |
if (error) { |
if (error) { |
if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY) == 0) |
goto out; |
cnp->cn_flags &= ~PDIRUNLOCK; |
|
return (error); |
|
} |
|
if (lockparent && (flags & ISLASTCN)) { |
|
if ((error = vn_lock(pdp, LK_EXCLUSIVE))) { |
|
vput(tdp); |
|
return (error); |
|
} |
|
cnp->cn_flags &= ~PDIRUNLOCK; |
|
} |
} |
*vpp = tdp; |
*vpp = tdp; |
} else if (dp->i_number == foundino) { |
} else if (dp->i_number == foundino) { |
|
|
} else { |
} else { |
error = VFS_VGET(vdp->v_mount, foundino, &tdp); |
error = VFS_VGET(vdp->v_mount, foundino, &tdp); |
if (error) |
if (error) |
return (error); |
goto out; |
if (!lockparent || !(flags & ISLASTCN)) { |
|
VOP_UNLOCK(pdp, 0); |
|
cnp->cn_flags |= PDIRUNLOCK; |
|
} |
|
*vpp = tdp; |
*vpp = tdp; |
} |
} |
|
|
|
|
*/ |
*/ |
if (cnp->cn_flags & MAKEENTRY) |
if (cnp->cn_flags & MAKEENTRY) |
cache_enter(vdp, *vpp, cnp); |
cache_enter(vdp, *vpp, cnp); |
return (0); |
error = 0; |
|
|
|
out: |
|
fstrans_done(vdp->v_mount); |
|
return error; |
} |
} |
|
|
void |
void |
ufs_dirbad(ip, offset, how) |
ufs_dirbad(struct inode *ip, doff_t offset, const char *how) |
struct inode *ip; |
|
doff_t offset; |
|
char *how; |
|
{ |
{ |
struct mount *mp; |
struct mount *mp; |
|
|
mp = ITOV(ip)->v_mount; |
mp = ITOV(ip)->v_mount; |
printf("%s: bad dir ino %d at offset %d: %s\n", |
printf("%s: bad dir ino %llu at offset %d: %s\n", |
mp->mnt_stat.f_mntonname, ip->i_number, offset, how); |
mp->mnt_stat.f_mntonname, (unsigned long long)ip->i_number, |
|
offset, how); |
if ((mp->mnt_stat.f_flag & MNT_RDONLY) == 0) |
if ((mp->mnt_stat.f_flag & MNT_RDONLY) == 0) |
panic("bad dir"); |
panic("bad dir"); |
} |
} |
Line 667 ufs_dirbad(ip, offset, how) |
|
Line 662 ufs_dirbad(ip, offset, how) |
|
* record length must be multiple of 4 |
* record length must be multiple of 4 |
* entry must fit in rest of its DIRBLKSIZ block |
* entry must fit in rest of its DIRBLKSIZ block |
* record must be large enough to contain entry |
* record must be large enough to contain entry |
* name is not longer than MAXNAMLEN |
* name is not longer than FFS_MAXNAMLEN |
* name must be as long as advertised, and null terminated |
* name must be as long as advertised, and null terminated |
*/ |
*/ |
int |
int |
ufs_dirbadentry(dp, ep, entryoffsetinblock) |
ufs_dirbadentry(struct vnode *dp, struct direct *ep, int entryoffsetinblock) |
struct vnode *dp; |
|
struct direct *ep; |
|
int entryoffsetinblock; |
|
{ |
{ |
int i; |
int i; |
int namlen; |
int namlen; |
Line 698 ufs_dirbadentry(dp, ep, entryoffsetinblo |
|
Line 690 ufs_dirbadentry(dp, ep, entryoffsetinblo |
|
dirblksiz - (entryoffsetinblock & (dirblksiz - 1)) || |
dirblksiz - (entryoffsetinblock & (dirblksiz - 1)) || |
ufs_rw16(ep->d_reclen, needswap) < |
ufs_rw16(ep->d_reclen, needswap) < |
DIRSIZ(FSFMT(dp), ep, needswap) || |
DIRSIZ(FSFMT(dp), ep, needswap) || |
namlen > MAXNAMLEN) { |
namlen > FFS_MAXNAMLEN) { |
/*return (1); */ |
/*return (1); */ |
printf("First bad, reclen=%x, DIRSIZ=%lu, namlen=%d, flags=%x " |
printf("First bad, reclen=%#x, DIRSIZ=%lu, namlen=%d, " |
"entryoffsetinblock=%d, dirblksiz = %d\n", |
"flags=%#x, entryoffsetinblock=%d, dirblksiz = %d\n", |
ufs_rw16(ep->d_reclen, needswap), |
ufs_rw16(ep->d_reclen, needswap), |
(u_long)DIRSIZ(FSFMT(dp), ep, needswap), |
(u_long)DIRSIZ(FSFMT(dp), ep, needswap), |
namlen, dp->v_mount->mnt_flag, entryoffsetinblock,dirblksiz); |
namlen, dp->v_mount->mnt_flag, entryoffsetinblock, |
|
dirblksiz); |
goto bad; |
goto bad; |
} |
} |
if (ep->d_ino == 0) |
if (ep->d_ino == 0) |
|
|
* argument ip is the inode to which the new directory entry will refer. |
* argument ip is the inode to which the new directory entry will refer. |
*/ |
*/ |
void |
void |
ufs_makedirentry(ip, cnp, newdirp) |
ufs_makedirentry(struct inode *ip, struct componentname *cnp, |
struct inode *ip; |
struct direct *newdirp) |
struct componentname *cnp; |
|
struct direct *newdirp; |
|
{ |
{ |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if ((cnp->cn_flags & SAVENAME) == 0) |
if ((cnp->cn_flags & SAVENAME) == 0) |
Line 757 ufs_makedirentry(ip, cnp, newdirp) |
|
Line 748 ufs_makedirentry(ip, cnp, newdirp) |
|
* soft dependency code). |
* soft dependency code). |
*/ |
*/ |
int |
int |
ufs_direnter(dvp, tvp, dirp, cnp, newdirbp) |
ufs_direnter(struct vnode *dvp, struct vnode *tvp, struct direct *dirp, |
struct vnode *dvp; |
struct componentname *cnp, struct buf *newdirbp) |
struct vnode *tvp; |
|
struct direct *dirp; |
|
struct componentname *cnp; |
|
struct buf *newdirbp; |
|
{ |
{ |
struct ucred *cr; |
kauth_cred_t cr; |
struct lwp *l; |
struct lwp *l; |
int newentrysize; |
int newentrysize; |
struct inode *dp; |
struct inode *dp; |
struct buf *bp; |
struct buf *bp; |
u_int dsize; |
u_int dsize; |
struct direct *ep, *nep; |
struct direct *ep, *nep; |
int error, ret, blkoff, loc, spacefree, flags; |
int error, ret, blkoff, loc, spacefree; |
char *dirbuf; |
char *dirbuf; |
struct timespec ts; |
struct timespec ts; |
struct ufsmount *ump = VFSTOUFS(dvp->v_mount); |
struct ufsmount *ump = VFSTOUFS(dvp->v_mount); |
const int needswap = UFS_MPNEEDSWAP(ump); |
const int needswap = UFS_MPNEEDSWAP(ump); |
int dirblksiz = ump->um_dirblksiz; |
int dirblksiz = ump->um_dirblksiz; |
|
|
|
UFS_WAPBL_JLOCK_ASSERT(dvp->v_mount); |
|
|
error = 0; |
error = 0; |
cr = cnp->cn_cred; |
cr = cnp->cn_cred; |
l = cnp->cn_lwp; |
l = curlwp; |
|
|
dp = VTOI(dvp); |
dp = VTOI(dvp); |
newentrysize = DIRSIZ(0, dirp, 0); |
newentrysize = DIRSIZ(0, dirp, 0); |
Line 794 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
Line 783 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
*/ |
*/ |
if (dp->i_offset & (dirblksiz - 1)) |
if (dp->i_offset & (dirblksiz - 1)) |
panic("ufs_direnter: newblk"); |
panic("ufs_direnter: newblk"); |
flags = B_CLRBUF; |
if ((error = UFS_BALLOC(dvp, (off_t)dp->i_offset, dirblksiz, |
if (!DOINGSOFTDEP(dvp)) |
cr, B_CLRBUF | B_SYNC, &bp)) != 0) { |
flags |= B_SYNC; |
|
if ((error = VOP_BALLOC(dvp, (off_t)dp->i_offset, dirblksiz, |
|
cr, flags, &bp)) != 0) { |
|
if (DOINGSOFTDEP(dvp) && newdirbp != NULL) |
|
bdwrite(newdirbp); |
|
return (error); |
return (error); |
} |
} |
dp->i_size = dp->i_offset + dirblksiz; |
dp->i_size = dp->i_offset + dirblksiz; |
Line 821 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
Line 805 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
} |
} |
} |
} |
blkoff = dp->i_offset & (ump->um_mountp->mnt_stat.f_iosize - 1); |
blkoff = dp->i_offset & (ump->um_mountp->mnt_stat.f_iosize - 1); |
memcpy((caddr_t)bp->b_data + blkoff, (caddr_t)dirp, |
memcpy((char *)bp->b_data + blkoff, dirp, newentrysize); |
newentrysize); |
|
#ifdef UFS_DIRHASH |
#ifdef UFS_DIRHASH |
if (dp->i_dirhash != NULL) { |
if (dp->i_dirhash != NULL) { |
ufsdirhash_newblk(dp, dp->i_offset); |
ufsdirhash_newblk(dp, dp->i_offset); |
Line 831 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
Line 814 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
dp->i_offset); |
dp->i_offset); |
} |
} |
#endif |
#endif |
if (DOINGSOFTDEP(dvp)) { |
error = VOP_BWRITE(bp); |
/* |
vfs_timestamp(&ts); |
* Ensure that the entire newly allocated block is a |
ret = UFS_UPDATE(dvp, &ts, &ts, UPDATE_DIROP); |
* valid directory so that future growth within the |
|
* block does not have to ensure that the block is |
|
* written before the inode. |
|
*/ |
|
blkoff += dirblksiz; |
|
while (blkoff < bp->b_bcount) { |
|
((struct direct *) |
|
(bp->b_data + blkoff))->d_reclen = dirblksiz; |
|
blkoff += dirblksiz; |
|
} |
|
if (softdep_setup_directory_add(bp, dp, dp->i_offset, |
|
ufs_rw32(dirp->d_ino, needswap), newdirbp, 1) == 0) { |
|
bdwrite(bp); |
|
TIMEVAL_TO_TIMESPEC(&time, &ts); |
|
return VOP_UPDATE(dvp, &ts, &ts, UPDATE_DIROP); |
|
} |
|
/* We have just allocated a directory block in an |
|
* indirect block. Rather than tracking when it gets |
|
* claimed by the inode, we simply do a VOP_FSYNC |
|
* now to ensure that it is there (in case the user |
|
* does a future fsync). Note that we have to unlock |
|
* the inode for the entry that we just entered, as |
|
* the VOP_FSYNC may need to lock other inodes which |
|
* can lead to deadlock if we also hold a lock on |
|
* the newly entered node. |
|
*/ |
|
error = VOP_BWRITE(bp); |
|
if (error != 0) |
|
return (error); |
|
if (tvp != NULL) |
|
VOP_UNLOCK(tvp, 0); |
|
error = VOP_FSYNC(dvp, l->l_proc->p_ucred, FSYNC_WAIT, 0, 0, l); |
|
if (tvp != 0) |
|
vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY); |
|
return (error); |
|
} else { |
|
error = VOP_BWRITE(bp); |
|
} |
|
TIMEVAL_TO_TIMESPEC(&time, &ts); |
|
ret = VOP_UPDATE(dvp, &ts, &ts, UPDATE_DIROP); |
|
if (error == 0) |
if (error == 0) |
return (ret); |
return (ret); |
return (error); |
return (error); |
Line 891 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
Line 834 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
/* |
/* |
* Increase size of directory if entry eats into new space. |
* Increase size of directory if entry eats into new space. |
* This should never push the size past a new multiple of |
* This should never push the size past a new multiple of |
* DIRBLKSIZE. |
* DIRBLKSIZ. |
* |
* |
* N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. |
* N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. |
*/ |
*/ |
Line 899 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
Line 842 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
dp->i_size = dp->i_offset + dp->i_count; |
dp->i_size = dp->i_offset + dp->i_count; |
DIP_ASSIGN(dp, size, dp->i_size); |
DIP_ASSIGN(dp, size, dp->i_size); |
dp->i_flag |= IN_CHANGE | IN_UPDATE; |
dp->i_flag |= IN_CHANGE | IN_UPDATE; |
|
UFS_WAPBL_UPDATE(dvp, NULL, NULL, UPDATE_DIROP); |
} |
} |
/* |
/* |
* Get the block containing the space for the new directory entry. |
* Get the block containing the space for the new directory entry. |
*/ |
*/ |
error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp); |
error = ufs_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, &bp, true); |
if (error) { |
if (error) { |
if (DOINGSOFTDEP(dvp) && newdirbp != NULL) |
|
bdwrite(newdirbp); |
|
return (error); |
return (error); |
} |
} |
/* |
/* |
Line 916 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
Line 858 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
* dp->i_offset + dp->i_count would yield the space. |
* dp->i_offset + dp->i_count would yield the space. |
*/ |
*/ |
ep = (struct direct *)dirbuf; |
ep = (struct direct *)dirbuf; |
dsize = DIRSIZ(FSFMT(dvp), ep, needswap); |
dsize = (ep->d_ino != 0) ? DIRSIZ(FSFMT(dvp), ep, needswap) : 0; |
spacefree = ufs_rw16(ep->d_reclen, needswap) - dsize; |
spacefree = ufs_rw16(ep->d_reclen, needswap) - dsize; |
for (loc = ufs_rw16(ep->d_reclen, needswap); loc < dp->i_count; ) { |
for (loc = ufs_rw16(ep->d_reclen, needswap); loc < dp->i_count; ) { |
|
uint16_t reclen; |
|
|
nep = (struct direct *)(dirbuf + loc); |
nep = (struct direct *)(dirbuf + loc); |
if (ep->d_ino) { |
|
/* trim the existing slot */ |
/* Trim the existing slot (NB: dsize may be zero). */ |
ep->d_reclen = ufs_rw16(dsize, needswap); |
ep->d_reclen = ufs_rw16(dsize, needswap); |
ep = (struct direct *)((char *)ep + dsize); |
ep = (struct direct *)((char *)ep + dsize); |
} else { |
|
/* overwrite; nothing there; header is ours */ |
reclen = ufs_rw16(nep->d_reclen, needswap); |
spacefree += dsize; |
loc += reclen; |
|
if (nep->d_ino == 0) { |
|
/* |
|
* A mid-block unused entry. Such entries are |
|
* never created by the kernel, but fsck_ffs |
|
* can create them (and it doesn't fix them). |
|
* |
|
* Add up the free space, and initialise the |
|
* relocated entry since we don't memcpy it. |
|
*/ |
|
spacefree += reclen; |
|
ep->d_ino = 0; |
|
dsize = 0; |
|
continue; |
} |
} |
dsize = DIRSIZ(FSFMT(dvp), nep, needswap); |
dsize = DIRSIZ(FSFMT(dvp), nep, needswap); |
spacefree += ufs_rw16(nep->d_reclen, needswap) - dsize; |
spacefree += reclen - dsize; |
loc += ufs_rw16(nep->d_reclen, needswap); |
|
#ifdef UFS_DIRHASH |
#ifdef UFS_DIRHASH |
if (dp->i_dirhash != NULL) |
if (dp->i_dirhash != NULL) |
ufsdirhash_move(dp, nep, |
ufsdirhash_move(dp, nep, |
dp->i_offset + ((char *)nep - dirbuf), |
dp->i_offset + ((char *)nep - dirbuf), |
dp->i_offset + ((char *)ep - dirbuf)); |
dp->i_offset + ((char *)ep - dirbuf)); |
#endif |
#endif |
if (DOINGSOFTDEP(dvp)) |
memcpy((void *)ep, (void *)nep, dsize); |
softdep_change_directoryentry_offset(dp, dirbuf, |
|
(caddr_t)nep, (caddr_t)ep, dsize); |
|
else |
|
memcpy((caddr_t)ep, (caddr_t)nep, dsize); |
|
} |
} |
/* |
/* |
|
* Here, `ep' points to a directory entry containing `dsize' in-use |
|
* bytes followed by `spacefree' unused bytes. If ep->d_ino == 0, |
|
* then the entry is completely unused (dsize == 0). The value |
|
* of ep->d_reclen is always indeterminate. |
|
* |
* Update the pointer fields in the previous entry (if any), |
* Update the pointer fields in the previous entry (if any), |
* copy in the new entry, and write out the block. |
* copy in the new entry, and write out the block. |
*/ |
*/ |
Line 978 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
Line 935 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
dirp->d_reclen == spacefree)) |
dirp->d_reclen == spacefree)) |
ufsdirhash_add(dp, dirp, dp->i_offset + ((char *)ep - dirbuf)); |
ufsdirhash_add(dp, dirp, dp->i_offset + ((char *)ep - dirbuf)); |
#endif |
#endif |
memcpy((caddr_t)ep, (caddr_t)dirp, (u_int)newentrysize); |
memcpy((void *)ep, (void *)dirp, (u_int)newentrysize); |
#ifdef UFS_DIRHASH |
#ifdef UFS_DIRHASH |
if (dp->i_dirhash != NULL) |
if (dp->i_dirhash != NULL) |
ufsdirhash_checkblock(dp, dirbuf - |
ufsdirhash_checkblock(dp, dirbuf - |
(dp->i_offset & (dirblksiz - 1)), |
(dp->i_offset & (dirblksiz - 1)), |
dp->i_offset & ~(dirblksiz - 1)); |
dp->i_offset & ~(dirblksiz - 1)); |
#endif |
#endif |
if (DOINGSOFTDEP(dvp)) { |
error = VOP_BWRITE(bp); |
softdep_setup_directory_add(bp, dp, |
|
dp->i_offset + (caddr_t)ep - dirbuf, |
|
ufs_rw32(dirp->d_ino, needswap), newdirbp, 0); |
|
bdwrite(bp); |
|
} else { |
|
error = VOP_BWRITE(bp); |
|
} |
|
dp->i_flag |= IN_CHANGE | IN_UPDATE; |
dp->i_flag |= IN_CHANGE | IN_UPDATE; |
/* |
/* |
* If all went well, and the directory can be shortened, proceed |
* If all went well, and the directory can be shortened, proceed |
Line 1002 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
Line 952 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
* lock on the newly entered node. |
* lock on the newly entered node. |
*/ |
*/ |
if (error == 0 && dp->i_endoff && dp->i_endoff < dp->i_size) { |
if (error == 0 && dp->i_endoff && dp->i_endoff < dp->i_size) { |
if (DOINGSOFTDEP(dvp) && (tvp != NULL)) |
|
VOP_UNLOCK(tvp, 0); |
|
#ifdef UFS_DIRHASH |
#ifdef UFS_DIRHASH |
if (dp->i_dirhash != NULL) |
if (dp->i_dirhash != NULL) |
ufsdirhash_dirtrunc(dp, dp->i_endoff); |
ufsdirhash_dirtrunc(dp, dp->i_endoff); |
#endif |
#endif |
(void) VOP_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, cr, l); |
(void) UFS_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, cr); |
if (DOINGSOFTDEP(dvp) && (tvp != NULL)) |
|
vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY); |
|
} |
} |
|
UFS_WAPBL_UPDATE(dvp, NULL, NULL, UPDATE_DIROP); |
return (error); |
return (error); |
} |
} |
|
|
Line 1028 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
Line 975 ufs_direnter(dvp, tvp, dirp, cnp, newdir |
|
* to the size of the previous entry. |
* to the size of the previous entry. |
*/ |
*/ |
int |
int |
ufs_dirremove(dvp, ip, flags, isrmdir) |
ufs_dirremove(struct vnode *dvp, struct inode *ip, int flags, int isrmdir) |
struct vnode *dvp; |
|
struct inode *ip; |
|
int flags; |
|
int isrmdir; |
|
{ |
{ |
struct inode *dp = VTOI(dvp); |
struct inode *dp = VTOI(dvp); |
struct direct *ep; |
struct direct *ep; |
Line 1042 ufs_dirremove(dvp, ip, flags, isrmdir) |
|
Line 985 ufs_dirremove(dvp, ip, flags, isrmdir) |
|
const int needswap = UFS_MPNEEDSWAP(dp->i_ump); |
const int needswap = UFS_MPNEEDSWAP(dp->i_ump); |
#endif |
#endif |
|
|
|
UFS_WAPBL_JLOCK_ASSERT(dvp->v_mount); |
|
|
if (flags & DOWHITEOUT) { |
if (flags & DOWHITEOUT) { |
/* |
/* |
* Whiteout entry: set d_ino to WINO. |
* Whiteout entry: set d_ino to WINO. |
*/ |
*/ |
error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (void *)&ep, |
error = ufs_blkatoff(dvp, (off_t)dp->i_offset, (void *)&ep, |
&bp); |
&bp, true); |
if (error) |
if (error) |
return (error); |
return (error); |
ep->d_ino = ufs_rw32(WINO, needswap); |
ep->d_ino = ufs_rw32(WINO, needswap); |
Line 1055 ufs_dirremove(dvp, ip, flags, isrmdir) |
|
Line 1000 ufs_dirremove(dvp, ip, flags, isrmdir) |
|
goto out; |
goto out; |
} |
} |
|
|
if ((error = VOP_BLKATOFF(dvp, |
if ((error = ufs_blkatoff(dvp, |
(off_t)(dp->i_offset - dp->i_count), (void *)&ep, &bp)) != 0) |
(off_t)(dp->i_offset - dp->i_count), (void *)&ep, &bp, true)) != 0) |
return (error); |
return (error); |
|
|
#ifdef UFS_DIRHASH |
#ifdef UFS_DIRHASH |
Line 1066 ufs_dirremove(dvp, ip, flags, isrmdir) |
|
Line 1011 ufs_dirremove(dvp, ip, flags, isrmdir) |
|
*/ |
*/ |
if (dp->i_dirhash != NULL) |
if (dp->i_dirhash != NULL) |
ufsdirhash_remove(dp, (dp->i_count == 0) ? ep : |
ufsdirhash_remove(dp, (dp->i_count == 0) ? ep : |
(struct direct *)((char *)ep + ep->d_reclen), dp->i_offset); |
(struct direct *)((char *)ep + |
|
ufs_rw16(ep->d_reclen, needswap)), dp->i_offset); |
#endif |
#endif |
|
|
if (dp->i_count == 0) { |
if (dp->i_count == 0) { |
Line 1093 ufs_dirremove(dvp, ip, flags, isrmdir) |
|
Line 1039 ufs_dirremove(dvp, ip, flags, isrmdir) |
|
#endif |
#endif |
|
|
out: |
out: |
if (DOINGSOFTDEP(dvp)) { |
if (ip) { |
if (ip) { |
ip->i_nlink--; |
ip->i_ffs_effnlink--; |
DIP_ASSIGN(ip, nlink, ip->i_nlink); |
softdep_change_linkcnt(ip); |
ip->i_flag |= IN_CHANGE; |
softdep_setup_remove(bp, dp, ip, isrmdir); |
UFS_WAPBL_UPDATE(ITOV(ip), NULL, NULL, 0); |
} |
|
bdwrite(bp); |
|
} else { |
|
if (ip) { |
|
ip->i_ffs_effnlink--; |
|
ip->i_nlink--; |
|
DIP_ASSIGN(ip, nlink, ip->i_nlink); |
|
ip->i_flag |= IN_CHANGE; |
|
} |
|
error = VOP_BWRITE(bp); |
|
} |
} |
|
error = VOP_BWRITE(bp); |
dp->i_flag |= IN_CHANGE | IN_UPDATE; |
dp->i_flag |= IN_CHANGE | IN_UPDATE; |
|
#ifdef FFS |
/* |
/* |
* If the last named reference to a snapshot goes away, |
* If the last named reference to a snapshot goes away, |
* drop its snapshot reference so that it will be reclaimed |
* drop its snapshot reference so that it will be reclaimed |
* when last open reference goes away. |
* when last open reference goes away. |
*/ |
*/ |
if (ip != 0 && (ip->i_flags & SF_SNAPSHOT) != 0 && |
if (ip != 0 && (ip->i_flags & SF_SNAPSHOT) != 0 && |
ip->i_ffs_effnlink == 0) |
ip->i_nlink == 0) |
ffs_snapgone(ip); |
ffs_snapgone(ip); |
|
UFS_WAPBL_UPDATE(dvp, NULL, NULL, 0); |
|
#endif |
return (error); |
return (error); |
} |
} |
|
|
|
|
* set up by a call to namei. |
* set up by a call to namei. |
*/ |
*/ |
int |
int |
ufs_dirrewrite(dp, oip, newinum, newtype, isrmdir, iflags) |
ufs_dirrewrite(struct inode *dp, struct inode *oip, ino_t newinum, int newtype, |
struct inode *dp, *oip; |
int isrmdir, int iflags) |
ino_t newinum; |
|
int newtype; |
|
int isrmdir; |
|
int iflags; |
|
{ |
{ |
struct buf *bp; |
struct buf *bp; |
struct direct *ep; |
struct direct *ep; |
struct vnode *vdp = ITOV(dp); |
struct vnode *vdp = ITOV(dp); |
int error; |
int error; |
|
|
error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, (void *)&ep, &bp); |
error = ufs_blkatoff(vdp, (off_t)dp->i_offset, (void *)&ep, &bp, true); |
if (error) |
if (error) |
return (error); |
return (error); |
ep->d_ino = ufs_rw32(newinum, UFS_MPNEEDSWAP(dp->i_ump)); |
ep->d_ino = ufs_rw32(newinum, UFS_MPNEEDSWAP(dp->i_ump)); |
if (!FSFMT(vdp)) |
if (!FSFMT(vdp)) |
ep->d_type = newtype; |
ep->d_type = newtype; |
oip->i_ffs_effnlink--; |
oip->i_nlink--; |
if (DOINGSOFTDEP(vdp)) { |
DIP_ASSIGN(oip, nlink, oip->i_nlink); |
softdep_change_linkcnt(oip); |
oip->i_flag |= IN_CHANGE; |
softdep_setup_directory_change(bp, dp, oip, newinum, isrmdir); |
UFS_WAPBL_UPDATE(ITOV(oip), NULL, NULL, UPDATE_DIROP); |
bdwrite(bp); |
error = VOP_BWRITE(bp); |
} else { |
|
oip->i_nlink--; |
|
DIP_ASSIGN(oip, nlink, oip->i_nlink); |
|
oip->i_flag |= IN_CHANGE; |
|
error = VOP_BWRITE(bp); |
|
} |
|
dp->i_flag |= iflags; |
dp->i_flag |= iflags; |
|
#ifdef FFS |
/* |
/* |
* If the last named reference to a snapshot goes away, |
* If the last named reference to a snapshot goes away, |
* drop its snapshot reference so that it will be reclaimed |
* drop its snapshot reference so that it will be reclaimed |
* when last open reference goes away. |
* when last open reference goes away. |
*/ |
*/ |
if ((oip->i_flags & SF_SNAPSHOT) != 0 && oip->i_ffs_effnlink == 0) |
if ((oip->i_flags & SF_SNAPSHOT) != 0 && oip->i_nlink == 0) |
ffs_snapgone(oip); |
ffs_snapgone(oip); |
|
UFS_WAPBL_UPDATE(vdp, NULL, NULL, UPDATE_DIROP); |
|
#endif |
return (error); |
return (error); |
} |
} |
|
|
Line 1177 ufs_dirrewrite(dp, oip, newinum, newtype |
|
Line 1110 ufs_dirrewrite(dp, oip, newinum, newtype |
|
* NB: does not handle corrupted directories. |
* NB: does not handle corrupted directories. |
*/ |
*/ |
int |
int |
ufs_dirempty(ip, parentino, cred) |
ufs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred) |
struct inode *ip; |
|
ino_t parentino; |
|
struct ucred *cred; |
|
{ |
{ |
doff_t off; |
doff_t off; |
struct dirtemplate dbuf; |
struct dirtemplate dbuf; |
Line 1192 ufs_dirempty(ip, parentino, cred) |
|
Line 1122 ufs_dirempty(ip, parentino, cred) |
|
|
|
for (off = 0; off < ip->i_size; |
for (off = 0; off < ip->i_size; |
off += ufs_rw16(dp->d_reclen, needswap)) { |
off += ufs_rw16(dp->d_reclen, needswap)) { |
error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, |
error = vn_rdwr(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, off, |
UIO_SYSSPACE, IO_NODELOCKED, cred, &count, NULL); |
UIO_SYSSPACE, IO_NODELOCKED, cred, &count, NULL); |
/* |
/* |
* Since we read MINDIRSIZ, residual must |
* Since we read MINDIRSIZ, residual must |
Line 1244 ufs_dirempty(ip, parentino, cred) |
|
Line 1174 ufs_dirempty(ip, parentino, cred) |
|
* The target is always vput before returning. |
* The target is always vput before returning. |
*/ |
*/ |
int |
int |
ufs_checkpath(source, target, cred) |
ufs_checkpath(struct inode *source, struct inode *target, kauth_cred_t cred) |
struct inode *source, *target; |
|
struct ucred *cred; |
|
{ |
{ |
struct vnode *vp = ITOV(target); |
struct vnode *vp = ITOV(target); |
int error, rootino, namlen; |
int error, rootino, namlen; |
Line 1268 ufs_checkpath(source, target, cred) |
|
Line 1196 ufs_checkpath(source, target, cred) |
|
error = ENOTDIR; |
error = ENOTDIR; |
break; |
break; |
} |
} |
error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, |
error = vn_rdwr(UIO_READ, vp, (void *)&dirbuf, |
sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, |
sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, |
IO_NODELOCKED, cred, NULL, NULL); |
IO_NODELOCKED, cred, NULL, NULL); |
if (error != 0) |
if (error != 0) |
|
|
vput(vp); |
vput(vp); |
return (error); |
return (error); |
} |
} |
|
|
|
#define UFS_DIRRABLKS 0 |
|
int ufs_dirrablks = UFS_DIRRABLKS; |
|
|
|
/* |
|
* ufs_blkatoff: Return buffer with the contents of block "offset" from |
|
* the beginning of directory "vp". If "res" is non-zero, fill it in with |
|
* a pointer to the remaining space in the directory. If the caller intends |
|
* to modify the buffer returned, "modify" must be true. |
|
*/ |
|
|
|
int |
|
ufs_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp, |
|
bool modify) |
|
{ |
|
struct inode *ip; |
|
struct buf *bp; |
|
daddr_t lbn; |
|
const int dirrablks = ufs_dirrablks; |
|
daddr_t *blks; |
|
int *blksizes; |
|
int run, error; |
|
struct mount *mp = vp->v_mount; |
|
const int bshift = mp->mnt_fs_bshift; |
|
const int bsize = 1 << bshift; |
|
off_t eof; |
|
|
|
blks = kmem_alloc((1 + dirrablks) * sizeof(daddr_t), KM_SLEEP); |
|
blksizes = kmem_alloc((1 + dirrablks) * sizeof(int), KM_SLEEP); |
|
ip = VTOI(vp); |
|
KASSERT(vp->v_size == ip->i_size); |
|
GOP_SIZE(vp, vp->v_size, &eof, 0); |
|
lbn = offset >> bshift; |
|
|
|
for (run = 0; run <= dirrablks;) { |
|
const off_t curoff = lbn << bshift; |
|
const int size = MIN(eof - curoff, bsize); |
|
|
|
if (size == 0) { |
|
break; |
|
} |
|
KASSERT(curoff < eof); |
|
blks[run] = lbn; |
|
blksizes[run] = size; |
|
lbn++; |
|
run++; |
|
if (size != bsize) { |
|
break; |
|
} |
|
} |
|
KASSERT(run >= 1); |
|
error = breadn(vp, blks[0], blksizes[0], &blks[1], &blksizes[1], |
|
run - 1, NOCRED, (modify ? B_MODIFY : 0), &bp); |
|
if (error != 0) { |
|
brelse(bp, 0); |
|
*bpp = NULL; |
|
goto out; |
|
} |
|
if (res) { |
|
*res = (char *)bp->b_data + (offset & (bsize - 1)); |
|
} |
|
*bpp = bp; |
|
|
|
out: |
|
kmem_free(blks, (1 + dirrablks) * sizeof(daddr_t)); |
|
kmem_free(blksizes, (1 + dirrablks) * sizeof(int)); |
|
return error; |
|
} |