From 00405f2286fd370a0bd264d0bbac0cfa120e7646 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Mon, 9 Feb 2009 23:06:31 +0000 Subject: [PATCH] * Add an additional argument to get_vnode() that gets the fs_vnode_ops of the node. That is needed for a layered filesystem to be able to construct a full fs_vnode out of a volume/inode pair. * Adapt places where get_vnode is used. Sadly this is a C API and we can't just use a default NULL for that argument. * Introduce a flag B_VNODE_WANTS_OVERLAY_SUB_NODE that can be returned in the flags field of a fs get_vnode call. A filesystem can use this flag to indicate that it doesn't support the full set of fs features (attributes, write support) and it'd like to have unsupported calls emulated by an overlay sub node. * Add a perliminary overlay filesystem that emulates file attributes using files on a filesystem where attributes aren't supported. It does currently only support reading attributes/attribute directories though. All other calls are just passed through to the super filesystem. * Adjust places where a HAS_FS_CALL() is taken as a guarantee that the operation is supported. For the overlay filesystem we may later return a B_UNSUPPORTED, so make sure that in that case proper fallback options are taken. * Make the iso9660 filesystem request overlay sub nodes. This can be fine tuned later to only trigger where there are features on a CD that need emulation at all. If you happened to know the attribute file format and location you could build an iso with read-only attribute support now. Note that this won't be enough to get a bootable iso-only image as the query and index support is yet missing. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29177 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/drivers/fs_interface.h | 3 +- headers/private/fs_shell/fssh_fs_interface.h | 3 +- src/add-ons/kernel/file_systems/bfs/Inode.cpp | 2 +- src/add-ons/kernel/file_systems/bfs/Inode.h | 3 +- .../file_systems/bfs/kernel_interface.cpp | 2 +- .../file_systems/cdda/kernel_interface.cpp | 2 +- .../kernel/file_systems/ext2/Volume.cpp | 2 +- .../file_systems/ext2/kernel_interface.cpp | 2 +- src/add-ons/kernel/file_systems/fat/dir.c | 2 +- src/add-ons/kernel/file_systems/fat/file.c | 4 +- .../file_systems/iso9660/kernel_interface.cpp | 8 +- .../kernel/file_systems/nfs/nfs_add_on.c | 10 +- .../kernel/file_systems/reiserfs/Volume.cpp | 2 +- src/system/kernel/device_manager/devfs.cpp | 11 +- src/system/kernel/fs/Jamfile | 1 + src/system/kernel/fs/overlay.cpp | 1097 +++++++++++++++++ src/system/kernel/fs/overlay.h | 16 + src/system/kernel/fs/rootfs.cpp | 7 +- src/system/kernel/fs/vfs.cpp | 31 +- src/system/kernel/fs/vfs_request_io.cpp | 6 +- src/tools/fs_shell/vfs.cpp | 18 +- 21 files changed, 1186 insertions(+), 46 deletions(-) create mode 100644 src/system/kernel/fs/overlay.cpp create mode 100644 src/system/kernel/fs/overlay.h diff --git a/headers/os/drivers/fs_interface.h b/headers/os/drivers/fs_interface.h index a758c79b48..6b3ec1c936 100644 --- a/headers/os/drivers/fs_interface.h +++ b/headers/os/drivers/fs_interface.h @@ -43,6 +43,7 @@ struct file_io_vec { // flags for publish_vnode() and fs_volume_ops::get_vnode() #define B_VNODE_PUBLISH_REMOVED 0x01 #define B_VNODE_DONT_CREATE_SPECIAL_SUB_NODE 0x02 +#define B_VNODE_WANTS_OVERLAY_SUB_NODE 0x04 #ifdef __cplusplus @@ -307,7 +308,7 @@ extern status_t publish_vnode(fs_volume *volume, ino_t vnodeID, void *privateNode, fs_vnode_ops *ops, int type, uint32 flags); extern status_t get_vnode(fs_volume *volume, ino_t vnodeID, - void **_privateNode); + void **_privateNode, fs_vnode_ops **_vnodeOps); extern status_t put_vnode(fs_volume *volume, ino_t vnodeID); extern status_t acquire_vnode(fs_volume *volume, ino_t vnodeID); extern status_t remove_vnode(fs_volume *volume, ino_t vnodeID); diff --git a/headers/private/fs_shell/fssh_fs_interface.h b/headers/private/fs_shell/fssh_fs_interface.h index d4d406c9c8..23f6f521b1 100644 --- a/headers/private/fs_shell/fssh_fs_interface.h +++ b/headers/private/fs_shell/fssh_fs_interface.h @@ -344,7 +344,8 @@ extern fssh_status_t fssh_publish_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID, void *privateNode, fssh_fs_vnode_ops *ops, int type, uint32_t flags); extern fssh_status_t fssh_get_vnode(fssh_fs_volume *volume, - fssh_vnode_id vnodeID, void **_privateNode); + fssh_vnode_id vnodeID, void **_privateNode, + fssh_fs_vnode_ops **_vnodeOps); extern fssh_status_t fssh_put_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID); extern fssh_status_t fssh_acquire_vnode(fssh_fs_volume *volume, diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.cpp b/src/add-ons/kernel/file_systems/bfs/Inode.cpp index 1e0b25d8a8..7733d72c25 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Inode.cpp @@ -2701,7 +2701,7 @@ AttributeIterator::GetNext(char* name, size_t* _length, uint32* _type, // if you haven't yet access to the attributes directory, get it if (fAttributes == NULL) { if (get_vnode(volume->FSVolume(), volume->ToVnode(fInode->Attributes()), - (void**)&fAttributes) != B_OK) { + (void**)&fAttributes, NULL) != B_OK) { FATAL(("get_vnode() failed in AttributeIterator::GetNext(ino_t" " = %Ld,name = \"%s\")\n", fInode->ID(), name)); return B_ENTRY_NOT_FOUND; diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.h b/src/add-ons/kernel/file_systems/bfs/Inode.h index 3547011ba2..e06efc17a8 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.h +++ b/src/add-ons/kernel/file_systems/bfs/Inode.h @@ -354,7 +354,8 @@ public: { Unset(); - return fStatus = get_vnode(volume->FSVolume(), id, (void**)&fInode); + return fStatus = get_vnode(volume->FSVolume(), id, (void**)&fInode, + NULL); } status_t SetTo(Volume* volume, block_run run) diff --git a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp index 81682ee15b..49f693c541 100644 --- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp @@ -556,7 +556,7 @@ bfs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* file, locker.Unlock(); Inode* inode; - status = get_vnode(volume->FSVolume(), *_vnodeID, (void**)&inode); + status = get_vnode(volume->FSVolume(), *_vnodeID, (void**)&inode, NULL); if (status != B_OK) { REPORT_ERROR(status); return B_ENTRY_NOT_FOUND; diff --git a/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp b/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp index 91e2da77f6..e30a6f3172 100644 --- a/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp @@ -1493,7 +1493,7 @@ cdda_lookup(fs_volume* _volume, fs_vnode* _dir, const char* name, ino_t* _id) if (inode == NULL) return B_ENTRY_NOT_FOUND; - status = get_vnode(volume->FSVolume(), inode->ID(), NULL); + status = get_vnode(volume->FSVolume(), inode->ID(), NULL, NULL); if (status < B_OK) return status; diff --git a/src/add-ons/kernel/file_systems/ext2/Volume.cpp b/src/add-ons/kernel/file_systems/ext2/Volume.cpp index 0a30ab77dc..480e67d689 100644 --- a/src/add-ons/kernel/file_systems/ext2/Volume.cpp +++ b/src/add-ons/kernel/file_systems/ext2/Volume.cpp @@ -310,7 +310,7 @@ Volume::Mount(const char* deviceName, uint32 flags) if ((fBlockCache = opener.InitCache(NumBlocks(), fBlockSize)) == NULL) return B_ERROR; - status = get_vnode(fFSVolume, EXT2_ROOT_NODE, (void**)&fRootNode); + status = get_vnode(fFSVolume, EXT2_ROOT_NODE, (void**)&fRootNode, NULL); if (status != B_OK) { TRACE("could not create root node: get_vnode() failed!\n"); return status; diff --git a/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp b/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp index 5804d1f9ab..4268a80fdb 100644 --- a/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp @@ -319,7 +319,7 @@ ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, break; } - return get_vnode(volume->FSVolume(), *_vnodeID, NULL); + return get_vnode(volume->FSVolume(), *_vnodeID, NULL, NULL); } diff --git a/src/add-ons/kernel/file_systems/fat/dir.c b/src/add-ons/kernel/file_systems/fat/dir.c index e0ce4d2c40..4fba295480 100644 --- a/src/add-ons/kernel/file_systems/fat/dir.c +++ b/src/add-ons/kernel/file_systems/fat/dir.c @@ -433,7 +433,7 @@ findfile(nspace *vol, vnode *dir, const char *file, ino_t *vnid, if (vnid) *vnid = found_vnid; if (node) - result = get_vnode(vol->volume, found_vnid, (void **)node); + result = get_vnode(vol->volume, found_vnid, (void **)node, NULL); result = B_OK; } else { result = ENOENT; diff --git a/src/add-ons/kernel/file_systems/fat/file.c b/src/add-ons/kernel/file_systems/fat/file.c index 8f14df5f48..88e6f8799e 100644 --- a/src/add-ons/kernel/file_systems/fat/file.c +++ b/src/add-ons/kernel/file_systems/fat/file.c @@ -890,7 +890,7 @@ dosfs_create(fs_volume *_vol, fs_vnode *_dir, const char *name, int omode, *vnid = dummy.vnid; dummy.magic = ~VNODE_MAGIC; - result = get_vnode(_vol, *vnid, (void **)&file); + result = get_vnode(_vol, *vnid, (void **)&file, NULL); if (result < B_OK) { if (vol->fs_flags & FS_FLAGS_OP_SYNC) _dosfs_sync(vol); @@ -1155,7 +1155,7 @@ dosfs_rename(fs_volume *_vol, fs_vnode *_odir, const char *oldname, if (vnid == vol->root_vnode.vnid) break; - result = get_vnode(_vol, vnid, (void **)&dir); + result = get_vnode(_vol, vnid, (void **)&dir, NULL); if (result < B_OK) goto bi1; parent = dir->dir_vnid; diff --git a/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp b/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp index 9d1082a942..32e43c0c3d 100644 --- a/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp @@ -224,12 +224,12 @@ fs_walk(fs_volume *_vol, fs_vnode *_base, const char *file, ino_t *_vnodeID) // base directory TRACE(("fs_walk - found \".\" file.\n")); *_vnodeID = baseNode->id; - return get_vnode(_vol, *_vnodeID, (void **)&newNode); + return get_vnode(_vol, *_vnodeID, NULL, NULL); } else if (strcmp(file, "..") == 0) { // parent directory TRACE(("fs_walk - found \"..\" file.\n")); *_vnodeID = baseNode->parID; - return get_vnode(_vol, *_vnodeID, (void **)&newNode); + return get_vnode(_vol, *_vnodeID, NULL, NULL); } // look up file in the directory @@ -272,7 +272,7 @@ fs_walk(fs_volume *_vol, fs_vnode *_base, const char *file, ino_t *_vnodeID) TRACE(("fs_walk - New vnode id is %Ld\n", *_vnodeID)); result = get_vnode(_vol, *_vnodeID, - (void **)&newNode); + (void **)&newNode, NULL); if (result == B_OK) { newNode->parID = baseNode->id; done = true; @@ -349,7 +349,7 @@ fs_read_vnode(fs_volume *_vol, ino_t vnodeID, fs_vnode *_node, _node->private_node = newNode; _node->ops = &gISO9660VnodeOps; *_type = newNode->attr.stat[FS_DATA_FORMAT].st_mode & ~(S_IWUSR | S_IWGRP | S_IWOTH); - *_flags = 0; + *_flags = B_VNODE_WANTS_OVERLAY_SUB_NODE; if ((newNode->flags & ISO_ISDIR) == 0) { newNode->cache = file_cache_create(ns->id, vnodeID, diff --git a/src/add-ons/kernel/file_systems/nfs/nfs_add_on.c b/src/add-ons/kernel/file_systems/nfs/nfs_add_on.c index a327c8de39..31d3ad1017 100644 --- a/src/add-ons/kernel/file_systems/nfs/nfs_add_on.c +++ b/src/add-ons/kernel/file_systems/nfs/nfs_add_on.c @@ -898,7 +898,7 @@ fs_walk(fs_volume *_volume, fs_vnode *_base, const char *file, ino_t *vnid) isLink=S_ISLNK(st.st_mode); } - if ((result=get_vnode (_volume,*vnid,(void **)&dummy))volume, device->id, (void**)&partition->raw_device); + status = get_vnode(fs->volume, device->id, (void**)&partition->raw_device, + NULL); if (status < B_OK) goto err1; @@ -937,7 +938,7 @@ devfs_lookup(fs_volume *_volume, fs_vnode *_dir, const char *name, ino_t *_id) { struct devfs *fs = (struct devfs *)_volume->private_volume; struct devfs_vnode *dir = (struct devfs_vnode *)_dir->private_node; - struct devfs_vnode *vnode, *vdummy; + struct devfs_vnode *vnode; status_t status; TRACE(("devfs_lookup: entry dir %p, name '%s'\n", dir, name)); @@ -958,7 +959,7 @@ devfs_lookup(fs_volume *_volume, fs_vnode *_dir, const char *name, ino_t *_id) return B_ENTRY_NOT_FOUND; } - status = get_vnode(fs->volume, vnode->id, (void**)&vdummy); + status = get_vnode(fs->volume, vnode->id, NULL, NULL); if (status < B_OK) return status; @@ -1062,7 +1063,7 @@ devfs_create(fs_volume* _volume, fs_vnode* _dir, const char* name, int openMode, if (openMode & O_EXCL) return B_FILE_EXISTS; - status = get_vnode(fs->volume, vnode->id, NULL); + status = get_vnode(fs->volume, vnode->id, NULL, NULL); if (status < B_OK) return status; @@ -2091,7 +2092,7 @@ devfs_unpublish_device(BaseDevice* device, bool disconnect) { devfs_vnode* node; status_t status = get_vnode(sDeviceFileSystem->volume, device->ID(), - (void**)&node); + (void**)&node, NULL); if (status != B_OK) return status; diff --git a/src/system/kernel/fs/Jamfile b/src/system/kernel/fs/Jamfile index 6cb6fa7a4f..7b8f2411ac 100644 --- a/src/system/kernel/fs/Jamfile +++ b/src/system/kernel/fs/Jamfile @@ -12,6 +12,7 @@ KernelMergeObject kernel_fs.o : fifo.cpp KPath.cpp node_monitor.cpp + overlay.cpp rootfs.cpp socket.cpp vfs.cpp diff --git a/src/system/kernel/fs/overlay.cpp b/src/system/kernel/fs/overlay.cpp new file mode 100644 index 0000000000..b8fc5ee604 --- /dev/null +++ b/src/system/kernel/fs/overlay.cpp @@ -0,0 +1,1097 @@ +/* + * Copyright 2009, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include "overlay.h" + + +//#define TRACE_OVERLAY +#ifdef TRACE_OVERLAY +#define TRACE(x...) dprintf("overlay: " x) +#define TRACE_ALWAYS(x...) dprintf("overlay: " x) +#else +#define TRACE(x...) /* nothing */ +#define TRACE_ALWAYS(x...) dprintf("overlay: " x) +#endif + + +namespace overlay { + +class AttributeFile; +class AttributeEntry; + + +class OverlayInode { +public: + OverlayInode(fs_volume *superVolume, + fs_vnode *superVnode); + ~OverlayInode(); + + status_t InitCheck(); + + fs_vnode * SuperVnode() { return &fSuperVnode; } + + AttributeFile * GetAttributeFile(); + +private: + fs_volume fSuperVolume; + fs_vnode fSuperVnode; + AttributeFile * fAttributeFile; +}; + + +class AttributeFile { +public: + AttributeFile(fs_volume *volume, fs_vnode *vnode); + ~AttributeFile(); + + status_t InitCheck() { return fStatus; } + + dev_t VolumeID() { return fVolumeID; } + ino_t ParentInode() { return fParentInode; } + + uint32 CountAttributes(); + AttributeEntry * FindAttribute(const char *name); + + status_t ReadAttributeDir(struct dirent *dirent, + size_t bufferSize, uint32 *numEntries); + status_t RewindAttributeDir() + { + fAttributeDirIndex = 0; + return B_OK; + } + +private: + #define ATTRIBUTE_OVERLAY_FILE_MAGIC 'attr' + #define ATTRIBUTE_OVERLAY_ATTRIBUTE_DIR_NAME "_HAIKU" + + struct attribute_file { + uint32 magic; // 'attr' + uint32 entry_count; + uint8 entries[1]; + } _PACKED; + + status_t fStatus; + dev_t fVolumeID; + ino_t fParentInode; + attribute_file * fFile; + uint32 fAttributeDirIndex; + AttributeEntry ** fEntries; +}; + + +class AttributeEntry { +public: + AttributeEntry(AttributeFile *parent, + uint8 *buffer); + ~AttributeEntry(); + + size_t EntrySize(); + + uint8 NameLength() { return fEntry->name_length; } + const char * Name() { return fEntry->name; } + + status_t FillDirent(struct dirent *dirent, + size_t bufferSize, uint32 *numEntries); + + status_t Read(off_t position, void *buffer, size_t *length); + status_t ReadStat(struct stat *stat); + +private: + struct attribute_entry { + type_code type; + uint32 size; + uint8 name_length; // including 0 byte + char name[1]; // 0 terminated, followed by data + } _PACKED; + + AttributeFile * fParent; + attribute_entry * fEntry; + uint8 * fData; +}; + + +// #pragma mark OverlayInode + + +OverlayInode::OverlayInode(fs_volume *superVolume, fs_vnode *superVnode) + : fSuperVolume(*superVolume), + fSuperVnode(*superVnode), + fAttributeFile(NULL) +{ + TRACE("inode created\n"); +} + + +OverlayInode::~OverlayInode() +{ + TRACE("inode destroyed\n"); + delete fAttributeFile; +} + + +status_t +OverlayInode::InitCheck() +{ + return B_OK; +} + + +AttributeFile * +OverlayInode::GetAttributeFile() +{ + if (fAttributeFile == NULL) { + fAttributeFile = new(std::nothrow) AttributeFile(&fSuperVolume, + &fSuperVnode); + if (fAttributeFile == NULL) { + TRACE_ALWAYS("no memory to allocate attribute file\n"); + return NULL; + } + } + + if (fAttributeFile->InitCheck() != B_OK) + return NULL; + + return fAttributeFile; +} + + +// #pragma mark AttributeFile + + +AttributeFile::AttributeFile(fs_volume *volume, fs_vnode *vnode) + : fStatus(B_NO_INIT), + fVolumeID(volume->id), + fParentInode(0), + fFile(NULL), + fAttributeDirIndex(0), + fEntries(NULL) +{ + if (vnode->ops->get_vnode_name == NULL) { + TRACE_ALWAYS("cannot get vnode name, hook missing\n"); + fStatus = B_UNSUPPORTED; + return; + } + + char nameBuffer[B_FILE_NAME_LENGTH]; + nameBuffer[sizeof(nameBuffer) - 1] = 0; + fStatus = vnode->ops->get_vnode_name(volume, vnode, nameBuffer, + sizeof(nameBuffer) - 1); + if (fStatus != B_OK) { + TRACE_ALWAYS("failed to get vnode name: %s\n", strerror(fStatus)); + return; + } + + if (strcmp(nameBuffer, ATTRIBUTE_OVERLAY_ATTRIBUTE_DIR_NAME) == 0) { + // we don't want attribute overlays on the attribute dir itself + fStatus = B_UNSUPPORTED; + return; + } + + struct stat stat; + if (vnode->ops->read_stat != NULL + && vnode->ops->read_stat(volume, vnode, &stat) == B_OK) { + fParentInode = stat.st_ino; + } + + // TODO: the ".." lookup is not actually valid for non-directory vnodes. + // we make use of the fact that a filesystem probably still provides the + // lookup hook and has hardcoded ".." to resolve to the parent entry. if we + // wanted to do this correctly we need some other way to relate this vnode + // to its parent directory vnode. + const char *lookup[] + = { "..", ATTRIBUTE_OVERLAY_ATTRIBUTE_DIR_NAME, nameBuffer }; + int32 lookupCount = sizeof(lookup) / sizeof(lookup[0]); + fs_vnode currentVnode = *vnode; + ino_t lastInodeNumber = 0; + + for (int32 i = 0; i < lookupCount; i++) { + if (currentVnode.ops->lookup == NULL) { + TRACE_ALWAYS("lookup not possible, lookup hook missing\n"); + fStatus = B_UNSUPPORTED; + if (i > 0) + put_vnode(volume, lastInodeNumber); + return; + } + + ino_t inodeNumber; + fStatus = currentVnode.ops->lookup(volume, ¤tVnode, lookup[i], + &inodeNumber); + + if (i > 0) + put_vnode(volume, lastInodeNumber); + + if (fStatus != B_OK) { + if (fStatus != B_ENTRY_NOT_FOUND) { + TRACE_ALWAYS("lookup of \"%s\" failed: %s\n", lookup[i], + strerror(fStatus)); + } + return; + } + + fStatus = get_vnode(volume, inodeNumber, ¤tVnode.private_node, + ¤tVnode.ops); + if (fStatus != B_OK) { + TRACE_ALWAYS("getting vnode failed: %s\n", strerror(fStatus)); + return; + } + + lastInodeNumber = inodeNumber; + } + + if (currentVnode.ops->read_stat == NULL || currentVnode.ops->open == NULL + || currentVnode.ops->read == NULL) { + TRACE_ALWAYS("can't use attribute file, hooks missing\n"); + put_vnode(volume, lastInodeNumber); + fStatus = B_UNSUPPORTED; + return; + } + + fStatus = currentVnode.ops->read_stat(volume, ¤tVnode, &stat); + if (fStatus != B_OK) { + TRACE_ALWAYS("failed to stat attribute file: %s\n", strerror(fStatus)); + put_vnode(volume, lastInodeNumber); + return; + } + + void *attrFileCookie = NULL; + fStatus = currentVnode.ops->open(volume, ¤tVnode, O_RDONLY, + &attrFileCookie); + if (fStatus != B_OK) { + TRACE_ALWAYS("failed to open attribute file: %s\n", strerror(fStatus)); + put_vnode(volume, lastInodeNumber); + return; + } + + size_t readLength = stat.st_size; + uint8 *buffer = (uint8 *)malloc(readLength); + if (buffer == NULL) { + TRACE_ALWAYS("cannot allocate memory for read buffer\n"); + put_vnode(volume, lastInodeNumber); + fStatus = B_NO_MEMORY; + return; + } + + fStatus = currentVnode.ops->read(volume, ¤tVnode, attrFileCookie, 0, + buffer, &readLength); + if (fStatus != B_OK) { + TRACE_ALWAYS("failed to read from file: %s\n", strerror(fStatus)); + put_vnode(volume, lastInodeNumber); + return; + } + + if (currentVnode.ops->close != NULL) + currentVnode.ops->close(volume, ¤tVnode, attrFileCookie); + if (currentVnode.ops->free_cookie != NULL) + currentVnode.ops->free_cookie(volume, ¤tVnode, attrFileCookie); + + put_vnode(volume, lastInodeNumber); + + fFile = (attribute_file *)buffer; + if (fFile->magic != ATTRIBUTE_OVERLAY_FILE_MAGIC) { + TRACE_ALWAYS("attribute file has bad magic\n"); + fStatus = B_BAD_VALUE; + return; + } + + fEntries = new(std::nothrow) AttributeEntry *[fFile->entry_count]; + if (fEntries == NULL) { + TRACE_ALWAYS("no memory to allocate entry pointers\n"); + fStatus = B_NO_MEMORY; + return; + } + + for (uint32 i = 0; i < fFile->entry_count; i++) + fEntries[i] = NULL; + + size_t totalSize = 0; + readLength -= sizeof(attribute_file) - 1; + for (uint32 i = 0; i < fFile->entry_count; i++) { + fEntries[i] = new(std::nothrow) AttributeEntry(this, + fFile->entries + totalSize); + if (fEntries[i] == NULL) { + TRACE_ALWAYS("no memory to allocate attribute entry\n"); + fStatus = B_NO_MEMORY; + return; + } + + totalSize += fEntries[i]->EntrySize(); + if (totalSize > readLength) { + TRACE_ALWAYS("attribute entries are too large for buffer\n"); + fStatus = B_BAD_VALUE; + return; + } + } +} + + +AttributeFile::~AttributeFile() +{ + if (fFile == NULL) + return; + + if (fEntries != NULL) { + for (uint32 i = 0; i < fFile->entry_count; i++) + delete fEntries[i]; + + delete [] fEntries; + } + + free(fFile); +} + + +uint32 +AttributeFile::CountAttributes() +{ + if (fFile == NULL) + return 0; + + return fFile->entry_count; +} + + +AttributeEntry * +AttributeFile::FindAttribute(const char *name) +{ + if (fFile == NULL) + return NULL; + + for (uint32 i = 0; i < fFile->entry_count; i++) { + if (strncmp(fEntries[i]->Name(), name, fEntries[i]->NameLength()) == 0) + return fEntries[i]; + } + + return NULL; +} + + +status_t +AttributeFile::ReadAttributeDir(struct dirent *dirent, size_t bufferSize, + uint32 *numEntries) +{ + if (fFile == NULL || fAttributeDirIndex >= fFile->entry_count) { + *numEntries = 0; + return B_OK; + } + + return fEntries[fAttributeDirIndex++]->FillDirent(dirent, bufferSize, + numEntries); +} + + +// #pragma mark AttributeEntry + + +AttributeEntry::AttributeEntry(AttributeFile *parent, uint8 *buffer) + : fParent(parent), + fEntry(NULL), + fData(NULL) +{ + fEntry = (attribute_entry *)buffer; + fData = (uint8 *)fEntry->name + fEntry->name_length; +} + + +AttributeEntry::~AttributeEntry() +{ +} + + +size_t +AttributeEntry::EntrySize() +{ + return sizeof(attribute_entry) - 1 + fEntry->name_length + fEntry->size; +} + + +status_t +AttributeEntry::FillDirent(struct dirent *dirent, size_t bufferSize, + uint32 *numEntries) +{ + dirent->d_dev = dirent->d_pdev = fParent->VolumeID(); + dirent->d_ino = (ino_t)this; + dirent->d_pino = fParent->ParentInode(); + dirent->d_reclen = sizeof(struct dirent) + fEntry->name_length; + if (bufferSize < dirent->d_reclen) { + *numEntries = 0; + return B_BAD_VALUE; + } + + strncpy(dirent->d_name, fEntry->name, fEntry->name_length); + dirent->d_name[fEntry->name_length - 1] = 0; + *numEntries = 1; + return B_OK; +} + + +status_t +AttributeEntry::Read(off_t position, void *buffer, size_t *length) +{ + *length = min_c(*length, fEntry->size); + if (*length <= position) { + *length = 0; + return B_OK; + } + + *length -= position; + memcpy(buffer, fData + position, *length); + return B_OK; +} + + +status_t +AttributeEntry::ReadStat(struct stat *stat) +{ + stat->st_dev = fParent->VolumeID(); + stat->st_ino = (ino_t)this; + stat->st_nlink = 1; + stat->st_blksize = 512; + stat->st_uid = 1; + stat->st_gid = 1; + stat->st_size = fEntry->size; + stat->st_mode = S_ATTR | 0x0777; + stat->st_type = fEntry->type; + stat->st_atime = stat->st_mtime = stat->st_crtime = time(NULL); + stat->st_blocks = (fEntry->size + stat->st_blksize - 1) / stat->st_blksize; + return B_OK; +} + + +// #pragma mark - + + +static status_t +overlay_put_vnode(fs_volume *volume, fs_vnode *vnode, bool reenter) +{ + OverlayInode *node = (OverlayInode *)vnode->private_node; + fs_vnode *superVnode = node->SuperVnode(); + + status_t result = B_OK; + if (superVnode->ops->put_vnode != NULL) + result = superVnode->ops->put_vnode(volume, superVnode, reenter); + + delete node; + return result; +} + + +static status_t +overlay_remove_vnode(fs_volume *volume, fs_vnode *vnode, bool reenter) +{ + OverlayInode *node = (OverlayInode *)vnode->private_node; + fs_vnode *superVnode = node->SuperVnode(); + + status_t result = B_OK; + if (superVnode->ops->remove_vnode != NULL) + result = superVnode->ops->remove_vnode(volume, superVnode, reenter); + + delete node; + return result; +} + + +static status_t +overlay_get_super_vnode(fs_volume *volume, fs_vnode *vnode, + fs_volume *superVolume, fs_vnode *_superVnode) +{ + OverlayInode *node = (OverlayInode *)vnode->private_node; + fs_vnode *superVnode = node->SuperVnode(); + + if (superVnode->ops->get_super_vnode != NULL) { + return superVnode->ops->get_super_vnode(volume, superVnode, superVolume, + _superVnode); + } + + *_superVnode = *superVnode; + return B_OK; +} + + +// #pragma mark - + + +#define OVERLAY_CALL(op, params...) \ + TRACE("relaying op: " #op "\n"); \ + OverlayInode *node = (OverlayInode *)vnode->private_node; \ + fs_vnode *superVnode = node->SuperVnode(); \ + if (superVnode->ops->op != NULL) \ + return superVnode->ops->op(volume, superVnode, params); + + +static status_t +overlay_lookup(fs_volume *volume, fs_vnode *vnode, const char *name, ino_t *id) +{ + OVERLAY_CALL(lookup, name, id) + return B_UNSUPPORTED; +} + + +static status_t +overlay_get_vnode_name(fs_volume *volume, fs_vnode *vnode, char *buffer, + size_t bufferSize) +{ + OVERLAY_CALL(get_vnode_name, buffer, bufferSize) + return B_UNSUPPORTED; +} + + +static bool +overlay_can_page(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(can_page, cookie) + return false; +} + + +static status_t +overlay_read_pages(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos, + const iovec *vecs, size_t count, size_t *numBytes) +{ + OVERLAY_CALL(read_pages, cookie, pos, vecs, count, numBytes) + return B_UNSUPPORTED; +} + + +static status_t +overlay_write_pages(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos, + const iovec *vecs, size_t count, size_t *numBytes) +{ + OVERLAY_CALL(write_pages, cookie, pos, vecs, count, numBytes) + return B_UNSUPPORTED; +} + + +#if 0 +static status_t +overlay_io(fs_volume *volume, fs_vnode *vnode, void *cookie, + io_request *request) +{ + OVERLAY_CALL(io, cookie, request) + return B_UNSUPPORTED; +} +#endif + + +static status_t +overlay_cancel_io(fs_volume *volume, fs_vnode *vnode, void *cookie, + io_request *request) +{ + OVERLAY_CALL(cancel_io, cookie, request) + return B_UNSUPPORTED; +} + + +static status_t +overlay_get_file_map(fs_volume *volume, fs_vnode *vnode, off_t offset, + size_t size, struct file_io_vec *vecs, size_t *count) +{ + OVERLAY_CALL(get_file_map, offset, size, vecs, count) + return B_UNSUPPORTED; +} + + +static status_t +overlay_ioctl(fs_volume *volume, fs_vnode *vnode, void *cookie, ulong op, + void *buffer, size_t length) +{ + OVERLAY_CALL(ioctl, cookie, op, buffer, length) + return B_UNSUPPORTED; +} + + +static status_t +overlay_set_flags(fs_volume *volume, fs_vnode *vnode, void *cookie, + int flags) +{ + OVERLAY_CALL(set_flags, cookie, flags) + return B_UNSUPPORTED; +} + + +static status_t +overlay_select(fs_volume *volume, fs_vnode *vnode, void *cookie, uint8 event, + selectsync *sync) +{ + OVERLAY_CALL(select, cookie, event, sync) + return B_UNSUPPORTED; +} + + +static status_t +overlay_deselect(fs_volume *volume, fs_vnode *vnode, void *cookie, uint8 event, + selectsync *sync) +{ + OVERLAY_CALL(deselect, cookie, event, sync) + return B_UNSUPPORTED; +} + + +static status_t +overlay_fsync(fs_volume *volume, fs_vnode *vnode) +{ + OverlayInode *node = (OverlayInode *)vnode->private_node; + fs_vnode *superVnode = node->SuperVnode(); + + if (superVnode->ops->fsync != NULL) + return superVnode->ops->fsync(volume, superVnode); + + return B_OK; +} + + +static status_t +overlay_read_symlink(fs_volume *volume, fs_vnode *vnode, char *buffer, + size_t *bufferSize) +{ + OVERLAY_CALL(read_symlink, buffer, bufferSize) + return B_UNSUPPORTED; +} + + +static status_t +overlay_create_symlink(fs_volume *volume, fs_vnode *vnode, const char *name, + const char *path, int mode) +{ + OVERLAY_CALL(create_symlink, name, path, mode) + return B_UNSUPPORTED; +} + + +static status_t +overlay_link(fs_volume *volume, fs_vnode *vnode, const char *name, + fs_vnode *target) +{ + OVERLAY_CALL(link, name, target) + return B_UNSUPPORTED; +} + + +static status_t +overlay_unlink(fs_volume *volume, fs_vnode *vnode, const char *name) +{ + OVERLAY_CALL(unlink, name) + return B_UNSUPPORTED; +} + + +static status_t +overlay_rename(fs_volume *volume, fs_vnode *vnode, + const char *fromName, fs_vnode *toDir, const char *toName) +{ + OVERLAY_CALL(rename, fromName, toDir, toName) + return B_UNSUPPORTED; +} + + +static status_t +overlay_access(fs_volume *volume, fs_vnode *vnode, int mode) +{ + OVERLAY_CALL(access, mode) + return B_UNSUPPORTED; +} + + +static status_t +overlay_read_stat(fs_volume *volume, fs_vnode *vnode, struct stat *stat) +{ + OVERLAY_CALL(read_stat, stat) + return B_UNSUPPORTED; +} + + +static status_t +overlay_write_stat(fs_volume *volume, fs_vnode *vnode, const struct stat *stat, + uint32 statMask) +{ + OVERLAY_CALL(write_stat, stat, statMask) + return B_UNSUPPORTED; +} + + +static status_t +overlay_create(fs_volume *volume, fs_vnode *vnode, const char *name, + int openMode, int perms, void **cookie, ino_t *newVnodeID) +{ + OVERLAY_CALL(create, name, openMode, perms, cookie, newVnodeID) + return B_UNSUPPORTED; +} + + +static status_t +overlay_open(fs_volume *volume, fs_vnode *vnode, int openMode, void **cookie) +{ + OVERLAY_CALL(open, openMode, cookie) + return B_UNSUPPORTED; +} + + +static status_t +overlay_close(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(close, cookie) + return B_UNSUPPORTED; +} + + +static status_t +overlay_free_cookie(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(free_cookie, cookie) + return B_UNSUPPORTED; +} + + +static status_t +overlay_read(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos, + void *buffer, size_t *length) +{ + OVERLAY_CALL(read, cookie, pos, buffer, length) + return B_UNSUPPORTED; +} + + +static status_t +overlay_write(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos, + const void *buffer, size_t *length) +{ + OVERLAY_CALL(write, cookie, pos, buffer, length) + return B_UNSUPPORTED; +} + + +static status_t +overlay_create_dir(fs_volume *volume, fs_vnode *vnode, const char *name, + int perms, ino_t *newVnodeID) +{ + OVERLAY_CALL(create_dir, name, perms, newVnodeID) + return B_UNSUPPORTED; +} + + +static status_t +overlay_remove_dir(fs_volume *volume, fs_vnode *vnode, const char *name) +{ + OVERLAY_CALL(remove_dir, name) + return B_UNSUPPORTED; +} + + +static status_t +overlay_open_dir(fs_volume *volume, fs_vnode *vnode, void **cookie) +{ + OVERLAY_CALL(open_dir, cookie) + return B_UNSUPPORTED; +} + + +static status_t +overlay_close_dir(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(close_dir, cookie) + return B_UNSUPPORTED; +} + + +static status_t +overlay_free_dir_cookie(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(free_dir_cookie, cookie) + return B_UNSUPPORTED; +} + + +static status_t +overlay_read_dir(fs_volume *volume, fs_vnode *vnode, void *cookie, + struct dirent *buffer, size_t bufferSize, uint32 *num) +{ + OVERLAY_CALL(read_dir, cookie, buffer, bufferSize, num) + return B_UNSUPPORTED; +} + + +static status_t +overlay_rewind_dir(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(rewind_dir, cookie) + return B_UNSUPPORTED; +} + + +static status_t +overlay_open_attr_dir(fs_volume *volume, fs_vnode *vnode, void **cookie) +{ + OVERLAY_CALL(open_attr_dir, cookie) + AttributeFile *attributeFile = node->GetAttributeFile(); + if (attributeFile == NULL) + return B_UNSUPPORTED; + + *cookie = attributeFile; + return B_OK; +} + + +static status_t +overlay_close_attr_dir(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(close_attr_dir, cookie) + return B_OK; +} + + +static status_t +overlay_free_attr_dir_cookie(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(free_attr_dir_cookie, cookie) + return B_OK; +} + + +static status_t +overlay_read_attr_dir(fs_volume *volume, fs_vnode *vnode, void *cookie, + struct dirent *buffer, size_t bufferSize, uint32 *num) +{ + OVERLAY_CALL(read_attr_dir, cookie, buffer, bufferSize, num) + AttributeFile *attributeFile = (AttributeFile *)cookie; + return attributeFile->ReadAttributeDir(buffer, bufferSize, num); +} + + +static status_t +overlay_rewind_attr_dir(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(rewind_attr_dir, cookie) + AttributeFile *attributeFile = (AttributeFile *)cookie; + return attributeFile->RewindAttributeDir(); +} + + +static status_t +overlay_create_attr(fs_volume *volume, fs_vnode *vnode, const char *name, + uint32 type, int openMode, void **cookie) +{ + OVERLAY_CALL(create_attr, name, type, openMode, cookie) + return B_UNSUPPORTED; +} + + +static status_t +overlay_open_attr(fs_volume *volume, fs_vnode *vnode, const char *name, + int openMode, void **cookie) +{ + OVERLAY_CALL(open_attr, name, openMode, cookie) + + AttributeFile *attributeFile = node->GetAttributeFile(); + if (attributeFile == NULL) + return B_UNSUPPORTED; + + AttributeEntry *entry = attributeFile->FindAttribute(name); + if (entry == NULL) + return B_ENTRY_NOT_FOUND; + + *cookie = entry; + return B_OK; +} + + +static status_t +overlay_close_attr(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(close_attr, cookie) + return B_OK; +} + + +static status_t +overlay_free_attr_cookie(fs_volume *volume, fs_vnode *vnode, void *cookie) +{ + OVERLAY_CALL(free_attr_cookie, cookie) + return B_OK; +} + + +static status_t +overlay_read_attr(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos, + void *buffer, size_t *length) +{ + OVERLAY_CALL(read_attr, cookie, pos, buffer, length) + AttributeEntry *entry = (AttributeEntry *)cookie; + return entry->Read(pos, buffer, length); +} + + +static status_t +overlay_write_attr(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos, + const void *buffer, size_t *length) +{ + OVERLAY_CALL(write_attr, cookie, pos, buffer, length) + return B_UNSUPPORTED; +} + + +static status_t +overlay_read_attr_stat(fs_volume *volume, fs_vnode *vnode, void *cookie, + struct stat *stat) +{ + OVERLAY_CALL(read_attr_stat, cookie, stat) + AttributeEntry *entry = (AttributeEntry *)cookie; + return entry->ReadStat(stat); +} + + +static status_t +overlay_write_attr_stat(fs_volume *volume, fs_vnode *vnode, void *cookie, + const struct stat *stat, int statMask) +{ + OVERLAY_CALL(write_attr_stat, cookie, stat, statMask) + return B_UNSUPPORTED; +} + + +static status_t +overlay_rename_attr(fs_volume *volume, fs_vnode *vnode, + const char *fromName, fs_vnode *toVnode, const char *toName) +{ + OVERLAY_CALL(rename_attr, fromName, toVnode, toName) + return B_UNSUPPORTED; +} + + +static status_t +overlay_remove_attr(fs_volume *volume, fs_vnode *vnode, const char *name) +{ + OVERLAY_CALL(remove_attr, name) + return B_UNSUPPORTED; +} + + +static status_t +overlay_create_special_node(fs_volume *volume, fs_vnode *vnode, + const char *name, fs_vnode *subVnode, mode_t mode, uint32 flags, + fs_vnode *_superVnode, ino_t *nodeID) +{ + OVERLAY_CALL(create_special_node, name, subVnode, mode, flags, _superVnode, nodeID) + return B_UNSUPPORTED; +} + + +// #pragma mark - + + +static fs_vnode_ops sOverlayVnodeOps = { + &overlay_lookup, + &overlay_get_vnode_name, + + &overlay_put_vnode, + &overlay_remove_vnode, + + &overlay_can_page, + &overlay_read_pages, + &overlay_write_pages, + + // TODO: the io scheduler uses it when available but we may simply + // return B_UNSUPPORTED and I'm not sure it then falls back correctly + NULL, //&overlay_io, + &overlay_cancel_io, + + &overlay_get_file_map, + + /* common */ + &overlay_ioctl, + &overlay_set_flags, + &overlay_select, + &overlay_deselect, + &overlay_fsync, + + &overlay_read_symlink, + &overlay_create_symlink, + &overlay_link, + &overlay_unlink, + &overlay_rename, + + &overlay_access, + &overlay_read_stat, + &overlay_write_stat, + + /* file */ + &overlay_create, + &overlay_open, + &overlay_close, + &overlay_free_cookie, + &overlay_read, + &overlay_write, + + /* directory */ + &overlay_create_dir, + &overlay_remove_dir, + &overlay_open_dir, + &overlay_close_dir, + &overlay_free_dir_cookie, + &overlay_read_dir, + &overlay_rewind_dir, + + /* attribute directory operations */ + &overlay_open_attr_dir, + &overlay_close_attr_dir, + &overlay_free_attr_dir_cookie, + &overlay_read_attr_dir, + &overlay_rewind_attr_dir, + + /* attribute operations */ + &overlay_create_attr, + &overlay_open_attr, + &overlay_close_attr, + &overlay_free_attr_cookie, + &overlay_read_attr, + &overlay_write_attr, + + &overlay_read_attr_stat, + &overlay_write_attr_stat, + &overlay_rename_attr, + &overlay_remove_attr, + + /* support for node and FS layers */ + &overlay_create_special_node, + &overlay_get_super_vnode +}; + + +} // namespace _overlay + +using namespace overlay; + + +// #pragma mark - + + +status_t +create_overlay_vnode(fs_volume *superVolume, fs_vnode *vnode) +{ + OverlayInode *node = new(std::nothrow) OverlayInode(superVolume, vnode); + if (node == NULL) + return B_NO_MEMORY; + + status_t status = node->InitCheck(); + if (status != B_OK) { + delete node; + return status; + } + + vnode->private_node = node; + vnode->ops = &sOverlayVnodeOps; + return B_OK; +} diff --git a/src/system/kernel/fs/overlay.h b/src/system/kernel/fs/overlay.h new file mode 100644 index 0000000000..0cf27fcb43 --- /dev/null +++ b/src/system/kernel/fs/overlay.h @@ -0,0 +1,16 @@ +/* + * Copyright 2009, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#ifndef _VFS_OVERLAY_H +#define _VFS_OVERLAY_H + +#include + +status_t create_overlay_vnode(fs_volume *superVolume, fs_vnode *vnode); + +#endif // _VFS_OVERLAY_H diff --git a/src/system/kernel/fs/rootfs.cpp b/src/system/kernel/fs/rootfs.cpp index 93a4fefc13..d7d2ef1ef8 100644 --- a/src/system/kernel/fs/rootfs.cpp +++ b/src/system/kernel/fs/rootfs.cpp @@ -284,8 +284,7 @@ remove_node(struct rootfs *fs, struct rootfs_vnode *directory, { // schedule this vnode to be removed when it's ref goes to zero - void* dummy; - bool gotNode = (get_vnode(fs->volume, vnode->id, &dummy) == B_OK); + bool gotNode = (get_vnode(fs->volume, vnode->id, NULL, NULL) == B_OK); status_t status = B_OK; if (gotNode) @@ -432,7 +431,7 @@ rootfs_lookup(fs_volume *_volume, fs_vnode *_dir, const char *name, ino_t *_id) { struct rootfs *fs = (struct rootfs *)_volume->private_volume; struct rootfs_vnode *dir = (struct rootfs_vnode *)_dir->private_node; - struct rootfs_vnode *vnode,*vdummy; + struct rootfs_vnode *vnode; status_t status; TRACE(("rootfs_lookup: entry dir %p, name '%s'\n", dir, name)); @@ -448,7 +447,7 @@ rootfs_lookup(fs_volume *_volume, fs_vnode *_dir, const char *name, ino_t *_id) goto err; } - status = get_vnode(fs->volume, vnode->id, (void**)&vdummy); + status = get_vnode(fs->volume, vnode->id, NULL, NULL); if (status < B_OK) goto err; diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 5676311667..ef1d7fded4 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -54,6 +54,7 @@ #include "fifo.h" #include "io_requests.h" +#include "overlay.h" //#define TRACE_VFS @@ -1180,6 +1181,9 @@ create_special_sub_node(struct vnode* vnode, uint32 flags) if (S_ISFIFO(vnode->type)) return create_fifo_vnode(vnode->mount->volume, vnode); + if ((flags & B_VNODE_WANTS_OVERLAY_SUB_NODE) != 0) + return create_overlay_vnode(vnode->mount->volume, vnode); + return B_BAD_VALUE; } @@ -1253,7 +1257,8 @@ restart: bool publishSpecialSubNode = false; if (gotNode) { vnode->type = type; - publishSpecialSubNode = is_special_node_type(type) + publishSpecialSubNode = (is_special_node_type(type) + || (flags & B_VNODE_WANTS_OVERLAY_SUB_NODE) != 0) && (flags & B_VNODE_DONT_CREATE_SPECIAL_SUB_NODE) == 0; } @@ -2485,8 +2490,9 @@ get_vnode_name(struct vnode *vnode, struct vnode *parent, struct dirent *buffer, if (HAS_FS_CALL(vnode, get_vnode_name)) { // The FS supports getting the name of a vnode. - return FS_CALL(vnode, get_vnode_name, buffer->d_name, - (char*)buffer + bufferSize - buffer->d_name); + if (FS_CALL(vnode, get_vnode_name, buffer->d_name, + (char*)buffer + bufferSize - buffer->d_name) == B_OK) + return B_OK; } // The FS doesn't support getting the name of a vnode. So we search the @@ -3584,7 +3590,8 @@ publish_vnode(fs_volume *volume, ino_t vnodeID, void *privateNode, extern "C" status_t -get_vnode(fs_volume *volume, ino_t vnodeID, void **_fsNode) +get_vnode(fs_volume *volume, ino_t vnodeID, void **_privateNode, + fs_vnode_ops **_vnodeOps) { struct vnode *vnode; @@ -3608,10 +3615,16 @@ get_vnode(fs_volume *volume, ino_t vnodeID, void **_fsNode) return status; } - if (_fsNode != NULL) - *_fsNode = resolvedNode.private_node; - } else if (_fsNode != NULL) - *_fsNode = vnode->private_node; + if (_privateNode != NULL) + *_privateNode = resolvedNode.private_node; + if (_vnodeOps != NULL) + *_vnodeOps = resolvedNode.ops; + } else { + if (_privateNode != NULL) + *_privateNode = vnode->private_node; + if (_vnodeOps != NULL) + *_vnodeOps = vnode->ops; + } return B_OK; } @@ -4005,7 +4018,7 @@ vfs_get_fs_node_from_path(fs_volume *volume, const char *path, bool kernel, } // Use get_vnode() to resolve the cookie for the right layer. - status = get_vnode(volume, vnode->id, _node); + status = get_vnode(volume, vnode->id, _node, NULL); put_vnode(vnode); return status; diff --git a/src/system/kernel/fs/vfs_request_io.cpp b/src/system/kernel/fs/vfs_request_io.cpp index b91f7b66cf..c7be98a405 100644 --- a/src/system/kernel/fs/vfs_request_io.cpp +++ b/src/system/kernel/fs/vfs_request_io.cpp @@ -401,14 +401,16 @@ synchronous_io(io_request* request, DoIO& io) status_t vfs_vnode_io(struct vnode* vnode, void* cookie, io_request* request) { - if (!HAS_FS_CALL(vnode, io)) { + status_t result = B_ERROR; + if (!HAS_FS_CALL(vnode, io) + || (result = FS_CALL(vnode, io, cookie, request)) == B_UNSUPPORTED) { // no io() call -- fall back to synchronous I/O IOBuffer* buffer = request->Buffer(); VnodeIO io(request->IsWrite(), buffer->IsPhysical(), vnode, cookie); return synchronous_io(request, io); } - return FS_CALL(vnode, io, cookie, request); + return result; } diff --git a/src/tools/fs_shell/vfs.cpp b/src/tools/fs_shell/vfs.cpp index 8d14b8621f..fb5eeacbb3 100644 --- a/src/tools/fs_shell/vfs.cpp +++ b/src/tools/fs_shell/vfs.cpp @@ -1985,7 +1985,8 @@ fssh_publish_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID, extern "C" fssh_status_t -fssh_get_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID, void **fsNode) +fssh_get_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID, + void **privateNode, fssh_fs_vnode_ops **vnodeOps) { struct vnode *vnode; @@ -2009,9 +2010,16 @@ fssh_get_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID, void **fsNode) return status; } - *fsNode = resolvedNode.private_node; - } else - *fsNode = vnode->private_node; + if (privateNode != NULL) + *privateNode = resolvedNode.private_node; + if (vnodeOps != NULL) + *vnodeOps = resolvedNode.ops; + } else { + if (privateNode != NULL) + *privateNode = vnode->private_node; + if (vnodeOps != NULL) + *vnodeOps = vnode->ops; + } return FSSH_B_OK; } @@ -2443,7 +2451,7 @@ vfs_get_fs_node_from_path(fssh_fs_volume *volume, const char *path, bool kernel, } // Use get_vnode() to resolve the cookie for the right layer. - status = ::fssh_get_vnode(volume, vnode->id, _node); + status = ::fssh_get_vnode(volume, vnode->id, _node, NULL); put_vnode(vnode); return FSSH_B_OK;