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