1 /* 2 * Copyright 2010, 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 "Utility.h" 13 14 #include <stdio.h> 15 16 17 //#define TRACE_EXT2 18 #ifdef TRACE_EXT2 19 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) 20 #else 21 # define TRACE(x...) ; 22 #endif 23 24 25 Attribute::Attribute(Inode* inode) 26 : 27 fVolume(inode->GetVolume()), 28 fBlock(fVolume), 29 fInode(inode), 30 fBodyEntry(NULL), 31 fBlockEntry(NULL), 32 fName(NULL) 33 { 34 35 } 36 37 38 Attribute::Attribute(Inode* inode, attr_cookie* cookie) 39 : 40 fVolume(inode->GetVolume()), 41 fBlock(fVolume), 42 fInode(inode), 43 fBodyEntry(NULL), 44 fBlockEntry(NULL), 45 fName(NULL) 46 { 47 Find(cookie->name); 48 } 49 50 51 Attribute::~Attribute() 52 { 53 Put(); 54 } 55 56 57 status_t 58 Attribute::InitCheck() 59 { 60 return (fBodyEntry != NULL || fBlockEntry != NULL) ? B_OK : B_NO_INIT; 61 } 62 63 64 status_t 65 Attribute::CheckAccess(const char* name, int openMode) 66 { 67 return fInode->CheckPermissions(open_mode_to_access(openMode) 68 | (openMode & O_TRUNC ? W_OK : 0)); 69 } 70 71 72 status_t 73 Attribute::Find(const char* name) 74 { 75 return _Find(name, -1); 76 } 77 78 79 status_t 80 Attribute::Find(int32 index) 81 { 82 return _Find(NULL, index); 83 } 84 85 86 status_t 87 Attribute::GetName(char* name, size_t* _nameLength) 88 { 89 if (fBodyEntry == NULL && fBlockEntry == NULL) 90 return B_NO_INIT; 91 if (fBodyEntry != NULL) 92 return _PrefixedName(fBodyEntry, name, _nameLength); 93 else 94 return _PrefixedName(fBlockEntry, name, _nameLength); 95 } 96 97 98 void 99 Attribute::Put() 100 { 101 if (fBodyEntry != NULL) { 102 recursive_lock_unlock(&fInode->SmallDataLock()); 103 fBlock.Unset(); 104 fBodyEntry = NULL; 105 } 106 107 if (fBlockEntry != NULL) { 108 fBlock.Unset(); 109 fBlockEntry = NULL; 110 } 111 } 112 113 114 status_t 115 Attribute::Create(const char* name, type_code type, int openMode, 116 attr_cookie** _cookie) 117 { 118 status_t status = CheckAccess(name, openMode); 119 if (status < B_OK) 120 return status; 121 122 attr_cookie* cookie = new(std::nothrow) attr_cookie; 123 if (cookie == NULL) 124 return B_NO_MEMORY; 125 126 fName = name; 127 128 // initialize the cookie 129 strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH); 130 cookie->type = type; 131 cookie->open_mode = openMode; 132 cookie->create = true; 133 134 if (Find(name) == B_OK) { 135 // attribute already exists 136 if ((openMode & O_TRUNC) != 0) 137 _Truncate(); 138 } 139 *_cookie = cookie; 140 return B_OK; 141 } 142 143 144 status_t 145 Attribute::Open(const char* name, int openMode, attr_cookie** _cookie) 146 { 147 TRACE("Open\n"); 148 status_t status = CheckAccess(name, openMode); 149 if (status < B_OK) 150 return status; 151 152 status = Find(name); 153 if (status < B_OK) 154 return status; 155 156 attr_cookie* cookie = new(std::nothrow) attr_cookie; 157 if (cookie == NULL) 158 return B_NO_MEMORY; 159 160 // initialize the cookie 161 strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH); 162 cookie->open_mode = openMode; 163 cookie->create = false; 164 165 // Should we truncate the attribute? 166 if ((openMode & O_TRUNC) != 0) 167 _Truncate(); 168 169 *_cookie = cookie; 170 return B_OK; 171 } 172 173 174 status_t 175 Attribute::Stat(struct stat& stat) 176 { 177 TRACE("Stat\n"); 178 if (fBodyEntry == NULL && fBlockEntry == NULL) 179 return B_NO_INIT; 180 181 stat.st_type = B_XATTR_TYPE; 182 183 if (fBodyEntry != NULL) 184 stat.st_size = fBodyEntry->ValueSize(); 185 else if (fBlockEntry != NULL) 186 stat.st_size = fBlockEntry->ValueSize(); 187 188 return B_OK; 189 } 190 191 192 status_t 193 Attribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* _length) 194 { 195 if (fBodyEntry == NULL && fBlockEntry == NULL) 196 return B_NO_INIT; 197 198 if (pos < 0LL) 199 return ERANGE; 200 201 size_t length = *_length; 202 const uint8* start = (uint8 *)fBlock.Block(); 203 if (fBlockEntry != NULL) { 204 pos += fBlockEntry->ValueOffset(); 205 if (((uint32)pos + length) > fVolume->BlockSize() 206 || length > fBlockEntry->ValueSize()) 207 return ERANGE; 208 } else { 209 start += fVolume->InodeBlockIndex(fInode->ID()) * fVolume->InodeSize(); 210 const uint8* end = start + fVolume->InodeSize(); 211 start += EXT2_INODE_NORMAL_SIZE + fInode->Node().ExtraInodeSize() 212 + sizeof(uint32); 213 pos += fBodyEntry->ValueOffset(); 214 if ((pos + length) > (end - start) || length > fBodyEntry->ValueSize()) 215 return ERANGE; 216 } 217 memcpy(buffer, start + (uint32)pos, length); 218 219 *_length = length; 220 return B_OK; 221 } 222 223 224 status_t 225 Attribute::Write(Transaction& transaction, attr_cookie* cookie, off_t pos, 226 const uint8* buffer, size_t* _length, bool* _created) 227 { 228 if (!cookie->create && fBodyEntry == NULL && fBlockEntry == NULL) 229 return B_NO_INIT; 230 231 // TODO: Implement 232 return B_ERROR; 233 } 234 235 236 status_t 237 Attribute::_Truncate() 238 { 239 // TODO: Implement 240 return B_ERROR; 241 } 242 243 244 status_t 245 Attribute::_Find(const char* name, int32 index) 246 { 247 Put(); 248 249 fName = name; 250 251 // try to find it in the small data region 252 if (fInode->HasExtraAttributes() 253 && recursive_lock_lock(&fInode->SmallDataLock()) == B_OK) { 254 off_t blockNum; 255 fVolume->GetInodeBlock(fInode->ID(), blockNum); 256 257 if (blockNum != 0) { 258 fBlock.SetTo(blockNum); 259 const uint8* start = fBlock.Block() 260 + fVolume->InodeBlockIndex(fInode->ID()) * fVolume->InodeSize(); 261 const uint8* end = start + fVolume->InodeSize(); 262 int32 count = 0; 263 if (_FindAttributeBody(start + EXT2_INODE_NORMAL_SIZE 264 + fInode->Node().ExtraInodeSize(), end, name, index, &count, 265 &fBodyEntry) == B_OK) 266 return B_OK; 267 index -= count; 268 } 269 270 recursive_lock_unlock(&fInode->SmallDataLock()); 271 fBlock.Unset(); 272 } 273 274 // then, search in the attribute directory 275 if (fInode->Node().ExtendedAttributesBlock() != 0) { 276 fBlock.SetTo(fInode->Node().ExtendedAttributesBlock()); 277 if (_FindAttributeBlock(fBlock.Block(), 278 fBlock.Block() + fVolume->BlockSize(), name, index, NULL, 279 &fBlockEntry) == B_OK) 280 return B_OK; 281 fBlock.Unset(); 282 } 283 284 return B_ENTRY_NOT_FOUND; 285 } 286 287 288 status_t 289 Attribute::_FindAttributeBody(const uint8* start, const uint8* end, 290 const char* name, int32 index, int32 *count, ext2_xattr_entry** _entry) 291 { 292 TRACE("_FindAttributeBody %p %p %s\n", start, end, name); 293 if (*((uint32*)start) != EXT2_XATTR_MAGIC) 294 return B_BAD_DATA; 295 return _FindAttribute(start + sizeof(uint32), end, name, index, count, 296 _entry); 297 } 298 299 300 status_t 301 Attribute::_FindAttributeBlock(const uint8* start, const uint8* end, const char* name, 302 int32 index, int32 *count, ext2_xattr_entry** _entry) 303 { 304 TRACE("_FindAttributeBlock %p %p %s\n", start, end, name); 305 ext2_xattr_header *header = (ext2_xattr_header*)start; 306 if (!header->IsValid()) 307 return B_BAD_DATA; 308 309 return _FindAttribute(start + sizeof(ext2_xattr_header), end, name, index, 310 count, _entry); 311 } 312 313 314 status_t 315 Attribute::_FindAttribute(const uint8* start, const uint8* end, const char* name, 316 int32 index, int32 *count, ext2_xattr_entry** _entry) 317 { 318 TRACE("_FindAttribute %p %p %s\n", start, end, name); 319 char buffer[EXT2_XATTR_NAME_LENGTH]; 320 321 int32 i = 0; 322 while (start < end) { 323 ext2_xattr_entry* entry = (ext2_xattr_entry*)start; 324 if (!entry->IsValid()) 325 break; 326 327 size_t length = EXT2_XATTR_NAME_LENGTH; 328 if ((name != NULL && _PrefixedName(entry, buffer, &length) == B_OK 329 && strncmp(name, buffer, length) == 0) || index == i) { 330 *_entry = entry; 331 return B_OK; 332 } 333 start += entry->Length(); 334 i++; 335 } 336 337 if (count != NULL) 338 *count = i; 339 return B_ENTRY_NOT_FOUND; 340 } 341 342 343 status_t 344 Attribute::_PrefixedName(ext2_xattr_entry* entry, char* _name, size_t* _nameLength) 345 { 346 const char *indexNames[] = { "0", "user" }; 347 size_t l = 0; 348 349 if (entry->NameIndex() < ((sizeof(indexNames) / sizeof(indexNames[0])))) 350 l = snprintf(_name, *_nameLength, "%s.%s.%.*s", 351 "linux", indexNames[entry->NameIndex()], entry->NameLength(), 352 entry->name); 353 else 354 l = snprintf(_name, *_nameLength, "%s.%d.%.*s", 355 "linux", entry->NameIndex(), entry->NameLength(), entry->name); 356 if (l < 1 || l > *_nameLength - 1) 357 return ENOBUFS; 358 359 *_nameLength = l + 1; 360 _name[l] = '\0'; 361 362 return B_OK; 363 } 364 365