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