
/*
 * $Id: store_io_ufs.c,v 1.16 2006/11/05 21:14:37 hno Exp $
 *
 * DEBUG: section 79    Storage Manager UFS Interface
 * AUTHOR: Duane Wessels
 *
 * SQUID Web Proxy Cache          http://www.squid-cache.org/
 * ----------------------------------------------------------
 *
 *  Squid is the result of efforts by numerous individuals from
 *  the Internet community; see the CONTRIBUTORS file for full
 *  details.   Many organizations have provided support for Squid's
 *  development; see the SPONSORS file for full details.  Squid is
 *  Copyrighted (C) 2001 by the Regents of the University of
 *  California; see the COPYRIGHT file for full details.  Squid
 *  incorporates software developed and/or copyrighted by other
 *  sources; see the CREDITS file for full details.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 */

#include "squid.h"
#include "store_ufs.h"


static DRCB storeUfsReadDone;
static DWCB storeUfsWriteDone;
static void storeUfsIOCallback(storeIOState * sio, int errflag);
static CBDUNL storeUfsIOFreeEntry;

CBDATA_TYPE(storeIOState);

/* === PUBLIC =========================================================== */

storeIOState *
storeUfsOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback,
    STIOCB * callback, void *callback_data)
{
    ufsinfo_t *ufsinfo = (ufsinfo_t *) SD->fsdata;
    sfileno f = e->swap_filen;
    char *path = storeUfsDirFullPath(SD, f, NULL);
    storeIOState *sio;
    struct stat sb;
    int fd;
    debug(79, 3) ("storeUfsOpen: fileno %08X\n", f);
    fd = file_open(path, O_RDONLY | O_BINARY | O_NOATIME);
    if (fd < 0) {
	debug(79, 3) ("storeUfsOpen: got failure (%d)\n", errno);
	return NULL;
    }
    debug(79, 3) ("storeUfsOpen: opened FD %d\n", fd);
    CBDATA_INIT_TYPE_FREECB(storeIOState, storeUfsIOFreeEntry);
    sio = cbdataAlloc(storeIOState);
    sio->fsstate = memPoolAlloc(ufs_state_pool);

    sio->swap_filen = f;
    sio->swap_dirn = SD->index;
    sio->mode = O_RDONLY | O_BINARY;
    sio->callback = callback;
    sio->callback_data = callback_data;
    cbdataLock(callback_data);
    sio->e = e;
    ((ufsstate_t *) (sio->fsstate))->fd = fd;
    ((ufsstate_t *) (sio->fsstate))->flags.writing = 0;
    ((ufsstate_t *) (sio->fsstate))->flags.reading = 0;
    ((ufsstate_t *) (sio->fsstate))->flags.close_request = 0;
    if (fstat(fd, &sb) == 0)
	sio->st_size = sb.st_size;
    store_open_disk_fd++;
    ufsinfo->open_files++;

    /* We should update the heap/dlink position here ! */
    return sio;
}

storeIOState *
storeUfsCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * callback, void *callback_data)
{
    storeIOState *sio;
    int fd;
    int mode = (O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
    char *path;
    ufsinfo_t *ufsinfo = (ufsinfo_t *) SD->fsdata;
    sfileno filn;
    sdirno dirn;

    /* Allocate a number */
    dirn = SD->index;
    filn = storeUfsDirMapBitAllocate(SD);
    ufsinfo->suggest = filn + 1;
    /* Shouldn't we handle a 'bitmap full' error here? */
    path = storeUfsDirFullPath(SD, filn, NULL);

    debug(79, 3) ("storeUfsCreate: fileno %08X\n", filn);
    fd = file_open(path, mode);
    if (fd < 0) {
	debug(79, 1) ("storeUfsCreate: Failed to create %s (%s)\n", path, xstrerror());
	return NULL;
    }
    debug(79, 3) ("storeUfsCreate: opened FD %d\n", fd);
    CBDATA_INIT_TYPE_FREECB(storeIOState, storeUfsIOFreeEntry);
    sio = cbdataAlloc(storeIOState);
    sio->fsstate = memPoolAlloc(ufs_state_pool);

    sio->swap_filen = filn;
    sio->swap_dirn = dirn;
    sio->mode = mode;
    sio->callback = callback;
    sio->callback_data = callback_data;
    cbdataLock(callback_data);
    sio->e = (StoreEntry *) e;
    ((ufsstate_t *) (sio->fsstate))->fd = fd;
    ((ufsstate_t *) (sio->fsstate))->flags.writing = 0;
    ((ufsstate_t *) (sio->fsstate))->flags.reading = 0;
    ((ufsstate_t *) (sio->fsstate))->flags.close_request = 0;
    store_open_disk_fd++;
    ufsinfo->open_files++;

    /* now insert into the replacement policy */
    storeUfsDirReplAdd(SD, e);
    return sio;
}

void
storeUfsClose(SwapDir * SD, storeIOState * sio)
{
    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;

    debug(79, 3) ("storeUfsClose: dirno %d, fileno %08X, FD %d\n",
	sio->swap_dirn, sio->swap_filen, ufsstate->fd);
    if (ufsstate->flags.reading || ufsstate->flags.writing) {
	ufsstate->flags.close_request = 1;
	return;
    }
    storeUfsIOCallback(sio, 0);
}

void
storeUfsRead(SwapDir * SD, storeIOState * sio, char *buf, size_t size, squid_off_t offset, STRCB * callback, void *callback_data)
{
    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;

    assert(sio->read.callback == NULL);
    assert(sio->read.callback_data == NULL);
    sio->read.callback = callback;
    sio->read.callback_data = callback_data;
    cbdataLock(callback_data);
    debug(79, 3) ("storeUfsRead: dirno %d, fileno %08X, FD %d\n",
	sio->swap_dirn, sio->swap_filen, ufsstate->fd);
    sio->offset = offset;
    ufsstate->flags.reading = 1;
    file_read(ufsstate->fd,
	buf,
	size,
	(off_t) offset,
	storeUfsReadDone,
	sio);
}

void
storeUfsWrite(SwapDir * SD, storeIOState * sio, char *buf, size_t size, squid_off_t offset, FREE * free_func)
{
    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
    debug(79, 3) ("storeUfsWrite: dirn %d, fileno %08X, FD %d\n", sio->swap_dirn, sio->swap_filen, ufsstate->fd);
    ufsstate->flags.writing = 1;
    file_write(ufsstate->fd,
	(off_t) offset,
	buf,
	size,
	storeUfsWriteDone,
	sio,
	free_func);
}

void
storeUfsUnlink(SwapDir * SD, StoreEntry * e)
{
    debug(79, 3) ("storeUfsUnlink: fileno %08X\n", e->swap_filen);
    storeUfsDirReplRemove(e);
    storeUfsDirMapBitReset(SD, e->swap_filen);
    storeUfsDirUnlinkFile(SD, e->swap_filen);
}

void
storeUfsRecycle(SwapDir * SD, StoreEntry * e)
{
    debug(79, 3) ("storeUfsUnlink: fileno %08X\n", e->swap_filen);

    /* Detach from underlying physical object */
    if (e->swap_filen > -1) {
	storeUfsDirReplRemove(e);
	storeUfsDirMapBitReset(SD, e->swap_filen);
	e->swap_filen = -1;
	e->swap_dirn = -1;
    }
}

/*  === STATIC =========================================================== */

static void
storeUfsReadDone(int fd, const char *buf, int len, int errflag, void *my_data)
{
    storeIOState *sio = my_data;
    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
    STRCB *callback = sio->read.callback;
    void *their_data = sio->read.callback_data;
    ssize_t rlen;

    debug(79, 3) ("storeUfsReadDone: dirno %d, fileno %08X, FD %d, len %d\n",
	sio->swap_dirn, sio->swap_filen, fd, len);
    ufsstate->flags.reading = 0;
    if (errflag) {
	debug(79, 3) ("storeUfsReadDone: got failure (%d)\n", errflag);
	rlen = -1;
    } else {
	rlen = len;
	sio->offset += len;
    }
    assert(callback);
    assert(their_data);
    sio->read.callback = NULL;
    sio->read.callback_data = NULL;
    if (cbdataValid(their_data))
	callback(their_data, buf, rlen);
    cbdataUnlock(their_data);
}

static void
storeUfsWriteDone(int fd, int errflag, size_t len, void *my_data)
{
    storeIOState *sio = my_data;
    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
    debug(79, 3) ("storeUfsWriteDone: dirno %d, fileno %08X, FD %d, len %ld\n",
	sio->swap_dirn, sio->swap_filen, fd, (long int) len);
    ufsstate->flags.writing = 0;
    if (errflag) {
	debug(79, 0) ("storeUfsWriteDone: got failure (%d)\n", errflag);
	storeUfsIOCallback(sio, errflag);
	return;
    }
    sio->offset += len;
    if (ufsstate->flags.close_request)
	storeUfsIOCallback(sio, errflag);
}

static void
storeUfsIOCallback(storeIOState * sio, int errflag)
{
    ufsstate_t *ufsstate = (ufsstate_t *) sio->fsstate;
    debug(79, 3) ("storeUfsIOCallback: errflag=%d\n", errflag);
    if (ufsstate->fd > -1) {
	SwapDir *SD = INDEXSD(sio->swap_dirn);
	ufsinfo_t *ufsinfo = (ufsinfo_t *) SD->fsdata;
	file_close(ufsstate->fd);
	store_open_disk_fd--;
	ufsinfo->open_files--;
    }
    if (cbdataValid(sio->callback_data))
	sio->callback(sio->callback_data, errflag, sio);
    cbdataUnlock(sio->callback_data);
    sio->callback_data = NULL;
    sio->callback = NULL;
    cbdataFree(sio);
}


/*
 * Clean up any references from the SIO before it get's released.
 */
static void
storeUfsIOFreeEntry(void *sio)
{
    memPoolFree(ufs_state_pool, ((storeIOState *) sio)->fsstate);
}
