xref: /haiku/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
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 **		iso9960/multi-session, 1.0.0
8 **			2001-03-11: added multi-session support, axeld.
9 */
10 
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <errno.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <dirent.h>
19 #include <sys/stat.h>
20 #include <time.h>
21 #include <malloc.h>
22 
23 #include <KernelExport.h>
24 #include <NodeMonitor.h>
25 #include <fs_interface.h>
26 #include <fs_cache.h>
27 
28 #include <fs_attr.h>
29 #include <fs_info.h>
30 #include <fs_index.h>
31 #include <fs_query.h>
32 #include <fs_volume.h>
33 
34 #include <util/kernel_cpp.h>
35 
36 #include "iso.h"
37 #include "iso9660.h"
38 
39 //#define TRACE_ISO9660 1
40 #if TRACE_ISO9660
41 #	define TRACE(x) dprintf x
42 #else
43 #	define TRACE(x) ;
44 #endif
45 
46 
47 /*  Start of fundamental (read-only) required functions */
48 static status_t		fs_mount(mount_id mountID, const char *device, uint32 flags,
49 				const char *args, void **_data, vnode_id *_rootID);
50 static status_t		fs_unmount(void *ns);
51 
52 static status_t		fs_get_vnode_name(void *_ns, void *_node,
53 				char *buffer, size_t bufferSize);
54 static status_t		fs_walk(void *_ns, void *_base, const char *file,
55 				vnode_id *_vnodeID, int *_type);
56 
57 static status_t		fs_read_vnode(void *_ns, vnode_id vnid, void **node, bool reenter);
58 static status_t		fs_release_vnode(void *_ns, void *_node, bool reenter);
59 static status_t		fs_get_file_map(fs_volume _fs, fs_vnode _node, off_t offset, size_t size,
60 				struct file_io_vec *vecs, size_t *_count);
61 static status_t		fs_read_stat(void *_ns, void *_node, struct stat *st);
62 static status_t		fs_open(void *_ns, void *_node, int omode, void **cookie);
63 static status_t		fs_read(void *_ns, void *_node, void *cookie, off_t pos,
64 				void *buf, size_t *len);
65 /// fs_free_cookie - free cookie for file created in open.
66 static status_t		fs_free_cookie(void *ns, void *node, void *cookie);
67 static status_t		fs_close(void *ns, void *node, void *cookie);
68 
69 // fs_access - checks permissions for access.
70 static status_t		fs_access(void *_ns, void *_node, int mode);
71 
72 // fs_opendir - creates fs-specific "cookie" struct that can tell where
73 //					we are at in the directory list.
74 static status_t		fs_open_dir(void* _ns, void* _node, void** cookie);
75 // fs_readdir - read 1 or more dirents, keep state in cookie, return
76 //					0 when no more entries.
77 static status_t		fs_read_dir(void *_ns, void *_node, void *cookie, struct dirent *buf,
78 				size_t bufsize, uint32 *_num);
79 // fs_rewinddir - set cookie to represent beginning of directory, so
80 //					later fs_readdir calls start at beginning.
81 static status_t		fs_rewind_dir(void *_ns, void *_node, void *cookie);
82 // fs_closedir - Do whatever you need to to close a directory (sometimes
83 //					nothing), but DON'T free the cookie!
84 static status_t		fs_close_dir(void *_ns, void *_node, void *cookie);
85 // fs_free_dircookie - Free the fs-specific cookie struct
86 static status_t		fs_free_dir_cookie(void *_ns, void *_node, void *cookie);
87 
88 // fs_rfsstat - Fill in fs_info struct for device.
89 static status_t		fs_read_fs_stat(void *_ns, struct fs_info *);
90 
91 // fs_readlink - Read in the name of a symbolic link.
92 static status_t 	fs_read_link(void *_ns, void *_node, char *buf, size_t *bufsize);
93 
94 
95 //	#pragma mark - Scanning
96 
97 struct identify_cookie {
98 	iso9660_info info;
99 };
100 
101 
102 static float
103 fs_identify_partition(int fd, partition_data *partition, void **_cookie)
104 {
105 	iso9660_info info;
106 	status_t status = iso9660_fs_identify(fd, &info);
107 	if (status != B_OK)
108 		return status;
109 
110 	identify_cookie *cookie = new identify_cookie;
111 	memcpy(&cookie->info, &info, sizeof(info));
112 
113 	*_cookie = cookie;
114 	return 0.8f;
115 }
116 
117 
118 static status_t
119 fs_scan_partition(int fd, partition_data *partition, void *_cookie)
120 {
121 	identify_cookie *cookie = (identify_cookie *)_cookie;
122 
123 	partition->status = B_PARTITION_VALID;
124 	partition->flags |= B_PARTITION_FILE_SYSTEM | B_PARTITION_READ_ONLY ;
125 	partition->block_size = ISO_PVD_SIZE;
126 	partition->content_size = ISO_PVD_SIZE * cookie->info.maxBlocks;
127 	partition->content_name = strdup(cookie->info.get_preferred_volume_name());
128 	if (partition->content_name == NULL)
129 		return B_NO_MEMORY;
130 
131 	return B_OK;
132 }
133 
134 
135 static void
136 fs_free_identify_partition_cookie(partition_data *partition, void *_cookie)
137 {
138 	identify_cookie *cookie = (identify_cookie *)_cookie;
139 
140 	delete cookie;
141 }
142 
143 
144 static status_t
145 fs_mount(mount_id mountID, const char *device, uint32 flags,
146 	const char *args, void **_data, vnode_id *_rootID)
147 {
148 	/*
149 	Kernel passes in nspace_id, (representing a disk or partition?)
150 	and a string representing the device (eg, "/dev/scsi/disk/030/raw)
151 	Flags will be used for things like specifying read-only mounting.
152 	parms is parameters passed in as switches from the mount command,
153 	and len is the length of the otions. data is a pointer to a
154 	driver-specific struct that should be allocated in this routine.
155 	It will then be passed back in by the kernel to a number of the other
156 	fs driver functions. vnid should also be passed back to the kernel,
157 	representing the vnode id of the root vnode.
158 	*/
159 	status_t result = EINVAL;
160 		// return EINVAL if it's not a device compatible with the driver.
161 	bool allow_joliet = true;
162 	nspace *vol;
163 
164 	(void)flags;
165 
166 	/* Create semaphore if it's not already created. When do we need to
167 		use semaphores? */
168 
169 	// Check for a 'nojoliet' parm
170 	// all we check for is the existance of 'nojoliet' in the parms.
171 	if (args != NULL) {
172 		uint32 i;
173 		char *spot;
174 		char *buf = strdup(args);
175 
176 		uint32 len = strlen(buf);
177 		// lower case the parms data
178 		for (i = 0; i < len + 1; i++)
179 			buf[i] = tolower(buf[i]);
180 
181 		// look for nojoliet
182 		spot = strstr(buf, "nojoliet");
183 		if (spot != NULL)
184 			allow_joliet = false;
185 
186 		free(buf);
187 	}
188 
189 	// Try and mount volume as an ISO volume.
190 	result = ISOMount(device, O_RDONLY, &vol, allow_joliet);
191 
192 	// If it is ISO …
193 	if (result == B_NO_ERROR) {
194 		//vnode_id rootID = vol->rootDirRec.startLBN[FS_DATA_FORMAT];
195 		//*vnid = rootID;
196 		*_rootID = ISO_ROOTNODE_ID;
197 		*_data = (void*)vol;
198 
199 		vol->id = mountID;
200 
201 		// You MUST do this. Create the vnode for the root.
202 		result = publish_vnode(mountID, *_rootID, (void*)&(vol->rootDirRec));
203 		if (result != B_NO_ERROR) {
204 			block_cache_delete(vol->fBlockCache, false);
205 			free(vol);
206 			result = EINVAL;
207 		} else
208 			result = B_NO_ERROR;
209 	}
210 	return result;
211 }
212 
213 
214 static status_t
215 fs_unmount(void *_ns)
216 {
217 	status_t result = B_NO_ERROR;
218 	nspace *ns = (nspace *)_ns;
219 
220 	TRACE(("fs_unmount - ENTER\n"));
221 
222 	// Unlike in BeOS, we need to put the reference to our root node ourselves
223 	put_vnode(ns->id, ISO_ROOTNODE_ID);
224 
225 	block_cache_delete(ns->fBlockCache, false);
226 	close(ns->fdOfSession);
227 	result = close(ns->fd);
228 
229 	free(ns);
230 
231 	TRACE(("fs_unmount - EXIT, result is %s\n", strerror(result)));
232 	return result;
233 }
234 
235 
236 static status_t
237 fs_read_fs_stat(void *_ns, struct fs_info *fss)
238 {
239 	// Fill in fs_info struct for device.
240 	nspace *ns = (nspace *)_ns;
241 	int i;
242 
243 	TRACE(("fs_read_fs_stat - ENTER\n"));
244 
245 	// Fill in device id.
246 	//fss->dev = ns->fd;
247 
248 	// Root vnode ID
249 	//fss->root = ISO_ROOTNODE_ID;
250 
251 	// File system flags.
252 	fss->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
253 
254 	// FS block size.
255 	fss->block_size = ns->logicalBlkSize[FS_DATA_FORMAT];
256 
257 	// IO size - specifies buffer size for file copying
258 	fss->io_size = 65536;
259 
260 	// Total blocks?
261 	fss->total_blocks = ns->volSpaceSize[FS_DATA_FORMAT];
262 
263 	// Free blocks = 0, read only
264 	fss->free_blocks = 0;
265 
266 	// Device name.
267 	strncpy(fss->device_name, ns->devicePath, sizeof(fss->device_name));
268 
269 	strncpy(fss->volume_name, ns->volIDString, sizeof(fss->volume_name));
270 	for (i = strlen(fss->volume_name)-1; i >=0 ; i--)
271 		if (fss->volume_name[i] != ' ')
272 			break;
273 
274 	if (i < 0)
275 		strcpy(fss->volume_name, "UNKNOWN");
276 	else
277 		fss->volume_name[i + 1] = 0;
278 
279 	// File system name
280 	strcpy(fss->fsh_name, "iso9660");
281 
282 	TRACE(("fs_read_fs_stat - EXIT\n"));
283 	return 0;
284 }
285 
286 
287 static status_t
288 fs_get_vnode_name(void *ns, void *_node, char *buffer, size_t bufferSize)
289 {
290 	vnode *node = (vnode*)_node;
291 
292 	strlcpy(buffer, node->fileIDString, bufferSize);
293 	return B_OK;
294 }
295 
296 
297 /* fs_walk - the walk function just "walks" through a directory looking for
298 	the specified file. When you find it, call get_vnode on its vnid to init
299 	it for the kernel.
300 */
301 static status_t
302 fs_walk(void *_ns, void *base, const char *file, vnode_id *_vnodeID, int *_type)
303 {
304 	/* Starting at the base, find file in the subdir, and return path
305 		string and vnode id of file. */
306 	nspace *ns = (nspace *)_ns;
307 	vnode *baseNode = (vnode*)base;
308 	uint32 dataLen = baseNode->dataLen[FS_DATA_FORMAT];
309 	vnode *newNode = NULL;
310 	status_t result = ENOENT;
311 	bool done = FALSE;
312 	uint32 totalRead = 0;
313 	off_t block = baseNode->startLBN[FS_DATA_FORMAT];
314 
315 	TRACE(("fs_walk - looking for %s in dir file of length %d\n", file,
316 		baseNode->dataLen[FS_DATA_FORMAT]));
317 
318 	if (strcmp(file, ".") == 0)  {
319 		// base directory
320 		TRACE(("fs_walk - found \".\" file.\n"));
321 		*_vnodeID = baseNode->id;
322 		*_type = S_IFDIR;
323 		if (get_vnode(ns->id, *_vnodeID, (void **)&newNode) != 0)
324     		result = EINVAL;
325 	    else
326 	    	result = B_NO_ERROR;
327 	} else if (strcmp(file, "..") == 0) {
328 		// parent directory
329 		TRACE(("fs_walk - found \"..\" file.\n"));
330 		*_vnodeID = baseNode->parID;
331 		*_type = S_IFDIR;
332 		if (get_vnode(ns->id, *_vnodeID, (void **)&newNode) != 0)
333 			result = EINVAL;
334 		else
335 			result = B_NO_ERROR;
336 	} else {
337 		// look up file in the directory
338 		char *blockData;
339 
340 		while ((totalRead < dataLen) && !done) {
341 			off_t cachedBlock = block;
342 
343 			blockData = (char *)block_cache_get_etc(ns->fBlockCache, block, 0, ns->logicalBlkSize[FS_DATA_FORMAT]);
344 			if (blockData != NULL) {
345 				int bytesRead = 0;
346 				off_t blockBytesRead = 0;
347 				vnode node;
348 				int initResult;
349 
350 				TRACE(("fs_walk - read buffer from disk at LBN %Ld into buffer 0x%x.\n",
351 					block, blockData));
352 
353 				// Move to the next 2-block set if necessary
354 				// Don't go over end of buffer, if dir record sits on boundary.
355 
356 				node.fileIDString = NULL;
357 				node.attr.slName = NULL;
358 
359 				while (blockBytesRead  < 2*ns->logicalBlkSize[FS_DATA_FORMAT]
360 					&& totalRead + blockBytesRead < dataLen
361 					&& blockData[0] != 0
362 					&& !done)
363 				{
364 					initResult = InitNode(&node, blockData, &bytesRead, ns->joliet_level);
365 					TRACE(("fs_walk - InitNode returned %s, filename %s, %d bytes read\n", strerror(initResult), node.fileIDString, bytesRead));
366 
367 					if (initResult == B_NO_ERROR) {
368 						if (strlen(node.fileIDString) == strlen(file)
369 							&& !strncmp(node.fileIDString, file, strlen(file)))
370 						{
371 							TRACE(("fs_walk - success, found vnode at block %Ld, pos %Ld\n", block, blockBytesRead));
372 							*_vnodeID = (block << 30) + (blockBytesRead & 0xFFFFFFFF);
373 							TRACE(("fs_walk - New vnode id is %Ld\n", *_vnodeID));
374 
375 							if (get_vnode(ns->id, *_vnodeID, (void **)&newNode) != 0)
376 								result = EINVAL;
377 							else {
378 								newNode->parID = baseNode->id;
379 								done = TRUE;
380 								result = B_NO_ERROR;
381 							}
382 						} else {
383 							if (node.fileIDString != NULL) {
384 								free(node.fileIDString);
385 								node.fileIDString = NULL;
386 							}
387 							if (node.attr.slName != NULL) {
388 								free(node.attr.slName);
389 								node.attr.slName = NULL;
390 							}
391 						}
392 					} else {
393 						result = initResult;
394 						if (bytesRead == 0)
395 							done = TRUE;
396 					}
397 					blockData += bytesRead;
398 					blockBytesRead += bytesRead;
399 
400 					TRACE(("fs_walk - Adding %d bytes to blockBytes read (total %Ld/%Ld).\n",
401 						bytesRead, blockBytesRead, baseNode->dataLen[FS_DATA_FORMAT]));
402 				}
403 				totalRead += ns->logicalBlkSize[FS_DATA_FORMAT];
404 				block++;
405 
406 				TRACE(("fs_walk - moving to next block %Ld, total read %Ld\n", block, totalRead));
407 				block_cache_put(ns->fBlockCache, cachedBlock);
408 
409 			} else
410 				done = TRUE;
411 		}
412 
413 		if (newNode)
414 			*_type = newNode->attr.stat[FS_DATA_FORMAT].st_mode;
415 
416 	}
417 	TRACE(("fs_walk - EXIT, result is %s, vnid is %Lu\n", strerror(result), *_vnodeID));
418 	return result;
419 }
420 
421 
422 static status_t
423 fs_read_vnode(void *_ns, vnode_id vnid, void **node, bool reenter)
424 {
425 	uint32 block, pos;
426 	nspace *ns = (nspace*)_ns;
427 	status_t result = B_NO_ERROR;
428 	vnode *newNode = (vnode*)calloc(sizeof(vnode), 1);
429 
430 	(void)reenter;
431 
432 	pos = (vnid & 0x3FFFFFFF);
433 	block = (vnid >> 30);
434 
435 	TRACE(("fs_read_vnode - ENTER, block = %ld, pos = %ld, raw = %Lu node 0x%x\n",
436 		block, pos, vnid, newNode));
437 
438 	if (newNode != NULL) {
439 		if (vnid == ISO_ROOTNODE_ID) {
440 			TRACE(("fs_read_vnode - root node requested.\n"));
441 			memcpy(newNode, &(ns->rootDirRec), sizeof(vnode));
442 			*node = (void*)newNode;
443 		} else {
444 			char *blockData = (char *)block_cache_get_etc(ns->fBlockCache, block, 0, ns->logicalBlkSize[FS_DATA_FORMAT]);
445 
446 			if (pos > ns->logicalBlkSize[FS_DATA_FORMAT]) {
447 				if (blockData != NULL)
448 					block_cache_put(ns->fBlockCache, block);
449 
450 				result = EINVAL;
451 		 	} else if (blockData != NULL) {
452 				result = InitNode(newNode, blockData + pos, NULL, ns->joliet_level);
453 				block_cache_put(ns->fBlockCache, block);
454 				newNode->id = vnid;
455 
456 				TRACE(("fs_read_vnode - init result is %s\n", strerror(result)));
457 				*node = (void *)newNode;
458 				TRACE(("fs_read_vnode - new file %s, size %ld\n", newNode->fileIDString, newNode->dataLen[FS_DATA_FORMAT]));
459 			}
460 		}
461 	} else
462 		result = ENOMEM;
463 
464 	if (result == B_OK && !(newNode->flags & ISO_ISDIR)) {
465 		newNode->cache = file_cache_create(ns->id, vnid, newNode->dataLen[FS_DATA_FORMAT], ns->fdOfSession);
466 	}
467 
468 	TRACE(("fs_read_vnode - EXIT, result is %s\n", strerror(result)));
469 	return result;
470 }
471 
472 
473 static status_t
474 fs_release_vnode(void *ns, void *_node, bool reenter)
475 {
476 	status_t result = B_NO_ERROR;
477 	vnode *node = (vnode*)_node;
478 
479 	(void)ns;
480 	(void)reenter;
481 
482 	TRACE(("fs_release_vnode - ENTER (0x%x)\n", node));
483 
484 	if (node != NULL) {
485 		if (node->id != ISO_ROOTNODE_ID) {
486 			if (node->fileIDString != NULL)
487 				free (node->fileIDString);
488 			if (node->attr.slName != NULL)
489 				free (node->attr.slName);
490 			if (node->cache != NULL)
491 				file_cache_delete(node->cache);
492 
493 			free(node);
494 		}
495 	}
496 
497 	TRACE(("fs_release_vnode - EXIT\n"));
498 	return result;
499 }
500 
501 
502 static status_t
503 fs_get_file_map(fs_volume _fs, fs_vnode _node, off_t pos, size_t reqLen,
504 	struct file_io_vec *vecs, size_t *_count)
505 {
506 	nspace *ns = (nspace *)_fs;			// global stuff
507 	vnode *node = (vnode *)_node;		// The read file vnode.
508 	uint16 blockSize = ns->logicalBlkSize[FS_DATA_FORMAT];
509 	uint32 startBlock = node->startLBN[FS_DATA_FORMAT] + (pos / blockSize);
510 	off_t blockPos = pos % blockSize;
511 	off_t numBlocks = 0;
512 	uint32 dataLen = node->dataLen[FS_DATA_FORMAT];
513 	size_t endLen = 0;
514 	size_t startLen =  0;
515 	size_t index = 0, max = *_count;
516 
517 	TRACE(("fs_get_file_map - ENTER (0x%x)\n", node));
518 
519 	// Allow an open to work on a dir, but no reads
520 	if (node->flags & ISO_ISDIR)
521 		return EISDIR;
522 
523 	if (pos < 0)
524 		pos = 0;
525 	*_count = 0;
526 
527 	// If passed-in requested length is bigger than file size, change it to
528 	// file size.
529 	if (reqLen + pos > dataLen)
530 		reqLen = dataLen - pos;
531 
532 	// Compute the length of the partial start-block read, if any.
533 	if (reqLen + blockPos <= blockSize)
534 		startLen = reqLen;
535 	else if (blockPos > 0)
536 		startLen = blockSize - blockPos;
537 
538 	if (blockPos == 0 && reqLen >= blockSize) {
539 		TRACE(("Setting startLen to 0\n"));
540 		startLen = 0;
541 	}
542 
543 	// Compute the length of the partial end-block read, if any.
544 	if (reqLen + blockPos > blockSize)
545 		endLen = (reqLen + blockPos) % blockSize;
546 
547 	// Compute the number of middle blocks to read.
548 	numBlocks = ((reqLen - endLen - startLen) /  blockSize);
549 
550 	if (pos >= dataLen) {
551 		// If pos >= file length, return
552 		return B_OK;
553 	}
554 
555 	// Read in the first, potentially partial, block.
556 	if (startLen > 0) {
557 		vecs[index].offset = startBlock * blockSize + blockPos;
558 		vecs[index].length = startLen;
559 		startBlock++;
560 		index++;
561 		if (index >= max) {
562 			// we're out of file_io_vecs; let's bail out
563 			*_count = index;
564 			return B_BUFFER_OVERFLOW;
565 		}
566 	}
567 
568 	// Read in the middle blocks.
569 	if (numBlocks > 0) {
570 		for (int32 i=startBlock; i<startBlock+numBlocks; i++) {
571 			vecs[index].offset = i * blockSize;
572 			vecs[index].length = blockSize;
573 			index++;
574 			if (index >= max) {
575 				// we're out of file_io_vecs; let's bail out
576 				*_count = index;
577 				return B_BUFFER_OVERFLOW;
578 			}
579 		}
580 	}
581 
582 	// Read in the last partial block.
583 	if (endLen > 0) {
584 		off_t endBlock = startBlock + numBlocks;
585 		vecs[index].offset = endBlock * blockSize;
586 		vecs[index].length = endLen;
587 		index++;
588 	}
589 
590 	*_count = index;
591 
592 	TRACE(("fs_get_file_map - EXIT\n"));
593 	return B_OK;
594 }
595 
596 
597 static status_t
598 fs_read_stat(void *_ns, void *_node, struct stat *st)
599 {
600 	nspace *ns = (nspace*)_ns;
601 	vnode *node = (vnode*)_node;
602 	status_t result = B_NO_ERROR;
603 	time_t time;
604 
605 	TRACE(("fs_read_stat - ENTER\n"));
606 
607 	st->st_dev = ns->id;
608 	st->st_ino = node->id;
609 	st->st_nlink = node->attr.stat[FS_DATA_FORMAT].st_nlink;
610 	st->st_uid = node->attr.stat[FS_DATA_FORMAT].st_uid;
611 	st->st_gid = node->attr.stat[FS_DATA_FORMAT].st_gid;
612 	st->st_blksize = 65536;
613 	st->st_mode = node->attr.stat[FS_DATA_FORMAT].st_mode;
614 
615 	// Same for file/dir in ISO9660
616 	st->st_size = node->dataLen[FS_DATA_FORMAT];
617 	if (ConvertRecDate(&(node->recordDate), &time) == B_NO_ERROR)
618 		st->st_ctime = st->st_mtime = st->st_atime = time;
619 
620 	TRACE(("fs_read_stat - EXIT, result is %s\n", strerror(result)));
621 
622 	return result;
623 }
624 
625 
626 static status_t
627 fs_open(void *_ns, void *_node, int omode, void **cookie)
628 {
629 	status_t result = B_NO_ERROR;
630 
631 	(void)_ns;
632 	(void)cookie;
633 
634 	// Do not allow any of the write-like open modes to get by
635 	if ((omode == O_WRONLY) || (omode == O_RDWR))
636 		result = EROFS;
637 	else if((omode & O_TRUNC) || (omode & O_CREAT))
638 		result = EROFS;
639 
640 	return result;
641 }
642 
643 
644 static status_t
645 fs_read(void *_ns, void *_node, void *cookie, off_t pos, void *buf, size_t *len)
646 {
647 #if 0
648 	nspace *ns = (nspace *)_ns;			// global stuff
649 	vnode *node = (vnode *)_node;		// The read file vnode.
650 	uint16 blockSize = ns->logicalBlkSize[FS_DATA_FORMAT];
651 	uint32 startBlock = node->startLBN[FS_DATA_FORMAT] + (pos / blockSize);
652 	off_t blockPos = pos % blockSize;
653 	off_t numBlocks = 0;
654 	uint32 dataLen = node->dataLen[FS_DATA_FORMAT];
655 	status_t result = B_NO_ERROR;
656 	size_t endLen = 0;
657 	size_t reqLen = *len;
658 	size_t startLen =  0;
659 
660 	(void)cookie;
661 
662 	// Allow an open to work on a dir, but no reads
663 	if (node->flags & ISO_ISDIR)
664 		return EISDIR;
665 
666 	if (pos < 0)
667 		pos = 0;
668 	*len = 0;
669 
670 	// If passed-in requested length is bigger than file size, change it to
671 	// file size.
672 	if (reqLen + pos > dataLen)
673 		reqLen = dataLen - pos;
674 
675 	// Compute the length of the partial start-block read, if any.
676 
677 	if (reqLen + blockPos <= blockSize)
678 		startLen = reqLen;
679 	else if (blockPos > 0)
680 		startLen = blockSize - blockPos;
681 
682 	if (blockPos == 0 && reqLen >= blockSize) {
683 		TRACE(("Setting startLen to 0, even block read\n"));
684 		startLen = 0;
685 	}
686 
687 	// Compute the length of the partial end-block read, if any.
688 	if (reqLen + blockPos > blockSize)
689 		endLen = (reqLen + blockPos) % blockSize;
690 
691 	// Compute the number of middle blocks to read.
692 	numBlocks = ((reqLen - endLen - startLen) /  blockSize);
693 
694 	//dprintf("fs_read - ENTER, pos is %Ld, len is %lu\n", pos, reqLen);
695 	//dprintf("fs_read - filename is %s\n", node->fileIDString);
696 	//dprintf("fs_read - total file length is %lu\n", dataLen);
697 	//dprintf("fs_read - start block of file is %lu\n", node->startLBN[FS_DATA_FORMAT]);
698 	//dprintf("fs_read - block pos is %Lu\n", blockPos);
699 	//dprintf("fs_read - read block will be %lu\n", startBlock);
700 	//dprintf("fs_read - startLen is %lu\n", startLen);
701 	//dprintf("fs_read - endLen is %lu\n", endLen);
702 	//dprintf("fs_read - num blocks to read is %Ld\n", numBlocks);
703 
704 	if (pos >= dataLen) {
705 		// If pos >= file length, return length of 0.
706 		*len = 0;
707 		return B_OK;
708 	}
709 
710 	// Read in the first, potentially partial, block.
711 	if (startLen > 0) {
712 		off_t cachedBlock = startBlock;
713 		char *blockData = (char *)block_cache_get_etc(ns->fBlockCache, startBlock, 0, blockSize);
714 		if (blockData != NULL) {
715 			//dprintf("fs_read - copying first block, len is %d.\n", startLen);
716 			memcpy(buf, blockData+blockPos, startLen);
717 			*len += startLen;
718 			block_cache_put(ns->fBlockCache, cachedBlock);
719 			startBlock++;
720 		} else
721 			result = EIO;
722 	}
723 
724 	// Read in the middle blocks.
725 	if (numBlocks > 0 && result == B_NO_ERROR) {
726 		TRACE(("fs_read - getting middle blocks\n"));
727 		char *endBuf = ((char *)buf) + startLen;
728 		for (int32 i=startBlock; i<startBlock+numBlocks; i++) {
729 			char *blockData = (char *)block_cache_get_etc(ns->fBlockCache, i, 0, blockSize);
730 			memcpy(endBuf, blockData, blockSize);
731 			*len += blockSize;
732 			endBuf += blockSize;
733 			block_cache_put(ns->fBlockCache, i);
734 		}
735 	}
736 
737 	// Read in the last partial block.
738 	if (result == B_NO_ERROR && endLen > 0) {
739 		off_t endBlock = startBlock + numBlocks;
740 		char *endBlockData = (char*)block_cache_get_etc(ns->fBlockCache, endBlock, 0, blockSize);
741 		if (endBlockData != NULL) {
742 			char *endBuf = ((char *)buf) + (reqLen - endLen);
743 
744 			memcpy(endBuf, endBlockData, endLen);
745 			block_cache_put(ns->fBlockCache, endBlock);
746 			*len += endLen;
747 		} else
748 			result = EIO;
749 	}
750 
751 	TRACE(("fs_read - EXIT, result is %s\n", strerror(result)));
752 	return result;
753 #else
754 	vnode *node = (vnode *)_node;           // The read file vnode.
755 	uint32 dataLen = node->dataLen[FS_DATA_FORMAT];
756 
757 	// set/check boundaries for pos/length
758 	if (pos < 0) {
759 		return B_BAD_VALUE;
760 	}
761 	if (pos >= dataLen) {
762 		// If pos >= file length, return length of 0.
763 		*len = 0;
764 		return B_OK;
765         }
766 	return file_cache_read(node->cache, pos, buf, len);
767 #endif
768 }
769 
770 
771 static status_t
772 fs_close(void *ns, void *node, void *cookie)
773 {
774 	(void)ns;
775 	(void)node;
776 	(void)cookie;
777 
778 	//dprintf("fs_close - ENTER\n");
779 	//dprintf("fs_close - EXIT\n");
780 	return B_OK;
781 }
782 
783 static status_t
784 fs_free_cookie(void *ns, void *node, void *cookie)
785 {
786 	(void)ns;
787 	(void)node;
788 	(void)cookie;
789 
790 	// We don't allocate file cookies, so we do nothing here.
791 	//dprintf("fs_free_cookie - ENTER\n");
792 	//if (cookie != NULL) free (cookie);
793 	//dprintf("fs_free_cookie - EXIT\n");
794 	return B_OK;
795 }
796 
797 // fs_access - checks permissions for access.
798 static status_t
799 fs_access(void *ns, void *node, int mode)
800 {
801 	(void)ns;
802 	(void)node;
803 	(void)mode;
804 
805 	// ns 	- global, fs-specific struct for device
806 	// node	- node to check permissions for
807 	// mode - requested permissions on node.
808 	//dprintf("fs_access - ENTER\n");
809 	//dprintf("fs_access - EXIT\n");
810 	return B_OK;
811 }
812 
813 static status_t
814 fs_read_link(void *_ns, void *_node, char *buffer, size_t *_bufferSize)
815 {
816 	vnode *node = (vnode *)_node;
817 	status_t result = EINVAL;
818 
819 	(void)_ns;
820 
821 	if (S_ISLNK(node->attr.stat[FS_DATA_FORMAT].st_mode)) {
822 		size_t length = strlen(node->attr.slName);
823 		if (length > *_bufferSize)
824 			memcpy(buffer, node->attr.slName, *_bufferSize);
825 		else {
826 			memcpy(buffer, node->attr.slName, length);
827 			*_bufferSize = length;
828 		}
829 
830 		result = B_NO_ERROR;
831 	}
832 
833 	return result;
834 }
835 
836 
837 static status_t
838 fs_open_dir(void *_ns, void *_node, void **cookie)
839 {
840 	vnode *node = (vnode *)_node;
841 	status_t result = B_NO_ERROR;
842 	dircookie *dirCookie = (dircookie *)malloc(sizeof(dircookie));
843 
844 	(void)_ns;
845 
846 	TRACE(("fs_open_dir - ENTER, node is 0x%x\n", _node));
847 
848 	if (!(node->flags & ISO_ISDIR))
849 		result = EMFILE;
850 
851 	if (dirCookie != NULL) {
852 		dirCookie->startBlock = node->startLBN[FS_DATA_FORMAT];
853 		dirCookie->block = node->startLBN[FS_DATA_FORMAT];
854 		dirCookie->totalSize = node->dataLen[FS_DATA_FORMAT];
855 		dirCookie->pos = 0;
856 		dirCookie->id = node->id;
857 		*cookie = (void *)dirCookie;
858 	} else
859 		result = ENOMEM;
860 
861 	TRACE(("fs_open_dir - EXIT\n"));
862 	return result;
863 }
864 
865 
866 static status_t
867 fs_read_dir(void *_ns, void *_node, void *_cookie, struct dirent *buffer,
868 	size_t bufferSize, uint32 *num)
869 {
870 	status_t result = B_NO_ERROR;
871 	nspace *ns = (nspace *)_ns;
872 	dircookie *dirCookie = (dircookie *)_cookie;
873 
874 	(void)_node;
875 
876 	TRACE(("fs_read_dir - ENTER\n"));
877 
878 	result = ISOReadDirEnt(ns, dirCookie, buffer, bufferSize);
879 
880 	// If we succeeded, return 1, the number of dirents we read.
881 	if (result == B_NO_ERROR)
882 		*num = 1;
883 	else
884 		*num = 0;
885 
886 	// When you get to the end, don't return an error, just return
887 	// a zero in *num.
888 
889 	if (result == ENOENT)
890 		result = B_NO_ERROR;
891 
892 	TRACE(("fs_read_dir - EXIT, result is %s\n", strerror(result)));
893 	return result;
894 }
895 
896 
897 static status_t
898 fs_rewind_dir(void *ns, void *node, void* _cookie)
899 {
900 	status_t result = EINVAL;
901 	dircookie *cookie = (dircookie*)_cookie;
902 
903 	(void)ns;
904 	(void)node;
905 
906 	//dprintf("fs_rewind_dir - ENTER\n");
907 	if (cookie != NULL) {
908 		cookie->block = cookie->startBlock;
909 		cookie->pos = 0;
910 		result = B_NO_ERROR;
911 	}
912 	//dprintf("fs_rewind_dir - EXIT, result is %s\n", strerror(result));
913 	return result;
914 }
915 
916 
917 static status_t
918 fs_close_dir(void *ns, void *node, void *cookie)
919 {
920 	(void)ns;
921 	(void)node;
922 	(void)cookie;
923 
924 	// ns 		- global, fs-specific struct for device
925 	// node		- directory to close
926 	// cookie	- current cookie for directory.
927 	//dprintf("fs_close_dir - ENTER\n");
928 	//dprintf("fs_close_dir - EXIT\n");
929 	return B_OK;
930 }
931 
932 
933 static status_t
934 fs_free_dir_cookie(void *ns, void *node, void *cookie)
935 {
936 	(void)ns;
937 	(void)node;
938 
939 	// ns 		- global, fs-specific struct for device
940 	// node		- directory related to cookie
941 	// cookie	- current cookie for directory, to free.
942 	//dprintf("fs_free_dir_cookie - ENTER\n");
943 	if (cookie != NULL)
944 		free(cookie);
945 
946 	//dprintf("fs_free_dir_cookie - EXIT\n");
947 	return B_OK;
948 }
949 
950 //	#pragma mark -
951 
952 
953 static status_t
954 iso_std_ops(int32 op, ...)
955 {
956 	switch (op) {
957 		case B_MODULE_INIT:
958 			return B_OK;
959 		case B_MODULE_UNINIT:
960 			return B_OK;
961 		default:
962 			return B_ERROR;
963 	}
964 }
965 
966 
967 static file_system_module_info sISO660FileSystem = {
968 	{
969 		"file_systems/iso9660" B_CURRENT_FS_API_VERSION,
970 		0,
971 		iso_std_ops,
972 	},
973 
974 	"ISO9660 File System",
975 
976 	// scanning
977 	fs_identify_partition,
978 	fs_scan_partition,
979 	fs_free_identify_partition_cookie,
980 	NULL,	// free_partition_content_cookie()
981 
982 	&fs_mount,
983 	&fs_unmount,
984 	&fs_read_fs_stat,
985 	NULL,
986 	NULL,
987 
988 	/* vnode operations */
989 	&fs_walk,
990 	&fs_get_vnode_name,
991 	&fs_read_vnode,
992 	&fs_release_vnode,
993 	NULL, 	// &fs_remove_vnode()
994 
995 	/* VM file access */
996 	NULL, 	// &fs_can_page
997 	NULL,	// &fs_read_pages
998 	NULL, 	// &fs_write_pages
999 
1000 	&fs_get_file_map,
1001 
1002 	NULL, 	// &fs_ioctl
1003 	NULL, 	// &fs_set_flags
1004 	NULL,	// &fs_select
1005 	NULL,	// &fs_deselect
1006 	NULL, 	// &fs_fsync
1007 
1008 	&fs_read_link,
1009 	NULL, 	// &fs_create_symlink,
1010 
1011 	NULL, 	// &fs_link,
1012 	NULL,	// &fs_unlink
1013 	NULL, 	// &fs_rename
1014 
1015 	&fs_access,
1016 	&fs_read_stat,
1017 	NULL, 	// &fs_write_stat
1018 
1019 	/* file operations */
1020 	NULL, 	// &fs_create
1021 	&fs_open,
1022 	&fs_close,
1023 	&fs_free_cookie,
1024 	&fs_read,
1025 	NULL, 	// &fs_write
1026 
1027 	/* directory operations */
1028 	NULL, 	// &fs_create_dir
1029 	NULL, 	// &fs_remove_dir
1030 	&fs_open_dir,
1031 	&fs_close_dir,
1032 	&fs_free_dir_cookie,
1033 	&fs_read_dir,
1034 	&fs_rewind_dir,
1035 
1036 	/* attribute directory operations */
1037 	NULL, 	// &fs_open_attr_dir
1038 	NULL, 	// &fs_close_attr_dir
1039 	NULL,	// &fs_free_attr_dir_cookie
1040 	NULL,	// &fs_read_attr_dir
1041 	NULL,	// &fs_rewind_attr_dir
1042 
1043 	/* attribute operations */
1044 	NULL,	// &fs_create_attr
1045 	NULL, 	// &fs_open_attr
1046 	NULL,	// &fs_close_attr
1047 	NULL,	// &fs_free_attr_cookie
1048 	NULL,	// &fs_read_attr
1049 	NULL,	// &fs_write_attr
1050 
1051 	NULL,	// &fs_read_attr_stat
1052 	NULL,	// &fs_write_attr_stat
1053 	NULL,	// &fs_rename_attr
1054 	NULL,	// &fs_remove_attr
1055 
1056 	/* index directory & index operations */
1057 	NULL,	// &fs_open_index_dir
1058 	NULL,	// &fs_close_index_dir
1059 	NULL,	// &fs_free_index_dir_cookie
1060 	NULL,	// &fs_read_index_dir
1061 	NULL,	// &fs_rewind_index_dir
1062 
1063 	NULL,	// &fs_create_index
1064 	NULL,	// &fs_remove_index
1065 	NULL,	// &fs_stat_index
1066 
1067 	/* query operations */
1068 	NULL,	// &fs_open_query
1069 	NULL,	// &fs_close_query
1070 	NULL,	// &fs_free_query_cookie
1071 	NULL,	// &fs_read_query
1072 	NULL,	// &fs_rewind_query
1073 };
1074 
1075 module_info *modules[] = {
1076 	(module_info *)&sISO660FileSystem,
1077 	NULL,
1078 };
1079 
1080