b015c10f0f4bdbc85807a4e3664843d4ac726781
[cascardo/bootimg.git] / bootimg.c
1 /*
2  *  Copyright (C) 2014  Thadeu Lima de Souza Cascardo <cascardo@cascardo.eti.br>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <sys/stat.h>
27
28 static int dump_or_load(int fd, size_t *len, int pgsz, char *fname, int dump)
29 {
30         int cfd;
31         char *page;
32         int i, npages;
33         int r;
34         int ifd, ofd;
35         int rem;
36         page = malloc(pgsz);
37         if (!page)
38                 return -ENOMEM;
39         if (dump)
40                 cfd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0644);
41         else
42                 cfd = open(fname, O_RDONLY);
43         if (cfd < 0)
44                 goto out;
45         if (dump) {
46                 ifd = fd;
47                 ofd = cfd;
48         } else {
49                 struct stat s;
50                 r = fstat(cfd, &s);
51                 if (r < 0) {
52                         goto out;
53                 }
54                 *len = s.st_size;
55                 ifd = cfd;
56                 ofd = fd;
57         }
58         npages = (*len + pgsz - 1) / pgsz;
59         for (i = 1; i <= npages; i++) {
60                 if (i == npages) {
61                         rem = *len & (pgsz - 1);
62                         if (rem > 0) {
63                                 r = read(ifd, page, rem);
64                                 if (r != rem)
65                                         goto out;
66                                 r = write(ofd, page, rem);
67                                 if (r != rem)
68                                         goto out;
69                                 /* Write padding */
70                                 if (!dump) {
71                                         memset(page, 0, pgsz - rem);
72                                         write(ofd, page, pgsz - rem);
73                                 }
74                         }
75                 } else {
76                         r = read(ifd, page, pgsz);
77                         if (r != pgsz)
78                                 goto out;
79                         r = write(ofd, page, pgsz);
80                         if (r != pgsz)
81                                 goto out;
82                 }
83         }
84         free(page);
85         close(cfd);
86         return 0;
87 out:
88         free(page);
89         close(cfd);
90         if (r < 0)
91                 return -errno;
92         return -EINVAL;
93 }
94
95 static int dump(int fd, size_t len, int pgsz, char *fname)
96 {
97         return dump_or_load(fd, &len, pgsz, fname, 1);
98 }
99
100 static int load(int fd, size_t *len, int pgsz, char *fname)
101 {
102         return dump_or_load(fd, len, pgsz, fname, 0);
103 }
104
105 int main(int argc, char **argv)
106 {
107         int fd;
108         uint32_t kernel, initrd, second, pgsz;
109         char buffer[4096];
110         char *p;
111         int r;
112         off_t offset;
113         if (argc < 2)
114                 return 1;
115         fd = open(argv[1], O_RDONLY);
116         if (fd < 0) {
117                 return 2;
118         }
119         r = read(fd, buffer, sizeof(buffer));
120         if (r < 32)
121                 return 3;
122         p = buffer + 8;
123         if (strncmp(buffer, "ANDROID!", 8)) {
124                 return 4;
125         }
126         kernel = *(uint32_t *) p;
127         p += 8;
128         initrd = *(uint32_t *) p;
129         p += 8;
130         second = *(uint32_t *) p;
131         p += 12;
132         pgsz = *(uint32_t *) p;
133         offset = pgsz;
134         if (kernel > 0) {
135                 lseek(fd, offset, SEEK_SET);
136                 dump(fd, kernel, pgsz, "kernel");
137         }
138         offset += ((kernel + pgsz - 1) / pgsz) * pgsz;
139         if (initrd > 0) {
140                 lseek(fd, offset, SEEK_SET);
141                 dump(fd, initrd, pgsz, "initrd");
142         }
143         offset += ((initrd + pgsz - 1) / pgsz) * pgsz;
144         if (second > 0) {
145                 lseek(fd, offset, SEEK_SET);
146                 dump(fd, second, pgsz, "second");
147         }
148         close(fd);
149         return 0;
150 }