summaryrefslogtreecommitdiff
path: root/ftpfs/ccache.c
diff options
context:
space:
mode:
Diffstat (limited to 'ftpfs/ccache.c')
-rw-r--r--ftpfs/ccache.c285
1 files changed, 285 insertions, 0 deletions
diff --git a/ftpfs/ccache.c b/ftpfs/ccache.c
new file mode 100644
index 00000000..c18f7577
--- /dev/null
+++ b/ftpfs/ccache.c
@@ -0,0 +1,285 @@
+/* Remote file contents caching
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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 <unistd.h>
+#include <string.h>
+
+#include <hurd/netfs.h>
+
+#include "ccache.h"
+
+#define READ_CHUNK_SIZE (8*1024)
+#define ALLOC_CHUNK_SIZE (64*1024)
+
+/* Read LEN bytes at OFFS in the file referred to by CC into DATA, or return
+ an error. */
+error_t
+ccache_read (struct ccache *cc, off_t offs, size_t len, void *data)
+{
+ error_t err = 0;
+ size_t max = offs + len;
+
+ mutex_lock (&cc->lock);
+
+ if (max > cc->size)
+ max = cc->size;
+
+ while (cc->max < max && !err)
+ {
+ if (cc->fetching_active)
+ /* Some thread is fetching data, so just let it do its thing, but get
+ a wakeup call when it's done. */
+ {
+ if (hurd_condition_wait (&cc->wakeup, &cc->lock))
+ err = EINTR;
+ }
+ else
+ {
+ int re_connected = 0;
+ struct netnode *nn = cc->node->nn;
+
+ cc->fetching_active = 1;
+
+ while (cc->max < max && !err)
+ {
+ mutex_unlock (&cc->lock);
+
+ if (! cc->conn)
+ /* We need to setup a connection to fetch data over. */
+ {
+ err = ftpfs_get_ftp_conn (nn->fs, &cc->conn);
+ if (! err)
+ {
+ err = ftp_conn_start_retrieve (cc->conn, nn->rmt_path,
+ &cc->data_conn);
+ if (err == ENOENT)
+ err = ESTALE;
+ if (err)
+ {
+ ftpfs_release_ftp_conn (nn->fs, cc->conn);
+ cc->conn = 0;
+ }
+ else
+ cc->data_conn_pos = 0;
+ }
+ re_connected = 1;
+ }
+
+ if (! err)
+ /* Try and read some data over the connection. */
+ {
+ size_t new_end = cc->max + READ_CHUNK_SIZE;
+
+ if (new_end > cc->size)
+ new_end = cc->size;
+
+ if (new_end > cc->alloced)
+ /* Make some room in memory for the new part of the
+ image. */
+ {
+ size_t alloc_end = cc->alloced + ALLOC_CHUNK_SIZE;
+
+ if (alloc_end < new_end)
+ alloc_end = new_end;
+ else if (alloc_end > cc->size)
+ alloc_end = cc->size;
+
+ if (cc->alloced == 0)
+ {
+ vm_address_t addr = 0;
+ err = vm_allocate (mach_task_self (),
+ &addr, alloc_end, 1);
+ if (! err)
+ cc->image = (char *)addr;
+ }
+ else
+ {
+ vm_address_t addr =
+ (vm_address_t)cc->image + cc->alloced;
+ err = vm_allocate (mach_task_self (),
+ &addr, alloc_end - cc->alloced,
+ 0);
+ if (err == EKERN_NO_SPACE)
+ /* Gack. We've goota move the whole splooge. */
+ {
+ addr = 0;
+ err = vm_allocate (mach_task_self (),
+ &addr, alloc_end, 1);
+ if (! err)
+ /* That worked; copy what's already-fetched. */
+ {
+ bcopy (cc->image, (void *)addr, cc->max);
+ vm_deallocate (mach_task_self (),
+ (vm_address_t)cc->image,
+ cc->alloced);
+ cc->image = (char *)addr;
+ }
+ }
+ }
+ if (! err)
+ cc->alloced = alloc_end;
+ }
+
+ if (! err)
+ {
+ ssize_t rd =
+ read (cc->data_conn,
+ cc->image + cc->data_conn_pos,
+ new_end - cc->data_conn_pos);
+ if (rd < 0)
+ err = errno;
+ else if (rd == 0)
+ /* EOF. This either means the file changed size, or
+ our data-connection got closed; we just try to
+ open the connection a second time, and then if
+ that fails, assume the size changed. */
+ {
+ if (re_connected)
+ err = EIO; /* Something's fucked */
+ else
+ /* Try opening the connection again. */
+ {
+ close (cc->data_conn);
+ ftp_conn_finish_transfer (cc->conn);
+ ftpfs_release_ftp_conn (nn->fs, cc->conn);
+ cc->conn = 0;
+ }
+ }
+ else
+ {
+ cc->max += rd;
+ cc->data_conn_pos += rd;
+ }
+ }
+ }
+
+ mutex_lock (&cc->lock);
+
+ if (cc->max < max && !err)
+ /* If anyone's waiting for data, let them look (if we're done
+ fetching, this gets delayed until below). */
+ condition_broadcast (&cc->wakeup);
+ }
+
+ if (!err && cc->conn && cc->max == cc->size)
+ /* We're finished reading all data, close the data connection. */
+ {
+ close (cc->data_conn);
+ ftp_conn_finish_transfer (cc->conn);
+ ftpfs_release_ftp_conn (nn->fs, cc->conn);
+ cc->conn = 0;
+ }
+
+ /* We're done, error or no. */
+ cc->fetching_active = 0;
+
+ /* Let others know something's going on. */
+ condition_broadcast (&cc->wakeup);
+ }
+ }
+
+ if (! err)
+ bcopy (cc->image + offs, data, max - offs);
+
+ mutex_unlock (&cc->lock);
+
+ return err;
+}
+
+/* Discard any cached contents in CC. */
+error_t
+ccache_invalidate (struct ccache *cc)
+{
+ error_t err = 0;
+
+ mutex_lock (&cc->lock);
+
+ while (cc->fetching_active && !err)
+ /* Some thread is fetching data, so just let it do its thing, but get
+ a wakeup call when it's done. */
+ {
+ if (hurd_condition_wait (&cc->wakeup, &cc->lock))
+ err = EINTR;
+ }
+
+ if (! err)
+ {
+ if (cc->alloced > 0)
+ {
+ vm_deallocate (mach_task_self (),
+ (vm_address_t)cc->image, cc->alloced);
+ cc->image = 0;
+ cc->alloced = 0;
+ cc->max = 0;
+ }
+ if (cc->conn)
+ {
+ close (cc->data_conn);
+ ftp_conn_finish_transfer (cc->conn);
+ ftpfs_release_ftp_conn (cc->node->nn->fs, cc->conn);
+ cc->conn = 0;
+ }
+ }
+
+ mutex_unlock (&cc->lock);
+
+ return err;
+}
+
+/* Return a ccache object for NODE in CC. */
+error_t
+ccache_create (struct node *node, struct ccache **cc)
+{
+ struct ccache *new = malloc (sizeof (struct ccache));
+
+ if (! new)
+ return ENOMEM;
+
+ new->node = node;
+ new->image = 0;
+ new->size = node->nn_stat.st_size;
+ new->max = 0;
+ new->alloced = 0;
+ mutex_init (&new->lock);
+ condition_init (&new->wakeup);
+ new->fetching_active = 0;
+ new->conn = 0;
+ new->data_conn = -1;
+
+ *cc = new;
+
+ return 0;
+}
+
+/* Free all resources used by CC. */
+void
+ccache_free (struct ccache *cc)
+{
+ if (cc->alloced > 0)
+ vm_deallocate (mach_task_self (), (vm_address_t)cc->image, cc->alloced);
+ if (cc->data_conn >= 0)
+ close (cc->data_conn);
+ if (cc->conn)
+ {
+ ftp_conn_finish_transfer (cc->conn);
+ ftpfs_release_ftp_conn (cc->node->nn->fs, cc->conn);
+ }
+ free (cc);
+}