disk.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:16k
- /*
- * $Id: disk.c,v 1.141.2.1 1999/02/12 22:32:15 wessels Exp $
- *
- * DEBUG: section 6 Disk I/O Routines
- * AUTHOR: Harvest Derived
- *
- * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
- * ----------------------------------------------------------
- *
- * Squid is the result of efforts by numerous individuals from the
- * Internet community. Development is led by Duane Wessels of the
- * National Laboratory for Applied Network Research and funded by the
- * National Science Foundation. Squid is Copyrighted (C) 1998 by
- * Duane Wessels and the University of California San Diego. Please
- * see the COPYRIGHT file for full details. Squid incorporates
- * software developed and/or copyrighted by other sources. Please 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"
- #define DISK_LINE_LEN 1024
- typedef struct open_ctrl_t {
- FOCB *callback;
- void *callback_data;
- char *path;
- } open_ctrl_t;
- static AIOCB diskHandleWriteComplete;
- static AIOCB diskHandleReadComplete;
- static PF diskHandleRead;
- static PF diskHandleWrite;
- static AIOCB fileOpenComplete;
- void
- disk_init(void)
- {
- #if USE_ASYNC_IO
- aioClose(dup(0));
- #endif
- }
- /* Open a disk file. Return a file descriptor */
- int
- file_open(const char *path, int mode, FOCB * callback, void *callback_data, void *tag)
- {
- int fd;
- open_ctrl_t *ctrlp;
- ctrlp = xmalloc(sizeof(open_ctrl_t));
- ctrlp->path = xstrdup(path);
- ctrlp->callback = callback;
- ctrlp->callback_data = callback_data;
- if (mode & O_WRONLY)
- mode |= O_APPEND;
- mode |= SQUID_NONBLOCK;
- /* Open file */
- Opening_FD++;
- #if USE_ASYNC_IO
- if (callback != NULL) {
- aioOpen(path, mode, 0644, fileOpenComplete, ctrlp, tag);
- return DISK_OK;
- }
- #endif
- errno = 0;
- fd = open(path, mode, 0644);
- fileOpenComplete(-1, ctrlp, fd, errno);
- if (fd < 0)
- return DISK_ERROR;
- return fd;
- }
- static void
- fileOpenComplete(int unused, void *data, int fd, int errcode)
- {
- open_ctrl_t *ctrlp = (open_ctrl_t *) data;
- debug(6, 5) ("fileOpenComplete: FD %d, data %p, errcode %dn",
- fd, data, errcode);
- Counter.syscalls.disk.opens++;
- Opening_FD--;
- if (fd == -2 && errcode == -2) { /* Cancelled - clean up */
- if (ctrlp->callback)
- (ctrlp->callback) (ctrlp->callback_data, fd, errcode);
- xfree(ctrlp->path);
- xfree(ctrlp);
- return;
- }
- if (fd < 0) {
- errno = errcode;
- debug(50, 3) ("fileOpenComplete: error opening file %s: %sn", ctrlp->path,
- xstrerror());
- if (ctrlp->callback)
- (ctrlp->callback) (ctrlp->callback_data, DISK_ERROR, errcode);
- xfree(ctrlp->path);
- xfree(ctrlp);
- return;
- }
- debug(6, 5) ("fileOpenComplete: FD %dn", fd);
- commSetCloseOnExec(fd);
- fd_open(fd, FD_FILE, ctrlp->path);
- if (ctrlp->callback)
- (ctrlp->callback) (ctrlp->callback_data, fd, errcode);
- xfree(ctrlp->path);
- xfree(ctrlp);
- }
- /* close a disk file. */
- void
- file_close(int fd)
- {
- fde *F = &fd_table[fd];
- PF *callback;
- #if USE_ASYNC_IO
- if (fd < 0) {
- debug(6, 0) ("file_close: FD less than zero: %dn", fd);
- return;
- }
- #else
- assert(fd >= 0);
- #endif
- assert(F->flags.open);
- if ((callback = F->read_handler)) {
- F->read_handler = NULL;
- callback(-1, F->read_data);
- }
- if (F->flags.write_daemon) {
- #if defined(_SQUID_MSWIN_) || defined(_SQUID_OS2_)
- /*
- * on some operating systems, you can not delete or rename
- * open files, so we won't allow delayed close.
- */
- while (!diskWriteIsComplete(fd))
- diskHandleWrite(fd, NULL);
- #else
- F->flags.close_request = 1;
- debug(6, 2) ("file_close: FD %d, delaying closen", fd);
- return;
- #endif
- }
- /*
- * Assert there is no write callback. Otherwise we might be
- * leaking write state data by closing the descriptor
- */
- assert(F->write_handler == NULL);
- F->flags.closing = 1;
- #if USE_ASYNC_IO
- aioClose(fd);
- #else
- #if CALL_FSYNC_BEFORE_CLOSE
- fsync(fd);
- #endif
- close(fd);
- #endif
- debug(6, F->flags.close_request ? 2 : 5)
- ("file_close: FD %d, really closingn", fd);
- #if !USE_ASYNC_IO
- fd_close(fd);
- #endif
- Counter.syscalls.disk.closes++;
- }
- /*
- * This function has the purpose of combining multiple writes. This is
- * to facilitate the ASYNC_IO option since it can only guarantee 1
- * write to a file per trip around the comm.c select() loop. That's bad
- * because more than 1 write can be made to the access.log file per
- * trip, and so this code is purely designed to help batch multiple
- * sequential writes to the access.log file. Squid will never issue
- * multiple writes for any other file type during 1 trip around the
- * select() loop. --SLF
- */
- static void
- diskCombineWrites(struct _fde_disk *fdd)
- {
- int len = 0;
- dwrite_q *q = NULL;
- dwrite_q *wq = NULL;
- /*
- * We need to combine multiple write requests on an FD's write
- * queue But only if we don't need to seek() in between them, ugh!
- * XXX This currently ignores any seeks (file_offset)
- */
- if (fdd->write_q != NULL && fdd->write_q->next != NULL) {
- len = 0;
- for (q = fdd->write_q; q != NULL; q = q->next)
- len += q->len - q->buf_offset;
- wq = xcalloc(1, sizeof(dwrite_q));
- wq->buf = xmalloc(len);
- wq->len = 0;
- wq->buf_offset = 0;
- wq->next = NULL;
- wq->free_func = xfree;
- do {
- q = fdd->write_q;
- len = q->len - q->buf_offset;
- xmemcpy(wq->buf + wq->len, q->buf + q->buf_offset, len);
- wq->len += len;
- fdd->write_q = q->next;
- if (q->free_func)
- (q->free_func) (q->buf);
- safe_free(q);
- } while (fdd->write_q != NULL);
- fdd->write_q_tail = wq;
- fdd->write_q = wq;
- }
- }
- /* write handler */
- static void
- diskHandleWrite(int fd, void *notused)
- {
- #if !USE_ASYNC_IO
- int len = 0;
- #endif
- fde *F = &fd_table[fd];
- struct _fde_disk *fdd = &F->disk;
- if (!fdd->write_q)
- return;
- #ifdef OPTIMISTIC_IO
- assert(!F->flags.calling_io_handler);
- #endif
- debug(6, 3) ("diskHandleWrite: FD %dn", fd);
- assert(fdd->write_q != NULL);
- assert(fdd->write_q->len > fdd->write_q->buf_offset);
- #if USE_ASYNC_IO
- aioWrite(fd,
- -1, /* seek offset, -1 == append */
- fdd->write_q->buf + fdd->write_q->buf_offset,
- fdd->write_q->len - fdd->write_q->buf_offset,
- diskHandleWriteComplete,
- fdd->write_q);
- #else
- debug(6, 3) ("diskHandleWrite: FD %d writing %d bytesn",
- fd, (int) (fdd->write_q->len - fdd->write_q->buf_offset));
- errno = 0;
- len = write(fd,
- fdd->write_q->buf + fdd->write_q->buf_offset,
- fdd->write_q->len - fdd->write_q->buf_offset);
- diskHandleWriteComplete(fd, fdd->write_q, len, errno);
- #endif
- }
- static void
- diskHandleWriteComplete(int fd, void *data, int len, int errcode)
- {
- fde *F = &fd_table[fd];
- struct _fde_disk *fdd = &F->disk;
- dwrite_q *q = fdd->write_q;
- int status = DISK_OK;
- int do_callback;
- int do_close;
- errno = errcode;
- debug(6, 3) ("diskHandleWriteComplete: FD %d len = %dn", fd, len);
- Counter.syscalls.disk.writes++;
- #if USE_ASYNC_IO
- /*
- * From: "Michael O'Reilly" <michael@metal.iinet.net.au>
- * Date: 24 Feb 1998 15:12:06 +0800
- *
- * A small patch to improve the AIO sanity. the patch below makes sure
- * the write request really does match the data passed back from the
- * async IO call. note that I haven't actually rebooted with this
- * patch yet, so 'provisional' is an understatement.
- */
- if (q && q != data) {
- dwrite_q *p = data;
- debug(50, 0) ("KARMA: q != data (%p, %p)n", q, p);
- debug(50, 0) ("KARMA: (%d, %d, %d FD %d)n",
- q->buf_offset, q->len, len, fd);
- debug(50, 0) ("KARMA: desc %s, type %d, open %d, flags 0x%xn",
- F->desc, F->type, F->flags.open, F->flags);
- debug(50, 0) ("KARMA: (%d, %d)n", p->buf_offset, p->len);
- len = -1;
- errcode = EFAULT;
- }
- #endif
- if (q == NULL) /* Someone aborted then write completed */
- return;
- if (len == -2 && errcode == -2) { /* Write cancelled - cleanup */
- do {
- fdd->write_q = q->next;
- if (q->free_func)
- (q->free_func) (q->buf);
- safe_free(q);
- } while ((q = fdd->write_q));
- return;
- }
- fd_bytes(fd, len, FD_WRITE);
- if (len < 0) {
- if (!ignoreErrno(errno)) {
- status = errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
- debug(50, 1) ("diskHandleWrite: FD %d: disk write error: %sn",
- fd, xstrerror());
- /*
- * If there is no write callback, then this file is
- * most likely something important like a log file, or
- * an interprocess pipe. Its not a swapfile. We feel
- * that a write failure on a log file is rather important,
- * and Squid doesn't otherwise deal with this condition.
- * So to get the administrators attention, we exit with
- * a fatal message.
- */
- if (fdd->wrt_handle == NULL)
- fatal("Write failure -- check your disk space and cache.log");
- /*
- * If there is a write failure, then we notify the
- * upper layer via the callback, at the end of this
- * function. Meanwhile, flush all pending buffers
- * here. Let the upper layer decide how to handle the
- * failure. This will prevent experiencing multiple,
- * repeated write failures for the same FD because of
- * the queued data.
- */
- do {
- fdd->write_q = q->next;
- if (q->free_func)
- (q->free_func) (q->buf);
- safe_free(q);
- } while ((q = fdd->write_q));
- }
- len = 0;
- }
- if (q != NULL) {
- /* q might become NULL from write failure above */
- q->buf_offset += len;
- if (q->buf_offset > q->len)
- debug(50, 1) ("diskHandleWriteComplete: q->buf_offset > q->len (%p,%d, %d, %d FD %d)n",
- q, (int) q->buf_offset, q->len, len, fd);
- assert(q->buf_offset <= q->len);
- if (q->buf_offset == q->len) {
- /* complete write */
- fdd->write_q = q->next;
- if (q->free_func)
- (q->free_func) (q->buf);
- safe_free(q);
- }
- }
- if (fdd->write_q == NULL) {
- /* no more data */
- fdd->write_q_tail = NULL;
- F->flags.write_daemon = 0;
- } else {
- /* another block is queued */
- diskCombineWrites(fdd);
- cbdataLock(fdd->wrt_handle_data);
- commSetSelect(fd, COMM_SELECT_WRITE, diskHandleWrite, NULL, 0);
- F->flags.write_daemon = 1;
- }
- do_close = F->flags.close_request;
- if (fdd->wrt_handle) {
- if (fdd->wrt_handle_data == NULL)
- do_callback = 1;
- else if (cbdataValid(fdd->wrt_handle_data))
- do_callback = 1;
- else
- do_callback = 0;
- if (fdd->wrt_handle_data != NULL)
- cbdataUnlock(fdd->wrt_handle_data);
- if (do_callback) {
- #ifdef OPTIMISTIC_IO
- F->flags.calling_io_handler = 1;
- #endif
- fdd->wrt_handle(fd, status, len, fdd->wrt_handle_data);
- /*
- * NOTE, this callback can close the FD, so we must
- * not touch 'F', 'fdd', etc. after this.
- */
- #ifdef OPTIMISTIC_IO
- F->flags.calling_io_handler = 0;
- #endif
- return;
- }
- }
- if (do_close)
- file_close(fd);
- }
- /* write block to a file */
- /* write back queue. Only one writer at a time. */
- /* call a handle when writing is complete. */
- void
- file_write(int fd,
- off_t file_offset,
- void *ptr_to_buf,
- int len,
- DWCB handle,
- void *handle_data,
- FREE * free_func)
- {
- dwrite_q *wq = NULL;
- fde *F = &fd_table[fd];
- assert(fd >= 0);
- assert(F->flags.open);
- /* if we got here. Caller is eligible to write. */
- wq = xcalloc(1, sizeof(dwrite_q));
- wq->file_offset = file_offset;
- wq->buf = ptr_to_buf;
- wq->len = len;
- wq->buf_offset = 0;
- wq->next = NULL;
- wq->free_func = free_func;
- F->disk.wrt_handle = handle;
- F->disk.wrt_handle_data = handle_data;
- /* add to queue */
- if (F->disk.write_q == NULL) {
- /* empty queue */
- F->disk.write_q = F->disk.write_q_tail = wq;
- } else {
- F->disk.write_q_tail->next = wq;
- F->disk.write_q_tail = wq;
- }
- if (!F->flags.write_daemon) {
- cbdataLock(F->disk.wrt_handle_data);
- #if USE_ASYNC_IO
- diskHandleWrite(fd, NULL);
- #else
- #ifdef OPTIMISTIC_IO
- if (F->flags.calling_io_handler)
- #endif
- commSetSelect(fd, COMM_SELECT_WRITE, diskHandleWrite, NULL, 0);
- #ifdef OPTIMISTIC_IO
- else
- diskHandleWrite(fd, NULL);
- #endif
- #endif
- #ifndef OPTIMISTIC_IO
- F->flags.write_daemon = 1;
- #endif
- }
- }
- /*
- * a wrapper around file_write to allow for MemBuf to be file_written
- * in a snap
- */
- void
- file_write_mbuf(int fd, off_t off, MemBuf mb, DWCB * handler, void *handler_data)
- {
- file_write(fd, off, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb));
- }
- /* Read from FD */
- static void
- diskHandleRead(int fd, void *data)
- {
- dread_ctrl *ctrl_dat = data;
- #if !USE_ASYNC_IO
- fde *F = &fd_table[fd];
- int len;
- #endif
- #ifdef OPTIMISTIC_IO
- assert(!F->flags.calling_io_handler);
- #endif /* OPTIMISTIC_IO */
- /*
- * FD < 0 indicates premature close; we just have to free
- * the state data.
- */
- if (fd < 0) {
- memFree(ctrl_dat, MEM_DREAD_CTRL);
- return;
- }
- #if USE_ASYNC_IO
- aioRead(fd,
- ctrl_dat->offset,
- ctrl_dat->buf,
- ctrl_dat->req_len,
- diskHandleReadComplete,
- ctrl_dat);
- #else
- if (F->disk.offset != ctrl_dat->offset) {
- debug(6, 3) ("diskHandleRead: FD %d seeking to offset %dn",
- fd, (int) ctrl_dat->offset);
- lseek(fd, ctrl_dat->offset, SEEK_SET); /* XXX ignore return? */
- Counter.syscalls.disk.seeks++;
- F->disk.offset = ctrl_dat->offset;
- }
- errno = 0;
- len = read(fd, ctrl_dat->buf, ctrl_dat->req_len);
- if (len > 0)
- F->disk.offset += len;
- diskHandleReadComplete(fd, ctrl_dat, len, errno);
- #endif
- }
- static void
- diskHandleReadComplete(int fd, void *data, int len, int errcode)
- {
- dread_ctrl *ctrl_dat = data;
- int rc = DISK_OK;
- #ifdef OPTIMISTIC_IO
- fde *F = &fd_table[fd];
- #endif /* OPTIMISTIC_IO */
- Counter.syscalls.disk.reads++;
- errno = errcode;
- if (len == -2 && errcode == -2) { /* Read cancelled - cleanup */
- cbdataUnlock(ctrl_dat->client_data);
- memFree(ctrl_dat, MEM_DREAD_CTRL);
- return;
- }
- fd_bytes(fd, len, FD_READ);
- if (len < 0) {
- if (ignoreErrno(errno)) {
- commSetSelect(fd, COMM_SELECT_READ, diskHandleRead, ctrl_dat, 0);
- return;
- }
- debug(50, 1) ("diskHandleRead: FD %d: %sn", fd, xstrerror());
- len = 0;
- rc = DISK_ERROR;
- } else if (len == 0) {
- rc = DISK_EOF;
- }
- #ifdef OPTIMISTIC_IO
- F->flags.calling_io_handler = 1;
- #endif /* OPTIMISTIC_IO */
- if (cbdataValid(ctrl_dat->client_data))
- ctrl_dat->handler(fd, ctrl_dat->buf, len, rc, ctrl_dat->client_data);
- #ifdef OPTIMISTIC_IO
- F->flags.calling_io_handler = 0;
- #endif /* OPTIMISTIC_IO */
- cbdataUnlock(ctrl_dat->client_data);
- memFree(ctrl_dat, MEM_DREAD_CTRL);
- }
- /* start read operation */
- /* buffer must be allocated from the caller.
- * It must have at least req_len space in there.
- * call handler when a reading is complete. */
- int
- file_read(int fd, char *buf, int req_len, off_t offset, DRCB * handler, void *client_data)
- {
- dread_ctrl *ctrl_dat;
- #ifdef OPTIMISTIC_IO
- fde *F = &fd_table[fd];
- #endif /* OPTIMISTIC_IO */
- assert(fd >= 0);
- ctrl_dat = memAllocate(MEM_DREAD_CTRL);
- ctrl_dat->fd = fd;
- ctrl_dat->offset = offset;
- ctrl_dat->req_len = req_len;
- ctrl_dat->buf = buf;
- ctrl_dat->end_of_file = 0;
- ctrl_dat->handler = handler;
- ctrl_dat->client_data = client_data;
- cbdataLock(client_data);
- #if USE_ASYNC_IO
- diskHandleRead(fd, ctrl_dat);
- #else
- #ifndef OPTIMISTIC_IO
- commSetSelect(fd,
- COMM_SELECT_READ,
- diskHandleRead,
- ctrl_dat,
- 0);
- #else
- if (F->flags.calling_io_handler)
- commSetSelect(fd, COMM_SELECT_READ, diskHandleRead, ctrl_dat, 0);
- else
- diskHandleRead(fd, ctrl_dat);
- #endif /* OPTIMISTIC_IO */
- #endif
- return DISK_OK;
- }
- int
- diskWriteIsComplete(int fd)
- {
- return fd_table[fd].disk.write_q ? 0 : 1;
- }