summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ip/ftpfs/file.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ip/ftpfs/file.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ip/ftpfs/file.c')
-rwxr-xr-xsys/src/cmd/ip/ftpfs/file.c257
1 files changed, 257 insertions, 0 deletions
diff --git a/sys/src/cmd/ip/ftpfs/file.c b/sys/src/cmd/ip/ftpfs/file.c
new file mode 100755
index 000000000..ff5bfeca0
--- /dev/null
+++ b/sys/src/cmd/ip/ftpfs/file.c
@@ -0,0 +1,257 @@
+#include <u.h>
+#include <libc.h>
+#include <String.h>
+#include "ftpfs.h"
+
+enum
+{
+ Chunk= 1024, /* chunk size for buffered data */
+ Nfile= 128, /* maximum number of cached files */
+};
+
+/* a file (with cached data) */
+struct File
+{
+ char *mem; /* part of file cached in memory */
+ ulong len; /* length of cached data */
+ long off; /* current offset into tpath */
+ short fd; /* fd to cache file */
+ char inuse;
+ char dirty;
+ ulong atime; /* time of last access */
+ Node *node;
+ char *template;
+};
+
+static File files[Nfile];
+static ulong now;
+static int ntmp;
+
+/*
+ * lookup a file, create one if not found. if there are no
+ * free files, free the last oldest clean one.
+ */
+static File*
+fileget(Node *node)
+{
+ File *fp;
+ File *oldest;
+
+ fp = node->fp;
+ if(fp)
+ return fp;
+
+ oldest = 0;
+ for(fp = files; fp < &files[Nfile]; fp++){
+ if(fp->inuse == 0)
+ break;
+ if(fp->dirty == 0 && (oldest == 0 || oldest->atime > fp->atime))
+ oldest = fp;
+ }
+
+ if(fp == &files[Nfile]){
+ uncache(oldest->node);
+ fp = oldest;
+ }
+ node->fp = fp;
+ fp->node = node;
+ fp->atime = now++;
+ fp->inuse = 1;
+ fp->fd = -1;
+ if(fp->mem){
+ free(fp->mem);
+ fp->mem = nil;
+ }
+ return fp;
+}
+
+/*
+ * free a cached file
+ */
+void
+filefree(Node *node)
+{
+ File *fp;
+
+ fp = node->fp;
+ if(fp == 0)
+ return;
+
+ if(fp->fd > 0){
+ ntmp--;
+ close(fp->fd);
+ remove(fp->template);
+ free(fp->template);
+ fp->template = 0;
+ }
+ fp->fd = -1;
+ if(fp->mem){
+ free(fp->mem);
+ fp->mem = nil;
+ }
+ fp->len = 0;
+ fp->inuse = 0;
+ fp->dirty = 0;
+
+ node->fp = 0;
+}
+
+/*
+ * satisfy read first from in memory chunk and then from temporary
+ * file. It's up to the caller to make sure that the file is valid.
+ */
+int
+fileread(Node *node, char *a, long off, int n)
+{
+ int sofar;
+ int i;
+ File *fp;
+
+ fp = node->fp;
+ if(fp == 0)
+ fatal("fileread");
+
+ if(off + n > fp->len)
+ n = fp->len - off;
+
+ for(sofar = 0; sofar < n; sofar += i, off += i, a += i){
+ if(off >= fp->len)
+ return sofar;
+ if(off < Chunk){
+ i = n;
+ if(off + i > Chunk)
+ i = Chunk - off;
+ memmove(a, fp->mem + off, i);
+ continue;
+ }
+ if(fp->off != off)
+ if(seek(fp->fd, off, 0) < 0){
+ fp->off = -1;
+ return -1;
+ }
+ i = read(fp->fd, a, n-sofar);
+ if(i < 0){
+ fp->off = -1;
+ return -1;
+ }
+ if(i == 0)
+ break;
+ fp->off = off + i;
+ }
+ return sofar;
+}
+
+void
+uncachedir(Node *parent, Node *child)
+{
+ Node *sp;
+
+ if(parent == 0 || parent == child)
+ return;
+ for(sp = parent->children; sp; sp = sp->sibs)
+ if(sp->opens == 0)
+ if(sp != child)
+ if(sp->fp != nil)
+ if(sp->fp->dirty == 0)
+ if(sp->fp->fd >= 0){
+ filefree(sp);
+ UNCACHED(sp);
+ }
+}
+
+static int
+createtmp(File *fp)
+{
+ char template[32];
+
+ strcpy(template, "/tmp/ftpXXXXXXXXXXX");
+ mktemp(template);
+ if(strcmp(template, "/") == 0){
+ fprint(2, "ftpfs can't create tmp file %s: %r\n", template);
+ return -1;
+ }
+ if(ntmp >= 16)
+ uncachedir(fp->node->parent, fp->node);
+
+ fp->fd = create(template, ORDWR|ORCLOSE, 0600);
+ fp->template = strdup(template);
+ fp->off = 0;
+ ntmp++;
+ return fp->fd;
+}
+
+/*
+ * write cached data (first Chunk bytes stay in memory)
+ */
+int
+filewrite(Node *node, char *a, long off, int n)
+{
+ int i, sofar;
+ File *fp;
+
+ fp = fileget(node);
+
+ if(fp->mem == nil){
+ fp->mem = malloc(Chunk);
+ if(fp->mem == nil)
+ return seterr("out of memory");
+ }
+
+ for(sofar = 0; sofar < n; sofar += i, off += i, a += i){
+ if(off < Chunk){
+ i = n;
+ if(off + i > Chunk)
+ i = Chunk - off;
+ memmove(fp->mem + off, a, i);
+ continue;
+ }
+ if(fp->fd < 0)
+ if(createtmp(fp) < 0)
+ return seterr("can't create temp file");
+ if(fp->off != off)
+ if(seek(fp->fd, off, 0) < 0){
+ fp->off = -1;
+ return seterr("can't seek temp file");
+ }
+ i = write(fp->fd, a, n-sofar);
+ if(i <= 0){
+ fp->off = -1;
+ return seterr("can't write temp file");
+ }
+ fp->off = off + i;
+ }
+
+ if(off > fp->len)
+ fp->len = off;
+ if(off > node->d->length)
+ node->d->length = off;
+ return sofar;
+}
+
+/*
+ * mark a file as dirty
+ */
+void
+filedirty(Node *node)
+{
+ File *fp;
+
+ fp = fileget(node);
+ fp->dirty = 1;
+}
+
+/*
+ * mark a file as clean
+ */
+void
+fileclean(Node *node)
+{
+ if(node->fp)
+ node->fp->dirty = 0;
+}
+
+int
+fileisdirty(Node *node)
+{
+ return node->fp && node->fp->dirty;
+}