diff options
author | Zdenek Behan <zbehan@chromium.org> | 2015-09-28 23:54:18 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-09-28 23:54:18 +0000 |
commit | 1158e1756d993c6d9d99779dd29334130a5dba50 (patch) | |
tree | bd0e21e7c9720a2ff4ea330ce2e677837ecad74e | |
parent | 0231f8ae917c4d27d076d2d17defd747d090ea7c (diff) | |
parent | 339e0eece8287189e4e794ca3bbb5390e9332fe9 (diff) | |
download | bsdiff-1158e1756d993c6d9d99779dd29334130a5dba50.tar.gz |
am 339e0eec: bsdiff: convert ebuild to the new eclass format
* commit '339e0eece8287189e4e794ca3bbb5390e9332fe9':
bsdiff: convert ebuild to the new eclass format
-rw-r--r-- | bspatch.c | 268 |
1 files changed, 258 insertions, 10 deletions
@@ -29,6 +29,9 @@ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59: #endif #include <bzlib.h> +#include <errno.h> +#include <inttypes.h> +#include <stdint.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -37,6 +40,235 @@ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59: #include <fcntl.h> #include <sys/types.h> // android +#define JOIN(a, b) __JOIN(a, b) +#define __JOIN(a, b) a ## b +#define COMPILE_ASSERT(expr, message) \ + typedef char JOIN(message, JOIN(_, __LINE__)) [(expr) ? 1 : -1] + +COMPILE_ASSERT(sizeof(int64_t) == 8, int64_t_64_bit); + +#define MIN(a, b) \ + ((a) < (b) ? (a) : (b)) + +// Reads next int from *ints. The int should be terminated with a comma +// or NULL char. *ints will be updated to the space right after the comma +// or set to NULL if this was the last number. This assumes the input is +// a valid string, as validated with PositionsStringIsValid(). +// Returns 1 on success. +int NextInt64(const char** ints, int64_t *out) { + if (!ints[0]) + return 0; + int r = sscanf(*ints, "%" PRIi64, out); + if (r == 1) { + const char* next_comma = strchr(*ints, ','); + const char* next_colon = strchr(*ints, ':'); + if (!next_comma && !next_colon) + *ints = NULL; + else if (!next_comma) + *ints = next_colon + 1; + else if (!next_colon) + *ints = next_comma + 1; + else + *ints = MIN(next_comma, next_colon) + 1; + return 1; + } + return 0; +} + +COMPILE_ASSERT(sizeof(intmax_t) == 8, intmax_t_not_64_bit); + +// Returns 1 if str can be converted to int64_t without over/underflowing. +// str is assumed to point to an optional negative sign followed by numbers, +// optionally followed by non-numeric characters, followed by '\0'. +int IsValidInt64(const char* str) { + const char* end_ptr; + errno = 0; + intmax_t result = strtoimax(str, &end_ptr, /* base: */ 10); + return errno == 0; +} + +// Input validator. Make sure the positions string is well formatted. +// All numerical values are checked to make sure they don't over/underflow +// int64_t. Returns 1 if valid. +int PositionsStringIsValid(const char* positions) { + if (positions == NULL) + errx(1, "bad string"); + + // Special case: empty string is valid + if (!positions[0]) + return 1; + + // Use a state machine to determine if the string is valid. + // Key: (s): state, ((s)) valid end state. + // n (negative_valid) is a boolean that starts out as true. + // If n is true, ':' is the delimiter, otherwise ','. + // + // .--------------------------. + // | | n ? ':' : ',' ; n = !n + // V '-'&&n 0-9 | + // start->(0)------------->(1)----->((2))---. + // `---------------------> <--' 0-9 + // 0-9 + int state = 0; + int negative_valid = 1; + const char* number_start = positions; + for (;; positions++) { + char c = *positions; + switch (state) { + case 0: + if (c == '-' && negative_valid) { + state = 1; + continue; + } + if (isdigit(c)) { + state = 2; + continue; + } + return 0; + case 1: + if (isdigit(c)) { + state = 2; + continue; + } + return 0; + case 2: + if (isdigit(c)) + continue; + // number_start must point to a valid number + if (!IsValidInt64(number_start)) { + return 0; + } + if ((negative_valid && c == ':') || + (!negative_valid && c == ',')) { + state = 0; + number_start = positions + 1; + negative_valid = !negative_valid; + continue; + } + return (c == '\0'); + } + } +} + +// Reads into a buffer a series of byte ranges from filename. +// Each range is a pair of comma-separated ints from positions. +// -1 as an offset means a sparse-hole. +// E.g. If positions were "1,5:23,4:-1,8:3,7", then we would return a buffer +// consisting of 5 bytes from offset 1 of the file, followed by +// 4 bytes from offset 23, then 8 bytes of all zeros, then 7 bytes from +// offset 3 in the file. +// Returns NULL on error. +static char* PositionedRead(const char* filename, + const char* positions, + ssize_t* old_size) { + if (!PositionsStringIsValid(positions)) { + errx(1, "invalid positions string for read\n"); + } + + // Get length + const char* p = positions; + int64_t length = 0; + for (;;) { + int64_t value; + if (0 == NextInt64(&p, &value)) { + break; + } + int r = NextInt64(&p, &value); + if (r == 0) { + errx(1, "bad length parse\n"); + } + if (value < 0) { + errx(1, "length can't be negative\n"); + } + length += value; + } + + // Malloc + if (length > 0x40000000) { // 1 GiB; sanity check + errx(1, "Read length too long (exceeds 1 GiB)"); + } + // Following bsdiff convention, allocate length + 1 to avoid malloc(0) + char* buf = malloc(length + 1); + if (buf == NULL) { + errx(1, "malloc failed\n"); + } + char* buf_tail = buf; + + int fd = open(filename, O_RDONLY); + if (fd < 0) { + errx(1, "open failed for read\n"); + } + + // Read bytes + p = positions; + for (;;) { + int64_t offset, read_length; + if (NextInt64(&p, &offset) == 0) { + break; + } + if (offset < 0) { + errx(1, "no support for sparse positions " + "yet during read\n"); + } + if (NextInt64(&p, &read_length) == 0) { + errx(1, "bad length parse (should never happen)\n"); + } + if (read_length < 0) { + errx(1, "length can't be negative " + "(should never happen)\n"); + } + ssize_t rc = pread(fd, buf_tail, read_length, offset); + if (rc != read_length) { + errx(1, "read failed\n"); + } + buf_tail += rc; + } + close(fd); + *old_size = length; + return buf; +} + +static void PositionedWrite(const char* filename, + const char* positions, + const char* buf, + ssize_t new_size) { + if (!PositionsStringIsValid(positions)) { + errx(1, "invalid positions string for write\n"); + } + int fd = open(filename, O_WRONLY | O_CREAT, 0666); + if (fd < 0) { + errx(1, "open failed for write\n"); + } + + for (;;) { + int64_t offset, length; + if (NextInt64(&positions, &offset) == 0) { + break; + } + if (NextInt64(&positions, &length) == 0) { + errx(1, "bad length parse for write\n"); + } + if (length < 0) { + errx(1, "length can't be negative for write\n"); + } + + if (offset < 0) { + // Sparse hole. Skip. + } else { + ssize_t rc = pwrite(fd, buf, length, offset); + if (rc != length) { + errx(1, "write failed\n"); + } + } + buf += length; + new_size -= length; + } + if (new_size != 0) { + errx(1, "output position length doesn't match new size\n"); + } + close(fd); +} + static off_t offtin(u_char *buf) { off_t y; @@ -70,7 +302,13 @@ int main(int argc,char * argv[]) off_t lenread; off_t i; - if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]); + if ((argc != 6) && (argc != 4)) { + errx(1,"usage: %s oldfile newfile patchfile \\\n" + " [in_offset,in_length,in_offset,in_length,... \\\n" + " out_offset,out_length," + "out_offset,out_length,...]\n",argv[0]); + } + int using_positioning = (argc == 6); /* Open patch file */ if ((f = fopen(argv[3], "r")) == NULL) @@ -133,12 +371,18 @@ int main(int argc,char * argv[]) if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL) errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err); - if(((fd=open(argv[1],O_RDONLY,0))<0) || - ((oldsize=lseek(fd,0,SEEK_END))==-1) || - ((old=malloc(oldsize+1))==NULL) || - (lseek(fd,0,SEEK_SET)!=0) || - (read(fd,old,oldsize)!=oldsize) || - (close(fd)==-1)) err(1,"%s",argv[1]); + // Read + + if (!using_positioning) { + if(((fd=open(argv[1],O_RDONLY,0))<0) || + ((oldsize=lseek(fd,0,SEEK_END))==-1) || + ((old=malloc(oldsize+1))==NULL) || + (lseek(fd,0,SEEK_SET)!=0) || + (read(fd,old,oldsize)!=oldsize) || + (close(fd)==-1)) err(1,"%s",argv[1]); + } else { + old = PositionedRead(argv[1], argv[4], &oldsize); + } if((new=malloc(newsize+1))==NULL) err(1,NULL); oldpos=0;newpos=0; @@ -199,9 +443,13 @@ int main(int argc,char * argv[]) err(1, "fclose(%s)", argv[3]); /* Write the new file */ - if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) || - (write(fd,new,newsize)!=newsize) || (close(fd)==-1)) - err(1,"%s",argv[2]); + if (!using_positioning) { + if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) || + (write(fd,new,newsize)!=newsize) || (close(fd)==-1)) + err(1,"%s",argv[2]); + } else { + PositionedWrite(argv[2], argv[5], new, newsize); + } free(new); free(old); |