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