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