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