1/* 2 * Copyright 2010-2011, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2010, François Revol, <revol@free.fr>. 4 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. 5 * This file may be used under the terms of the MIT License. 6 */ 7 8//! connection between pure inode and kernel_interface attributes 9 10 11#include "Attribute.h" 12 13#include <new> 14#include <stdio.h> 15#include <stdlib.h> 16 17#include "BPlusTree.h" 18#include "CRCTable.h" 19#include "Utility.h" 20 21 22//#define TRACE_BTRFS 23#ifdef TRACE_BTRFS 24# define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x) 25#else 26# define TRACE(x...) ; 27#endif 28 29 30Attribute::Attribute(Inode* inode) 31 : 32 fVolume(inode->GetVolume()), 33 fInode(inode), 34 fName(NULL) 35{ 36} 37 38 39Attribute::Attribute(Inode* inode, attr_cookie* cookie) 40 : 41 fVolume(inode->GetVolume()), 42 fInode(inode), 43 fName(cookie->name) 44{ 45} 46 47 48Attribute::~Attribute() 49{ 50} 51 52 53status_t 54Attribute::CheckAccess(const char* name, int openMode) 55{ 56 return fInode->CheckPermissions(open_mode_to_access(openMode) 57 | (openMode & O_TRUNC ? W_OK : 0)); 58} 59 60 61status_t 62Attribute::Open(const char* name, int openMode, attr_cookie** _cookie) 63{ 64 TRACE("Open\n"); 65 status_t status = CheckAccess(name, openMode); 66 if (status < B_OK) 67 return status; 68 69 status = _Lookup(name, strlen(name)); 70 if (status < B_OK) 71 return status; 72 73 attr_cookie* cookie = new(std::nothrow) attr_cookie; 74 if (cookie == NULL) 75 return B_NO_MEMORY; 76 77 fName = name; 78 79 // initialize the cookie 80 strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH); 81 cookie->open_mode = openMode; 82 cookie->create = false; 83 84 *_cookie = cookie; 85 return B_OK; 86} 87 88 89status_t 90Attribute::Stat(struct stat& stat) 91{ 92 TRACE("Stat\n"); 93 94 size_t nameLength = strlen(fName); 95 btrfs_dir_entry *entries; 96 size_t length; 97 status_t status = _Lookup(fName, nameLength, &entries, &length); 98 if (status < B_OK) 99 return status; 100 101 btrfs_dir_entry *entry; 102 status = _FindEntry(entries, length, fName, nameLength, &entry); 103 if (status != B_OK) { 104 free(entries); 105 return status; 106 } 107 108 // found an entry to stat 109 stat.st_type = B_XATTR_TYPE; 110 stat.st_size = entry->DataLength(); 111 free(entries); 112 return B_OK; 113} 114 115 116status_t 117Attribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* _length) 118{ 119 if (pos < 0LL) 120 return ERANGE; 121 122 size_t nameLength = strlen(fName); 123 btrfs_dir_entry *entries; 124 size_t length; 125 status_t status = _Lookup(fName, nameLength, &entries, &length); 126 if (status < B_OK) 127 return status; 128 129 btrfs_dir_entry *entry; 130 status = _FindEntry(entries, length, fName, nameLength, &entry); 131 if (status != B_OK) { 132 free(entries); 133 return status; 134 } 135 136 // found an entry to read 137 if (pos + *_length > entry->DataLength()) 138 length = entry->DataLength() - pos; 139 else 140 length = *_length - pos; 141 memcpy(buffer, (uint8*)entry + entry->NameLength() 142 + sizeof(btrfs_dir_entry) + (uint32)pos, length); 143 *_length = length; 144 145 free(entries); 146 return B_OK; 147} 148 149 150status_t 151Attribute::_Lookup(const char* name, size_t nameLength, 152 btrfs_dir_entry **_entries, size_t *_length) 153{ 154 uint32 hash = calculate_crc((uint32)~1, (uint8*)name, nameLength); 155 struct btrfs_key key; 156 key.SetType(BTRFS_KEY_TYPE_XATTR_ITEM); 157 key.SetObjectID(fInode->ID()); 158 key.SetOffset(hash); 159 160 btrfs_dir_entry *entries; 161 size_t length; 162 status_t status = fInode->GetVolume()->FSTree()->FindExact(key, 163 (void**)&entries, &length); 164 if (status != B_OK) { 165 TRACE("AttributeIterator::Lookup(): Couldn't find entry with hash %lu " 166 "\"%s\"\n", hash, name); 167 return status; 168 } 169 170 if (_entries == NULL) 171 free(entries); 172 else 173 *_entries = entries; 174 175 if (_length != NULL) 176 *_length = length; 177 178 return B_OK; 179} 180 181 182status_t 183Attribute::_FindEntry(btrfs_dir_entry *entries, size_t length, 184 const char* name, size_t nameLength, btrfs_dir_entry **_entry) 185{ 186 btrfs_dir_entry *entry = entries; 187 uint16 current = 0; 188 while (current < length) { 189 current += entry->Length(); 190 break; 191 // TODO there could be several entries with the same name hash 192 entry = (btrfs_dir_entry *)((uint8*)entry + entry->Length()); 193 } 194 195 *_entry = entry; 196 return B_OK; 197} 198 199