#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
+#include <sys/stat.h>
-static int dump(int fd, size_t len, int pgsz, char *fname)
+static int dump_or_load(int fd, uint32_t *len, int pgsz, char *fname, int dump)
{
- int out;
+ int cfd;
char *page;
int i, npages;
int r;
+ int ifd, ofd;
+ int rem;
page = malloc(pgsz);
if (!page)
return -ENOMEM;
- out = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0644);
- if (out < 0)
- return -errno;
- npages = (len + pgsz - 1) / pgsz;
+ if (dump)
+ cfd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ else
+ cfd = open(fname, O_RDONLY);
+ if (cfd < 0)
+ goto out;
+ if (dump) {
+ ifd = fd;
+ ofd = cfd;
+ } else {
+ struct stat s;
+ r = fstat(cfd, &s);
+ if (r < 0) {
+ goto out;
+ }
+ *len = s.st_size;
+ ifd = cfd;
+ ofd = fd;
+ }
+ npages = (*len + pgsz - 1) / pgsz;
for (i = 1; i <= npages; i++) {
if (i == npages) {
- len = len & (pgsz - 1);
- if (len > 0) {
- r = read(fd, page, len);
- if (r != len)
+ rem = *len & (pgsz - 1);
+ if (rem > 0) {
+ r = read(ifd, page, rem);
+ if (r != rem)
goto out;
- r = write(out, page, len);
- if (r != len)
+ r = write(ofd, page, rem);
+ if (r != rem)
goto out;
+ /* Write padding */
+ if (!dump) {
+ memset(page, 0, pgsz - rem);
+ write(ofd, page, pgsz - rem);
+ }
}
} else {
- r = read(fd, page, pgsz);
+ r = read(ifd, page, pgsz);
if (r != pgsz)
goto out;
- r = write(out, page, pgsz);
+ r = write(ofd, page, pgsz);
if (r != pgsz)
goto out;
}
}
free(page);
- close(out);
+ close(cfd);
return 0;
out:
free(page);
- close(out);
+ close(cfd);
if (r < 0)
return -errno;
return -EINVAL;
}
+static int dump(int fd, uint32_t len, int pgsz, char *fname)
+{
+ return dump_or_load(fd, &len, pgsz, fname, 1);
+}
+
+static int load(int fd, uint32_t *len, int pgsz, char *fname)
+{
+ return dump_or_load(fd, len, pgsz, fname, 0);
+}
+
int main(int argc, char **argv)
{
int fd;
- uint32_t kernel, initrd, second, pgsz;
+ uint32_t *kernel, *initrd, *second, pgsz;
char buffer[4096];
char *p;
int r;
off_t offset;
+ int is_load;
if (argc < 2)
return 1;
- fd = open(argv[1], O_RDONLY);
+ if (argc > 2)
+ is_load = 1;
+ if (is_load)
+ fd = open(argv[1], O_RDWR);
+ else
+ fd = open(argv[1], O_RDONLY);
if (fd < 0) {
return 2;
}
if (strncmp(buffer, "ANDROID!", 8)) {
return 4;
}
- kernel = *(uint32_t *) p;
+ kernel = (uint32_t *) p;
p += 8;
- initrd = *(uint32_t *) p;
+ initrd = (uint32_t *) p;
p += 8;
- second = *(uint32_t *) p;
+ second = (uint32_t *) p;
p += 12;
pgsz = *(uint32_t *) p;
offset = pgsz;
- if (kernel > 0) {
+ if (!is_load && *kernel > 0) {
lseek(fd, offset, SEEK_SET);
- dump(fd, kernel, pgsz, "kernel");
+ dump(fd, *kernel, pgsz, "kernel");
+ } else {
+ lseek(fd, offset, SEEK_SET);
+ load(fd, kernel, pgsz, "kernel");
}
- offset += ((kernel + pgsz - 1) / pgsz) * pgsz;
- if (initrd > 0) {
+ offset += ((*kernel + pgsz - 1) / pgsz) * pgsz;
+ if (!is_load && *initrd > 0) {
+ lseek(fd, offset, SEEK_SET);
+ dump(fd, *initrd, pgsz, "initrd");
+ } else {
lseek(fd, offset, SEEK_SET);
- dump(fd, initrd, pgsz, "initrd");
+ load(fd, initrd, pgsz, "initrd");
}
- offset += ((initrd + pgsz - 1) / pgsz) * pgsz;
- if (second > 0) {
+ offset += ((*initrd + pgsz - 1) / pgsz) * pgsz;
+ if (!is_load && *second > 0) {
lseek(fd, offset, SEEK_SET);
- dump(fd, second, pgsz, "second");
+ dump(fd, *second, pgsz, "second");
+ } else {
+ /* For now, don't care about using second. */
+ /* The header must be written back, and we may need to
+ * truncate the file. */
+ ftruncate(fd, offset);
+ lseek(fd, 0, SEEK_SET);
+ write(fd, buffer, pgsz);
}
close(fd);
return 0;