xref: /haiku/src/add-ons/kernel/file_systems/udf/kernel_interface.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
1 /*
2  * Copyright 2008, Salvatore Benedetto, salvatore.benedetto@gmail.com.
3  * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 /*! \file kernel_interface.cpp */
8 
9 #include <Drivers.h>
10 #include <ctype.h>
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include <KernelExport.h>
17 #include <util/kernel_cpp.h>
18 
19 #include "Icb.h"
20 #include "Recognition.h"
21 #include "Utils.h"
22 #include "Volume.h"
23 
24 
25 #undef TRACE
26 #undef TRACE_ERROR
27 #define UDF_KERNEL_INTERFACE_DEBUG
28 #ifdef UDF_KERNEL_INTERFACE_DEBUG
29 #	define TRACE(x)			dprintf x
30 #	define TRACE_ERROR(x)	dprintf x
31 #else
32 #	define TRACE(x)			/* nothing */
33 #	define TRACE_ERROR(x)	dprintf x
34 #endif
35 
36 extern fs_volume_ops gUDFVolumeOps;
37 extern fs_vnode_ops gUDFVnodeOps;
38 
39 
40 //	#pragma mark - fs_volume_ops fuctions
41 
42 
43 static float
44 udf_identify_partition(int fd, partition_data *partition, void **_cookie)
45 {
46 	TRACE(("udf_identify_partition: fd = %d, id = %d, offset = %d, size = %d "
47 		"content_size = %d, block_size = %d\n", fd, partition->id,
48 		partition->offset, partition->size, partition->content_size,
49 		partition->block_size));
50 
51 	logical_volume_descriptor logicalVolumeDescriptor;
52 	partition_descriptor partitionDescriptors[kMaxPartitionDescriptors];
53 	uint8 descriptorCount = kMaxPartitionDescriptors;
54 	uint32 blockShift;
55 	status_t error = udf_recognize(fd, partition->offset, partition->size,
56 		partition->block_size, blockShift, logicalVolumeDescriptor,
57 		partitionDescriptors, descriptorCount);
58 	if (error != B_OK)
59 		return -1;
60 
61 	return 0.8f;
62 }
63 
64 
65 static status_t
66 udf_scan_partition(int fd, partition_data *partition, void *_cookie)
67 {
68 	TRACE(("udf_scan_partition: fd = %d\n", fd));
69 
70 #if 0
71 	UdfString name(logicalVolumeDescriptor.logical_volume_identifier());
72 	partition->content_name = strdup(name.Utf8());
73 #endif
74 	return B_ERROR;
75 }
76 
77 
78 static status_t
79 udf_unmount(fs_volume *_volume)
80 {
81 	TRACE(("udb_unmount: _volume = %p\n", _volume));
82 	Volume *volume = (Volume *)_volume->private_volume;
83 	delete volume;
84 	return B_OK;
85 }
86 
87 
88 static status_t
89 udf_read_fs_stat(fs_volume *_volume, struct fs_info *info)
90 {
91 	TRACE(("udf_read_fs_stat: _volume = %p, info = %p\n", _volume, info));
92 
93 	Volume *volume = (Volume *)_volume->private_volume;
94 
95 	// File system flags.
96 	info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
97 
98 	info->io_size = 65536;
99 		// whatever is appropriate here? Just use the same value as BFS (and iso9660) for now
100 
101 	info->block_size = volume->BlockSize();
102 	info->total_blocks = volume->Length();
103 	info->free_blocks = 0;
104 
105 	// Volume name
106 	sprintf(info->volume_name, "%s", volume->Name());
107 
108 	// File system name
109 	strcpy(info->fsh_name, "udf");
110 
111 	return B_OK;
112 }
113 
114 
115 static status_t
116 udf_get_vnode(fs_volume *_volume, ino_t id, fs_vnode *_node, int *_type,
117 	uint32 *_flags, bool reenter)
118 {
119 	TRACE(("udf_get_vnode: _volume = %p, _node = %p, reenter = %s\n",
120 		_volume, _node, (reenter ? "true" : "false")));
121 
122 	Volume *volume = (Volume *)_volume->private_volume;
123 
124 	// Convert the given vnode id to an address, and create
125 	// and return a corresponding Icb object for it.
126 	TRACE(("udf_get_vnode: id = %d, blockSize = %d\n", id, volume->BlockSize()));
127 	Icb *icb = new(std::nothrow) Icb(volume,
128 		to_long_address(id, volume->BlockSize()));
129 	if (icb) {
130 		if(icb->InitCheck() == B_OK) {
131 			if (_node)
132 				_node->private_node = icb;
133 				_node->ops = &gUDFVnodeOps;
134 				_flags = 0;
135 		} else {
136 			TRACE_ERROR(("udf_get_vnode: InitCheck failed\n"));
137 			delete icb;
138 			return B_ERROR;
139 		}
140 	} else
141 		return B_NO_MEMORY;
142 
143 	return B_OK;
144 }
145 
146 
147 //	#pragma mark - fs_vnode_ops functions
148 
149 
150 static status_t
151 udf_lookup(fs_volume *_volume, fs_vnode *_directory, const char *file,
152 	ino_t *vnodeID)
153 {
154 	TRACE(("udf_lookup: _directory = %p, filename = %s\n", _directory, file));
155 
156 	Volume *volume = (Volume *)_volume->private_volume;
157 	Icb *dir = (Icb *)_directory->private_node;
158 	Icb *node = NULL;
159 
160 	status_t status = B_OK;
161 
162 	if (strcmp(file, ".") == 0) {
163 		TRACE(("udf_lookup: file = ./\n"));
164 		*vnodeID = dir->Id();
165 		status = get_vnode(volume->FSVolume(), *vnodeID, (void **)&node);
166 		if (status != B_OK)
167 			return B_ENTRY_NOT_FOUND;
168 	} else {
169 		status = dir->Find(file, vnodeID);
170 		if (status != B_OK)
171 			return status;
172 
173 		Icb *icb;
174 		status = get_vnode(volume->FSVolume(), *vnodeID, (void **)&icb);
175 		if (status != B_OK)
176 			return B_ENTRY_NOT_FOUND;
177 	}
178 	TRACE(("udf_lookup: vnodeId = %Ld found!\n", *vnodeID));
179 
180 	return B_OK;
181 }
182 
183 
184 static status_t
185 udf_put_vnode(fs_volume *volume, fs_vnode *node, bool reenter)
186 {
187 	TRACE(("udf_put_vnode: volume = %p, node = %p\n", volume, node));
188 // No debug-to-file in release_vnode; can cause a deadlock in
189 // rare circumstances.
190 #if !DEBUG_TO_FILE
191 	DEBUG_INIT_ETC(NULL, ("node: %p", node));
192 #endif
193 	Icb *icb = (Icb *)node->private_node;
194 	delete icb;
195 #if !DEBUG_TO_FILE
196 	RETURN(B_OK);
197 #else
198 	return B_OK;
199 #endif
200 }
201 
202 
203 static status_t
204 udf_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
205 {
206 	TRACE(("udf_remove_vnode: _volume = %p, _node = %p\n", _volume, _node));
207 	return B_ERROR;
208 }
209 
210 
211 static status_t
212 udf_read_stat(fs_volume *_volume, fs_vnode *node, struct stat *stat)
213 {
214 	TRACE(("udf_read_stat: _volume = %p, node = %p\n", _volume, node));
215 
216 	if (!_volume || !node || !stat)
217 		return B_BAD_VALUE;
218 
219 	Volume *volume = (Volume *)_volume->private_volume;
220 	Icb *icb = (Icb *)node->private_node;
221 
222 	stat->st_dev = volume->ID();
223 	stat->st_ino = icb->Id();
224 	stat->st_nlink = icb->FileLinkCount();
225 	stat->st_blksize = volume->BlockSize();
226 
227 	TRACE(("udf_read_stat: st_dev = %d, st_ino = %d, st_blksize = %d\n",
228 		stat->st_dev, stat->st_ino, stat->st_blksize));
229 
230 	stat->st_uid = icb->Uid();
231 	stat->st_gid = icb->Gid();
232 
233 	stat->st_mode = icb->Mode();
234 	stat->st_size = icb->Length();
235 
236 	// File times. For now, treat the modification time as creation
237 	// time as well, since true creation time is an optional extended
238 	// attribute, and supporting EAs is going to be a PITA. ;-)
239 	stat->st_atime = icb->AccessTime();
240 	stat->st_mtime = stat->st_ctime = stat->st_crtime = icb->ModificationTime();
241 
242 	TRACE(("udf_read_stat: mode = 0x%lx, st_ino: %Ld\n", stat->st_mode,
243 		stat->st_ino));
244 
245 	return B_OK;
246 }
247 
248 
249 static status_t
250 udf_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
251 {
252 	TRACE(("udf_open: _volume = %p, _node = %p\n", _volume, _node));
253 	return B_OK;
254 }
255 
256 
257 static status_t
258 udf_close(fs_volume* _volume, fs_vnode* _node, void* _cookie)
259 {
260 	TRACE(("udf_close: _volume = %p, _node = %p\n", _volume, _node));
261 	return B_OK;
262 }
263 
264 
265 static status_t
266 udf_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
267 {
268 	TRACE(("udf_free_cookie: _volume = %p, _node = %p\n", _volume, _node));
269 	return B_OK;
270 }
271 
272 
273 static status_t
274 udf_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
275 {
276 	TRACE(("udf_access: _volume = %p, _node = %p\n", _volume, _node));
277 	return B_OK;
278 }
279 
280 
281 static status_t
282 udf_read(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos,
283 	void *buffer, size_t *length)
284 {
285 	TRACE(("udf_read: ID = %ld, pos = %lld, length = %lu\n",
286 		((Volume *)volume->private_volume)->ID(), pos, *length));
287 
288 	Icb *icb = (Icb *)vnode->private_node;
289 
290 //	if (!inode->HasUserAccessableStream()) {
291 //		*_length = 0;
292 //		RETURN_ERROR(B_BAD_VALUE);
293 //	}
294 
295 	RETURN(icb->Read(pos, buffer, length));
296 }
297 
298 
299 static status_t
300 udf_open_dir(fs_volume *volume, fs_vnode *vnode, void **cookie)
301 {
302 	TRACE(("udf_open_dir: volume = %p, vnode = %p\n", volume, vnode));
303 
304 	if (!volume || !vnode || !cookie)
305 		RETURN(B_BAD_VALUE);
306 
307 	Icb *dir = (Icb *)vnode->private_node;
308 
309 	if (!dir->IsDirectory()) {
310 		TRACE_ERROR(("udf_open_dir: given Icb is not a directory (type: %d)\n",
311 			dir->Type()));
312 		return B_BAD_VALUE;
313 	}
314 
315 	DirectoryIterator *iterator = NULL;
316 	status_t status = dir->GetDirectoryIterator(&iterator);
317 	if (status != B_OK) {
318 		TRACE_ERROR(("udf_open_dir: error getting directory iterator: 0x%lx, "
319 			"`%s'\n", status, strerror(status)));
320 		return status;
321 	}
322 	*cookie = (void *)iterator;
323 	TRACE(("udf_open_dir: *cookie = %p\n", *cookie));
324 
325 	return B_OK;
326 }
327 
328 
329 static status_t
330 udf_close_dir(fs_volume *_volume, fs_vnode *node, void *_cookie)
331 {
332 	TRACE(("udf_close_dir: _volume = %p, node = %p\n", _volume, node));
333 	return B_OK;
334 }
335 
336 
337 static status_t
338 udf_free_dir_cookie(fs_volume *_volume, fs_vnode *node, void *_cookie)
339 {
340 	TRACE(("udf_free_dir_cookie: _volume = %p, node = %p\n", _volume, node));
341 	return B_OK;
342 }
343 
344 
345 static status_t
346 udf_read_dir(fs_volume *_volume, fs_vnode *vnode, void *cookie,
347 	struct dirent *dirent, size_t bufferSize, uint32 *_num)
348 {
349 	TRACE(("udf_read_dir: _volume = %p, vnode = %p, bufferSize = %ld\n",
350 		_volume, vnode, bufferSize));
351 
352 	if (!_volume || !vnode || !cookie || !_num || bufferSize < sizeof(dirent))
353 		return B_BAD_VALUE;
354 
355 	Volume *volume = (Volume *)_volume->private_volume;
356 	Icb *dir = (Icb *)vnode->private_node;
357 	DirectoryIterator *iterator = (DirectoryIterator *)cookie;
358 
359 	if (dir != iterator->Parent()) {
360 		TRACE_ERROR(("udf_read_dir: Icb does not match parent Icb of given "
361 			"DirectoryIterator! (iterator->Parent = %p)\n", iterator->Parent()));
362 		return B_BAD_VALUE;
363 	}
364 
365 	uint32 nameLength = bufferSize - sizeof(struct dirent) + 1;
366 	ino_t id;
367 	status_t status = iterator->GetNextEntry(dirent->d_name, &nameLength, &id);
368 	TRACE(("udf_read_dir: dirent->d_name = %s, length = %ld\n", dirent->d_name, nameLength));
369 	if (!status) {
370 		*_num = 1;
371 		dirent->d_dev = volume->ID();
372 		dirent->d_ino = id;
373 		dirent->d_reclen = sizeof(struct dirent) + nameLength - 1;
374 	} else {
375 		*_num = 0;
376 		// Clear the status for end of directory
377 		if (status == B_ENTRY_NOT_FOUND)
378 			status = B_OK;
379 	}
380 
381 	RETURN(status);
382 }
383 
384 
385 status_t
386 udf_rewind_dir(fs_volume *volume, fs_vnode *vnode, void *cookie)
387 {
388 	TRACE(("udf_rewind_dir: volume = %p, vnode = %p, cookie = %p\n",
389 		volume, vnode, cookie));
390 
391 	if (!volume || !vnode || !cookie)
392 		RETURN(B_BAD_VALUE);
393 
394 	Icb *dir = (Icb *)vnode->private_node;
395 	DirectoryIterator *iterator = (DirectoryIterator *)cookie;
396 
397 	if (dir != iterator->Parent()) {
398 		PRINT(("udf_rewind_dir: icb does not match parent Icb of given "
399 			"DirectoryIterator! (iterator->Parent = %p)\n", iterator->Parent()));
400 		return B_BAD_VALUE;
401 	}
402 
403 	iterator->Rewind();
404 
405 	return B_OK;
406 }
407 
408 
409 //	#pragma mark -
410 
411 
412 /*! \brief mount
413 
414 	\todo I'm using the B_GET_GEOMETRY ioctl() to find out where the end of the
415 	      partition is. This won't work for handling multi-session semantics correctly.
416 	      To support them correctly in R5 I need either:
417 	      - A way to get the proper info (best)
418 	      - To ignore trying to find anchor volume descriptor pointers at
419 	        locations N-256 and N. (acceptable, perhaps, but not really correct)
420 	      Either way we should address this problem properly for OBOS::R1.
421 	\todo Looks like B_GET_GEOMETRY doesn't work on non-device files (i.e.
422 	      disk images), so I need to use stat or something else for those
423 	      instances.
424 */
425 static status_t
426 udf_mount(fs_volume *_volume, const char *_device, uint32 flags,
427 	const char *args, ino_t *_rootVnodeID)
428 {
429 	TRACE(("udf_mount: device = %s\n", _device));
430 	status_t status = B_OK;
431 	Volume *volume = NULL;
432 	off_t deviceOffset = 0;
433 	off_t numBlock = 0;
434 	partition_info info;
435 	device_geometry geometry;
436 
437 	// Here we need to figure out the length of the device, and if we're
438 	// attempting to open a multisession volume, we need to figure out the
439 	// offset into the raw disk at which the volume begins, then open
440 	// the raw volume itself instead of the fake partition device the
441 	// kernel gives us, since multisession UDF volumes are allowed to access
442 	// the data in their own partition, as well as the data in any partitions
443 	// that precede them physically on the disc.
444 	int device = open(_device, O_RDONLY);
445 	status = device < B_OK ? device : B_OK;
446 	if (!status) {
447 		// First try to treat the device like a special partition device. If that's
448 		// what we have, then we can use the partition_info data to figure out the
449 		// name of the raw device (which we'll open instead), the offset into the
450 		// raw device at which the volume of interest will begin, and the total
451 		// length from the beginning of the raw device that we're allowed to access.
452 		//
453 		// If that fails, then we try to treat the device as an actual raw device,
454 		// and see if we can get the device size with B_GET_GEOMETRY syscall, since
455 		// stat()ing a raw device appears to not work.
456 		//
457 		// Finally, if that also fails, we're probably stuck with trying to mount
458 		// a regular file, so we just stat() it to get the device size.
459 		//
460 		// If that fails, you're just SOL.
461 
462 		if (ioctl(device, B_GET_PARTITION_INFO, &info) == 0) {
463 			TRACE(("partition_info:\n"));
464 			TRACE(("\toffset:             %Ld\n", info.offset));
465 			TRACE(("\tsize:               %Ld\n", info.size));
466 			TRACE(("\tlogical_block_size: %ld\n", info.logical_block_size));
467 			TRACE(("\tsession:            %ld\n", info.session));
468 			TRACE(("\tpartition:          %ld\n", info.partition));
469 			TRACE(("\tdevice:             `%s'\n", info.device));
470 			_device = info.device;
471 			deviceOffset = info.offset / info.logical_block_size;
472 			numBlock = deviceOffset + info.size / info.logical_block_size;
473 		} else if (ioctl(device, B_GET_GEOMETRY, &geometry) == 0) {
474 			TRACE(("geometry_info:\n"));
475 			TRACE(("\tsectors_per_track: %ld\n", geometry.sectors_per_track));
476 			TRACE(("\tcylinder_count:    %ld\n", geometry.cylinder_count));
477 			TRACE(("\thead_count:        %ld\n", geometry.head_count));
478 			deviceOffset = 0;
479 			numBlock = (off_t)geometry.sectors_per_track
480 				* geometry.cylinder_count * geometry.head_count;
481 		} else {
482 			struct stat stat;
483 			status = fstat(device, &stat) < 0 ? B_ERROR : B_OK;
484 			if (!status) {
485 				TRACE(("stat_info:\n"));
486 				TRACE(("\tst_size: %Ld\n", stat.st_size));
487 				deviceOffset = 0;
488 				numBlock = stat.st_size / 2048;
489 			}
490 		}
491 		// Close the device
492 		close(device);
493 	}
494 
495 	// Create and mount the volume
496 	volume = new(std::nothrow) Volume(_volume);
497 	status = volume->Mount(_device, deviceOffset, numBlock, 2048, flags);
498 	if (status != B_OK) {
499 		delete volume;
500 		return status;
501 	}
502 
503 	_volume->private_volume = volume;
504 	_volume->ops = &gUDFVolumeOps;
505 	*_rootVnodeID = volume->RootIcb()->Id();
506 
507 	TRACE(("udf_mount: succefully mounted the partition\n"));
508 	return B_OK;
509 }
510 
511 
512 //	#pragma mark -
513 
514 
515 static status_t
516 udf_std_ops(int32 op, ...)
517 {
518 	switch (op) {
519 		case B_MODULE_INIT:
520 		case B_MODULE_UNINIT:
521 			return B_OK;
522 		default:
523 			return B_ERROR;
524 	}
525 }
526 
527 fs_volume_ops gUDFVolumeOps = {
528 	&udf_unmount,
529 	&udf_read_fs_stat,
530 	NULL,	// write_fs_stat
531 	NULL,	// sync
532 	&udf_get_vnode,
533 
534 	/* index directory & index operations */
535 	NULL,	// open_index_dir
536 	NULL,	// close_index_dir
537 	NULL,	// free_index_dir_cookie
538 	NULL,	// read_index_dir
539 	NULL,	// rewind_index_dir
540 	NULL,	// create_index
541 	NULL,	// remove_index
542 	NULL,	// read_index_stat
543 
544 	/* query operations */
545 	NULL,	// open_query
546 	NULL,	// close_query
547 	NULL,	// free_query_cookie
548 	NULL,	// read_query
549 	NULL,	// rewind_query
550 
551 	/* support for FS layers */
552 	NULL,	// create_sub_vnode
553 	NULL,	// delete_sub_vnode
554 };
555 
556 fs_vnode_ops gUDFVnodeOps = {
557 	/* vnode operatoins */
558 	&udf_lookup,
559 	NULL,	// get_vnode_name
560 	&udf_put_vnode,
561 	&udf_remove_vnode,
562 
563 	/* VM file access */
564 	NULL,	// can_page
565 	NULL,	// read_pages
566 	NULL,	// write_pages
567 
568 	/* asynchronous I/O */
569 	NULL,	// io()
570 	NULL,	// cancel_io()
571 
572 	/* cache file access */
573 	NULL,	// &udf_get_file_map,
574 
575 	/* common operations */
576 	NULL,	// ioctl
577 	NULL,	// set_flags
578 	NULL,	// select
579 	NULL,	// deselect
580 	NULL,	// fsync
581 	NULL,	// read_symlink
582 	NULL,	// create_symlnk
583 	NULL,	// link
584 	NULL,	// unlink
585 	NULL,	// rename
586 	&udf_access,
587 	&udf_read_stat,
588 	NULL,	// write_stat
589 
590 	/* file operations */
591 	NULL,	// create
592 	&udf_open,
593 	&udf_close,
594 	&udf_free_cookie,
595 	&udf_read,
596 	NULL,	// write
597 
598 	/* directory operations */
599 	NULL,	// create_dir
600 	NULL,	// remove_dir
601 	&udf_open_dir,
602 	&udf_close_dir,
603 	&udf_free_dir_cookie,
604 	&udf_read_dir,
605 	&udf_rewind_dir,
606 
607 	/* attribue directory operations */
608 	NULL,	// open_attr_dir
609 	NULL,	// close_attr_dir
610 	NULL,	// free_attr_dir_cookie
611 	NULL,	// read_attr_dir
612 	NULL,	// rewind_attr_dir
613 
614 	/* attribute operations */
615 	NULL,	// create_attr
616 	NULL,	// open_attr
617 	NULL,	// close_attr
618 	NULL,	// free_attr_cookie
619 	NULL,	// read_attr
620 	NULL,	// write_attr
621 	NULL,	// read_attr_stat
622 	NULL,	// write_attr_stat
623 	NULL,	// rename_attr
624 	NULL,	// remove_attr
625 
626 	/* support for node and FS layers */
627 	NULL,	// create_special_node
628 	NULL	// get_super_vnode
629 
630 };
631 
632 static file_system_module_info sUDFFileSystem = {
633 	{
634 		"file_systems/udf" B_CURRENT_FS_API_VERSION,
635 		0,
636 		udf_std_ops,
637 	},
638 
639 	"udf",					// short_name
640 	"UDF File System",		// pretty_name
641 	0, // DDM flags
642 
643 	&udf_identify_partition,
644 	&udf_scan_partition,
645 	NULL, // &udf_free_identify_patition_cookie,
646 	NULL,	// free_partition_content_cookie()
647 
648 	&udf_mount,
649 
650 	NULL,
651 };
652 
653 module_info *modules[] = {
654 	(module_info *)&sUDFFileSystem,
655 	NULL,
656 };
657