vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / file_systems / udf / kernel_interface.cpp
blob7c0b6d40de1d5c5f1917d9e9384ce8630e7cc1fc
1 /*
2 * Copyright 2012, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2010, Michael Lotz, mmlr@mlotz.ch.
4 * Copyright 2008, Salvatore Benedetto, salvatore.benedetto@gmail.com.
5 * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
6 * Distributed under the terms of the MIT License.
7 */
10 /*! \file kernel_interface.cpp */
13 #include <Drivers.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include <KernelExport.h>
21 #include <util/kernel_cpp.h>
23 #include <io_requests.h>
25 #include "Icb.h"
26 #include "Recognition.h"
27 #include "Utils.h"
28 #include "Volume.h"
31 #undef TRACE
32 #undef TRACE_ERROR
33 //#define UDF_KERNEL_INTERFACE_DEBUG
34 #ifdef UDF_KERNEL_INTERFACE_DEBUG
35 # define TRACE(x) dprintf x
36 # define TRACE_ERROR(x) dprintf x
37 #else
38 # define TRACE(x) /* nothing */
39 # define TRACE_ERROR(x) dprintf x
40 #endif
42 extern fs_volume_ops gUDFVolumeOps;
43 extern fs_vnode_ops gUDFVnodeOps;
46 struct identify_cookie {
47 struct logical_volume_descriptor logical_volume_descriptor;
51 // #pragma mark - io callbacks
54 static status_t
55 iterative_io_get_vecs_hook(void *cookie, io_request *request, off_t offset,
56 size_t size, struct file_io_vec *vecs, size_t *count)
58 Icb *icb = (Icb *)cookie;
59 return file_map_translate(icb->FileMap(), offset, size, vecs, count,
60 icb->GetVolume()->BlockSize());
64 static status_t
65 iterative_io_finished_hook(void *cookie, io_request *request, status_t status,
66 bool partialTransfer, size_t bytesTransferred)
68 // nothing to do
69 return B_OK;
73 // #pragma mark - fs_volume_ops fuctions
76 static float
77 udf_identify_partition(int fd, partition_data *partition, void **_cookie)
79 TRACE(("udf_identify_partition: fd = %d, id = %ld, offset = %Ld, size = %Ld "
80 "content_size = %Ld, block_size = %lu\n", fd, partition->id,
81 partition->offset, partition->size, partition->content_size,
82 partition->block_size));
84 primary_volume_descriptor primaryVolumeDescriptor;
85 logical_volume_descriptor logicalVolumeDescriptor;
86 partition_descriptor partitionDescriptors[kMaxPartitionDescriptors];
87 uint8 descriptorCount = kMaxPartitionDescriptors;
88 uint32 blockShift;
89 status_t error = udf_recognize(fd, partition->offset, partition->size,
90 partition->block_size, blockShift, primaryVolumeDescriptor,
91 logicalVolumeDescriptor, partitionDescriptors, descriptorCount);
92 if (error != B_OK)
93 return -1;
95 identify_cookie *cookie = new(std::nothrow) identify_cookie;
96 if (cookie == NULL)
97 return -1;
99 cookie->logical_volume_descriptor = logicalVolumeDescriptor;
100 *_cookie = cookie;
101 return 0.8f;
105 static status_t
106 udf_scan_partition(int fd, partition_data *partition, void *_cookie)
108 TRACE(("udf_scan_partition: fd = %d\n", fd));
109 identify_cookie *cookie = (identify_cookie *)_cookie;
110 logical_volume_descriptor &volumeDescriptor
111 = cookie->logical_volume_descriptor;
113 partition->status = B_PARTITION_VALID;
114 partition->flags |= B_PARTITION_FILE_SYSTEM;
115 partition->content_size = partition->size;
116 // TODO: not actually correct
117 partition->block_size = volumeDescriptor.logical_block_size();
119 UdfString name(volumeDescriptor.logical_volume_identifier());
120 partition->content_name = strdup(name.Utf8());
121 if (partition->content_name == NULL)
122 return B_NO_MEMORY;
124 return B_OK;
128 static void
129 udf_free_identify_partition_cookie(partition_data *partition, void *cookie)
131 delete (identify_cookie *)cookie;
135 static status_t
136 udf_unmount(fs_volume *_volume)
138 TRACE(("udb_unmount: _volume = %p\n", _volume));
139 Volume *volume = (Volume *)_volume->private_volume;
140 delete volume;
141 return B_OK;
145 static status_t
146 udf_read_fs_stat(fs_volume *_volume, struct fs_info *info)
148 TRACE(("udf_read_fs_stat: _volume = %p, info = %p\n", _volume, info));
150 Volume *volume = (Volume *)_volume->private_volume;
152 // File system flags.
153 info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
155 info->io_size = 65536;
156 // whatever is appropriate here? Just use the same value as BFS (and iso9660) for now
158 info->block_size = volume->BlockSize();
159 info->total_blocks = volume->Length();
160 info->free_blocks = 0;
162 // Volume name
163 sprintf(info->volume_name, "%s", volume->Name());
165 // File system name
166 strcpy(info->fsh_name, "udf");
168 return B_OK;
172 static status_t
173 udf_get_vnode(fs_volume *_volume, ino_t id, fs_vnode *node, int *_type,
174 uint32 *_flags, bool reenter)
176 TRACE(("udf_get_vnode: _volume = %p, _node = %p, reenter = %s\n",
177 _volume, _node, (reenter ? "true" : "false")));
179 Volume *volume = (Volume *)_volume->private_volume;
181 // Convert the given vnode id to an address, and create
182 // and return a corresponding Icb object for it.
183 TRACE(("udf_get_vnode: id = %Ld, blockSize = %lu\n", id,
184 volume->BlockSize()));
186 Icb *icb = new(std::nothrow) Icb(volume,
187 to_long_address(id, volume->BlockSize()));
188 if (icb == NULL)
189 return B_NO_MEMORY;
191 if (icb->InitCheck() == B_OK) {
192 node->private_node = icb;
193 node->ops = &gUDFVnodeOps;
194 *_type = icb->Mode();
195 *_flags = 0;
196 return B_OK;
199 TRACE_ERROR(("udf_get_vnode: InitCheck failed\n"));
200 delete icb;
201 return B_ERROR;
205 // #pragma mark - fs_vnode_ops functions
208 static status_t
209 udf_lookup(fs_volume *_volume, fs_vnode *_directory, const char *file,
210 ino_t *vnodeID)
212 TRACE(("udf_lookup: _directory = %p, filename = %s\n", _directory, file));
214 Volume *volume = (Volume *)_volume->private_volume;
215 Icb *dir = (Icb *)_directory->private_node;
216 Icb *node = NULL;
218 status_t status = B_OK;
220 if (strcmp(file, ".") == 0) {
221 TRACE(("udf_lookup: file = ./\n"));
222 *vnodeID = dir->Id();
223 status = get_vnode(volume->FSVolume(), *vnodeID, (void **)&node);
224 if (status != B_OK)
225 return B_ENTRY_NOT_FOUND;
226 } else {
227 status = dir->Find(file, vnodeID);
228 if (status != B_OK)
229 return status;
231 Icb *icb;
232 status = get_vnode(volume->FSVolume(), *vnodeID, (void **)&icb);
233 if (status != B_OK)
234 return B_ENTRY_NOT_FOUND;
236 TRACE(("udf_lookup: vnodeId = %Ld found!\n", *vnodeID));
238 return B_OK;
242 static status_t
243 udf_put_vnode(fs_volume *volume, fs_vnode *node, bool reenter)
245 TRACE(("udf_put_vnode: volume = %p, node = %p\n", volume, node));
246 // No debug-to-file in release_vnode; can cause a deadlock in
247 // rare circumstances.
248 #if !DEBUG_TO_FILE
249 DEBUG_INIT_ETC(NULL, ("node: %p", node));
250 #endif
251 Icb *icb = (Icb *)node->private_node;
252 delete icb;
253 #if !DEBUG_TO_FILE
254 RETURN(B_OK);
255 #else
256 return B_OK;
257 #endif
261 static status_t
262 udf_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
264 TRACE(("udf_remove_vnode: _volume = %p, _node = %p\n", _volume, _node));
265 return B_ERROR;
269 static status_t
270 udf_read_stat(fs_volume *_volume, fs_vnode *node, struct stat *stat)
272 TRACE(("udf_read_stat: _volume = %p, node = %p\n", _volume, node));
274 if (!_volume || !node || !stat)
275 return B_BAD_VALUE;
277 Volume *volume = (Volume *)_volume->private_volume;
278 Icb *icb = (Icb *)node->private_node;
280 stat->st_dev = volume->ID();
281 stat->st_ino = icb->Id();
282 stat->st_nlink = icb->FileLinkCount();
283 stat->st_blksize = volume->BlockSize();
285 TRACE(("udf_read_stat: st_dev = %ld, st_ino = %Ld, st_blksize = %d\n",
286 stat->st_dev, stat->st_ino, stat->st_blksize));
288 stat->st_uid = icb->Uid();
289 stat->st_gid = icb->Gid();
291 stat->st_mode = icb->Mode();
292 stat->st_size = icb->Length();
293 stat->st_blocks = (stat->st_size + 511) / 512;
295 // File times. For now, treat the modification time as creation
296 // time as well, since true creation time is an optional extended
297 // attribute, and supporting EAs is going to be a PITA. ;-)
298 icb->GetAccessTime(stat->st_atim);
299 icb->GetModificationTime(stat->st_mtim);
300 icb->GetModificationTime(stat->st_ctim);
301 icb->GetModificationTime(stat->st_crtim);
302 //icb->GetChangeTime(stat->st_ctim);
303 //icb->GetCreationTime(stat->st_crtim);
305 TRACE(("udf_read_stat: mode = 0x%x, st_ino: %Ld\n", stat->st_mode,
306 stat->st_ino));
308 return B_OK;
312 static status_t
313 udf_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
315 TRACE(("udf_open: _volume = %p, _node = %p\n", _volume, _node));
316 return B_OK;
320 static status_t
321 udf_close(fs_volume* _volume, fs_vnode* _node, void* _cookie)
323 TRACE(("udf_close: _volume = %p, _node = %p\n", _volume, _node));
324 return B_OK;
328 static status_t
329 udf_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
331 TRACE(("udf_free_cookie: _volume = %p, _node = %p\n", _volume, _node));
332 return B_OK;
336 static status_t
337 udf_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
339 TRACE(("udf_access: _volume = %p, _node = %p\n", _volume, _node));
340 return B_OK;
344 static status_t
345 udf_read(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos,
346 void *buffer, size_t *length)
348 TRACE(("udf_read: ID = %ld, pos = %lld, length = %lu\n",
349 ((Volume *)volume->private_volume)->ID(), pos, *length));
351 Icb *icb = (Icb *)vnode->private_node;
352 DEBUG_INIT_ETC("udf_read", ("ID = %ld, pos = %lld, length = %lu",
353 ((Volume *)volume->private_volume)->ID(), pos, *length));
355 // if (!inode->HasUserAccessableStream()) {
356 // *_length = 0;
357 // RETURN_ERROR(B_BAD_VALUE);
358 // }
360 RETURN(icb->Read(pos, buffer, length));
364 static status_t
365 udf_io(fs_volume *volume, fs_vnode *vnode, void *cookie, io_request *request)
367 if (io_request_is_write(request)) {
368 notify_io_request(request, B_READ_ONLY_DEVICE);
369 return B_READ_ONLY_DEVICE;
372 Icb *icb = (Icb *)vnode->private_node;
373 if (icb->FileCache() == NULL) {
374 notify_io_request(request, B_BAD_VALUE);
375 return B_BAD_VALUE;
378 return do_iterative_fd_io(((Volume *)volume->private_volume)->Device(),
379 request, iterative_io_get_vecs_hook, iterative_io_finished_hook, icb);
383 static status_t
384 udf_get_file_map(fs_volume *_volume, fs_vnode *vnode, off_t offset, size_t size,
385 struct file_io_vec *vecs, size_t *count)
387 Icb *icb = (Icb *)vnode->private_node;
388 return icb->GetFileMap(offset, size, vecs, count);
392 static status_t
393 udf_open_dir(fs_volume *volume, fs_vnode *vnode, void **cookie)
395 TRACE(("udf_open_dir: volume = %p, vnode = %p\n", volume, vnode));
396 DEBUG_INIT_ETC("udf_open_dir", ("ID = %ld",
397 ((Volume *)volume->private_volume)->ID()));
399 if (!volume || !vnode || !cookie)
400 RETURN(B_BAD_VALUE);
402 Icb *dir = (Icb *)vnode->private_node;
404 if (!dir->IsDirectory()) {
405 TRACE_ERROR(("udf_open_dir: given Icb is not a directory (type: %d)\n",
406 dir->Type()));
407 return B_NOT_A_DIRECTORY;
410 DirectoryIterator *iterator = NULL;
411 status_t status = dir->GetDirectoryIterator(&iterator);
412 if (status != B_OK) {
413 TRACE_ERROR(("udf_open_dir: error getting directory iterator: 0x%"
414 B_PRIx32 ", `%s'\n", status, strerror(status)));
415 return status;
417 *cookie = (void *)iterator;
418 TRACE(("udf_open_dir: *cookie = %p\n", *cookie));
420 return B_OK;
424 static status_t
425 udf_close_dir(fs_volume *_volume, fs_vnode *node, void *_cookie)
427 TRACE(("udf_close_dir: _volume = %p, node = %p\n", _volume, node));
428 return B_OK;
432 static status_t
433 udf_free_dir_cookie(fs_volume *_volume, fs_vnode *node, void *_cookie)
435 TRACE(("udf_free_dir_cookie: _volume = %p, node = %p\n", _volume, node));
436 return B_OK;
440 static status_t
441 udf_read_dir(fs_volume *_volume, fs_vnode *vnode, void *cookie,
442 struct dirent *dirent, size_t bufferSize, uint32 *_num)
444 TRACE(("udf_read_dir: _volume = %p, vnode = %p, bufferSize = %ld\n",
445 _volume, vnode, bufferSize));
447 if (!_volume || !vnode || !cookie || !_num
448 || bufferSize < sizeof(struct dirent)) {
449 return B_BAD_VALUE;
452 Volume *volume = (Volume *)_volume->private_volume;
453 Icb *dir = (Icb *)vnode->private_node;
454 DirectoryIterator *iterator = (DirectoryIterator *)cookie;
456 DEBUG_INIT_ETC("udf_read_dir", ("ID = %ld", volume->ID()));
458 if (dir != iterator->Parent()) {
459 TRACE_ERROR(("udf_read_dir: Icb does not match parent Icb of given "
460 "DirectoryIterator! (iterator->Parent = %p)\n", iterator->Parent()));
461 return B_BAD_VALUE;
464 uint32 nameLength = bufferSize - sizeof(struct dirent) + 1;
465 ino_t id;
466 status_t status = iterator->GetNextEntry(dirent->d_name, &nameLength, &id);
467 if (!status) {
468 TRACE(("udf_read_dir: dirent->d_name = %s, length = %ld\n", dirent->d_name, nameLength));
469 *_num = 1;
470 dirent->d_dev = volume->ID();
471 dirent->d_ino = id;
472 dirent->d_reclen = sizeof(struct dirent) + nameLength - 1;
473 } else {
474 *_num = 0;
475 // Clear the status for end of directory
476 if (status == B_ENTRY_NOT_FOUND)
477 status = B_OK;
480 RETURN(status);
484 status_t
485 udf_rewind_dir(fs_volume *volume, fs_vnode *vnode, void *cookie)
487 TRACE(("udf_rewind_dir: volume = %p, vnode = %p, cookie = %p\n",
488 volume, vnode, cookie));
489 DEBUG_INIT_ETC("udf_rewind_dir", ("ID = %ld",
490 ((Volume *)volume->private_volume)->ID()));
492 if (!volume || !vnode || !cookie)
493 RETURN(B_BAD_VALUE);
495 Icb *dir = (Icb *)vnode->private_node;
496 DirectoryIterator *iterator = (DirectoryIterator *)cookie;
498 if (dir != iterator->Parent()) {
499 PRINT(("udf_rewind_dir: icb does not match parent Icb of given "
500 "DirectoryIterator! (iterator->Parent = %p)\n", iterator->Parent()));
501 return B_BAD_VALUE;
504 iterator->Rewind();
506 return B_OK;
510 // #pragma mark -
513 /*! \brief mount
515 \todo I'm using the B_GET_GEOMETRY ioctl() to find out where the end of the
516 partition is. This won't work for handling multi-session semantics correctly.
517 To support them correctly in R5 I need either:
518 - A way to get the proper info (best)
519 - To ignore trying to find anchor volume descriptor pointers at
520 locations N-256 and N. (acceptable, perhaps, but not really correct)
521 Either way we should address this problem properly for OBOS::R1.
522 \todo Looks like B_GET_GEOMETRY doesn't work on non-device files (i.e.
523 disk images), so I need to use stat or something else for those
524 instances.
526 static status_t
527 udf_mount(fs_volume *_volume, const char *_device, uint32 flags,
528 const char *args, ino_t *_rootVnodeID)
530 TRACE(("udf_mount: device = %s\n", _device));
531 status_t status = B_OK;
532 Volume *volume = NULL;
533 off_t deviceOffset = 0;
534 off_t numBlock = 0;
535 partition_info info;
536 device_geometry geometry;
538 // Here we need to figure out the length of the device, and if we're
539 // attempting to open a multisession volume, we need to figure out the
540 // offset into the raw disk at which the volume begins, then open
541 // the raw volume itself instead of the fake partition device the
542 // kernel gives us, since multisession UDF volumes are allowed to access
543 // the data in their own partition, as well as the data in any partitions
544 // that precede them physically on the disc.
545 int device = open(_device, O_RDONLY);
546 status = device < B_OK ? device : B_OK;
547 if (!status) {
548 // First try to treat the device like a special partition device. If that's
549 // what we have, then we can use the partition_info data to figure out the
550 // name of the raw device (which we'll open instead), the offset into the
551 // raw device at which the volume of interest will begin, and the total
552 // length from the beginning of the raw device that we're allowed to access.
554 // If that fails, then we try to treat the device as an actual raw device,
555 // and see if we can get the device size with B_GET_GEOMETRY syscall, since
556 // stat()ing a raw device appears to not work.
558 // Finally, if that also fails, we're probably stuck with trying to mount
559 // a regular file, so we just stat() it to get the device size.
561 // If that fails, you're just SOL.
563 if (ioctl(device, B_GET_PARTITION_INFO, &info) == 0) {
564 TRACE(("partition_info:\n"));
565 TRACE(("\toffset: %Ld\n", info.offset));
566 TRACE(("\tsize: %Ld\n", info.size));
567 TRACE(("\tlogical_block_size: %ld\n", info.logical_block_size));
568 TRACE(("\tsession: %ld\n", info.session));
569 TRACE(("\tpartition: %ld\n", info.partition));
570 TRACE(("\tdevice: `%s'\n", info.device));
571 _device = info.device;
572 deviceOffset = info.offset / info.logical_block_size;
573 numBlock = deviceOffset + info.size / info.logical_block_size;
574 } else if (ioctl(device, B_GET_GEOMETRY, &geometry) == 0) {
575 TRACE(("geometry_info:\n"));
576 TRACE(("\tsectors_per_track: %ld\n", geometry.sectors_per_track));
577 TRACE(("\tcylinder_count: %ld\n", geometry.cylinder_count));
578 TRACE(("\thead_count: %ld\n", geometry.head_count));
579 deviceOffset = 0;
580 numBlock = (off_t)geometry.sectors_per_track
581 * geometry.cylinder_count * geometry.head_count;
582 } else {
583 struct stat stat;
584 status = fstat(device, &stat) < 0 ? B_ERROR : B_OK;
585 if (!status) {
586 TRACE(("stat_info:\n"));
587 TRACE(("\tst_size: %Ld\n", stat.st_size));
588 deviceOffset = 0;
589 numBlock = stat.st_size / 2048;
592 // Close the device
593 close(device);
596 // Create and mount the volume
597 volume = new(std::nothrow) Volume(_volume);
598 status = volume->Mount(_device, deviceOffset, numBlock, 2048, flags);
599 if (status != B_OK) {
600 delete volume;
601 return status;
604 _volume->private_volume = volume;
605 _volume->ops = &gUDFVolumeOps;
606 *_rootVnodeID = volume->RootIcb()->Id();
608 TRACE(("udf_mount: succefully mounted the partition\n"));
609 return B_OK;
613 // #pragma mark -
616 static status_t
617 udf_std_ops(int32 op, ...)
619 switch (op) {
620 case B_MODULE_INIT:
621 init_entities();
622 return B_OK;
623 case B_MODULE_UNINIT:
624 return B_OK;
625 default:
626 return B_ERROR;
630 fs_volume_ops gUDFVolumeOps = {
631 &udf_unmount,
632 &udf_read_fs_stat,
633 NULL, // write_fs_stat
634 NULL, // sync
635 &udf_get_vnode,
637 /* index directory & index operations */
638 NULL, // open_index_dir
639 NULL, // close_index_dir
640 NULL, // free_index_dir_cookie
641 NULL, // read_index_dir
642 NULL, // rewind_index_dir
643 NULL, // create_index
644 NULL, // remove_index
645 NULL, // read_index_stat
647 /* query operations */
648 NULL, // open_query
649 NULL, // close_query
650 NULL, // free_query_cookie
651 NULL, // read_query
652 NULL, // rewind_query
654 /* support for FS layers */
655 NULL, // create_sub_vnode
656 NULL, // delete_sub_vnode
659 fs_vnode_ops gUDFVnodeOps = {
660 /* vnode operatoins */
661 &udf_lookup,
662 NULL, // get_vnode_name
663 &udf_put_vnode,
664 &udf_remove_vnode,
666 /* VM file access */
667 NULL, // can_page
668 NULL, // read_pages
669 NULL, // write_pages
671 /* asynchronous I/O */
672 &udf_io,
673 NULL, // cancel_io()
675 /* cache file access */
676 &udf_get_file_map,
678 /* common operations */
679 NULL, // ioctl
680 NULL, // set_flags
681 NULL, // select
682 NULL, // deselect
683 NULL, // fsync
684 NULL, // read_symlink
685 NULL, // create_symlnk
686 NULL, // link
687 NULL, // unlink
688 NULL, // rename
689 &udf_access,
690 &udf_read_stat,
691 NULL, // write_stat
692 NULL, // preallocate
694 /* file operations */
695 NULL, // create
696 &udf_open,
697 &udf_close,
698 &udf_free_cookie,
699 &udf_read,
700 NULL, // write
702 /* directory operations */
703 NULL, // create_dir
704 NULL, // remove_dir
705 &udf_open_dir,
706 &udf_close_dir,
707 &udf_free_dir_cookie,
708 &udf_read_dir,
709 &udf_rewind_dir,
711 /* attribue directory operations */
712 NULL, // open_attr_dir
713 NULL, // close_attr_dir
714 NULL, // free_attr_dir_cookie
715 NULL, // read_attr_dir
716 NULL, // rewind_attr_dir
718 /* attribute operations */
719 NULL, // create_attr
720 NULL, // open_attr
721 NULL, // close_attr
722 NULL, // free_attr_cookie
723 NULL, // read_attr
724 NULL, // write_attr
725 NULL, // read_attr_stat
726 NULL, // write_attr_stat
727 NULL, // rename_attr
728 NULL, // remove_attr
730 /* support for node and FS layers */
731 NULL, // create_special_node
732 NULL // get_super_vnode
736 static file_system_module_info sUDFFileSystem = {
738 "file_systems/udf" B_CURRENT_FS_API_VERSION,
740 udf_std_ops,
743 "udf", // short_name
744 "UDF File System", // pretty_name
745 0, // DDM flags
747 &udf_identify_partition,
748 &udf_scan_partition,
749 &udf_free_identify_partition_cookie,
750 NULL, // free_partition_content_cookie()
752 &udf_mount,
754 NULL,
757 module_info *modules[] = {
758 (module_info *)&sUDFFileSystem,
759 NULL,