2 * Copyright 1999, Be Incorporated. All Rights Reserved.
3 * This file may be used under the terms of the Be Sample Code License.
5 * Copyright 2001, pinc Software. All Rights Reserved.
18 # include <sys/stat.h>
22 # include <ByteOrder.h>
24 # include <KernelExport.h>
25 # include <fs_cache.h>
28 #include "rock_ridge.h"
30 //#define TRACE_ISO9660 1
32 # define TRACE(x) dprintf x
39 static status_t
unicode_to_utf8(const char *src
, int32
*srcLen
, char *dst
,
43 // ISO9660 should start with this string
44 const char *kISO9660IDString
= "CD001";
48 get_device_block_size(int fd
)
50 device_geometry geometry
;
52 if (ioctl(fd
, B_GET_GEOMETRY
, &geometry
) < 0) {
54 if (fstat(fd
, &st
) < 0 || S_ISDIR(st
.st_mode
))
57 return 512; /* just assume it's a plain old file or something */
60 return geometry
.bytes_per_sector
;
64 // From EncodingComversions.cpp
66 // Pierre's (modified) Uber Macro
68 // NOTE: iso9660 seems to store the unicode text in big-endian form
69 #define u_to_utf8(str, uni_str)\
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);\
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);\
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;\
95 unicode_to_utf8(const char *src
, int32
*srcLen
, char *dst
, int32
*dstLen
)
97 int32 srcLimit
= *srcLen
;
98 int32 dstLimit
= *dstLen
;
102 for (srcCount
= 0; srcCount
< srcLimit
; srcCount
+= 2) {
103 uint16
*UNICODE
= (uint16
*)&src
[srcCount
];
109 u_to_utf8(UTF8
, UNICODE
);
111 utf8Len
= UTF8
- utf8
;
112 if (dstCount
+ utf8Len
> dstLimit
)
115 for (j
= 0; j
< utf8Len
; j
++)
116 dst
[dstCount
+ j
] = utf8
[j
];
123 return dstCount
> 0 ? B_NO_ERROR
: B_ERROR
;
128 sanitize_iso_name(iso9660_inode
* node
, bool removeTrailingPoints
)
130 // Get rid of semicolons, which are used to delineate file versions.
131 if (char* semi
= strchr(node
->name
, ';')) {
133 node
->name_length
= semi
- node
->name
;
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';
145 init_volume_date(ISOVolDate
*date
, char *buffer
)
147 memcpy(date
, buffer
, ISO_VOL_DATE_SIZE
);
153 init_node_date(ISORecDate
*date
, char *buffer
)
155 memcpy(date
, buffer
, sizeof(struct ISORecDate
));
161 InitVolDesc(iso9660_volume
*volume
, char *buffer
)
163 TRACE(("InitVolDesc - ENTER\n"));
165 volume
->volDescType
= *(uint8
*)buffer
;
166 buffer
+= sizeof(volume
->volDescType
);
168 const size_t kStdIDStringLen
= sizeof(volume
->stdIDString
) - 1;
169 volume
->stdIDString
[kStdIDStringLen
] = '\0';
170 strncpy(volume
->stdIDString
, buffer
, kStdIDStringLen
);
171 buffer
+= kStdIDStringLen
;
173 volume
->volDescVersion
= *(uint8
*)buffer
;
174 buffer
+= sizeof(volume
->volDescVersion
);
176 buffer
+= sizeof(volume
->unused1
); // skip unused 8th byte
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
));
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
));
190 buffer
+= sizeof(volume
->unused2
) - 1; // skip unused 73-80 bytes
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
]);
197 buffer
+= sizeof(volume
->unused3
) - 1; // skip unused 89-120 bytes
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
]);
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
]);
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
]);
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
]);
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
]);
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
]);
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
]);
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
]);
239 // Fill in directory record.
240 volume
->joliet_level
= 0;
241 InitNode(volume
, &volume
->rootDirRec
, buffer
, NULL
);
243 volume
->rootDirRec
.id
= ISO_ROOTNODE_ID
;
244 buffer
+= ISO_ROOT_DIR_REC_SIZE
;
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
));
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
));
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
));
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
));
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
));
276 const size_t kAbstractFNameLen
= sizeof(volume
->abstractFName
) - 1;
277 volume
->abstractFName
[kAbstractFNameLen
] = '\0';
278 strncpy(volume
->abstractFName
, buffer
, kAbstractFNameLen
);
279 buffer
+= kAbstractFNameLen
;
281 const size_t kBiblioFNameLen
= sizeof(volume
->biblioFName
) - 1;
282 volume
->biblioFName
[kBiblioFNameLen
] = '\0';
283 strncpy(volume
->biblioFName
, buffer
, kBiblioFNameLen
);
284 buffer
+= kBiblioFNameLen
;
286 init_volume_date(&volume
->createDate
, buffer
);
287 buffer
+= ISO_VOL_DATE_SIZE
;
289 init_volume_date(&volume
->modDate
, buffer
);
290 buffer
+= ISO_VOL_DATE_SIZE
;
292 init_volume_date(&volume
->expireDate
, buffer
);
293 buffer
+= ISO_VOL_DATE_SIZE
;
295 init_volume_date(&volume
->effectiveDate
, buffer
);
296 buffer
+= ISO_VOL_DATE_SIZE
;
298 volume
->fileStructVers
= *(uint8
*)buffer
;
304 parse_rock_ridge(iso9660_volume
* volume
, iso9660_inode
* node
, char* buffer
,
305 char* end
, bool relocated
)
307 // Now we're at the start of the rock ridge stuff
308 char* altName
= NULL
;
310 char* newSlName
= NULL
;
311 uint16 altNameSize
= 0;
312 uint16 slNameSize
= 0;
316 TRACE(("RR: Start of extensions at %p\n", buffer
));
320 if (buffer
+ 2 >= end
)
322 length
= *(uint8
*)(buffer
+ 2);
323 if (buffer
+ length
> end
)
328 switch (((int)buffer
[0] << 8) + buffer
[1]) {
329 // Stat structure stuff
333 TRACE(("RR: found PX, length %u\n", length
));
334 node
->attr
.pxVer
= *(uint8
*)(buffer
+ bytePos
++);
337 node
->attr
.stat
[LSB_DATA
].st_mode
338 = *(mode_t
*)(buffer
+ bytePos
);
340 node
->attr
.stat
[MSB_DATA
].st_mode
341 = *(mode_t
*)(buffer
+ bytePos
);
345 node
->attr
.stat
[LSB_DATA
].st_nlink
346 = *(nlink_t
*)(buffer
+bytePos
);
348 node
->attr
.stat
[MSB_DATA
].st_nlink
349 = *(nlink_t
*)(buffer
+ bytePos
);
353 node
->attr
.stat
[LSB_DATA
].st_uid
354 = *(uid_t
*)(buffer
+ bytePos
);
356 node
->attr
.stat
[MSB_DATA
].st_uid
357 = *(uid_t
*)(buffer
+ bytePos
);
361 node
->attr
.stat
[LSB_DATA
].st_gid
362 = *(gid_t
*)(buffer
+ bytePos
);
364 node
->attr
.stat
[MSB_DATA
].st_gid
365 = *(gid_t
*)(buffer
+ bytePos
);
371 TRACE(("RR: found PN, length %u\n", length
));
374 // Symbolic link info
380 bool useSeparator
= true;
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
++);
388 uint8 slFlags
= *(uint8
*)(buffer
+ bytePos
++);
389 TRACE(("sl flags are %u\n", slFlags
));
391 // skip symlink flags
394 while (!slDone
&& bytePos
< length
) {
395 uint8 compFlag
= *(uint8
*)(buffer
+ bytePos
++);
396 uint8 compLen
= *(uint8
*)(buffer
+ bytePos
++);
399 useSeparator
= false;
403 TRACE(("sl comp flags are %u, length is %u\n", compFlag
, compLen
));
404 TRACE(("Current name size is %u\n", slNameSize
));
408 useSeparator
= false;
410 // Add the component to the total path.
411 slNameSize
+= compLen
;
414 newSlName
= (char*)realloc(slName
,
416 if (newSlName
== NULL
) {
423 TRACE(("Adding separator\n"));
424 slName
[addPos
++] = '/';
427 TRACE(("doing memcopy of %u bytes at offset %d\n", compLen
, addPos
));
428 memcpy(slName
+ addPos
, buffer
+ bytePos
,
436 TRACE(("InitNode - found link to current directory\n"));
438 newSlName
= (char*)realloc(slName
,
440 if (newSlName
== NULL
) {
446 memcpy(slName
+ addPos
, "./", 2);
447 useSeparator
= false;
452 newSlName
= (char*)realloc(slName
,
454 if (newSlName
== NULL
) {
460 memcpy(slName
+ addPos
, "../", 3);
461 useSeparator
= false;
465 TRACE(("InitNode - found link to root directory\n"));
467 newSlName
= (char*)realloc(slName
,
469 if (newSlName
== NULL
) {
474 memcpy(slName
+ addPos
, "/", 1);
475 useSeparator
= false;
487 slName
[slNameSize
] = '\0';
489 TRACE(("Current sl name is \'%s\'\n", slName
));
491 node
->attr
.slName
= slName
;
492 TRACE(("InitNode = symlink name is \'%s\'\n", slName
));
501 uint16 oldEnd
= altNameSize
;
503 altNameSize
+= length
- 5;
504 char* newAltName
= (char*)realloc(altName
, altNameSize
+ 1);
505 if (newAltName
== NULL
) {
509 altName
= newAltName
;
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
++);
516 TRACE(("RR: nm buffer is %s, start at %p\n", (buffer
+ bytePos
),
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
));
524 // If the name is not continued in another record, update
526 if (!(flags
& NM_CONTINUE
)) {
527 // Get rid of the ISO name, replace with RR name.
528 if (node
->name
!= NULL
)
530 node
->name
= altName
;
531 node
->name_length
= altNameSize
;
536 // Deep directory record masquerading as a file.
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);
545 char* buffer
= (char*)block_cache_get(volume
->fBlockCache
,
546 node
->startLBN
[FS_DATA_FORMAT
]);
550 InitNode(volume
, node
, buffer
, NULL
, true);
551 block_cache_put(volume
->fBlockCache
,
552 node
->startLBN
[FS_DATA_FORMAT
]);
557 TRACE(("RR: found PL, length %u\n", length
));
561 // Relocated directory, we should skip.
562 TRACE(("RR: found RE, length %u\n", length
));
564 return B_UNSUPPORTED
;
568 TRACE(("RR: found TF, length %u\n", length
));
572 TRACE(("RR: found RR, length %u\n", length
));
576 TRACE(("RR: found SF, sparse files not supported!\n"));
577 // TODO: support sparse files
578 return B_UNSUPPORTED
;
581 if (buffer
[0] == '\0') {
582 TRACE(("RR: end of extensions\n"));
585 TRACE(("RR: Unknown tag %c%c\n", buffer
[0], buffer
[1]));
593 // #pragma mark - ISO-9660 specific exported functions
597 ISOMount(const char *path
, uint32 flags
, iso9660_volume
**_newVolume
,
600 // path: path to device (eg, /dev/disk/scsi/030/raw)
601 // partition: partition number on device ????
602 // flags: currently unused
604 // determine if it is an ISO volume.
605 char buffer
[ISO_PVD_SIZE
];
608 off_t offset
= 0x8000;
610 partition_info partitionInfo
;
612 iso9660_volume
*volume
;
616 TRACE(("ISOMount - ENTER\n"));
618 volume
= (iso9660_volume
*)calloc(sizeof(iso9660_volume
), 1);
619 if (volume
== NULL
) {
620 TRACE(("ISOMount - mem error \n"));
624 memset(&partitionInfo
, 0, sizeof(partition_info
));
626 /* open and lock the device */
627 volume
->fdOfSession
= open(path
, O_RDONLY
);
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
);
635 TRACE(("ISOMount: open device/file \"%s\"\n", partitionInfo
.device
));
637 volume
->fd
= open(partitionInfo
.device
, O_RDONLY
);
640 if (volume
->fdOfSession
< 0 || volume
->fd
< 0) {
642 close(volume
->fdOfSession
);
644 TRACE(("ISO9660 ERROR - Unable to open <%s>\n", path
));
649 deviceBlockSize
= get_device_block_size(volume
->fdOfSession
);
650 if (deviceBlockSize
< 0) {
651 TRACE(("ISO9660 ERROR - device block size is 0\n"));
653 close(volume
->fdOfSession
);
659 volume
->joliet_level
= 0;
660 while (!done
&& offset
< 0x10000) {
661 retval
= read_pos(volume
->fdOfSession
, offset
, (void*)buffer
,
663 if (retval
< ISO_PVD_SIZE
) {
668 if (strncmp(buffer
+ 1, kISO9660IDString
, 5) == 0) {
669 if (*buffer
== 0x01 && !isISO
) {
673 TRACE(("ISOMount: Is an ISO9660 volume, initting rec\n"));
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
]));
681 int multiplier
= deviceBlockSize
/ volume
->logicalBlkSize
[FS_DATA_FORMAT
];
682 TRACE(("ISOMount: block size multiplier is %d\n", multiplier
));
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
];
690 maxBlocks
= volume
->volSpaceSize
[FS_DATA_FORMAT
];
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);
697 } else if (*buffer
== 0x02 && isISO
&& allowJoliet
) {
698 // ISO_VD_SUPPLEMENTARY
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;
709 TRACE(("ISO9660 Extensions: Microsoft Joliet Level %d\n", volume
->joliet_level
));
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],
718 } else if (*(unsigned char *)buffer
== 0xff) {
722 TRACE(("found header %d\n",*buffer
));
728 // It isn't an ISO disk.
729 close(volume
->fdOfSession
);
733 TRACE(("ISOMount: Not an ISO9660 volume!\n"));
737 TRACE(("ISOMount - EXIT, returning %p\n", volume
));
738 *_newVolume
= volume
;
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.
749 ISOReadDirEnt(iso9660_volume
*volume
, dircookie
*cookie
, struct dirent
*dirent
,
752 int result
= B_NO_ERROR
;
755 TRACE(("ISOReadDirEnt - ENTER\n"));
758 off_t totalRead
= cookie
->pos
+ (cookie
->block
- cookie
->startBlock
)
759 * volume
->logicalBlkSize
[FS_DATA_FORMAT
];
761 // If we're at the end of the data in a block, move to the next block.
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
);
771 += volume
->logicalBlkSize
[FS_DATA_FORMAT
] - cookie
->pos
;
777 if (totalRead
>= cookie
->totalSize
)
781 off_t cacheBlock
= cookie
->block
;
783 if (blockData
!= NULL
&& totalRead
< cookie
->totalSize
) {
785 size_t bytesRead
= 0;
786 result
= InitNode(volume
, &node
, blockData
+ cookie
->pos
,
789 // if we hit an entry that we don't support, we just skip it
790 if (result
!= B_OK
&& result
!= B_UNSUPPORTED
)
793 if (result
== B_OK
&& (node
.flags
& ISO_IS_ASSOCIATED_FILE
) == 0) {
794 size_t nameBufferSize
= bufferSize
- sizeof(struct dirent
);
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;
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
));
808 // TODO: this can be just normal if we support reading more
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
;
818 cookie
->pos
+= bytesRead
;
820 if (cookie
->pos
== volume
->logicalBlkSize
[FS_DATA_FORMAT
]) {
825 if (totalRead
>= cookie
->totalSize
)
826 result
= B_ENTRY_NOT_FOUND
;
828 result
= B_NO_MEMORY
;
832 if (blockData
!= NULL
)
833 block_cache_put(volume
->fBlockCache
, cacheBlock
);
836 TRACE(("ISOReadDirEnt - EXIT, result is %s, vnid is %Lu\n",
837 strerror(result
), dirent
->d_ino
));
844 InitNode(iso9660_volume
* volume
, iso9660_inode
* node
, char* buffer
,
845 size_t* _bytesRead
, bool relocated
)
847 uint8 recordLength
= *(uint8
*)buffer
++;
850 TRACE(("InitNode - ENTER, bufstart is %p, record length is %d bytes\n",
851 buffer
, recordLength
));
853 if (_bytesRead
!= NULL
)
854 *_bytesRead
= recordLength
;
855 if (recordLength
== 0)
856 return B_ENTRY_NOT_FOUND
;
858 char* end
= buffer
+ recordLength
;
863 node
->attr
.slName
= NULL
;
864 memset(node
->attr
.stat
, 0, sizeof(node
->attr
.stat
));
866 free(node
->attr
.slName
);
868 node
->extAttrRecLen
= *(uint8
*)buffer
++;
869 TRACE(("InitNode - ext attr length is %d\n", (int)node
->extAttrRecLen
));
871 node
->startLBN
[LSB_DATA
] = *(uint32
*)buffer
;
873 node
->startLBN
[MSB_DATA
] = *(uint32
*)buffer
;
875 TRACE(("InitNode - data start LBN is %d\n",
876 (int)node
->startLBN
[FS_DATA_FORMAT
]));
878 node
->dataLen
[LSB_DATA
] = *(uint32
*)buffer
;
880 node
->dataLen
[MSB_DATA
] = *(uint32
*)buffer
;
882 TRACE(("InitNode - data length is %d\n",
883 (int)node
->dataLen
[FS_DATA_FORMAT
]));
885 init_node_date(&node
->recordDate
, buffer
);
888 node
->flags
= *(uint8
*)buffer
;
890 TRACE(("InitNode - flags are %d\n", node
->flags
));
892 node
->fileUnitSize
= *(uint8
*)buffer
;
894 TRACE(("InitNode - fileUnitSize is %d\n", node
->fileUnitSize
));
896 node
->interleaveGapSize
= *(uint8
*)buffer
;
898 TRACE(("InitNode - interleave gap size = %d\n", node
->interleaveGapSize
));
900 node
->volSeqNum
= *(uint32
*)buffer
;
902 TRACE(("InitNode - volume seq num is %d\n", (int)node
->volSeqNum
));
904 nameLength
= *(uint8
*)buffer
;
907 // for relocated directories we take the name from the placeholder entry
909 node
->name_length
= nameLength
;
910 TRACE(("InitNode - file id length is %" B_PRIu32
"\n",
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
;
919 if (node
->name_length
== 0) {
920 TRACE(("InitNode - File ID String is 0 length\n"));
921 return B_ENTRY_NOT_FOUND
;
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
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
)
945 int32 sourceLength
= node
->name_length
;
946 int32 destLength
= node
->name_length
* 2;
948 status_t status
= unicode_to_utf8(buffer
, &sourceLength
,
949 node
->name
, &destLength
);
951 dprintf("iso9660: error converting unicode->utf8\n");
955 node
->name
[destLength
] = '\0';
956 node
->name_length
= destLength
;
958 sanitize_iso_name(node
, false);
960 node
->name
= (char*)malloc(node
->name_length
+ 1);
961 if (node
->name
== NULL
)
964 // convert all characters to lower case
965 for (uint32 i
= 0; i
< node
->name_length
; i
++)
966 node
->name
[i
] = tolower(buffer
[i
]);
968 node
->name
[node
->name_length
] = '\0';
970 sanitize_iso_name(node
, true);
973 if (node
->name
== NULL
) {
974 TRACE(("InitNode - unable to allocate memory!\n"));
979 buffer
+= nameLength
;
980 if (nameLength
% 2 == 0)
983 TRACE(("DirRec ID String is: %s\n", node
->name
));
985 return parse_rock_ridge(volume
, node
, buffer
, end
, relocated
);
990 ConvertRecDate(ISORecDate
* inDate
, time_t* outDate
)
996 year
= inDate
->year
- 70;
997 tz
= inDate
->offsetGMT
;
1002 const int monlen
[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1004 days
= (year
* 365);
1007 days
+= (year
+ 1)/ 4;
1009 for (i
= 1; (i
< inDate
->month
) && (i
< 12); i
++) {
1010 days
+= monlen
[i
-1];
1013 if (((year
+ 2) % 4) == 0 && inDate
->month
> 2)
1016 days
+= inDate
->date
- 1;
1017 time
= ((((days
*24) + inDate
->hour
) * 60 + inDate
->minute
) * 60)
1020 if (-48 <= tz
&& tz
<= 52)
1021 time
-= tz
* 15 * 60;