1 /******************************* LICENCE **************************************
2 * Any code in this file may be redistributed or modified under the terms of
3 * the GNU General Public Licence as published by the Free Software
4 * Foundation; version 2 of the licence.
5 ****************************** END LICENCE ***********************************/
6
7 /******************************************************************************
8 * Author:
9 * Andrew Smith, http://littlesvr.ca/misc/contactandrew.php
10 *
11 * Contributors:
12 *
13 ******************************************************************************/
14
15 #include <stdlib.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <limits.h>
21
22 #include "bk.h"
23 #include "bkInternal.h"
24 #include "bkRead.h"
25 #include "bkRead7x.h"
26 #include "bkTime.h"
27 #include "bkError.h"
28 #include "bkLink.h"
29 #include "bkMisc.h"
30 #include "bkIoWrappers.h"
31
32 /* numbers as recorded on image */
33 #define VDTYPE_BOOT 0
34 #define VDTYPE_PRIMARY 1
35 #define VDTYPE_SUPPLEMENTARY 2
36 #define VDTYPE_VOLUMEPARTITION 3
37 #define VDTYPE_TERMINATOR 255
38
39 /* this function is really just for use in readRockridgeSymlink()
40 * returns number of chars appended
41 * destMaxLen doesn't include '\0'
42 * if maxSrcLen is -1 tries to copy all of it */
appendStringIfHaveRoom(char * dest,const char * src,size_t destMaxLen,size_t destCharsAlreadyUsed,int maxSrcLen)43 size_t appendStringIfHaveRoom(char* dest, const char* src, size_t destMaxLen,
44 size_t destCharsAlreadyUsed, int maxSrcLen)
45 {
46 size_t srcLen;
47
48 if(maxSrcLen == -1)
49 srcLen = strlen(src);
50 else
51 srcLen = maxSrcLen;
52
53 if(destCharsAlreadyUsed + srcLen > destMaxLen)
54 return 0;
55
56 strncat(dest, src, srcLen);
57
58 return srcLen;
59 }
60
61 /*******************************************************************************
62 * bk_open_image()
63 *
64 * */
bk_open_image(VolInfo * volInfo,const char * filename)65 int bk_open_image(VolInfo* volInfo, const char* filename)
66 {
67 size_t len;
68
69 volInfo->imageForReading = open(filename, O_RDONLY, 0);
70 if(volInfo->imageForReading == -1)
71 {
72 volInfo->imageForReading = 0;
73 return BKERROR_OPEN_READ_FAILED;
74 }
75
76 int rc;
77 BkStatStruct statStruct;
78
79 /* record inode number */
80 rc = bkStat(filename, &statStruct);
81 if(rc == -1)
82 return BKERROR_STAT_FAILED;
83
84 volInfo->imageForReadingInode = statStruct.st_ino;
85
86 /* skip the first 150 sectors if the image is an NRG */
87 len = strlen(filename);
88 if( (filename[len - 3] == 'N' || filename[len - 3] == 'n') &&
89 (filename[len - 2] == 'R' || filename[len - 2] == 'r') &&
90 (filename[len - 1] == 'G' || filename[len - 1] == 'g') )
91 {
92 readSeekSet(volInfo, NBYTES_LOGICAL_BLOCK * 16, SEEK_SET);
93 }
94
95 return 1;
96 }
97
98 /*******************************************************************************
99 * bk_read_dir_tree()
100 * filenameType can be only one (do not | more then one)
101 * */
bk_read_dir_tree(VolInfo * volInfo,int filenameType,bool keepPosixPermissions,void (* progressFunction)(VolInfo *))102 int bk_read_dir_tree(VolInfo* volInfo, int filenameType,
103 bool keepPosixPermissions,
104 void(*progressFunction)(VolInfo*))
105 {
106 volInfo->progressFunction = progressFunction;
107
108 if(filenameType == FNTYPE_ROCKRIDGE || filenameType == FNTYPE_9660)
109 readSeekSet(volInfo, volInfo->pRootDrOffset, SEEK_SET);
110 else /* if(filenameType == FNTYPE_JOLIET) */
111 readSeekSet(volInfo, volInfo->sRootDrOffset, SEEK_SET);
112
113 return readDir(volInfo, &(volInfo->dirTree),
114 filenameType, keepPosixPermissions);
115 }
116
117 /*******************************************************************************
118 * bk_read_vol_info()
119 * public function to read volume information
120 * assumes pvd is first descriptor in set
121 * */
bk_read_vol_info(VolInfo * volInfo)122 int bk_read_vol_info(VolInfo* volInfo)
123 {
124 int rc;
125 unsigned char vdType; /* to check what descriptor follows */
126 bool haveMorePvd; /* to skip extra pvds */
127 unsigned char escapeSequence[3]; /* only interested in a joliet sequence */
128 char timeString[17]; /* for creation time */
129 bk_off_t locationOfNextDescriptor;
130 unsigned bootCatalogLocation; /* logical sector number */
131 char elToritoSig[24];
132 unsigned char bootMediaType;
133 unsigned short bootRecordSize;
134 unsigned bootRecordSectorNumber;
135
136 /* vars for checking rockridge */
137 unsigned realRootLoc; /* location of the root dr inside root dir */
138 unsigned char recordLen; /* length of rood dr */
139 unsigned char sPsUentry[7]; /* su entry SP */
140
141 /* will always have this unless image is broken */
142 volInfo->filenameTypes = FNTYPE_9660;
143
144 /* might not have supplementary descriptor */
145 volInfo->sRootDrOffset = 0;
146
147 /* skip system area */
148 readSeekSet(volInfo, NLS_SYSTEM_AREA * NBYTES_LOGICAL_BLOCK, SEEK_SET);
149
150 /* READ PVD */
151 /* make sure pvd exists */
152 rc = read711(volInfo, &vdType);
153 if(rc != 1)
154 return BKERROR_READ_GENERIC;
155
156 /* first descriptor must be primary */
157 if(vdType != VDTYPE_PRIMARY)
158 return BKERROR_VD_NOT_PRIMARY;
159
160 readSeekSet(volInfo, 39, SEEK_CUR);
161
162 rc = readRead(volInfo, volInfo->volId, 32);
163 if(rc != 32)
164 return BKERROR_READ_GENERIC;
165 volInfo->volId[32] = '\0';
166 stripSpacesFromEndOfString(volInfo->volId);
167
168 readSeekSet(volInfo, 84, SEEK_CUR);
169
170 /* am now at root dr */
171 volInfo->pRootDrOffset = readSeekTell(volInfo);
172
173 /* SEE if rockridge exists */
174 readSeekSet(volInfo, 2, SEEK_CUR);
175
176 rc = read733(volInfo, &realRootLoc);
177 if(rc != 8)
178 return BKERROR_READ_GENERIC;
179 realRootLoc *= NBYTES_LOGICAL_BLOCK;
180
181 readSeekSet(volInfo, realRootLoc, SEEK_SET);
182
183 rc = read711(volInfo, &recordLen);
184 if(rc != 1)
185 return BKERROR_READ_GENERIC;
186
187 if(recordLen >= 41)
188 /* a minimum root with SP su field */
189 {
190 /* root dr has filename length of 1 */
191 readSeekSet(volInfo, 33, SEEK_CUR);
192
193 /* a rockridge root dr has an SP su entry here */
194
195 rc = readRead(volInfo, &sPsUentry, 7);
196 if(rc != 7)
197 return BKERROR_READ_GENERIC;
198
199 if( sPsUentry[0] == 0x53 && sPsUentry[1] == 0x50 &&
200 sPsUentry[2] == 7 &&
201 sPsUentry[4] == 0xBE && sPsUentry[5] == 0xEF )
202 /* rockridge it is */
203 {
204 volInfo->filenameTypes |= FNTYPE_ROCKRIDGE;
205 }
206 }
207
208 /* go back to where it was before trying rockridge */
209 readSeekSet(volInfo, volInfo->pRootDrOffset, SEEK_SET);
210 /* END SEE if rockridge exists */
211
212 readSeekSet(volInfo, 162, SEEK_CUR);
213
214 rc = readRead(volInfo, volInfo->publisher, 128);
215 if(rc != 128)
216 return BKERROR_READ_GENERIC;
217 volInfo->publisher[128] = '\0';
218 stripSpacesFromEndOfString(volInfo->publisher);
219
220 rc = readRead(volInfo, volInfo->dataPreparer, 128);
221 if(rc != 128)
222 return BKERROR_READ_GENERIC;
223 volInfo->dataPreparer[128] = '\0';
224 stripSpacesFromEndOfString(volInfo->dataPreparer);
225
226 readSeekSet(volInfo, 239, SEEK_CUR);
227
228 rc = readRead(volInfo, timeString, 17);
229 if(rc != 17)
230 return BKERROR_READ_GENERIC;
231
232 longStringToEpoch(timeString, &(volInfo->creationTime));
233
234 /* skip the rest of the extent */
235 readSeekSet(volInfo, 1218, SEEK_CUR);
236 /* END READ PVD */
237
238 /* SKIP all extra copies of pvd */
239 haveMorePvd = true;
240 while(haveMorePvd)
241 {
242 rc = read711(volInfo, &vdType);
243 if(rc != 1)
244 return BKERROR_READ_GENERIC;
245
246 if(vdType == VDTYPE_PRIMARY)
247 {
248 readSeekSet(volInfo, 2047, SEEK_CUR);
249 }
250 else
251 {
252 readSeekSet(volInfo, -1, SEEK_CUR);
253 haveMorePvd = false;
254 }
255 }
256 /* END SKIP all extra copies of pvd */
257
258 /* TRY read boot record */
259
260 locationOfNextDescriptor = readSeekTell(volInfo) + 2048;
261
262 rc = read711(volInfo, &vdType);
263 if(rc != 1)
264 return BKERROR_READ_GENERIC;
265
266 if(vdType == VDTYPE_BOOT)
267 {
268
269 readSeekSet(volInfo, 6, SEEK_CUR);
270
271 rc = readRead(volInfo, elToritoSig, 24);
272 if(rc != 24)
273 return BKERROR_READ_GENERIC;
274 elToritoSig[23] = '\0'; /* just in case */
275
276 if(strcmp(elToritoSig, "EL TORITO SPECIFICATION") == 0)
277 /* el torito confirmed */
278 {
279 readSeekSet(volInfo, 40, SEEK_CUR);
280
281 rc = read731(volInfo, &bootCatalogLocation);
282 if(rc != 4)
283 return BKERROR_READ_GENERIC;
284
285 readSeekSet(volInfo, bootCatalogLocation * NBYTES_LOGICAL_BLOCK, SEEK_SET);
286
287 /* skip validation entry */
288 readSeekSet(volInfo, 32, SEEK_CUR);
289
290 /* skip boot indicator */
291 readSeekSet(volInfo, 1, SEEK_CUR);
292
293 rc = readRead(volInfo, &bootMediaType, 1);
294 if(rc != 1)
295 return BKERROR_READ_GENERIC;
296 if(bootMediaType == 0)
297 volInfo->bootMediaType = BOOT_MEDIA_NO_EMULATION;
298 else if(bootMediaType == 1)
299 volInfo->bootMediaType = BOOT_MEDIA_1_2_FLOPPY;
300 else if(bootMediaType == 2)
301 volInfo->bootMediaType = BOOT_MEDIA_1_44_FLOPPY;
302 else if(bootMediaType == 3)
303 volInfo->bootMediaType = BOOT_MEDIA_2_88_FLOPPY;
304 else if(bootMediaType == 4)
305 {
306 /* !! print warning */
307 printf("hard disk boot emulation not supported\n");
308 volInfo->bootMediaType = BOOT_MEDIA_NONE;
309 }
310 else
311 {
312 /* !! print warning */
313 printf("unknown boot media type on iso\n");
314 volInfo->bootMediaType = BOOT_MEDIA_NONE;
315 }
316
317 /* skip load segment, system type and unused byte */
318 readSeekSet(volInfo, 4, SEEK_CUR);
319
320 rc = read721(volInfo, &bootRecordSize);
321 if(rc != 2)
322 return BKERROR_READ_GENERIC;
323 volInfo->bootRecordSize = bootRecordSize;
324
325 if(volInfo->bootMediaType == BOOT_MEDIA_NO_EMULATION)
326 volInfo->bootRecordSize *= NBYTES_VIRTUAL_SECTOR;
327 else if(volInfo->bootMediaType == BOOT_MEDIA_1_2_FLOPPY)
328 volInfo->bootRecordSize = 1200 * 1024;
329 else if(volInfo->bootMediaType == BOOT_MEDIA_1_44_FLOPPY)
330 volInfo->bootRecordSize = 1440 * 1024;
331 else if(volInfo->bootMediaType == BOOT_MEDIA_2_88_FLOPPY)
332 volInfo->bootRecordSize = 2880 * 1024;
333
334 volInfo->bootRecordIsOnImage = true;
335
336 rc = read731(volInfo, &bootRecordSectorNumber);
337 if(rc != 4)
338 return BKERROR_READ_GENERIC;
339 volInfo->bootRecordOffset = bootRecordSectorNumber *
340 NBYTES_LOGICAL_BLOCK;
341 }
342 else
343 /* !! print warning */
344 printf("err, boot record not el torito\n");
345
346 /* go to the sector after the boot record */
347 readSeekSet(volInfo, locationOfNextDescriptor, SEEK_SET);
348 }
349 else
350 /* not boot record */
351 {
352 /* go back */
353 readSeekSet(volInfo, -1, SEEK_CUR);
354 }
355 /* END TRY read boot record */
356
357 /* TRY read svd */
358 rc = read711(volInfo, &vdType);
359 if(rc != 1)
360 return BKERROR_READ_GENERIC;
361
362 if(vdType == VDTYPE_SUPPLEMENTARY)
363 /* make sure it's joliet (by escape sequence) */
364 {
365 readSeekSet(volInfo, 87, SEEK_CUR);
366
367 rc = readRead(volInfo, escapeSequence, 3);
368 if(rc != 3)
369 return BKERROR_READ_GENERIC;
370
371 if( (escapeSequence[0] == 0x25 && escapeSequence[1] == 0x2F &&
372 escapeSequence[2] == 0x40) ||
373 (escapeSequence[0] == 0x25 && escapeSequence[1] == 0x2F &&
374 escapeSequence[2] == 0x43) ||
375 (escapeSequence[0] == 0x25 && escapeSequence[1] == 0x2F &&
376 escapeSequence[2] == 0x45) )
377 /* is indeed joliet */
378 {
379 readSeekSet(volInfo, 65, SEEK_CUR);
380
381 volInfo->sRootDrOffset = readSeekTell(volInfo);
382
383 volInfo->filenameTypes |= FNTYPE_JOLIET;
384 }
385 }
386 /* END TRY read svd */
387
388 return 1;
389 }
390
391 /*******************************************************************************
392 * dirDrFollows()
393 * checks whether the next directory record is for a directory (not a file)
394 * returns 2 if it does, and 1 if it does not
395 * */
dirDrFollows(VolInfo * volInfo)396 int dirDrFollows(VolInfo* volInfo)
397 {
398 unsigned char fileFlags;
399 bk_off_t origPos;
400 int rc;
401
402 origPos = readSeekTell(volInfo);
403
404 readSeekSet(volInfo, 25, SEEK_CUR);
405
406 rc = read711(volInfo, &fileFlags);
407 if(rc != 1)
408 return BKERROR_READ_GENERIC;
409
410 readSeekSet(volInfo, origPos, SEEK_SET);
411
412 if((fileFlags >> 1 & 1) == 1)
413 return 2;
414 else
415 return 1;
416 }
417
418 /*******************************************************************************
419 * haveNextRecordInSector()
420 * If a directory record won't fit into what's left in a logical block, the rest
421 * of the block is filled with 0s. This function checks whether that's the case.
422 * If the next byte is zero returns false otherwise true
423 * File position remains unchanged
424 * Also returns false on read error */
haveNextRecordInSector(VolInfo * volInfo)425 bool haveNextRecordInSector(VolInfo* volInfo)
426 {
427 bk_off_t origPos;
428 char testByte;
429 int rc;
430
431 origPos = readSeekTell(volInfo);
432
433 rc = readRead(volInfo, &testByte, 1);
434 if(rc != 1)
435 return false;
436
437 readSeekSet(volInfo, origPos, SEEK_SET);
438
439 return (testByte == 0) ? false : true;
440 }
441
442 /*******************************************************************************
443 * readDir()
444 * Reads a directory record for a directory (not a file)
445 * Do not use this to read self or parent records unless it's the following:
446 * - if the root dr (inside vd) is read, it's filename will be ""
447 * filenameType can be only one (do not | more then one)
448 *
449 * note to self: directory identifiers do not end with ";1"
450 *
451 * */
readDir(VolInfo * volInfo,BkDir * dir,int filenameType,bool keepPosixPermissions)452 int readDir(VolInfo* volInfo, BkDir* dir, int filenameType,
453 bool keepPosixPermissions)
454 {
455 int rc;
456 unsigned char recordLength;
457 unsigned locExtent; /* to know where to go before readDir9660() */
458 unsigned lenExtent; /* parameter to readDirContents() */
459 unsigned char lenFileId9660; /* also len joliet fileid (bytes) */
460 int lenSU; /* calculated as recordLength - 33 - lenFileId9660 */
461 bk_off_t origPos;
462
463 /* should anything fail, will still be safe to delete dir, this also
464 * needs to be done before calling readDirContents() (now is good) */
465 dir->children = NULL;
466
467 if(volInfo->stopOperation)
468 return BKERROR_OPER_CANCELED_BY_USER;
469
470 maybeUpdateProgress(volInfo);
471
472 rc = readRead(volInfo, &recordLength, 1);
473 if(rc != 1)
474 return BKERROR_READ_GENERIC;
475
476 readSeekSet(volInfo, 1, SEEK_CUR);
477
478 rc = read733(volInfo, &locExtent);
479 if(rc != 8)
480 return BKERROR_READ_GENERIC;
481
482 rc = read733(volInfo, &lenExtent);
483 if(rc != 8)
484 return BKERROR_READ_GENERIC;
485
486 readSeekSet(volInfo, 14, SEEK_CUR);
487
488 rc = readRead(volInfo, &lenFileId9660, 1);
489 if(rc != 1)
490 return BKERROR_READ_GENERIC;
491
492 lenSU = recordLength - 33 - lenFileId9660;
493 if(lenFileId9660 % 2 == 0)
494 lenSU -= 1;
495
496 /* READ directory name */
497 if(volInfo->rootRead)
498 {
499 bk_off_t posBeforeName = readSeekTell(volInfo);
500
501 rc = readRead(volInfo, BK_BASE_PTR(dir)->name, lenFileId9660);
502 if(rc != lenFileId9660)
503 return BKERROR_READ_GENERIC;
504 BK_BASE_PTR(dir)->name[lenFileId9660] = '\0';
505
506 /* record 9660 name for writing later */
507 strncpy(BK_BASE_PTR(dir)->original9660name, BK_BASE_PTR(dir)->name, 14);
508 BK_BASE_PTR(dir)->original9660name[14] = '\0';
509
510 /* skip padding field if it's there */
511 if(lenFileId9660 % 2 == 0)
512 readSeekSet(volInfo, 1, SEEK_CUR);
513
514 if(filenameType != FNTYPE_9660)
515 readSeekSet(volInfo, posBeforeName, SEEK_SET);
516 }
517
518 if(filenameType == FNTYPE_JOLIET)
519 {
520 if(volInfo->rootRead)
521 {
522 char nameAsOnDisk[UCHAR_MAX];
523 /* in the worst possible case i'll use 129 bytes for this: */
524 char nameInAscii[UCHAR_MAX];
525 int ucsCount, byteCount;
526
527 /* ucs2 byte count must be even */
528 if(lenFileId9660 % 2 != 0)
529 return BKERROR_INVALID_UCS2;
530
531 rc = readRead(volInfo, nameAsOnDisk, lenFileId9660);
532 if(rc != lenFileId9660)
533 return BKERROR_READ_GENERIC;
534
535 for(ucsCount = 1, byteCount = 0; ucsCount < lenFileId9660;
536 ucsCount += 2, byteCount += 1)
537 {
538 nameInAscii[byteCount] = nameAsOnDisk[ucsCount];
539 }
540 nameInAscii[byteCount] = '\0';
541
542 strncpy(BK_BASE_PTR(dir)->name, nameInAscii, lenFileId9660);
543 BK_BASE_PTR(dir)->name[lenFileId9660] = '\0';
544
545 /* padding field */
546 if(lenFileId9660 % 2 == 0)
547 readSeekSet(volInfo, 1, SEEK_CUR);
548 }
549 }
550 else if(filenameType == FNTYPE_ROCKRIDGE)
551 {
552 if(volInfo->rootRead)
553 {
554 /* skip 9660 filename */
555 readSeekSet(volInfo, lenFileId9660, SEEK_CUR);
556 /* skip padding field */
557 if(lenFileId9660 % 2 == 0)
558 readSeekSet(volInfo, 1, SEEK_CUR);
559
560 rc = readRockridgeFilename(volInfo, BK_BASE_PTR(dir)->name, lenSU, 0);
561 if(rc < 0)
562 return rc;
563 }
564 }
565 else if(filenameType != FNTYPE_9660)
566 return BKERROR_UNKNOWN_FILENAME_TYPE;
567 /* END READ directory name */
568
569 if(keepPosixPermissions)
570 {
571 if( !(volInfo->rootRead) )
572 {
573 unsigned char realRootRecordLen;
574
575 origPos = readSeekTell(volInfo);
576
577 /* go to real root record */
578 readSeekSet(volInfo, locExtent * NBYTES_LOGICAL_BLOCK, SEEK_SET);
579
580 /* read record length */
581 rc = readRead(volInfo, &realRootRecordLen, 1);
582 if(rc != 1)
583 return BKERROR_READ_GENERIC;
584
585 /* go to sys use fields */
586 readSeekSet(volInfo, 33, SEEK_CUR);
587
588 rc = readPosixFileMode(volInfo, &(BK_BASE_PTR(dir)->posixFileMode), realRootRecordLen - 34);
589 if(rc <= 0)
590 return rc;
591
592 /* return */
593 readSeekSet(volInfo, origPos, SEEK_SET);
594 }
595 else
596 {
597 rc = readPosixFileMode(volInfo, &(BK_BASE_PTR(dir)->posixFileMode), lenSU);
598 if(rc <= 0)
599 return rc;
600 }
601 }
602 else
603 {
604 /* this is good for root also */
605 BK_BASE_PTR(dir)->posixFileMode = volInfo->posixDirDefaults;
606 }
607
608 readSeekSet(volInfo, lenSU, SEEK_CUR);
609
610 origPos = readSeekTell(volInfo);
611
612 readSeekSet(volInfo, locExtent * NBYTES_LOGICAL_BLOCK, SEEK_SET);
613
614 volInfo->rootRead = true;
615
616 rc = readDirContents(volInfo, dir, lenExtent, filenameType, keepPosixPermissions);
617 if(rc < 0)
618 return rc;
619
620 readSeekSet(volInfo, origPos, SEEK_SET);
621
622 return recordLength;
623 }
624
625 /*******************************************************************************
626 * readDirContents()
627 * Reads the extent pointed to from a directory record of a directory.
628 * size is number of bytes
629 * */
readDirContents(VolInfo * volInfo,BkDir * dir,unsigned size,int filenameType,bool keepPosixPermissions)630 int readDirContents(VolInfo* volInfo, BkDir* dir, unsigned size,
631 int filenameType, bool keepPosixPermissions)
632 {
633 int rc;
634 unsigned bytesRead = 0;
635 unsigned childrenBytesRead;
636 BkFileBase** nextChild; /* pointer to pointer to modify pointer :) */
637
638 /* skip self and parent */
639 rc = skipDR(volInfo);
640 if(rc <= 0)
641 return rc;
642 bytesRead += rc;
643 rc = skipDR(volInfo);
644 if(rc <= 0)
645 return rc;
646 bytesRead += rc;
647
648 nextChild = &(dir->children);
649 childrenBytesRead = 0;
650 while(childrenBytesRead + bytesRead < size)
651 {
652 if(haveNextRecordInSector(volInfo))
653 /* read it */
654 {
655 int recordLength;
656
657 rc = dirDrFollows(volInfo);
658 if(rc == 2)
659 /* directory descriptor record */
660 {
661 *nextChild = malloc(sizeof(BkDir));
662 if(*nextChild == NULL)
663 return BKERROR_OUT_OF_MEMORY;
664
665 memset(*nextChild, 0, sizeof(BkDir));
666
667 recordLength = readDir(volInfo, BK_DIR_PTR(*nextChild),
668 filenameType, keepPosixPermissions);
669 if(recordLength < 0)
670 return recordLength;
671 }
672 else
673 /* file descriptor record */
674 {
675 BkFileBase* specialFile;
676
677 /* assume it's a file for now */
678 *nextChild = malloc(sizeof(BkFile));
679 if(*nextChild == NULL)
680 return BKERROR_OUT_OF_MEMORY;
681
682 memset(*nextChild, 0, sizeof(BkFile));
683
684 recordLength = readFileInfo(volInfo, BK_FILE_PTR(*nextChild),
685 filenameType, keepPosixPermissions,
686 &specialFile);
687 if(recordLength < 0)
688 return recordLength;
689
690 if(specialFile != NULL)
691 /* it's a special file, replace the allocated BkFile */
692 {
693 free(*nextChild);
694 *nextChild = specialFile;
695 }
696 }
697
698 childrenBytesRead += recordLength;
699
700 nextChild = &((*nextChild)->next);
701 *nextChild = NULL;
702 }
703 else
704 /* read zeroes until get to next record (that would be in the next
705 * sector btw) or get to the end of data (dir->self.dataLength) */
706 {
707 char testByte;
708 bk_off_t origPos;
709
710 do
711 {
712 origPos = readSeekTell(volInfo);
713
714 rc = readRead(volInfo, &testByte, 1);
715 if(rc != 1)
716 return BKERROR_READ_GENERIC;
717
718 if(testByte != 0)
719 {
720 readSeekSet(volInfo, origPos, SEEK_SET);
721 break;
722 }
723
724 childrenBytesRead += 1;
725
726 } while (childrenBytesRead + bytesRead < size);
727 }
728 }
729
730 return bytesRead;
731 }
732
733 /*******************************************************************************
734 * readFileInfo()
735 * Reads the directory record for a file
736 * */
readFileInfo(VolInfo * volInfo,BkFile * file,int filenameType,bool keepPosixPermissions,BkFileBase ** specialFile)737 int readFileInfo(VolInfo* volInfo, BkFile* file, int filenameType,
738 bool keepPosixPermissions, BkFileBase** specialFile)
739 {
740 int rc;
741 unsigned char recordLength;
742 unsigned locExtent; /* block num where the file is */
743 unsigned lenExtent; /* in bytes */
744 unsigned char lenFileId9660; /* also len joliet fileid (bytes) */
745 int lenSU; /* calculated as recordLength - 33 - lenFileId9660 */
746 bk_off_t posBeforeName;
747 char nameAsOnDisk[UCHAR_MAX + 1];
748
749 /* so if anything failes it's still safe to delete file */
750 file->pathAndName = NULL;
751
752 if(volInfo->stopOperation)
753 return BKERROR_OPER_CANCELED_BY_USER;
754
755 maybeUpdateProgress(volInfo);
756
757 *specialFile = NULL;
758
759 rc = readRead(volInfo, &recordLength, 1);
760 if(rc != 1)
761 return BKERROR_READ_GENERIC;
762
763 readSeekSet(volInfo, 1, SEEK_CUR);
764
765 rc = read733(volInfo, &locExtent);
766 if(rc != 8)
767 return BKERROR_READ_GENERIC;
768
769 rc = read733(volInfo, &lenExtent);
770 if(rc != 8)
771 return BKERROR_READ_GENERIC;
772
773 /* The length of isolinux.bin given in the initial/default entry of
774 * the el torito boot catalog does not match the actual length of the file
775 * but apparently when executed by the bios that's not a problem.
776 * However, if i ever want to read that file myself, i need
777 * the length proper.
778 * So i'm looking for a file that starts in the same logical sector as the
779 * boot record from the initial/default entry. */
780 if(volInfo->bootMediaType == BOOT_MEDIA_NO_EMULATION &&
781 locExtent == volInfo->bootRecordOffset / NBYTES_LOGICAL_BLOCK)
782 {
783 volInfo->bootRecordSize = lenExtent;
784
785 volInfo->bootRecordIsVisible = true;
786 volInfo->bootRecordOnImage = file;
787 }
788
789 readSeekSet(volInfo, 14, SEEK_CUR);
790
791 rc = readRead(volInfo, &lenFileId9660, 1);
792 if(rc != 1)
793 return BKERROR_READ_GENERIC;
794
795 lenSU = recordLength - 33 - lenFileId9660;
796 if(lenFileId9660 % 2 == 0)
797 lenSU -= 1;
798
799 /* READ 9660 name */
800 posBeforeName = readSeekTell(volInfo);
801
802 rc = readRead(volInfo, nameAsOnDisk, lenFileId9660);
803 if(rc != lenFileId9660)
804 return BKERROR_READ_GENERIC;
805 nameAsOnDisk[lenFileId9660] = '\0';
806
807 /* record 9660 name for writing later */
808 strncpy(BK_BASE_PTR(file)->original9660name, nameAsOnDisk, 14);
809 BK_BASE_PTR(file)->original9660name[14] = '\0';
810
811 removeCrapFromFilename(nameAsOnDisk, lenFileId9660);
812
813 strncpy(BK_BASE_PTR(file)->name, nameAsOnDisk, NCHARS_FILE_ID_MAX_STORE - 1);
814 BK_BASE_PTR(file)->name[NCHARS_FILE_ID_MAX_STORE - 1] = '\0';
815
816 /* padding field */
817 if(lenFileId9660 % 2 == 0)
818 readSeekSet(volInfo, 1, SEEK_CUR);
819
820 if(filenameType != FNTYPE_9660)
821 readSeekSet(volInfo, posBeforeName, SEEK_SET);
822 /* END READ 9660 name */
823
824 if(filenameType == FNTYPE_JOLIET)
825 {
826 char nameAsOnDisk[UCHAR_MAX];
827 /* in the worst possible case i'll use 129 bytes for this: */
828 char nameInAscii[UCHAR_MAX];
829 int ucsCount, byteCount;
830
831 if(lenFileId9660 % 2 != 0)
832 return BKERROR_INVALID_UCS2;
833
834 rc = readRead(volInfo, nameAsOnDisk, lenFileId9660);
835 if(rc != lenFileId9660)
836 return BKERROR_READ_GENERIC;
837
838 for(ucsCount = 1, byteCount = 0; ucsCount < lenFileId9660;
839 ucsCount += 2, byteCount += 1)
840 {
841 nameInAscii[byteCount] = nameAsOnDisk[ucsCount];
842 }
843
844 removeCrapFromFilename(nameInAscii, lenFileId9660 / 2);
845
846 if( strlen(nameInAscii) > NCHARS_FILE_ID_MAX_STORE - 1 )
847 return BKERROR_MAX_NAME_LENGTH_EXCEEDED;
848
849 strncpy(BK_BASE_PTR(file)->name, nameInAscii, NCHARS_FILE_ID_MAX_STORE - 1);
850 BK_BASE_PTR(file)->name[NCHARS_FILE_ID_MAX_STORE - 1] = '\0';
851
852 /* padding field */
853 if(lenFileId9660 % 2 == 0)
854 readSeekSet(volInfo, 1, SEEK_CUR);
855 }
856 else if(filenameType == FNTYPE_ROCKRIDGE)
857 {
858 /* skip 9660 filename */
859 readSeekSet(volInfo, lenFileId9660, SEEK_CUR);
860 /* skip padding field */
861 if(lenFileId9660 % 2 == 0)
862 readSeekSet(volInfo, 1, SEEK_CUR);
863
864 rc = readRockridgeFilename(volInfo, BK_BASE_PTR(file)->name, lenSU, 0);
865 if(rc < 0)
866 return rc;
867 }
868 else if(filenameType != FNTYPE_9660)
869 return BKERROR_UNKNOWN_FILENAME_TYPE;
870
871 if(keepPosixPermissions)
872 {
873 rc = readPosixFileMode(volInfo, &(BK_BASE_PTR(file)->posixFileMode), lenSU);
874 if(rc < 0)
875 return rc;
876 }
877 else
878 {
879 BK_BASE_PTR(file)->posixFileMode = volInfo->posixFileDefaults;
880 }
881 //~ printf("'%s' %X\n", BK_BASE_PTR(file)->name, BK_BASE_PTR(file)->posixFileMode);
882 rc = readRockridgeSymlink(volInfo, (BkSymLink**)specialFile, lenSU);
883 if(rc < 0)
884 return rc;
885
886 if(*specialFile != NULL)
887 /* the file is actually a symbolic link */
888 {
889 strcpy((*specialFile)->name, BK_BASE_PTR(file)->name);
890 strcpy((*specialFile)->original9660name, BK_BASE_PTR(file)->original9660name);
891 /* apparently permissions for symbolic links are never used */
892 (*specialFile)->posixFileMode = 0120777;
893 }
894
895 if(volInfo->scanForDuplicateFiles)
896 {
897 BkHardLink* newLink;
898
899 rc = findInHardLinkTable(volInfo, locExtent * NBYTES_LOGICAL_BLOCK, NULL,
900 lenExtent, true, &newLink);
901 if(rc < 0)
902 return rc;
903
904 if(newLink == NULL)
905 /* not found */
906 {
907 rc = addToHardLinkTable(volInfo, locExtent * NBYTES_LOGICAL_BLOCK,
908 NULL, lenExtent, true, &newLink);
909 if(rc < 0)
910 return rc;
911 }
912
913 file->location = newLink;
914 }
915
916 readSeekSet(volInfo, lenSU, SEEK_CUR);
917
918 file->onImage = true;
919 file->position = ((bk_off_t)locExtent) * ((bk_off_t)NBYTES_LOGICAL_BLOCK);
920 file->size = lenExtent;
921
922 return recordLength;
923 }
924
925 /*******************************************************************************
926 * readPosixFileMode()
927 * looks for the PX system use field and gets the permissions field out of it
928 * */
readPosixFileMode(VolInfo * volInfo,unsigned * posixFileMode,int lenSU)929 int readPosixFileMode(VolInfo* volInfo, unsigned* posixFileMode, int lenSU)
930 {
931 bk_off_t origPos;
932 unsigned char* suFields;
933 int rc;
934 bool foundPosix;
935 bool foundCE;
936 int count;
937 unsigned logicalBlockOfCE;
938 unsigned offsetInLogicalBlockOfCE;
939 unsigned lengthOfCE; /* in bytes */
940
941 suFields = malloc(lenSU);
942 if(suFields == NULL)
943 return BKERROR_OUT_OF_MEMORY;
944
945 origPos = readSeekTell(volInfo);
946
947 rc = readRead(volInfo, suFields, lenSU);
948
949 if(rc != lenSU)
950 return BKERROR_READ_GENERIC;
951 count = 0;
952 foundPosix = false;
953 foundCE = false;
954 while(count < lenSU && !foundPosix)
955 {
956 if(suFields[count] == 0)
957 /* not an SU field, mkisofs sometimes has a trailing 0 in the directory
958 * record and this is the easiest way to ignore it */
959 break;
960
961 if(suFields[count] == 'P' && suFields[count + 1] == 'X')
962 {
963 //~ printf("%X %X %X %X\n", *(suFields + count + 4), *(suFields + count + 5), *(suFields + count + 6), *(suFields + count + 7));
964 read733FromCharArray(suFields + count + 4, posixFileMode);
965
966 /* not interested in anything else from this field */
967
968 foundPosix = true;
969 }
970 else if(suFields[count] == 'C' && suFields[count + 1] == 'E')
971 {
972 foundCE = true;
973 read733FromCharArray(suFields + count + 4, &logicalBlockOfCE);
974 read733FromCharArray(suFields + count + 12, &offsetInLogicalBlockOfCE);
975 read733FromCharArray(suFields + count + 20, &lengthOfCE);
976 }
977
978 /* skip su record */
979 count += suFields[count + 2];
980 }
981
982 free(suFields);
983 readSeekSet(volInfo, origPos, SEEK_SET);
984
985 if(!foundPosix)
986 {
987 if(!foundCE)
988 return BKERROR_NO_POSIX_PRESENT;
989 else
990 {
991 readSeekSet(volInfo, logicalBlockOfCE * NBYTES_LOGICAL_BLOCK +
992 offsetInLogicalBlockOfCE, SEEK_SET);
993 rc = readPosixFileMode(volInfo, posixFileMode, lengthOfCE);
994
995 readSeekSet(volInfo, origPos, SEEK_SET);
996
997 return rc;
998 }
999 }
1000
1001 return 1;
1002 }
1003
1004 /*******************************************************************************
1005 * readRockridgeFilename()
1006 * Finds the NM entry in the system use fields and reads a maximum of
1007 * NCHARS_FILE_ID_MAX_STORE characters from it (truncates if necessary).
1008 * The continue system use field is not implemented so if the name is not in
1009 * this directory record, the function returns a failure.
1010 * Leaves the file pointer where it was.
1011 */
readRockridgeFilename(VolInfo * volInfo,char * dest,int lenSU,unsigned numCharsReadAlready)1012 int readRockridgeFilename(VolInfo* volInfo, char* dest, int lenSU,
1013 unsigned numCharsReadAlready)
1014 {
1015 bk_off_t origPos;
1016 unsigned char* suFields;
1017 int rc;
1018 int count;
1019 int lengthThisNM;
1020 int usableLenThisNM;
1021 bool foundName;
1022 bool nameContinues; /* in another NM entry */
1023 bool foundCE;
1024 unsigned logicalBlockOfCE;
1025 unsigned offsetInLogicalBlockOfCE;
1026 unsigned lengthOfCE; /* in bytes */
1027
1028 suFields = malloc(lenSU);
1029 if(suFields == NULL)
1030 return BKERROR_OUT_OF_MEMORY;
1031
1032 origPos = readSeekTell(volInfo);
1033
1034 rc = readRead(volInfo, suFields, lenSU);
1035 if(rc != lenSU)
1036 {
1037 free(suFields);
1038 return BKERROR_READ_GENERIC;
1039 }
1040
1041 count = 0;
1042 foundName = false;
1043 nameContinues = false;
1044 foundCE = false;
1045 while(count < lenSU)
1046 {
1047 if(suFields[count] == 0)
1048 /* not an SU field, mkisofs sometimes has a trailing 0 in the directory
1049 * record and this is the easiest way to ignore it */
1050 break;
1051
1052 if(suFields[count] == 'N' && suFields[count + 1] == 'M')
1053 {
1054 lengthThisNM = suFields[count + 2] - 5;
1055
1056 /* the data structures cannot handle filenames longer than
1057 * NCHARS_FILE_ID_MAX_STORE so in case the image contains an
1058 * invalid, long filename, truncate it rather than corrupt memory */
1059 if(lengthThisNM + numCharsReadAlready > NCHARS_FILE_ID_MAX_STORE - 1)
1060 usableLenThisNM = NCHARS_FILE_ID_MAX_STORE - numCharsReadAlready - 1;
1061 else
1062 usableLenThisNM = lengthThisNM;
1063
1064 strncpy(dest + numCharsReadAlready, (char*)suFields + count + 5, usableLenThisNM);
1065 dest[usableLenThisNM + numCharsReadAlready] = '\0';
1066
1067 numCharsReadAlready += usableLenThisNM;
1068
1069 foundName = true;
1070 nameContinues = suFields[count + 4] & 0x01; /* NM 'continue' flag */
1071 }
1072 else if(suFields[count] == 'C' && suFields[count + 1] == 'E')
1073 {
1074 foundCE = true;
1075 read733FromCharArray(suFields + count + 4, &logicalBlockOfCE);
1076 read733FromCharArray(suFields + count + 12, &offsetInLogicalBlockOfCE);
1077 read733FromCharArray(suFields + count + 20, &lengthOfCE);
1078 }
1079
1080 /* skip su record */
1081 count += suFields[count + 2];
1082 }
1083
1084 free(suFields);
1085 readSeekSet(volInfo, origPos, SEEK_SET);
1086
1087 if( !foundName || (foundName && nameContinues) )
1088 {
1089 if(!foundCE)
1090 return BKERROR_RR_FILENAME_MISSING;
1091 else
1092 {
1093 readSeekSet(volInfo,
1094 logicalBlockOfCE * NBYTES_LOGICAL_BLOCK + offsetInLogicalBlockOfCE,
1095 SEEK_SET);
1096 rc = readRockridgeFilename(volInfo, dest, lengthOfCE, numCharsReadAlready);
1097
1098 readSeekSet(volInfo, origPos, SEEK_SET);
1099
1100 return rc;
1101 }
1102 }
1103 else
1104 return 1;
1105 }
1106
1107 /* if no SL record is found does not return failure */
readRockridgeSymlink(VolInfo * volInfo,BkSymLink ** dest,int lenSU)1108 int readRockridgeSymlink(VolInfo* volInfo, BkSymLink** dest, int lenSU)
1109 {
1110 bk_off_t origPos;
1111 unsigned char* suFields;
1112 int rc;
1113 int count;
1114 int count2;
1115
1116 if (lenSU <= 0)
1117 return 1;
1118
1119 suFields = malloc(lenSU);
1120 if(suFields == NULL)
1121 return BKERROR_OUT_OF_MEMORY;
1122
1123 origPos = readSeekTell(volInfo);
1124
1125 rc = readRead(volInfo, suFields, lenSU);
1126 if(rc != lenSU)
1127 {
1128 free(suFields);
1129 return BKERROR_READ_GENERIC;
1130 }
1131
1132 count = 0;
1133 while(count < lenSU)
1134 {
1135 if(suFields[count] == 0)
1136 /* not an SU field, mkisofs sometimes has a trailing 0 in the directory
1137 * record and this is the easiest way to ignore it */
1138 break;
1139
1140 if(suFields[count] == 'S' && suFields[count + 1] == 'L')
1141 {
1142 size_t numCharsUsed; /* in dest->target, not including '\0' */
1143
1144 *dest = malloc(sizeof(BkSymLink));
1145 if(*dest == NULL)
1146 return BKERROR_OUT_OF_MEMORY;
1147
1148 memset(*dest, 0, sizeof(BkSymLink));
1149
1150 numCharsUsed = 0;
1151 (*dest)->target[0] = '\0';
1152 /* read sym link component records and assemble (*dest)->target
1153 * right now component records cannot spawn multiple SL entries */
1154 count2 = count + 5;
1155 while(count2 < count + suFields[count + 2])
1156 {
1157 if(suFields[count2] & 0x02)
1158 {
1159 numCharsUsed += appendStringIfHaveRoom((*dest)->target,
1160 ".", NCHARS_SYMLINK_TARGET_MAX - 1,
1161 numCharsUsed, -1);
1162 }
1163 else if(suFields[count2] & 0x04)
1164 {
1165 numCharsUsed += appendStringIfHaveRoom((*dest)->target,
1166 "..", NCHARS_SYMLINK_TARGET_MAX - 1,
1167 numCharsUsed, -1);
1168 }
1169 else if(suFields[count2] & 0x08)
1170 {
1171 strcpy((*dest)->target, "/");
1172 numCharsUsed = 1;
1173 }
1174
1175 /* if bits 1-5 are set there is no component content */
1176 if( !(suFields[count2] & 0x3E) )
1177 {
1178 numCharsUsed += appendStringIfHaveRoom((*dest)->target,
1179 (char*)(suFields + count2 + 2),
1180 NCHARS_SYMLINK_TARGET_MAX - 1,
1181 numCharsUsed, suFields[count2 + 1]);
1182 }
1183
1184 /* next component record */
1185 count2 += suFields[count2 + 1] + 2;
1186
1187 if(count2 < count + suFields[count + 2])
1188 /* another component record follows, insert separator */
1189 {
1190 numCharsUsed += appendStringIfHaveRoom((*dest)->target,
1191 "/", NCHARS_SYMLINK_TARGET_MAX - 1,
1192 numCharsUsed, -1);
1193 }
1194 }
1195
1196 /* ignore any other SU fields */
1197 break;
1198 }
1199
1200 /* skip su field */
1201 count += suFields[count + 2];
1202 }
1203
1204 free(suFields);
1205 readSeekSet(volInfo, origPos, SEEK_SET);
1206
1207 return 1;
1208 }
1209
1210 /*******************************************************************************
1211 * removeCrapFromFilename()
1212 * filenames as read from 9660 Sometimes end with ;1 (terminator+version num)
1213 * this removes the useless ending and terminates the destination with a '\0'
1214 * */
removeCrapFromFilename(char * filename,int length)1215 void removeCrapFromFilename(char* filename, int length)
1216 {
1217 int count;
1218 bool stop = false;
1219
1220 for(count = 0; count < length && !stop; count++)
1221 {
1222 if(filename[count] == ';')
1223 {
1224 filename[count] = '\0';
1225 stop = true;
1226 }
1227 }
1228
1229 /* if did not get a ';' terminate string anyway */
1230 filename[count] = '\0';
1231 }
1232
1233 /*******************************************************************************
1234 * skipDR()
1235 * Seek past a directory entry. Good for skipping "." and ".."
1236 * */
skipDR(VolInfo * volInfo)1237 int skipDR(VolInfo* volInfo)
1238 {
1239 unsigned char dRLen;
1240 int rc;
1241
1242 rc = read711(volInfo, &dRLen);
1243 if(rc <= 0)
1244 return BKERROR_READ_GENERIC;
1245
1246 readSeekSet(volInfo, dRLen - 1, SEEK_CUR);
1247
1248 return dRLen;
1249 }
1250
1251 /*******************************************************************************
1252 * stripSpacesFromEndOfString
1253 * Some strings in the ISO volume are padded with spaces (hopefully on the right)
1254 * this function removes them.
1255 * */
stripSpacesFromEndOfString(char * str)1256 void stripSpacesFromEndOfString(char* str)
1257 {
1258 size_t count;
1259
1260 for(count = strlen(str) - 1; str[count] == ' '; count--)
1261 {
1262 str[count] = '\0';
1263
1264 if(count == 0) /* unsigned */
1265 break;
1266 }
1267 }
1268