xref: /haiku/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp (revision 2a64cb1138cb25680860aedaa89e9e74e462cc81)
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>
12f47bff08SAxel Dörfler 
13f47bff08SAxel Dörfler #ifndef FS_SHELL
1421e1553eSJérôme Duval #	include <dirent.h>
155b2c5d03SAxel Dörfler #	include <errno.h>
165b2c5d03SAxel Dörfler #	include <fcntl.h>
175b2c5d03SAxel Dörfler #	include <stdio.h>
185b2c5d03SAxel Dörfler #	include <stdlib.h>
195b2c5d03SAxel Dörfler #	include <string.h>
2021e1553eSJérôme Duval #	include <sys/stat.h>
2121e1553eSJérôme Duval #	include <time.h>
225b2c5d03SAxel Dörfler #	include <unistd.h>
2321e1553eSJérôme Duval 
2421e1553eSJérôme Duval #	include <KernelExport.h>
2521e1553eSJérôme Duval #	include <NodeMonitor.h>
2621e1553eSJérôme Duval #	include <fs_interface.h>
2721e1553eSJérôme Duval #	include <fs_cache.h>
2821e1553eSJérôme Duval 
2921e1553eSJérôme Duval #	include <fs_attr.h>
3021e1553eSJérôme Duval #	include <fs_info.h>
3121e1553eSJérôme Duval #	include <fs_index.h>
3221e1553eSJérôme Duval #	include <fs_query.h>
3321e1553eSJérôme Duval #	include <fs_volume.h>
3421e1553eSJérôme Duval 
3521e1553eSJérôme Duval #	include <util/kernel_cpp.h>
36f47bff08SAxel Dörfler #endif
3721e1553eSJérôme Duval 
38dc9a52b9SAxel Dörfler #include "iso9660.h"
395b2c5d03SAxel Dörfler #include "iso9660_identify.h"
405b2c5d03SAxel Dörfler 
415b2c5d03SAxel Dörfler 
425b2c5d03SAxel Dörfler //#define TRACE_ISO9660
435b2c5d03SAxel Dörfler #ifdef TRACE_ISO9660
4421e1553eSJérôme Duval #	define TRACE(x) dprintf x
4521e1553eSJérôme Duval #else
4621e1553eSJérôme Duval #	define TRACE(x) ;
4721e1553eSJérôme Duval #endif
4821e1553eSJérôme Duval 
4921e1553eSJérôme Duval 
5021e1553eSJérôme Duval struct identify_cookie {
5121e1553eSJérôme Duval 	iso9660_info info;
5221e1553eSJérôme Duval };
5321e1553eSJérôme Duval 
540eaadd30SMichael Lotz extern fs_volume_ops gISO9660VolumeOps;
550eaadd30SMichael Lotz extern fs_vnode_ops gISO9660VnodeOps;
560eaadd30SMichael Lotz 
5721e1553eSJérôme Duval 
585b2c5d03SAxel Dörfler //	#pragma mark - Scanning
595b2c5d03SAxel Dörfler 
605b2c5d03SAxel Dörfler 
6121e1553eSJérôme Duval static float
6221e1553eSJérôme Duval fs_identify_partition(int fd, partition_data *partition, void **_cookie)
6321e1553eSJérôme Duval {
645b2c5d03SAxel Dörfler 	iso9660_info *info = new iso9660_info;
6521e1553eSJérôme Duval 
665b2c5d03SAxel Dörfler 	status_t status = iso9660_fs_identify(fd, info);
675b2c5d03SAxel Dörfler 	if (status != B_OK) {
685b2c5d03SAxel Dörfler 		delete info;
695b2c5d03SAxel Dörfler 		return -1;
705b2c5d03SAxel Dörfler 	}
7121e1553eSJérôme Duval 
725b2c5d03SAxel Dörfler 	*_cookie = info;
735b2c5d03SAxel Dörfler 	return 0.6f;
7421e1553eSJérôme Duval }
7521e1553eSJérôme Duval 
7621e1553eSJérôme Duval 
7721e1553eSJérôme Duval static status_t
7821e1553eSJérôme Duval fs_scan_partition(int fd, partition_data *partition, void *_cookie)
7921e1553eSJérôme Duval {
805b2c5d03SAxel Dörfler 	iso9660_info *info = (iso9660_info *)_cookie;
8121e1553eSJérôme Duval 
8221e1553eSJérôme Duval 	partition->status = B_PARTITION_VALID;
8321e1553eSJérôme Duval 	partition->flags |= B_PARTITION_FILE_SYSTEM | B_PARTITION_READ_ONLY ;
8421e1553eSJérôme Duval 	partition->block_size = ISO_PVD_SIZE;
855b2c5d03SAxel Dörfler 	partition->content_size = ISO_PVD_SIZE * info->max_blocks;
865b2c5d03SAxel Dörfler 	partition->content_name = strdup(info->PreferredName());
875b2c5d03SAxel Dörfler 
8821e1553eSJérôme Duval 	if (partition->content_name == NULL)
8921e1553eSJérôme Duval 		return B_NO_MEMORY;
9021e1553eSJérôme Duval 
9121e1553eSJérôme Duval 	return B_OK;
9221e1553eSJérôme Duval }
9321e1553eSJérôme Duval 
9421e1553eSJérôme Duval 
9521e1553eSJérôme Duval static void
9621e1553eSJérôme Duval fs_free_identify_partition_cookie(partition_data *partition, void *_cookie)
9721e1553eSJérôme Duval {
985b2c5d03SAxel Dörfler 	delete (iso9660_info *)_cookie;
9921e1553eSJérôme Duval }
10021e1553eSJérôme Duval 
10121e1553eSJérôme Duval 
102ff99132eSAxel Dörfler //	#pragma mark - FS hooks
103ff99132eSAxel Dörfler 
104ff99132eSAxel Dörfler 
10521e1553eSJérôme Duval static status_t
1060eaadd30SMichael Lotz fs_mount(fs_volume *_volume, const char *device, uint32 flags,
1070eaadd30SMichael Lotz 	const char *args, ino_t *_rootID)
10821e1553eSJérôme Duval {
1095b2c5d03SAxel Dörfler 	bool allowJoliet = true;
110eb097431SAxel Dörfler 	iso9660_volume *volume;
11121e1553eSJérôme Duval 
11221e1553eSJérôme Duval 	// Check for a 'nojoliet' parm
11321e1553eSJérôme Duval 	// all we check for is the existance of 'nojoliet' in the parms.
11421e1553eSJérôme Duval 	if (args != NULL) {
11521e1553eSJérôme Duval 		uint32 i;
11621e1553eSJérôme Duval 		char *spot;
11721e1553eSJérôme Duval 		char *buf = strdup(args);
11821e1553eSJérôme Duval 
11921e1553eSJérôme Duval 		uint32 len = strlen(buf);
12021e1553eSJérôme Duval 		// lower case the parms data
12121e1553eSJérôme Duval 		for (i = 0; i < len + 1; i++)
12221e1553eSJérôme Duval 			buf[i] = tolower(buf[i]);
12321e1553eSJérôme Duval 
12421e1553eSJérôme Duval 		// look for nojoliet
12521e1553eSJérôme Duval 		spot = strstr(buf, "nojoliet");
12621e1553eSJérôme Duval 		if (spot != NULL)
1275b2c5d03SAxel Dörfler 			allowJoliet = false;
12821e1553eSJérôme Duval 
12921e1553eSJérôme Duval 		free(buf);
13021e1553eSJérôme Duval 	}
13121e1553eSJérôme Duval 
13221e1553eSJérôme Duval 	// Try and mount volume as an ISO volume.
133ff99132eSAxel Dörfler 	status_t result = ISOMount(device, O_RDONLY, &volume, allowJoliet);
134ff99132eSAxel Dörfler 	if (result == B_OK) {
13521e1553eSJérôme Duval 		*_rootID = ISO_ROOTNODE_ID;
13621e1553eSJérôme Duval 
1370eaadd30SMichael Lotz 		_volume->private_volume = volume;
1380eaadd30SMichael Lotz 		_volume->ops = &gISO9660VolumeOps;
1390eaadd30SMichael Lotz 		volume->volume = _volume;
1400eaadd30SMichael Lotz 		volume->id = _volume->id;
14121e1553eSJérôme Duval 
1420eaadd30SMichael Lotz 		result = publish_vnode(_volume, *_rootID, &volume->rootDirRec,
143a26c2439SMichael Lotz 			&gISO9660VnodeOps,
144a26c2439SMichael Lotz 			volume->rootDirRec.attr.stat[FS_DATA_FORMAT].st_mode, 0);
145ff99132eSAxel Dörfler 		if (result != B_OK) {
146ff99132eSAxel Dörfler 			block_cache_delete(volume->fBlockCache, false);
147ff99132eSAxel Dörfler 			free(volume);
148ff99132eSAxel Dörfler 			result = B_ERROR;
149ff99132eSAxel Dörfler 		}
15021e1553eSJérôme Duval 	}
15121e1553eSJérôme Duval 	return result;
15221e1553eSJérôme Duval }
15321e1553eSJérôme Duval 
15421e1553eSJérôme Duval 
15521e1553eSJérôme Duval static status_t
1560eaadd30SMichael Lotz fs_unmount(fs_volume *_vol)
15721e1553eSJérôme Duval {
15821e1553eSJérôme Duval 	status_t result = B_NO_ERROR;
159eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
16021e1553eSJérôme Duval 
16121e1553eSJérôme Duval 	TRACE(("fs_unmount - ENTER\n"));
16221e1553eSJérôme Duval 
1639d254f45SJérôme Duval 	// Unlike in BeOS, we need to put the reference to our root node ourselves
1640eaadd30SMichael Lotz 	put_vnode(_vol, ISO_ROOTNODE_ID);
1659d254f45SJérôme Duval 
16621e1553eSJérôme Duval 	block_cache_delete(ns->fBlockCache, false);
16721e1553eSJérôme Duval 	close(ns->fdOfSession);
16821e1553eSJérôme Duval 	result = close(ns->fd);
16921e1553eSJérôme Duval 
17021e1553eSJérôme Duval 	free(ns);
17121e1553eSJérôme Duval 
17221e1553eSJérôme Duval 	TRACE(("fs_unmount - EXIT, result is %s\n", strerror(result)));
17321e1553eSJérôme Duval 	return result;
17421e1553eSJérôme Duval }
17521e1553eSJérôme Duval 
17621e1553eSJérôme Duval 
17721e1553eSJérôme Duval static status_t
1780eaadd30SMichael Lotz fs_read_fs_stat(fs_volume *_vol, struct fs_info *fss)
17921e1553eSJérôme Duval {
180eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
18121e1553eSJérôme Duval 	int i;
18221e1553eSJérôme Duval 
18321e1553eSJérôme Duval 	fss->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
18421e1553eSJérôme Duval 	fss->block_size = ns->logicalBlkSize[FS_DATA_FORMAT];
18521e1553eSJérôme Duval 	fss->io_size = 65536;
18621e1553eSJérôme Duval 	fss->total_blocks = ns->volSpaceSize[FS_DATA_FORMAT];
18721e1553eSJérôme Duval 	fss->free_blocks = 0;
18821e1553eSJérôme Duval 
18921e1553eSJérôme Duval 	strncpy(fss->device_name, ns->devicePath, sizeof(fss->device_name));
19021e1553eSJérôme Duval 
19121e1553eSJérôme Duval 	strncpy(fss->volume_name, ns->volIDString, sizeof(fss->volume_name));
192ff99132eSAxel Dörfler 	for (i = strlen(fss->volume_name) - 1; i >=0 ; i--) {
19321e1553eSJérôme Duval 		if (fss->volume_name[i] != ' ')
19421e1553eSJérôme Duval 			break;
195ff99132eSAxel Dörfler 	}
19621e1553eSJérôme Duval 
19721e1553eSJérôme Duval 	if (i < 0)
19821e1553eSJérôme Duval 		strcpy(fss->volume_name, "UNKNOWN");
19921e1553eSJérôme Duval 	else
20021e1553eSJérôme Duval 		fss->volume_name[i + 1] = 0;
20121e1553eSJérôme Duval 
20221e1553eSJérôme Duval 	strcpy(fss->fsh_name, "iso9660");
203ff99132eSAxel Dörfler 	return B_OK;
20421e1553eSJérôme Duval }
20521e1553eSJérôme Duval 
2063b723f79SJérôme Duval 
2073b723f79SJérôme Duval static status_t
2080eaadd30SMichael Lotz fs_get_vnode_name(fs_volume *_vol, fs_vnode *_node, char *buffer, size_t bufferSize)
2093b723f79SJérôme Duval {
210eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode*)_node->private_node;
2113b723f79SJérôme Duval 
212eb097431SAxel Dörfler 	strlcpy(buffer, node->name, bufferSize);
2133b723f79SJérôme Duval 	return B_OK;
2143b723f79SJérôme Duval }
2153b723f79SJérôme Duval 
2163b723f79SJérôme Duval 
21721e1553eSJérôme Duval static status_t
2180eaadd30SMichael Lotz fs_walk(fs_volume *_vol, fs_vnode *_base, const char *file, ino_t *_vnodeID)
21921e1553eSJérôme Duval {
220eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
221eb097431SAxel Dörfler 	iso9660_inode *baseNode = (iso9660_inode*)_base->private_node;
222eb097431SAxel Dörfler 	iso9660_inode *newNode = NULL;
22321e1553eSJérôme Duval 
224eb097431SAxel Dörfler 	TRACE(("fs_walk - looking for %s in dir file of length %ld\n", file,
22521e1553eSJérôme Duval 		baseNode->dataLen[FS_DATA_FORMAT]));
22621e1553eSJérôme Duval 
22721e1553eSJérôme Duval 	if (strcmp(file, ".") == 0)  {
22821e1553eSJérôme Duval 		// base directory
22921e1553eSJérôme Duval 		TRACE(("fs_walk - found \".\" file.\n"));
23021e1553eSJérôme Duval 		*_vnodeID = baseNode->id;
23149004dc7SMichael Lotz 		return get_vnode(_vol, *_vnodeID, NULL);
23221e1553eSJérôme Duval 	} else if (strcmp(file, "..") == 0) {
23321e1553eSJérôme Duval 		// parent directory
23421e1553eSJérôme Duval 		TRACE(("fs_walk - found \"..\" file.\n"));
23521e1553eSJérôme Duval 		*_vnodeID = baseNode->parID;
23649004dc7SMichael Lotz 		return get_vnode(_vol, *_vnodeID, NULL);
237ff99132eSAxel Dörfler 	}
238ff99132eSAxel Dörfler 
23921e1553eSJérôme Duval 	// look up file in the directory
240ff99132eSAxel Dörfler 	uint32 dataLength = baseNode->dataLen[FS_DATA_FORMAT];
241ff99132eSAxel Dörfler 	status_t result = ENOENT;
242eb097431SAxel Dörfler 	size_t totalRead = 0;
243ff99132eSAxel Dörfler 	off_t block = baseNode->startLBN[FS_DATA_FORMAT];
244ff99132eSAxel Dörfler 	bool done = false;
24521e1553eSJérôme Duval 
246ff99132eSAxel Dörfler 	while (totalRead < dataLength && !done) {
24721e1553eSJérôme Duval 		off_t cachedBlock = block;
248eb097431SAxel Dörfler 		char* blockData = (char*)block_cache_get(ns->fBlockCache, block);
249*2a64cb11SAxel Dörfler 		if (blockData == NULL)
250*2a64cb11SAxel Dörfler 			break;
251*2a64cb11SAxel Dörfler 
252eb097431SAxel Dörfler 		size_t bytesRead = 0;
25321e1553eSJérôme Duval 		off_t blockBytesRead = 0;
254eb097431SAxel Dörfler 		iso9660_inode node;
25521e1553eSJérôme Duval 		int initResult;
25621e1553eSJérôme Duval 
257eb097431SAxel Dörfler 		TRACE(("fs_walk - read buffer from disk at LBN %Ld into buffer "
258eb097431SAxel Dörfler 			"%p.\n", block, blockData));
25921e1553eSJérôme Duval 
260*2a64cb11SAxel Dörfler 		// Move to the next block if necessary
26121e1553eSJérôme Duval 		// Don't go over end of buffer, if dir record sits on boundary.
26221e1553eSJérôme Duval 
263*2a64cb11SAxel Dörfler 		while (blockBytesRead < ns->logicalBlkSize[FS_DATA_FORMAT]
264ff99132eSAxel Dörfler 			&& totalRead + blockBytesRead < dataLength
26521e1553eSJérôme Duval 			&& blockData[0] != 0
266ff99132eSAxel Dörfler 			&& !done) {
267ff99132eSAxel Dörfler 			initResult = InitNode(&node, blockData, &bytesRead,
268ff99132eSAxel Dörfler 				ns->joliet_level);
269eb097431SAxel Dörfler 			TRACE(("fs_walk - InitNode returned %s, filename %s, %lu bytes "
270eb097431SAxel Dörfler 				"read\n", strerror(initResult), node.name,
271eb097431SAxel Dörfler 				bytesRead));
27221e1553eSJérôme Duval 
273ff99132eSAxel Dörfler 			if (initResult == B_OK) {
274eb097431SAxel Dörfler 				if (!strcmp(node.name, file)) {
275*2a64cb11SAxel Dörfler 					TRACE(("fs_walk - success, found vnode at block %Ld, pos "
276*2a64cb11SAxel Dörfler 						"%Ld\n", block, blockBytesRead));
277*2a64cb11SAxel Dörfler 
278*2a64cb11SAxel Dörfler 					*_vnodeID = (block << 30) + (blockBytesRead & 0xffffffff);
27921e1553eSJérôme Duval 					TRACE(("fs_walk - New vnode id is %Ld\n", *_vnodeID));
28021e1553eSJérôme Duval 
2810eaadd30SMichael Lotz 					result = get_vnode(_vol, *_vnodeID,
28249004dc7SMichael Lotz 						(void **)&newNode);
283ff99132eSAxel Dörfler 					if (result == B_OK) {
28421e1553eSJérôme Duval 						newNode->parID = baseNode->id;
285ff99132eSAxel Dörfler 						done = true;
28621e1553eSJérôme Duval 					}
28721e1553eSJérôme Duval 				} else {
288eb097431SAxel Dörfler 					free(node.name);
28921e1553eSJérôme Duval 					free(node.attr.slName);
29021e1553eSJérôme Duval 				}
29121e1553eSJérôme Duval 			} else {
29221e1553eSJérôme Duval 				result = initResult;
29321e1553eSJérôme Duval 				if (bytesRead == 0)
294f47bff08SAxel Dörfler 					done = true;
29521e1553eSJérôme Duval 			}
29621e1553eSJérôme Duval 			blockData += bytesRead;
29721e1553eSJérôme Duval 			blockBytesRead += bytesRead;
29821e1553eSJérôme Duval 
299eb097431SAxel Dörfler 			TRACE(("fs_walk - Adding %lu bytes to blockBytes read (total "
300eb097431SAxel Dörfler 				"%Ld/%lu).\n", bytesRead, blockBytesRead,
301eb097431SAxel Dörfler 				baseNode->dataLen[FS_DATA_FORMAT]));
30221e1553eSJérôme Duval 		}
30321e1553eSJérôme Duval 		totalRead += ns->logicalBlkSize[FS_DATA_FORMAT];
30421e1553eSJérôme Duval 		block++;
30521e1553eSJérôme Duval 
306eb097431SAxel Dörfler 		TRACE(("fs_walk - moving to next block %Ld, total read %lu\n",
307eb097431SAxel Dörfler 			block, totalRead));
30821e1553eSJérôme Duval 		block_cache_put(ns->fBlockCache, cachedBlock);
30921e1553eSJérôme Duval 	}
31021e1553eSJérôme Duval 
311ff99132eSAxel Dörfler 	TRACE(("fs_walk - EXIT, result is %s, vnid is %Lu\n",
312ff99132eSAxel Dörfler 		strerror(result), *_vnodeID));
31321e1553eSJérôme Duval 	return result;
31421e1553eSJérôme Duval }
31521e1553eSJérôme Duval 
31621e1553eSJérôme Duval 
31721e1553eSJérôme Duval static status_t
3180eaadd30SMichael Lotz fs_read_vnode(fs_volume *_vol, ino_t vnodeID, fs_vnode *_node,
3190eaadd30SMichael Lotz 	int *_type, uint32 *_flags, bool reenter)
32021e1553eSJérôme Duval {
321eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume*)_vol->private_volume;
322ff99132eSAxel Dörfler 
323eb097431SAxel Dörfler 	iso9660_inode *newNode = (iso9660_inode*)calloc(sizeof(iso9660_inode), 1);
324ff99132eSAxel Dörfler 	if (newNode == NULL)
325ff99132eSAxel Dörfler 		return B_NO_MEMORY;
32621e1553eSJérôme Duval 
327ff99132eSAxel Dörfler 	uint32 pos = vnodeID & 0x3fffffff;
328ff99132eSAxel Dörfler 	uint32 block = vnodeID >> 30;
32921e1553eSJérôme Duval 
330eb097431SAxel Dörfler 	TRACE(("fs_read_vnode - block = %ld, pos = %ld, raw = %Lu node %p\n",
331ff99132eSAxel Dörfler 		block, pos, vnodeID, newNode));
33221e1553eSJérôme Duval 
333d02cde30SMichael Lotz 	if (pos > ns->logicalBlkSize[FS_DATA_FORMAT]) {
334d02cde30SMichael Lotz 		free(newNode);
335ff99132eSAxel Dörfler 		return B_BAD_VALUE;
336d02cde30SMichael Lotz 	}
33721e1553eSJérôme Duval 
338eb097431SAxel Dörfler 	char *data = (char *)block_cache_get(ns->fBlockCache, block);
339ff99132eSAxel Dörfler 	if (data == NULL) {
340ff99132eSAxel Dörfler 		free(newNode);
341ff99132eSAxel Dörfler 		return B_IO_ERROR;
342ff99132eSAxel Dörfler 	}
34321e1553eSJérôme Duval 
344ff99132eSAxel Dörfler 	status_t result = InitNode(newNode, data + pos, NULL, ns->joliet_level);
34521e1553eSJérôme Duval 	block_cache_put(ns->fBlockCache, block);
34621e1553eSJérôme Duval 
347ff99132eSAxel Dörfler 	if (result < B_OK) {
348ff99132eSAxel Dörfler 		free(newNode);
349ff99132eSAxel Dörfler 		return result;
35021e1553eSJérôme Duval 	}
35121e1553eSJérôme Duval 
352ff99132eSAxel Dörfler 	newNode->id = vnodeID;
3530eaadd30SMichael Lotz 	_node->private_node = newNode;
3540eaadd30SMichael Lotz 	_node->ops = &gISO9660VnodeOps;
3550eaadd30SMichael Lotz 	*_type = newNode->attr.stat[FS_DATA_FORMAT].st_mode & ~(S_IWUSR | S_IWGRP | S_IWOTH);
356a26c2439SMichael Lotz 	*_flags = 0;
357ff99132eSAxel Dörfler 
358ff99132eSAxel Dörfler 	if ((newNode->flags & ISO_ISDIR) == 0) {
359ff99132eSAxel Dörfler 		newNode->cache = file_cache_create(ns->id, vnodeID,
3603d268edaSAxel Dörfler 			newNode->dataLen[FS_DATA_FORMAT]);
3615b4cb109SJérôme Duval 	}
3625b4cb109SJérôme Duval 
363ff99132eSAxel Dörfler 	return B_OK;
36421e1553eSJérôme Duval }
36521e1553eSJérôme Duval 
36621e1553eSJérôme Duval 
36721e1553eSJérôme Duval static status_t
368eb097431SAxel Dörfler fs_release_vnode(fs_volume* /*_volume*/, fs_vnode* _node, bool /*reenter*/)
36921e1553eSJérôme Duval {
370eb097431SAxel Dörfler 	iso9660_inode* node = (iso9660_inode*)_node->private_node;
37121e1553eSJérôme Duval 
372eb097431SAxel Dörfler 	TRACE(("fs_release_vnode - ENTER (%p)\n", node));
37321e1553eSJérôme Duval 
37421e1553eSJérôme Duval 	if (node->id != ISO_ROOTNODE_ID) {
375eb097431SAxel Dörfler 		free(node->name);
37621e1553eSJérôme Duval 		free(node->attr.slName);
377eb097431SAxel Dörfler 
3785b4cb109SJérôme Duval 		if (node->cache != NULL)
3795b4cb109SJérôme Duval 			file_cache_delete(node->cache);
38021e1553eSJérôme Duval 
38121e1553eSJérôme Duval 		free(node);
38221e1553eSJérôme Duval 	}
38321e1553eSJérôme Duval 
3845b4cb109SJérôme Duval 	TRACE(("fs_release_vnode - EXIT\n"));
385eb097431SAxel Dörfler 	return B_OK;
38621e1553eSJérôme Duval }
38721e1553eSJérôme Duval 
38821e1553eSJérôme Duval 
38921e1553eSJérôme Duval static status_t
3900eaadd30SMichael Lotz fs_read_pages(fs_volume *_vol, fs_vnode *_node, void * _cookie, off_t pos,
391e6bd90c5SIngo Weinhold 	const iovec *vecs, size_t count, size_t *_numBytes)
3925b4cb109SJérôme Duval {
393eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
394eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
3955b4cb109SJérôme Duval 
396ff99132eSAxel Dörfler 	uint32 fileSize = node->dataLen[FS_DATA_FORMAT];
397ff99132eSAxel Dörfler 	size_t bytesLeft = *_numBytes;
3985b4cb109SJérôme Duval 
399ff99132eSAxel Dörfler 	if (pos >= fileSize) {
400ff99132eSAxel Dörfler 		*_numBytes = 0;
4015b4cb109SJérôme Duval 		return B_OK;
4025b4cb109SJérôme Duval 	}
403ff99132eSAxel Dörfler 	if (pos + bytesLeft > fileSize) {
404ff99132eSAxel Dörfler 		bytesLeft = fileSize - pos;
405ff99132eSAxel Dörfler 		*_numBytes = bytesLeft;
4065b4cb109SJérôme Duval 	}
4075b4cb109SJérôme Duval 
408ff99132eSAxel Dörfler 	file_io_vec fileVec;
409ff99132eSAxel Dörfler 	fileVec.offset = pos + node->startLBN[FS_DATA_FORMAT]
410ff99132eSAxel Dörfler 		* ns->logicalBlkSize[FS_DATA_FORMAT];
411ff99132eSAxel Dörfler 	fileVec.length = bytesLeft;
4125b4cb109SJérôme Duval 
413ff99132eSAxel Dörfler 	uint32 vecIndex = 0;
414ff99132eSAxel Dörfler 	size_t vecOffset = 0;
415ff99132eSAxel Dörfler 	return read_file_io_vec_pages(ns->fd, &fileVec, 1, vecs, count,
416ff99132eSAxel Dörfler 		&vecIndex, &vecOffset, &bytesLeft);
4175b4cb109SJérôme Duval }
4185b4cb109SJérôme Duval 
4195b4cb109SJérôme Duval 
4205b4cb109SJérôme Duval static status_t
4210eaadd30SMichael Lotz fs_read_stat(fs_volume *_vol, fs_vnode *_node, struct stat *st)
42221e1553eSJérôme Duval {
423eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume*)_vol->private_volume;
424eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode*)_node->private_node;
4253b723f79SJérôme Duval 	status_t result = B_NO_ERROR;
42621e1553eSJérôme Duval 	time_t time;
42721e1553eSJérôme Duval 
4283b723f79SJérôme Duval 	TRACE(("fs_read_stat - ENTER\n"));
42921e1553eSJérôme Duval 
43021e1553eSJérôme Duval 	st->st_dev = ns->id;
43121e1553eSJérôme Duval 	st->st_ino = node->id;
43221e1553eSJérôme Duval 	st->st_nlink = node->attr.stat[FS_DATA_FORMAT].st_nlink;
43321e1553eSJérôme Duval 	st->st_uid = node->attr.stat[FS_DATA_FORMAT].st_uid;
43421e1553eSJérôme Duval 	st->st_gid = node->attr.stat[FS_DATA_FORMAT].st_gid;
43521e1553eSJérôme Duval 	st->st_blksize = 65536;
43621e1553eSJérôme Duval 	st->st_mode = node->attr.stat[FS_DATA_FORMAT].st_mode;
43721e1553eSJérôme Duval 
43821e1553eSJérôme Duval 	// Same for file/dir in ISO9660
43921e1553eSJérôme Duval 	st->st_size = node->dataLen[FS_DATA_FORMAT];
4402c348abbSAxel Dörfler 	st->st_blocks = (st->st_size + 511) / 512;
44121e1553eSJérôme Duval 	if (ConvertRecDate(&(node->recordDate), &time) == B_NO_ERROR)
44221e1553eSJérôme Duval 		st->st_ctime = st->st_mtime = st->st_atime = time;
44321e1553eSJérôme Duval 
4443b723f79SJérôme Duval 	TRACE(("fs_read_stat - EXIT, result is %s\n", strerror(result)));
44521e1553eSJérôme Duval 
44621e1553eSJérôme Duval 	return result;
44721e1553eSJérôme Duval }
44821e1553eSJérôme Duval 
44921e1553eSJérôme Duval 
45021e1553eSJérôme Duval static status_t
4510eaadd30SMichael Lotz fs_open(fs_volume *_vol, fs_vnode *_node, int omode, void **cookie)
45221e1553eSJérôme Duval {
45321e1553eSJérôme Duval 	status_t result = B_NO_ERROR;
45421e1553eSJérôme Duval 
4550eaadd30SMichael Lotz 	(void)_vol;
45621e1553eSJérôme Duval 	(void)cookie;
45721e1553eSJérôme Duval 
45821e1553eSJérôme Duval 	// Do not allow any of the write-like open modes to get by
45921e1553eSJérôme Duval 	if ((omode == O_WRONLY) || (omode == O_RDWR))
46021e1553eSJérôme Duval 		result = EROFS;
46121e1553eSJérôme Duval 	else if((omode & O_TRUNC) || (omode & O_CREAT))
46221e1553eSJérôme Duval 		result = EROFS;
46321e1553eSJérôme Duval 
46421e1553eSJérôme Duval 	return result;
46521e1553eSJérôme Duval }
46621e1553eSJérôme Duval 
46721e1553eSJérôme Duval 
46821e1553eSJérôme Duval static status_t
4690eaadd30SMichael Lotz fs_read(fs_volume *_vol, fs_vnode *_node, void *cookie, off_t pos, void *buffer,
470ff99132eSAxel Dörfler 	size_t *_length)
47121e1553eSJérôme Duval {
472eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
47321e1553eSJérôme Duval 
47421e1553eSJérôme Duval 	if (node->flags & ISO_ISDIR)
47521e1553eSJérôme Duval 		return EISDIR;
47621e1553eSJérôme Duval 
477ff99132eSAxel Dörfler 	uint32 fileSize = node->dataLen[FS_DATA_FORMAT];
4785b4cb109SJérôme Duval 
4795b4cb109SJérôme Duval 	// set/check boundaries for pos/length
480ff99132eSAxel Dörfler 	if (pos < 0)
4815b4cb109SJérôme Duval 		return B_BAD_VALUE;
482ff99132eSAxel Dörfler 	if (pos >= fileSize) {
483ff99132eSAxel Dörfler 		*_length = 0;
4845b4cb109SJérôme Duval 		return B_OK;
4855b4cb109SJérôme Duval 	}
486ff99132eSAxel Dörfler 
487ff99132eSAxel Dörfler 	return file_cache_read(node->cache, NULL, pos, buffer, _length);
48821e1553eSJérôme Duval }
48921e1553eSJérôme Duval 
49021e1553eSJérôme Duval 
49121e1553eSJérôme Duval static status_t
4920eaadd30SMichael Lotz fs_close(fs_volume *_vol, fs_vnode *_node, void *cookie)
49321e1553eSJérôme Duval {
4940eaadd30SMichael Lotz 	(void)_vol;
4950eaadd30SMichael Lotz 	(void)_node;
49621e1553eSJérôme Duval 	(void)cookie;
49721e1553eSJérôme Duval 
49821e1553eSJérôme Duval 	return B_OK;
49921e1553eSJérôme Duval }
50021e1553eSJérôme Duval 
501ff99132eSAxel Dörfler 
50221e1553eSJérôme Duval static status_t
5030eaadd30SMichael Lotz fs_free_cookie(fs_volume *_vol, fs_vnode *_node, void *cookie)
50421e1553eSJérôme Duval {
5050eaadd30SMichael Lotz 	(void)_vol;
5060eaadd30SMichael Lotz 	(void)_node;
50721e1553eSJérôme Duval 	(void)cookie;
50821e1553eSJérôme Duval 
50921e1553eSJérôme Duval 	return B_OK;
51021e1553eSJérôme Duval }
51121e1553eSJérôme Duval 
512ff99132eSAxel Dörfler 
51321e1553eSJérôme Duval static status_t
5140eaadd30SMichael Lotz fs_access(fs_volume *_vol, fs_vnode *_node, int mode)
51521e1553eSJérôme Duval {
5160eaadd30SMichael Lotz 	(void)_vol;
5170eaadd30SMichael Lotz 	(void)_node;
51821e1553eSJérôme Duval 	(void)mode;
51921e1553eSJérôme Duval 
52021e1553eSJérôme Duval 	return B_OK;
52121e1553eSJérôme Duval }
52221e1553eSJérôme Duval 
523ff99132eSAxel Dörfler 
52421e1553eSJérôme Duval static status_t
5250eaadd30SMichael Lotz fs_read_link(fs_volume *_vol, fs_vnode *_node, char *buffer, size_t *_bufferSize)
52621e1553eSJérôme Duval {
527eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
52821e1553eSJérôme Duval 
529ff99132eSAxel Dörfler 	if (!S_ISLNK(node->attr.stat[FS_DATA_FORMAT].st_mode))
530ff99132eSAxel Dörfler 		return B_BAD_VALUE;
53121e1553eSJérôme Duval 
53221e1553eSJérôme Duval 	size_t length = strlen(node->attr.slName);
53321e1553eSJérôme Duval 	if (length > *_bufferSize)
5343b723f79SJérôme Duval 		memcpy(buffer, node->attr.slName, *_bufferSize);
5353b723f79SJérôme Duval 	else {
53621e1553eSJérôme Duval 		memcpy(buffer, node->attr.slName, length);
5373b723f79SJérôme Duval 		*_bufferSize = length;
5383b723f79SJérôme Duval 	}
53921e1553eSJérôme Duval 
540ff99132eSAxel Dörfler 	return B_OK;
54121e1553eSJérôme Duval }
54221e1553eSJérôme Duval 
54321e1553eSJérôme Duval 
54421e1553eSJérôme Duval static status_t
545eb097431SAxel Dörfler fs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie)
54621e1553eSJérôme Duval {
547eb097431SAxel Dörfler 	iso9660_inode *node = (iso9660_inode *)_node->private_node;
54821e1553eSJérôme Duval 
549eb097431SAxel Dörfler 	TRACE(("fs_open_dir - node is %p\n", node));
55021e1553eSJérôme Duval 
55121e1553eSJérôme Duval 	if (!(node->flags & ISO_ISDIR))
552ff99132eSAxel Dörfler 		return B_NOT_A_DIRECTORY;
55321e1553eSJérôme Duval 
554ff99132eSAxel Dörfler 	dircookie* dirCookie = (dircookie*)malloc(sizeof(dircookie));
555ff99132eSAxel Dörfler 	if (dirCookie == NULL)
556ff99132eSAxel Dörfler 		return B_NO_MEMORY;
557ff99132eSAxel Dörfler 
55821e1553eSJérôme Duval 	dirCookie->startBlock = node->startLBN[FS_DATA_FORMAT];
55921e1553eSJérôme Duval 	dirCookie->block = node->startLBN[FS_DATA_FORMAT];
56021e1553eSJérôme Duval 	dirCookie->totalSize = node->dataLen[FS_DATA_FORMAT];
56121e1553eSJérôme Duval 	dirCookie->pos = 0;
56221e1553eSJérôme Duval 	dirCookie->id = node->id;
563eb097431SAxel Dörfler 	*_cookie = (void*)dirCookie;
56421e1553eSJérôme Duval 
565ff99132eSAxel Dörfler 	return B_OK;
56621e1553eSJérôme Duval }
56721e1553eSJérôme Duval 
56821e1553eSJérôme Duval 
56921e1553eSJérôme Duval static status_t
57044d0dbc8SAxel Dörfler fs_read_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie,
57144d0dbc8SAxel Dörfler 	struct dirent *buffer, size_t bufferSize, uint32 *num)
57221e1553eSJérôme Duval {
573eb097431SAxel Dörfler 	iso9660_volume *ns = (iso9660_volume *)_vol->private_volume;
57421e1553eSJérôme Duval 	dircookie *dirCookie = (dircookie *)_cookie;
57521e1553eSJérôme Duval 
5765b4cb109SJérôme Duval 	TRACE(("fs_read_dir - ENTER\n"));
57721e1553eSJérôme Duval 
578ff99132eSAxel Dörfler 	status_t result = ISOReadDirEnt(ns, dirCookie, buffer, bufferSize);
57921e1553eSJérôme Duval 
58021e1553eSJérôme Duval 	// If we succeeded, return 1, the number of dirents we read.
581ff99132eSAxel Dörfler 	if (result == B_OK)
58221e1553eSJérôme Duval 		*num = 1;
58321e1553eSJérôme Duval 	else
58421e1553eSJérôme Duval 		*num = 0;
58521e1553eSJérôme Duval 
58621e1553eSJérôme Duval 	// When you get to the end, don't return an error, just return
58721e1553eSJérôme Duval 	// a zero in *num.
58821e1553eSJérôme Duval 
589eb097431SAxel Dörfler 	if (result == B_ENTRY_NOT_FOUND)
590ff99132eSAxel Dörfler 		result = B_OK;
59121e1553eSJérôme Duval 
5925b4cb109SJérôme Duval 	TRACE(("fs_read_dir - EXIT, result is %s\n", strerror(result)));
59321e1553eSJérôme Duval 	return result;
59421e1553eSJérôme Duval }
59521e1553eSJérôme Duval 
59621e1553eSJérôme Duval 
59721e1553eSJérôme Duval static status_t
5980eaadd30SMichael Lotz fs_rewind_dir(fs_volume *_vol, fs_vnode *_node, void* _cookie)
59921e1553eSJérôme Duval {
60021e1553eSJérôme Duval 	dircookie *cookie = (dircookie*)_cookie;
60121e1553eSJérôme Duval 
60221e1553eSJérôme Duval 	cookie->block = cookie->startBlock;
60321e1553eSJérôme Duval 	cookie->pos = 0;
604ff99132eSAxel Dörfler 	return B_OK;
60521e1553eSJérôme Duval }
60621e1553eSJérôme Duval 
60721e1553eSJérôme Duval 
60821e1553eSJérôme Duval static status_t
6090eaadd30SMichael Lotz fs_close_dir(fs_volume *_vol, fs_vnode *_node, void *cookie)
61021e1553eSJérôme Duval {
61121e1553eSJérôme Duval 	return B_OK;
61221e1553eSJérôme Duval }
61321e1553eSJérôme Duval 
61421e1553eSJérôme Duval 
61521e1553eSJérôme Duval static status_t
6160eaadd30SMichael Lotz fs_free_dir_cookie(fs_volume *_vol, fs_vnode *_node, void *cookie)
61721e1553eSJérôme Duval {
61821e1553eSJérôme Duval 	free(cookie);
61921e1553eSJérôme Duval 	return B_OK;
62021e1553eSJérôme Duval }
62121e1553eSJérôme Duval 
622ff99132eSAxel Dörfler 
62321e1553eSJérôme Duval //	#pragma mark -
62421e1553eSJérôme Duval 
62521e1553eSJérôme Duval 
62621e1553eSJérôme Duval static status_t
62721e1553eSJérôme Duval iso_std_ops(int32 op, ...)
62821e1553eSJérôme Duval {
62921e1553eSJérôme Duval 	switch (op) {
63021e1553eSJérôme Duval 		case B_MODULE_INIT:
63121e1553eSJérôme Duval 		case B_MODULE_UNINIT:
63221e1553eSJérôme Duval 			return B_OK;
63321e1553eSJérôme Duval 		default:
63421e1553eSJérôme Duval 			return B_ERROR;
63521e1553eSJérôme Duval 	}
63621e1553eSJérôme Duval }
63721e1553eSJérôme Duval 
63821e1553eSJérôme Duval 
6390eaadd30SMichael Lotz fs_volume_ops gISO9660VolumeOps = {
6400eaadd30SMichael Lotz 	&fs_unmount,
6410eaadd30SMichael Lotz 	&fs_read_fs_stat,
6420eaadd30SMichael Lotz 	NULL,
6430eaadd30SMichael Lotz 	NULL,
6440eaadd30SMichael Lotz 	&fs_read_vnode,
6450eaadd30SMichael Lotz 
6460eaadd30SMichael Lotz 	/* index and index directory ops */
6470eaadd30SMichael Lotz 	NULL,
6480eaadd30SMichael Lotz 	NULL,
6490eaadd30SMichael Lotz 	NULL,
6500eaadd30SMichael Lotz 	NULL,
6510eaadd30SMichael Lotz 	NULL,
6520eaadd30SMichael Lotz 	NULL,
6530eaadd30SMichael Lotz 	NULL,
6540eaadd30SMichael Lotz 	NULL,
6550eaadd30SMichael Lotz 
6560eaadd30SMichael Lotz 	/* query ops */
6570eaadd30SMichael Lotz 	NULL,
6580eaadd30SMichael Lotz 	NULL,
6590eaadd30SMichael Lotz 	NULL,
6600eaadd30SMichael Lotz 	NULL,
6610eaadd30SMichael Lotz 	NULL,
6620eaadd30SMichael Lotz 
6630eaadd30SMichael Lotz 	/* FS layer ops */
6640eaadd30SMichael Lotz 	NULL,
6650eaadd30SMichael Lotz 	NULL,
6660eaadd30SMichael Lotz };
6670eaadd30SMichael Lotz 
6680eaadd30SMichael Lotz fs_vnode_ops gISO9660VnodeOps = {
6690eaadd30SMichael Lotz 	&fs_walk,
6700eaadd30SMichael Lotz 	&fs_get_vnode_name,
6710eaadd30SMichael Lotz 	&fs_release_vnode,
6720eaadd30SMichael Lotz 	NULL,
6730eaadd30SMichael Lotz 
6740eaadd30SMichael Lotz 	/* vm-related ops */
6750eaadd30SMichael Lotz 	NULL,
6760eaadd30SMichael Lotz 	&fs_read_pages,
6770eaadd30SMichael Lotz 	NULL,
6780eaadd30SMichael Lotz 
679ec598fe4SIngo Weinhold 	NULL,	// io()
680ec598fe4SIngo Weinhold 	NULL,	// cancel_io()
681ec598fe4SIngo Weinhold 
6820eaadd30SMichael Lotz 	/* cache file access */
6830eaadd30SMichael Lotz 	NULL,
6840eaadd30SMichael Lotz 
6850eaadd30SMichael Lotz 	/* common */
6860eaadd30SMichael Lotz 	NULL,
6870eaadd30SMichael Lotz 	NULL,
6880eaadd30SMichael Lotz 	NULL,
6890eaadd30SMichael Lotz 	NULL,
6900eaadd30SMichael Lotz 	NULL,
6910eaadd30SMichael Lotz 	&fs_read_link,
6920eaadd30SMichael Lotz 	NULL,
6930eaadd30SMichael Lotz 	NULL,
6940eaadd30SMichael Lotz 	NULL,
6950eaadd30SMichael Lotz 	NULL,
6960eaadd30SMichael Lotz 	&fs_access,
6970eaadd30SMichael Lotz 	&fs_read_stat,
6980eaadd30SMichael Lotz 	NULL,
6990eaadd30SMichael Lotz 
7000eaadd30SMichael Lotz 	/* file */
7010eaadd30SMichael Lotz 	NULL,
7020eaadd30SMichael Lotz 	&fs_open,
7030eaadd30SMichael Lotz 	&fs_close,
7040eaadd30SMichael Lotz 	&fs_free_cookie,
7050eaadd30SMichael Lotz 	&fs_read,
7060eaadd30SMichael Lotz 	NULL,
7070eaadd30SMichael Lotz 
7080eaadd30SMichael Lotz 	/* dir */
7090eaadd30SMichael Lotz 	NULL,
7100eaadd30SMichael Lotz 	NULL,
7110eaadd30SMichael Lotz 	&fs_open_dir,
7120eaadd30SMichael Lotz 	&fs_close_dir,
7130eaadd30SMichael Lotz 	&fs_free_dir_cookie,
7140eaadd30SMichael Lotz 	&fs_read_dir,
7150eaadd30SMichael Lotz 	&fs_rewind_dir,
7160eaadd30SMichael Lotz 
7170eaadd30SMichael Lotz 	/* attribute directory ops */
7180eaadd30SMichael Lotz 	NULL,
7190eaadd30SMichael Lotz 	NULL,
7200eaadd30SMichael Lotz 	NULL,
7210eaadd30SMichael Lotz 	NULL,
7220eaadd30SMichael Lotz 	NULL,
7230eaadd30SMichael Lotz 
7240eaadd30SMichael Lotz 	/* attribute ops */
7250eaadd30SMichael Lotz 	NULL,
7260eaadd30SMichael Lotz 	NULL,
7270eaadd30SMichael Lotz 	NULL,
7280eaadd30SMichael Lotz 	NULL,
7290eaadd30SMichael Lotz 	NULL,
7300eaadd30SMichael Lotz 	NULL,
7310eaadd30SMichael Lotz 	NULL,
7320eaadd30SMichael Lotz 	NULL,
7330eaadd30SMichael Lotz 	NULL,
7340eaadd30SMichael Lotz 	NULL,
7350eaadd30SMichael Lotz 
7360eaadd30SMichael Lotz 	/* node and FS layer support */
7370eaadd30SMichael Lotz 	NULL,
7380eaadd30SMichael Lotz 	NULL,
7390eaadd30SMichael Lotz };
7400eaadd30SMichael Lotz 
74121e1553eSJérôme Duval static file_system_module_info sISO660FileSystem = {
74221e1553eSJérôme Duval 	{
74321e1553eSJérôme Duval 		"file_systems/iso9660" B_CURRENT_FS_API_VERSION,
74421e1553eSJérôme Duval 		0,
74521e1553eSJérôme Duval 		iso_std_ops,
74621e1553eSJérôme Duval 	},
74721e1553eSJérôme Duval 
7481da9f5ceSAxel Dörfler 	"iso9660",					// short_name
7491da9f5ceSAxel Dörfler 	"ISO9660 File System",		// pretty_name
75076a8ec23SIngo Weinhold 	0,							// DDM flags
75121e1553eSJérôme Duval 
75221e1553eSJérôme Duval 	// scanning
75321e1553eSJérôme Duval 	fs_identify_partition,
75421e1553eSJérôme Duval 	fs_scan_partition,
75521e1553eSJérôme Duval 	fs_free_identify_partition_cookie,
75621e1553eSJérôme Duval 	NULL,	// free_partition_content_cookie()
75721e1553eSJérôme Duval 
75821e1553eSJérôme Duval 	&fs_mount,
7590eaadd30SMichael Lotz 
7600eaadd30SMichael Lotz 	/* capability querying */
7610eaadd30SMichael Lotz 	NULL,
7620eaadd30SMichael Lotz 	NULL,
7630eaadd30SMichael Lotz 	NULL,
7640eaadd30SMichael Lotz 	NULL,
76521e1553eSJérôme Duval 	NULL,
76621e1553eSJérôme Duval 	NULL,
76721e1553eSJérôme Duval 
7680eaadd30SMichael Lotz 	/* shadow partition modifications */
7690eaadd30SMichael Lotz 	NULL,
77021e1553eSJérôme Duval 
7710eaadd30SMichael Lotz 	/* writing */
7720eaadd30SMichael Lotz 	NULL,
7730eaadd30SMichael Lotz 	NULL,
7740eaadd30SMichael Lotz 	NULL,
7750eaadd30SMichael Lotz 	NULL,
7760eaadd30SMichael Lotz 	NULL,
7770eaadd30SMichael Lotz 	NULL,
7780eaadd30SMichael Lotz 	NULL,
77921e1553eSJérôme Duval };
78021e1553eSJérôme Duval 
78121e1553eSJérôme Duval module_info *modules[] = {
78221e1553eSJérôme Duval 	(module_info *)&sISO660FileSystem,
78321e1553eSJérôme Duval 	NULL,
78421e1553eSJérôme Duval };
78521e1553eSJérôme Duval 
786