#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/ubc.h>
#include <sys/quota.h>
#include <miscfs/specfs/specdev.h>
#include <miscfs/fifofs/fifo.h>
#include <hfs/hfs.h>
#include <hfs/hfs_catalog.h>
#include <hfs/hfs_cnode.h>
#include <hfs/hfs_quota.h>
extern int prtactive;
extern void hfs_relnamehints(struct cnode *dcp);
__private_extern__
int
hfs_inactive(ap)
struct vop_inactive_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
struct hfsmount *hfsmp = VTOHFS(vp);
struct proc *p = ap->a_p;
struct timeval tv;
int error = 0;
int recycle = 0;
int forkcount = 0;
int truncated = 0;
int started_tr = 0, grabbed_lock = 0;
cat_cookie_t cookie;
int cat_reserve = 0;
if (prtactive && vp->v_usecount != 0)
vprint("hfs_inactive: pushing active", vp);
if (cp->c_mode == 0)
goto out;
if (hfsmp->hfs_flags & HFS_READ_ONLY)
goto out;
if (cp->c_datafork)
++forkcount;
if (cp->c_rsrcfork)
++forkcount;
if ((vp->v_type == VREG) && (cp->c_flag & C_DELETED)) {
if (VTOF(vp)->ff_blocks != 0) {
error = VOP_TRUNCATE(vp, (off_t)0, IO_NDELAY, NOCRED, p);
if (error)
goto out;
truncated = 1;
}
recycle = 1;
}
if ((cp->c_flag & C_DELETED) && (forkcount <= 1)) {
SET(cp->c_flag, C_TRANSIT);
cp->c_flag &= ~C_DELETED;
cp->c_rdev = 0;
hfs_global_shared_lock_acquire(hfsmp);
grabbed_lock = 1;
if (hfsmp->jnl) {
if (journal_start_transaction(hfsmp->jnl) != 0) {
error = EINVAL;
goto out;
}
started_tr = 1;
}
if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
goto out;
}
cat_reserve = 1;
error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (error) goto out;
if (cp->c_blocks > 0)
printf("hfs_inactive: attempting to delete a non-empty file!");
error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
if (error && truncated && (error != ENXIO))
printf("hfs_inactive: couldn't delete a truncated file!");
if (error == 0) {
hfsmp->hfs_privdir_attr.ca_entries--;
(void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
&hfsmp->hfs_privdir_attr, NULL, NULL);
}
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (error) goto out;
#if QUOTA
(void)hfs_chkiq(cp, -1, NOCRED, 0);
#endif
cp->c_mode = 0;
cp->c_flag |= C_NOEXISTS | C_CHANGE | C_UPDATE;
if (error == 0)
hfs_volupdate(hfsmp, VOL_RMFILE, 0);
}
if (cp->c_flag & (C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE)) {
tv = time;
if ((cp->c_flag & C_CHANGEMASK) == C_ACCESS) {
cp->c_flag |= C_MODIFIED;
}
VOP_UPDATE(vp, &tv, &tv, 0);
}
out:
if (cat_reserve)
cat_postflight(hfsmp, &cookie, p);
if (started_tr) {
journal_end_transaction(hfsmp->jnl);
started_tr = 0;
}
if (grabbed_lock) {
hfs_global_shared_lock_release(hfsmp);
}
VOP_UNLOCK(vp, 0, p);
if (cp->c_mode == 0 || recycle)
vrecycle(vp, (struct slock *)0, p);
return (error);
}
__private_extern__
int
hfs_reclaim(ap)
struct vop_reclaim_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
struct vnode *devvp = NULL;
struct filefork *fp = NULL;
struct filefork *altfp = NULL;
int i;
if (prtactive && vp->v_usecount != 0)
vprint("hfs_reclaim(): pushing active", vp);
(void) hfs_addhotfile(vp);
devvp = cp->c_devvp;
if ((fp = cp->c_datafork) && (cp->c_vp == vp)) {
cp->c_datafork = NULL;
cp->c_vp = NULL;
altfp = cp->c_rsrcfork;
} else if ((fp = cp->c_rsrcfork) && (cp->c_rsrc_vp == vp)) {
cp->c_rsrcfork = NULL;
cp->c_rsrc_vp = NULL;
if (VPARENT(vp) == cp->c_vp) {
cp->c_flag &= ~C_VPREFHELD;
}
altfp = cp->c_datafork;
} else {
cp->c_vp = NULL;
fp = NULL;
altfp = NULL;
}
if (altfp == NULL)
hfs_chashremove(cp);
if (fp) {
fp->ff_cp = NULL;
if ((vp->v_type == VLNK) && (fp->ff_symlinkptr != NULL)) {
FREE(fp->ff_symlinkptr, M_TEMP);
fp->ff_symlinkptr = NULL;
}
FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK);
fp = NULL;
}
cache_purge(vp);
if (devvp && altfp == NULL) {
cp->c_devvp = NULL;
vrele(devvp);
}
vp->v_data = NULL;
if (altfp == NULL) {
#if QUOTA
for (i = 0; i < MAXQUOTAS; i++) {
if (cp->c_dquot[i] != NODQUOT) {
dqreclaim(vp, cp->c_dquot[i]);
cp->c_dquot[i] = NODQUOT;
}
}
#endif
if (vp->v_type == VDIR)
hfs_relnamehints(cp);
if (cp->c_desc.cd_flags & CD_HASBUF) {
char *nameptr;
nameptr = cp->c_desc.cd_nameptr;
cp->c_desc.cd_nameptr = 0;
cp->c_desc.cd_flags &= ~CD_HASBUF;
cp->c_desc.cd_namelen = 0;
remove_name(nameptr);
}
CLR(cp->c_flag, (C_ALLOC | C_TRANSIT));
if (ISSET(cp->c_flag, C_WALLOC) || ISSET(cp->c_flag, C_WTRANSIT))
wakeup(cp);
FREE_ZONE(cp, sizeof(struct cnode), M_HFSNODE);
}
return (0);
}
__private_extern__
int
hfs_getcnode(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *descp, int wantrsrc,
struct cat_attr *attrp, struct cat_fork *forkp, struct vnode **vpp)
{
dev_t dev = hfsmp->hfs_raw_dev;
struct vnode *vp = NULL;
struct vnode *rvp = NULL;
struct vnode *new_vp = NULL;
struct cnode *cp = NULL;
struct proc *p = current_proc();
int retval = E_NONE;
if (HFSTOVFS(hfsmp)->mnt_kern_flag & MNTK_UNMOUNT) {
*vpp = NULL;
return (EPERM);
}
cp = hfs_chashget(dev, cnid, wantrsrc, &vp, &rvp);
if (cp != NULL) {
if ((hfsmp->hfs_privdir_desc.cd_cnid != 0)
&& (cp->c_parentcnid == hfsmp->hfs_privdir_desc.cd_cnid)
&& (cp->c_nlink == 0)) {
retval = ENOENT;
goto exit;
}
if (hfsmp->jnl &&
(cp->c_parentcnid == kRootDirID) &&
((cp->c_cnid == hfsmp->hfs_jnlfileid) ||
(cp->c_cnid == hfsmp->hfs_jnlinfoblkid))) {
retval = ENOENT;
goto exit;
}
if (wantrsrc && rvp != NULL) {
vp = rvp;
rvp = NULL;
goto done;
}
if (!wantrsrc && vp != NULL) {
if (descp && cp->c_flag & C_HARDLINK) {
replace_desc(cp, descp);
}
goto done;
}
}
if (descp != NULL) {
retval = hfs_getnewvnode(hfsmp, cp, descp, wantrsrc, attrp,
forkp, &new_vp);
} else {
struct cat_desc cndesc = {0};
struct cat_attr cnattr = {0};
struct cat_fork cnfork = {0};
if (cnid == kRootParID) {
static char hfs_rootname[] = "/";
cndesc.cd_nameptr = &hfs_rootname[0];
cndesc.cd_namelen = 1;
cndesc.cd_parentcnid = kRootParID;
cndesc.cd_cnid = kRootParID;
cndesc.cd_flags = CD_ISDIR;
cnattr.ca_fileid = kRootParID;
cnattr.ca_nlink = 2;
cnattr.ca_entries = 1;
cnattr.ca_mode = (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
} else {
retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p);
if (retval)
goto exit;
retval = cat_idlookup(hfsmp, cnid, &cndesc, &cnattr, &cnfork);
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (retval)
goto exit;
if ((hfsmp->hfs_privdir_desc.cd_cnid != 0) &&
(cndesc.cd_parentcnid == hfsmp->hfs_privdir_desc.cd_cnid) &&
(cnattr.ca_nlink == 0)) {
cat_releasedesc(&cndesc);
retval = ENOENT;
goto exit;
}
}
retval = hfs_getnewvnode(hfsmp, cp, &cndesc, 0, &cnattr, &cnfork, &new_vp);
if (retval == 0
&& new_vp
&& (VTOC(new_vp)->c_flag & C_HARDLINK)
&& cndesc.cd_nameptr
&& cndesc.cd_namelen > 0) {
replace_desc(VTOC(new_vp), &cndesc);
}
cat_releasedesc(&cndesc);
}
exit:
if (vp)
vrele(vp);
else if (rvp)
vrele(rvp);
if (retval) {
*vpp = NULL;
return (retval);
}
vp = new_vp;
done:
if (vp == NULL)
panic("hfs_getcnode: missing vp!");
if (UBCISVALID(vp))
UBCINFOCHECK("hfs_getcnode", vp);
*vpp = vp;
return (0);
}
extern int (**hfs_vnodeop_p) (void *);
extern int (**hfs_specop_p) (void *);
extern int (**hfs_fifoop_p) (void *);
__private_extern__
int
hfs_getnewvnode(struct hfsmount *hfsmp, struct cnode *cp,
struct cat_desc *descp, int wantrsrc,
struct cat_attr *attrp, struct cat_fork *forkp,
struct vnode **vpp)
{
struct mount *mp = HFSTOVFS(hfsmp);
struct vnode *vp = NULL;
struct vnode *rvp = NULL;
struct vnode *new_vp = NULL;
struct cnode *cp2 = NULL;
struct filefork *fp = NULL;
int allocated = 0;
int i;
int retval;
dev_t dev;
struct proc *p = current_proc();
#if 0
if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
*vpp = NULL;
return (EPERM);
}
#endif
#if !FIFO
if (IFTOVT(attrp->ca_mode) == VFIFO) {
*vpp = NULL;
return (EOPNOTSUPP);
}
#endif
dev = hfsmp->hfs_raw_dev;
if (cp == NULL) {
MALLOC_ZONE(cp2, struct cnode *, sizeof(struct cnode),
M_HFSNODE, M_WAITOK);
bzero(cp2, sizeof(struct cnode));
allocated = 1;
SET(cp2->c_flag, C_ALLOC);
cp2->c_cnid = descp->cd_cnid;
cp2->c_fileid = attrp->ca_fileid;
if (cp2->c_fileid == 0) {
FREE_ZONE(cp2, sizeof(struct cnode), M_HFSNODE);
*vpp = NULL;
return (ENOENT);
}
cp2->c_dev = dev;
lockinit(&cp2->c_lock, PINOD, "cnode", 0, 0);
(void) lockmgr(&cp2->c_lock, LK_EXCLUSIVE, (struct slock *)0, p);
cp = hfs_chashget(dev, attrp->ca_fileid, wantrsrc, &vp, &rvp);
if (cp != NULL) {
FREE_ZONE(cp2, sizeof(struct cnode), M_HFSNODE);
allocated = 0;
if (wantrsrc && rvp != NULL) {
*vpp = rvp;
return (0);
}
if (!wantrsrc && vp != NULL) {
*vpp = vp;
return (0);
}
} else {
cp = cp2;
hfs_chashinsert(cp);
}
}
if ((retval = getnewvnode(VT_HFS, mp, hfs_vnodeop_p, &new_vp))) {
if (allocated) {
hfs_chashremove(cp);
if (ISSET(cp->c_flag, C_WALLOC)) {
CLR(cp->c_flag, C_WALLOC);
wakeup(cp);
}
FREE_ZONE(cp2, sizeof(struct cnode), M_HFSNODE);
allocated = 0;
} else if (rvp) {
vput(rvp);
} else if (vp) {
vput(vp);
}
*vpp = NULL;
return (retval);
}
if (allocated) {
bcopy(attrp, &cp->c_attr, sizeof(struct cat_attr));
bcopy(descp, &cp->c_desc, sizeof(struct cat_desc));
}
new_vp->v_data = cp;
if (wantrsrc && S_ISREG(cp->c_mode))
cp->c_rsrc_vp = new_vp;
else
cp->c_vp = new_vp;
if (rvp)
vrele(rvp);
if (vp)
vrele(vp);
vp = new_vp;
vp->v_ubcinfo = UBC_NOINFO;
if (allocated) {
descp->cd_namelen = 0;
descp->cd_nameptr = NULL;
descp->cd_flags &= ~CD_HASBUF;
if (IFTOVT(cp->c_mode) == VREG &&
(descp->cd_cnid != attrp->ca_fileid)) {
cp->c_flag |= C_HARDLINK;
}
if (IFTOVT(cp->c_mode) != VDIR) {
cp->c_devvp = hfsmp->hfs_devvp;
VREF(cp->c_devvp);
}
#if QUOTA
for (i = 0; i < MAXQUOTAS; i++)
cp->c_dquot[i] = NODQUOT;
#endif
}
if (IFTOVT(cp->c_mode) != VDIR) {
if (forkp && attrp->ca_blocks < forkp->cf_blocks)
panic("hfs_getnewvnode: bad ca_blocks (too small)");
MALLOC_ZONE(fp, struct filefork *, sizeof(struct filefork),
M_HFSFORK, M_WAITOK);
bzero(fp, sizeof(struct filefork));
fp->ff_cp = cp;
if (forkp)
bcopy(forkp, &fp->ff_data, sizeof(struct cat_fork));
rl_init(&fp->ff_invalidranges);
if (wantrsrc) {
if (cp->c_rsrcfork != NULL)
panic("stale rsrc fork");
cp->c_rsrcfork = fp;
} else {
if (cp->c_datafork != NULL)
panic("stale data fork");
cp->c_datafork = fp;
}
}
vp->v_type = IFTOVT(cp->c_mode);
if ((descp->cd_flags & CD_ISMETA) && (vp->v_type == VREG))
vp->v_flag |= VSYSTEM;
if (cp->c_cnid == kRootDirID)
vp->v_flag |= VROOT;
if ((vp->v_type == VREG) && !(vp->v_flag & VSYSTEM)
&& (UBCINFOMISSING(vp) || UBCINFORECLAIMED(vp))) {
ubc_info_init(vp);
} else {
vp->v_ubcinfo = UBC_NOINFO;
}
if (vp->v_type == VCHR || vp->v_type == VBLK) {
struct vnode *nvp;
vp->v_op = hfs_specop_p;
if ((nvp = checkalias(vp, cp->c_rdev, mp))) {
nvp->v_data = vp->v_data;
vp->v_data = NULL;
vp->v_op = spec_vnodeop_p;
vrele(vp);
vgone(vp);
cp->c_vp = nvp;
vp = nvp;
}
} else if (vp->v_type == VFIFO) {
#if FIFO
vp->v_op = hfs_fifoop_p;
#endif
}
(void) hfs_removehotfile(vp);
CLR(cp->c_flag, C_ALLOC);
if (ISSET(cp->c_flag, C_WALLOC)) {
CLR(cp->c_flag, C_WALLOC);
wakeup((caddr_t)cp);
}
*vpp = vp;
return (0);
}