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