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