1/* Stream - inode stream access functions 2** 3** Initial version by Axel D��rfler, axeld@pinc-software.de 4** This file may be used under the terms of the OpenBeOS License. 5*/ 6 7 8#include "Inode.h" 9 10 11// The classes in the namespace "Access" provide different type of access 12// to the inode's data stream. 13// Uncached accesses the underlaying device directly, Cached uses the 14// standard cache, while Logged directs write accesses through the log. 15// 16// The classes interface is similar to the one of the CachedBlock class, 17// but adds two other (static) functions for reading/writing several 18// blocks at once. 19// We don't use a real pure virtual interface as the class base, but we 20// provide the same mechanism using templates. 21 22namespace Access { 23 24class Uncached { 25 public: 26 Uncached(Volume *volume); 27 Uncached(Volume *volume, off_t block, bool empty = false); 28 Uncached(Volume *volume, block_run run, bool empty = false); 29 ~Uncached(); 30 31 void Unset(); 32 uint8 *SetTo(off_t block, bool empty = false); 33 uint8 *SetTo(block_run run, bool empty = false); 34 status_t WriteBack(Transaction *transaction); 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 { return fVolume->BlockShift(); } 40 41 static status_t Read(Volume *volume, block_run run, uint8 *buffer); 42 static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); 43 44 private: 45 Volume *fVolume; 46 off_t fBlockNumber; 47 uint8 *fBlock; 48}; 49 50class Cached : public CachedBlock { 51 public: 52 Cached(Volume *volume); 53 Cached(Volume *volume, off_t block, bool empty = false); 54 Cached(Volume *volume, block_run run, bool empty = false); 55 56 status_t WriteBack(Transaction *transaction); 57 static status_t Read(Volume *volume, block_run run, uint8 *buffer); 58 static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); 59}; 60 61class Logged : public CachedBlock { 62 public: 63 Logged(Volume *volume); 64 Logged(Volume *volume,off_t block, bool empty = false); 65 Logged(Volume *volume, block_run run, bool empty = false); 66 67 static status_t Read(Volume *volume, block_run run, uint8 *buffer); 68 static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); 69}; 70 71 72Uncached::Uncached(Volume *volume) 73 : 74 fVolume(volume), 75 fBlock(NULL) 76{ 77} 78 79 80Uncached::Uncached(Volume *volume,off_t block, bool empty) 81 : 82 fVolume(volume), 83 fBlock(NULL) 84{ 85 SetTo(block,empty); 86} 87 88 89Uncached::Uncached(Volume *volume,block_run run,bool empty) 90 : 91 fVolume(volume), 92 fBlock(NULL) 93{ 94 SetTo(volume->ToBlock(run),empty); 95} 96 97 98Uncached::~Uncached() 99{ 100 Unset(); 101} 102 103 104void 105Uncached::Unset() 106{ 107 if (fBlock != NULL) 108 fVolume->Pool().PutBuffer((void *)fBlock); 109} 110 111 112uint8 * 113Uncached::SetTo(off_t block, bool empty) 114{ 115 Unset(); 116 fBlockNumber = block; 117 if (fVolume->Pool().GetBuffer((void **)&fBlock) < B_OK) 118 return NULL; 119 120 if (empty) 121 memset(fBlock, 0, BlockSize()); 122 else 123 read_pos(fVolume->Device(), fBlockNumber << BlockShift(), fBlock, BlockSize()); 124 125 return fBlock; 126} 127 128 129uint8 * 130Uncached::SetTo(block_run run, bool empty) 131{ 132 return SetTo(fVolume->ToBlock(run), empty); 133} 134 135 136status_t 137Uncached::WriteBack(Transaction *transaction) 138{ 139 if (fBlock == NULL) 140 RETURN_ERROR(B_BAD_VALUE); 141 142 return write_pos(fVolume->Device(), fBlockNumber << BlockShift(), fBlock, BlockSize()); 143} 144 145 146status_t 147Uncached::Read(Volume *volume, block_run run, uint8 *buffer) 148{ 149 return read_pos(volume->Device(), volume->ToBlock(run) << volume->BlockShift(), buffer, run.Length() << volume->BlockShift()); 150} 151 152 153status_t 154Uncached::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) 155{ 156 return write_pos(volume->Device(), volume->ToBlock(run) << volume->BlockShift(), buffer, run.Length() << volume->BlockShift()); 157} 158 159 160// #pragma mark - 161 162 163Cached::Cached(Volume *volume) 164 : CachedBlock(volume) 165{ 166} 167 168 169Cached::Cached(Volume *volume,off_t block,bool empty) 170 : CachedBlock(volume, block, empty) 171{ 172} 173 174 175Cached::Cached(Volume *volume,block_run run,bool empty) 176 : CachedBlock(volume, run, empty) 177{ 178} 179 180 181status_t 182Cached::WriteBack(Transaction *transaction) 183{ 184 if (transaction == NULL || fBlock == NULL) 185 RETURN_ERROR(B_BAD_VALUE); 186 187 return fVolume->WriteBlocks(fBlockNumber, fBlock, 1); 188} 189 190 191status_t 192Cached::Read(Volume *volume, block_run run, uint8 *buffer) 193{ 194 return cached_read(volume->Device(), volume->ToBlock(run), buffer, run.Length(), volume->BlockSize()); 195} 196 197 198status_t 199Cached::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) 200{ 201 return volume->WriteBlocks(volume->ToBlock(run), buffer, run.Length()); 202} 203 204 205// #pragma mark - 206 207 208Logged::Logged(Volume *volume) 209 : CachedBlock(volume) 210{ 211} 212 213 214Logged::Logged(Volume *volume, off_t block, bool empty) 215 : CachedBlock(volume, block, empty) 216{ 217} 218 219 220Logged::Logged(Volume *volume, block_run run, bool empty) 221 : CachedBlock(volume, run, empty) 222{ 223} 224 225 226status_t 227Logged::Read(Volume *volume, block_run run, uint8 *buffer) 228{ 229 return cached_read(volume->Device(), volume->ToBlock(run), buffer, run.Length(), volume->BlockSize()); 230} 231 232 233status_t 234Logged::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) 235{ 236 return transaction->WriteBlocks(volume->ToBlock(run), buffer, run.Length()); 237} 238 239}; // namespace Access 240 241 242// #pragma mark - 243 244 245// The Stream template class allows to have only one straight-forward 246// implementation of the FindBlockRun(), ReadAt(), and WriteAt() methods. 247// They will access the disk through the given cache class only, which 248// means either uncached, cached, or logged (see above). 249 250template<class Cache> 251class Stream : public Inode { 252 private: 253 // The constructor only exists to make the compiler happy - it 254 // is never called in the code itself 255 Stream() : Inode(NULL, -1) {} 256 257 public: 258 status_t FindBlockRun(off_t pos, block_run &run, off_t &offset); 259 status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); 260 status_t WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *length); 261}; 262 263 264/** see Inode::FindBlockRun() for the documentation of this method */ 265 266template<class Cache> 267status_t 268Stream<Cache>::FindBlockRun(off_t pos, block_run &run, off_t &offset) 269{ 270 data_stream *data = &Node()->data; 271 272 // find matching block run 273 274 if (data->MaxDirectRange() > 0 && pos >= data->MaxDirectRange()) { 275 if (data->MaxDoubleIndirectRange() > 0 && pos >= data->MaxIndirectRange()) { 276 // access to double indirect blocks 277 278 Cache cached(fVolume); 279 280 off_t start = pos - data->MaxIndirectRange(); 281 int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + cached.BlockShift())) 282 * (fVolume->BlockSize() / sizeof(block_run)); 283 int32 directSize = NUM_ARRAY_BLOCKS << cached.BlockShift(); 284 int32 index = start / indirectSize; 285 int32 runsPerBlock = cached.BlockSize() / sizeof(block_run); 286 287 block_run *indirect = (block_run *)cached.SetTo( 288 fVolume->ToBlock(data->double_indirect) + index / runsPerBlock); 289 if (indirect == NULL) 290 RETURN_ERROR(B_ERROR); 291 292 //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); 293 //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); 294 295 int32 current = (start % indirectSize) / directSize; 296 297 indirect = (block_run *)cached.SetTo( 298 fVolume->ToBlock(indirect[index % runsPerBlock]) + current / runsPerBlock); 299 if (indirect == NULL) 300 RETURN_ERROR(B_ERROR); 301 302 run = indirect[current % runsPerBlock]; 303 offset = data->MaxIndirectRange() + (index * indirectSize) + (current * directSize); 304 //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); 305 } else { 306 // access to indirect blocks 307 308 int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); 309 off_t runBlockEnd = data->MaxDirectRange(); 310 311 Cache cached(fVolume); 312 off_t block = fVolume->ToBlock(data->indirect); 313 314 for (int32 i = 0; i < data->indirect.Length(); i++) { 315 block_run *indirect = (block_run *)cached.SetTo(block + i); 316 if (indirect == NULL) 317 RETURN_ERROR(B_IO_ERROR); 318 319 int32 current = -1; 320 while (++current < runsPerBlock) { 321 if (indirect[current].IsZero()) 322 break; 323 324 runBlockEnd += indirect[current].Length() << cached.BlockShift(); 325 if (runBlockEnd > pos) { 326 run = indirect[current]; 327 offset = runBlockEnd - (run.Length() << cached.BlockShift()); 328 //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); 329 //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset); 330 return fVolume->ValidateBlockRun(run); 331 } 332 } 333 } 334 RETURN_ERROR(B_ERROR); 335 } 336 } else { 337 // access from direct blocks 338 339 off_t runBlockEnd = 0LL; 340 int32 current = -1; 341 342 while (++current < NUM_DIRECT_BLOCKS) { 343 if (data->direct[current].IsZero()) 344 break; 345 346 runBlockEnd += data->direct[current].Length() << fVolume->BlockShift(); 347 if (runBlockEnd > pos) { 348 run = data->direct[current]; 349 offset = runBlockEnd - (run.Length() << fVolume->BlockShift()); 350 //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset); 351 return fVolume->ValidateBlockRun(run); 352 } 353 } 354 //PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data->size,pos)); 355 return B_ENTRY_NOT_FOUND; 356 } 357 return fVolume->ValidateBlockRun(run); 358} 359 360 361template<class Cache> 362status_t 363Stream<Cache>::ReadAt(off_t pos, uint8 *buffer, size_t *_length) 364{ 365 size_t length = *_length; 366 367 // set/check boundaries for pos/length 368 if (pos < 0) 369 return B_BAD_VALUE; 370 if (pos >= Node()->data.Size() || length == 0) { 371 *_length = 0; 372 return B_NO_ERROR; 373 } 374 375 if (pos + length > Node()->data.Size()) 376 length = Node()->data.Size() - pos; 377 378 block_run run; 379 off_t offset; 380 if (FindBlockRun(pos, run, offset) < B_OK) { 381 *_length = 0; 382 RETURN_ERROR(B_BAD_VALUE); 383 } 384 385 uint32 bytesRead = 0; 386 uint32 blockSize = fVolume->BlockSize(); 387 uint32 blockShift = fVolume->BlockShift(); 388 uint8 *block; 389 390 // the first block_run we read could not be aligned to the block_size boundary 391 // (read partial block at the beginning) 392 393 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 394 if (pos % blockSize != 0) { 395 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); 396 run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); 397 398 Cache cached(fVolume,run); 399 if ((block = cached.Block()) == NULL) { 400 *_length = 0; 401 RETURN_ERROR(B_BAD_VALUE); 402 } 403 404 bytesRead = blockSize - (pos % blockSize); 405 if (length < bytesRead) 406 bytesRead = length; 407 408 memcpy(buffer, block + (pos % blockSize), bytesRead); 409 pos += bytesRead; 410 411 length -= bytesRead; 412 if (length == 0) { 413 *_length = bytesRead; 414 return B_OK; 415 } 416 417 if (FindBlockRun(pos, run, offset) < B_OK) { 418 *_length = bytesRead; 419 RETURN_ERROR(B_BAD_VALUE); 420 } 421 } 422 423 // the first block_run is already filled in at this point 424 // read the following complete blocks using cached_read(), 425 // the last partial block is read using the generic Cache class 426 427 bool partial = false; 428 429 while (length > 0) { 430 // offset is the offset to the current pos in the block_run 431 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); 432 run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); 433 434 if (uint32(run.Length() << blockShift) > length) { 435 if (length < blockSize) { 436 Cache cached(fVolume, run); 437 if ((block = cached.Block()) == NULL) { 438 *_length = bytesRead; 439 RETURN_ERROR(B_BAD_VALUE); 440 } 441 memcpy(buffer + bytesRead, block, length); 442 bytesRead += length; 443 break; 444 } 445 run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift); 446 partial = true; 447 } 448 449 if (Cache::Read(fVolume, run, buffer + bytesRead) < B_OK) { 450 *_length = bytesRead; 451 RETURN_ERROR(B_BAD_VALUE); 452 } 453 454 int32 bytes = run.Length() << blockShift; 455#ifdef DEBUG 456 if ((uint32)bytes > length) 457 DEBUGGER(("bytes greater than length")); 458#endif 459 length -= bytes; 460 bytesRead += bytes; 461 if (length == 0) 462 break; 463 464 pos += bytes; 465 466 if (partial) { 467 // if the last block was read only partially, point block_run 468 // to the remaining part 469 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length()); 470 run.length = HOST_ENDIAN_TO_BFS_INT16(1); 471 offset = pos; 472 } else if (FindBlockRun(pos, run, offset) < B_OK) { 473 *_length = bytesRead; 474 RETURN_ERROR(B_BAD_VALUE); 475 } 476 } 477 478 *_length = bytesRead; 479 return B_OK; 480} 481 482 483template<class Cache> 484status_t 485Stream<Cache>::WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *_length) 486{ 487 size_t length = *_length; 488 489 // set/check boundaries for pos/length 490 if (pos < 0) 491 return B_BAD_VALUE; 492 493 if (pos + length > Size()) { 494 off_t oldSize = Size(); 495 496 // uncached files can't be resized (Inode::SetFileSize() also 497 // doesn't allow this, but this way we don't have to start a 498 // transaction to find out). 499 if (Flags() & INODE_NO_CACHE) 500 return B_BAD_VALUE; 501 502 // the transaction doesn't have to be started already 503 // ToDo: what's that INODE_NO_TRANSACTION flag good for again? 504 if ((Flags() & INODE_NO_TRANSACTION) == 0 505 && !transaction->IsStarted()) 506 transaction->Start(fVolume, BlockNumber()); 507 508 // let's grow the data stream to the size needed 509 status_t status = SetFileSize(transaction, pos + length); 510 if (status < B_OK) { 511 *_length = 0; 512 RETURN_ERROR(status); 513 } 514 // If the position of the write was beyond the file size, we 515 // have to fill the gap between that position and the old file 516 // size with zeros. 517 FillGapWithZeros(oldSize, pos); 518 } 519 520 // If we don't want to write anything, we can now return (we may 521 // just have changed the file size using the position parameter) 522 if (length == 0) 523 return B_OK; 524 525 block_run run; 526 off_t offset; 527 if (FindBlockRun(pos, run, offset) < B_OK) { 528 *_length = 0; 529 RETURN_ERROR(B_BAD_VALUE); 530 } 531 532 bool logStream = (Flags() & INODE_LOGGED) == INODE_LOGGED; 533 if (logStream 534 && !transaction->IsStarted()) 535 transaction->Start(fVolume, BlockNumber()); 536 537 uint32 bytesWritten = 0; 538 uint32 blockSize = fVolume->BlockSize(); 539 uint32 blockShift = fVolume->BlockShift(); 540 uint8 *block; 541 542 // the first block_run we write could not be aligned to the block_size boundary 543 // (write partial block at the beginning) 544 545 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 546 if (pos % blockSize != 0) { 547 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); 548 run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); 549 550 Cache cached(fVolume, run); 551 if ((block = cached.Block()) == NULL) { 552 *_length = 0; 553 RETURN_ERROR(B_BAD_VALUE); 554 } 555 556 bytesWritten = blockSize - (pos % blockSize); 557 if (length < bytesWritten) 558 bytesWritten = length; 559 560 memcpy(block + (pos % blockSize),buffer,bytesWritten); 561 562 cached.WriteBack(transaction); 563 564 pos += bytesWritten; 565 566 length -= bytesWritten; 567 if (length == 0) { 568 *_length = bytesWritten; 569 return B_OK; 570 } 571 572 if (FindBlockRun(pos, run, offset) < B_OK) { 573 *_length = bytesWritten; 574 RETURN_ERROR(B_BAD_VALUE); 575 } 576 } 577 578 // the first block_run is already filled in at this point 579 // write the following complete blocks using Volume::WriteBlocks(), 580 // the last partial block is written using the generic Cache class 581 582 bool partial = false; 583 584 while (length > 0) { 585 // offset is the offset to the current pos in the block_run 586 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); 587 run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); 588 589 if (uint32(run.Length() << blockShift) > length) { 590 if (length < blockSize) { 591 Cache cached(fVolume,run); 592 if ((block = cached.Block()) == NULL) { 593 *_length = bytesWritten; 594 RETURN_ERROR(B_BAD_VALUE); 595 } 596 memcpy(block, buffer + bytesWritten, length); 597 598 cached.WriteBack(transaction); 599 600 bytesWritten += length; 601 break; 602 } 603 run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift); 604 partial = true; 605 } 606 607 if (Cache::Write(transaction, fVolume, run, buffer + bytesWritten) < B_OK) { 608 *_length = bytesWritten; 609 RETURN_ERROR(B_BAD_VALUE); 610 } 611 612 int32 bytes = run.Length() << blockShift; 613 length -= bytes; 614 bytesWritten += bytes; 615 if (length == 0) 616 break; 617 618 pos += bytes; 619 620 if (partial) { 621 // if the last block was written only partially, point block_run 622 // to the remaining part 623 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length()); 624 run.length = HOST_ENDIAN_TO_BFS_INT16(1); 625 offset = pos; 626 } else if (FindBlockRun(pos, run, offset) < B_OK) { 627 *_length = bytesWritten; 628 RETURN_ERROR(B_BAD_VALUE); 629 } 630 } 631 632 *_length = bytesWritten; 633 634 return B_OK; 635} 636 637