xref: /haiku/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp (revision 2c348abbf7aca35c66b88e159bdafa41f2a9743f)
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,
1400eaadd30SMichael Lotz 			&gISO9660VnodeOps, volume->rootDirRec.attr.stat[FS_DATA_FORMAT].st_mode, 0);
141ff99132eSAxel Dörfler 		if (result != B_OK) {
142ff99132eSAxel Dörfler 			block_cache_delete(volume->fBlockCache, false);
143ff99132eSAxel Dörfler 			free(volume);
144ff99132eSAxel Dörfler 			result = B_ERROR;
145ff99132eSAxel Dörfler 		}
14621e1553eSJérôme Duval 	}
14721e1553eSJérôme Duval 	return result;
14821e1553eSJérôme Duval }
14921e1553eSJérôme Duval 
15021e1553eSJérôme Duval 
15121e1553eSJérôme Duval static status_t
1520eaadd30SMichael Lotz fs_unmount(fs_volume *_vol)
15321e1553eSJérôme Duval {
15421e1553eSJérôme Duval 	status_t result = B_NO_ERROR;
155eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
15621e1553eSJérôme Duval 
15721e1553eSJérôme Duval 	TRACE(("fs_unmount - ENTER\n"));
15821e1553eSJérôme Duval 
1599d254f45SJérôme Duval 	// Unlike in BeOS, we need to put the reference to our root node ourselves
1600eaadd30SMichael Lotz 	put_vnode(_vol, ISO_ROOTNODE_ID);
1619d254f45SJérôme Duval 
16221e1553eSJérôme Duval 	block_cache_delete(ns->fBlockCache, false);
16321e1553eSJérôme Duval 	close(ns->fdOfSession);
16421e1553eSJérôme Duval 	result = close(ns->fd);
16521e1553eSJérôme Duval 
16621e1553eSJérôme Duval 	free(ns);
16721e1553eSJérôme Duval 
16821e1553eSJérôme Duval 	TRACE(("fs_unmount - EXIT, result is %s\n", strerror(result)));
16921e1553eSJérôme Duval 	return result;
17021e1553eSJérôme Duval }
17121e1553eSJérôme Duval 
17221e1553eSJérôme Duval 
17321e1553eSJérôme Duval static status_t
1740eaadd30SMichael Lotz fs_read_fs_stat(fs_volume *_vol, struct fs_info *fss)
17521e1553eSJérôme Duval {
176eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
17721e1553eSJérôme Duval 	int i;
17821e1553eSJérôme Duval 
17921e1553eSJérôme Duval 	fss->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
18021e1553eSJérôme Duval 	fss->block_size = ns->logicalBlkSize[FS_DATA_FORMAT];
18121e1553eSJérôme Duval 	fss->io_size = 65536;
18221e1553eSJérôme Duval 	fss->total_blocks = ns->volSpaceSize[FS_DATA_FORMAT];
18321e1553eSJérôme Duval 	fss->free_blocks = 0;
18421e1553eSJérôme Duval 
18521e1553eSJérôme Duval 	strncpy(fss->device_name, ns->devicePath, sizeof(fss->device_name));
18621e1553eSJérôme Duval 
18721e1553eSJérôme Duval 	strncpy(fss->volume_name, ns->volIDString, sizeof(fss->volume_name));
188ff99132eSAxel Dörfler 	for (i = strlen(fss->volume_name) - 1; i >=0 ; i--) {
18921e1553eSJérôme Duval 		if (fss->volume_name[i] != ' ')
19021e1553eSJérôme Duval 			break;
191ff99132eSAxel Dörfler 	}
19221e1553eSJérôme Duval 
19321e1553eSJérôme Duval 	if (i < 0)
19421e1553eSJérôme Duval 		strcpy(fss->volume_name, "UNKNOWN");
19521e1553eSJérôme Duval 	else
19621e1553eSJérôme Duval 		fss->volume_name[i + 1] = 0;
19721e1553eSJérôme Duval 
19821e1553eSJérôme Duval 	strcpy(fss->fsh_name, "iso9660");
199ff99132eSAxel Dörfler 	return B_OK;
20021e1553eSJérôme Duval }
20121e1553eSJérôme Duval 
2023b723f79SJérôme Duval 
2033b723f79SJérôme Duval static status_t
2040eaadd30SMichael Lotz fs_get_vnode_name(fs_volume *_vol, fs_vnode *_node, char *buffer, size_t bufferSize)
2053b723f79SJérôme Duval {
206eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode*)_node->private_node;
2073b723f79SJérôme Duval 
208eb097431SAxel Dörfler 	strlcpy(buffer, node->name, bufferSize);
2093b723f79SJérôme Duval 	return B_OK;
2103b723f79SJérôme Duval }
2113b723f79SJérôme Duval 
2123b723f79SJérôme Duval 
21321e1553eSJérôme Duval static status_t
2140eaadd30SMichael Lotz fs_walk(fs_volume *_vol, fs_vnode *_base, const char *file, ino_t *_vnodeID)
21521e1553eSJérôme Duval {
216eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
217eb097431SAxel Dörfler 	iso9660_inode *baseNode = (iso9660_inode*)_base->private_node;
218eb097431SAxel Dörfler 	iso9660_inode *newNode = NULL;
21921e1553eSJérôme Duval 
220eb097431SAxel Dörfler 	TRACE(("fs_walk - looking for %s in dir file of length %ld\n", file,
22121e1553eSJérôme Duval 		baseNode->dataLen[FS_DATA_FORMAT]));
22221e1553eSJérôme Duval 
22321e1553eSJérôme Duval 	if (strcmp(file, ".") == 0)  {
22421e1553eSJérôme Duval 		// base directory
22521e1553eSJérôme Duval 		TRACE(("fs_walk - found \".\" file.\n"));
22621e1553eSJérôme Duval 		*_vnodeID = baseNode->id;
2270eaadd30SMichael Lotz 		return get_vnode(_vol, *_vnodeID, (void **)&newNode);
22821e1553eSJérôme Duval 	} else if (strcmp(file, "..") == 0) {
22921e1553eSJérôme Duval 		// parent directory
23021e1553eSJérôme Duval 		TRACE(("fs_walk - found \"..\" file.\n"));
23121e1553eSJérôme Duval 		*_vnodeID = baseNode->parID;
2320eaadd30SMichael Lotz 		return get_vnode(_vol, *_vnodeID, (void **)&newNode);
233ff99132eSAxel Dörfler 	}
234ff99132eSAxel Dörfler 
23521e1553eSJérôme Duval 	// look up file in the directory
236ff99132eSAxel Dörfler 	uint32 dataLength = baseNode->dataLen[FS_DATA_FORMAT];
237ff99132eSAxel Dörfler 	status_t result = ENOENT;
238eb097431SAxel Dörfler 	size_t totalRead = 0;
239ff99132eSAxel Dörfler 	off_t block = baseNode->startLBN[FS_DATA_FORMAT];
240ff99132eSAxel Dörfler 	bool done = false;
24121e1553eSJérôme Duval 
242ff99132eSAxel Dörfler 	while (totalRead < dataLength && !done) {
24321e1553eSJérôme Duval 		off_t cachedBlock = block;
244eb097431SAxel Dörfler 		char* blockData = (char*)block_cache_get(ns->fBlockCache, block);
24521e1553eSJérôme Duval 		if (blockData != NULL) {
246eb097431SAxel Dörfler 			size_t bytesRead = 0;
24721e1553eSJérôme Duval 			off_t blockBytesRead = 0;
248eb097431SAxel Dörfler 			iso9660_inode node;
24921e1553eSJérôme Duval 			int initResult;
25021e1553eSJérôme Duval 
251eb097431SAxel Dörfler 			TRACE(("fs_walk - read buffer from disk at LBN %Ld into buffer "
252eb097431SAxel Dörfler 				"%p.\n", block, blockData));
25321e1553eSJérôme Duval 
25421e1553eSJérôme Duval 			// Move to the next 2-block set if necessary
25521e1553eSJérôme Duval 			// Don't go over end of buffer, if dir record sits on boundary.
25621e1553eSJérôme Duval 
25721e1553eSJérôme Duval 			while (blockBytesRead < 2 * ns->logicalBlkSize[FS_DATA_FORMAT]
258ff99132eSAxel Dörfler 				&& totalRead + blockBytesRead < dataLength
25921e1553eSJérôme Duval 				&& blockData[0] != 0
260ff99132eSAxel Dörfler 				&& !done) {
261ff99132eSAxel Dörfler 				initResult = InitNode(&node, blockData, &bytesRead,
262ff99132eSAxel Dörfler 					ns->joliet_level);
263eb097431SAxel Dörfler 				TRACE(("fs_walk - InitNode returned %s, filename %s, %lu bytes "
264eb097431SAxel Dörfler 					"read\n", strerror(initResult), node.name,
265eb097431SAxel Dörfler 					bytesRead));
26621e1553eSJérôme Duval 
267ff99132eSAxel Dörfler 				if (initResult == B_OK) {
268eb097431SAxel Dörfler 					if (!strcmp(node.name, file)) {
26921e1553eSJérôme Duval 						TRACE(("fs_walk - success, found vnode at block %Ld, pos %Ld\n", block, blockBytesRead));
270ff99132eSAxel Dörfler 						*_vnodeID = (block << 30)
271ff99132eSAxel Dörfler 							+ (blockBytesRead & 0xffffffff);
27221e1553eSJérôme Duval 						TRACE(("fs_walk - New vnode id is %Ld\n", *_vnodeID));
27321e1553eSJérôme Duval 
2740eaadd30SMichael Lotz 						result = get_vnode(_vol, *_vnodeID,
275ff99132eSAxel Dörfler 							(void **)&newNode);
276ff99132eSAxel Dörfler 						if (result == B_OK) {
27721e1553eSJérôme Duval 							newNode->parID = baseNode->id;
278ff99132eSAxel Dörfler 							done = true;
27921e1553eSJérôme Duval 						}
28021e1553eSJérôme Duval 					} else {
281eb097431SAxel Dörfler 						free(node.name);
28221e1553eSJérôme Duval 						free(node.attr.slName);
28321e1553eSJérôme Duval 					}
28421e1553eSJérôme Duval 				} else {
28521e1553eSJérôme Duval 					result = initResult;
28621e1553eSJérôme Duval 					if (bytesRead == 0)
28721e1553eSJérôme Duval 						done = TRUE;
28821e1553eSJérôme Duval 				}
28921e1553eSJérôme Duval 				blockData += bytesRead;
29021e1553eSJérôme Duval 				blockBytesRead += bytesRead;
29121e1553eSJérôme Duval 
292eb097431SAxel Dörfler 				TRACE(("fs_walk - Adding %lu bytes to blockBytes read (total "
293eb097431SAxel Dörfler 					"%Ld/%lu).\n", bytesRead, blockBytesRead,
294eb097431SAxel Dörfler 					baseNode->dataLen[FS_DATA_FORMAT]));
29521e1553eSJérôme Duval 			}
29621e1553eSJérôme Duval 			totalRead += ns->logicalBlkSize[FS_DATA_FORMAT];
29721e1553eSJérôme Duval 			block++;
29821e1553eSJérôme Duval 
299eb097431SAxel Dörfler 			TRACE(("fs_walk - moving to next block %Ld, total read %lu\n",
300eb097431SAxel Dörfler 				block, totalRead));
30121e1553eSJérôme Duval 			block_cache_put(ns->fBlockCache, cachedBlock);
30221e1553eSJérôme Duval 
30321e1553eSJérôme Duval 		} else
30421e1553eSJérôme Duval 			done = TRUE;
30521e1553eSJérôme Duval 	}
30621e1553eSJérôme Duval 
307ff99132eSAxel Dörfler 	TRACE(("fs_walk - EXIT, result is %s, vnid is %Lu\n",
308ff99132eSAxel Dörfler 		strerror(result), *_vnodeID));
30921e1553eSJérôme Duval 	return result;
31021e1553eSJérôme Duval }
31121e1553eSJérôme Duval 
31221e1553eSJérôme Duval 
31321e1553eSJérôme Duval static status_t
3140eaadd30SMichael Lotz fs_read_vnode(fs_volume *_vol, ino_t vnodeID, fs_vnode *_node,
3150eaadd30SMichael Lotz 	int *_type, uint32 *_flags, bool reenter)
31621e1553eSJérôme Duval {
317eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume*)_vol->private_volume;
318ff99132eSAxel Dörfler 
319eb097431SAxel Dörfler 	iso9660_inode *newNode = (iso9660_inode*)calloc(sizeof(iso9660_inode), 1);
320ff99132eSAxel Dörfler 	if (newNode == NULL)
321ff99132eSAxel Dörfler 		return B_NO_MEMORY;
32221e1553eSJérôme Duval 
323ff99132eSAxel Dörfler 	uint32 pos = vnodeID & 0x3fffffff;
324ff99132eSAxel Dörfler 	uint32 block = vnodeID >> 30;
32521e1553eSJérôme Duval 
326eb097431SAxel Dörfler 	TRACE(("fs_read_vnode - block = %ld, pos = %ld, raw = %Lu node %p\n",
327ff99132eSAxel Dörfler 		block, pos, vnodeID, newNode));
32821e1553eSJérôme Duval 
329d02cde30SMichael Lotz 	if (pos > ns->logicalBlkSize[FS_DATA_FORMAT]) {
330d02cde30SMichael Lotz 		free(newNode);
331ff99132eSAxel Dörfler 		return B_BAD_VALUE;
332d02cde30SMichael Lotz 	}
33321e1553eSJérôme Duval 
334eb097431SAxel Dörfler 	char *data = (char *)block_cache_get(ns->fBlockCache, block);
335ff99132eSAxel Dörfler 	if (data == NULL) {
336ff99132eSAxel Dörfler 		free(newNode);
337ff99132eSAxel Dörfler 		return B_IO_ERROR;
338ff99132eSAxel Dörfler 	}
33921e1553eSJérôme Duval 
340ff99132eSAxel Dörfler 	status_t result = InitNode(newNode, data + pos, NULL, ns->joliet_level);
34121e1553eSJérôme Duval 	block_cache_put(ns->fBlockCache, block);
34221e1553eSJérôme Duval 
343ff99132eSAxel Dörfler 	if (result < B_OK) {
344ff99132eSAxel Dörfler 		free(newNode);
345ff99132eSAxel Dörfler 		return result;
34621e1553eSJérôme Duval 	}
34721e1553eSJérôme Duval 
348ff99132eSAxel Dörfler 	newNode->id = vnodeID;
3490eaadd30SMichael Lotz 	_node->private_node = newNode;
3500eaadd30SMichael Lotz 	_node->ops = &gISO9660VnodeOps;
3510eaadd30SMichael Lotz 	*_type = newNode->attr.stat[FS_DATA_FORMAT].st_mode & ~(S_IWUSR | S_IWGRP | S_IWOTH);
3520eaadd30SMichael Lotz 	*_flags = 0;
353ff99132eSAxel Dörfler 
354ff99132eSAxel Dörfler 	if ((newNode->flags & ISO_ISDIR) == 0) {
355ff99132eSAxel Dörfler 		newNode->cache = file_cache_create(ns->id, vnodeID,
3563d268edaSAxel Dörfler 			newNode->dataLen[FS_DATA_FORMAT]);
3575b4cb109SJérôme Duval 	}
3585b4cb109SJérôme Duval 
359ff99132eSAxel Dörfler 	return B_OK;
36021e1553eSJérôme Duval }
36121e1553eSJérôme Duval 
36221e1553eSJérôme Duval 
36321e1553eSJérôme Duval static status_t
364eb097431SAxel Dörfler fs_release_vnode(fs_volume* /*_volume*/, fs_vnode* _node, bool /*reenter*/)
36521e1553eSJérôme Duval {
366eb097431SAxel Dörfler 	iso9660_inode* node = (iso9660_inode*)_node->private_node;
36721e1553eSJérôme Duval 
368eb097431SAxel Dörfler 	TRACE(("fs_release_vnode - ENTER (%p)\n", node));
36921e1553eSJérôme Duval 
37021e1553eSJérôme Duval 	if (node->id != ISO_ROOTNODE_ID) {
371eb097431SAxel Dörfler 		free(node->name);
37221e1553eSJérôme Duval 		free(node->attr.slName);
373eb097431SAxel Dörfler 
3745b4cb109SJérôme Duval 		if (node->cache != NULL)
3755b4cb109SJérôme Duval 			file_cache_delete(node->cache);
37621e1553eSJérôme Duval 
37721e1553eSJérôme Duval 		free(node);
37821e1553eSJérôme Duval 	}
37921e1553eSJérôme Duval 
3805b4cb109SJérôme Duval 	TRACE(("fs_release_vnode - EXIT\n"));
381eb097431SAxel Dörfler 	return B_OK;
38221e1553eSJérôme Duval }
38321e1553eSJérôme Duval 
38421e1553eSJérôme Duval 
38521e1553eSJérôme Duval static status_t
3860eaadd30SMichael Lotz fs_read_pages(fs_volume *_vol, fs_vnode *_node, void * _cookie, off_t pos,
387e6bd90c5SIngo Weinhold 	const iovec *vecs, size_t count, size_t *_numBytes)
3885b4cb109SJérôme Duval {
389eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
390eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
3915b4cb109SJérôme Duval 
392ff99132eSAxel Dörfler 	uint32 fileSize = node->dataLen[FS_DATA_FORMAT];
393ff99132eSAxel Dörfler 	size_t bytesLeft = *_numBytes;
3945b4cb109SJérôme Duval 
395ff99132eSAxel Dörfler 	if (pos >= fileSize) {
396ff99132eSAxel Dörfler 		*_numBytes = 0;
3975b4cb109SJérôme Duval 		return B_OK;
3985b4cb109SJérôme Duval 	}
399ff99132eSAxel Dörfler 	if (pos + bytesLeft > fileSize) {
400ff99132eSAxel Dörfler 		bytesLeft = fileSize - pos;
401ff99132eSAxel Dörfler 		*_numBytes = bytesLeft;
4025b4cb109SJérôme Duval 	}
4035b4cb109SJérôme Duval 
404ff99132eSAxel Dörfler 	file_io_vec fileVec;
405ff99132eSAxel Dörfler 	fileVec.offset = pos + node->startLBN[FS_DATA_FORMAT]
406ff99132eSAxel Dörfler 		* ns->logicalBlkSize[FS_DATA_FORMAT];
407ff99132eSAxel Dörfler 	fileVec.length = bytesLeft;
4085b4cb109SJérôme Duval 
409ff99132eSAxel Dörfler 	uint32 vecIndex = 0;
410ff99132eSAxel Dörfler 	size_t vecOffset = 0;
411ff99132eSAxel Dörfler 	return read_file_io_vec_pages(ns->fd, &fileVec, 1, vecs, count,
412ff99132eSAxel Dörfler 		&vecIndex, &vecOffset, &bytesLeft);
4135b4cb109SJérôme Duval }
4145b4cb109SJérôme Duval 
4155b4cb109SJérôme Duval 
4165b4cb109SJérôme Duval static status_t
4170eaadd30SMichael Lotz fs_read_stat(fs_volume *_vol, fs_vnode *_node, struct stat *st)
41821e1553eSJérôme Duval {
419eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume*)_vol->private_volume;
420eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode*)_node->private_node;
4213b723f79SJérôme Duval 	status_t result = B_NO_ERROR;
42221e1553eSJérôme Duval 	time_t time;
42321e1553eSJérôme Duval 
4243b723f79SJérôme Duval 	TRACE(("fs_read_stat - ENTER\n"));
42521e1553eSJérôme Duval 
42621e1553eSJérôme Duval 	st->st_dev = ns->id;
42721e1553eSJérôme Duval 	st->st_ino = node->id;
42821e1553eSJérôme Duval 	st->st_nlink = node->attr.stat[FS_DATA_FORMAT].st_nlink;
42921e1553eSJérôme Duval 	st->st_uid = node->attr.stat[FS_DATA_FORMAT].st_uid;
43021e1553eSJérôme Duval 	st->st_gid = node->attr.stat[FS_DATA_FORMAT].st_gid;
43121e1553eSJérôme Duval 	st->st_blksize = 65536;
43221e1553eSJérôme Duval 	st->st_mode = node->attr.stat[FS_DATA_FORMAT].st_mode;
43321e1553eSJérôme Duval 
43421e1553eSJérôme Duval 	// Same for file/dir in ISO9660
43521e1553eSJérôme Duval 	st->st_size = node->dataLen[FS_DATA_FORMAT];
436*2c348abbSAxel Dörfler 	st->st_blocks = (st->st_size + 511) / 512;
43721e1553eSJérôme Duval 	if (ConvertRecDate(&(node->recordDate), &time) == B_NO_ERROR)
43821e1553eSJérôme Duval 		st->st_ctime = st->st_mtime = st->st_atime = time;
43921e1553eSJérôme Duval 
4403b723f79SJérôme Duval 	TRACE(("fs_read_stat - EXIT, result is %s\n", strerror(result)));
44121e1553eSJérôme Duval 
44221e1553eSJérôme Duval 	return result;
44321e1553eSJérôme Duval }
44421e1553eSJérôme Duval 
44521e1553eSJérôme Duval 
44621e1553eSJérôme Duval static status_t
4470eaadd30SMichael Lotz fs_open(fs_volume *_vol, fs_vnode *_node, int omode, void **cookie)
44821e1553eSJérôme Duval {
44921e1553eSJérôme Duval 	status_t result = B_NO_ERROR;
45021e1553eSJérôme Duval 
4510eaadd30SMichael Lotz 	(void)_vol;
45221e1553eSJérôme Duval 	(void)cookie;
45321e1553eSJérôme Duval 
45421e1553eSJérôme Duval 	// Do not allow any of the write-like open modes to get by
45521e1553eSJérôme Duval 	if ((omode == O_WRONLY) || (omode == O_RDWR))
45621e1553eSJérôme Duval 		result = EROFS;
45721e1553eSJérôme Duval 	else if((omode & O_TRUNC) || (omode & O_CREAT))
45821e1553eSJérôme Duval 		result = EROFS;
45921e1553eSJérôme Duval 
46021e1553eSJérôme Duval 	return result;
46121e1553eSJérôme Duval }
46221e1553eSJérôme Duval 
46321e1553eSJérôme Duval 
46421e1553eSJérôme Duval static status_t
4650eaadd30SMichael Lotz fs_read(fs_volume *_vol, fs_vnode *_node, void *cookie, off_t pos, void *buffer,
466ff99132eSAxel Dörfler 	size_t *_length)
46721e1553eSJérôme Duval {
468eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
46921e1553eSJérôme Duval 
47021e1553eSJérôme Duval 	if (node->flags & ISO_ISDIR)
47121e1553eSJérôme Duval 		return EISDIR;
47221e1553eSJérôme Duval 
473ff99132eSAxel Dörfler 	uint32 fileSize = node->dataLen[FS_DATA_FORMAT];
4745b4cb109SJérôme Duval 
4755b4cb109SJérôme Duval 	// set/check boundaries for pos/length
476ff99132eSAxel Dörfler 	if (pos < 0)
4775b4cb109SJérôme Duval 		return B_BAD_VALUE;
478ff99132eSAxel Dörfler 	if (pos >= fileSize) {
479ff99132eSAxel Dörfler 		*_length = 0;
4805b4cb109SJérôme Duval 		return B_OK;
4815b4cb109SJérôme Duval 	}
482ff99132eSAxel Dörfler 
483ff99132eSAxel Dörfler 	return file_cache_read(node->cache, NULL, pos, buffer, _length);
48421e1553eSJérôme Duval }
48521e1553eSJérôme Duval 
48621e1553eSJérôme Duval 
48721e1553eSJérôme Duval static status_t
4880eaadd30SMichael Lotz fs_close(fs_volume *_vol, fs_vnode *_node, void *cookie)
48921e1553eSJérôme Duval {
4900eaadd30SMichael Lotz 	(void)_vol;
4910eaadd30SMichael Lotz 	(void)_node;
49221e1553eSJérôme Duval 	(void)cookie;
49321e1553eSJérôme Duval 
49421e1553eSJérôme Duval 	return B_OK;
49521e1553eSJérôme Duval }
49621e1553eSJérôme Duval 
497ff99132eSAxel Dörfler 
49821e1553eSJérôme Duval static status_t
4990eaadd30SMichael Lotz fs_free_cookie(fs_volume *_vol, fs_vnode *_node, void *cookie)
50021e1553eSJérôme Duval {
5010eaadd30SMichael Lotz 	(void)_vol;
5020eaadd30SMichael Lotz 	(void)_node;
50321e1553eSJérôme Duval 	(void)cookie;
50421e1553eSJérôme Duval 
50521e1553eSJérôme Duval 	return B_OK;
50621e1553eSJérôme Duval }
50721e1553eSJérôme Duval 
508ff99132eSAxel Dörfler 
50921e1553eSJérôme Duval static status_t
5100eaadd30SMichael Lotz fs_access(fs_volume *_vol, fs_vnode *_node, int mode)
51121e1553eSJérôme Duval {
5120eaadd30SMichael Lotz 	(void)_vol;
5130eaadd30SMichael Lotz 	(void)_node;
51421e1553eSJérôme Duval 	(void)mode;
51521e1553eSJérôme Duval 
51621e1553eSJérôme Duval 	return B_OK;
51721e1553eSJérôme Duval }
51821e1553eSJérôme Duval 
519ff99132eSAxel Dörfler 
52021e1553eSJérôme Duval static status_t
5210eaadd30SMichael Lotz fs_read_link(fs_volume *_vol, fs_vnode *_node, char *buffer, size_t *_bufferSize)
52221e1553eSJérôme Duval {
523eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
52421e1553eSJérôme Duval 
525ff99132eSAxel Dörfler 	if (!S_ISLNK(node->attr.stat[FS_DATA_FORMAT].st_mode))
526ff99132eSAxel Dörfler 		return B_BAD_VALUE;
52721e1553eSJérôme Duval 
52821e1553eSJérôme Duval 	size_t length = strlen(node->attr.slName);
52921e1553eSJérôme Duval 	if (length > *_bufferSize)
5303b723f79SJérôme Duval 		memcpy(buffer, node->attr.slName, *_bufferSize);
5313b723f79SJérôme Duval 	else {
53221e1553eSJérôme Duval 		memcpy(buffer, node->attr.slName, length);
5333b723f79SJérôme Duval 		*_bufferSize = length;
5343b723f79SJérôme Duval 	}
53521e1553eSJérôme Duval 
536ff99132eSAxel Dörfler 	return B_OK;
53721e1553eSJérôme Duval }
53821e1553eSJérôme Duval 
53921e1553eSJérôme Duval 
54021e1553eSJérôme Duval static status_t
541eb097431SAxel Dörfler fs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie)
54221e1553eSJérôme Duval {
543eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
54421e1553eSJérôme Duval 
545eb097431SAxel Dörfler 	TRACE(("fs_open_dir - node is %p\n", node));
54621e1553eSJérôme Duval 
54721e1553eSJérôme Duval 	if (!(node->flags & ISO_ISDIR))
548ff99132eSAxel Dörfler 		return B_NOT_A_DIRECTORY;
54921e1553eSJérôme Duval 
550ff99132eSAxel Dörfler 	dircookie* dirCookie = (dircookie*)malloc(sizeof(dircookie));
551ff99132eSAxel Dörfler 	if (dirCookie == NULL)
552ff99132eSAxel Dörfler 		return B_NO_MEMORY;
553ff99132eSAxel Dörfler 
55421e1553eSJérôme Duval 	dirCookie->startBlock = node->startLBN[FS_DATA_FORMAT];
55521e1553eSJérôme Duval 	dirCookie->block = node->startLBN[FS_DATA_FORMAT];
55621e1553eSJérôme Duval 	dirCookie->totalSize = node->dataLen[FS_DATA_FORMAT];
55721e1553eSJérôme Duval 	dirCookie->pos = 0;
55821e1553eSJérôme Duval 	dirCookie->id = node->id;
559eb097431SAxel Dörfler 	*_cookie = (void*)dirCookie;
56021e1553eSJérôme Duval 
561ff99132eSAxel Dörfler 	return B_OK;
56221e1553eSJérôme Duval }
56321e1553eSJérôme Duval 
56421e1553eSJérôme Duval 
56521e1553eSJérôme Duval static status_t
56644d0dbc8SAxel Dörfler fs_read_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie,
56744d0dbc8SAxel Dörfler 	struct dirent *buffer, size_t bufferSize, uint32 *num)
56821e1553eSJérôme Duval {
569eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
57021e1553eSJérôme Duval 	dircookie *dirCookie = (dircookie *)_cookie;
57121e1553eSJérôme Duval 
5725b4cb109SJérôme Duval 	TRACE(("fs_read_dir - ENTER\n"));
57321e1553eSJérôme Duval 
574ff99132eSAxel Dörfler 	status_t result = ISOReadDirEnt(ns, dirCookie, buffer, bufferSize);
57521e1553eSJérôme Duval 
57621e1553eSJérôme Duval 	// If we succeeded, return 1, the number of dirents we read.
577ff99132eSAxel Dörfler 	if (result == B_OK)
57821e1553eSJérôme Duval 		*num = 1;
57921e1553eSJérôme Duval 	else
58021e1553eSJérôme Duval 		*num = 0;
58121e1553eSJérôme Duval 
58221e1553eSJérôme Duval 	// When you get to the end, don't return an error, just return
58321e1553eSJérôme Duval 	// a zero in *num.
58421e1553eSJérôme Duval 
585eb097431SAxel Dörfler 	if (result == B_ENTRY_NOT_FOUND)
586ff99132eSAxel Dörfler 		result = B_OK;
58721e1553eSJérôme Duval 
5885b4cb109SJérôme Duval 	TRACE(("fs_read_dir - EXIT, result is %s\n", strerror(result)));
58921e1553eSJérôme Duval 	return result;
59021e1553eSJérôme Duval }
59121e1553eSJérôme Duval 
59221e1553eSJérôme Duval 
59321e1553eSJérôme Duval static status_t
5940eaadd30SMichael Lotz fs_rewind_dir(fs_volume *_vol, fs_vnode *_node, void* _cookie)
59521e1553eSJérôme Duval {
59621e1553eSJérôme Duval 	dircookie *cookie = (dircookie*)_cookie;
59721e1553eSJérôme Duval 
59821e1553eSJérôme Duval 	cookie->block = cookie->startBlock;
59921e1553eSJérôme Duval 	cookie->pos = 0;
600ff99132eSAxel Dörfler 	return B_OK;
60121e1553eSJérôme Duval }
60221e1553eSJérôme Duval 
60321e1553eSJérôme Duval 
60421e1553eSJérôme Duval static status_t
6050eaadd30SMichael Lotz fs_close_dir(fs_volume *_vol, fs_vnode *_node, void *cookie)
60621e1553eSJérôme Duval {
60721e1553eSJérôme Duval 	return B_OK;
60821e1553eSJérôme Duval }
60921e1553eSJérôme Duval 
61021e1553eSJérôme Duval 
61121e1553eSJérôme Duval static status_t
6120eaadd30SMichael Lotz fs_free_dir_cookie(fs_volume *_vol, fs_vnode *_node, void *cookie)
61321e1553eSJérôme Duval {
61421e1553eSJérôme Duval 	free(cookie);
61521e1553eSJérôme Duval 	return B_OK;
61621e1553eSJérôme Duval }
61721e1553eSJérôme Duval 
618ff99132eSAxel Dörfler 
61921e1553eSJérôme Duval //	#pragma mark -
62021e1553eSJérôme Duval 
62121e1553eSJérôme Duval 
62221e1553eSJérôme Duval static status_t
62321e1553eSJérôme Duval iso_std_ops(int32 op, ...)
62421e1553eSJérôme Duval {
62521e1553eSJérôme Duval 	switch (op) {
62621e1553eSJérôme Duval 		case B_MODULE_INIT:
62721e1553eSJérôme Duval 		case B_MODULE_UNINIT:
62821e1553eSJérôme Duval 			return B_OK;
62921e1553eSJérôme Duval 		default:
63021e1553eSJérôme Duval 			return B_ERROR;
63121e1553eSJérôme Duval 	}
63221e1553eSJérôme Duval }
63321e1553eSJérôme Duval 
63421e1553eSJérôme Duval 
6350eaadd30SMichael Lotz fs_volume_ops gISO9660VolumeOps = {
6360eaadd30SMichael Lotz 	&fs_unmount,
6370eaadd30SMichael Lotz 	&fs_read_fs_stat,
6380eaadd30SMichael Lotz 	NULL,
6390eaadd30SMichael Lotz 	NULL,
6400eaadd30SMichael Lotz 	&fs_read_vnode,
6410eaadd30SMichael Lotz 
6420eaadd30SMichael Lotz 	/* index and index directory ops */
6430eaadd30SMichael Lotz 	NULL,
6440eaadd30SMichael Lotz 	NULL,
6450eaadd30SMichael Lotz 	NULL,
6460eaadd30SMichael Lotz 	NULL,
6470eaadd30SMichael Lotz 	NULL,
6480eaadd30SMichael Lotz 	NULL,
6490eaadd30SMichael Lotz 	NULL,
6500eaadd30SMichael Lotz 	NULL,
6510eaadd30SMichael Lotz 
6520eaadd30SMichael Lotz 	/* query ops */
6530eaadd30SMichael Lotz 	NULL,
6540eaadd30SMichael Lotz 	NULL,
6550eaadd30SMichael Lotz 	NULL,
6560eaadd30SMichael Lotz 	NULL,
6570eaadd30SMichael Lotz 	NULL,
6580eaadd30SMichael Lotz 
6590eaadd30SMichael Lotz 	/* FS layer ops */
6600eaadd30SMichael Lotz 	NULL,
6610eaadd30SMichael Lotz 	NULL,
6620eaadd30SMichael Lotz };
6630eaadd30SMichael Lotz 
6640eaadd30SMichael Lotz fs_vnode_ops gISO9660VnodeOps = {
6650eaadd30SMichael Lotz 	&fs_walk,
6660eaadd30SMichael Lotz 	&fs_get_vnode_name,
6670eaadd30SMichael Lotz 	&fs_release_vnode,
6680eaadd30SMichael Lotz 	NULL,
6690eaadd30SMichael Lotz 
6700eaadd30SMichael Lotz 	/* vm-related ops */
6710eaadd30SMichael Lotz 	NULL,
6720eaadd30SMichael Lotz 	&fs_read_pages,
6730eaadd30SMichael Lotz 	NULL,
6740eaadd30SMichael Lotz 
675ec598fe4SIngo Weinhold 	NULL,	// io()
676ec598fe4SIngo Weinhold 	NULL,	// cancel_io()
677ec598fe4SIngo Weinhold 
6780eaadd30SMichael Lotz 	/* cache file access */
6790eaadd30SMichael Lotz 	NULL,
6800eaadd30SMichael Lotz 
6810eaadd30SMichael Lotz 	/* common */
6820eaadd30SMichael Lotz 	NULL,
6830eaadd30SMichael Lotz 	NULL,
6840eaadd30SMichael Lotz 	NULL,
6850eaadd30SMichael Lotz 	NULL,
6860eaadd30SMichael Lotz 	NULL,
6870eaadd30SMichael Lotz 	&fs_read_link,
6880eaadd30SMichael Lotz 	NULL,
6890eaadd30SMichael Lotz 	NULL,
6900eaadd30SMichael Lotz 	NULL,
6910eaadd30SMichael Lotz 	NULL,
6920eaadd30SMichael Lotz 	&fs_access,
6930eaadd30SMichael Lotz 	&fs_read_stat,
6940eaadd30SMichael Lotz 	NULL,
6950eaadd30SMichael Lotz 
6960eaadd30SMichael Lotz 	/* file */
6970eaadd30SMichael Lotz 	NULL,
6980eaadd30SMichael Lotz 	&fs_open,
6990eaadd30SMichael Lotz 	&fs_close,
7000eaadd30SMichael Lotz 	&fs_free_cookie,
7010eaadd30SMichael Lotz 	&fs_read,
7020eaadd30SMichael Lotz 	NULL,
7030eaadd30SMichael Lotz 
7040eaadd30SMichael Lotz 	/* dir */
7050eaadd30SMichael Lotz 	NULL,
7060eaadd30SMichael Lotz 	NULL,
7070eaadd30SMichael Lotz 	&fs_open_dir,
7080eaadd30SMichael Lotz 	&fs_close_dir,
7090eaadd30SMichael Lotz 	&fs_free_dir_cookie,
7100eaadd30SMichael Lotz 	&fs_read_dir,
7110eaadd30SMichael Lotz 	&fs_rewind_dir,
7120eaadd30SMichael Lotz 
7130eaadd30SMichael Lotz 	/* attribute directory ops */
7140eaadd30SMichael Lotz 	NULL,
7150eaadd30SMichael Lotz 	NULL,
7160eaadd30SMichael Lotz 	NULL,
7170eaadd30SMichael Lotz 	NULL,
7180eaadd30SMichael Lotz 	NULL,
7190eaadd30SMichael Lotz 
7200eaadd30SMichael Lotz 	/* attribute ops */
7210eaadd30SMichael Lotz 	NULL,
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 
7320eaadd30SMichael Lotz 	/* node and FS layer support */
7330eaadd30SMichael Lotz 	NULL,
7340eaadd30SMichael Lotz 	NULL,
7350eaadd30SMichael Lotz };
7360eaadd30SMichael Lotz 
73721e1553eSJérôme Duval static file_system_module_info sISO660FileSystem = {
73821e1553eSJérôme Duval 	{
73921e1553eSJérôme Duval 		"file_systems/iso9660" B_CURRENT_FS_API_VERSION,
74021e1553eSJérôme Duval 		0,
74121e1553eSJérôme Duval 		iso_std_ops,
74221e1553eSJérôme Duval 	},
74321e1553eSJérôme Duval 
7441da9f5ceSAxel Dörfler 	"iso9660",					// short_name
7451da9f5ceSAxel Dörfler 	"ISO9660 File System",		// pretty_name
74676a8ec23SIngo Weinhold 	0,							// DDM flags
74721e1553eSJérôme Duval 
74821e1553eSJérôme Duval 	// scanning
74921e1553eSJérôme Duval 	fs_identify_partition,
75021e1553eSJérôme Duval 	fs_scan_partition,
75121e1553eSJérôme Duval 	fs_free_identify_partition_cookie,
75221e1553eSJérôme Duval 	NULL,	// free_partition_content_cookie()
75321e1553eSJérôme Duval 
75421e1553eSJérôme Duval 	&fs_mount,
7550eaadd30SMichael Lotz 
7560eaadd30SMichael Lotz 	/* capability querying */
7570eaadd30SMichael Lotz 	NULL,
7580eaadd30SMichael Lotz 	NULL,
7590eaadd30SMichael Lotz 	NULL,
7600eaadd30SMichael Lotz 	NULL,
76121e1553eSJérôme Duval 	NULL,
76221e1553eSJérôme Duval 	NULL,
76321e1553eSJérôme Duval 
7640eaadd30SMichael Lotz 	/* shadow partition modifications */
7650eaadd30SMichael Lotz 	NULL,
76621e1553eSJérôme Duval 
7670eaadd30SMichael Lotz 	/* writing */
7680eaadd30SMichael Lotz 	NULL,
7690eaadd30SMichael Lotz 	NULL,
7700eaadd30SMichael Lotz 	NULL,
7710eaadd30SMichael Lotz 	NULL,
7720eaadd30SMichael Lotz 	NULL,
7730eaadd30SMichael Lotz 	NULL,
7740eaadd30SMichael Lotz 	NULL,
77521e1553eSJérôme Duval };
77621e1553eSJérôme Duval 
77721e1553eSJérôme Duval module_info *modules[] = {
77821e1553eSJérôme Duval 	(module_info *)&sISO660FileSystem,
77921e1553eSJérôme Duval 	NULL,
78021e1553eSJérôme Duval };
78121e1553eSJérôme Duval 
782