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