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
39static status_t unicode_to_utf8(const char	*src, int32	*srcLen, char *dst,
40	int32 *dstLen);
41
42
43// ISO9660 should start with this string
44const char *kISO9660IDString = "CD001";
45
46
47static int
48get_device_block_size(int fd)
49{
50	device_geometry geometry;
51
52	if (ioctl(fd, B_GET_GEOMETRY, &geometry, sizeof(device_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
94static status_t
95unicode_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
127static void
128sanitize_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
144static int
145init_volume_date(ISOVolDate *date, char *buffer)
146{
147	memcpy(date, buffer, ISO_VOL_DATE_SIZE);
148	return 0;
149}
150
151
152static int
153init_node_date(ISORecDate *date, char *buffer)
154{
155	memcpy(date, buffer, sizeof(struct ISORecDate));
156	return 0;
157}
158
159
160static status_t
161InitVolDesc(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
303static status_t
304parse_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
596status_t
597ISOMount(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,
632				sizeof(partition_info)) < 0) {
633			TRACE(("B_GET_PARTITION_INFO: ioctl returned error\n"));
634			strcpy(partitionInfo.device, path);
635		}
636		TRACE(("ISOMount: open device/file \"%s\"\n", partitionInfo.device));
637
638		volume->fd = open(partitionInfo.device, O_RDONLY);
639	}
640
641	if (volume->fdOfSession < 0 || volume->fd < 0) {
642		close(volume->fd);
643		close(volume->fdOfSession);
644
645		TRACE(("ISO9660 ERROR - Unable to open <%s>\n", path));
646		free(volume);
647		return B_BAD_VALUE;
648	}
649
650	deviceBlockSize = get_device_block_size(volume->fdOfSession);
651	if (deviceBlockSize < 0)  {
652		TRACE(("ISO9660 ERROR - device block size is 0\n"));
653		close(volume->fd);
654		close(volume->fdOfSession);
655
656		free(volume);
657		return B_BAD_VALUE;
658	}
659
660	volume->joliet_level = 0;
661	while (!done && offset < 0x10000) {
662		retval = read_pos(volume->fdOfSession, offset, (void*)buffer,
663			ISO_PVD_SIZE);
664		if (retval < ISO_PVD_SIZE) {
665			isISO = false;
666			break;
667		}
668
669		if (strncmp(buffer + 1, kISO9660IDString, 5) == 0) {
670			if (*buffer == 0x01 && !isISO) {
671				// ISO_VD_PRIMARY
672				off_t maxBlocks;
673
674				TRACE(("ISOMount: Is an ISO9660 volume, initting rec\n"));
675
676				InitVolDesc(volume, buffer);
677				strncpy(volume->devicePath,path,127);
678				volume->id = ISO_ROOTNODE_ID;
679				TRACE(("ISO9660: volume->blockSize = %d\n", volume->logicalBlkSize[FS_DATA_FORMAT]));
680
681#if TRACE_ISO9660
682				int multiplier = deviceBlockSize / volume->logicalBlkSize[FS_DATA_FORMAT];
683				TRACE(("ISOMount: block size multiplier is %d\n", multiplier));
684#endif
685
686				// if the session is on a real device, size != 0
687				if (partitionInfo.size != 0) {
688					maxBlocks = (partitionInfo.size + partitionInfo.offset)
689						/ volume->logicalBlkSize[FS_DATA_FORMAT];
690				} else
691					maxBlocks = volume->volSpaceSize[FS_DATA_FORMAT];
692
693				/* Initialize access to the cache so that we can do cached i/o */
694				TRACE(("ISO9660: cache init: dev %d, max blocks %lld\n", volume->fd, maxBlocks));
695				volume->fBlockCache = block_cache_create(volume->fd, maxBlocks,
696					volume->logicalBlkSize[FS_DATA_FORMAT], true);
697				isISO = true;
698			} else if (*buffer == 0x02 && isISO && allowJoliet) {
699				// ISO_VD_SUPPLEMENTARY
700
701				// JOLIET extension
702				// test escape sequence for level of UCS-2 characterset
703			    if (buffer[88] == 0x25 && buffer[89] == 0x2f) {
704					switch (buffer[90]) {
705						case 0x40: volume->joliet_level = 1; break;
706						case 0x43: volume->joliet_level = 2; break;
707						case 0x45: volume->joliet_level = 3; break;
708					}
709
710					TRACE(("ISO9660 Extensions: Microsoft Joliet Level %d\n", volume->joliet_level));
711
712					// Because Joliet-stuff starts at other sector,
713					// update root directory record.
714					if (volume->joliet_level > 0) {
715						InitNode(volume, &volume->rootDirRec, &buffer[156],
716							NULL);
717					}
718				}
719			} else if (*(unsigned char *)buffer == 0xff) {
720				// ISO_VD_END
721				done = true;
722			} else
723				TRACE(("found header %d\n",*buffer));
724		}
725		offset += 0x800;
726	}
727
728	if (!isISO) {
729		// It isn't an ISO disk.
730		close(volume->fdOfSession);
731		close(volume->fd);
732		free(volume);
733
734		TRACE(("ISOMount: Not an ISO9660 volume!\n"));
735		return B_BAD_VALUE;
736	}
737
738	TRACE(("ISOMount - EXIT, returning %p\n", volume));
739	*_newVolume = volume;
740	return B_OK;
741}
742
743
744/*!	Reads in a single directory entry and fills in the values in the
745	dirent struct. Uses the cookie to keep track of the current block
746	and position within the block. Also uses the cookie to determine when
747	it has reached the end of the directory file.
748*/
749status_t
750ISOReadDirEnt(iso9660_volume *volume, dircookie *cookie, struct dirent *dirent,
751	size_t bufferSize)
752{
753	int	result = B_NO_ERROR;
754	bool done = false;
755
756	TRACE(("ISOReadDirEnt - ENTER\n"));
757
758	while (!done) {
759		off_t totalRead = cookie->pos + (cookie->block - cookie->startBlock)
760			* volume->logicalBlkSize[FS_DATA_FORMAT];
761
762		// If we're at the end of the data in a block, move to the next block.
763		char *blockData;
764		while (true) {
765			blockData
766				= (char*)block_cache_get(volume->fBlockCache, cookie->block);
767			if (blockData != NULL && *(blockData + cookie->pos) == 0) {
768				// NULL data, move to next block.
769				block_cache_put(volume->fBlockCache, cookie->block);
770				blockData = NULL;
771				totalRead
772					+= volume->logicalBlkSize[FS_DATA_FORMAT] - cookie->pos;
773				cookie->pos = 0;
774				cookie->block++;
775			} else
776				break;
777
778			if (totalRead >= cookie->totalSize)
779				break;
780		}
781
782		off_t cacheBlock = cookie->block;
783
784		if (blockData != NULL && totalRead < cookie->totalSize) {
785			iso9660_inode node;
786			size_t bytesRead = 0;
787			result = InitNode(volume, &node, blockData + cookie->pos,
788				&bytesRead);
789
790			// if we hit an entry that we don't support, we just skip it
791			if (result != B_OK && result != B_UNSUPPORTED)
792				break;
793
794			if (result == B_OK && (node.flags & ISO_IS_ASSOCIATED_FILE) == 0) {
795				size_t nameBufferSize = bufferSize - offsetof(struct dirent, d_name);
796
797				dirent->d_dev = volume->id;
798				dirent->d_ino = ((ino_t)cookie->block << 30)
799					+ (cookie->pos & 0x3fffffff);
800				dirent->d_reclen = offsetof(struct dirent, d_name) + node.name_length + 1;
801
802				if (node.name_length <= nameBufferSize) {
803					// need to do some size checking here.
804					strlcpy(dirent->d_name, node.name, node.name_length + 1);
805					TRACE(("ISOReadDirEnt  - success, name is %s, block %lld, "
806						"pos %lld, inode id %lld\n", dirent->d_name, cookie->block,
807						cookie->pos, dirent->d_ino));
808				} else {
809					// TODO: this can be just normal if we support reading more
810					// than one entry.
811					TRACE(("ISOReadDirEnt - ERROR, name %s does not fit in "
812						"buffer of size %d\n", node.name, (int)nameBufferSize));
813					result = B_BAD_VALUE;
814				}
815
816				done = true;
817			}
818
819			cookie->pos += bytesRead;
820
821			if (cookie->pos == volume->logicalBlkSize[FS_DATA_FORMAT]) {
822				cookie->pos = 0;
823				cookie->block++;
824			}
825		} else {
826			if (totalRead >= cookie->totalSize)
827				result = B_ENTRY_NOT_FOUND;
828			else
829				result = B_NO_MEMORY;
830			done = true;
831		}
832
833		if (blockData != NULL)
834			block_cache_put(volume->fBlockCache, cacheBlock);
835	}
836
837	TRACE(("ISOReadDirEnt - EXIT, result is %s, vnid is %Lu\n",
838		strerror(result), dirent->d_ino));
839
840	return result;
841}
842
843
844status_t
845InitNode(iso9660_volume* volume, iso9660_inode* node, char* buffer,
846	size_t* _bytesRead, bool relocated)
847{
848	uint8 recordLength = *(uint8*)buffer++;
849	size_t nameLength;
850
851	TRACE(("InitNode - ENTER, bufstart is %p, record length is %d bytes\n",
852		buffer, recordLength));
853
854	if (_bytesRead != NULL)
855		*_bytesRead = recordLength;
856	if (recordLength == 0)
857		return B_ENTRY_NOT_FOUND;
858
859	char* end = buffer + recordLength;
860
861	if (!relocated) {
862		node->cache = NULL;
863		node->name = NULL;
864		node->attr.slName = NULL;
865		memset(node->attr.stat, 0, sizeof(node->attr.stat));
866	} else
867		free(node->attr.slName);
868
869	node->extAttrRecLen = *(uint8*)buffer++;
870	TRACE(("InitNode - ext attr length is %d\n", (int)node->extAttrRecLen));
871
872	node->startLBN[LSB_DATA] = *(uint32*)buffer;
873	buffer += 4;
874	node->startLBN[MSB_DATA] = *(uint32*)buffer;
875	buffer += 4;
876	TRACE(("InitNode - data start LBN is %d\n",
877		(int)node->startLBN[FS_DATA_FORMAT]));
878
879	node->dataLen[LSB_DATA] = *(uint32*)buffer;
880	buffer += 4;
881	node->dataLen[MSB_DATA] = *(uint32*)buffer;
882	buffer += 4;
883	TRACE(("InitNode - data length is %d\n",
884		(int)node->dataLen[FS_DATA_FORMAT]));
885
886	init_node_date(&node->recordDate, buffer);
887	buffer += 7;
888
889	node->flags = *(uint8*)buffer;
890	buffer++;
891	TRACE(("InitNode - flags are %d\n", node->flags));
892
893	node->fileUnitSize = *(uint8*)buffer;
894	buffer++;
895	TRACE(("InitNode - fileUnitSize is %d\n", node->fileUnitSize));
896
897	node->interleaveGapSize = *(uint8*)buffer;
898	buffer++;
899	TRACE(("InitNode - interleave gap size = %d\n", node->interleaveGapSize));
900
901	node->volSeqNum = *(uint32*)buffer;
902	buffer += 4;
903	TRACE(("InitNode - volume seq num is %d\n", (int)node->volSeqNum));
904
905	nameLength = *(uint8*)buffer;
906	buffer++;
907
908	// for relocated directories we take the name from the placeholder entry
909	if (!relocated) {
910		node->name_length = nameLength;
911		TRACE(("InitNode - file id length is %" B_PRIu32 "\n",
912			node->name_length));
913	}
914
915	// Set defaults, in case there is no RockRidge stuff.
916	node->attr.stat[FS_DATA_FORMAT].st_mode |= (node->flags & ISO_IS_DIR) != 0
917		? S_IFDIR | S_IXUSR | S_IRUSR | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH
918		: S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
919
920	if (node->name_length == 0) {
921		TRACE(("InitNode - File ID String is 0 length\n"));
922		return B_ENTRY_NOT_FOUND;
923	}
924
925	if (!relocated) {
926		// JOLIET extension:
927		// on joliet discs, buffer[0] can be 0 for Unicoded filenames,
928		// so I've added a check here to test explicitely for
929		// directories (which have length 1)
930		// Take care of "." and "..", the first two dirents are
931		// these in iso.
932		if (node->name_length == 1 && buffer[0] == 0) {
933			node->name = strdup(".");
934			node->name_length = 1;
935		} else if (node->name_length == 1 && buffer[0] == 1) {
936			node->name = strdup("..");
937			node->name_length = 2;
938		} else if (volume->joliet_level > 0) {
939			// JOLIET extension: convert Unicode16 string to UTF8
940			// Assume that the unicode->utf8 conversion produces 4 byte
941			// utf8 characters, and allocate that much space
942			node->name = (char*)malloc(node->name_length * 2 + 1);
943			if (node->name == NULL)
944				return B_NO_MEMORY;
945
946			int32 sourceLength = node->name_length;
947			int32 destLength = node->name_length * 2;
948
949			status_t status = unicode_to_utf8(buffer, &sourceLength,
950				node->name, &destLength);
951			if (status < B_OK) {
952				dprintf("iso9660: error converting unicode->utf8\n");
953				return status;
954			}
955
956			node->name[destLength] = '\0';
957			node->name_length = destLength;
958
959			sanitize_iso_name(node, false);
960		} else {
961			node->name = (char*)malloc(node->name_length + 1);
962			if (node->name == NULL)
963				return B_NO_MEMORY;
964
965			// convert all characters to lower case
966			for (uint32 i = 0; i < node->name_length; i++)
967				node->name[i] = tolower(buffer[i]);
968
969			node->name[node->name_length] = '\0';
970
971			sanitize_iso_name(node, true);
972		}
973
974		if (node->name == NULL) {
975			TRACE(("InitNode - unable to allocate memory!\n"));
976			return B_NO_MEMORY;
977		}
978	}
979
980	buffer += nameLength;
981	if (nameLength % 2 == 0)
982		buffer++;
983
984	TRACE(("DirRec ID String is: %s\n", node->name));
985
986	return parse_rock_ridge(volume, node, buffer, end, relocated);
987}
988
989
990status_t
991ConvertRecDate(ISORecDate* inDate, time_t* outDate)
992{
993	time_t	time;
994	int		days, i, year;
995	int8_t tz;
996
997	year = inDate->year - 70;
998	tz = inDate->offsetGMT;
999
1000	if (year < 0) {
1001		time = 0;
1002	} else {
1003		const int monlen[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1004
1005		days = (year * 365);
1006
1007		if (year > 2)
1008			days += (year + 1)/ 4;
1009
1010		for (i = 1; (i < inDate->month) && (i < 12); i++) {
1011			days += monlen[i-1];
1012		}
1013
1014		if (((year + 2) % 4) == 0 && inDate->month > 2)
1015			days++;
1016
1017		days += inDate->date - 1;
1018		time = ((((days*24) + inDate->hour) * 60 + inDate->minute) * 60)
1019					+ inDate->second;
1020
1021		if (-48 <= tz && tz <= 52)
1022			time -= tz * 15 * 60;
1023	}
1024	*outDate = time;
1025	return 0;
1026}
1027
1028