xref: /haiku/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2  * Copyright 1999, Be Incorporated.   All Rights Reserved.
3  * This file may be used under the terms of the Be Sample Code License.
4  *
5  * Copyright 2001, pinc Software.  All Rights Reserved.
6  *
7  * iso9960/multi-session, 1.0.0
8  */
9 
10 
11 #include <ctype.h>
12 #include <dirent.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/stat.h>
19 #include <time.h>
20 #include <unistd.h>
21 
22 #include <KernelExport.h>
23 #include <NodeMonitor.h>
24 #include <fs_interface.h>
25 #include <fs_cache.h>
26 
27 #include <fs_attr.h>
28 #include <fs_info.h>
29 #include <fs_index.h>
30 #include <fs_query.h>
31 #include <fs_volume.h>
32 
33 #include <util/kernel_cpp.h>
34 
35 #include "iso9660.h"
36 #include "iso9660_identify.h"
37 
38 
39 //#define TRACE_ISO9660
40 #ifdef TRACE_ISO9660
41 #	define TRACE(x) dprintf x
42 #else
43 #	define TRACE(x) ;
44 #endif
45 
46 
47 struct identify_cookie {
48 	iso9660_info info;
49 };
50 
51 
52 //	#pragma mark - Scanning
53 
54 
55 static float
56 fs_identify_partition(int fd, partition_data *partition, void **_cookie)
57 {
58 	iso9660_info *info = new iso9660_info;
59 
60 	status_t status = iso9660_fs_identify(fd, info);
61 	if (status != B_OK) {
62 		delete info;
63 		return -1;
64 	}
65 
66 	*_cookie = info;
67 	return 0.6f;
68 }
69 
70 
71 static status_t
72 fs_scan_partition(int fd, partition_data *partition, void *_cookie)
73 {
74 	iso9660_info *info = (iso9660_info *)_cookie;
75 
76 	partition->status = B_PARTITION_VALID;
77 	partition->flags |= B_PARTITION_FILE_SYSTEM | B_PARTITION_READ_ONLY ;
78 	partition->block_size = ISO_PVD_SIZE;
79 	partition->content_size = ISO_PVD_SIZE * info->max_blocks;
80 	partition->content_name = strdup(info->PreferredName());
81 
82 	if (partition->content_name == NULL)
83 		return B_NO_MEMORY;
84 
85 	return B_OK;
86 }
87 
88 
89 static void
90 fs_free_identify_partition_cookie(partition_data *partition, void *_cookie)
91 {
92 	delete (iso9660_info *)_cookie;
93 }
94 
95 
96 //	#pragma mark - FS hooks
97 
98 
99 static status_t
100 fs_mount(dev_t mountID, const char *device, uint32 flags,
101 	const char *args, void **_volume, ino_t *_rootID)
102 {
103 	bool allowJoliet = true;
104 	nspace *volume;
105 
106 	// Check for a 'nojoliet' parm
107 	// all we check for is the existance of 'nojoliet' in the parms.
108 	if (args != NULL) {
109 		uint32 i;
110 		char *spot;
111 		char *buf = strdup(args);
112 
113 		uint32 len = strlen(buf);
114 		// lower case the parms data
115 		for (i = 0; i < len + 1; i++)
116 			buf[i] = tolower(buf[i]);
117 
118 		// look for nojoliet
119 		spot = strstr(buf, "nojoliet");
120 		if (spot != NULL)
121 			allowJoliet = false;
122 
123 		free(buf);
124 	}
125 
126 	// Try and mount volume as an ISO volume.
127 	status_t result = ISOMount(device, O_RDONLY, &volume, allowJoliet);
128 	if (result == B_OK) {
129 		*_rootID = ISO_ROOTNODE_ID;
130 		*_volume = volume;
131 
132 		volume->id = mountID;
133 
134 		result = publish_vnode(mountID, *_rootID, &volume->rootDirRec);
135 		if (result != B_OK) {
136 			block_cache_delete(volume->fBlockCache, false);
137 			free(volume);
138 			result = B_ERROR;
139 		}
140 	}
141 	return result;
142 }
143 
144 
145 static status_t
146 fs_unmount(void *_ns)
147 {
148 	status_t result = B_NO_ERROR;
149 	nspace *ns = (nspace *)_ns;
150 
151 	TRACE(("fs_unmount - ENTER\n"));
152 
153 	// Unlike in BeOS, we need to put the reference to our root node ourselves
154 	put_vnode(ns->id, ISO_ROOTNODE_ID);
155 
156 	block_cache_delete(ns->fBlockCache, false);
157 	close(ns->fdOfSession);
158 	result = close(ns->fd);
159 
160 	free(ns);
161 
162 	TRACE(("fs_unmount - EXIT, result is %s\n", strerror(result)));
163 	return result;
164 }
165 
166 
167 static status_t
168 fs_read_fs_stat(void *_ns, struct fs_info *fss)
169 {
170 	nspace *ns = (nspace *)_ns;
171 	int i;
172 
173 	fss->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
174 	fss->block_size = ns->logicalBlkSize[FS_DATA_FORMAT];
175 	fss->io_size = 65536;
176 	fss->total_blocks = ns->volSpaceSize[FS_DATA_FORMAT];
177 	fss->free_blocks = 0;
178 
179 	strncpy(fss->device_name, ns->devicePath, sizeof(fss->device_name));
180 
181 	strncpy(fss->volume_name, ns->volIDString, sizeof(fss->volume_name));
182 	for (i = strlen(fss->volume_name) - 1; i >=0 ; i--) {
183 		if (fss->volume_name[i] != ' ')
184 			break;
185 	}
186 
187 	if (i < 0)
188 		strcpy(fss->volume_name, "UNKNOWN");
189 	else
190 		fss->volume_name[i + 1] = 0;
191 
192 	strcpy(fss->fsh_name, "iso9660");
193 	return B_OK;
194 }
195 
196 
197 static status_t
198 fs_get_vnode_name(void *ns, void *_node, char *buffer, size_t bufferSize)
199 {
200 	vnode *node = (vnode*)_node;
201 
202 	strlcpy(buffer, node->fileIDString, bufferSize);
203 	return B_OK;
204 }
205 
206 
207 static status_t
208 fs_walk(void *_ns, void *base, const char *file, ino_t *_vnodeID, int *_type)
209 {
210 	nspace *ns = (nspace *)_ns;
211 	vnode *baseNode = (vnode*)base;
212 	vnode *newNode = NULL;
213 
214 	TRACE(("fs_walk - looking for %s in dir file of length %d\n", file,
215 		baseNode->dataLen[FS_DATA_FORMAT]));
216 
217 	if (strcmp(file, ".") == 0)  {
218 		// base directory
219 		TRACE(("fs_walk - found \".\" file.\n"));
220 		*_vnodeID = baseNode->id;
221 		*_type = S_IFDIR;
222 		return get_vnode(ns->id, *_vnodeID, (void **)&newNode);
223 	} else if (strcmp(file, "..") == 0) {
224 		// parent directory
225 		TRACE(("fs_walk - found \"..\" file.\n"));
226 		*_vnodeID = baseNode->parID;
227 		*_type = S_IFDIR;
228 		return get_vnode(ns->id, *_vnodeID, (void **)&newNode);
229 	}
230 
231 	// look up file in the directory
232 	uint32 dataLength = baseNode->dataLen[FS_DATA_FORMAT];
233 	status_t result = ENOENT;
234 	uint32 totalRead = 0;
235 	off_t block = baseNode->startLBN[FS_DATA_FORMAT];
236 	bool done = false;
237 
238 	while (totalRead < dataLength && !done) {
239 		off_t cachedBlock = block;
240 		char *blockData = (char *)block_cache_get_etc(ns->fBlockCache, block, 0,
241 			ns->logicalBlkSize[FS_DATA_FORMAT]);
242 		if (blockData != NULL) {
243 			int bytesRead = 0;
244 			off_t blockBytesRead = 0;
245 			vnode node;
246 			int initResult;
247 
248 			TRACE(("fs_walk - read buffer from disk at LBN %Ld into buffer 0x%x.\n",
249 				block, blockData));
250 
251 			// Move to the next 2-block set if necessary
252 			// Don't go over end of buffer, if dir record sits on boundary.
253 
254 			node.fileIDString = NULL;
255 			node.attr.slName = NULL;
256 
257 			while (blockBytesRead < 2 * ns->logicalBlkSize[FS_DATA_FORMAT]
258 				&& totalRead + blockBytesRead < dataLength
259 				&& blockData[0] != 0
260 				&& !done) {
261 				initResult = InitNode(&node, blockData, &bytesRead,
262 					ns->joliet_level);
263 				TRACE(("fs_walk - InitNode returned %s, filename %s, %d bytes read\n", strerror(initResult), node.fileIDString, bytesRead));
264 
265 				if (initResult == B_OK) {
266 					if (!strcmp(node.fileIDString, file)) {
267 						TRACE(("fs_walk - success, found vnode at block %Ld, pos %Ld\n", block, blockBytesRead));
268 						*_vnodeID = (block << 30)
269 							+ (blockBytesRead & 0xffffffff);
270 						TRACE(("fs_walk - New vnode id is %Ld\n", *_vnodeID));
271 
272 						result = get_vnode(ns->id, *_vnodeID,
273 							(void **)&newNode);
274 						if (result == B_OK) {
275 							newNode->parID = baseNode->id;
276 							done = true;
277 						}
278 					} else {
279 						free(node.fileIDString);
280 						node.fileIDString = NULL;
281 						free(node.attr.slName);
282 						node.attr.slName = NULL;
283 					}
284 				} else {
285 					result = initResult;
286 					if (bytesRead == 0)
287 						done = TRUE;
288 				}
289 				blockData += bytesRead;
290 				blockBytesRead += bytesRead;
291 
292 				TRACE(("fs_walk - Adding %d bytes to blockBytes read (total %Ld/%Ld).\n",
293 					bytesRead, blockBytesRead, baseNode->dataLen[FS_DATA_FORMAT]));
294 			}
295 			totalRead += ns->logicalBlkSize[FS_DATA_FORMAT];
296 			block++;
297 
298 			TRACE(("fs_walk - moving to next block %Ld, total read %Ld\n", block, totalRead));
299 			block_cache_put(ns->fBlockCache, cachedBlock);
300 
301 		} else
302 			done = TRUE;
303 	}
304 
305 	if (newNode)
306 		*_type = newNode->attr.stat[FS_DATA_FORMAT].st_mode;
307 
308 	TRACE(("fs_walk - EXIT, result is %s, vnid is %Lu\n",
309 		strerror(result), *_vnodeID));
310 	return result;
311 }
312 
313 
314 static status_t
315 fs_read_vnode(void *_ns, ino_t vnodeID, void **_node, bool reenter)
316 {
317 	nspace *ns = (nspace*)_ns;
318 
319 	vnode *newNode = (vnode*)calloc(sizeof(vnode), 1);
320 	if (newNode == NULL)
321 		return B_NO_MEMORY;
322 
323 	uint32 pos = vnodeID & 0x3fffffff;
324 	uint32 block = vnodeID >> 30;
325 
326 	TRACE(("fs_read_vnode - block = %ld, pos = %ld, raw = %Lu node 0x%x\n",
327 		block, pos, vnodeID, newNode));
328 
329 	if (pos > ns->logicalBlkSize[FS_DATA_FORMAT])
330 		return B_BAD_VALUE;
331 
332 	char *data = (char *)block_cache_get_etc(ns->fBlockCache,
333 		block, 0, ns->logicalBlkSize[FS_DATA_FORMAT]);
334 	if (data == NULL) {
335 		free(newNode);
336 		return B_IO_ERROR;
337 	}
338 
339 	status_t result = InitNode(newNode, data + pos, NULL, ns->joliet_level);
340 	block_cache_put(ns->fBlockCache, block);
341 
342 	if (result < B_OK) {
343 		free(newNode);
344 		return result;
345 	}
346 
347 	newNode->id = vnodeID;
348 	*_node = (void *)newNode;
349 
350 	if ((newNode->flags & ISO_ISDIR) == 0) {
351 		newNode->cache = file_cache_create(ns->id, vnodeID,
352 			newNode->dataLen[FS_DATA_FORMAT]);
353 	}
354 
355 	return B_OK;
356 }
357 
358 
359 static status_t
360 fs_release_vnode(void *ns, void *_node, bool reenter)
361 {
362 	status_t result = B_NO_ERROR;
363 	vnode *node = (vnode*)_node;
364 
365 	(void)ns;
366 	(void)reenter;
367 
368 	TRACE(("fs_release_vnode - ENTER (0x%x)\n", node));
369 
370 	if (node != NULL) {
371 		if (node->id != ISO_ROOTNODE_ID) {
372 			if (node->fileIDString != NULL)
373 				free (node->fileIDString);
374 			if (node->attr.slName != NULL)
375 				free (node->attr.slName);
376 			if (node->cache != NULL)
377 				file_cache_delete(node->cache);
378 
379 			free(node);
380 		}
381 	}
382 
383 	TRACE(("fs_release_vnode - EXIT\n"));
384 	return result;
385 }
386 
387 
388 static status_t
389 fs_read_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos,
390 	const iovec *vecs, size_t count, size_t *_numBytes, bool reenter)
391 {
392 	nspace *ns = (nspace *)_fs;
393 	vnode *node = (vnode *)_node;
394 
395 	uint32 fileSize = node->dataLen[FS_DATA_FORMAT];
396 	size_t bytesLeft = *_numBytes;
397 
398 	if (pos >= fileSize) {
399 		*_numBytes = 0;
400 		return B_OK;
401 	}
402 	if (pos + bytesLeft > fileSize) {
403 		bytesLeft = fileSize - pos;
404 		*_numBytes = bytesLeft;
405 	}
406 
407 	file_io_vec fileVec;
408 	fileVec.offset = pos + node->startLBN[FS_DATA_FORMAT]
409 		* ns->logicalBlkSize[FS_DATA_FORMAT];
410 	fileVec.length = bytesLeft;
411 
412 	uint32 vecIndex = 0;
413 	size_t vecOffset = 0;
414 	return read_file_io_vec_pages(ns->fd, &fileVec, 1, vecs, count,
415 		&vecIndex, &vecOffset, &bytesLeft);
416 }
417 
418 
419 static status_t
420 fs_read_stat(void *_ns, void *_node, struct stat *st)
421 {
422 	nspace *ns = (nspace*)_ns;
423 	vnode *node = (vnode*)_node;
424 	status_t result = B_NO_ERROR;
425 	time_t time;
426 
427 	TRACE(("fs_read_stat - ENTER\n"));
428 
429 	st->st_dev = ns->id;
430 	st->st_ino = node->id;
431 	st->st_nlink = node->attr.stat[FS_DATA_FORMAT].st_nlink;
432 	st->st_uid = node->attr.stat[FS_DATA_FORMAT].st_uid;
433 	st->st_gid = node->attr.stat[FS_DATA_FORMAT].st_gid;
434 	st->st_blksize = 65536;
435 	st->st_mode = node->attr.stat[FS_DATA_FORMAT].st_mode;
436 
437 	// Same for file/dir in ISO9660
438 	st->st_size = node->dataLen[FS_DATA_FORMAT];
439 	if (ConvertRecDate(&(node->recordDate), &time) == B_NO_ERROR)
440 		st->st_ctime = st->st_mtime = st->st_atime = time;
441 
442 	TRACE(("fs_read_stat - EXIT, result is %s\n", strerror(result)));
443 
444 	return result;
445 }
446 
447 
448 static status_t
449 fs_open(void *_ns, void *_node, int omode, void **cookie)
450 {
451 	status_t result = B_NO_ERROR;
452 
453 	(void)_ns;
454 	(void)cookie;
455 
456 	// Do not allow any of the write-like open modes to get by
457 	if ((omode == O_WRONLY) || (omode == O_RDWR))
458 		result = EROFS;
459 	else if((omode & O_TRUNC) || (omode & O_CREAT))
460 		result = EROFS;
461 
462 	return result;
463 }
464 
465 
466 static status_t
467 fs_read(void *_ns, void *_node, void *cookie, off_t pos, void *buffer,
468 	size_t *_length)
469 {
470 	vnode *node = (vnode *)_node;
471 
472 	if (node->flags & ISO_ISDIR)
473 		return EISDIR;
474 
475 	uint32 fileSize = node->dataLen[FS_DATA_FORMAT];
476 
477 	// set/check boundaries for pos/length
478 	if (pos < 0)
479 		return B_BAD_VALUE;
480 	if (pos >= fileSize) {
481 		*_length = 0;
482 		return B_OK;
483 	}
484 
485 	return file_cache_read(node->cache, NULL, pos, buffer, _length);
486 }
487 
488 
489 static status_t
490 fs_close(void *ns, void *node, void *cookie)
491 {
492 	(void)ns;
493 	(void)node;
494 	(void)cookie;
495 
496 	return B_OK;
497 }
498 
499 
500 static status_t
501 fs_free_cookie(void *ns, void *node, void *cookie)
502 {
503 	(void)ns;
504 	(void)node;
505 	(void)cookie;
506 
507 	return B_OK;
508 }
509 
510 
511 static status_t
512 fs_access(void *ns, void *node, int mode)
513 {
514 	(void)ns;
515 	(void)node;
516 	(void)mode;
517 
518 	return B_OK;
519 }
520 
521 
522 static status_t
523 fs_read_link(void */*_volume*/, void *_node, char *buffer, size_t *_bufferSize)
524 {
525 	vnode *node = (vnode *)_node;
526 
527 	if (!S_ISLNK(node->attr.stat[FS_DATA_FORMAT].st_mode))
528 		return B_BAD_VALUE;
529 
530 	size_t length = strlen(node->attr.slName);
531 	if (length > *_bufferSize)
532 		memcpy(buffer, node->attr.slName, *_bufferSize);
533 	else {
534 		memcpy(buffer, node->attr.slName, length);
535 		*_bufferSize = length;
536 	}
537 
538 	return B_OK;
539 }
540 
541 
542 static status_t
543 fs_open_dir(void *_ns, void *_node, void **cookie)
544 {
545 	vnode *node = (vnode *)_node;
546 
547 	TRACE(("fs_open_dir - node is 0x%x\n", _node));
548 
549 	if (!(node->flags & ISO_ISDIR))
550 		return B_NOT_A_DIRECTORY;
551 
552 	dircookie *dirCookie = (dircookie *)malloc(sizeof(dircookie));
553 	if (dirCookie == NULL)
554 		return B_NO_MEMORY;
555 
556 	dirCookie->startBlock = node->startLBN[FS_DATA_FORMAT];
557 	dirCookie->block = node->startLBN[FS_DATA_FORMAT];
558 	dirCookie->totalSize = node->dataLen[FS_DATA_FORMAT];
559 	dirCookie->pos = 0;
560 	dirCookie->id = node->id;
561 	*cookie = (void *)dirCookie;
562 
563 	return B_OK;
564 }
565 
566 
567 static status_t
568 fs_read_dir(void *_ns, void *_node, void *_cookie, struct dirent *buffer,
569 	size_t bufferSize, uint32 *num)
570 {
571 	nspace *ns = (nspace *)_ns;
572 	dircookie *dirCookie = (dircookie *)_cookie;
573 
574 	TRACE(("fs_read_dir - ENTER\n"));
575 
576 	status_t result = ISOReadDirEnt(ns, dirCookie, buffer, bufferSize);
577 
578 	// If we succeeded, return 1, the number of dirents we read.
579 	if (result == B_OK)
580 		*num = 1;
581 	else
582 		*num = 0;
583 
584 	// When you get to the end, don't return an error, just return
585 	// a zero in *num.
586 
587 	if (result == ENOENT)
588 		result = B_OK;
589 
590 	TRACE(("fs_read_dir - EXIT, result is %s\n", strerror(result)));
591 	return result;
592 }
593 
594 
595 static status_t
596 fs_rewind_dir(void *ns, void *node, void* _cookie)
597 {
598 	dircookie *cookie = (dircookie*)_cookie;
599 
600 	cookie->block = cookie->startBlock;
601 	cookie->pos = 0;
602 	return B_OK;
603 }
604 
605 
606 static status_t
607 fs_close_dir(void *ns, void *node, void *cookie)
608 {
609 	return B_OK;
610 }
611 
612 
613 static status_t
614 fs_free_dir_cookie(void *ns, void *node, void *cookie)
615 {
616 	free(cookie);
617 	return B_OK;
618 }
619 
620 
621 //	#pragma mark -
622 
623 
624 static status_t
625 iso_std_ops(int32 op, ...)
626 {
627 	switch (op) {
628 		case B_MODULE_INIT:
629 		case B_MODULE_UNINIT:
630 			return B_OK;
631 		default:
632 			return B_ERROR;
633 	}
634 }
635 
636 
637 static file_system_module_info sISO660FileSystem = {
638 	{
639 		"file_systems/iso9660" B_CURRENT_FS_API_VERSION,
640 		0,
641 		iso_std_ops,
642 	},
643 
644 	"ISO9660 File System",
645 	0,	// DDM flags
646 
647 	// scanning
648 	fs_identify_partition,
649 	fs_scan_partition,
650 	fs_free_identify_partition_cookie,
651 	NULL,	// free_partition_content_cookie()
652 
653 	&fs_mount,
654 	&fs_unmount,
655 	&fs_read_fs_stat,
656 	NULL,
657 	NULL,
658 
659 	/* vnode operations */
660 	&fs_walk,
661 	&fs_get_vnode_name,
662 	&fs_read_vnode,
663 	&fs_release_vnode,
664 	NULL, 	// fs_remove_vnode()
665 
666 	/* VM file access */
667 	NULL, 	// fs_can_page
668 	&fs_read_pages,
669 	NULL, 	// fs_write_pages
670 
671 	NULL,	// fs_get_file_map
672 
673 	NULL, 	// fs_ioctl
674 	NULL, 	// fs_set_flags
675 	NULL,	// fs_select
676 	NULL,	// fs_deselect
677 	NULL, 	// fs_fsync
678 
679 	&fs_read_link,
680 	NULL, 	// fs_create_symlink
681 
682 	NULL, 	// fs_link,
683 	NULL,	// fs_unlink
684 	NULL, 	// fs_rename
685 
686 	&fs_access,
687 	&fs_read_stat,
688 	NULL, 	// fs_write_stat
689 
690 	/* file operations */
691 	NULL, 	// fs_create
692 	&fs_open,
693 	&fs_close,
694 	&fs_free_cookie,
695 	&fs_read,
696 	NULL, 	// fs_write
697 
698 	/* directory operations */
699 	NULL, 	// fs_create_dir
700 	NULL, 	// fs_remove_dir
701 	&fs_open_dir,
702 	&fs_close_dir,
703 	&fs_free_dir_cookie,
704 	&fs_read_dir,
705 	&fs_rewind_dir,
706 
707 	/* attribute directory operations */
708 	NULL, 	// fs_open_attr_dir
709 	NULL, 	// fs_close_attr_dir
710 	NULL,	// fs_free_attr_dir_cookie
711 	NULL,	// fs_read_attr_dir
712 	NULL,	// fs_rewind_attr_dir
713 
714 	/* attribute operations */
715 	NULL,	// fs_create_attr
716 	NULL, 	// fs_open_attr
717 	NULL,	// fs_close_attr
718 	NULL,	// fs_free_attr_cookie
719 	NULL,	// fs_read_attr
720 	NULL,	// fs_write_attr
721 
722 	NULL,	// fs_read_attr_stat
723 	NULL,	// fs_write_attr_stat
724 	NULL,	// fs_rename_attr
725 	NULL,	// fs_remove_attr
726 
727 	/* index directory & index operations */
728 	NULL,	// fs_open_index_dir
729 	NULL,	// fs_close_index_dir
730 	NULL,	// fs_free_index_dir_cookie
731 	NULL,	// fs_read_index_dir
732 	NULL,	// fs_rewind_index_dir
733 
734 	NULL,	// fs_create_index
735 	NULL,	// fs_remove_index
736 	NULL,	// fs_stat_index
737 
738 	/* query operations */
739 	NULL,	// fs_open_query
740 	NULL,	// fs_close_query
741 	NULL,	// fs_free_query_cookie
742 	NULL,	// fs_read_query
743 	NULL,	// fs_rewind_query
744 };
745 
746 module_info *modules[] = {
747 	(module_info *)&sISO660FileSystem,
748 	NULL,
749 };
750 
751