1 /*
2 * Copyright 1999, Be Incorporated. All Rights Reserved.
3 * This file may be used under the terms of the Be Sample Code License.
4 *
5 * Copyright 2001, pinc Software. All Rights Reserved.
6 */
7
8
9 #include "iso9660.h"
10
11 #include <ctype.h>
12
13 #ifndef FS_SHELL
14 # include <dirent.h>
15 # include <fcntl.h>
16 # include <stdlib.h>
17 # include <string.h>
18 # include <sys/stat.h>
19 # include <time.h>
20 # include <unistd.h>
21
22 # include <ByteOrder.h>
23 # include <Drivers.h>
24 # include <KernelExport.h>
25 # include <fs_cache.h>
26 #endif
27
28 #include "rock_ridge.h"
29
30 //#define TRACE_ISO9660 1
31 #if TRACE_ISO9660
32 # define TRACE(x) dprintf x
33 #else
34 # define TRACE(x) ;
35 #endif
36
37
38 // Just needed here
39 static status_t unicode_to_utf8(const char *src, int32 *srcLen, char *dst,
40 int32 *dstLen);
41
42
43 // ISO9660 should start with this string
44 const char *kISO9660IDString = "CD001";
45
46
47 static int
get_device_block_size(int fd)48 get_device_block_size(int fd)
49 {
50 device_geometry geometry;
51
52 if (ioctl(fd, B_GET_GEOMETRY, &geometry, sizeof(device_geometry)) < 0) {
53 struct stat st;
54 if (fstat(fd, &st) < 0 || S_ISDIR(st.st_mode))
55 return 0;
56
57 return 512; /* just assume it's a plain old file or something */
58 }
59
60 return geometry.bytes_per_sector;
61 }
62
63
64 // From EncodingComversions.cpp
65
66 // Pierre's (modified) Uber Macro
67
68 // NOTE: iso9660 seems to store the unicode text in big-endian form
69 #define u_to_utf8(str, uni_str)\
70 {\
71 if ((B_BENDIAN_TO_HOST_INT16(uni_str[0])&0xff80) == 0)\
72 *str++ = B_BENDIAN_TO_HOST_INT16(*uni_str++);\
73 else if ((B_BENDIAN_TO_HOST_INT16(uni_str[0])&0xf800) == 0) {\
74 str[0] = 0xc0|(B_BENDIAN_TO_HOST_INT16(uni_str[0])>>6);\
75 str[1] = 0x80|(B_BENDIAN_TO_HOST_INT16(*uni_str++)&0x3f);\
76 str += 2;\
77 } else if ((B_BENDIAN_TO_HOST_INT16(uni_str[0])&0xfc00) != 0xd800) {\
78 str[0] = 0xe0|(B_BENDIAN_TO_HOST_INT16(uni_str[0])>>12);\
79 str[1] = 0x80|((B_BENDIAN_TO_HOST_INT16(uni_str[0])>>6)&0x3f);\
80 str[2] = 0x80|(B_BENDIAN_TO_HOST_INT16(*uni_str++)&0x3f);\
81 str += 3;\
82 } else {\
83 int val;\
84 val = ((B_BENDIAN_TO_HOST_INT16(uni_str[0])-0xd7c0)<<10) | (B_BENDIAN_TO_HOST_INT16(uni_str[1])&0x3ff);\
85 str[0] = 0xf0 | (val>>18);\
86 str[1] = 0x80 | ((val>>12)&0x3f);\
87 str[2] = 0x80 | ((val>>6)&0x3f);\
88 str[3] = 0x80 | (val&0x3f);\
89 uni_str += 2; str += 4;\
90 }\
91 }
92
93
94 static status_t
unicode_to_utf8(const char * src,int32 * srcLen,char * dst,int32 * dstLen)95 unicode_to_utf8(const char *src, int32 *srcLen, char *dst, int32 *dstLen)
96 {
97 int32 srcLimit = *srcLen;
98 int32 dstLimit = *dstLen;
99 int32 srcCount = 0;
100 int32 dstCount = 0;
101
102 for (srcCount = 0; srcCount < srcLimit; srcCount += 2) {
103 uint16 *UNICODE = (uint16 *)&src[srcCount];
104 uint8 utf8[4];
105 uint8 *UTF8 = utf8;
106 int32 utf8Len;
107 int32 j;
108
109 u_to_utf8(UTF8, UNICODE);
110
111 utf8Len = UTF8 - utf8;
112 if (dstCount + utf8Len > dstLimit)
113 break;
114
115 for (j = 0; j < utf8Len; j++)
116 dst[dstCount + j] = utf8[j];
117 dstCount += utf8Len;
118 }
119
120 *srcLen = srcCount;
121 *dstLen = dstCount;
122
123 return dstCount > 0 ? B_NO_ERROR : B_ERROR;
124 }
125
126
127 static void
sanitize_iso_name(iso9660_inode * node,bool removeTrailingPoints)128 sanitize_iso_name(iso9660_inode* node, bool removeTrailingPoints)
129 {
130 // Get rid of semicolons, which are used to delineate file versions.
131 if (char* semi = strchr(node->name, ';')) {
132 semi[0] = '\0';
133 node->name_length = semi - node->name;
134 }
135
136 if (removeTrailingPoints) {
137 // Get rid of trailing points
138 if (node->name_length > 2 && node->name[node->name_length - 1] == '.')
139 node->name[--node->name_length] = '\0';
140 }
141 }
142
143
144 static int
init_volume_date(ISOVolDate * date,char * buffer)145 init_volume_date(ISOVolDate *date, char *buffer)
146 {
147 memcpy(date, buffer, ISO_VOL_DATE_SIZE);
148 return 0;
149 }
150
151
152 static int
init_node_date(ISORecDate * date,char * buffer)153 init_node_date(ISORecDate *date, char *buffer)
154 {
155 memcpy(date, buffer, sizeof(struct ISORecDate));
156 return 0;
157 }
158
159
160 static status_t
InitVolDesc(iso9660_volume * volume,char * buffer)161 InitVolDesc(iso9660_volume *volume, char *buffer)
162 {
163 TRACE(("InitVolDesc - ENTER\n"));
164
165 volume->volDescType = *(uint8 *)buffer;
166 buffer += sizeof(volume->volDescType);
167
168 const size_t kStdIDStringLen = sizeof(volume->stdIDString) - 1;
169 volume->stdIDString[kStdIDStringLen] = '\0';
170 strncpy(volume->stdIDString, buffer, kStdIDStringLen);
171 buffer += kStdIDStringLen;
172
173 volume->volDescVersion = *(uint8 *)buffer;
174 buffer += sizeof(volume->volDescVersion);
175
176 buffer += sizeof(volume->unused1); // skip unused 8th byte
177
178 const size_t kSystemIDStringLen = sizeof(volume->systemIDString) - 1;
179 volume->systemIDString[kSystemIDStringLen] = '\0';
180 strncpy(volume->systemIDString, buffer, kSystemIDStringLen);
181 buffer += kSystemIDStringLen;
182 TRACE(("InitVolDesc - system id string is %s\n", volume->systemIDString));
183
184 const size_t kVolIDStringLen = sizeof(volume->volIDString) - 1;
185 volume->volIDString[kVolIDStringLen] = '\0';
186 strncpy(volume->volIDString, buffer, kVolIDStringLen);
187 buffer += kVolIDStringLen;
188 TRACE(("InitVolDesc - volume id string is %s\n", volume->volIDString));
189
190 buffer += sizeof(volume->unused2) - 1; // skip unused 73-80 bytes
191
192 volume->volSpaceSize[LSB_DATA] = *(uint32 *)buffer;
193 buffer += sizeof(volume->volSpaceSize[LSB_DATA]);
194 volume->volSpaceSize[MSB_DATA] = *(uint32 *)buffer;
195 buffer += sizeof(volume->volSpaceSize[MSB_DATA]);
196
197 buffer += sizeof(volume->unused3) - 1; // skip unused 89-120 bytes
198
199 volume->volSetSize[LSB_DATA] = *(uint16*)buffer;
200 buffer += sizeof(volume->volSetSize[LSB_DATA]);
201 volume->volSetSize[MSB_DATA] = *(uint16*)buffer;
202 buffer += sizeof(volume->volSetSize[MSB_DATA]);
203
204 volume->volSeqNum[LSB_DATA] = *(uint16*)buffer;
205 buffer += sizeof(volume->volSeqNum[LSB_DATA]);
206 volume->volSeqNum[MSB_DATA] = *(uint16*)buffer;
207 buffer += sizeof(volume->volSeqNum[MSB_DATA]);
208
209 volume->logicalBlkSize[LSB_DATA] = *(uint16*)buffer;
210 buffer += sizeof(volume->logicalBlkSize[LSB_DATA]);
211 volume->logicalBlkSize[MSB_DATA] = *(uint16*)buffer;
212 buffer += sizeof(volume->logicalBlkSize[MSB_DATA]);
213
214 volume->pathTblSize[LSB_DATA] = *(uint32*)buffer;
215 buffer += sizeof(volume->pathTblSize[LSB_DATA]);
216 volume->pathTblSize[MSB_DATA] = *(uint32*)buffer;
217 buffer += sizeof(volume->pathTblSize[MSB_DATA]);
218
219 volume->lPathTblLoc[LSB_DATA] = *(uint16*)buffer;
220 buffer += sizeof(volume->lPathTblLoc[LSB_DATA]);
221 volume->lPathTblLoc[MSB_DATA] = *(uint16*)buffer;
222 buffer += sizeof(volume->lPathTblLoc[MSB_DATA]);
223
224 volume->optLPathTblLoc[LSB_DATA] = *(uint16*)buffer;
225 buffer += sizeof(volume->optLPathTblLoc[LSB_DATA]);
226 volume->optLPathTblLoc[MSB_DATA] = *(uint16*)buffer;
227 buffer += sizeof(volume->optLPathTblLoc[MSB_DATA]);
228
229 volume->mPathTblLoc[LSB_DATA] = *(uint16*)buffer;
230 buffer += sizeof(volume->mPathTblLoc[LSB_DATA]);
231 volume->mPathTblLoc[MSB_DATA] = *(uint16*)buffer;
232 buffer += sizeof(volume->mPathTblLoc[MSB_DATA]);
233
234 volume->optMPathTblLoc[LSB_DATA] = *(uint16*)buffer;
235 buffer += sizeof(volume->optMPathTblLoc[LSB_DATA]);
236 volume->optMPathTblLoc[MSB_DATA] = *(uint16*)buffer;
237 buffer += sizeof(volume->optMPathTblLoc[MSB_DATA]);
238
239 // Fill in directory record.
240 volume->joliet_level = 0;
241 InitNode(volume, &volume->rootDirRec, buffer, NULL);
242
243 volume->rootDirRec.id = ISO_ROOTNODE_ID;
244 buffer += ISO_ROOT_DIR_REC_SIZE;
245
246 const size_t kVolSetIDStringLen = sizeof(volume->volSetIDString) - 1;
247 volume->volSetIDString[kVolSetIDStringLen] = '\0';
248 strncpy(volume->volSetIDString, buffer, kVolSetIDStringLen);
249 buffer += kVolSetIDStringLen;
250 TRACE(("InitVolDesc - volume set id string is %s\n", volume->volSetIDString));
251
252 const size_t kPubIDStringLen = sizeof(volume->pubIDString) - 1;
253 volume->pubIDString[kPubIDStringLen] = '\0';
254 strncpy(volume->pubIDString, buffer, kPubIDStringLen);
255 buffer += kPubIDStringLen;
256 TRACE(("InitVolDesc - volume pub id string is %s\n", volume->pubIDString));
257
258 const size_t kDataPreparerLen = sizeof(volume->dataPreparer) - 1;
259 volume->dataPreparer[kDataPreparerLen] = '\0';
260 strncpy(volume->dataPreparer, buffer, kDataPreparerLen);
261 buffer += kDataPreparerLen;
262 TRACE(("InitVolDesc - volume dataPreparer string is %s\n", volume->dataPreparer));
263
264 const size_t kAppIDStringLen = sizeof(volume->appIDString) - 1;
265 volume->appIDString[kAppIDStringLen] = '\0';
266 strncpy(volume->appIDString, buffer, kAppIDStringLen);
267 buffer += kAppIDStringLen;
268 TRACE(("InitVolDesc - volume app id string is %s\n", volume->appIDString));
269
270 const size_t kCopyrightLen = sizeof(volume->copyright) - 1;
271 volume->copyright[kCopyrightLen] = '\0';
272 strncpy(volume->copyright, buffer, kCopyrightLen);
273 buffer += kCopyrightLen;
274 TRACE(("InitVolDesc - copyright is %s\n", volume->copyright));
275
276 const size_t kAbstractFNameLen = sizeof(volume->abstractFName) - 1;
277 volume->abstractFName[kAbstractFNameLen] = '\0';
278 strncpy(volume->abstractFName, buffer, kAbstractFNameLen);
279 buffer += kAbstractFNameLen;
280
281 const size_t kBiblioFNameLen = sizeof(volume->biblioFName) - 1;
282 volume->biblioFName[kBiblioFNameLen] = '\0';
283 strncpy(volume->biblioFName, buffer, kBiblioFNameLen);
284 buffer += kBiblioFNameLen;
285
286 init_volume_date(&volume->createDate, buffer);
287 buffer += ISO_VOL_DATE_SIZE;
288
289 init_volume_date(&volume->modDate, buffer);
290 buffer += ISO_VOL_DATE_SIZE;
291
292 init_volume_date(&volume->expireDate, buffer);
293 buffer += ISO_VOL_DATE_SIZE;
294
295 init_volume_date(&volume->effectiveDate, buffer);
296 buffer += ISO_VOL_DATE_SIZE;
297
298 volume->fileStructVers = *(uint8*)buffer;
299 return B_OK;
300 }
301
302
303 static status_t
parse_rock_ridge(iso9660_volume * volume,iso9660_inode * node,char * buffer,char * end,bool relocated)304 parse_rock_ridge(iso9660_volume* volume, iso9660_inode* node, char* buffer,
305 char* end, bool relocated)
306 {
307 // Now we're at the start of the rock ridge stuff
308 char* altName = NULL;
309 char* slName = NULL;
310 char* newSlName = NULL;
311 uint16 altNameSize = 0;
312 uint16 slNameSize = 0;
313 uint8 length = 0;
314 bool done = false;
315
316 TRACE(("RR: Start of extensions at %p\n", buffer));
317
318 while (!done) {
319 buffer += length;
320 if (buffer + 2 >= end)
321 break;
322 length = *(uint8*)(buffer + 2);
323 if (buffer + length > end)
324 break;
325 if (length == 0)
326 break;
327
328 switch (((int)buffer[0] << 8) + buffer[1]) {
329 // Stat structure stuff
330 case 'PX':
331 {
332 uint8 bytePos = 3;
333 TRACE(("RR: found PX, length %u\n", length));
334 node->attr.pxVer = *(uint8*)(buffer + bytePos++);
335
336 // st_mode
337 node->attr.stat[LSB_DATA].st_mode
338 = *(mode_t*)(buffer + bytePos);
339 bytePos += 4;
340 node->attr.stat[MSB_DATA].st_mode
341 = *(mode_t*)(buffer + bytePos);
342 bytePos += 4;
343
344 // st_nlink
345 node->attr.stat[LSB_DATA].st_nlink
346 = *(nlink_t*)(buffer+bytePos);
347 bytePos += 4;
348 node->attr.stat[MSB_DATA].st_nlink
349 = *(nlink_t*)(buffer + bytePos);
350 bytePos += 4;
351
352 // st_uid
353 node->attr.stat[LSB_DATA].st_uid
354 = *(uid_t*)(buffer + bytePos);
355 bytePos += 4;
356 node->attr.stat[MSB_DATA].st_uid
357 = *(uid_t*)(buffer + bytePos);
358 bytePos += 4;
359
360 // st_gid
361 node->attr.stat[LSB_DATA].st_gid
362 = *(gid_t*)(buffer + bytePos);
363 bytePos += 4;
364 node->attr.stat[MSB_DATA].st_gid
365 = *(gid_t*)(buffer + bytePos);
366 bytePos += 4;
367 break;
368 }
369
370 case 'PN':
371 TRACE(("RR: found PN, length %u\n", length));
372 break;
373
374 // Symbolic link info
375 case 'SL':
376 {
377 uint8 bytePos = 3;
378 uint8 addPos = 0;
379 bool slDone = false;
380 bool useSeparator = true;
381
382 TRACE(("RR: found SL, length %u\n", length));
383 TRACE(("Buffer is at %p\n", buffer));
384 TRACE(("Current length is %u\n", slNameSize));
385 //kernel_debugger("");
386 node->attr.slVer = *(uint8*)(buffer + bytePos++);
387 #if TRACE_ISO9660
388 uint8 slFlags = *(uint8*)(buffer + bytePos++);
389 TRACE(("sl flags are %u\n", slFlags));
390 #else
391 // skip symlink flags
392 ++bytePos;
393 #endif
394 while (!slDone && bytePos < length) {
395 uint8 compFlag = *(uint8*)(buffer + bytePos++);
396 uint8 compLen = *(uint8*)(buffer + bytePos++);
397
398 if (slName == NULL)
399 useSeparator = false;
400
401 addPos = slNameSize;
402
403 TRACE(("sl comp flags are %u, length is %u\n", compFlag, compLen));
404 TRACE(("Current name size is %u\n", slNameSize));
405
406 switch (compFlag) {
407 case SLCP_CONTINUE:
408 useSeparator = false;
409 default:
410 // Add the component to the total path.
411 slNameSize += compLen;
412 if (useSeparator)
413 slNameSize++;
414 newSlName = (char*)realloc(slName,
415 slNameSize + 1);
416 if (newSlName == NULL) {
417 free(slName);
418 return B_NO_MEMORY;
419 }
420 slName = newSlName;
421
422 if (useSeparator) {
423 TRACE(("Adding separator\n"));
424 slName[addPos++] = '/';
425 }
426
427 TRACE(("doing memcopy of %u bytes at offset %d\n", compLen, addPos));
428 memcpy(slName + addPos, buffer + bytePos,
429 compLen);
430
431 addPos += compLen;
432 useSeparator = true;
433 break;
434
435 case SLCP_CURRENT:
436 TRACE(("InitNode - found link to current directory\n"));
437 slNameSize += 2;
438 newSlName = (char*)realloc(slName,
439 slNameSize + 1);
440 if (newSlName == NULL) {
441 free(slName);
442 return B_NO_MEMORY;
443 }
444 slName = newSlName;
445
446 memcpy(slName + addPos, "./", 2);
447 useSeparator = false;
448 break;
449
450 case SLCP_PARENT:
451 slNameSize += 3;
452 newSlName = (char*)realloc(slName,
453 slNameSize + 1);
454 if (newSlName == NULL) {
455 free(slName);
456 return B_NO_MEMORY;
457 }
458 slName = newSlName;
459
460 memcpy(slName + addPos, "../", 3);
461 useSeparator = false;
462 break;
463
464 case SLCP_ROOT:
465 TRACE(("InitNode - found link to root directory\n"));
466 slNameSize += 1;
467 newSlName = (char*)realloc(slName,
468 slNameSize + 1);
469 if (newSlName == NULL) {
470 free(slName);
471 return B_NO_MEMORY;
472 }
473 slName = newSlName;
474 memcpy(slName + addPos, "/", 1);
475 useSeparator = false;
476 break;
477
478 case SLCP_VOLROOT:
479 slDone = true;
480 break;
481
482 case SLCP_HOST:
483 slDone = true;
484 break;
485 }
486 if (slName != NULL)
487 slName[slNameSize] = '\0';
488 bytePos += compLen;
489 TRACE(("Current sl name is \'%s\'\n", slName));
490 }
491 node->attr.slName = slName;
492 TRACE(("InitNode = symlink name is \'%s\'\n", slName));
493 break;
494 }
495
496 // Altername name
497 case 'NM':
498 {
499 uint8 bytePos = 3;
500 uint8 flags = 0;
501 uint16 oldEnd = altNameSize;
502
503 altNameSize += length - 5;
504 char* newAltName = (char*)realloc(altName, altNameSize + 1);
505 if (newAltName == NULL) {
506 free(altName);
507 return B_NO_MEMORY;
508 }
509 altName = newAltName;
510
511 TRACE(("RR: found NM, length %u\n", length));
512 // Read flag and version.
513 node->attr.nmVer = *(uint8 *)(buffer + bytePos++);
514 flags = *(uint8 *)(buffer + bytePos++);
515
516 TRACE(("RR: nm buffer is %s, start at %p\n", (buffer + bytePos),
517 buffer + bytePos));
518
519 // Build the file name.
520 memcpy(altName + oldEnd, buffer + bytePos, length - 5);
521 altName[altNameSize] = '\0';
522 TRACE(("RR: alt name is %s\n", altName));
523
524 // If the name is not continued in another record, update
525 // the record name.
526 if (!(flags & NM_CONTINUE)) {
527 // Get rid of the ISO name, replace with RR name.
528 if (node->name != NULL)
529 free(node->name);
530 node->name = altName;
531 node->name_length = altNameSize;
532 }
533 break;
534 }
535
536 // Deep directory record masquerading as a file.
537 case 'CL':
538 {
539 TRACE(("RR: found CL, length %u\n", length));
540 // Reinitialize the node with the information at the
541 // "." entry of the pointed to directory data
542 node->startLBN[LSB_DATA] = *(uint32*)(buffer+4);
543 node->startLBN[MSB_DATA] = *(uint32*)(buffer+8);
544
545 char* buffer = (char*)block_cache_get(volume->fBlockCache,
546 node->startLBN[FS_DATA_FORMAT]);
547 if (buffer == NULL)
548 break;
549
550 InitNode(volume, node, buffer, NULL, true);
551 block_cache_put(volume->fBlockCache,
552 node->startLBN[FS_DATA_FORMAT]);
553 break;
554 }
555
556 case 'PL':
557 TRACE(("RR: found PL, length %u\n", length));
558 break;
559
560 case 'RE':
561 // Relocated directory, we should skip.
562 TRACE(("RR: found RE, length %u\n", length));
563 if (!relocated)
564 return B_UNSUPPORTED;
565 break;
566
567 case 'TF':
568 TRACE(("RR: found TF, length %u\n", length));
569 break;
570
571 case 'RR':
572 TRACE(("RR: found RR, length %u\n", length));
573 break;
574
575 case 'SF':
576 TRACE(("RR: found SF, sparse files not supported!\n"));
577 // TODO: support sparse files
578 return B_UNSUPPORTED;
579
580 default:
581 if (buffer[0] == '\0') {
582 TRACE(("RR: end of extensions\n"));
583 done = true;
584 } else
585 TRACE(("RR: Unknown tag %c%c\n", buffer[0], buffer[1]));
586 break;
587 }
588 }
589
590 return B_OK;
591 }
592
593 // #pragma mark - ISO-9660 specific exported functions
594
595
596 status_t
ISOMount(const char * path,uint32 flags,iso9660_volume ** _newVolume,bool allowJoliet)597 ISOMount(const char *path, uint32 flags, iso9660_volume **_newVolume,
598 bool allowJoliet)
599 {
600 // path: path to device (eg, /dev/disk/scsi/030/raw)
601 // partition: partition number on device ????
602 // flags: currently unused
603
604 // determine if it is an ISO volume.
605 char buffer[ISO_PVD_SIZE];
606 bool done = false;
607 bool isISO = false;
608 off_t offset = 0x8000;
609 ssize_t retval;
610 partition_info partitionInfo;
611 int deviceBlockSize;
612 iso9660_volume *volume;
613
614 (void)flags;
615
616 TRACE(("ISOMount - ENTER\n"));
617
618 volume = (iso9660_volume *)calloc(sizeof(iso9660_volume), 1);
619 if (volume == NULL) {
620 TRACE(("ISOMount - mem error \n"));
621 return B_NO_MEMORY;
622 }
623
624 memset(&partitionInfo, 0, sizeof(partition_info));
625
626 /* open and lock the device */
627 volume->fdOfSession = open(path, O_RDONLY);
628
629 /* try to open the raw device to get access to the other sessions as well */
630 if (volume->fdOfSession >= 0) {
631 if (ioctl(volume->fdOfSession, B_GET_PARTITION_INFO, &partitionInfo,
632 sizeof(partition_info)) < 0) {
633 TRACE(("B_GET_PARTITION_INFO: ioctl returned error\n"));
634 strcpy(partitionInfo.device, path);
635 }
636 TRACE(("ISOMount: open device/file \"%s\"\n", partitionInfo.device));
637
638 volume->fd = open(partitionInfo.device, O_RDONLY);
639 }
640
641 if (volume->fdOfSession < 0 || volume->fd < 0) {
642 close(volume->fd);
643 close(volume->fdOfSession);
644
645 TRACE(("ISO9660 ERROR - Unable to open <%s>\n", path));
646 free(volume);
647 return B_BAD_VALUE;
648 }
649
650 deviceBlockSize = get_device_block_size(volume->fdOfSession);
651 if (deviceBlockSize < 0) {
652 TRACE(("ISO9660 ERROR - device block size is 0\n"));
653 close(volume->fd);
654 close(volume->fdOfSession);
655
656 free(volume);
657 return B_BAD_VALUE;
658 }
659
660 volume->joliet_level = 0;
661 while (!done && offset < 0x10000) {
662 retval = read_pos(volume->fdOfSession, offset, (void*)buffer,
663 ISO_PVD_SIZE);
664 if (retval < ISO_PVD_SIZE) {
665 isISO = false;
666 break;
667 }
668
669 if (strncmp(buffer + 1, kISO9660IDString, 5) == 0) {
670 if (*buffer == 0x01 && !isISO) {
671 // ISO_VD_PRIMARY
672 off_t maxBlocks;
673
674 TRACE(("ISOMount: Is an ISO9660 volume, initting rec\n"));
675
676 InitVolDesc(volume, buffer);
677 strncpy(volume->devicePath,path,127);
678 volume->id = ISO_ROOTNODE_ID;
679 TRACE(("ISO9660: volume->blockSize = %d\n", volume->logicalBlkSize[FS_DATA_FORMAT]));
680
681 #if TRACE_ISO9660
682 int multiplier = deviceBlockSize / volume->logicalBlkSize[FS_DATA_FORMAT];
683 TRACE(("ISOMount: block size multiplier is %d\n", multiplier));
684 #endif
685
686 // if the session is on a real device, size != 0
687 if (partitionInfo.size != 0) {
688 maxBlocks = (partitionInfo.size + partitionInfo.offset)
689 / volume->logicalBlkSize[FS_DATA_FORMAT];
690 } else
691 maxBlocks = volume->volSpaceSize[FS_DATA_FORMAT];
692
693 /* Initialize access to the cache so that we can do cached i/o */
694 TRACE(("ISO9660: cache init: dev %d, max blocks %lld\n", volume->fd, maxBlocks));
695 volume->fBlockCache = block_cache_create(volume->fd, maxBlocks,
696 volume->logicalBlkSize[FS_DATA_FORMAT], true);
697 isISO = true;
698 } else if (*buffer == 0x02 && isISO && allowJoliet) {
699 // ISO_VD_SUPPLEMENTARY
700
701 // JOLIET extension
702 // test escape sequence for level of UCS-2 characterset
703 if (buffer[88] == 0x25 && buffer[89] == 0x2f) {
704 switch (buffer[90]) {
705 case 0x40: volume->joliet_level = 1; break;
706 case 0x43: volume->joliet_level = 2; break;
707 case 0x45: volume->joliet_level = 3; break;
708 }
709
710 TRACE(("ISO9660 Extensions: Microsoft Joliet Level %d\n", volume->joliet_level));
711
712 // Because Joliet-stuff starts at other sector,
713 // update root directory record.
714 if (volume->joliet_level > 0) {
715 InitNode(volume, &volume->rootDirRec, &buffer[156],
716 NULL);
717 }
718 }
719 } else if (*(unsigned char *)buffer == 0xff) {
720 // ISO_VD_END
721 done = true;
722 } else
723 TRACE(("found header %d\n",*buffer));
724 }
725 offset += 0x800;
726 }
727
728 if (!isISO) {
729 // It isn't an ISO disk.
730 close(volume->fdOfSession);
731 close(volume->fd);
732 free(volume);
733
734 TRACE(("ISOMount: Not an ISO9660 volume!\n"));
735 return B_BAD_VALUE;
736 }
737
738 TRACE(("ISOMount - EXIT, returning %p\n", volume));
739 *_newVolume = volume;
740 return B_OK;
741 }
742
743
744 /*! Reads in a single directory entry and fills in the values in the
745 dirent struct. Uses the cookie to keep track of the current block
746 and position within the block. Also uses the cookie to determine when
747 it has reached the end of the directory file.
748 */
749 status_t
ISOReadDirEnt(iso9660_volume * volume,dircookie * cookie,struct dirent * dirent,size_t bufferSize)750 ISOReadDirEnt(iso9660_volume *volume, dircookie *cookie, struct dirent *dirent,
751 size_t bufferSize)
752 {
753 int result = B_NO_ERROR;
754 bool done = false;
755
756 TRACE(("ISOReadDirEnt - ENTER\n"));
757
758 while (!done) {
759 off_t totalRead = cookie->pos + (cookie->block - cookie->startBlock)
760 * volume->logicalBlkSize[FS_DATA_FORMAT];
761
762 // If we're at the end of the data in a block, move to the next block.
763 char *blockData;
764 while (true) {
765 blockData
766 = (char*)block_cache_get(volume->fBlockCache, cookie->block);
767 if (blockData != NULL && *(blockData + cookie->pos) == 0) {
768 // NULL data, move to next block.
769 block_cache_put(volume->fBlockCache, cookie->block);
770 blockData = NULL;
771 totalRead
772 += volume->logicalBlkSize[FS_DATA_FORMAT] - cookie->pos;
773 cookie->pos = 0;
774 cookie->block++;
775 } else
776 break;
777
778 if (totalRead >= cookie->totalSize)
779 break;
780 }
781
782 off_t cacheBlock = cookie->block;
783
784 if (blockData != NULL && totalRead < cookie->totalSize) {
785 iso9660_inode node;
786 size_t bytesRead = 0;
787 result = InitNode(volume, &node, blockData + cookie->pos,
788 &bytesRead);
789
790 // if we hit an entry that we don't support, we just skip it
791 if (result != B_OK && result != B_UNSUPPORTED)
792 break;
793
794 if (result == B_OK && (node.flags & ISO_IS_ASSOCIATED_FILE) == 0) {
795 size_t nameBufferSize = bufferSize - offsetof(struct dirent, d_name);
796
797 dirent->d_dev = volume->id;
798 dirent->d_ino = ((ino_t)cookie->block << 30)
799 + (cookie->pos & 0x3fffffff);
800 dirent->d_reclen = offsetof(struct dirent, d_name) + node.name_length + 1;
801
802 if (node.name_length <= nameBufferSize) {
803 // need to do some size checking here.
804 strlcpy(dirent->d_name, node.name, node.name_length + 1);
805 TRACE(("ISOReadDirEnt - success, name is %s, block %lld, "
806 "pos %lld, inode id %lld\n", dirent->d_name, cookie->block,
807 cookie->pos, dirent->d_ino));
808 } else {
809 // TODO: this can be just normal if we support reading more
810 // than one entry.
811 TRACE(("ISOReadDirEnt - ERROR, name %s does not fit in "
812 "buffer of size %d\n", node.name, (int)nameBufferSize));
813 result = B_BAD_VALUE;
814 }
815
816 done = true;
817 }
818
819 cookie->pos += bytesRead;
820
821 if (cookie->pos == volume->logicalBlkSize[FS_DATA_FORMAT]) {
822 cookie->pos = 0;
823 cookie->block++;
824 }
825 } else {
826 if (totalRead >= cookie->totalSize)
827 result = B_ENTRY_NOT_FOUND;
828 else
829 result = B_NO_MEMORY;
830 done = true;
831 }
832
833 if (blockData != NULL)
834 block_cache_put(volume->fBlockCache, cacheBlock);
835 }
836
837 TRACE(("ISOReadDirEnt - EXIT, result is %s, vnid is %Lu\n",
838 strerror(result), dirent->d_ino));
839
840 return result;
841 }
842
843
844 status_t
InitNode(iso9660_volume * volume,iso9660_inode * node,char * buffer,size_t * _bytesRead,bool relocated)845 InitNode(iso9660_volume* volume, iso9660_inode* node, char* buffer,
846 size_t* _bytesRead, bool relocated)
847 {
848 uint8 recordLength = *(uint8*)buffer++;
849 size_t nameLength;
850
851 TRACE(("InitNode - ENTER, bufstart is %p, record length is %d bytes\n",
852 buffer, recordLength));
853
854 if (_bytesRead != NULL)
855 *_bytesRead = recordLength;
856 if (recordLength == 0)
857 return B_ENTRY_NOT_FOUND;
858
859 char* end = buffer + recordLength;
860
861 if (!relocated) {
862 node->cache = NULL;
863 node->name = NULL;
864 node->attr.slName = NULL;
865 memset(node->attr.stat, 0, sizeof(node->attr.stat));
866 } else
867 free(node->attr.slName);
868
869 node->extAttrRecLen = *(uint8*)buffer++;
870 TRACE(("InitNode - ext attr length is %d\n", (int)node->extAttrRecLen));
871
872 node->startLBN[LSB_DATA] = *(uint32*)buffer;
873 buffer += 4;
874 node->startLBN[MSB_DATA] = *(uint32*)buffer;
875 buffer += 4;
876 TRACE(("InitNode - data start LBN is %d\n",
877 (int)node->startLBN[FS_DATA_FORMAT]));
878
879 node->dataLen[LSB_DATA] = *(uint32*)buffer;
880 buffer += 4;
881 node->dataLen[MSB_DATA] = *(uint32*)buffer;
882 buffer += 4;
883 TRACE(("InitNode - data length is %d\n",
884 (int)node->dataLen[FS_DATA_FORMAT]));
885
886 init_node_date(&node->recordDate, buffer);
887 buffer += 7;
888
889 node->flags = *(uint8*)buffer;
890 buffer++;
891 TRACE(("InitNode - flags are %d\n", node->flags));
892
893 node->fileUnitSize = *(uint8*)buffer;
894 buffer++;
895 TRACE(("InitNode - fileUnitSize is %d\n", node->fileUnitSize));
896
897 node->interleaveGapSize = *(uint8*)buffer;
898 buffer++;
899 TRACE(("InitNode - interleave gap size = %d\n", node->interleaveGapSize));
900
901 node->volSeqNum = *(uint32*)buffer;
902 buffer += 4;
903 TRACE(("InitNode - volume seq num is %d\n", (int)node->volSeqNum));
904
905 nameLength = *(uint8*)buffer;
906 buffer++;
907
908 // for relocated directories we take the name from the placeholder entry
909 if (!relocated) {
910 node->name_length = nameLength;
911 TRACE(("InitNode - file id length is %" B_PRIu32 "\n",
912 node->name_length));
913 }
914
915 // Set defaults, in case there is no RockRidge stuff.
916 node->attr.stat[FS_DATA_FORMAT].st_mode |= (node->flags & ISO_IS_DIR) != 0
917 ? S_IFDIR | S_IXUSR | S_IRUSR | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH
918 : S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
919
920 if (node->name_length == 0) {
921 TRACE(("InitNode - File ID String is 0 length\n"));
922 return B_ENTRY_NOT_FOUND;
923 }
924
925 if (!relocated) {
926 // JOLIET extension:
927 // on joliet discs, buffer[0] can be 0 for Unicoded filenames,
928 // so I've added a check here to test explicitely for
929 // directories (which have length 1)
930 // Take care of "." and "..", the first two dirents are
931 // these in iso.
932 if (node->name_length == 1 && buffer[0] == 0) {
933 node->name = strdup(".");
934 node->name_length = 1;
935 } else if (node->name_length == 1 && buffer[0] == 1) {
936 node->name = strdup("..");
937 node->name_length = 2;
938 } else if (volume->joliet_level > 0) {
939 // JOLIET extension: convert Unicode16 string to UTF8
940 // Assume that the unicode->utf8 conversion produces 4 byte
941 // utf8 characters, and allocate that much space
942 node->name = (char*)malloc(node->name_length * 2 + 1);
943 if (node->name == NULL)
944 return B_NO_MEMORY;
945
946 int32 sourceLength = node->name_length;
947 int32 destLength = node->name_length * 2;
948
949 status_t status = unicode_to_utf8(buffer, &sourceLength,
950 node->name, &destLength);
951 if (status < B_OK) {
952 dprintf("iso9660: error converting unicode->utf8\n");
953 return status;
954 }
955
956 node->name[destLength] = '\0';
957 node->name_length = destLength;
958
959 sanitize_iso_name(node, false);
960 } else {
961 node->name = (char*)malloc(node->name_length + 1);
962 if (node->name == NULL)
963 return B_NO_MEMORY;
964
965 // convert all characters to lower case
966 for (uint32 i = 0; i < node->name_length; i++)
967 node->name[i] = tolower(buffer[i]);
968
969 node->name[node->name_length] = '\0';
970
971 sanitize_iso_name(node, true);
972 }
973
974 if (node->name == NULL) {
975 TRACE(("InitNode - unable to allocate memory!\n"));
976 return B_NO_MEMORY;
977 }
978 }
979
980 buffer += nameLength;
981 if (nameLength % 2 == 0)
982 buffer++;
983
984 TRACE(("DirRec ID String is: %s\n", node->name));
985
986 return parse_rock_ridge(volume, node, buffer, end, relocated);
987 }
988
989
990 status_t
ConvertRecDate(ISORecDate * inDate,time_t * outDate)991 ConvertRecDate(ISORecDate* inDate, time_t* outDate)
992 {
993 time_t time;
994 int days, i, year;
995 int8_t tz;
996
997 year = inDate->year - 70;
998 tz = inDate->offsetGMT;
999
1000 if (year < 0) {
1001 time = 0;
1002 } else {
1003 const int monlen[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1004
1005 days = (year * 365);
1006
1007 if (year > 2)
1008 days += (year + 1)/ 4;
1009
1010 for (i = 1; (i < inDate->month) && (i < 12); i++) {
1011 days += monlen[i-1];
1012 }
1013
1014 if (((year + 2) % 4) == 0 && inDate->month > 2)
1015 days++;
1016
1017 days += inDate->date - 1;
1018 time = ((((days*24) + inDate->hour) * 60 + inDate->minute) * 60)
1019 + inDate->second;
1020
1021 if (-48 <= tz && tz <= 52)
1022 time -= tz * 15 * 60;
1023 }
1024 *outDate = time;
1025 return 0;
1026 }
1027
1028