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