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