1 /* 2 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de. 3 * This file may be used under the terms of the MIT License. 4 */ 5 6 7 //! Inode stream access functions 8 9 10 #include "Stream.h" 11 #include "Directory.h" 12 #include "File.h" 13 #include "Link.h" 14 15 #include <stdlib.h> 16 #include <unistd.h> 17 #include <string.h> 18 19 20 using namespace BFS; 21 using std::nothrow; 22 23 24 class CachedBlock { 25 public: 26 CachedBlock(Volume& volume); 27 CachedBlock(Volume& volume, block_run run); 28 ~CachedBlock(); 29 30 uint8* SetTo(block_run run); 31 uint8* SetTo(off_t offset); 32 33 void Unset(); 34 35 uint8* Block() const { return fBlock; } 36 off_t BlockNumber() const { return fBlockNumber; } 37 uint32 BlockSize() const { return fVolume.BlockSize(); } 38 uint32 BlockShift() const 39 { return fVolume.BlockShift(); } 40 41 private: 42 Volume& fVolume; 43 off_t fBlockNumber; 44 uint8* fBlock; 45 }; 46 47 48 CachedBlock::CachedBlock(Volume& volume) 49 : 50 fVolume(volume), 51 fBlockNumber(-1LL), 52 fBlock(NULL) 53 { 54 } 55 56 57 CachedBlock::CachedBlock(Volume &volume, block_run run) 58 : 59 fVolume(volume), 60 fBlockNumber(-1LL), 61 fBlock(NULL) 62 { 63 SetTo(run); 64 } 65 66 67 CachedBlock::~CachedBlock() 68 { 69 free(fBlock); 70 } 71 72 73 inline void 74 CachedBlock::Unset() 75 { 76 fBlockNumber = -1; 77 } 78 79 80 inline uint8* 81 CachedBlock::SetTo(off_t block) 82 { 83 if (block == fBlockNumber) 84 return fBlock; 85 if (fBlock == NULL) { 86 fBlock = (uint8*)malloc(BlockSize()); 87 if (fBlock == NULL) 88 return NULL; 89 } 90 91 fBlockNumber = block; 92 if (read_pos(fVolume.Device(), block << BlockShift(), fBlock, BlockSize()) 93 < (ssize_t)BlockSize()) 94 return NULL; 95 96 return fBlock; 97 } 98 99 100 inline uint8* 101 CachedBlock::SetTo(block_run run) 102 { 103 return SetTo(fVolume.ToBlock(run)); 104 } 105 106 107 // #pragma mark - 108 109 110 Stream::Stream(Volume& volume, block_run run) 111 : 112 fVolume(volume) 113 { 114 if (read_pos(volume.Device(), volume.ToOffset(run), this, sizeof(bfs_inode)) 115 != sizeof(bfs_inode)) 116 return; 117 } 118 119 120 Stream::Stream(Volume& volume, off_t id) 121 : 122 fVolume(volume) 123 { 124 if (read_pos(volume.Device(), volume.ToOffset(id), this, sizeof(bfs_inode)) 125 != sizeof(bfs_inode)) 126 return; 127 } 128 129 130 Stream::~Stream() 131 { 132 } 133 134 135 status_t 136 Stream::InitCheck() 137 { 138 return bfs_inode::InitCheck(&fVolume); 139 } 140 141 142 status_t 143 Stream::GetNextSmallData(const small_data** _smallData) const 144 { 145 // TODO: Stream derives from bfs_inode and we read only sizeof(bfs_inode) 146 // bytes from disk, i.e. the small data region is not in memory. 147 panic("Stream::GetNextSmallData(): small data region is not loaded!"); 148 149 const small_data* smallData = *_smallData; 150 151 // begin from the start? 152 if (smallData == NULL) 153 smallData = small_data_start; 154 else 155 smallData = smallData->Next(); 156 157 // is already last item? 158 if (smallData->IsLast(this)) 159 return B_ENTRY_NOT_FOUND; 160 161 *_smallData = smallData; 162 163 return B_OK; 164 } 165 166 167 status_t 168 Stream::GetName(char* name, size_t size) const 169 { 170 const small_data* smallData = NULL; 171 while (GetNextSmallData(&smallData) == B_OK) { 172 if (*smallData->Name() == FILE_NAME_NAME 173 && smallData->NameSize() == FILE_NAME_NAME_LENGTH) { 174 strlcpy(name, (const char*)smallData->Data(), size); 175 return B_OK; 176 } 177 } 178 return B_ERROR; 179 } 180 181 182 status_t 183 Stream::ReadLink(char* buffer, size_t bufferSize) 184 { 185 // link in the stream 186 187 if (Flags() & INODE_LONG_SYMLINK) 188 return ReadAt(0, (uint8*)buffer, &bufferSize); 189 190 // link in the inode 191 192 strlcpy(buffer, short_symlink, bufferSize); 193 return B_OK; 194 } 195 196 197 status_t 198 Stream::FindBlockRun(off_t pos, block_run& run, off_t& offset) 199 { 200 // find matching block run 201 202 if (data.MaxDirectRange() > 0 && pos >= data.MaxDirectRange()) { 203 if (data.MaxDoubleIndirectRange() > 0 204 && pos >= data.MaxIndirectRange()) { 205 // access to double indirect blocks 206 207 CachedBlock cached(fVolume); 208 209 int32 runsPerBlock; 210 int32 directSize; 211 int32 indirectSize; 212 get_double_indirect_sizes(data.double_indirect.Length(), 213 cached.BlockSize(), runsPerBlock, directSize, indirectSize); 214 215 off_t start = pos - data.MaxIndirectRange(); 216 int32 index = start / indirectSize; 217 218 block_run* indirect = (block_run*)cached.SetTo( 219 fVolume.ToBlock(data.double_indirect) + index / runsPerBlock); 220 if (indirect == NULL) 221 return B_ERROR; 222 223 //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); 224 //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); 225 226 int32 current = (start % indirectSize) / directSize; 227 228 indirect = (block_run*)cached.SetTo(fVolume.ToBlock(indirect[ 229 index % runsPerBlock]) + current / runsPerBlock); 230 if (indirect == NULL) 231 return B_ERROR; 232 233 run = indirect[current % runsPerBlock]; 234 offset = data.MaxIndirectRange() + (index * indirectSize) 235 + (current * directSize); 236 //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); 237 } else { 238 // access to indirect blocks 239 240 int32 runsPerBlock = fVolume.BlockSize() / sizeof(block_run); 241 off_t runBlockEnd = data.MaxDirectRange(); 242 243 CachedBlock cached(fVolume); 244 off_t block = fVolume.ToBlock(data.indirect); 245 246 for (int32 i = 0; i < data.indirect.Length(); i++) { 247 block_run* indirect = (block_run *)cached.SetTo(block + i); 248 if (indirect == NULL) 249 return B_IO_ERROR; 250 251 int32 current = -1; 252 while (++current < runsPerBlock) { 253 if (indirect[current].IsZero()) 254 break; 255 256 runBlockEnd 257 += (uint32)indirect[current].Length() << cached.BlockShift(); 258 if (runBlockEnd > pos) { 259 run = indirect[current]; 260 offset = runBlockEnd 261 - ((uint32)run.Length() << cached.BlockShift()); 262 //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); 263 //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); 264 return fVolume.ValidateBlockRun(run); 265 } 266 } 267 } 268 return B_ERROR; 269 } 270 } else { 271 // access from direct blocks 272 273 off_t runBlockEnd = 0LL; 274 int32 current = -1; 275 276 while (++current < NUM_DIRECT_BLOCKS) { 277 if (data.direct[current].IsZero()) 278 break; 279 280 runBlockEnd += (uint32)data.direct[current].Length() << fVolume.BlockShift(); 281 if (runBlockEnd > pos) { 282 run = data.direct[current]; 283 offset = runBlockEnd - ((uint32)run.Length() << fVolume.BlockShift()); 284 //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); 285 return fVolume.ValidateBlockRun(run); 286 } 287 } 288 //PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data.size,pos)); 289 return B_ENTRY_NOT_FOUND; 290 } 291 return fVolume.ValidateBlockRun(run); 292 } 293 294 295 status_t 296 Stream::ReadAt(off_t pos, uint8* buffer, size_t* _length) 297 { 298 // set/check boundaries for pos/length 299 300 if (pos < 0) 301 return B_BAD_VALUE; 302 if (pos >= data.Size()) { 303 *_length = 0; 304 return B_NO_ERROR; 305 } 306 307 size_t length = *_length; 308 309 if (pos + (off_t)length > data.Size()) 310 length = data.Size() - pos; 311 312 block_run run; 313 off_t offset; 314 if (FindBlockRun(pos, run, offset) < B_OK) { 315 *_length = 0; 316 return B_BAD_VALUE; 317 } 318 319 uint32 bytesRead = 0; 320 uint32 blockSize = fVolume.BlockSize(); 321 uint32 blockShift = fVolume.BlockShift(); 322 uint8* block; 323 324 // the first block_run we read could not be aligned to the block_size boundary 325 // (read partial block at the beginning) 326 327 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 328 if (pos % blockSize != 0) { 329 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() 330 + ((pos - offset) >> blockShift)); 331 run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() 332 - ((pos - offset) >> blockShift)); 333 334 CachedBlock cached(fVolume, run); 335 if ((block = cached.Block()) == NULL) { 336 *_length = 0; 337 return B_BAD_VALUE; 338 } 339 340 bytesRead = blockSize - (pos % blockSize); 341 if (length < bytesRead) 342 bytesRead = length; 343 344 memcpy(buffer, block + (pos % blockSize), bytesRead); 345 pos += bytesRead; 346 347 length -= bytesRead; 348 if (length == 0) { 349 *_length = bytesRead; 350 return B_OK; 351 } 352 353 if (FindBlockRun(pos, run, offset) < B_OK) { 354 *_length = bytesRead; 355 return B_BAD_VALUE; 356 } 357 } 358 359 // the first block_run is already filled in at this point 360 // read the following complete blocks using cached_read(), 361 // the last partial block is read using the generic Cache class 362 363 bool partial = false; 364 365 while (length > 0) { 366 // offset is the offset to the current pos in the block_run 367 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() 368 + ((pos - offset) >> blockShift)); 369 run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() 370 - ((pos - offset) >> blockShift)); 371 372 if (uint32(run.Length() << blockShift) > length) { 373 if (length < blockSize) { 374 CachedBlock cached(fVolume, run); 375 if ((block = cached.Block()) == NULL) { 376 *_length = bytesRead; 377 return B_BAD_VALUE; 378 } 379 memcpy(buffer + bytesRead, block, length); 380 bytesRead += length; 381 break; 382 } 383 run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift); 384 partial = true; 385 } 386 387 if (read_pos(fVolume.Device(), fVolume.ToOffset(run), buffer + bytesRead, 388 run.Length() << fVolume.BlockShift()) < B_OK) { 389 *_length = bytesRead; 390 return B_BAD_VALUE; 391 } 392 393 int32 bytes = run.Length() << blockShift; 394 length -= bytes; 395 bytesRead += bytes; 396 if (length == 0) 397 break; 398 399 pos += bytes; 400 401 if (partial) { 402 // if the last block was read only partially, point block_run 403 // to the remaining part 404 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length()); 405 run.length = 1; 406 offset = pos; 407 } else if (FindBlockRun(pos, run, offset) < B_OK) { 408 *_length = bytesRead; 409 return B_BAD_VALUE; 410 } 411 } 412 413 *_length = bytesRead; 414 return B_NO_ERROR; 415 } 416 417 418 Node* 419 Stream::NodeFactory(Volume& volume, off_t id) 420 { 421 Stream stream(volume, id); 422 if (stream.InitCheck() != B_OK) 423 return NULL; 424 425 if (stream.IsContainer()) 426 return new(nothrow) Directory(stream); 427 428 if (stream.IsSymlink()) 429 return new(nothrow) Link(stream); 430 431 return new(nothrow) File(stream); 432 } 433 434 435 // #pragma mark - 436 437 438 status_t 439 bfs_inode::InitCheck(Volume* volume) const 440 { 441 if ((Flags() & INODE_NOT_READY) != 0) { 442 // the other fields may not yet contain valid values 443 return B_BUSY; 444 } 445 446 if (Magic1() != INODE_MAGIC1 447 || !(Flags() & INODE_IN_USE) 448 || inode_num.Length() != 1 449 // matches inode size? 450 || (uint32)InodeSize() != volume->InodeSize() 451 // parent resides on disk? 452 || parent.AllocationGroup() > int32(volume->AllocationGroups()) 453 || parent.AllocationGroup() < 0 454 || parent.Start() > (1L << volume->AllocationGroupShift()) 455 || parent.Length() != 1 456 // attributes, too? 457 || attributes.AllocationGroup() > int32(volume->AllocationGroups()) 458 || attributes.AllocationGroup() < 0 459 || attributes.Start() > (1L << volume->AllocationGroupShift())) 460 return B_BAD_DATA; 461 462 // TODO: Add some tests to check the integrity of the other stuff here, 463 // especially for the data_stream! 464 465 return B_OK; 466 } 467