Merge branch 'v1.4' into v1.5
[librecmc/librecmc.git] / package / utils / osafeloader / src / osafeloader.c
1 /*
2  * osafeloader
3  *
4  * Copyright (C) 2016 Rafał Miłecki <zajec5@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <byteswap.h>
12 #include <endian.h>
13 #include <errno.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 #include "md5.h"
21
22 #if !defined(__BYTE_ORDER)
23 #error "Unknown byte order"
24 #endif
25
26 #if __BYTE_ORDER == __BIG_ENDIAN
27 #define cpu_to_be32(x)  (x)
28 #define be32_to_cpu(x)  (x)
29 #define cpu_to_be16(x)  (x)
30 #define be16_to_cpu(x)  (x)
31 #elif __BYTE_ORDER == __LITTLE_ENDIAN
32 #define cpu_to_be32(x)  bswap_32(x)
33 #define be32_to_cpu(x)  bswap_32(x)
34 #define cpu_to_be16(x)  bswap_16(x)
35 #define be16_to_cpu(x)  bswap_16(x)
36 #else
37 #error "Unsupported endianness"
38 #endif
39
40 struct safeloader_header {
41         uint32_t imagesize;
42         uint8_t md5[16];
43 } __attribute__ ((packed));
44
45 char *safeloader_path;
46 char *partition_name;
47 char *out_path;
48
49 static inline size_t osafeloader_min(size_t x, size_t y) {
50         return x < y ? x : y;
51 }
52
53 static const uint8_t md5_salt[16] = {
54         0x7a, 0x2b, 0x15, 0xed,
55         0x9b, 0x98, 0x59, 0x6d,
56         0xe5, 0x04, 0xab, 0x44,
57         0xac, 0x2a, 0x9f, 0x4e,
58 };
59
60 /**************************************************
61  * Info
62  **************************************************/
63
64 static int osafeloader_info(int argc, char **argv) {
65         FILE *safeloader;
66         struct safeloader_header hdr;
67         MD5_CTX ctx;
68         size_t bytes, imagesize;
69         uint8_t buf[1024];
70         uint8_t md5[16];
71         char name[32];
72         int base, size, i;
73         int err = 0;
74
75         if (argc < 3) {
76                 fprintf(stderr, "No SafeLoader file passed\n");
77                 err = -EINVAL;
78                 goto out;
79         }
80         safeloader_path = argv[2];
81
82         safeloader = fopen(safeloader_path, "r");
83         if (!safeloader) {
84                 fprintf(stderr, "Couldn't open %s\n", safeloader_path);
85                 err = -EACCES;
86                 goto out;
87         }
88
89         bytes = fread(&hdr, 1, sizeof(hdr), safeloader);
90         if (bytes != sizeof(hdr)) {
91                 fprintf(stderr, "Couldn't read %s header\n", safeloader_path);
92                 err =  -EIO;
93                 goto err_close;
94         }
95         imagesize = be32_to_cpu(hdr.imagesize);
96
97         MD5_Init(&ctx);
98         MD5_Update(&ctx, md5_salt, sizeof(md5_salt));
99         while ((bytes = fread(buf, 1, osafeloader_min(sizeof(buf), imagesize), safeloader)) > 0) {
100                 MD5_Update(&ctx, buf, bytes);
101                 imagesize -= bytes;
102         }
103         MD5_Final(md5, &ctx);
104
105         if (memcmp(md5, hdr.md5, 16)) {
106                 fprintf(stderr, "Broken SafeLoader file with invalid MD5\n");
107                 err =  -EIO;
108                 goto err_close;
109         }
110
111         printf("%10s: %d\n", "Image size", be32_to_cpu(hdr.imagesize));
112         printf("%10s: ", "MD5");
113         for (i = 0; i < 16; i++)
114                 printf("%02x", md5[i]);
115         printf("\n");
116
117         /* Skip header & vendor info */
118         fseek(safeloader, 0x1014, SEEK_SET);
119
120         while (fscanf(safeloader, "fwup-ptn %s base 0x%x size 0x%x\t\r\n", name, &base, &size) == 3) {
121                 printf("%10s: %s (0x%x - 0x%x)\n", "Partition", name, base, base + size);
122         }
123
124 err_close:
125         fclose(safeloader);
126 out:
127         return err;
128 }
129
130 /**************************************************
131  * Extract
132  **************************************************/
133
134 static void osafeloader_extract_parse_options(int argc, char **argv) {
135         int c;
136
137         while ((c = getopt(argc, argv, "p:o:")) != -1) {
138                 switch (c) {
139                 case 'p':
140                         partition_name = optarg;
141                         break;
142                 case 'o':
143                         out_path = optarg;
144                         break;
145                 }
146         }
147 }
148
149 static int osafeloader_extract(int argc, char **argv) {
150         FILE *safeloader;
151         FILE *out;
152         struct safeloader_header hdr;
153         size_t bytes;
154         char name[32];
155         int base, size;
156         int err = 0;
157
158         if (argc < 3) {
159                 fprintf(stderr, "No SafeLoader file passed\n");
160                 err = -EINVAL;
161                 goto out;
162         }
163         safeloader_path = argv[2];
164
165         optind = 3;
166         osafeloader_extract_parse_options(argc, argv);
167         if (!partition_name) {
168                 fprintf(stderr, "No partition name specified\n");
169                 err = -EINVAL;
170                 goto out;
171         } else if (!out_path) {
172                 fprintf(stderr, "No output file specified\n");
173                 err = -EINVAL;
174                 goto out;
175         }
176
177         safeloader = fopen(safeloader_path, "r");
178         if (!safeloader) {
179                 fprintf(stderr, "Couldn't open %s\n", safeloader_path);
180                 err = -EACCES;
181                 goto out;
182         }
183
184         out = fopen(out_path, "w");
185         if (!out) {
186                 fprintf(stderr, "Couldn't open %s\n", out_path);
187                 err = -EACCES;
188                 goto err_close_safeloader;
189         }
190
191         bytes = fread(&hdr, 1, sizeof(hdr), safeloader);
192         if (bytes != sizeof(hdr)) {
193                 fprintf(stderr, "Couldn't read %s header\n", safeloader_path);
194                 err =  -EIO;
195                 goto err_close_out;
196         }
197
198         /* Skip vendor info */
199         fseek(safeloader, 0x1000, SEEK_CUR);
200
201         err = -ENOENT;
202         while (fscanf(safeloader, "fwup-ptn %s base 0x%x size 0x%x\t\r\n", name, &base, &size) == 3) {
203                 uint8_t buf[1024];
204
205                 if (strcmp(name, partition_name))
206                         continue;
207
208                 err = 0;
209
210                 fseek(safeloader, sizeof(hdr) + 0x1000 + base, SEEK_SET);
211
212                 while ((bytes = fread(buf, 1, osafeloader_min(sizeof(buf), size), safeloader)) > 0) {
213                         if (fwrite(buf, 1, bytes, out) != bytes) {
214                                 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path);
215                                 err = -EIO;
216                                 break;
217                         }
218                         size -= bytes;
219                 }
220
221                 if (size) {
222                         fprintf(stderr, "Couldn't extract whole partition %s from %s (%d B left)\n", partition_name, safeloader_path, size);
223                         err = -EIO;
224                 }
225
226                 break;
227         }
228
229 err_close_out:
230         fclose(out);
231 err_close_safeloader:
232         fclose(safeloader);
233 out:
234         return err;
235 }
236
237 /**************************************************
238  * Start
239  **************************************************/
240
241 static void usage() {
242         printf("Usage:\n");
243         printf("\n");
244         printf("Info about SafeLoader:\n");
245         printf("\tosafeloader info <file>\n");
246         printf("\n");
247         printf("Extract from SafeLoader:\n");
248         printf("\tosafeloader extract <file> [options]\n");
249         printf("\t-p name\t\t\t\tname of partition to extract\n");
250         printf("\t-o file\t\t\t\toutput file\n");
251 }
252
253 int main(int argc, char **argv) {
254         if (argc > 1) {
255                 if (!strcmp(argv[1], "info"))
256                         return osafeloader_info(argc, argv);
257                 else if (!strcmp(argv[1], "extract"))
258                         return osafeloader_extract(argc, argv);
259         }
260
261         usage();
262         return 0;
263 }