/*
 * Copyright 2000, International Business Machines Corporation and others.
 * All Rights Reserved.
 * 
 * This software has been released under the terms of the IBM Public
 * License.  For details, see the LICENSE file in the top-level source
 * directory or online at http://www.openafs.org/dl/license10.html
 */

/*
	System:		VICE-TWO
	Module:		purge.c
	Institution:	The Information Technology Center, Carnegie-Mellon University

 */
#include <afsconfig.h>
#include <afs/param.h>

RCSID("$Header: /cvs/openafs/src/vol/purge.c,v 1.5 2001/08/08 00:04:23 shadow Exp $");

#include <stdio.h>
#ifdef AFS_NT40_ENV
#include <fcntl.h>
#include <io.h>
#else
#include <sys/param.h>
#include <sys/file.h>
#include <sys/time.h>
#endif
#include <sys/stat.h>
#include <afs/assert.h>

#include <rx/xdr.h>
#include "afs/afsint.h"
#include "nfs.h"
#include "lwp.h"
#include "lock.h"
#include <afs/afssyscalls.h>
#include "ihandle.h"
#ifdef AFS_NT40_ENV
#include "ntops.h"
#endif
#include "vnode.h"
#include "volume.h"
#include "viceinode.h"
#include "partition.h"
#include "fssync.h"

/* forward declarations */
void  PurgeIndex_r(Volume *vp, VnodeClass class);
void PurgeHeader_r(Volume *vp);

void VPurgeVolume_r(Error *ec, Volume *vp)
{
    struct DiskPartition *tpartp = vp->partition;    
    char purgePath[MAXPATHLEN];

    /* N.B.  it's important here to use the partition pointed to by the
     * volume header. This routine can, under some circumstances, be called
     * when two volumes with the same id exist on different partitions.
     */
    sprintf(purgePath, "%s/%s", VPartitionPath(vp->partition),
	    VolumeExternalName(V_id(vp)));
    PurgeIndex_r(vp, vLarge);
    PurgeIndex_r(vp, vSmall);
    PurgeHeader_r(vp);
    unlink(purgePath);
    /*
     * Call the fileserver to break all call backs for that volume
     */
	FSYNC_askfs(V_id(vp), tpartp->name, FSYNC_RESTOREVOLUME, 0);
}

void VPurgeVolume(Error *ec, Volume *vp)
{
    VOL_LOCK
    VPurgeVolume_r(ec, vp);
    VOL_UNLOCK
}

#define MAXOBLITATONCE	200
/* delete a portion of an index, adjusting offset appropriately.  Returns 0 if
   things work and we should be called again, 1 if success full and done, and -1
   if an error occurred.  It adjusts offset appropriately on 0 or 1 return codes,
   and otherwise doesn't touch it */
static ObliterateRegion(avp, aclass, afile, aoffset)
afs_int32 *aoffset;
StreamHandle_t *afile;
VnodeClass aclass;
Volume *avp; {
    register struct VnodeClassInfo *vcp;
    Inode inodes[MAXOBLITATONCE];
    register afs_int32 iindex, nscanned;
    afs_int32 offset;
    char buf[SIZEOF_LARGEDISKVNODE];
    int hitEOF;
    register int i;
    register afs_int32 code;
    register struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;

    hitEOF = 0;
    vcp = &VnodeClassInfo[aclass];
    offset = *aoffset;	/* original offset */
    iindex = 0;
    nscanned = 0;
    /* advance over up to MAXOBLITATONCE inodes.  nscanned tells us how many we examined.
       We remember the inodes in an array, and idec them after zeroing them in the index.
       The reason for these contortions is to make volume deletion idempotent, even
       if we crash in the middle of a delete operation. */
    STREAM_SEEK(afile, offset, 0);
    while (1) {
	if (iindex >= MAXOBLITATONCE) {
	    break;
	}
	code = STREAM_READ(vnode, vcp->diskSize, 1, afile);
	nscanned++;
	offset += vcp->diskSize;
	if (code != 1) {
	    hitEOF = 1;
	    break;
	}
	if (vnode->type != vNull) {
	    if (vnode->vnodeMagic != vcp->magic)
		goto fail; /* something really wrong; let salvager take care of it */
	    if (VNDISK_GET_INO(vnode))
		inodes[iindex++] = VNDISK_GET_INO(vnode);
	}
    }

    /* next, obliterate the index and fflush (and fsync) it */
    STREAM_SEEK(afile, *aoffset, 0);	/* seek back to start of vnode index region */
    memset(buf, 0, sizeof(buf));	/* zero out our proto-vnode */
    for(i=0;i<nscanned;i++) {
	if (STREAM_WRITE(buf, vcp->diskSize, 1, afile) != 1)
	    goto fail;
    }
    STREAM_FLUSH(afile);		/* ensure 0s are on the disk */
    OS_SYNC(afile->str_fd);

    /* finally, do the idec's */
    for(i=0;i<iindex;i++) {
	IH_DEC(V_linkHandle(avp), inodes[i], V_parentId(avp));
	DOPOLL;
    }

    /* return the new offset */
    *aoffset = offset;
    return hitEOF;	/* return 1 if hit EOF (don't call again), otherwise 0 */

  fail:
    return -1;
}

void PurgeIndex(Volume *vp, VnodeClass class)
{
    VOL_LOCK
    PurgeIndex_r(vp, class);
    VOL_UNLOCK
}

void  PurgeIndex_r(Volume *vp, VnodeClass class)
{
    StreamHandle_t *ifile;
    struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
    afs_int32 offset;
    register afs_int32 code;
    FdHandle_t *fdP;
    

    fdP = IH_OPEN(vp->vnodeIndex[class].handle);
    if (fdP == NULL)
	return;

    ifile = FDH_FDOPEN(fdP, "r+");
    if (!ifile) {
	FDH_REALLYCLOSE(fdP);
	return;
    }

    offset = vcp->diskSize;
    while(1) {
	code = ObliterateRegion(vp, class, ifile, &offset);
	if (code) break;	/* if error or hit EOF */
    }
    STREAM_CLOSE(ifile);
    FDH_CLOSE(fdP);
}

void PurgeHeader(Volume *vp)
{
    VOL_LOCK
    PurgeHeader_r(vp);
    VOL_UNLOCK
}

void PurgeHeader_r(Volume *vp)
{
    IH_REALLYCLOSE(V_diskDataHandle(vp));
    IH_DEC(V_linkHandle(vp), vp->vnodeIndex[vLarge].handle->ih_ino, V_id(vp));
    IH_DEC(V_linkHandle(vp), vp->vnodeIndex[vSmall].handle->ih_ino, V_id(vp));
    IH_DEC(V_linkHandle(vp), vp->diskDataHandle->ih_ino, V_id(vp));
#ifdef AFS_NAMEI_ENV
    /* And last, but not least, the link count table itself. */
    IH_REALLYCLOSE(V_linkHandle(vp));
    IH_DEC(V_linkHandle(vp), vp->linkHandle->ih_ino, V_parentId(vp));
#endif
}


