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.
10 /*! \file kernel_interface.cpp */
20 #include <KernelExport.h>
21 #include <util/kernel_cpp.h>
23 #include <io_requests.h>
26 #include "Recognition.h"
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
38 # define TRACE(x) /* nothing */
39 # define TRACE_ERROR(x) dprintf x
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
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());
65 iterative_io_finished_hook(void *cookie
, io_request
*request
, status_t status
,
66 bool partialTransfer
, size_t bytesTransferred
)
73 // #pragma mark - fs_volume_ops fuctions
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
;
89 status_t error
= udf_recognize(fd
, partition
->offset
, partition
->size
,
90 partition
->block_size
, blockShift
, primaryVolumeDescriptor
,
91 logicalVolumeDescriptor
, partitionDescriptors
, descriptorCount
);
95 identify_cookie
*cookie
= new(std::nothrow
) identify_cookie
;
99 cookie
->logical_volume_descriptor
= logicalVolumeDescriptor
;
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
)
129 udf_free_identify_partition_cookie(partition_data
*partition
, void *cookie
)
131 delete (identify_cookie
*)cookie
;
136 udf_unmount(fs_volume
*_volume
)
138 TRACE(("udb_unmount: _volume = %p\n", _volume
));
139 Volume
*volume
= (Volume
*)_volume
->private_volume
;
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;
163 sprintf(info
->volume_name
, "%s", volume
->Name());
166 strcpy(info
->fsh_name
, "udf");
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()));
191 if (icb
->InitCheck() == B_OK
) {
192 node
->private_node
= icb
;
193 node
->ops
= &gUDFVnodeOps
;
194 *_type
= icb
->Mode();
199 TRACE_ERROR(("udf_get_vnode: InitCheck failed\n"));
205 // #pragma mark - fs_vnode_ops functions
209 udf_lookup(fs_volume
*_volume
, fs_vnode
*_directory
, const char *file
,
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
;
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
);
225 return B_ENTRY_NOT_FOUND
;
227 status
= dir
->Find(file
, vnodeID
);
232 status
= get_vnode(volume
->FSVolume(), *vnodeID
, (void **)&icb
);
234 return B_ENTRY_NOT_FOUND
;
236 TRACE(("udf_lookup: vnodeId = %Ld found!\n", *vnodeID
));
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.
249 DEBUG_INIT_ETC(NULL
, ("node: %p", node
));
251 Icb
*icb
= (Icb
*)node
->private_node
;
262 udf_remove_vnode(fs_volume
* _volume
, fs_vnode
* _node
, bool reenter
)
264 TRACE(("udf_remove_vnode: _volume = %p, _node = %p\n", _volume
, _node
));
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
)
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
,
313 udf_open(fs_volume
* _volume
, fs_vnode
* _node
, int openMode
, void** _cookie
)
315 TRACE(("udf_open: _volume = %p, _node = %p\n", _volume
, _node
));
321 udf_close(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
)
323 TRACE(("udf_close: _volume = %p, _node = %p\n", _volume
, _node
));
329 udf_free_cookie(fs_volume
* _volume
, fs_vnode
* _node
, void* _cookie
)
331 TRACE(("udf_free_cookie: _volume = %p, _node = %p\n", _volume
, _node
));
337 udf_access(fs_volume
* _volume
, fs_vnode
* _node
, int accessMode
)
339 TRACE(("udf_access: _volume = %p, _node = %p\n", _volume
, _node
));
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()) {
357 // RETURN_ERROR(B_BAD_VALUE);
360 RETURN(icb
->Read(pos
, buffer
, length
));
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
);
378 return do_iterative_fd_io(((Volume
*)volume
->private_volume
)->Device(),
379 request
, iterative_io_get_vecs_hook
, iterative_io_finished_hook
, icb
);
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
);
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
)
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",
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
)));
417 *cookie
= (void *)iterator
;
418 TRACE(("udf_open_dir: *cookie = %p\n", *cookie
));
425 udf_close_dir(fs_volume
*_volume
, fs_vnode
*node
, void *_cookie
)
427 TRACE(("udf_close_dir: _volume = %p, node = %p\n", _volume
, node
));
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
));
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
)) {
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()));
464 uint32 nameLength
= bufferSize
- sizeof(struct dirent
) + 1;
466 status_t status
= iterator
->GetNextEntry(dirent
->d_name
, &nameLength
, &id
);
468 TRACE(("udf_read_dir: dirent->d_name = %s, length = %ld\n", dirent
->d_name
, nameLength
));
470 dirent
->d_dev
= volume
->ID();
472 dirent
->d_reclen
= sizeof(struct dirent
) + nameLength
- 1;
475 // Clear the status for end of directory
476 if (status
== B_ENTRY_NOT_FOUND
)
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
)
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()));
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
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;
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
;
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
));
580 numBlock
= (off_t
)geometry
.sectors_per_track
581 * geometry
.cylinder_count
* geometry
.head_count
;
584 status
= fstat(device
, &stat
) < 0 ? B_ERROR
: B_OK
;
586 TRACE(("stat_info:\n"));
587 TRACE(("\tst_size: %Ld\n", stat
.st_size
));
589 numBlock
= stat
.st_size
/ 2048;
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
) {
604 _volume
->private_volume
= volume
;
605 _volume
->ops
= &gUDFVolumeOps
;
606 *_rootVnodeID
= volume
->RootIcb()->Id();
608 TRACE(("udf_mount: succefully mounted the partition\n"));
617 udf_std_ops(int32 op
, ...)
623 case B_MODULE_UNINIT
:
630 fs_volume_ops gUDFVolumeOps
= {
633 NULL
, // write_fs_stat
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 */
650 NULL
, // free_query_cookie
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 */
662 NULL
, // get_vnode_name
671 /* asynchronous I/O */
675 /* cache file access */
678 /* common operations */
684 NULL
, // read_symlink
685 NULL
, // create_symlnk
694 /* file operations */
702 /* directory operations */
707 &udf_free_dir_cookie
,
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 */
722 NULL
, // free_attr_cookie
725 NULL
, // read_attr_stat
726 NULL
, // write_attr_stat
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
,
744 "UDF File System", // pretty_name
747 &udf_identify_partition
,
749 &udf_free_identify_partition_cookie
,
750 NULL
, // free_partition_content_cookie()
757 module_info
*modules
[] = {
758 (module_info
*)&sUDFFileSystem
,