summaryrefslogtreecommitdiff
path: root/sys/src/cmd/hgfs/patch.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/src/cmd/hgfs/patch.c')
-rw-r--r--sys/src/cmd/hgfs/patch.c159
1 files changed, 159 insertions, 0 deletions
diff --git a/sys/src/cmd/hgfs/patch.c b/sys/src/cmd/hgfs/patch.c
new file mode 100644
index 000000000..a3d6ef37a
--- /dev/null
+++ b/sys/src/cmd/hgfs/patch.c
@@ -0,0 +1,159 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+int
+fcopy(int dfd, int sfd, vlong len)
+{
+ uchar buf[BUFSZ];
+ int n;
+
+ while(len > 0){
+ if((n = BUFSZ) > len)
+ n = len;
+ if((n = read(sfd, buf, n)) < 0)
+ return -1;
+ if(write(dfd, buf, n) != n)
+ return -1;
+ len -= n;
+ }
+ return 0;
+}
+
+static uchar patchmark[12] = {
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+};
+
+int
+fpatchmark(int pfd, char *)
+{
+ if(write(pfd, patchmark, 12) != 12)
+ return -1;
+ return 0;
+}
+
+typedef struct Frag Frag;
+struct Frag
+{
+ Frag *next;
+ int fd;
+ vlong len;
+ vlong off;
+};
+
+int
+fpatch(int ofd, int bfd, int pfd)
+{
+ vlong off, fstart, fend, start, end, len;
+ int err, front, back;
+ Frag *h, *f, *p;
+ uchar buf[12];
+
+ h = nil;
+ err = -1;
+
+ if(bfd >= 0){
+ h = malloc(sizeof(Frag));
+ h->next = nil;
+ h->off = 0;
+ h->fd = bfd;
+ h->len = seek(h->fd, 0, 2);
+ if(h->len < 0)
+ goto errout;
+ }
+
+ off = 0;
+ while(pfd >= 0){
+ if(readn(pfd, buf, 12) != 12)
+ break;
+
+ if(memcmp(buf, patchmark, 12) == 0){
+ off = 0;
+ continue;
+ }
+
+ start = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
+ end = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
+ len = buf[8]<<24 | buf[9]<<16 | buf[10]<<8 | buf[11];
+
+ if(start > end){
+ werrstr("bad patch: start > end");
+ goto errout;
+ }
+
+ start += off;
+ end += off;
+ off += start + len - end;
+
+ fstart = 0;
+ for(f = h; f; f = f->next, fstart = fend){
+ fend = fstart + f->len;
+ if(fend <= start)
+ continue;
+ if(fstart >= end)
+ break;
+
+ front = start > fstart;
+ back = end < fend;
+ if(front && back){
+ p = malloc(sizeof(Frag));
+ *p = *f;
+ f->next = p;
+ f->len = start - fstart;
+ p->off += end - fstart;
+ p->len -= end - fstart;
+ break;
+ } else if(back){
+ f->off += end - fstart;
+ f->len -= end - fstart;
+ break;
+ } else if(front){
+ f->len = start - fstart;
+ } else {
+ f->len = 0;
+ }
+ }
+
+ fstart = 0;
+ for(p = nil, f = h; f && fstart < start; p = f, f = f->next)
+ fstart += f->len;
+
+ f = malloc(sizeof(Frag));
+ f->fd = pfd;
+ f->len = len;
+ f->off = seek(f->fd, 0, 1);
+
+ if(p){
+ f->next = p->next;
+ p->next = f;
+ } else {
+ f->next = h;
+ h = f;
+ }
+
+ if(f->off < 0)
+ goto errout;
+ if(seek(pfd, f->len, 1) < 0)
+ goto errout;
+ }
+
+ for(f = h; f; f = f->next){
+ if(seek(f->fd, f->off, 0) < 0)
+ goto errout;
+ if(fcopy(ofd, f->fd, f->len) < 0)
+ goto errout;
+ }
+ err = 0;
+
+errout:
+ while(f = h){
+ h = f->next;
+ free(f);
+ }
+
+ return err;
+}