blob: c9acb947dc948cc3a493d8cb2b2881bf11e30dfa [file] [log] [blame]
Uwe Hermann7eb845e2008-11-02 17:01:06 +00001/*
2 * This file is part of the bayou project.
3 *
4 * Copyright (C) 2008 Advanced Micro Devices, Inc.
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 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Uwe Hermann7eb845e2008-11-02 17:01:06 +000014 */
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <fcntl.h>
20#include <unistd.h>
21#include <sys/stat.h>
22#include <sys/mman.h>
23#include <arpa/inet.h>
24
25#include "liblar.h"
26#include "self.h"
27
28static int lar_compress(struct LAR *lar, int algo, char *src, char *dst,
29 int len)
30{
31 int ret;
32
33 if (!lar->cfuncs[algo])
34 return -1;
35
36 lar->cfuncs[algo] (src, len, dst, &ret);
37 return ret;
38}
39
40static int lar_decompress(struct LAR *lar, int algo, char *src, char *dst,
41 int slen, int dlen)
42{
43 if (!lar->dfuncs[algo])
44 return -1;
45
46 lar->dfuncs[algo] (src, slen, dst, dlen);
47 return dlen;
48}
49
50static struct LARHeader *lar_get_header(struct LAR *lar, const char *filename)
51{
52 char *buffer;
53 int offset = 0;
54 struct LARHeader *lheader = NULL;
55 struct lar_header *header;
56
57 printf("Getting %s\n", filename);
58
59 buffer = malloc(sizeof(struct lar_header) + MAX_PATHLEN);
60
61 if (buffer == NULL)
62 return NULL;
63
64 while (1) {
65 int ret;
66
67 if (lseek(lar->fd, offset, SEEK_SET) == -1)
68 goto err;
69
70 ret = read(lar->fd, buffer, sizeof(struct lar_header));
71
72 if (ret <= 0)
73 goto err;
74
75 header = (struct lar_header *)buffer;
76
77 if (strncmp(header->magic, MAGIC, sizeof(MAGIC)))
78 goto err;
79
80 ret = read(lar->fd, buffer + sizeof(struct lar_header),
81 ntohl(header->offset) - sizeof(struct lar_header));
82
83 if (ret <= 0)
84 goto err;
85
86 if (!strcmp(buffer + sizeof(struct lar_header), filename))
87 break;
88
89 offset += ntohl(header->offset) +
90 ((ntohl(header->len) + 15) & ~0xF);
91 }
92
93 lheader = calloc(sizeof(struct LARHeader), 1);
94
95 if (lheader == NULL)
96 goto err;
97
98 lheader->hoffset = offset;
99 lheader->offset = offset + ntohl(header->offset);
100
101 lheader->reallen = ntohl(header->reallen);
102 lheader->len = ntohl(header->len);
103
104 lheader->loadaddress = ntohl(header->loadaddress);
105 lheader->compression = ntohl(header->compression);
106 lheader->entry = ntohl(header->entry);
107 lheader->checksum = ntohl(header->checksum);
108
109err:
110 free(buffer);
111 return lheader;
112}
113
114static int LAR_AppendBlob(struct LAR *lar, unsigned char *buffer,
115 int len, int rlen, struct LARAttr *attr)
116{
117 int nlen, nsize, lsize, i;
118 struct lar_header *header;
119 u8 *lptr;
120 u32 csum = 0;
121
122 if (attr == NULL)
123 return -1;
124
125 nlen = strlen(attr->name) + 1;
126
127 if (nlen > MAX_PATHLEN - 1)
128 nlen = MAX_PATHLEN - 1;
129
130 nsize = (nlen + 15) & ~0xF;
131
132 lsize = sizeof(struct lar_header) + nsize + len;
133 lptr = calloc(lsize + 1, 1);
134
135 if (lptr == NULL)
136 return -1;
137
138 header = (struct lar_header *)lptr;
139
140 memcpy(header->magic, MAGIC, 8);
141 header->reallen = htonl(rlen);
142 header->len = htonl(len);
143 header->offset = htonl(lsize - len);
144 header->loadaddress = htonl(attr->loadaddr);
145 header->compression = htonl(attr->compression);
146 header->entry = htonl(attr->entry);
147
148 strncpy(((char *)header) + sizeof(struct lar_header), attr->name, nlen);
149
150 for (i = 0; i < sizeof(struct lar_header) + nsize; i += 4)
151 csum += *((u32 *) (lptr + i));
152
153 for (i = 0; i < len; i += 4) {
154 /*
155 * The checksum needs to include the 16 byte padding at
156 * the end of the data before the next lar header. The
157 * problem is that the padding isn't going to be in the
158 * buffer, and if we try to read off the end of the buffer,
159 * we are just asking for trouble. So account for the
160 * situation where the datalen is not a multiple of four
161 * and get a safe value to add into the checksum.
162 * The rest of the padding will be zero, and can be safely
163 * ignored here.
164 */
165 if ((len - i) < 4) {
166 u32 val = 0;
167 int t;
168
169 for (t = 0; t < (len - i); t++)
170 val |= *((u8 *) buffer + (i + t)) << (t * 8);
171 csum += val;
172 } else
173 csum += *((u32 *) (buffer + i));
174 }
175
176 header->checksum = (u32) (~0 - csum);
177
178 lseek(lar->fd, 0, SEEK_END);
179
180 /* FIXME: Error check here. */
181
182 write(lar->fd, header, sizeof(struct lar_header) + nsize);
183 write(lar->fd, buffer, len);
184
185 /* Add in padding to the next 16 byte boundary. */
186 if (lsize & 0xF) {
187 int i;
188 char null = '\0';
189
190 for (i = lsize & 0xF; i < 0x10; i++)
191 write(lar->fd, &null, 1);
192 }
193
194 return 0;
195}
196
197int LAR_AppendBuffer(struct LAR *lar, unsigned char *buffer, int len,
198 struct LARAttr *attr)
199{
200 unsigned char *tbuf;
201 int rlen, ret = -1;
202
203 if (attr->compression == ALGO_NONE)
204 return LAR_AppendBlob(lar, buffer, len, len, attr);
205
206 tbuf = malloc(len);
207
208 if (tbuf == NULL)
209 return -1;
210
211 rlen = lar_compress(lar, attr->compression, (char *)buffer,
212 (char *)tbuf, len);
213
214 if (rlen > 0)
215 ret = LAR_AppendBlob(lar, tbuf, rlen, len, attr);
216
217 free(tbuf);
218 return ret;
219}
220
221int LAR_AppendSelf(struct LAR *lar, const char *filename, struct LARAttr *attr)
222{
223 unsigned char *buffer;
224 int len = elf_to_self(filename, &buffer,
225 lar->cfuncs[attr->compression]);
226 int ret;
227
228 if (len == -1)
229 return -1;
230
231 ret = LAR_AppendBlob(lar, buffer, len, len, attr);
232 free(buffer);
233
234 return ret;
235}
236
237int LAR_AppendFile(struct LAR *lar, const char *filename, struct LARAttr *attr)
238{
239 int fd;
240 struct stat s;
241 char *filep;
242 int ret;
243
244 if (iself((char *)filename))
245 return LAR_AppendSelf(lar, filename, attr);
246
247 fd = open(filename, O_RDONLY);
248
249 if (fd == -1)
250 return -1;
251
252 if (fstat(fd, &s))
253 return -1;
254
255 filep = (char *)mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
256
257 if (filep == MAP_FAILED)
258 return -1;
259
260 ret = LAR_AppendBuffer(lar, (unsigned char *)filep, s.st_size, attr);
261
262 munmap(filep, s.st_size);
263 return ret;
264}
265
266int LAR_DeleteFile(struct LAR *lar, const char *filename)
267{
268 struct LARHeader *header = lar_get_header(lar, filename);
269 int len, ret = -1;
270 char *filep, *buffer;
271
272 if (header == NULL)
273 return -1;
274
275 buffer = malloc(4096);
276 if (buffer == NULL)
277 return -1;
278
279 len = header->offset + header->len;
280
281 /* First, map the space and zero it out. */
282
283 filep = (char *)mmap(0, len, PROT_READ, MAP_SHARED, lar->fd,
284 header->hoffset);
285
286 if (filep == MAP_FAILED)
287 return -1;
288
289 memset(filep, 0, len);
290 munmap(filep, len);
291
292 /* Now move the rest of the LAR into place. */
293 /* FIXME: This does not account for the bootblock! */
294
295 int dst = header->hoffset;
296 int src = header->hoffset + len;
297
298 while (1) {
299 int l, w;
300
301 if (lseek(lar->fd, src, SEEK_SET))
302 goto err;
303
304 l = read(lar->fd, buffer, 8192);
305
306 if (l == -1)
307 goto err;
308 if (l == 0)
309 goto err;
310 if (lseek(lar->fd, dst, SEEK_SET))
311 goto err;
312
313 w = write(lar->fd, buffer, l);
314
315 if (w <= 0)
316 goto err;
317
318 dst += w;
319 src += w;
320 }
321
322 ret = 0;
323
324err:
325 free(buffer);
326 return ret;
327}
328
329void LAR_CloseFile(struct LARFile *file)
330{
331 if (file != NULL) {
332 if (file->buffer != NULL)
333 free(file->buffer);
334 free(file);
335 }
336}
337
338struct LARFile *LAR_MapFile(struct LAR *lar, const char *filename)
339{
340 struct LARFile *file;
341 struct LARHeader *header = lar_get_header(lar, filename);
342 char *filep;
343 int ret;
344
345 if (header == NULL)
346 return NULL;
347
348 file = calloc(sizeof(struct LARFile), 1);
349
350 if (file == NULL)
351 return NULL;
352
353 file->len = header->reallen;
354 file->buffer = calloc(header->reallen, 1);
355
356 if (file->buffer == NULL)
357 goto err;
358
359 /*
360 * The offset needs to be a multiple of PAGE_SIZE, so just mmap
361 * from offset 0, its easier then doing the math.
362 */
363
364 filep = mmap(0, header->offset + header->len,
365 PROT_READ, MAP_SHARED, lar->fd, 0);
366
367 if (filep == MAP_FAILED) {
368 printf("Map failed: %m\n");
369 goto err;
370 }
371
372 if (header->compression != ALGO_NONE) {
373 ret = lar_decompress(lar, header->compression,
374 filep + header->offset, file->buffer,
375 header->len, header->reallen);
376 } else {
377 memcpy(file->buffer, filep + header->offset, header->len);
378 ret = header->len;
379 }
380
381 munmap(filep, header->offset + header->len);
382
383 if (ret == header->reallen)
384 return file;
385
386err:
387 if (file->buffer)
388 free(file->buffer);
389
390 free(file);
391 return NULL;
392}
393
394int LAR_SetCompressionFuncs(struct LAR *lar, int algo,
395 LAR_CompFunc cfunc, LAR_DecompFunc dfunc)
396{
397
398 if (algo >= ALGO_INVALID)
399 return -1;
400
401 lar->cfuncs[algo] = cfunc;
402 lar->dfuncs[algo] = dfunc;
403
404 return 0;
405}
406
407void LAR_Close(struct LAR *lar)
408{
409 if (lar != NULL) {
410 if (lar->fd)
411 close(lar->fd);
412
413 free(lar);
414 }
415}
416
417struct LAR *LAR_Open(const char *filename)
418{
419 struct LAR *lar = calloc(sizeof(struct LAR), 1);
420
421 if (lar == NULL)
422 return NULL;
423
424 lar->fd = open(filename, O_RDWR);
425
426 if (lar->fd > 0)
427 return lar;
428
429 free(lar);
430 return NULL;
431}
432
433struct LAR *LAR_Create(const char *filename)
434{
435 struct LAR *lar = calloc(sizeof(struct LAR), 1);
436
437 if (lar == NULL)
438 return NULL;
439
440 lar->fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
441
442 if (lar->fd > 0)
443 return lar;
444
445 free(lar);
446 return NULL;
447}
448
449void LAR_SetAttrs(struct LARAttr *attrs, char *name, int algo)
450{
451 if (attrs == NULL)
452 return;
453
454 memset(attrs, 0, sizeof(*attrs));
455 snprintf(attrs->name, sizeof(attrs->name) - 1, name);
456 attrs->compression = algo;
457}