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