Fix more class/struct mixups.
[haiku.git] / src / add-ons / kernel / file_systems / iso9660 / iso9660.cpp
blobf02a22c5c529b743f46899bf1c60de8a1a33c495
1 /*
2 * Copyright 1999, Be Incorporated. All Rights Reserved.
3 * This file may be used under the terms of the Be Sample Code License.
5 * Copyright 2001, pinc Software. All Rights Reserved.
6 */
9 #include "iso9660.h"
11 #include <ctype.h>
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>
22 # include <ByteOrder.h>
23 # include <Drivers.h>
24 # include <KernelExport.h>
25 # include <fs_cache.h>
26 #endif
28 #include "rock_ridge.h"
30 //#define TRACE_ISO9660 1
31 #if TRACE_ISO9660
32 # define TRACE(x) dprintf x
33 #else
34 # define TRACE(x) ;
35 #endif
38 // Just needed here
39 static status_t unicode_to_utf8(const char *src, int32 *srcLen, char *dst,
40 int32 *dstLen);
43 // ISO9660 should start with this string
44 const char *kISO9660IDString = "CD001";
47 static int
48 get_device_block_size(int fd)
50 device_geometry geometry;
52 if (ioctl(fd, B_GET_GEOMETRY, &geometry) < 0) {
53 struct stat st;
54 if (fstat(fd, &st) < 0 || S_ISDIR(st.st_mode))
55 return 0;
57 return 512; /* just assume it's a plain old file or something */
60 return geometry.bytes_per_sector;
64 // From EncodingComversions.cpp
66 // Pierre's (modified) Uber Macro
68 // NOTE: iso9660 seems to store the unicode text in big-endian form
69 #define u_to_utf8(str, uni_str)\
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;\
94 static status_t
95 unicode_to_utf8(const char *src, int32 *srcLen, char *dst, int32 *dstLen)
97 int32 srcLimit = *srcLen;
98 int32 dstLimit = *dstLen;
99 int32 srcCount = 0;
100 int32 dstCount = 0;
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;
109 u_to_utf8(UTF8, UNICODE);
111 utf8Len = UTF8 - utf8;
112 if (dstCount + utf8Len > dstLimit)
113 break;
115 for (j = 0; j < utf8Len; j++)
116 dst[dstCount + j] = utf8[j];
117 dstCount += utf8Len;
120 *srcLen = srcCount;
121 *dstLen = dstCount;
123 return dstCount > 0 ? B_NO_ERROR : B_ERROR;
127 static void
128 sanitize_iso_name(iso9660_inode* node, bool removeTrailingPoints)
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;
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';
144 static int
145 init_volume_date(ISOVolDate *date, char *buffer)
147 memcpy(date, buffer, ISO_VOL_DATE_SIZE);
148 return 0;
152 static int
153 init_node_date(ISORecDate *date, char *buffer)
155 memcpy(date, buffer, sizeof(struct ISORecDate));
156 return 0;
160 static status_t
161 InitVolDesc(iso9660_volume *volume, char *buffer)
163 TRACE(("InitVolDesc - ENTER\n"));
165 volume->volDescType = *(uint8 *)buffer;
166 buffer += sizeof(volume->volDescType);
168 const size_t kStdIDStringLen = sizeof(volume->stdIDString) - 1;
169 volume->stdIDString[kStdIDStringLen] = '\0';
170 strncpy(volume->stdIDString, buffer, kStdIDStringLen);
171 buffer += kStdIDStringLen;
173 volume->volDescVersion = *(uint8 *)buffer;
174 buffer += sizeof(volume->volDescVersion);
176 buffer += sizeof(volume->unused1); // skip unused 8th byte
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));
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));
190 buffer += sizeof(volume->unused2) - 1; // skip unused 73-80 bytes
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]);
197 buffer += sizeof(volume->unused3) - 1; // skip unused 89-120 bytes
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]);
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]);
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]);
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]);
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]);
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]);
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]);
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]);
239 // Fill in directory record.
240 volume->joliet_level = 0;
241 InitNode(volume, &volume->rootDirRec, buffer, NULL);
243 volume->rootDirRec.id = ISO_ROOTNODE_ID;
244 buffer += ISO_ROOT_DIR_REC_SIZE;
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));
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));
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));
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));
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));
276 const size_t kAbstractFNameLen = sizeof(volume->abstractFName) - 1;
277 volume->abstractFName[kAbstractFNameLen] = '\0';
278 strncpy(volume->abstractFName, buffer, kAbstractFNameLen);
279 buffer += kAbstractFNameLen;
281 const size_t kBiblioFNameLen = sizeof(volume->biblioFName) - 1;
282 volume->biblioFName[kBiblioFNameLen] = '\0';
283 strncpy(volume->biblioFName, buffer, kBiblioFNameLen);
284 buffer += kBiblioFNameLen;
286 init_volume_date(&volume->createDate, buffer);
287 buffer += ISO_VOL_DATE_SIZE;
289 init_volume_date(&volume->modDate, buffer);
290 buffer += ISO_VOL_DATE_SIZE;
292 init_volume_date(&volume->expireDate, buffer);
293 buffer += ISO_VOL_DATE_SIZE;
295 init_volume_date(&volume->effectiveDate, buffer);
296 buffer += ISO_VOL_DATE_SIZE;
298 volume->fileStructVers = *(uint8*)buffer;
299 return B_OK;
303 static status_t
304 parse_rock_ridge(iso9660_volume* volume, iso9660_inode* node, char* buffer,
305 char* end, bool relocated)
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;
316 TRACE(("RR: Start of extensions at %p\n", buffer));
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;
328 switch (((int)buffer[0] << 8) + buffer[1]) {
329 // Stat structure stuff
330 case 'PX':
332 uint8 bytePos = 3;
333 TRACE(("RR: found PX, length %u\n", length));
334 node->attr.pxVer = *(uint8*)(buffer + bytePos++);
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;
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;
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;
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;
370 case 'PN':
371 TRACE(("RR: found PN, length %u\n", length));
372 break;
374 // Symbolic link info
375 case 'SL':
377 uint8 bytePos = 3;
378 uint8 addPos = 0;
379 bool slDone = false;
380 bool useSeparator = true;
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++);
398 if (slName == NULL)
399 useSeparator = false;
401 addPos = slNameSize;
403 TRACE(("sl comp flags are %u, length is %u\n", compFlag, compLen));
404 TRACE(("Current name size is %u\n", slNameSize));
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;
420 slName = newSlName;
422 if (useSeparator) {
423 TRACE(("Adding separator\n"));
424 slName[addPos++] = '/';
427 TRACE(("doing memcopy of %u bytes at offset %d\n", compLen, addPos));
428 memcpy(slName + addPos, buffer + bytePos,
429 compLen);
431 addPos += compLen;
432 useSeparator = true;
433 break;
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;
444 slName = newSlName;
446 memcpy(slName + addPos, "./", 2);
447 useSeparator = false;
448 break;
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;
458 slName = newSlName;
460 memcpy(slName + addPos, "../", 3);
461 useSeparator = false;
462 break;
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;
473 slName = newSlName;
474 memcpy(slName + addPos, "/", 1);
475 useSeparator = false;
476 break;
478 case SLCP_VOLROOT:
479 slDone = true;
480 break;
482 case SLCP_HOST:
483 slDone = true;
484 break;
486 if (slName != NULL)
487 slName[slNameSize] = '\0';
488 bytePos += compLen;
489 TRACE(("Current sl name is \'%s\'\n", slName));
491 node->attr.slName = slName;
492 TRACE(("InitNode = symlink name is \'%s\'\n", slName));
493 break;
496 // Altername name
497 case 'NM':
499 uint8 bytePos = 3;
500 uint8 flags = 0;
501 uint16 oldEnd = altNameSize;
503 altNameSize += length - 5;
504 char* newAltName = (char*)realloc(altName, altNameSize + 1);
505 if (newAltName == NULL) {
506 free(altName);
507 return B_NO_MEMORY;
509 altName = newAltName;
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++);
516 TRACE(("RR: nm buffer is %s, start at %p\n", (buffer + bytePos),
517 buffer + bytePos));
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));
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;
533 break;
536 // Deep directory record masquerading as a file.
537 case 'CL':
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);
545 char* buffer = (char*)block_cache_get(volume->fBlockCache,
546 node->startLBN[FS_DATA_FORMAT]);
547 if (buffer == NULL)
548 break;
550 InitNode(volume, node, buffer, NULL, true);
551 block_cache_put(volume->fBlockCache,
552 node->startLBN[FS_DATA_FORMAT]);
553 break;
556 case 'PL':
557 TRACE(("RR: found PL, length %u\n", length));
558 break;
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;
567 case 'TF':
568 TRACE(("RR: found TF, length %u\n", length));
569 break;
571 case 'RR':
572 TRACE(("RR: found RR, length %u\n", length));
573 break;
575 case 'SF':
576 TRACE(("RR: found SF, sparse files not supported!\n"));
577 // TODO: support sparse files
578 return B_UNSUPPORTED;
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;
590 return B_OK;
593 // #pragma mark - ISO-9660 specific exported functions
596 status_t
597 ISOMount(const char *path, uint32 flags, iso9660_volume **_newVolume,
598 bool allowJoliet)
600 // path: path to device (eg, /dev/disk/scsi/030/raw)
601 // partition: partition number on device ????
602 // flags: currently unused
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;
614 (void)flags;
616 TRACE(("ISOMount - ENTER\n"));
618 volume = (iso9660_volume *)calloc(sizeof(iso9660_volume), 1);
619 if (volume == NULL) {
620 TRACE(("ISOMount - mem error \n"));
621 return B_NO_MEMORY;
624 memset(&partitionInfo, 0, sizeof(partition_info));
626 /* open and lock the device */
627 volume->fdOfSession = open(path, O_RDONLY);
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) < 0) {
632 TRACE(("B_GET_PARTITION_INFO: ioctl returned error\n"));
633 strcpy(partitionInfo.device, path);
635 TRACE(("ISOMount: open device/file \"%s\"\n", partitionInfo.device));
637 volume->fd = open(partitionInfo.device, O_RDONLY);
640 if (volume->fdOfSession < 0 || volume->fd < 0) {
641 close(volume->fd);
642 close(volume->fdOfSession);
644 TRACE(("ISO9660 ERROR - Unable to open <%s>\n", path));
645 free(volume);
646 return B_BAD_VALUE;
649 deviceBlockSize = get_device_block_size(volume->fdOfSession);
650 if (deviceBlockSize < 0) {
651 TRACE(("ISO9660 ERROR - device block size is 0\n"));
652 close(volume->fd);
653 close(volume->fdOfSession);
655 free(volume);
656 return B_BAD_VALUE;
659 volume->joliet_level = 0;
660 while (!done && offset < 0x10000) {
661 retval = read_pos(volume->fdOfSession, offset, (void*)buffer,
662 ISO_PVD_SIZE);
663 if (retval < ISO_PVD_SIZE) {
664 isISO = false;
665 break;
668 if (strncmp(buffer + 1, kISO9660IDString, 5) == 0) {
669 if (*buffer == 0x01 && !isISO) {
670 // ISO_VD_PRIMARY
671 off_t maxBlocks;
673 TRACE(("ISOMount: Is an ISO9660 volume, initting rec\n"));
675 InitVolDesc(volume, buffer);
676 strncpy(volume->devicePath,path,127);
677 volume->id = ISO_ROOTNODE_ID;
678 TRACE(("ISO9660: volume->blockSize = %d\n", volume->logicalBlkSize[FS_DATA_FORMAT]));
680 #if TRACE_ISO9660
681 int multiplier = deviceBlockSize / volume->logicalBlkSize[FS_DATA_FORMAT];
682 TRACE(("ISOMount: block size multiplier is %d\n", multiplier));
683 #endif
685 // if the session is on a real device, size != 0
686 if (partitionInfo.size != 0) {
687 maxBlocks = (partitionInfo.size + partitionInfo.offset)
688 / volume->logicalBlkSize[FS_DATA_FORMAT];
689 } else
690 maxBlocks = volume->volSpaceSize[FS_DATA_FORMAT];
692 /* Initialize access to the cache so that we can do cached i/o */
693 TRACE(("ISO9660: cache init: dev %d, max blocks %Ld\n", volume->fd, maxBlocks));
694 volume->fBlockCache = block_cache_create(volume->fd, maxBlocks,
695 volume->logicalBlkSize[FS_DATA_FORMAT], true);
696 isISO = true;
697 } else if (*buffer == 0x02 && isISO && allowJoliet) {
698 // ISO_VD_SUPPLEMENTARY
700 // JOLIET extension
701 // test escape sequence for level of UCS-2 characterset
702 if (buffer[88] == 0x25 && buffer[89] == 0x2f) {
703 switch (buffer[90]) {
704 case 0x40: volume->joliet_level = 1; break;
705 case 0x43: volume->joliet_level = 2; break;
706 case 0x45: volume->joliet_level = 3; break;
709 TRACE(("ISO9660 Extensions: Microsoft Joliet Level %d\n", volume->joliet_level));
711 // Because Joliet-stuff starts at other sector,
712 // update root directory record.
713 if (volume->joliet_level > 0) {
714 InitNode(volume, &volume->rootDirRec, &buffer[156],
715 NULL);
718 } else if (*(unsigned char *)buffer == 0xff) {
719 // ISO_VD_END
720 done = true;
721 } else
722 TRACE(("found header %d\n",*buffer));
724 offset += 0x800;
727 if (!isISO) {
728 // It isn't an ISO disk.
729 close(volume->fdOfSession);
730 close(volume->fd);
731 free(volume);
733 TRACE(("ISOMount: Not an ISO9660 volume!\n"));
734 return B_BAD_VALUE;
737 TRACE(("ISOMount - EXIT, returning %p\n", volume));
738 *_newVolume = volume;
739 return B_OK;
743 /*! Reads in a single directory entry and fills in the values in the
744 dirent struct. Uses the cookie to keep track of the current block
745 and position within the block. Also uses the cookie to determine when
746 it has reached the end of the directory file.
748 status_t
749 ISOReadDirEnt(iso9660_volume *volume, dircookie *cookie, struct dirent *dirent,
750 size_t bufferSize)
752 int result = B_NO_ERROR;
753 bool done = false;
755 TRACE(("ISOReadDirEnt - ENTER\n"));
757 while (!done) {
758 off_t totalRead = cookie->pos + (cookie->block - cookie->startBlock)
759 * volume->logicalBlkSize[FS_DATA_FORMAT];
761 // If we're at the end of the data in a block, move to the next block.
762 char *blockData;
763 while (true) {
764 blockData
765 = (char*)block_cache_get(volume->fBlockCache, cookie->block);
766 if (blockData != NULL && *(blockData + cookie->pos) == 0) {
767 // NULL data, move to next block.
768 block_cache_put(volume->fBlockCache, cookie->block);
769 blockData = NULL;
770 totalRead
771 += volume->logicalBlkSize[FS_DATA_FORMAT] - cookie->pos;
772 cookie->pos = 0;
773 cookie->block++;
774 } else
775 break;
777 if (totalRead >= cookie->totalSize)
778 break;
781 off_t cacheBlock = cookie->block;
783 if (blockData != NULL && totalRead < cookie->totalSize) {
784 iso9660_inode node;
785 size_t bytesRead = 0;
786 result = InitNode(volume, &node, blockData + cookie->pos,
787 &bytesRead);
789 // if we hit an entry that we don't support, we just skip it
790 if (result != B_OK && result != B_UNSUPPORTED)
791 break;
793 if (result == B_OK && (node.flags & ISO_IS_ASSOCIATED_FILE) == 0) {
794 size_t nameBufferSize = bufferSize - sizeof(struct dirent);
796 dirent->d_dev = volume->id;
797 dirent->d_ino = ((ino_t)cookie->block << 30)
798 + (cookie->pos & 0x3fffffff);
799 dirent->d_reclen = sizeof(struct dirent) + node.name_length + 1;
801 if (node.name_length <= nameBufferSize) {
802 // need to do some size checking here.
803 strlcpy(dirent->d_name, node.name, node.name_length + 1);
804 TRACE(("ISOReadDirEnt - success, name is %s, block %Ld, "
805 "pos %Ld, inode id %Ld\n", dirent->d_name, cookie->block,
806 cookie->pos, dirent->d_ino));
807 } else {
808 // TODO: this can be just normal if we support reading more
809 // than one entry.
810 TRACE(("ISOReadDirEnt - ERROR, name %s does not fit in "
811 "buffer of size %d\n", node.name, (int)nameBufferSize));
812 result = B_BAD_VALUE;
815 done = true;
818 cookie->pos += bytesRead;
820 if (cookie->pos == volume->logicalBlkSize[FS_DATA_FORMAT]) {
821 cookie->pos = 0;
822 cookie->block++;
824 } else {
825 if (totalRead >= cookie->totalSize)
826 result = B_ENTRY_NOT_FOUND;
827 else
828 result = B_NO_MEMORY;
829 done = true;
832 if (blockData != NULL)
833 block_cache_put(volume->fBlockCache, cacheBlock);
836 TRACE(("ISOReadDirEnt - EXIT, result is %s, vnid is %Lu\n",
837 strerror(result), dirent->d_ino));
839 return result;
843 status_t
844 InitNode(iso9660_volume* volume, iso9660_inode* node, char* buffer,
845 size_t* _bytesRead, bool relocated)
847 uint8 recordLength = *(uint8*)buffer++;
848 size_t nameLength;
850 TRACE(("InitNode - ENTER, bufstart is %p, record length is %d bytes\n",
851 buffer, recordLength));
853 if (_bytesRead != NULL)
854 *_bytesRead = recordLength;
855 if (recordLength == 0)
856 return B_ENTRY_NOT_FOUND;
858 char* end = buffer + recordLength;
860 if (!relocated) {
861 node->cache = NULL;
862 node->name = NULL;
863 node->attr.slName = NULL;
864 memset(node->attr.stat, 0, sizeof(node->attr.stat));
865 } else
866 free(node->attr.slName);
868 node->extAttrRecLen = *(uint8*)buffer++;
869 TRACE(("InitNode - ext attr length is %d\n", (int)node->extAttrRecLen));
871 node->startLBN[LSB_DATA] = *(uint32*)buffer;
872 buffer += 4;
873 node->startLBN[MSB_DATA] = *(uint32*)buffer;
874 buffer += 4;
875 TRACE(("InitNode - data start LBN is %d\n",
876 (int)node->startLBN[FS_DATA_FORMAT]));
878 node->dataLen[LSB_DATA] = *(uint32*)buffer;
879 buffer += 4;
880 node->dataLen[MSB_DATA] = *(uint32*)buffer;
881 buffer += 4;
882 TRACE(("InitNode - data length is %d\n",
883 (int)node->dataLen[FS_DATA_FORMAT]));
885 init_node_date(&node->recordDate, buffer);
886 buffer += 7;
888 node->flags = *(uint8*)buffer;
889 buffer++;
890 TRACE(("InitNode - flags are %d\n", node->flags));
892 node->fileUnitSize = *(uint8*)buffer;
893 buffer++;
894 TRACE(("InitNode - fileUnitSize is %d\n", node->fileUnitSize));
896 node->interleaveGapSize = *(uint8*)buffer;
897 buffer++;
898 TRACE(("InitNode - interleave gap size = %d\n", node->interleaveGapSize));
900 node->volSeqNum = *(uint32*)buffer;
901 buffer += 4;
902 TRACE(("InitNode - volume seq num is %d\n", (int)node->volSeqNum));
904 nameLength = *(uint8*)buffer;
905 buffer++;
907 // for relocated directories we take the name from the placeholder entry
908 if (!relocated) {
909 node->name_length = nameLength;
910 TRACE(("InitNode - file id length is %" B_PRIu32 "\n",
911 node->name_length));
914 // Set defaults, in case there is no RockRidge stuff.
915 node->attr.stat[FS_DATA_FORMAT].st_mode |= (node->flags & ISO_IS_DIR) != 0
916 ? S_IFDIR | S_IXUSR | S_IRUSR | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH
917 : S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
919 if (node->name_length == 0) {
920 TRACE(("InitNode - File ID String is 0 length\n"));
921 return B_ENTRY_NOT_FOUND;
924 if (!relocated) {
925 // JOLIET extension:
926 // on joliet discs, buffer[0] can be 0 for Unicoded filenames,
927 // so I've added a check here to test explicitely for
928 // directories (which have length 1)
929 // Take care of "." and "..", the first two dirents are
930 // these in iso.
931 if (node->name_length == 1 && buffer[0] == 0) {
932 node->name = strdup(".");
933 node->name_length = 1;
934 } else if (node->name_length == 1 && buffer[0] == 1) {
935 node->name = strdup("..");
936 node->name_length = 2;
937 } else if (volume->joliet_level > 0) {
938 // JOLIET extension: convert Unicode16 string to UTF8
939 // Assume that the unicode->utf8 conversion produces 4 byte
940 // utf8 characters, and allocate that much space
941 node->name = (char*)malloc(node->name_length * 2 + 1);
942 if (node->name == NULL)
943 return B_NO_MEMORY;
945 int32 sourceLength = node->name_length;
946 int32 destLength = node->name_length * 2;
948 status_t status = unicode_to_utf8(buffer, &sourceLength,
949 node->name, &destLength);
950 if (status < B_OK) {
951 dprintf("iso9660: error converting unicode->utf8\n");
952 return status;
955 node->name[destLength] = '\0';
956 node->name_length = destLength;
958 sanitize_iso_name(node, false);
959 } else {
960 node->name = (char*)malloc(node->name_length + 1);
961 if (node->name == NULL)
962 return B_NO_MEMORY;
964 // convert all characters to lower case
965 for (uint32 i = 0; i < node->name_length; i++)
966 node->name[i] = tolower(buffer[i]);
968 node->name[node->name_length] = '\0';
970 sanitize_iso_name(node, true);
973 if (node->name == NULL) {
974 TRACE(("InitNode - unable to allocate memory!\n"));
975 return B_NO_MEMORY;
979 buffer += nameLength;
980 if (nameLength % 2 == 0)
981 buffer++;
983 TRACE(("DirRec ID String is: %s\n", node->name));
985 return parse_rock_ridge(volume, node, buffer, end, relocated);
989 status_t
990 ConvertRecDate(ISORecDate* inDate, time_t* outDate)
992 time_t time;
993 int days, i, year;
994 int8_t tz;
996 year = inDate->year - 70;
997 tz = inDate->offsetGMT;
999 if (year < 0) {
1000 time = 0;
1001 } else {
1002 const int monlen[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1004 days = (year * 365);
1006 if (year > 2)
1007 days += (year + 1)/ 4;
1009 for (i = 1; (i < inDate->month) && (i < 12); i++) {
1010 days += monlen[i-1];
1013 if (((year + 2) % 4) == 0 && inDate->month > 2)
1014 days++;
1016 days += inDate->date - 1;
1017 time = ((((days*24) + inDate->hour) * 60 + inDate->minute) * 60)
1018 + inDate->second;
1020 if (-48 <= tz && tz <= 52)
1021 time -= tz * 15 * 60;
1023 *outDate = time;
1024 return 0;