diff --git a/src/add-ons/kernel/file_systems/ext2/Inode.cpp b/src/add-ons/kernel/file_systems/ext2/Inode.cpp index 409c43b214..e265f497c0 100644 --- a/src/add-ons/kernel/file_systems/ext2/Inode.cpp +++ b/src/add-ons/kernel/file_systems/ext2/Inode.cpp @@ -7,11 +7,12 @@ #include "Inode.h" #include +#include #include "CachedBlock.h" -//#define TRACE_EXT2 +#define TRACE_EXT2 #ifdef TRACE_EXT2 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) #else @@ -25,7 +26,8 @@ Inode::Inode(Volume* volume, ino_t id) fID(id), fCache(NULL), fMap(NULL), - fNode(NULL) + fNode(NULL), + fAttributesBlock(NULL) { rw_lock_init(&fLock, "ext2 inode"); @@ -53,6 +55,11 @@ Inode::~Inode() file_cache_delete(FileCache()); file_map_delete(Map()); + if (fAttributesBlock) { + uint32 block = B_LENDIAN_TO_HOST_INT32(Node().file_access_control); + block_cache_put(fVolume->BlockCache(), block); + } + if (fNode != NULL) { uint32 block; if (fVolume->GetInodeBlock(ID(), block) == B_OK) @@ -206,3 +213,30 @@ Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) return file_cache_read(FileCache(), NULL, pos, buffer, _length); } + +status_t +Inode::AttributeBlockReadAt(off_t pos, uint8* buffer, size_t* _length) +{ + size_t length = *_length; + + if (!fAttributesBlock) { + uint32 block = B_LENDIAN_TO_HOST_INT32(Node().file_access_control); + + if (block == 0) + return B_ENTRY_NOT_FOUND; + + TRACE("inode %Ld attributes at block %lu\n", ID(), block); + fAttributesBlock = (ext2_xattr_header*)block_cache_get( + GetVolume()->BlockCache(), block); + } + + if (!fAttributesBlock) + return B_ENTRY_NOT_FOUND; + + if (pos < 0LL || ((uint32)pos + length) > GetVolume()->BlockSize()) + return ERANGE; + + memcpy(buffer, ((uint8 *)fAttributesBlock) + (uint32)pos, length); + *_length = length; + return B_NO_ERROR; +} diff --git a/src/add-ons/kernel/file_systems/ext2/Inode.h b/src/add-ons/kernel/file_systems/ext2/Inode.h index c7d1015367..52db832f81 100644 --- a/src/add-ons/kernel/file_systems/ext2/Inode.h +++ b/src/add-ons/kernel/file_systems/ext2/Inode.h @@ -49,6 +49,9 @@ public: status_t FindBlock(off_t offset, uint32& block); status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); + status_t AttributeBlockReadAt(off_t pos, uint8 *buffer, + size_t *length); + ext2_inode& Node() { return *fNode; } void* FileCache() const { return fCache; } @@ -66,6 +69,7 @@ private: void* fCache; void* fMap; ext2_inode* fNode; + ext2_xattr_header* fAttributesBlock; }; #endif // INODE_H diff --git a/src/add-ons/kernel/file_systems/ext2/Jamfile b/src/add-ons/kernel/file_systems/ext2/Jamfile index eafc2a5efa..16d9e20c15 100644 --- a/src/add-ons/kernel/file_systems/ext2/Jamfile +++ b/src/add-ons/kernel/file_systems/ext2/Jamfile @@ -13,6 +13,7 @@ UsePrivateKernelHeaders ; KernelAddon ext2 : Volume.cpp Inode.cpp + AttributeIterator.cpp DirectoryIterator.cpp kernel_interface.cpp diff --git a/src/add-ons/kernel/file_systems/ext2/ext2.h b/src/add-ons/kernel/file_systems/ext2/ext2.h index 336a9045e1..96d9779950 100644 --- a/src/add-ons/kernel/file_systems/ext2/ext2.h +++ b/src/add-ons/kernel/file_systems/ext2/ext2.h @@ -10,6 +10,7 @@ #include #include +#include #define EXT2_SUPER_BLOCK_OFFSET 1024 @@ -286,6 +287,78 @@ struct ext2_dir_entry { #define EXT2_TYPE_SOCKET 6 #define EXT2_TYPE_SYMLINK 7 +#define EXT2_XATTR_MAGIC 0xea020000 +#define EXT2_XATTR_ROUND ((1 << 2) - 1) +#define EXT2_XATTR_NAME_LENGTH 255 + +#define EXT2_XATTR_INDEX_USER 1 + +struct ext2_xattr_header { + uint32 magic; + uint32 refcount; + uint32 blocks; // must be 1 for ext2 + uint32 hash; + uint32 reserved[4]; // zero + + bool IsValid() const + { + return B_LENDIAN_TO_HOST_INT32(magic) == EXT2_XATTR_MAGIC + && B_LENDIAN_TO_HOST_INT32(blocks) == 1 + && refcount <= 1024; + } + + void Dump() const { + for (int i = 0; i < Length(); i++) + dprintf("%02x ", ((uint8 *)this)[i]); + dprintf("\n"); + } + + static size_t Length() + { + return sizeof(ext2_xattr_header); + } +}; + +struct ext2_xattr_entry { + uint8 name_length; + uint8 name_index; + uint16 value_offset; + uint32 value_block; // must be zero for ext2 + uint32 value_size; + uint32 hash; + char name[EXT2_XATTR_NAME_LENGTH]; + + uint8 NameLength() const { return name_length; } + uint8 NameIndex() const { return name_index; } + uint16 ValueOffset() const { return + B_LENDIAN_TO_HOST_INT16(value_offset); } + uint32 ValueSize() const { return + B_LENDIAN_TO_HOST_INT32(value_size); } + + // padded sizes + uint32 Length() const { return (MinimumSize() + NameLength() + + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND; } + + bool IsValid() const + { + return NameLength() > 0 && value_block == 0; + // There is no maximum size, as the last entry spans until the + // end of the block + } + + void Dump(bool full=false) const { + for (int i = 0; i < (full ? sizeof(this) : MinimumSize()); i++) + dprintf("%02x ", ((uint8 *)this)[i]); + dprintf("\n"); + } + + static size_t MinimumSize() + { + return sizeof(ext2_xattr_entry) - EXT2_XATTR_NAME_LENGTH; + } +} _PACKED; + + extern fs_volume_ops gExt2VolumeOps; extern fs_vnode_ops gExt2VnodeOps; 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 80bd7fc31f..9fd67015a1 100644 --- a/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp @@ -11,11 +11,20 @@ #include #include +#include "AttributeIterator.h" #include "DirectoryIterator.h" #include "ext2.h" #include "Inode.h" +//#define TRACE_EXT2 +#ifdef TRACE_EXT2 +# define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) +#else +# define TRACE(x...) ; +#endif + + #define EXT2_IO_SIZE 65536 @@ -497,6 +506,198 @@ ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) } +static status_t +ext2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie) +{ + Inode* inode = (Inode*)_node->private_node; + Volume* volume = (Volume*)_volume->private_volume; + TRACE("%s()\n", __FUNCTION__); + + if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR)) + return ENOSYS; + + // on directories too ? + if (!inode->IsFile()) + return EINVAL; + + AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode); + if (iterator == NULL) + return B_NO_MEMORY; + + *_cookie = iterator; + return B_OK; +} + +static status_t +ext2_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie) +{ + TRACE("%s()\n", __FUNCTION__); + return B_OK; +} + + +static status_t +ext2_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) +{ + TRACE("%s()\n", __FUNCTION__); + delete (AttributeIterator *)_cookie; + return B_OK; +} + + +static status_t +ext2_read_attr_dir(fs_volume* _volume, fs_vnode* _node, + void* _cookie, struct dirent* dirent, size_t bufferSize, + uint32* _num) +{ + Inode* inode = (Inode*)_node->private_node; + AttributeIterator *iterator = (AttributeIterator *)_cookie; + TRACE("%s()\n", __FUNCTION__); + + size_t length = bufferSize; + status_t status = iterator->GetNext(dirent->d_name, &length); + if (status == B_ENTRY_NOT_FOUND) { + *_num = 0; + return B_OK; + } else if (status != B_OK) + return status; + + Volume* volume = (Volume*)_volume->private_volume; + + dirent->d_dev = volume->ID(); + dirent->d_ino = inode->ID(); + dirent->d_reclen = sizeof(struct dirent) + length; + + *_num = 1; + return B_OK; +} + + +static status_t +ext2_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie) +{ + AttributeIterator *iterator = (AttributeIterator *)_cookie; + TRACE("%s()\n", __FUNCTION__); + return iterator->Rewind(); +} + + + /* attribute operations */ +static status_t +ext2_create_attr(fs_volume* _volume, fs_vnode* _node, + const char* name, uint32 type, int openMode, void** _cookie) +{ + return EROFS; +} + + +static status_t +ext2_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name, + int openMode, void** _cookie) +{ + TRACE("%s()\n", __FUNCTION__); + if ((openMode & O_RWMASK) != O_RDONLY) + return EROFS; + + Inode* inode = (Inode*)_node->private_node; + Volume* volume = (Volume*)_volume->private_volume; + + if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR)) + return ENOSYS; + + // on directories too ? + if (!inode->IsFile()) + return EINVAL; + + ext2_xattr_entry *entry = new ext2_xattr_entry; + + AttributeIterator i(inode); + status_t status = i.Find(name, entry); + if (status == B_OK) { + entry->Dump(); + *_cookie = entry; + return B_OK; + } + + delete entry; + return status; +} + + +static status_t +ext2_close_attr(fs_volume* _volume, fs_vnode* _node, + void* cookie) +{ + return B_OK; +} + + +static status_t +ext2_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, + void* cookie) +{ + ext2_xattr_entry *entry = (ext2_xattr_entry *)cookie; + + delete entry; + return B_OK; +} + + +static status_t +ext2_read_attr(fs_volume* _volume, fs_vnode* _node, void* cookie, + off_t pos, void* buffer, size_t* length) +{ + return ENOSYS; +} + + +static status_t +ext2_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie, + off_t pos, const void* buffer, size_t* length) +{ + return EROFS; +} + + + +static status_t +ext2_read_attr_stat(fs_volume* _volume, fs_vnode* _node, + void* cookie, struct stat* stat) +{ + ext2_xattr_entry *entry = (ext2_xattr_entry *)cookie; + + stat->st_type = B_RAW_TYPE; + stat->st_size = entry->ValueSize(); + TRACE("%s: st_size %d\n", __FUNCTION__, stat->st_size); + + return B_OK; +} + + +static status_t +ext2_write_attr_stat(fs_volume* _volume, fs_vnode* _node, + void* cookie, const struct stat* stat, int statMask) +{ + return EROFS; +} + + +static status_t +ext2_rename_attr(fs_volume* _volume, fs_vnode* fromVnode, + const char* fromName, fs_vnode* toVnode, const char* toName) +{ + return ENOSYS; +} + + +static status_t +ext2_remove_attr(fs_volume* _volume, fs_vnode* vnode, + const char* name) +{ + return ENOSYS; +} + + fs_volume_ops gExt2VolumeOps = { &ext2_unmount, &ext2_read_fs_info, @@ -556,7 +757,25 @@ fs_vnode_ops gExt2VnodeOps = { &ext2_read_dir, &ext2_rewind_dir, - NULL, + /* attribute directory operations */ + &ext2_open_attr_dir, + &ext2_close_attr_dir, + &ext2_free_attr_dir_cookie, + &ext2_read_attr_dir, + &ext2_rewind_attr_dir, + + /* attribute operations */ + NULL, //&ext2_create_attr, + &ext2_open_attr, + &ext2_close_attr, + &ext2_free_attr_cookie, + NULL, //&ext2_read_attr, + NULL, //&ext2_write_attr, + &ext2_read_attr_stat, + NULL, //&ext2_write_attr_stat, + NULL, //&ext2_rename_attr, + NULL, //&ext2_remove_attr, + }; static file_system_module_info sExt2FileSystem = {