diff options
Diffstat (limited to 'sys/src/cmd/hgfs/patch.c')
| -rw-r--r-- | sys/src/cmd/hgfs/patch.c | 159 |
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; +} |
