1 /*
2 * Copyright (c) 2007 Vreixo Formoso
3 * Copyright (c) 2009 Thomas Schmitt
4 *
5 * This file is part of the libisofs project; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License version 2
7 * or later as published by the Free Software Foundation.
8 * See COPYING file for details.
9 */
10
11 /*
12 * This file contains functions related to the reading of SUSP,
13 * Rock Ridge and AAIP extensions on an ECMA-119 image.
14 */
15
16 #ifdef HAVE_CONFIG_H
17 #include "../config.h"
18 #endif
19
20 #include "libisofs.h"
21 #include "ecma119.h"
22 #include "util.h"
23 #include "rockridge.h"
24 #include "messages.h"
25
26 #include <sys/stat.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 struct susp_iterator
31 {
32 uint8_t* base;
33 int pos;
34 int size;
35 IsoDataSource *src;
36 int msgid;
37
38 /* Number of blocks in the ISO 9660 filesystem */
39 uint32_t fs_blocks;
40
41 /* block and offset for next continuation area */
42 uint32_t ce_block;
43 uint32_t ce_off;
44
45 /** Length of the next continuation area, 0 if no more CA are specified */
46 uint32_t ce_len;
47
48 uint8_t *buffer; /*< If there are continuation areas */
49 };
50
51 SuspIterator*
susp_iter_new(IsoDataSource * src,struct ecma119_dir_record * record,uint32_t fs_blocks,uint8_t len_skp,int msgid)52 susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record,
53 uint32_t fs_blocks, uint8_t len_skp, int msgid)
54 {
55 int pad = (record->len_fi[0] + 1) % 2;
56 struct susp_iterator *iter = malloc(sizeof(struct susp_iterator));
57 if (iter == NULL) {
58 return NULL;
59 }
60
61 iter->base = record->file_id + record->len_fi[0] + pad;
62 iter->pos = len_skp; /* 0 in most cases */
63 iter->size = record->len_dr[0] - record->len_fi[0] - 33 - pad;
64 iter->src = src;
65 iter->msgid = msgid;
66 iter->fs_blocks = fs_blocks;
67
68 iter->ce_len = 0;
69 iter->buffer = NULL;
70
71 return iter;
72 }
73
74 /* More than 1 MiB in a single file's CE area is suspicious */
75 #define ISO_SUSP_MAX_CE_BYTES (1024 * 1024)
76
77
78 /* @param flag bit0 = First call on root:
79 Not yet clear whether this is SUSP at all
80 */
susp_iter_next(SuspIterator * iter,struct susp_sys_user_entry ** sue,int flag)81 int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue,
82 int flag)
83 {
84 struct susp_sys_user_entry *entry;
85
86 entry = (struct susp_sys_user_entry*)(iter->base + iter->pos);
87
88 if (flag & 1) {
89 /* Yet unclear whether it is SUSP at all */
90 if (iter->size < 7)
91 return 0;
92 if (!SUSP_SIG(entry, 'S', 'P'))
93 return 0;
94 if (entry->len_sue[0] < 7)
95 return 0;
96 /* Looks like SUSP enough to pass the further processing here. */
97 }
98 if ( (iter->pos + 4 > iter->size) || (SUSP_SIG(entry, 'S', 'T'))) {
99
100 /*
101 * End of the System Use Area or Continuation Area.
102 * Note that ST is not needed when the space left is less than 4.
103 * (IEEE 1281, SUSP. section 4)
104 */
105 if (iter->ce_len) {
106 uint32_t block, nblocks, skipped_blocks, skipped_bytes;
107
108 /* A CE was found, there is another continuation area */
109 skipped_blocks = iter->ce_off / BLOCK_SIZE;
110 skipped_bytes = skipped_blocks * BLOCK_SIZE;
111 nblocks = DIV_UP(iter->ce_off - skipped_bytes + iter->ce_len,
112 BLOCK_SIZE);
113 if (nblocks <= 0 || iter->ce_len > ISO_SUSP_MAX_CE_BYTES)
114 return ISO_SUSP_WRONG_CE_SIZE;
115 if (((uint64_t) iter->ce_block) + skipped_blocks + nblocks >
116 (uint64_t) iter->fs_blocks)
117 return ISO_SUSP_WRONG_CE_SIZE;
118 iter->buffer = realloc(iter->buffer, nblocks * BLOCK_SIZE);
119
120 /* Read blocks needed to cache the given CE area range */
121 for (block = 0; block < nblocks; ++block) {
122 int ret;
123 ret = iter->src->read_block(iter->src,
124 iter->ce_block + skipped_blocks + block,
125 iter->buffer + block * BLOCK_SIZE);
126 if (ret < 0) {
127 return ret;
128 }
129 }
130 iter->base = iter->buffer + (iter->ce_off - skipped_bytes);
131 iter->pos = 0;
132 iter->size = iter->ce_len;
133 iter->ce_len = 0;
134 entry = (struct susp_sys_user_entry*)iter->base;
135 } else {
136 return 0;
137 }
138 }
139
140 if (entry->len_sue[0] == 0) {
141 /* a wrong image with this lead us to a infinity loop */
142 iso_msg_submit(iter->msgid, ISO_WRONG_RR, 0,
143 "Damaged RR/SUSP information.");
144 return ISO_WRONG_RR;
145 }
146
147 iter->pos += entry->len_sue[0];
148
149 if (SUSP_SIG(entry, 'C', 'E')) {
150 /* Continuation entry */
151 if (iter->ce_len) {
152 int ret;
153 ret = iso_msg_submit(iter->msgid, ISO_UNSUPPORTED_SUSP, 0,
154 "More than one CE System user entry has found in a single "
155 "System Use field or continuation area. This breaks SUSP "
156 "standard and it's not supported. Ignoring last CE. Maybe "
157 "the image is damaged.");
158 if (ret < 0) {
159 return ret;
160 }
161 } else {
162 iter->ce_block = iso_read_bb(entry->data.CE.block, 4, NULL);
163 iter->ce_off = iso_read_bb(entry->data.CE.offset, 4, NULL);
164 iter->ce_len = iso_read_bb(entry->data.CE.len, 4, NULL);
165 }
166
167 /* we don't want to return CE entry to the user */
168 return susp_iter_next(iter, sue, 0);
169 } else if (SUSP_SIG(entry, 'P', 'D')) {
170 /* skip padding */
171 return susp_iter_next(iter, sue, 0);
172 }
173
174 *sue = entry;
175 return ISO_SUCCESS;
176 }
177
susp_iter_free(SuspIterator * iter)178 void susp_iter_free(SuspIterator *iter)
179 {
180 free(iter->buffer);
181 free(iter);
182 }
183
184 /**
185 * Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1).
186 *
187 * @return
188 * 1 on success, < 0 on error
189 */
read_rr_PX(struct susp_sys_user_entry * px,struct stat * st)190 int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st)
191 {
192 if (px == NULL || st == NULL) {
193 return ISO_NULL_POINTER;
194 }
195 if (px->sig[0] != 'P' || px->sig[1] != 'X') {
196 return ISO_WRONG_ARG_VALUE;
197 }
198
199 if (px->len_sue[0] != 44 && px->len_sue[0] != 36) {
200 return ISO_WRONG_RR;
201 }
202
203 st->st_mode = iso_read_bb(px->data.PX.mode, 4, NULL);
204 st->st_nlink = iso_read_bb(px->data.PX.links, 4, NULL);
205 st->st_uid = iso_read_bb(px->data.PX.uid, 4, NULL);
206 st->st_gid = iso_read_bb(px->data.PX.gid, 4, NULL);
207 st->st_ino = 0;
208 if (px->len_sue[0] == 44) {
209 /* this corresponds to RRIP 1.12, so we have inode serial number */
210 st->st_ino = iso_read_bb(px->data.PX.serial, 4, NULL);
211 /* Indicate that st_ino is valid */
212 return 2;
213 }
214 return 1;
215 }
216
217 /**
218 * Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6)
219 *
220 * @return
221 * 1 on success, < 0 on error
222 */
read_rr_TF(struct susp_sys_user_entry * tf,struct stat * st)223 int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st)
224 {
225 time_t time;
226 int s;
227 int nts = 0;
228
229 if (tf == NULL || st == NULL) {
230 return ISO_NULL_POINTER;
231 }
232 if (tf->sig[0] != 'T' || tf->sig[1] != 'F') {
233 return ISO_WRONG_ARG_VALUE;
234 }
235
236 if (tf->data.TF.flags[0] & (1 << 7)) {
237 /* long form */
238 s = 17;
239 } else {
240 s = 7;
241 }
242
243 /* 1. Creation time */
244 if (tf->data.TF.flags[0] & (1 << 0)) {
245 /* Linux accepts ctime by Creation time and by Attributes time.
246 * If both are given, then Attribute time will win.
247 */
248 if (tf->len_sue[0] < 5 + (nts+1) * s) {
249 /* RR TF entry too short. */
250 return ISO_WRONG_RR;
251 }
252 if (s == 7) {
253 time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
254 } else {
255 time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
256 }
257 st->st_ctime = time;
258 ++nts;
259 }
260
261 /* 2. modify time */
262 if (tf->data.TF.flags[0] & (1 << 1)) {
263 if (tf->len_sue[0] < 5 + (nts+1) * s) {
264 /* RR TF entry too short. */
265 return ISO_WRONG_RR;
266 }
267 if (s == 7) {
268 time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
269 } else {
270 time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
271 }
272 st->st_mtime = time;
273 ++nts;
274 }
275
276 /* 3. access time */
277 if (tf->data.TF.flags[0] & (1 << 2)) {
278 if (tf->len_sue[0] < 5 + (nts+1) * s) {
279 /* RR TF entry too short. */
280 return ISO_WRONG_RR;
281 }
282 if (s == 7) {
283 time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
284 } else {
285 time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
286 }
287 st->st_atime = time;
288 ++nts;
289 }
290
291 /* 4. attributes time */
292 if (tf->data.TF.flags[0] & (1 << 3)) {
293 if (tf->len_sue[0] < 5 + (nts+1) * s) {
294 /* RR TF entry too short. */
295 return ISO_WRONG_RR;
296 }
297 if (s == 7) {
298 time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
299 } else {
300 time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
301 }
302 st->st_ctime = time;
303 ++nts;
304 }
305
306 /* we ignore backup, expire and effect times */
307
308 return ISO_SUCCESS;
309 }
310
311 /**
312 * Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to
313 * the given name. You can pass a pointer to NULL as name.
314 *
315 * @return
316 * 1 on success, < 0 on error
317 */
read_rr_NM(struct susp_sys_user_entry * nm,char ** name,int * cont)318 int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont)
319 {
320 if (nm == NULL || name == NULL) {
321 return ISO_NULL_POINTER;
322 }
323 if (nm->sig[0] != 'N' || nm->sig[1] != 'M') {
324 return ISO_WRONG_ARG_VALUE;
325 }
326
327 if (nm->len_sue[0] == 5) {
328 if (nm->data.NM.flags[0] & 0x2) {
329 /* it is a "." entry */
330 if (*name == NULL) {
331 return ISO_SUCCESS;
332 } else {
333 /* we can't have a previous not-NULL name */
334 return ISO_WRONG_RR;
335 }
336 }
337 }
338
339 if (nm->len_sue[0] <= 5) {
340 /* ".." entry is an error, as we will never call it */
341 return ISO_WRONG_RR;
342 }
343
344 /* concatenate the results */
345 if (*cont) {
346 *name = realloc(*name, strlen(*name) + nm->len_sue[0] - 5 + 1);
347 strncat(*name, (char*)nm->data.NM.name, nm->len_sue[0] - 5);
348 } else {
349 *name = iso_util_strcopy((char*)nm->data.NM.name, nm->len_sue[0] - 5);
350 }
351 if (*name == NULL) {
352 return ISO_OUT_OF_MEM;
353 }
354
355 /* and set cond according to the value of CONTINUE flag */
356 *cont = nm->data.NM.flags[0] & 0x01;
357 return ISO_SUCCESS;
358 }
359
360 /**
361 * Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues.
362 *
363 * @param cont
364 * 0 not continue, 1 continue, 2 continue component
365 * @return
366 * 1 on success, < 0 on error
367 */
read_rr_SL(struct susp_sys_user_entry * sl,char ** dest,int * cont)368 int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont)
369 {
370 int pos;
371
372 if (sl == NULL || dest == NULL) {
373 return ISO_NULL_POINTER;
374 }
375 if (sl->sig[0] != 'S' || sl->sig[1] != 'L') {
376 return ISO_WRONG_ARG_VALUE;
377 }
378
379 for (pos = 0; pos + 5 < sl->len_sue[0];
380 pos += 2 + sl->data.SL.comps[pos + 1]) {
381 char *comp;
382 uint8_t len;
383 uint8_t flags = sl->data.SL.comps[pos];
384
385 if (flags & 0x2) {
386 /* current directory */
387 len = 1;
388 comp = ".";
389 } else if (flags & 0x4) {
390 /* parent directory */
391 len = 2;
392 comp = "..";
393 } else if (flags & 0x8) {
394 /* root directory */
395 len = 1;
396 comp = "/";
397 } else if (flags & ~0x01) {
398 /* unsupported flag component */
399 return ISO_UNSUPPORTED_RR;
400 } else {
401 len = sl->data.SL.comps[pos + 1];
402 comp = (char*)&sl->data.SL.comps[pos + 2];
403 }
404
405 if (*cont == 1) {
406 /* new component */
407 size_t size = strlen(*dest);
408 int has_slash;
409
410 *dest = realloc(*dest, strlen(*dest) + len + 2);
411 if (*dest == NULL) {
412 return ISO_OUT_OF_MEM;
413 }
414 /* it is a new compoenent, add the '/' */
415 has_slash = 0;
416 if (size > 0)
417 if ((*dest)[size - 1] == '/')
418 has_slash = 1;
419 if (!has_slash) {
420 (*dest)[size] = '/';
421 (*dest)[size+1] = '\0';
422 }
423 strncat(*dest, comp, len);
424 } else if (*cont == 2) {
425 /* the component continues */
426 *dest = realloc(*dest, strlen(*dest) + len + 1);
427 if (*dest == NULL) {
428 return ISO_OUT_OF_MEM;
429 }
430 /* we don't have to add the '/' */
431 strncat(*dest, comp, len);
432 } else {
433 *dest = iso_util_strcopy(comp, len);
434 }
435 if (*dest == NULL) {
436 return ISO_OUT_OF_MEM;
437 }
438 /* do the component continue or not? */
439 *cont = (flags & 0x01) ? 2 : 1;
440 }
441
442 if (*cont == 2) {
443 /* TODO check that SL flag is set to continute too ?*/
444 } else {
445 *cont = sl->data.SL.flags[0] & 0x1 ? 1 : 0;
446 }
447
448 return ISO_SUCCESS;
449 }
450
451 /**
452 * Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2).
453 *
454 * @return
455 * 1 on success, < 0 on error
456 */
read_rr_PN(struct susp_sys_user_entry * pn,struct stat * st)457 int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st)
458 {
459 int high_shift= 0;
460
461 if (pn == NULL || st == NULL) {
462 return ISO_NULL_POINTER;
463 }
464 if (pn->sig[0] != 'P' || pn->sig[1] != 'N') {
465 return ISO_WRONG_ARG_VALUE;
466 }
467
468 if (pn->len_sue[0] != 20) {
469 return ISO_WRONG_RR;
470 }
471
472 /* (dev_t << 32) causes compiler warnings on FreeBSD
473 because sizeof(dev_t) is 4.
474 */
475 st->st_rdev = (dev_t)iso_read_bb(pn->data.PN.low, 4, NULL);
476 if (sizeof(st->st_rdev) > 4) {
477 high_shift = 32;
478 st->st_rdev |= (dev_t)((dev_t)iso_read_bb(pn->data.PN.high, 4, NULL) <<
479 high_shift);
480 }
481
482 /* was originally:
483 st->st_rdev = (dev_t)((dev_t)iso_read_bb(pn->data.PN.high, 4, NULL) << 32)
484 | (dev_t)iso_read_bb(pn->data.PN.low, 4, NULL);
485 */
486
487 return ISO_SUCCESS;
488 }
489
490
491 /* AA is the obsolete field signature of AAIP versions < 2.0
492 */
read_aaip_AA(struct susp_sys_user_entry * sue,unsigned char ** aa_string,size_t * aa_size,size_t * aa_len,size_t * prev_field,int * is_done,int flag)493 int read_aaip_AA(struct susp_sys_user_entry *sue,
494 unsigned char **aa_string, size_t *aa_size, size_t *aa_len,
495 size_t *prev_field, int *is_done, int flag)
496 {
497 unsigned char *aapt;
498
499 if (*is_done) {
500
501 /* To coexist with Apple ISO :
502 Gracefully react on possibly trailing Apple AA
503 */
504 if (sue->version[0] != 1 || sue->len_sue[0] == 7)
505 return ISO_SUCCESS;
506
507 return ISO_WRONG_RR;
508 }
509 if (*aa_size == 0 || *aa_string == NULL) {
510 /* Gracefully react on possibly leading Apple AA
511 */
512 if (sue->version[0] != 1 || sue->len_sue[0] < 9)
513 return ISO_SUCCESS;
514 }
515
516 /* A valid AAIP AA entry has 5 header bytes and at least 1 component byte
517 */
518 if (sue->len_sue[0] < 6)
519 return ISO_WRONG_RR;
520
521 /* Possibly create or grow storage */
522 if (*aa_size == 0 || *aa_string == NULL) {
523 *aa_size = *aa_len + sue->len_sue[0];
524 *aa_string = calloc(*aa_size, 1);
525 *aa_len = 0;
526 } else if (*aa_len + sue->len_sue[0] > *aa_size) {
527
528 if (sue->version[0] != 1) {
529 /* Apple ISO within the AAIP field group is not AAIP compliant
530 */
531 return ISO_WRONG_RR;
532 }
533
534 *aa_size += *aa_len + sue->len_sue[0];
535 *aa_string = realloc(*aa_string, *aa_size);
536 }
537 if (*aa_string == NULL)
538 return ISO_OUT_OF_MEM;
539
540 if (*aa_len > 0) {
541 /* Mark prev_field as being continued */
542 (*aa_string)[*prev_field + 4] = 1;
543 }
544
545 *prev_field = *aa_len;
546
547 /* Compose new SUSP header with signature aa[], cont == 0 */
548 aapt = *aa_string + *aa_len;
549
550 aapt[0] = 'A';
551 aapt[1] = 'L';
552 aapt[2] = sue->len_sue[0];
553 aapt[3] = 1;
554 aapt[4] = 0;
555
556 /* Append sue payload */
557 memcpy(aapt + 5, sue->data.AL.comps, sue->len_sue[0] - 5);
558 *is_done = !(sue->data.AL.flags[0] & 1);
559 *aa_len += sue->len_sue[0];
560
561 return ISO_SUCCESS;
562 }
563
564
565 /* AL is the field signature of AAIP versions >= 2.0
566 */
read_aaip_AL(struct susp_sys_user_entry * sue,unsigned char ** aa_string,size_t * aa_size,size_t * aa_len,size_t * prev_field,int * is_done,int flag)567 int read_aaip_AL(struct susp_sys_user_entry *sue,
568 unsigned char **aa_string, size_t *aa_size, size_t *aa_len,
569 size_t *prev_field, int *is_done, int flag)
570 {
571 unsigned char *aapt;
572
573 if (*is_done)
574 return ISO_WRONG_RR;
575 if (sue->version[0] != 1)
576 return ISO_WRONG_RR;
577
578 /* A valid AL entry has 5 header bytes and at least 1 component byte
579 */
580 if (sue->len_sue[0] < 6)
581 return ISO_WRONG_RR;
582
583 /* Possibly create or grow storage */
584 if (*aa_size == 0 || *aa_string == NULL) {
585 *aa_size = *aa_len + sue->len_sue[0];
586 *aa_string = calloc(*aa_size, 1);
587 *aa_len = 0;
588 } else if (*aa_len + sue->len_sue[0] > *aa_size) {
589 *aa_size += *aa_len + sue->len_sue[0];
590 *aa_string = realloc(*aa_string, *aa_size);
591 }
592 if (*aa_string == NULL)
593 return ISO_OUT_OF_MEM;
594
595 if (*aa_len > 0) {
596 /* Mark prev_field as being continued */
597 (*aa_string)[*prev_field + 4] = 1;
598 }
599
600 *prev_field = *aa_len;
601
602 /* Compose new SUSP header with signature aa[], cont == 0 */
603 aapt = *aa_string + *aa_len;
604
605 aapt[0] = 'A';
606 aapt[1] = 'L';
607 aapt[2] = sue->len_sue[0];
608 aapt[3] = 1;
609 aapt[4] = 0;
610
611 /* Append sue payload */
612 memcpy(aapt + 5, sue->data.AL.comps, sue->len_sue[0] - 5);
613 *is_done = !(sue->data.AL.flags[0] & 1);
614 *aa_len += sue->len_sue[0];
615
616 return ISO_SUCCESS;
617 }
618
619 /**
620 * Reads the zisofs parameters from a ZF field (see doc/zisofs_format.txt
621 * and doc/zisofs2_format.txt).
622 *
623 * @return
624 * 1 on success, < 0 on error
625 */
read_zisofs_ZF(struct susp_sys_user_entry * zf,uint8_t algorithm[2],uint8_t * header_size_div4,uint8_t * block_size_log2,uint64_t * uncompressed_size,int flag)626 int read_zisofs_ZF(struct susp_sys_user_entry *zf, uint8_t algorithm[2],
627 uint8_t *header_size_div4, uint8_t *block_size_log2,
628 uint64_t *uncompressed_size, int flag)
629 {
630 if (zf == NULL) {
631 return ISO_NULL_POINTER;
632 }
633 if ((zf->sig[0] != 'Z' || zf->sig[1] != 'F') &&
634 (zf->sig[0] != 'Z' || zf->sig[1] != '2'))
635 return ISO_WRONG_ARG_VALUE;
636 if (zf->len_sue[0] != 16) {
637 return ISO_WRONG_RR;
638 }
639 if (zf->version[0] > 2)
640 return ISO_WRONG_RR;
641 algorithm[0] = zf->data.ZF.parameters[0];
642 algorithm[1] = zf->data.ZF.parameters[1];
643 *header_size_div4 = zf->data.ZF.parameters[2];
644 *block_size_log2 = zf->data.ZF.parameters[3];
645 if (zf->version[0] == 1)
646 *uncompressed_size = iso_read_bb(&(zf->data.ZF.parameters[4]), 4,
647 NULL);
648 else
649 *uncompressed_size = iso_read_lsb64(&(zf->data.ZF.parameters[4]));
650 return ISO_SUCCESS;
651 }
652
653
654