Fix for 64-bit platforms.
[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, uint32_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, uint32_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, uint32_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         int is_load;
114         if (argc < 2)
115                 return 1;
116         if (argc > 2)
117                 is_load = 1;
118         if (is_load)
119                 fd = open(argv[1], O_RDWR);
120         else
121                 fd = open(argv[1], O_RDONLY);
122         if (fd < 0) {
123                 return 2;
124         }
125         r = read(fd, buffer, sizeof(buffer));
126         if (r < 32)
127                 return 3;
128         p = buffer + 8;
129         if (strncmp(buffer, "ANDROID!", 8)) {
130                 return 4;
131         }
132         kernel = (uint32_t *) p;
133         p += 8;
134         initrd = (uint32_t *) p;
135         p += 8;
136         second = (uint32_t *) p;
137         p += 12;
138         pgsz = *(uint32_t *) p;
139         offset = pgsz;
140         if (!is_load && *kernel > 0) {
141                 lseek(fd, offset, SEEK_SET);
142                 dump(fd, *kernel, pgsz, "kernel");
143         } else {
144                 lseek(fd, offset, SEEK_SET);
145                 load(fd, kernel, pgsz, "kernel");
146         }
147         offset += ((*kernel + pgsz - 1) / pgsz) * pgsz;
148         if (!is_load && *initrd > 0) {
149                 lseek(fd, offset, SEEK_SET);
150                 dump(fd, *initrd, pgsz, "initrd");
151         } else {
152                 lseek(fd, offset, SEEK_SET);
153                 load(fd, initrd, pgsz, "initrd");
154         }
155         offset += ((*initrd + pgsz - 1) / pgsz) * pgsz;
156         if (!is_load && *second > 0) {
157                 lseek(fd, offset, SEEK_SET);
158                 dump(fd, *second, pgsz, "second");
159         } else {
160                 /* For now, don't care about using second. */
161                 /* The header must be written back, and we may need to
162                  * truncate the file. */
163                 ftruncate(fd, offset);
164                 lseek(fd, 0, SEEK_SET);
165                 write(fd, buffer, pgsz);
166         }
167         close(fd);
168         return 0;
169 }