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