* Renamed udf.cpp to kernel_interface.cpp and ported it to the new FS API.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26994 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Salvatore Benedetto 2008-08-16 20:27:16 +00:00
parent 6ae7f6879f
commit 51933b5533
2 changed files with 585 additions and 1034 deletions

View File

@ -0,0 +1,585 @@
/*
* Copyright 2008, Salvatore Benedetto, salvatore.benedetto@gmail.com.
* Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
* Distributed under the terms of the MIT License.
*/
/*! \file kernel_interface.cpp */
#include <Drivers.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <KernelExport.h>
#include <util/kernel_cpp.h>
#include "DirectoryIterator.h"
#include "Icb.h"
#include "Recognition.h"
#include "Utils.h"
#include "Volume.h"
#undef TRACE
#undef TRACE_ERROR
#define UDF_KERNEL_INTERFACE_DEBUG
#ifdef UDF_KERNEL_INTERFACE_DEBUG
# define TRACE(x) dprintf x
# define TRACE_ERROR(x) dprintf x
#else
# define TRACE(x) /* nothing */
# define TRACE_ERROR(x) dprintf x
#endif
extern fs_volume_ops gUDFVolumeOps;
extern fs_vnode_ops gUDFVnodeOps;
// #pragma mark - fs_volume_ops fuctions
static float
udf_identify_partition(int fd, partition_data *partition, void **_cookie)
{
TRACE(("udf_identify_partition: fd = %d\n", fd));
logical_volume_descriptor logicalVolumeDescriptor;
partition_descriptor partitionDescriptors[kMaxPartitionDescriptors];
uint8 partitionDescriptorCount;
uint32 blockShift;
status_t error = udf_recognize(fd, partition->offset, partition->size, 2048, blockShift,
logicalVolumeDescriptor, partitionDescriptors, partitionDescriptorCount);
if (!error) {
UdfString name(logicalVolumeDescriptor.logical_volume_identifier());
strcpy(partition->name, name.Utf8());
}
return 0.8f;
}
static status_t
udf_scan_partition(int fd, partition_data *partition, void *_cookie)
{
TRACE(("udf_scan_partition: fd = %d\n", fd));
return B_ERROR;
}
static status_t
udf_unmount(fs_volume *_volume)
{
TRACE(("udb_unmount: _volume = %p\n", _volume));
Volume *volume = (Volume *)_volume->private_volume;
delete volume;
return B_OK;
}
static status_t
udf_read_fs_stat(fs_volume *_volume, struct fs_info *info)
{
TRACE(("udf_read_fs_stat: _volume = %p, info = %p\n", _volume, info));
Volume *volume = (Volume *)_volume->private_volume;
// File system flags.
info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
info->io_size = 65536;
// whatever is appropriate here? Just use the same value as BFS (and iso9660) for now
info->block_size = volume->BlockSize();
info->total_blocks = volume->Length();
info->free_blocks = 0;
// Volume name
sprintf(info->volume_name, "%s", volume->Name());
// File system name
strcpy(info->fsh_name, "udf");
return B_OK;
}
static status_t
udf_get_vnode(fs_volume *_volume, ino_t id, fs_vnode *_node, int *_type,
uint32 *_flags, bool reenter)
{
TRACE(("udf_get_vnode: id = %Ld, reenter = %s\n",
id, (reenter ? "true" : "false")));
Volume *volume = (Volume *)_volume->private_volume;
// Convert the given vnode id to an address, and create
// and return a corresponding Icb object for it.
Icb *icb = new(std::nothrow) Icb(volume,
to_long_address(id, volume->BlockSize()));
if (icb) {
if(icb->InitCheck() == B_OK) {
if (_node)
_node->private_node = icb;
_node->ops = &gUDFVnodeOps;
_flags = 0;
} else {
TRACE_ERROR(("udf_get_vnode: InitCheck failed\n"));
delete icb;
return B_ERROR;
}
} else
return B_NO_MEMORY;
return B_OK;
}
// #pragma mark - fs_vnode_ops functions
static status_t
udf_lookup(fs_volume *_volume, fs_vnode *_directory, const char *file,
ino_t *vnodeID)
{
TRACE(("udf_lookup: _directory = %p, filename = %s\n", _directory, file));
Volume *volume = (Volume *)_volume->private_volume;
Icb *dir = (Icb *)_directory->private_node;
Icb *node = NULL;
status_t status = B_OK;
if (strcmp(file, ".") == 0) {
TRACE(("udf_lookup: file = ./\n"));
*vnodeID = dir->Id();
status = get_vnode(volume->FSVolume(), *vnodeID, (void **)&node);
if (status != B_OK)
return B_ENTRY_NOT_FOUND;
} else {
status = dir->Find(file, vnodeID);
if (status == B_OK) {
Icb *icb;
status = get_vnode(volume->FSVolume(), *vnodeID,
(void **)&icb);
if (status != B_OK)
return B_ENTRY_NOT_FOUND;
}
}
TRACE(("udf_lookup: vnodeId: %Ld\n", *vnodeID));
return status;
}
static status_t
udf_put_vnode(fs_volume *volume, fs_vnode *node, bool reenter)
{
// No debug-to-file in release_vnode; can cause a deadlock in
// rare circumstances.
#if !DEBUG_TO_FILE
DEBUG_INIT_ETC(NULL, ("node: %p", node));
#endif
Icb *icb = reinterpret_cast<Icb*>(node);
delete icb;
#if !DEBUG_TO_FILE
RETURN(B_OK);
#else
return B_OK;
#endif
}
static status_t
udf_read_stat(fs_volume *_volume, fs_vnode *node, struct stat *stat)
{
DEBUG_INIT(NULL);
if (!_volume || !node || !stat)
RETURN(B_BAD_VALUE);
Volume *volume = (Volume *)_volume->private_volume;
Icb *icb = reinterpret_cast<Icb*>(node);
//stat->st_dev = volume->Id();
stat->st_ino = icb->Id();
stat->st_nlink = icb->FileLinkCount();
stat->st_blksize = volume->BlockSize();
stat->st_uid = icb->Uid();
stat->st_gid = icb->Gid();
stat->st_mode = icb->Mode();
PRINT(("mode = 0x%lx\n", uint32(icb->Mode())));
stat->st_size = icb->Length();
// File times. For now, treat the modification time as creation
// time as well, since true creation time is an optional extended
// attribute, and supporting EAs is going to be a PITA. ;-)
stat->st_atime = icb->AccessTime();
stat->st_mtime = stat->st_ctime = stat->st_crtime = icb->ModificationTime();
PRINT(("stat->st_ino: %Ld\n", stat->st_ino));
RETURN(B_OK);
}
static status_t
udf_read(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos,
void *buffer, size_t *length)
{
TRACE(("udf_read: ID = %d, pos = %d, length = %d\n",
((Volume *)volume->private_volume)->ID(), pos, length));
Icb *icb = (Icb *)vnode->private_node;
// if (!inode->HasUserAccessableStream()) {
// *_length = 0;
// RETURN_ERROR(B_BAD_VALUE);
// }
RETURN(icb->Read(pos, buffer, length));
}
static status_t
udf_open_dir(fs_volume *volume, fs_vnode *vnode, void **cookie)
{
DEBUG_INIT_ETC(NULL, ("node: %p, cookie: %p", node, cookie));
if (!volume || !vnode || !cookie)
RETURN(B_BAD_VALUE);
Icb *dir = (Icb *)vnode->private_node;
status_t status = B_OK;
if (dir->IsDirectory()) {
DirectoryIterator *iterator = NULL;
status = dir->GetDirectoryIterator(&iterator);
if (!status) {
*cookie = reinterpret_cast<void*>(iterator);
} else {
PRINT(("Error getting directory iterator: 0x%lx, `%s'\n", status, strerror(error)));
}
} else {
PRINT(("Given icb is not a directory (type: %d)\n", dir->Type()));
status = B_BAD_VALUE;
}
RETURN(status);
}
static status_t
udf_read_dir(fs_volume *_volume, fs_vnode *vnode, void *cookie,
struct dirent *dirent, size_t bufferSize, uint32 *_num)
{
DEBUG_INIT_ETC(NULL,
("dir: %p, iterator: %p, bufferSize: %ld", node, cookie, bufferSize));
if (!_volume || !vnode || !cookie || !_num || bufferSize < sizeof(dirent))
RETURN(B_BAD_VALUE);
Volume *volume = (Volume *)_volume->private_volume;
Icb *dir = (Icb *)vnode->private_node;
DirectoryIterator *iterator = reinterpret_cast<DirectoryIterator*>(cookie);
if (dir != iterator->Parent()) {
PRINT(("Icb does not match parent Icb of given DirectoryIterator! (iterator->Parent = %p)\n",
iterator->Parent()));
return B_BAD_VALUE;
}
uint32 nameLength = bufferSize - sizeof(dirent) + 1;
ino_t id;
status_t status = iterator->GetNextEntry(dirent->d_name, &nameLength, &id);
if (!status) {
*_num = 1;
dirent->d_dev = volume->ID();
dirent->d_ino = id;
dirent->d_reclen = sizeof(dirent) + nameLength - 1;
} else {
*_num = 0;
// Clear the status for end of directory
if (status == B_ENTRY_NOT_FOUND)
status = B_OK;
}
RETURN(status);
}
status_t
udf_rewind_dir(fs_volume *volume, fs_vnode *vnode, void *cookie)
{
DEBUG_INIT_ETC(NULL,
("dir: %p, iterator: %p", node, cookie));
if (!volume || !vnode || !cookie)
RETURN(B_BAD_VALUE);
Icb *dir = (Icb *)vnode->private_node;
DirectoryIterator *iterator = reinterpret_cast<DirectoryIterator*>(cookie);
if (dir != iterator->Parent()) {
PRINT(("Icb does not match parent Icb of given DirectoryIterator! (iterator->Parent = %p)\n",
iterator->Parent()));
return B_BAD_VALUE;
}
iterator->Rewind();
RETURN(B_OK);
}
// #pragma mark -
/*! \brief mount
\todo I'm using the B_GET_GEOMETRY ioctl() to find out where the end of the
partition is. This won't work for handling multi-session semantics correctly.
To support them correctly in R5 I need either:
- A way to get the proper info (best)
- To ignore trying to find anchor volume descriptor pointers at
locations N-256 and N. (acceptable, perhaps, but not really correct)
Either way we should address this problem properly for OBOS::R1.
\todo Looks like B_GET_GEOMETRY doesn't work on non-device files (i.e.
disk images), so I need to use stat or something else for those
instances.
*/
static status_t
udf_mount(fs_volume *_volume, const char *_device, uint32 flags,
const char *args, ino_t *_rootVnodeID)
{
TRACE(("udf_mount: device = %s\n", _device));
status_t status = B_OK;
Volume *volume = NULL;
off_t deviceOffset = 0;
off_t numBlock = 0;
partition_info info;
device_geometry geometry;
// Here we need to figure out the length of the device, and if we're
// attempting to open a multisession volume, we need to figure out the
// offset into the raw disk at which the volume begins, then open
// the raw volume itself instead of the fake partition device the
// kernel gives us, since multisession UDF volumes are allowed to access
// the data in their own partition, as well as the data in any partitions
// that precede them physically on the disc.
int device = open(_device, O_RDONLY);
status = device < B_OK ? device : B_OK;
if (!status) {
// First try to treat the device like a special partition device. If that's
// what we have, then we can use the partition_info data to figure out the
// name of the raw device (which we'll open instead), the offset into the
// raw device at which the volume of interest will begin, and the total
// length from the beginning of the raw device that we're allowed to access.
//
// If that fails, then we try to treat the device as an actual raw device,
// and see if we can get the device size with B_GET_GEOMETRY syscall, since
// stat()ing a raw device appears to not work.
//
// Finally, if that also fails, we're probably stuck with trying to mount
// a regular file, so we just stat() it to get the device size.
//
// If that fails, you're just SOL.
if (ioctl(device, B_GET_PARTITION_INFO, &info) == 0) {
PRINT(("partition_info:\n"));
PRINT((" offset: %Ld\n", info.offset));
PRINT((" size: %Ld\n", info.size));
PRINT((" logical_block_size: %ld\n", info.logical_block_size));
PRINT((" session: %ld\n", info.session));
PRINT((" partition: %ld\n", info.partition));
PRINT((" device: `%s'\n", info.device));
_device = info.device;
deviceOffset = info.offset / info.logical_block_size;
numBlock = deviceOffset + info.size / info.logical_block_size;
} else if (ioctl(device, B_GET_GEOMETRY, &geometry) == 0) {
PRINT(("geometry_info:\n"));
PRINT((" sectors_per_track: %ld\n", geometry.sectors_per_track));
PRINT((" cylinder_count: %ld\n", geometry.cylinder_count));
PRINT((" head_count: %ld\n", geometry.head_count));
deviceOffset = 0;
numBlock = (off_t)geometry.sectors_per_track
* geometry.cylinder_count * geometry.head_count;
} else {
struct stat stat;
status = fstat(device, &stat) < 0 ? B_ERROR : B_OK;
if (!status) {
PRINT(("stat_info:\n"));
PRINT((" st_size: %Ld\n", stat.st_size));
deviceOffset = 0;
numBlock = stat.st_size / 2048;
}
}
// Close the device
close(device);
}
// Create and mount the volume
volume = new(std::nothrow) Volume(_volume);
status = volume->Mount(_device, deviceOffset, numBlock, 2048, flags);
if (status != B_OK) {
delete volume;
return status;
}
_volume->private_volume = volume;
_volume->ops = &gUDFVolumeOps;
*_rootVnodeID = *(ino_t *)volume->RootIcb();
return B_OK;
}
// #pragma mark -
static status_t
udf_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
case B_MODULE_UNINIT:
return B_OK;
default:
return B_ERROR;
}
}
fs_volume_ops gUDFVolumeOps = {
&udf_unmount,
&udf_read_fs_stat,
NULL, // write_fs_stat
NULL, // sync
&udf_get_vnode,
/* index directory & index operations */
NULL, // open_index_dir
NULL, // close_index_dir
NULL, // free_index_dir_cookie
NULL, // read_index_dir
NULL, // rewind_index_dir
NULL, // create_index
NULL, // remove_index
NULL, // read_index_stat
/* query operations */
NULL, // open_query
NULL, // close_query
NULL, // free_query_cookie
NULL, // read_query
NULL, // rewind_query
/* support for FS layers */
NULL, // create_sub_vnode
NULL, // delete_sub_vnode
};
fs_vnode_ops gUDFVnodeOps = {
/* vnode operatoins */
&udf_lookup,
NULL, // get_vnode_name
&udf_put_vnode,
NULL, // remove_vnode
/* VM file access */
NULL, // can_page
NULL, // read_pages
NULL, // write_pages
/* asynchronous I/O */
NULL, // io()
NULL, // cancel_io()
/* cache file access */
NULL, // &udf_get_file_map,
/* common operations */
NULL, // ioctl
NULL, // set_flags
NULL, // select
NULL, // deselect
NULL, // fsync
NULL, // read_symlink
NULL, // create_symlnk
NULL, // link
NULL, // unlink
NULL, // rename
NULL, // access
&udf_read_stat,
NULL, // write_stat
/* file operations */
NULL, // create
NULL, // open
NULL, // close
NULL, // free_cockie
&udf_read,
NULL, // write
/* directory operations */
NULL, // create_dir
NULL, // remove_dir
&udf_open_dir,
NULL, // close_dir
NULL, // free_dir_cookie
&udf_read_dir,
&udf_rewind_dir,
/* attribue directory operations */
NULL, // open_attr_dir
NULL, // close_attr_dir
NULL, // free_attr_dir_cookie
NULL, // read_attr_dir
NULL, // rewind_attr_dir
/* attribute operations */
NULL, // create_attr
NULL, // open_attr
NULL, // close_attr
NULL, // free_attr_cookie
NULL, // read_attr
NULL, // write_attr
NULL, // read_attr_stat
NULL, // write_attr_stat
NULL, // rename_attr
NULL, // remove_attr
/* support for node and FS layers */
NULL, // create_special_node
NULL // get_super_vnode
};
static file_system_module_info sUDFFileSystem = {
{
"file_systems/udf" B_CURRENT_FS_API_VERSION,
0,
udf_std_ops,
},
"udf", // short_name
"UDF File System", // pretty_name
0, // DDM flags
&udf_identify_partition,
&udf_scan_partition,
NULL, // &udf_free_identify_patition_cookie,
NULL, // free_partition_content_cookie()
&udf_mount,
NULL,
};
module_info *module[] = {
(module_info *)&sUDFFileSystem,
NULL,
};

File diff suppressed because it is too large Load Diff