1 /* 2 * Copyright 2022, Raghav Sharma, raghavself28@gmail.com 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "NodeAttribute.h" 8 9 #include "VerifyHeader.h" 10 11 12 NodeAttribute::NodeAttribute(Inode* inode) 13 : 14 fInode(inode), 15 fName(NULL), 16 fLeafBuffer(NULL), 17 fNodeBuffer(NULL) 18 { 19 fLastEntryOffset = 0; 20 fLastNodeOffset = 0; 21 fNodeFlag = 0; 22 } 23 24 25 NodeAttribute::~NodeAttribute() 26 { 27 delete fMap; 28 delete[] fLeafBuffer; 29 delete[] fNodeBuffer; 30 } 31 32 33 status_t 34 NodeAttribute::Init() 35 { 36 status_t status; 37 38 fMap = new(std::nothrow) ExtentMapEntry; 39 if (fMap == NULL) 40 return B_NO_MEMORY; 41 42 status = _FillMapEntry(0); 43 44 if (status != B_OK) 45 return status; 46 47 // allocate memory for both Node and Leaf Buffer 48 int len = fInode->DirBlockSize(); 49 fLeafBuffer = new(std::nothrow) char[len]; 50 if (fLeafBuffer == NULL) 51 return B_NO_MEMORY; 52 fNodeBuffer = new(std::nothrow) char[len]; 53 if (fNodeBuffer == NULL) 54 return B_NO_MEMORY; 55 56 // fill up Node buffer just one time 57 status = _FillBuffer(fNodeBuffer, fMap->br_startblock); 58 if (status != B_OK) 59 return status; 60 61 NodeHeader* header = NodeHeader::Create(fInode,fNodeBuffer); 62 if (header == NULL) 63 return B_NO_MEMORY; 64 65 if (!VerifyHeader<NodeHeader>(header, fNodeBuffer, fInode, 0, fMap, ATTR_NODE)) { 66 ERROR("Invalid data header"); 67 delete header; 68 return B_BAD_VALUE; 69 } 70 delete header; 71 72 return B_OK; 73 } 74 75 76 status_t 77 NodeAttribute::_FillMapEntry(xfs_extnum_t num) 78 { 79 void* attributeFork = DIR_AFORK_PTR(fInode->Buffer(), 80 fInode->CoreInodeSize(), fInode->ForkOffset()); 81 82 uint64* pointerToMap = (uint64*)(((char*)attributeFork + num * EXTENT_SIZE)); 83 uint64 firstHalf = pointerToMap[0]; 84 uint64 secondHalf = pointerToMap[1]; 85 //dividing the 128 bits into 2 parts. 86 87 firstHalf = B_BENDIAN_TO_HOST_INT64(firstHalf); 88 secondHalf = B_BENDIAN_TO_HOST_INT64(secondHalf); 89 fMap->br_state = firstHalf >> 63; 90 fMap->br_startoff = (firstHalf & MASK(63)) >> 9; 91 fMap->br_startblock = ((firstHalf & MASK(9)) << 43) | (secondHalf >> 21); 92 fMap->br_blockcount = secondHalf & MASK(21); 93 94 return B_OK; 95 } 96 97 98 xfs_fsblock_t 99 NodeAttribute::_LogicalToFileSystemBlock(uint32 logicalBlock) 100 { 101 xfs_extnum_t totalExtents = fInode->AttrExtentsCount(); 102 103 // If logicalBlock is lesser than or equal to ExtentsCount then 104 // simply read and return extent block count. 105 // else read last Map and add difference of logical block offset 106 if (logicalBlock < (uint32)totalExtents) { 107 _FillMapEntry(logicalBlock); 108 return fMap->br_startblock; 109 } else { 110 _FillMapEntry(totalExtents - 1); 111 return fMap->br_startblock + logicalBlock - fMap->br_startoff; 112 } 113 } 114 115 116 status_t 117 NodeAttribute::_FillBuffer(char* buffer, xfs_fsblock_t block) 118 { 119 int len = fInode->DirBlockSize(); 120 121 xfs_daddr_t readPos = 122 fInode->FileSystemBlockToAddr(block); 123 124 if (read_pos(fInode->GetVolume()->Device(), readPos, buffer, len) != len) { 125 ERROR("Extent::FillBlockBuffer(): IO Error"); 126 return B_IO_ERROR; 127 } 128 129 return B_OK; 130 } 131 132 133 status_t 134 NodeAttribute::Open(const char* name, int openMode, attr_cookie** _cookie) 135 { 136 TRACE("NodeAttribute::Open\n"); 137 138 size_t length = strlen(name); 139 status_t status = Lookup(name, &length); 140 if (status < B_OK) 141 return status; 142 143 attr_cookie* cookie = new(std::nothrow) attr_cookie; 144 if (cookie == NULL) 145 return B_NO_MEMORY; 146 147 fName = name; 148 149 // initialize the cookie 150 strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH); 151 cookie->open_mode = openMode; 152 cookie->create = false; 153 154 *_cookie = cookie; 155 return B_OK; 156 } 157 158 159 status_t 160 NodeAttribute::Stat(attr_cookie* cookie, struct stat& stat) 161 { 162 TRACE("NodeAttribute::Stat\n"); 163 164 fName = cookie->name; 165 166 size_t namelength = strlen(fName); 167 168 status_t status; 169 170 // check if this attribute exists 171 status = Lookup(fName, &namelength); 172 173 if(status != B_OK) 174 return status; 175 176 // We have valid attribute entry to stat 177 if (fLocalEntry != NULL) { 178 uint16 valuelen = B_BENDIAN_TO_HOST_INT16(fLocalEntry->valuelen); 179 stat.st_type = B_XATTR_TYPE; 180 stat.st_size = valuelen + fLocalEntry->namelen; 181 } else { 182 uint32 valuelen = B_BENDIAN_TO_HOST_INT32(fRemoteEntry->valuelen); 183 stat.st_type = B_XATTR_TYPE; 184 stat.st_size = valuelen + fRemoteEntry->namelen; 185 } 186 187 return B_OK; 188 } 189 190 191 status_t 192 NodeAttribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* length) 193 { 194 TRACE("NodeAttribute::Read\n"); 195 196 if (pos < 0) 197 return B_BAD_VALUE; 198 199 fName = cookie->name; 200 201 size_t namelength = strlen(fName); 202 203 status_t status; 204 205 status = Lookup(fName, &namelength); 206 207 if (status != B_OK) 208 return status; 209 210 uint32 lengthToRead = 0; 211 212 if (fLocalEntry != NULL) { 213 uint16 valuelen = B_BENDIAN_TO_HOST_INT16(fLocalEntry->valuelen); 214 if (pos + *length > valuelen) 215 lengthToRead = valuelen - pos; 216 else 217 lengthToRead = *length; 218 219 char* ptrToOffset = (char*) fLocalEntry + sizeof(uint16) 220 + sizeof(uint8) + fLocalEntry->namelen; 221 222 memcpy(buffer, ptrToOffset, lengthToRead); 223 224 *length = lengthToRead; 225 226 return B_OK; 227 } else { 228 uint32 valuelen = B_BENDIAN_TO_HOST_INT32(fRemoteEntry->valuelen); 229 if (pos + *length > valuelen) 230 lengthToRead = valuelen - pos; 231 else 232 lengthToRead = *length; 233 234 // For remote value blocks, value is stored in seperate file system block 235 uint32 blkno = B_BENDIAN_TO_HOST_INT32(fRemoteEntry->valueblk); 236 237 xfs_daddr_t readPos = 238 fInode->FileSystemBlockToAddr(blkno); 239 240 if (fInode->Version() == 3) 241 pos += sizeof(AttrRemoteHeader); 242 243 readPos += pos; 244 245 // TODO : Implement remote header checks for V5 file system 246 if (read_pos(fInode->GetVolume()->Device(), readPos, buffer, lengthToRead) 247 != lengthToRead) { 248 ERROR("Extent::FillBlockBuffer(): IO Error"); 249 return B_IO_ERROR; 250 } 251 252 *length = lengthToRead; 253 254 return B_OK; 255 } 256 } 257 258 259 status_t 260 NodeAttribute::GetNext(char* name, size_t* nameLength) 261 { 262 TRACE("NodeAttribute::GetNext\n"); 263 264 NodeHeader* node = NodeHeader::Create(fInode, fNodeBuffer); 265 NodeEntry* firstNodeEntry = (NodeEntry*)(fNodeBuffer + NodeHeader::Size(fInode)); 266 267 int totalNodeEntries = node->Count(); 268 269 delete node; 270 271 for (int i = fLastNodeOffset; i < totalNodeEntries; i++) { 272 273 NodeEntry* nodeEntry = (NodeEntry*)((char*)firstNodeEntry + i * sizeof(NodeEntry)); 274 fLastNodeOffset = i; 275 276 // if we are at next node entry fill up leaf buffer 277 if (fNodeFlag == 0) { 278 // First see the leaf block from NodeEntry and logical block offset 279 uint32 logicalBlock = B_BENDIAN_TO_HOST_INT32(nodeEntry->before); 280 TRACE("Logical block : %d", logicalBlock); 281 // Now calculate File system Block of This logical block 282 xfs_fsblock_t block = _LogicalToFileSystemBlock(logicalBlock); 283 _FillBuffer(fLeafBuffer, block); 284 fNodeFlag = 1; 285 fLastEntryOffset = 0; 286 } 287 288 AttrLeafHeader* header = AttrLeafHeader::Create(fInode, fLeafBuffer); 289 AttrLeafEntry* firstLeafEntry = 290 (AttrLeafEntry*)(fLeafBuffer + AttrLeafHeader::Size(fInode)); 291 292 int totalEntries = header->Count(); 293 294 delete header; 295 296 for (int j = fLastEntryOffset; j < totalEntries; j++) { 297 298 AttrLeafEntry* entry = (AttrLeafEntry*)( 299 (char*)firstLeafEntry + j * sizeof(AttrLeafEntry)); 300 301 uint32 offset = B_BENDIAN_TO_HOST_INT16(entry->nameidx); 302 TRACE("offset:(%" B_PRIu16 ")\n", offset); 303 fLastEntryOffset = j + 1; 304 305 // First check if its local or remote value 306 if (entry->flags & XFS_ATTR_LOCAL) { 307 AttrLeafNameLocal* local = (AttrLeafNameLocal*)(fLeafBuffer + offset); 308 memcpy(name, local->nameval, local->namelen); 309 name[local->namelen] = '\0'; 310 *nameLength = local->namelen + 1; 311 TRACE("Entry found name : %s, namelength : %ld", name, *nameLength); 312 return B_OK; 313 } else { 314 AttrLeafNameRemote* remote = (AttrLeafNameRemote*)(fLeafBuffer + offset); 315 memcpy(name, remote->name, remote->namelen); 316 name[remote->namelen] = '\0'; 317 *nameLength = remote->namelen + 1; 318 TRACE("Entry found name : %s, namelength : %ld", name, *nameLength); 319 return B_OK; 320 } 321 } 322 323 // we are now at next nodeEntry initialize it as 0 324 fNodeFlag = 0; 325 } 326 327 return B_ENTRY_NOT_FOUND; 328 } 329 330 331 status_t 332 NodeAttribute::Lookup(const char* name, size_t* nameLength) 333 { 334 TRACE("NodeAttribute::Lookup\n"); 335 uint32 hashValueOfRequest = hashfunction(name, *nameLength); 336 TRACE("Hashval:(%" B_PRIu32 ")\n", hashValueOfRequest); 337 338 // first we need to find leaf block which might contain our entry 339 NodeHeader* node = NodeHeader::Create(fInode, fNodeBuffer); 340 NodeEntry* nodeEntry = (NodeEntry*)(fNodeBuffer + NodeHeader::Size(fInode)); 341 342 int TotalNodeEntries = node->Count(); 343 int left = 0; 344 int right = TotalNodeEntries - 1; 345 346 delete node; 347 348 hashLowerBound<NodeEntry>(nodeEntry, left, right, hashValueOfRequest); 349 350 // We found our potential leaf block, now read leaf buffer 351 // First see the leaf block from NodeEntry and logical block offset 352 uint32 logicalBlock = B_BENDIAN_TO_HOST_INT32(nodeEntry[left].before); 353 // Now calculate File system Block of This logical block 354 xfs_fsblock_t block = _LogicalToFileSystemBlock(logicalBlock); 355 _FillBuffer(fLeafBuffer, block); 356 357 AttrLeafHeader* header = AttrLeafHeader::Create(fInode,fLeafBuffer); 358 AttrLeafEntry* entry = (AttrLeafEntry*)(fLeafBuffer + AttrLeafHeader::Size(fInode)); 359 360 int numberOfLeafEntries = header->Count(); 361 left = 0; 362 right = numberOfLeafEntries - 1; 363 364 delete header; 365 366 hashLowerBound<AttrLeafEntry>(entry, left, right, hashValueOfRequest); 367 368 while (B_BENDIAN_TO_HOST_INT32(entry[left].hashval) 369 == hashValueOfRequest) { 370 371 uint32 offset = B_BENDIAN_TO_HOST_INT16(entry[left].nameidx); 372 TRACE("offset:(%" B_PRIu16 ")\n", offset); 373 int status; 374 375 // First check if its local or remote value 376 if (entry[left].flags & XFS_ATTR_LOCAL) { 377 AttrLeafNameLocal* local = (AttrLeafNameLocal*)(fLeafBuffer + offset); 378 char* PtrToOffset = (char*)local + sizeof(uint8) + sizeof(uint16); 379 status = strncmp(name, PtrToOffset, *nameLength); 380 if (status == 0) { 381 fLocalEntry = local; 382 fRemoteEntry = NULL; 383 return B_OK; 384 } 385 } else { 386 AttrLeafNameRemote* remote = (AttrLeafNameRemote*)(fLeafBuffer + offset); 387 char* PtrToOffset = (char*)remote + sizeof(uint8) + 2 * sizeof(uint32); 388 status = strncmp(name, PtrToOffset, *nameLength); 389 if (status == 0) { 390 fRemoteEntry = remote; 391 fLocalEntry = NULL; 392 return B_OK; 393 } 394 } 395 left++; 396 } 397 398 return B_ENTRY_NOT_FOUND; 399 }