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