xref: /haiku/src/add-ons/kernel/file_systems/udf/kernel_interface.cpp (revision 0562493379cd52eb7103531f895f10bb8e77c085)
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 	stat->st_blocks = (stat->st_size + 511) / 512;
236 
237 	// File times. For now, treat the modification time as creation
238 	// time as well, since true creation time is an optional extended
239 	// attribute, and supporting EAs is going to be a PITA. ;-)
240 	stat->st_atime = icb->AccessTime();
241 	stat->st_mtime = stat->st_ctime = stat->st_crtime = icb->ModificationTime();
242 
243 	TRACE(("udf_read_stat: mode = 0x%lx, st_ino: %Ld\n", stat->st_mode,
244 		stat->st_ino));
245 
246 	return B_OK;
247 }
248 
249 
250 static status_t
251 udf_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
252 {
253 	TRACE(("udf_open: _volume = %p, _node = %p\n", _volume, _node));
254 	return B_OK;
255 }
256 
257 
258 static status_t
259 udf_close(fs_volume* _volume, fs_vnode* _node, void* _cookie)
260 {
261 	TRACE(("udf_close: _volume = %p, _node = %p\n", _volume, _node));
262 	return B_OK;
263 }
264 
265 
266 static status_t
267 udf_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
268 {
269 	TRACE(("udf_free_cookie: _volume = %p, _node = %p\n", _volume, _node));
270 	return B_OK;
271 }
272 
273 
274 static status_t
275 udf_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
276 {
277 	TRACE(("udf_access: _volume = %p, _node = %p\n", _volume, _node));
278 	return B_OK;
279 }
280 
281 
282 static status_t
283 udf_read(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos,
284 	void *buffer, size_t *length)
285 {
286 	TRACE(("udf_read: ID = %ld, pos = %lld, length = %lu\n",
287 		((Volume *)volume->private_volume)->ID(), pos, *length));
288 
289 	Icb *icb = (Icb *)vnode->private_node;
290 
291 //	if (!inode->HasUserAccessableStream()) {
292 //		*_length = 0;
293 //		RETURN_ERROR(B_BAD_VALUE);
294 //	}
295 
296 	RETURN(icb->Read(pos, buffer, length));
297 }
298 
299 
300 static status_t
301 udf_open_dir(fs_volume *volume, fs_vnode *vnode, void **cookie)
302 {
303 	TRACE(("udf_open_dir: volume = %p, vnode = %p\n", volume, vnode));
304 
305 	if (!volume || !vnode || !cookie)
306 		RETURN(B_BAD_VALUE);
307 
308 	Icb *dir = (Icb *)vnode->private_node;
309 
310 	if (!dir->IsDirectory()) {
311 		TRACE_ERROR(("udf_open_dir: given Icb is not a directory (type: %d)\n",
312 			dir->Type()));
313 		return B_BAD_VALUE;
314 	}
315 
316 	DirectoryIterator *iterator = NULL;
317 	status_t status = dir->GetDirectoryIterator(&iterator);
318 	if (status != B_OK) {
319 		TRACE_ERROR(("udf_open_dir: error getting directory iterator: 0x%lx, "
320 			"`%s'\n", status, strerror(status)));
321 		return status;
322 	}
323 	*cookie = (void *)iterator;
324 	TRACE(("udf_open_dir: *cookie = %p\n", *cookie));
325 
326 	return B_OK;
327 }
328 
329 
330 static status_t
331 udf_close_dir(fs_volume *_volume, fs_vnode *node, void *_cookie)
332 {
333 	TRACE(("udf_close_dir: _volume = %p, node = %p\n", _volume, node));
334 	return B_OK;
335 }
336 
337 
338 static status_t
339 udf_free_dir_cookie(fs_volume *_volume, fs_vnode *node, void *_cookie)
340 {
341 	TRACE(("udf_free_dir_cookie: _volume = %p, node = %p\n", _volume, node));
342 	return B_OK;
343 }
344 
345 
346 static status_t
347 udf_read_dir(fs_volume *_volume, fs_vnode *vnode, void *cookie,
348 	struct dirent *dirent, size_t bufferSize, uint32 *_num)
349 {
350 	TRACE(("udf_read_dir: _volume = %p, vnode = %p, bufferSize = %ld\n",
351 		_volume, vnode, bufferSize));
352 
353 	if (!_volume || !vnode || !cookie || !_num || bufferSize < sizeof(dirent))
354 		return B_BAD_VALUE;
355 
356 	Volume *volume = (Volume *)_volume->private_volume;
357 	Icb *dir = (Icb *)vnode->private_node;
358 	DirectoryIterator *iterator = (DirectoryIterator *)cookie;
359 
360 	if (dir != iterator->Parent()) {
361 		TRACE_ERROR(("udf_read_dir: Icb does not match parent Icb of given "
362 			"DirectoryIterator! (iterator->Parent = %p)\n", iterator->Parent()));
363 		return B_BAD_VALUE;
364 	}
365 
366 	uint32 nameLength = bufferSize - sizeof(struct dirent) + 1;
367 	ino_t id;
368 	status_t status = iterator->GetNextEntry(dirent->d_name, &nameLength, &id);
369 	TRACE(("udf_read_dir: dirent->d_name = %s, length = %ld\n", dirent->d_name, nameLength));
370 	if (!status) {
371 		*_num = 1;
372 		dirent->d_dev = volume->ID();
373 		dirent->d_ino = id;
374 		dirent->d_reclen = sizeof(struct dirent) + nameLength - 1;
375 	} else {
376 		*_num = 0;
377 		// Clear the status for end of directory
378 		if (status == B_ENTRY_NOT_FOUND)
379 			status = B_OK;
380 	}
381 
382 	RETURN(status);
383 }
384 
385 
386 status_t
387 udf_rewind_dir(fs_volume *volume, fs_vnode *vnode, void *cookie)
388 {
389 	TRACE(("udf_rewind_dir: volume = %p, vnode = %p, cookie = %p\n",
390 		volume, vnode, cookie));
391 
392 	if (!volume || !vnode || !cookie)
393 		RETURN(B_BAD_VALUE);
394 
395 	Icb *dir = (Icb *)vnode->private_node;
396 	DirectoryIterator *iterator = (DirectoryIterator *)cookie;
397 
398 	if (dir != iterator->Parent()) {
399 		PRINT(("udf_rewind_dir: icb does not match parent Icb of given "
400 			"DirectoryIterator! (iterator->Parent = %p)\n", iterator->Parent()));
401 		return B_BAD_VALUE;
402 	}
403 
404 	iterator->Rewind();
405 
406 	return B_OK;
407 }
408 
409 
410 //	#pragma mark -
411 
412 
413 /*! \brief mount
414 
415 	\todo I'm using the B_GET_GEOMETRY ioctl() to find out where the end of the
416 	      partition is. This won't work for handling multi-session semantics correctly.
417 	      To support them correctly in R5 I need either:
418 	      - A way to get the proper info (best)
419 	      - To ignore trying to find anchor volume descriptor pointers at
420 	        locations N-256 and N. (acceptable, perhaps, but not really correct)
421 	      Either way we should address this problem properly for OBOS::R1.
422 	\todo Looks like B_GET_GEOMETRY doesn't work on non-device files (i.e.
423 	      disk images), so I need to use stat or something else for those
424 	      instances.
425 */
426 static status_t
427 udf_mount(fs_volume *_volume, const char *_device, uint32 flags,
428 	const char *args, ino_t *_rootVnodeID)
429 {
430 	TRACE(("udf_mount: device = %s\n", _device));
431 	status_t status = B_OK;
432 	Volume *volume = NULL;
433 	off_t deviceOffset = 0;
434 	off_t numBlock = 0;
435 	partition_info info;
436 	device_geometry geometry;
437 
438 	// Here we need to figure out the length of the device, and if we're
439 	// attempting to open a multisession volume, we need to figure out the
440 	// offset into the raw disk at which the volume begins, then open
441 	// the raw volume itself instead of the fake partition device the
442 	// kernel gives us, since multisession UDF volumes are allowed to access
443 	// the data in their own partition, as well as the data in any partitions
444 	// that precede them physically on the disc.
445 	int device = open(_device, O_RDONLY);
446 	status = device < B_OK ? device : B_OK;
447 	if (!status) {
448 		// First try to treat the device like a special partition device. If that's
449 		// what we have, then we can use the partition_info data to figure out the
450 		// name of the raw device (which we'll open instead), the offset into the
451 		// raw device at which the volume of interest will begin, and the total
452 		// length from the beginning of the raw device that we're allowed to access.
453 		//
454 		// If that fails, then we try to treat the device as an actual raw device,
455 		// and see if we can get the device size with B_GET_GEOMETRY syscall, since
456 		// stat()ing a raw device appears to not work.
457 		//
458 		// Finally, if that also fails, we're probably stuck with trying to mount
459 		// a regular file, so we just stat() it to get the device size.
460 		//
461 		// If that fails, you're just SOL.
462 
463 		if (ioctl(device, B_GET_PARTITION_INFO, &info) == 0) {
464 			TRACE(("partition_info:\n"));
465 			TRACE(("\toffset:             %Ld\n", info.offset));
466 			TRACE(("\tsize:               %Ld\n", info.size));
467 			TRACE(("\tlogical_block_size: %ld\n", info.logical_block_size));
468 			TRACE(("\tsession:            %ld\n", info.session));
469 			TRACE(("\tpartition:          %ld\n", info.partition));
470 			TRACE(("\tdevice:             `%s'\n", info.device));
471 			_device = info.device;
472 			deviceOffset = info.offset / info.logical_block_size;
473 			numBlock = deviceOffset + info.size / info.logical_block_size;
474 		} else if (ioctl(device, B_GET_GEOMETRY, &geometry) == 0) {
475 			TRACE(("geometry_info:\n"));
476 			TRACE(("\tsectors_per_track: %ld\n", geometry.sectors_per_track));
477 			TRACE(("\tcylinder_count:    %ld\n", geometry.cylinder_count));
478 			TRACE(("\thead_count:        %ld\n", geometry.head_count));
479 			deviceOffset = 0;
480 			numBlock = (off_t)geometry.sectors_per_track
481 				* geometry.cylinder_count * geometry.head_count;
482 		} else {
483 			struct stat stat;
484 			status = fstat(device, &stat) < 0 ? B_ERROR : B_OK;
485 			if (!status) {
486 				TRACE(("stat_info:\n"));
487 				TRACE(("\tst_size: %Ld\n", stat.st_size));
488 				deviceOffset = 0;
489 				numBlock = stat.st_size / 2048;
490 			}
491 		}
492 		// Close the device
493 		close(device);
494 	}
495 
496 	// Create and mount the volume
497 	volume = new(std::nothrow) Volume(_volume);
498 	status = volume->Mount(_device, deviceOffset, numBlock, 2048, flags);
499 	if (status != B_OK) {
500 		delete volume;
501 		return status;
502 	}
503 
504 	_volume->private_volume = volume;
505 	_volume->ops = &gUDFVolumeOps;
506 	*_rootVnodeID = volume->RootIcb()->Id();
507 
508 	TRACE(("udf_mount: succefully mounted the partition\n"));
509 	return B_OK;
510 }
511 
512 
513 //	#pragma mark -
514 
515 
516 static status_t
517 udf_std_ops(int32 op, ...)
518 {
519 	switch (op) {
520 		case B_MODULE_INIT:
521 		case B_MODULE_UNINIT:
522 			return B_OK;
523 		default:
524 			return B_ERROR;
525 	}
526 }
527 
528 fs_volume_ops gUDFVolumeOps = {
529 	&udf_unmount,
530 	&udf_read_fs_stat,
531 	NULL,	// write_fs_stat
532 	NULL,	// sync
533 	&udf_get_vnode,
534 
535 	/* index directory & index operations */
536 	NULL,	// open_index_dir
537 	NULL,	// close_index_dir
538 	NULL,	// free_index_dir_cookie
539 	NULL,	// read_index_dir
540 	NULL,	// rewind_index_dir
541 	NULL,	// create_index
542 	NULL,	// remove_index
543 	NULL,	// read_index_stat
544 
545 	/* query operations */
546 	NULL,	// open_query
547 	NULL,	// close_query
548 	NULL,	// free_query_cookie
549 	NULL,	// read_query
550 	NULL,	// rewind_query
551 
552 	/* support for FS layers */
553 	NULL,	// create_sub_vnode
554 	NULL,	// delete_sub_vnode
555 };
556 
557 fs_vnode_ops gUDFVnodeOps = {
558 	/* vnode operatoins */
559 	&udf_lookup,
560 	NULL,	// get_vnode_name
561 	&udf_put_vnode,
562 	&udf_remove_vnode,
563 
564 	/* VM file access */
565 	NULL,	// can_page
566 	NULL,	// read_pages
567 	NULL,	// write_pages
568 
569 	/* asynchronous I/O */
570 	NULL,	// io()
571 	NULL,	// cancel_io()
572 
573 	/* cache file access */
574 	NULL,	// &udf_get_file_map,
575 
576 	/* common operations */
577 	NULL,	// ioctl
578 	NULL,	// set_flags
579 	NULL,	// select
580 	NULL,	// deselect
581 	NULL,	// fsync
582 	NULL,	// read_symlink
583 	NULL,	// create_symlnk
584 	NULL,	// link
585 	NULL,	// unlink
586 	NULL,	// rename
587 	&udf_access,
588 	&udf_read_stat,
589 	NULL,	// write_stat
590 
591 	/* file operations */
592 	NULL,	// create
593 	&udf_open,
594 	&udf_close,
595 	&udf_free_cookie,
596 	&udf_read,
597 	NULL,	// write
598 
599 	/* directory operations */
600 	NULL,	// create_dir
601 	NULL,	// remove_dir
602 	&udf_open_dir,
603 	&udf_close_dir,
604 	&udf_free_dir_cookie,
605 	&udf_read_dir,
606 	&udf_rewind_dir,
607 
608 	/* attribue directory operations */
609 	NULL,	// open_attr_dir
610 	NULL,	// close_attr_dir
611 	NULL,	// free_attr_dir_cookie
612 	NULL,	// read_attr_dir
613 	NULL,	// rewind_attr_dir
614 
615 	/* attribute operations */
616 	NULL,	// create_attr
617 	NULL,	// open_attr
618 	NULL,	// close_attr
619 	NULL,	// free_attr_cookie
620 	NULL,	// read_attr
621 	NULL,	// write_attr
622 	NULL,	// read_attr_stat
623 	NULL,	// write_attr_stat
624 	NULL,	// rename_attr
625 	NULL,	// remove_attr
626 
627 	/* support for node and FS layers */
628 	NULL,	// create_special_node
629 	NULL	// get_super_vnode
630 
631 };
632 
633 static file_system_module_info sUDFFileSystem = {
634 	{
635 		"file_systems/udf" B_CURRENT_FS_API_VERSION,
636 		0,
637 		udf_std_ops,
638 	},
639 
640 	"udf",					// short_name
641 	"UDF File System",		// pretty_name
642 	0, // DDM flags
643 
644 	&udf_identify_partition,
645 	&udf_scan_partition,
646 	NULL, // &udf_free_identify_patition_cookie,
647 	NULL,	// free_partition_content_cookie()
648 
649 	&udf_mount,
650 
651 	NULL,
652 };
653 
654 module_info *modules[] = {
655 	(module_info *)&sUDFFileSystem,
656 	NULL,
657 };
658