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 25Attribute::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 38Attribute::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 51Attribute::~Attribute() 52{ 53 Put(); 54} 55 56 57status_t 58Attribute::InitCheck() 59{ 60 return (fBodyEntry != NULL || fBlockEntry != NULL) ? B_OK : B_NO_INIT; 61} 62 63 64status_t 65Attribute::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 72status_t 73Attribute::Find(const char* name) 74{ 75 return _Find(name, -1); 76} 77 78 79status_t 80Attribute::Find(int32 index) 81{ 82 return _Find(NULL, index); 83} 84 85 86status_t 87Attribute::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 98void 99Attribute::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 114status_t 115Attribute::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 144status_t 145Attribute::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 174status_t 175Attribute::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 192status_t 193Attribute::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 ((off_t)(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 224status_t 225Attribute::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 236status_t 237Attribute::_Truncate() 238{ 239 // TODO: Implement 240 return B_ERROR; 241} 242 243 244status_t 245Attribute::_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 288status_t 289Attribute::_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 300status_t 301Attribute::_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 314status_t 315Attribute::_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 343status_t 344Attribute::_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