xref: /haiku/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp (revision 49004dc730f842ea3c162b56dee63696c51d17c7)
121e1553eSJérôme Duval /*
2245aecdaSAxel Dörfler  * Copyright 1999, Be Incorporated.   All Rights Reserved.
3245aecdaSAxel Dörfler  * This file may be used under the terms of the Be Sample Code License.
4245aecdaSAxel Dörfler  *
5245aecdaSAxel Dörfler  * Copyright 2001, pinc Software.  All Rights Reserved.
6245aecdaSAxel Dörfler  *
7245aecdaSAxel Dörfler  * iso9960/multi-session, 1.0.0
821e1553eSJérôme Duval  */
921e1553eSJérôme Duval 
10245aecdaSAxel Dörfler 
1121e1553eSJérôme Duval #include <ctype.h>
1221e1553eSJérôme Duval #include <dirent.h>
135b2c5d03SAxel Dörfler #include <errno.h>
145b2c5d03SAxel Dörfler #include <fcntl.h>
155b2c5d03SAxel Dörfler #include <stdio.h>
165b2c5d03SAxel Dörfler #include <stdlib.h>
175b2c5d03SAxel Dörfler #include <string.h>
1821e1553eSJérôme Duval #include <sys/stat.h>
1921e1553eSJérôme Duval #include <time.h>
205b2c5d03SAxel Dörfler #include <unistd.h>
2121e1553eSJérôme Duval 
2221e1553eSJérôme Duval #include <KernelExport.h>
2321e1553eSJérôme Duval #include <NodeMonitor.h>
2421e1553eSJérôme Duval #include <fs_interface.h>
2521e1553eSJérôme Duval #include <fs_cache.h>
2621e1553eSJérôme Duval 
2721e1553eSJérôme Duval #include <fs_attr.h>
2821e1553eSJérôme Duval #include <fs_info.h>
2921e1553eSJérôme Duval #include <fs_index.h>
3021e1553eSJérôme Duval #include <fs_query.h>
3121e1553eSJérôme Duval #include <fs_volume.h>
3221e1553eSJérôme Duval 
3321e1553eSJérôme Duval #include <util/kernel_cpp.h>
3421e1553eSJérôme Duval 
35dc9a52b9SAxel Dörfler #include "iso9660.h"
365b2c5d03SAxel Dörfler #include "iso9660_identify.h"
375b2c5d03SAxel Dörfler 
385b2c5d03SAxel Dörfler 
395b2c5d03SAxel Dörfler //#define TRACE_ISO9660
405b2c5d03SAxel Dörfler #ifdef TRACE_ISO9660
4121e1553eSJérôme Duval #	define TRACE(x) dprintf x
4221e1553eSJérôme Duval #else
4321e1553eSJérôme Duval #	define TRACE(x) ;
4421e1553eSJérôme Duval #endif
4521e1553eSJérôme Duval 
4621e1553eSJérôme Duval 
4721e1553eSJérôme Duval struct identify_cookie {
4821e1553eSJérôme Duval 	iso9660_info info;
4921e1553eSJérôme Duval };
5021e1553eSJérôme Duval 
510eaadd30SMichael Lotz extern fs_volume_ops gISO9660VolumeOps;
520eaadd30SMichael Lotz extern fs_vnode_ops gISO9660VnodeOps;
530eaadd30SMichael Lotz 
5421e1553eSJérôme Duval 
555b2c5d03SAxel Dörfler //	#pragma mark - Scanning
565b2c5d03SAxel Dörfler 
575b2c5d03SAxel Dörfler 
5821e1553eSJérôme Duval static float
5921e1553eSJérôme Duval fs_identify_partition(int fd, partition_data *partition, void **_cookie)
6021e1553eSJérôme Duval {
615b2c5d03SAxel Dörfler 	iso9660_info *info = new iso9660_info;
6221e1553eSJérôme Duval 
635b2c5d03SAxel Dörfler 	status_t status = iso9660_fs_identify(fd, info);
645b2c5d03SAxel Dörfler 	if (status != B_OK) {
655b2c5d03SAxel Dörfler 		delete info;
665b2c5d03SAxel Dörfler 		return -1;
675b2c5d03SAxel Dörfler 	}
6821e1553eSJérôme Duval 
695b2c5d03SAxel Dörfler 	*_cookie = info;
705b2c5d03SAxel Dörfler 	return 0.6f;
7121e1553eSJérôme Duval }
7221e1553eSJérôme Duval 
7321e1553eSJérôme Duval 
7421e1553eSJérôme Duval static status_t
7521e1553eSJérôme Duval fs_scan_partition(int fd, partition_data *partition, void *_cookie)
7621e1553eSJérôme Duval {
775b2c5d03SAxel Dörfler 	iso9660_info *info = (iso9660_info *)_cookie;
7821e1553eSJérôme Duval 
7921e1553eSJérôme Duval 	partition->status = B_PARTITION_VALID;
8021e1553eSJérôme Duval 	partition->flags |= B_PARTITION_FILE_SYSTEM | B_PARTITION_READ_ONLY ;
8121e1553eSJérôme Duval 	partition->block_size = ISO_PVD_SIZE;
825b2c5d03SAxel Dörfler 	partition->content_size = ISO_PVD_SIZE * info->max_blocks;
835b2c5d03SAxel Dörfler 	partition->content_name = strdup(info->PreferredName());
845b2c5d03SAxel Dörfler 
8521e1553eSJérôme Duval 	if (partition->content_name == NULL)
8621e1553eSJérôme Duval 		return B_NO_MEMORY;
8721e1553eSJérôme Duval 
8821e1553eSJérôme Duval 	return B_OK;
8921e1553eSJérôme Duval }
9021e1553eSJérôme Duval 
9121e1553eSJérôme Duval 
9221e1553eSJérôme Duval static void
9321e1553eSJérôme Duval fs_free_identify_partition_cookie(partition_data *partition, void *_cookie)
9421e1553eSJérôme Duval {
955b2c5d03SAxel Dörfler 	delete (iso9660_info *)_cookie;
9621e1553eSJérôme Duval }
9721e1553eSJérôme Duval 
9821e1553eSJérôme Duval 
99ff99132eSAxel Dörfler //	#pragma mark - FS hooks
100ff99132eSAxel Dörfler 
101ff99132eSAxel Dörfler 
10221e1553eSJérôme Duval static status_t
1030eaadd30SMichael Lotz fs_mount(fs_volume *_volume, const char *device, uint32 flags,
1040eaadd30SMichael Lotz 	const char *args, ino_t *_rootID)
10521e1553eSJérôme Duval {
1065b2c5d03SAxel Dörfler 	bool allowJoliet = true;
107eb097431SAxel Dörfler 	iso9660_volume *volume;
10821e1553eSJérôme Duval 
10921e1553eSJérôme Duval 	// Check for a 'nojoliet' parm
11021e1553eSJérôme Duval 	// all we check for is the existance of 'nojoliet' in the parms.
11121e1553eSJérôme Duval 	if (args != NULL) {
11221e1553eSJérôme Duval 		uint32 i;
11321e1553eSJérôme Duval 		char *spot;
11421e1553eSJérôme Duval 		char *buf = strdup(args);
11521e1553eSJérôme Duval 
11621e1553eSJérôme Duval 		uint32 len = strlen(buf);
11721e1553eSJérôme Duval 		// lower case the parms data
11821e1553eSJérôme Duval 		for (i = 0; i < len + 1; i++)
11921e1553eSJérôme Duval 			buf[i] = tolower(buf[i]);
12021e1553eSJérôme Duval 
12121e1553eSJérôme Duval 		// look for nojoliet
12221e1553eSJérôme Duval 		spot = strstr(buf, "nojoliet");
12321e1553eSJérôme Duval 		if (spot != NULL)
1245b2c5d03SAxel Dörfler 			allowJoliet = false;
12521e1553eSJérôme Duval 
12621e1553eSJérôme Duval 		free(buf);
12721e1553eSJérôme Duval 	}
12821e1553eSJérôme Duval 
12921e1553eSJérôme Duval 	// Try and mount volume as an ISO volume.
130ff99132eSAxel Dörfler 	status_t result = ISOMount(device, O_RDONLY, &volume, allowJoliet);
131ff99132eSAxel Dörfler 	if (result == B_OK) {
13221e1553eSJérôme Duval 		*_rootID = ISO_ROOTNODE_ID;
13321e1553eSJérôme Duval 
1340eaadd30SMichael Lotz 		_volume->private_volume = volume;
1350eaadd30SMichael Lotz 		_volume->ops = &gISO9660VolumeOps;
1360eaadd30SMichael Lotz 		volume->volume = _volume;
1370eaadd30SMichael Lotz 		volume->id = _volume->id;
13821e1553eSJérôme Duval 
1390eaadd30SMichael Lotz 		result = publish_vnode(_volume, *_rootID, &volume->rootDirRec,
140a26c2439SMichael Lotz 			&gISO9660VnodeOps,
141a26c2439SMichael Lotz 			volume->rootDirRec.attr.stat[FS_DATA_FORMAT].st_mode, 0);
142ff99132eSAxel Dörfler 		if (result != B_OK) {
143ff99132eSAxel Dörfler 			block_cache_delete(volume->fBlockCache, false);
144ff99132eSAxel Dörfler 			free(volume);
145ff99132eSAxel Dörfler 			result = B_ERROR;
146ff99132eSAxel Dörfler 		}
14721e1553eSJérôme Duval 	}
14821e1553eSJérôme Duval 	return result;
14921e1553eSJérôme Duval }
15021e1553eSJérôme Duval 
15121e1553eSJérôme Duval 
15221e1553eSJérôme Duval static status_t
1530eaadd30SMichael Lotz fs_unmount(fs_volume *_vol)
15421e1553eSJérôme Duval {
15521e1553eSJérôme Duval 	status_t result = B_NO_ERROR;
156eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
15721e1553eSJérôme Duval 
15821e1553eSJérôme Duval 	TRACE(("fs_unmount - ENTER\n"));
15921e1553eSJérôme Duval 
1609d254f45SJérôme Duval 	// Unlike in BeOS, we need to put the reference to our root node ourselves
1610eaadd30SMichael Lotz 	put_vnode(_vol, ISO_ROOTNODE_ID);
1629d254f45SJérôme Duval 
16321e1553eSJérôme Duval 	block_cache_delete(ns->fBlockCache, false);
16421e1553eSJérôme Duval 	close(ns->fdOfSession);
16521e1553eSJérôme Duval 	result = close(ns->fd);
16621e1553eSJérôme Duval 
16721e1553eSJérôme Duval 	free(ns);
16821e1553eSJérôme Duval 
16921e1553eSJérôme Duval 	TRACE(("fs_unmount - EXIT, result is %s\n", strerror(result)));
17021e1553eSJérôme Duval 	return result;
17121e1553eSJérôme Duval }
17221e1553eSJérôme Duval 
17321e1553eSJérôme Duval 
17421e1553eSJérôme Duval static status_t
1750eaadd30SMichael Lotz fs_read_fs_stat(fs_volume *_vol, struct fs_info *fss)
17621e1553eSJérôme Duval {
177eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
17821e1553eSJérôme Duval 	int i;
17921e1553eSJérôme Duval 
18021e1553eSJérôme Duval 	fss->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
18121e1553eSJérôme Duval 	fss->block_size = ns->logicalBlkSize[FS_DATA_FORMAT];
18221e1553eSJérôme Duval 	fss->io_size = 65536;
18321e1553eSJérôme Duval 	fss->total_blocks = ns->volSpaceSize[FS_DATA_FORMAT];
18421e1553eSJérôme Duval 	fss->free_blocks = 0;
18521e1553eSJérôme Duval 
18621e1553eSJérôme Duval 	strncpy(fss->device_name, ns->devicePath, sizeof(fss->device_name));
18721e1553eSJérôme Duval 
18821e1553eSJérôme Duval 	strncpy(fss->volume_name, ns->volIDString, sizeof(fss->volume_name));
189ff99132eSAxel Dörfler 	for (i = strlen(fss->volume_name) - 1; i >=0 ; i--) {
19021e1553eSJérôme Duval 		if (fss->volume_name[i] != ' ')
19121e1553eSJérôme Duval 			break;
192ff99132eSAxel Dörfler 	}
19321e1553eSJérôme Duval 
19421e1553eSJérôme Duval 	if (i < 0)
19521e1553eSJérôme Duval 		strcpy(fss->volume_name, "UNKNOWN");
19621e1553eSJérôme Duval 	else
19721e1553eSJérôme Duval 		fss->volume_name[i + 1] = 0;
19821e1553eSJérôme Duval 
19921e1553eSJérôme Duval 	strcpy(fss->fsh_name, "iso9660");
200ff99132eSAxel Dörfler 	return B_OK;
20121e1553eSJérôme Duval }
20221e1553eSJérôme Duval 
2033b723f79SJérôme Duval 
2043b723f79SJérôme Duval static status_t
2050eaadd30SMichael Lotz fs_get_vnode_name(fs_volume *_vol, fs_vnode *_node, char *buffer, size_t bufferSize)
2063b723f79SJérôme Duval {
207eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode*)_node->private_node;
2083b723f79SJérôme Duval 
209eb097431SAxel Dörfler 	strlcpy(buffer, node->name, bufferSize);
2103b723f79SJérôme Duval 	return B_OK;
2113b723f79SJérôme Duval }
2123b723f79SJérôme Duval 
2133b723f79SJérôme Duval 
21421e1553eSJérôme Duval static status_t
2150eaadd30SMichael Lotz fs_walk(fs_volume *_vol, fs_vnode *_base, const char *file, ino_t *_vnodeID)
21621e1553eSJérôme Duval {
217eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
218eb097431SAxel Dörfler 	iso9660_inode *baseNode = (iso9660_inode*)_base->private_node;
219eb097431SAxel Dörfler 	iso9660_inode *newNode = NULL;
22021e1553eSJérôme Duval 
221eb097431SAxel Dörfler 	TRACE(("fs_walk - looking for %s in dir file of length %ld\n", file,
22221e1553eSJérôme Duval 		baseNode->dataLen[FS_DATA_FORMAT]));
22321e1553eSJérôme Duval 
22421e1553eSJérôme Duval 	if (strcmp(file, ".") == 0)  {
22521e1553eSJérôme Duval 		// base directory
22621e1553eSJérôme Duval 		TRACE(("fs_walk - found \".\" file.\n"));
22721e1553eSJérôme Duval 		*_vnodeID = baseNode->id;
228*49004dc7SMichael Lotz 		return get_vnode(_vol, *_vnodeID, NULL);
22921e1553eSJérôme Duval 	} else if (strcmp(file, "..") == 0) {
23021e1553eSJérôme Duval 		// parent directory
23121e1553eSJérôme Duval 		TRACE(("fs_walk - found \"..\" file.\n"));
23221e1553eSJérôme Duval 		*_vnodeID = baseNode->parID;
233*49004dc7SMichael Lotz 		return get_vnode(_vol, *_vnodeID, NULL);
234ff99132eSAxel Dörfler 	}
235ff99132eSAxel Dörfler 
23621e1553eSJérôme Duval 	// look up file in the directory
237ff99132eSAxel Dörfler 	uint32 dataLength = baseNode->dataLen[FS_DATA_FORMAT];
238ff99132eSAxel Dörfler 	status_t result = ENOENT;
239eb097431SAxel Dörfler 	size_t totalRead = 0;
240ff99132eSAxel Dörfler 	off_t block = baseNode->startLBN[FS_DATA_FORMAT];
241ff99132eSAxel Dörfler 	bool done = false;
24221e1553eSJérôme Duval 
243ff99132eSAxel Dörfler 	while (totalRead < dataLength && !done) {
24421e1553eSJérôme Duval 		off_t cachedBlock = block;
245eb097431SAxel Dörfler 		char* blockData = (char*)block_cache_get(ns->fBlockCache, block);
24621e1553eSJérôme Duval 		if (blockData != NULL) {
247eb097431SAxel Dörfler 			size_t bytesRead = 0;
24821e1553eSJérôme Duval 			off_t blockBytesRead = 0;
249eb097431SAxel Dörfler 			iso9660_inode node;
25021e1553eSJérôme Duval 			int initResult;
25121e1553eSJérôme Duval 
252eb097431SAxel Dörfler 			TRACE(("fs_walk - read buffer from disk at LBN %Ld into buffer "
253eb097431SAxel Dörfler 				"%p.\n", block, blockData));
25421e1553eSJérôme Duval 
25521e1553eSJérôme Duval 			// Move to the next 2-block set if necessary
25621e1553eSJérôme Duval 			// Don't go over end of buffer, if dir record sits on boundary.
25721e1553eSJérôme Duval 
25821e1553eSJérôme Duval 			while (blockBytesRead < 2 * ns->logicalBlkSize[FS_DATA_FORMAT]
259ff99132eSAxel Dörfler 				&& totalRead + blockBytesRead < dataLength
26021e1553eSJérôme Duval 				&& blockData[0] != 0
261ff99132eSAxel Dörfler 				&& !done) {
262ff99132eSAxel Dörfler 				initResult = InitNode(&node, blockData, &bytesRead,
263ff99132eSAxel Dörfler 					ns->joliet_level);
264eb097431SAxel Dörfler 				TRACE(("fs_walk - InitNode returned %s, filename %s, %lu bytes "
265eb097431SAxel Dörfler 					"read\n", strerror(initResult), node.name,
266eb097431SAxel Dörfler 					bytesRead));
26721e1553eSJérôme Duval 
268ff99132eSAxel Dörfler 				if (initResult == B_OK) {
269eb097431SAxel Dörfler 					if (!strcmp(node.name, file)) {
27021e1553eSJérôme Duval 						TRACE(("fs_walk - success, found vnode at block %Ld, pos %Ld\n", block, blockBytesRead));
271ff99132eSAxel Dörfler 						*_vnodeID = (block << 30)
272ff99132eSAxel Dörfler 							+ (blockBytesRead & 0xffffffff);
27321e1553eSJérôme Duval 						TRACE(("fs_walk - New vnode id is %Ld\n", *_vnodeID));
27421e1553eSJérôme Duval 
2750eaadd30SMichael Lotz 						result = get_vnode(_vol, *_vnodeID,
276*49004dc7SMichael Lotz 							(void **)&newNode);
277ff99132eSAxel Dörfler 						if (result == B_OK) {
27821e1553eSJérôme Duval 							newNode->parID = baseNode->id;
279ff99132eSAxel Dörfler 							done = true;
28021e1553eSJérôme Duval 						}
28121e1553eSJérôme Duval 					} else {
282eb097431SAxel Dörfler 						free(node.name);
28321e1553eSJérôme Duval 						free(node.attr.slName);
28421e1553eSJérôme Duval 					}
28521e1553eSJérôme Duval 				} else {
28621e1553eSJérôme Duval 					result = initResult;
28721e1553eSJérôme Duval 					if (bytesRead == 0)
28821e1553eSJérôme Duval 						done = TRUE;
28921e1553eSJérôme Duval 				}
29021e1553eSJérôme Duval 				blockData += bytesRead;
29121e1553eSJérôme Duval 				blockBytesRead += bytesRead;
29221e1553eSJérôme Duval 
293eb097431SAxel Dörfler 				TRACE(("fs_walk - Adding %lu bytes to blockBytes read (total "
294eb097431SAxel Dörfler 					"%Ld/%lu).\n", bytesRead, blockBytesRead,
295eb097431SAxel Dörfler 					baseNode->dataLen[FS_DATA_FORMAT]));
29621e1553eSJérôme Duval 			}
29721e1553eSJérôme Duval 			totalRead += ns->logicalBlkSize[FS_DATA_FORMAT];
29821e1553eSJérôme Duval 			block++;
29921e1553eSJérôme Duval 
300eb097431SAxel Dörfler 			TRACE(("fs_walk - moving to next block %Ld, total read %lu\n",
301eb097431SAxel Dörfler 				block, totalRead));
30221e1553eSJérôme Duval 			block_cache_put(ns->fBlockCache, cachedBlock);
30321e1553eSJérôme Duval 
30421e1553eSJérôme Duval 		} else
30521e1553eSJérôme Duval 			done = TRUE;
30621e1553eSJérôme Duval 	}
30721e1553eSJérôme Duval 
308ff99132eSAxel Dörfler 	TRACE(("fs_walk - EXIT, result is %s, vnid is %Lu\n",
309ff99132eSAxel Dörfler 		strerror(result), *_vnodeID));
31021e1553eSJérôme Duval 	return result;
31121e1553eSJérôme Duval }
31221e1553eSJérôme Duval 
31321e1553eSJérôme Duval 
31421e1553eSJérôme Duval static status_t
3150eaadd30SMichael Lotz fs_read_vnode(fs_volume *_vol, ino_t vnodeID, fs_vnode *_node,
3160eaadd30SMichael Lotz 	int *_type, uint32 *_flags, bool reenter)
31721e1553eSJérôme Duval {
318eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume*)_vol->private_volume;
319ff99132eSAxel Dörfler 
320eb097431SAxel Dörfler 	iso9660_inode *newNode = (iso9660_inode*)calloc(sizeof(iso9660_inode), 1);
321ff99132eSAxel Dörfler 	if (newNode == NULL)
322ff99132eSAxel Dörfler 		return B_NO_MEMORY;
32321e1553eSJérôme Duval 
324ff99132eSAxel Dörfler 	uint32 pos = vnodeID & 0x3fffffff;
325ff99132eSAxel Dörfler 	uint32 block = vnodeID >> 30;
32621e1553eSJérôme Duval 
327eb097431SAxel Dörfler 	TRACE(("fs_read_vnode - block = %ld, pos = %ld, raw = %Lu node %p\n",
328ff99132eSAxel Dörfler 		block, pos, vnodeID, newNode));
32921e1553eSJérôme Duval 
330d02cde30SMichael Lotz 	if (pos > ns->logicalBlkSize[FS_DATA_FORMAT]) {
331d02cde30SMichael Lotz 		free(newNode);
332ff99132eSAxel Dörfler 		return B_BAD_VALUE;
333d02cde30SMichael Lotz 	}
33421e1553eSJérôme Duval 
335eb097431SAxel Dörfler 	char *data = (char *)block_cache_get(ns->fBlockCache, block);
336ff99132eSAxel Dörfler 	if (data == NULL) {
337ff99132eSAxel Dörfler 		free(newNode);
338ff99132eSAxel Dörfler 		return B_IO_ERROR;
339ff99132eSAxel Dörfler 	}
34021e1553eSJérôme Duval 
341ff99132eSAxel Dörfler 	status_t result = InitNode(newNode, data + pos, NULL, ns->joliet_level);
34221e1553eSJérôme Duval 	block_cache_put(ns->fBlockCache, block);
34321e1553eSJérôme Duval 
344ff99132eSAxel Dörfler 	if (result < B_OK) {
345ff99132eSAxel Dörfler 		free(newNode);
346ff99132eSAxel Dörfler 		return result;
34721e1553eSJérôme Duval 	}
34821e1553eSJérôme Duval 
349ff99132eSAxel Dörfler 	newNode->id = vnodeID;
3500eaadd30SMichael Lotz 	_node->private_node = newNode;
3510eaadd30SMichael Lotz 	_node->ops = &gISO9660VnodeOps;
3520eaadd30SMichael Lotz 	*_type = newNode->attr.stat[FS_DATA_FORMAT].st_mode & ~(S_IWUSR | S_IWGRP | S_IWOTH);
353a26c2439SMichael Lotz 	*_flags = 0;
354ff99132eSAxel Dörfler 
355ff99132eSAxel Dörfler 	if ((newNode->flags & ISO_ISDIR) == 0) {
356ff99132eSAxel Dörfler 		newNode->cache = file_cache_create(ns->id, vnodeID,
3573d268edaSAxel Dörfler 			newNode->dataLen[FS_DATA_FORMAT]);
3585b4cb109SJérôme Duval 	}
3595b4cb109SJérôme Duval 
360ff99132eSAxel Dörfler 	return B_OK;
36121e1553eSJérôme Duval }
36221e1553eSJérôme Duval 
36321e1553eSJérôme Duval 
36421e1553eSJérôme Duval static status_t
365eb097431SAxel Dörfler fs_release_vnode(fs_volume* /*_volume*/, fs_vnode* _node, bool /*reenter*/)
36621e1553eSJérôme Duval {
367eb097431SAxel Dörfler 	iso9660_inode* node = (iso9660_inode*)_node->private_node;
36821e1553eSJérôme Duval 
369eb097431SAxel Dörfler 	TRACE(("fs_release_vnode - ENTER (%p)\n", node));
37021e1553eSJérôme Duval 
37121e1553eSJérôme Duval 	if (node->id != ISO_ROOTNODE_ID) {
372eb097431SAxel Dörfler 		free(node->name);
37321e1553eSJérôme Duval 		free(node->attr.slName);
374eb097431SAxel Dörfler 
3755b4cb109SJérôme Duval 		if (node->cache != NULL)
3765b4cb109SJérôme Duval 			file_cache_delete(node->cache);
37721e1553eSJérôme Duval 
37821e1553eSJérôme Duval 		free(node);
37921e1553eSJérôme Duval 	}
38021e1553eSJérôme Duval 
3815b4cb109SJérôme Duval 	TRACE(("fs_release_vnode - EXIT\n"));
382eb097431SAxel Dörfler 	return B_OK;
38321e1553eSJérôme Duval }
38421e1553eSJérôme Duval 
38521e1553eSJérôme Duval 
38621e1553eSJérôme Duval static status_t
3870eaadd30SMichael Lotz fs_read_pages(fs_volume *_vol, fs_vnode *_node, void * _cookie, off_t pos,
388e6bd90c5SIngo Weinhold 	const iovec *vecs, size_t count, size_t *_numBytes)
3895b4cb109SJérôme Duval {
390eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
391eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
3925b4cb109SJérôme Duval 
393ff99132eSAxel Dörfler 	uint32 fileSize = node->dataLen[FS_DATA_FORMAT];
394ff99132eSAxel Dörfler 	size_t bytesLeft = *_numBytes;
3955b4cb109SJérôme Duval 
396ff99132eSAxel Dörfler 	if (pos >= fileSize) {
397ff99132eSAxel Dörfler 		*_numBytes = 0;
3985b4cb109SJérôme Duval 		return B_OK;
3995b4cb109SJérôme Duval 	}
400ff99132eSAxel Dörfler 	if (pos + bytesLeft > fileSize) {
401ff99132eSAxel Dörfler 		bytesLeft = fileSize - pos;
402ff99132eSAxel Dörfler 		*_numBytes = bytesLeft;
4035b4cb109SJérôme Duval 	}
4045b4cb109SJérôme Duval 
405ff99132eSAxel Dörfler 	file_io_vec fileVec;
406ff99132eSAxel Dörfler 	fileVec.offset = pos + node->startLBN[FS_DATA_FORMAT]
407ff99132eSAxel Dörfler 		* ns->logicalBlkSize[FS_DATA_FORMAT];
408ff99132eSAxel Dörfler 	fileVec.length = bytesLeft;
4095b4cb109SJérôme Duval 
410ff99132eSAxel Dörfler 	uint32 vecIndex = 0;
411ff99132eSAxel Dörfler 	size_t vecOffset = 0;
412ff99132eSAxel Dörfler 	return read_file_io_vec_pages(ns->fd, &fileVec, 1, vecs, count,
413ff99132eSAxel Dörfler 		&vecIndex, &vecOffset, &bytesLeft);
4145b4cb109SJérôme Duval }
4155b4cb109SJérôme Duval 
4165b4cb109SJérôme Duval 
4175b4cb109SJérôme Duval static status_t
4180eaadd30SMichael Lotz fs_read_stat(fs_volume *_vol, fs_vnode *_node, struct stat *st)
41921e1553eSJérôme Duval {
420eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume*)_vol->private_volume;
421eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode*)_node->private_node;
4223b723f79SJérôme Duval 	status_t result = B_NO_ERROR;
42321e1553eSJérôme Duval 	time_t time;
42421e1553eSJérôme Duval 
4253b723f79SJérôme Duval 	TRACE(("fs_read_stat - ENTER\n"));
42621e1553eSJérôme Duval 
42721e1553eSJérôme Duval 	st->st_dev = ns->id;
42821e1553eSJérôme Duval 	st->st_ino = node->id;
42921e1553eSJérôme Duval 	st->st_nlink = node->attr.stat[FS_DATA_FORMAT].st_nlink;
43021e1553eSJérôme Duval 	st->st_uid = node->attr.stat[FS_DATA_FORMAT].st_uid;
43121e1553eSJérôme Duval 	st->st_gid = node->attr.stat[FS_DATA_FORMAT].st_gid;
43221e1553eSJérôme Duval 	st->st_blksize = 65536;
43321e1553eSJérôme Duval 	st->st_mode = node->attr.stat[FS_DATA_FORMAT].st_mode;
43421e1553eSJérôme Duval 
43521e1553eSJérôme Duval 	// Same for file/dir in ISO9660
43621e1553eSJérôme Duval 	st->st_size = node->dataLen[FS_DATA_FORMAT];
4372c348abbSAxel Dörfler 	st->st_blocks = (st->st_size + 511) / 512;
43821e1553eSJérôme Duval 	if (ConvertRecDate(&(node->recordDate), &time) == B_NO_ERROR)
43921e1553eSJérôme Duval 		st->st_ctime = st->st_mtime = st->st_atime = time;
44021e1553eSJérôme Duval 
4413b723f79SJérôme Duval 	TRACE(("fs_read_stat - EXIT, result is %s\n", strerror(result)));
44221e1553eSJérôme Duval 
44321e1553eSJérôme Duval 	return result;
44421e1553eSJérôme Duval }
44521e1553eSJérôme Duval 
44621e1553eSJérôme Duval 
44721e1553eSJérôme Duval static status_t
4480eaadd30SMichael Lotz fs_open(fs_volume *_vol, fs_vnode *_node, int omode, void **cookie)
44921e1553eSJérôme Duval {
45021e1553eSJérôme Duval 	status_t result = B_NO_ERROR;
45121e1553eSJérôme Duval 
4520eaadd30SMichael Lotz 	(void)_vol;
45321e1553eSJérôme Duval 	(void)cookie;
45421e1553eSJérôme Duval 
45521e1553eSJérôme Duval 	// Do not allow any of the write-like open modes to get by
45621e1553eSJérôme Duval 	if ((omode == O_WRONLY) || (omode == O_RDWR))
45721e1553eSJérôme Duval 		result = EROFS;
45821e1553eSJérôme Duval 	else if((omode & O_TRUNC) || (omode & O_CREAT))
45921e1553eSJérôme Duval 		result = EROFS;
46021e1553eSJérôme Duval 
46121e1553eSJérôme Duval 	return result;
46221e1553eSJérôme Duval }
46321e1553eSJérôme Duval 
46421e1553eSJérôme Duval 
46521e1553eSJérôme Duval static status_t
4660eaadd30SMichael Lotz fs_read(fs_volume *_vol, fs_vnode *_node, void *cookie, off_t pos, void *buffer,
467ff99132eSAxel Dörfler 	size_t *_length)
46821e1553eSJérôme Duval {
469eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
47021e1553eSJérôme Duval 
47121e1553eSJérôme Duval 	if (node->flags & ISO_ISDIR)
47221e1553eSJérôme Duval 		return EISDIR;
47321e1553eSJérôme Duval 
474ff99132eSAxel Dörfler 	uint32 fileSize = node->dataLen[FS_DATA_FORMAT];
4755b4cb109SJérôme Duval 
4765b4cb109SJérôme Duval 	// set/check boundaries for pos/length
477ff99132eSAxel Dörfler 	if (pos < 0)
4785b4cb109SJérôme Duval 		return B_BAD_VALUE;
479ff99132eSAxel Dörfler 	if (pos >= fileSize) {
480ff99132eSAxel Dörfler 		*_length = 0;
4815b4cb109SJérôme Duval 		return B_OK;
4825b4cb109SJérôme Duval 	}
483ff99132eSAxel Dörfler 
484ff99132eSAxel Dörfler 	return file_cache_read(node->cache, NULL, pos, buffer, _length);
48521e1553eSJérôme Duval }
48621e1553eSJérôme Duval 
48721e1553eSJérôme Duval 
48821e1553eSJérôme Duval static status_t
4890eaadd30SMichael Lotz fs_close(fs_volume *_vol, fs_vnode *_node, void *cookie)
49021e1553eSJérôme Duval {
4910eaadd30SMichael Lotz 	(void)_vol;
4920eaadd30SMichael Lotz 	(void)_node;
49321e1553eSJérôme Duval 	(void)cookie;
49421e1553eSJérôme Duval 
49521e1553eSJérôme Duval 	return B_OK;
49621e1553eSJérôme Duval }
49721e1553eSJérôme Duval 
498ff99132eSAxel Dörfler 
49921e1553eSJérôme Duval static status_t
5000eaadd30SMichael Lotz fs_free_cookie(fs_volume *_vol, fs_vnode *_node, void *cookie)
50121e1553eSJérôme Duval {
5020eaadd30SMichael Lotz 	(void)_vol;
5030eaadd30SMichael Lotz 	(void)_node;
50421e1553eSJérôme Duval 	(void)cookie;
50521e1553eSJérôme Duval 
50621e1553eSJérôme Duval 	return B_OK;
50721e1553eSJérôme Duval }
50821e1553eSJérôme Duval 
509ff99132eSAxel Dörfler 
51021e1553eSJérôme Duval static status_t
5110eaadd30SMichael Lotz fs_access(fs_volume *_vol, fs_vnode *_node, int mode)
51221e1553eSJérôme Duval {
5130eaadd30SMichael Lotz 	(void)_vol;
5140eaadd30SMichael Lotz 	(void)_node;
51521e1553eSJérôme Duval 	(void)mode;
51621e1553eSJérôme Duval 
51721e1553eSJérôme Duval 	return B_OK;
51821e1553eSJérôme Duval }
51921e1553eSJérôme Duval 
520ff99132eSAxel Dörfler 
52121e1553eSJérôme Duval static status_t
5220eaadd30SMichael Lotz fs_read_link(fs_volume *_vol, fs_vnode *_node, char *buffer, size_t *_bufferSize)
52321e1553eSJérôme Duval {
524eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
52521e1553eSJérôme Duval 
526ff99132eSAxel Dörfler 	if (!S_ISLNK(node->attr.stat[FS_DATA_FORMAT].st_mode))
527ff99132eSAxel Dörfler 		return B_BAD_VALUE;
52821e1553eSJérôme Duval 
52921e1553eSJérôme Duval 	size_t length = strlen(node->attr.slName);
53021e1553eSJérôme Duval 	if (length > *_bufferSize)
5313b723f79SJérôme Duval 		memcpy(buffer, node->attr.slName, *_bufferSize);
5323b723f79SJérôme Duval 	else {
53321e1553eSJérôme Duval 		memcpy(buffer, node->attr.slName, length);
5343b723f79SJérôme Duval 		*_bufferSize = length;
5353b723f79SJérôme Duval 	}
53621e1553eSJérôme Duval 
537ff99132eSAxel Dörfler 	return B_OK;
53821e1553eSJérôme Duval }
53921e1553eSJérôme Duval 
54021e1553eSJérôme Duval 
54121e1553eSJérôme Duval static status_t
542eb097431SAxel Dörfler fs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie)
54321e1553eSJérôme Duval {
544eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
54521e1553eSJérôme Duval 
546eb097431SAxel Dörfler 	TRACE(("fs_open_dir - node is %p\n", node));
54721e1553eSJérôme Duval 
54821e1553eSJérôme Duval 	if (!(node->flags & ISO_ISDIR))
549ff99132eSAxel Dörfler 		return B_NOT_A_DIRECTORY;
55021e1553eSJérôme Duval 
551ff99132eSAxel Dörfler 	dircookie* dirCookie = (dircookie*)malloc(sizeof(dircookie));
552ff99132eSAxel Dörfler 	if (dirCookie == NULL)
553ff99132eSAxel Dörfler 		return B_NO_MEMORY;
554ff99132eSAxel Dörfler 
55521e1553eSJérôme Duval 	dirCookie->startBlock = node->startLBN[FS_DATA_FORMAT];
55621e1553eSJérôme Duval 	dirCookie->block = node->startLBN[FS_DATA_FORMAT];
55721e1553eSJérôme Duval 	dirCookie->totalSize = node->dataLen[FS_DATA_FORMAT];
55821e1553eSJérôme Duval 	dirCookie->pos = 0;
55921e1553eSJérôme Duval 	dirCookie->id = node->id;
560eb097431SAxel Dörfler 	*_cookie = (void*)dirCookie;
56121e1553eSJérôme Duval 
562ff99132eSAxel Dörfler 	return B_OK;
56321e1553eSJérôme Duval }
56421e1553eSJérôme Duval 
56521e1553eSJérôme Duval 
56621e1553eSJérôme Duval static status_t
56744d0dbc8SAxel Dörfler fs_read_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie,
56844d0dbc8SAxel Dörfler 	struct dirent *buffer, size_t bufferSize, uint32 *num)
56921e1553eSJérôme Duval {
570eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
57121e1553eSJérôme Duval 	dircookie *dirCookie = (dircookie *)_cookie;
57221e1553eSJérôme Duval 
5735b4cb109SJérôme Duval 	TRACE(("fs_read_dir - ENTER\n"));
57421e1553eSJérôme Duval 
575ff99132eSAxel Dörfler 	status_t result = ISOReadDirEnt(ns, dirCookie, buffer, bufferSize);
57621e1553eSJérôme Duval 
57721e1553eSJérôme Duval 	// If we succeeded, return 1, the number of dirents we read.
578ff99132eSAxel Dörfler 	if (result == B_OK)
57921e1553eSJérôme Duval 		*num = 1;
58021e1553eSJérôme Duval 	else
58121e1553eSJérôme Duval 		*num = 0;
58221e1553eSJérôme Duval 
58321e1553eSJérôme Duval 	// When you get to the end, don't return an error, just return
58421e1553eSJérôme Duval 	// a zero in *num.
58521e1553eSJérôme Duval 
586eb097431SAxel Dörfler 	if (result == B_ENTRY_NOT_FOUND)
587ff99132eSAxel Dörfler 		result = B_OK;
58821e1553eSJérôme Duval 
5895b4cb109SJérôme Duval 	TRACE(("fs_read_dir - EXIT, result is %s\n", strerror(result)));
59021e1553eSJérôme Duval 	return result;
59121e1553eSJérôme Duval }
59221e1553eSJérôme Duval 
59321e1553eSJérôme Duval 
59421e1553eSJérôme Duval static status_t
5950eaadd30SMichael Lotz fs_rewind_dir(fs_volume *_vol, fs_vnode *_node, void* _cookie)
59621e1553eSJérôme Duval {
59721e1553eSJérôme Duval 	dircookie *cookie = (dircookie*)_cookie;
59821e1553eSJérôme Duval 
59921e1553eSJérôme Duval 	cookie->block = cookie->startBlock;
60021e1553eSJérôme Duval 	cookie->pos = 0;
601ff99132eSAxel Dörfler 	return B_OK;
60221e1553eSJérôme Duval }
60321e1553eSJérôme Duval 
60421e1553eSJérôme Duval 
60521e1553eSJérôme Duval static status_t
6060eaadd30SMichael Lotz fs_close_dir(fs_volume *_vol, fs_vnode *_node, void *cookie)
60721e1553eSJérôme Duval {
60821e1553eSJérôme Duval 	return B_OK;
60921e1553eSJérôme Duval }
61021e1553eSJérôme Duval 
61121e1553eSJérôme Duval 
61221e1553eSJérôme Duval static status_t
6130eaadd30SMichael Lotz fs_free_dir_cookie(fs_volume *_vol, fs_vnode *_node, void *cookie)
61421e1553eSJérôme Duval {
61521e1553eSJérôme Duval 	free(cookie);
61621e1553eSJérôme Duval 	return B_OK;
61721e1553eSJérôme Duval }
61821e1553eSJérôme Duval 
619ff99132eSAxel Dörfler 
62021e1553eSJérôme Duval //	#pragma mark -
62121e1553eSJérôme Duval 
62221e1553eSJérôme Duval 
62321e1553eSJérôme Duval static status_t
62421e1553eSJérôme Duval iso_std_ops(int32 op, ...)
62521e1553eSJérôme Duval {
62621e1553eSJérôme Duval 	switch (op) {
62721e1553eSJérôme Duval 		case B_MODULE_INIT:
62821e1553eSJérôme Duval 		case B_MODULE_UNINIT:
62921e1553eSJérôme Duval 			return B_OK;
63021e1553eSJérôme Duval 		default:
63121e1553eSJérôme Duval 			return B_ERROR;
63221e1553eSJérôme Duval 	}
63321e1553eSJérôme Duval }
63421e1553eSJérôme Duval 
63521e1553eSJérôme Duval 
6360eaadd30SMichael Lotz fs_volume_ops gISO9660VolumeOps = {
6370eaadd30SMichael Lotz 	&fs_unmount,
6380eaadd30SMichael Lotz 	&fs_read_fs_stat,
6390eaadd30SMichael Lotz 	NULL,
6400eaadd30SMichael Lotz 	NULL,
6410eaadd30SMichael Lotz 	&fs_read_vnode,
6420eaadd30SMichael Lotz 
6430eaadd30SMichael Lotz 	/* index and index directory ops */
6440eaadd30SMichael Lotz 	NULL,
6450eaadd30SMichael Lotz 	NULL,
6460eaadd30SMichael Lotz 	NULL,
6470eaadd30SMichael Lotz 	NULL,
6480eaadd30SMichael Lotz 	NULL,
6490eaadd30SMichael Lotz 	NULL,
6500eaadd30SMichael Lotz 	NULL,
6510eaadd30SMichael Lotz 	NULL,
6520eaadd30SMichael Lotz 
6530eaadd30SMichael Lotz 	/* query ops */
6540eaadd30SMichael Lotz 	NULL,
6550eaadd30SMichael Lotz 	NULL,
6560eaadd30SMichael Lotz 	NULL,
6570eaadd30SMichael Lotz 	NULL,
6580eaadd30SMichael Lotz 	NULL,
6590eaadd30SMichael Lotz 
6600eaadd30SMichael Lotz 	/* FS layer ops */
6610eaadd30SMichael Lotz 	NULL,
6620eaadd30SMichael Lotz 	NULL,
6630eaadd30SMichael Lotz };
6640eaadd30SMichael Lotz 
6650eaadd30SMichael Lotz fs_vnode_ops gISO9660VnodeOps = {
6660eaadd30SMichael Lotz 	&fs_walk,
6670eaadd30SMichael Lotz 	&fs_get_vnode_name,
6680eaadd30SMichael Lotz 	&fs_release_vnode,
6690eaadd30SMichael Lotz 	NULL,
6700eaadd30SMichael Lotz 
6710eaadd30SMichael Lotz 	/* vm-related ops */
6720eaadd30SMichael Lotz 	NULL,
6730eaadd30SMichael Lotz 	&fs_read_pages,
6740eaadd30SMichael Lotz 	NULL,
6750eaadd30SMichael Lotz 
676ec598fe4SIngo Weinhold 	NULL,	// io()
677ec598fe4SIngo Weinhold 	NULL,	// cancel_io()
678ec598fe4SIngo Weinhold 
6790eaadd30SMichael Lotz 	/* cache file access */
6800eaadd30SMichael Lotz 	NULL,
6810eaadd30SMichael Lotz 
6820eaadd30SMichael Lotz 	/* common */
6830eaadd30SMichael Lotz 	NULL,
6840eaadd30SMichael Lotz 	NULL,
6850eaadd30SMichael Lotz 	NULL,
6860eaadd30SMichael Lotz 	NULL,
6870eaadd30SMichael Lotz 	NULL,
6880eaadd30SMichael Lotz 	&fs_read_link,
6890eaadd30SMichael Lotz 	NULL,
6900eaadd30SMichael Lotz 	NULL,
6910eaadd30SMichael Lotz 	NULL,
6920eaadd30SMichael Lotz 	NULL,
6930eaadd30SMichael Lotz 	&fs_access,
6940eaadd30SMichael Lotz 	&fs_read_stat,
6950eaadd30SMichael Lotz 	NULL,
6960eaadd30SMichael Lotz 
6970eaadd30SMichael Lotz 	/* file */
6980eaadd30SMichael Lotz 	NULL,
6990eaadd30SMichael Lotz 	&fs_open,
7000eaadd30SMichael Lotz 	&fs_close,
7010eaadd30SMichael Lotz 	&fs_free_cookie,
7020eaadd30SMichael Lotz 	&fs_read,
7030eaadd30SMichael Lotz 	NULL,
7040eaadd30SMichael Lotz 
7050eaadd30SMichael Lotz 	/* dir */
7060eaadd30SMichael Lotz 	NULL,
7070eaadd30SMichael Lotz 	NULL,
7080eaadd30SMichael Lotz 	&fs_open_dir,
7090eaadd30SMichael Lotz 	&fs_close_dir,
7100eaadd30SMichael Lotz 	&fs_free_dir_cookie,
7110eaadd30SMichael Lotz 	&fs_read_dir,
7120eaadd30SMichael Lotz 	&fs_rewind_dir,
7130eaadd30SMichael Lotz 
7140eaadd30SMichael Lotz 	/* attribute directory ops */
7150eaadd30SMichael Lotz 	NULL,
7160eaadd30SMichael Lotz 	NULL,
7170eaadd30SMichael Lotz 	NULL,
7180eaadd30SMichael Lotz 	NULL,
7190eaadd30SMichael Lotz 	NULL,
7200eaadd30SMichael Lotz 
7210eaadd30SMichael Lotz 	/* attribute ops */
7220eaadd30SMichael Lotz 	NULL,
7230eaadd30SMichael Lotz 	NULL,
7240eaadd30SMichael Lotz 	NULL,
7250eaadd30SMichael Lotz 	NULL,
7260eaadd30SMichael Lotz 	NULL,
7270eaadd30SMichael Lotz 	NULL,
7280eaadd30SMichael Lotz 	NULL,
7290eaadd30SMichael Lotz 	NULL,
7300eaadd30SMichael Lotz 	NULL,
7310eaadd30SMichael Lotz 	NULL,
7320eaadd30SMichael Lotz 
7330eaadd30SMichael Lotz 	/* node and FS layer support */
7340eaadd30SMichael Lotz 	NULL,
7350eaadd30SMichael Lotz 	NULL,
7360eaadd30SMichael Lotz };
7370eaadd30SMichael Lotz 
73821e1553eSJérôme Duval static file_system_module_info sISO660FileSystem = {
73921e1553eSJérôme Duval 	{
74021e1553eSJérôme Duval 		"file_systems/iso9660" B_CURRENT_FS_API_VERSION,
74121e1553eSJérôme Duval 		0,
74221e1553eSJérôme Duval 		iso_std_ops,
74321e1553eSJérôme Duval 	},
74421e1553eSJérôme Duval 
7451da9f5ceSAxel Dörfler 	"iso9660",					// short_name
7461da9f5ceSAxel Dörfler 	"ISO9660 File System",		// pretty_name
74776a8ec23SIngo Weinhold 	0,							// DDM flags
74821e1553eSJérôme Duval 
74921e1553eSJérôme Duval 	// scanning
75021e1553eSJérôme Duval 	fs_identify_partition,
75121e1553eSJérôme Duval 	fs_scan_partition,
75221e1553eSJérôme Duval 	fs_free_identify_partition_cookie,
75321e1553eSJérôme Duval 	NULL,	// free_partition_content_cookie()
75421e1553eSJérôme Duval 
75521e1553eSJérôme Duval 	&fs_mount,
7560eaadd30SMichael Lotz 
7570eaadd30SMichael Lotz 	/* capability querying */
7580eaadd30SMichael Lotz 	NULL,
7590eaadd30SMichael Lotz 	NULL,
7600eaadd30SMichael Lotz 	NULL,
7610eaadd30SMichael Lotz 	NULL,
76221e1553eSJérôme Duval 	NULL,
76321e1553eSJérôme Duval 	NULL,
76421e1553eSJérôme Duval 
7650eaadd30SMichael Lotz 	/* shadow partition modifications */
7660eaadd30SMichael Lotz 	NULL,
76721e1553eSJérôme Duval 
7680eaadd30SMichael Lotz 	/* writing */
7690eaadd30SMichael Lotz 	NULL,
7700eaadd30SMichael Lotz 	NULL,
7710eaadd30SMichael Lotz 	NULL,
7720eaadd30SMichael Lotz 	NULL,
7730eaadd30SMichael Lotz 	NULL,
7740eaadd30SMichael Lotz 	NULL,
7750eaadd30SMichael Lotz 	NULL,
77621e1553eSJérôme Duval };
77721e1553eSJérôme Duval 
77821e1553eSJérôme Duval module_info *modules[] = {
77921e1553eSJérôme Duval 	(module_info *)&sISO660FileSystem,
78021e1553eSJérôme Duval 	NULL,
78121e1553eSJérôme Duval };
78221e1553eSJérôme Duval 
783