summaryrefslogtreecommitdiff
path: root/sys/src/cmd/cfs/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/cfs/file.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/cfs/file.c')
-rwxr-xr-xsys/src/cmd/cfs/file.c298
1 files changed, 298 insertions, 0 deletions
diff --git a/sys/src/cmd/cfs/file.c b/sys/src/cmd/cfs/file.c
new file mode 100755
index 000000000..457a434e6
--- /dev/null
+++ b/sys/src/cmd/cfs/file.c
@@ -0,0 +1,298 @@
+#include <u.h>
+#include <libc.h>
+#include "cformat.h"
+#include "lru.h"
+#include "bcache.h"
+#include "disk.h"
+#include "inode.h"
+#include "file.h"
+
+/*
+ * merge data with that which already exists in a block
+ *
+ * we allow only one range per block, always use the new
+ * data if the ranges don't overlap.
+ */
+void
+fmerge(Dptr *p, char *to, char *from, int start, int len)
+{
+ int end;
+
+ end = start + len;
+ memmove(to+start, from, end-start);
+
+ /*
+ * if ranges do not overlap...
+ */
+ if(start>p->end || p->start>end){
+ /*
+ * just use the new data
+ */
+ p->start = start;
+ p->end = end;
+ } else {
+ /*
+ * merge ranges
+ */
+ if(start < p->start)
+ p->start = start;
+ if(end > p->end)
+ p->end = end;
+ }
+
+}
+
+/*
+ * write a block (or less) of data onto a disk, follow it with any necessary
+ * pointer writes.
+ *
+ * N.B. ordering is everything
+ */
+int
+fbwrite(Icache *ic, Ibuf *b, char *a, ulong off, int len)
+{
+ int wrinode;
+ ulong fbno;
+ Bbuf *dbb; /* data block */
+ Bbuf *ibb; /* indirect block */
+ Dptr *p;
+ Dptr t;
+
+ fbno = off / ic->bsize;
+ p = &b->inode.ptr;
+ ibb = 0;
+ wrinode = 0;
+
+ /*
+ * are there any pages for this inode?
+ */
+ if(p->bno == Notabno){
+ wrinode = 1;
+ goto dowrite;
+ }
+
+ /*
+ * is it an indirect block?
+ */
+ if(p->bno & Indbno){
+ ibb = bcread(ic, p->bno);
+ if(ibb == 0)
+ return -1;
+ p = (Dptr*)ibb->data;
+ p += fbno % ic->p2b;
+ goto dowrite;
+ }
+
+ /*
+ * is it the wrong direct block?
+ */
+ if((p->fbno%ic->p2b) != (fbno%ic->p2b)){
+ /*
+ * yes, make an indirect block
+ */
+ t = *p;
+ dpalloc(ic, p);
+ if(p->bno == Notabno){
+ *p = t;
+ return -1;
+ }
+ ibb = bcalloc(ic, p->bno);
+ if(ibb == 0){
+ *p = t;
+ return -1;
+ }
+ p = (Dptr*)ibb->data;
+ p += t.fbno % ic->p2b;
+ *p = t;
+ p = (Dptr*)ibb->data;
+ p += fbno % ic->p2b;
+ }
+ wrinode = 1;
+
+dowrite:
+ /*
+ * get the data block into the block cache
+ */
+ if(p->bno == Notabno){
+ /*
+ * create a new block
+ */
+ dalloc(ic, p);
+ if(p->bno == Notabno)
+ return -1; /* no blocks left (maybe) */
+ dbb = bcalloc(ic, p->bno);
+ } else {
+ /*
+ * use what's there
+ */
+ dbb = bcread(ic, p->bno);
+ }
+ if(dbb == 0)
+ return -1;
+
+ /*
+ * merge in the new data
+ */
+ if(p->fbno != fbno){
+ p->start = p->end = 0;
+ p->fbno = fbno;
+ }
+ fmerge(p, dbb->data, a, off % ic->bsize, len);
+
+ /*
+ * write changed blocks back in the
+ * correct order
+ */
+ bcmark(ic, dbb);
+ if(ibb)
+ bcmark(ic, ibb);
+ if(wrinode)
+ if(iwrite(ic, b) < 0)
+ return -1;
+ return len;
+}
+
+/*
+ * write `n' bytes to the cache
+ *
+ * return number of bytes written
+ */
+long
+fwrite(Icache *ic, Ibuf *b, char *a, ulong off, long n)
+{
+ int len;
+ long sofar;
+
+ for(sofar = 0; sofar < n; sofar += len){
+ len = ic->bsize - ((off+sofar)%ic->bsize);
+ if(len > n - sofar)
+ len = n - sofar;
+ if(fbwrite(ic, b, a+sofar, off+sofar, len) < 0)
+ return sofar;
+ }
+ return sofar;
+}
+
+/*
+ * get a pointer to the next valid data at or after `off'
+ */
+Dptr *
+fpget(Icache *ic, Ibuf *b, ulong off)
+{
+ ulong fbno;
+ long doff;
+ Bbuf *ibb; /* indirect block */
+ Dptr *p, *p0, *pf;
+
+ fbno = off / ic->bsize;
+ p = &b->inode.ptr;
+
+ /*
+ * are there any pages for this inode?
+ */
+ if(p->bno == Notabno)
+ return 0;
+
+ /*
+ * if it's a direct block, life is easy?
+ */
+ if(!(p->bno & Indbno)){
+ /*
+ * a direct block, return p if it's at least past what we want
+ */
+ if(p->fbno > fbno)
+ return p;
+ if(p->fbno < fbno)
+ return 0;
+ doff = off % ic->bsize;
+ if(doff>=p->start && doff<p->end)
+ return p;
+ else
+ return 0;
+ }
+
+ /*
+ * read the indirect block
+ */
+ ibb = bcread(ic, p->bno);
+ if(ibb == 0)
+ return 0;
+
+ /*
+ * find the next valid pointer
+ */
+ p0 = (Dptr*)ibb->data;
+ pf = p0 + (fbno % ic->p2b);
+ if(pf->bno!=Notabno && pf->fbno==fbno){
+ doff = off % ic->bsize;
+ if(doff<pf->end)
+ return pf;
+ }
+ for(p = pf+1; p < p0 + ic->p2b; p++){
+ fbno++;
+ if(p->fbno==fbno && p->bno!=Notabno && p->start<p->end)
+ return p;
+ }
+ for(p = p0; p < pf; p++){
+ fbno++;
+ if(p->fbno==fbno && p->bno!=Notabno && p->start<p->end)
+ return p;
+ }
+ return 0;
+}
+
+/*
+ * read `n' bytes from the cache.
+ *
+ * if we hit a gap and we've read something,
+ * return number of bytes read so far.
+ *
+ * if we start with a gap, return minus the number of bytes
+ * to the next data.
+ *
+ * if there are no bytes cached, return 0.
+ */
+long
+fread(Icache *ic, Ibuf *b, char *a, ulong off, long n)
+{
+ int len, start;
+ long sofar, gap;
+ Dptr *p;
+ Bbuf *bb;
+
+ for(sofar = 0; sofar < n; sofar += len, off += len){
+ /*
+ * get pointer to next data
+ */
+ len = n - sofar;
+ p = fpget(ic, b, off);
+
+ /*
+ * if no more data, return what we have so far
+ */
+ if(p == 0)
+ return sofar;
+
+ /*
+ * if there's a gap, return the size of the gap
+ */
+ gap = (ic->bsize*p->fbno + p->start) - off;
+ if(gap>0)
+ if(sofar == 0)
+ return -gap;
+ else
+ return sofar;
+
+ /*
+ * return what we have
+ */
+ bb = bcread(ic, p->bno);
+ if(bb == 0)
+ return sofar;
+ start = p->start - gap;
+ if(p->end - start < len)
+ len = p->end - start;
+ memmove(a + sofar, bb->data + start, len);
+ }
+ return sofar;
+}