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 const small_data *smallData = *_smallData; 141 142 // begin from the start? 143 if (smallData == NULL) 144 smallData = small_data_start; 145 else 146 smallData = smallData->Next(); 147 148 // is already last item? 149 if (smallData->IsLast(this)) 150 return B_ENTRY_NOT_FOUND; 151 152 *_smallData = smallData; 153 154 return B_OK; 155 } 156 157 158 status_t 159 Stream::GetName(char *name, size_t size) const 160 { 161 const small_data *smallData = NULL; 162 while (GetNextSmallData(&smallData) == B_OK) { 163 if (*smallData->Name() == FILE_NAME_NAME 164 && smallData->NameSize() == FILE_NAME_NAME_LENGTH) { 165 strlcpy(name, (const char *)smallData->Data(), size); 166 return B_OK; 167 } 168 } 169 return B_ERROR; 170 } 171 172 173 status_t 174 Stream::ReadLink(char *buffer, size_t bufferSize) 175 { 176 // link in the stream 177 178 if (Flags() & INODE_LONG_SYMLINK) 179 return ReadAt(0, (uint8 *)buffer, &bufferSize); 180 181 // link in the inode 182 183 strlcpy(buffer, short_symlink, bufferSize); 184 return B_OK; 185 } 186 187 188 status_t 189 Stream::FindBlockRun(off_t pos, block_run &run, off_t &offset) 190 { 191 // find matching block run 192 193 if (data.MaxDirectRange() > 0 && pos >= data.MaxDirectRange()) { 194 if (data.MaxDoubleIndirectRange() > 0 && pos >= data.MaxIndirectRange()) { 195 // access to double indirect blocks 196 197 CachedBlock cached(fVolume); 198 199 off_t start = pos - data.MaxIndirectRange(); 200 int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + cached.BlockShift())) 201 * (fVolume.BlockSize() / sizeof(block_run)); 202 int32 directSize = NUM_ARRAY_BLOCKS << cached.BlockShift(); 203 int32 index = start / indirectSize; 204 int32 runsPerBlock = cached.BlockSize() / sizeof(block_run); 205 206 block_run *indirect = (block_run *)cached.SetTo( 207 fVolume.ToBlock(data.double_indirect) + index / runsPerBlock); 208 if (indirect == NULL) 209 return B_ERROR; 210 211 //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); 212 //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); 213 214 int32 current = (start % indirectSize) / directSize; 215 216 indirect = (block_run *)cached.SetTo( 217 fVolume.ToBlock(indirect[index % runsPerBlock]) + current / runsPerBlock); 218 if (indirect == NULL) 219 return B_ERROR; 220 221 run = indirect[current % runsPerBlock]; 222 offset = data.MaxIndirectRange() + (index * indirectSize) + (current * directSize); 223 //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); 224 } else { 225 // access to indirect blocks 226 227 int32 runsPerBlock = fVolume.BlockSize() / sizeof(block_run); 228 off_t runBlockEnd = data.MaxDirectRange(); 229 230 CachedBlock cached(fVolume); 231 off_t block = fVolume.ToBlock(data.indirect); 232 233 for (int32 i = 0;i < data.indirect.Length();i++) { 234 block_run *indirect = (block_run *)cached.SetTo(block + i); 235 if (indirect == NULL) 236 return B_IO_ERROR; 237 238 int32 current = -1; 239 while (++current < runsPerBlock) { 240 if (indirect[current].IsZero()) 241 break; 242 243 runBlockEnd += indirect[current].Length() << cached.BlockShift(); 244 if (runBlockEnd > pos) { 245 run = indirect[current]; 246 offset = runBlockEnd - (run.Length() << cached.BlockShift()); 247 //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); 248 //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); 249 return fVolume.ValidateBlockRun(run); 250 } 251 } 252 } 253 return B_ERROR; 254 } 255 } else { 256 // access from direct blocks 257 258 off_t runBlockEnd = 0LL; 259 int32 current = -1; 260 261 while (++current < NUM_DIRECT_BLOCKS) { 262 if (data.direct[current].IsZero()) 263 break; 264 265 runBlockEnd += data.direct[current].Length() << fVolume.BlockShift(); 266 if (runBlockEnd > pos) { 267 run = data.direct[current]; 268 offset = runBlockEnd - (run.Length() << fVolume.BlockShift()); 269 //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); 270 return fVolume.ValidateBlockRun(run); 271 } 272 } 273 //PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data.size,pos)); 274 return B_ENTRY_NOT_FOUND; 275 } 276 return fVolume.ValidateBlockRun(run); 277 } 278 279 280 status_t 281 Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length) 282 { 283 // set/check boundaries for pos/length 284 285 if (pos < 0) 286 return B_BAD_VALUE; 287 if (pos >= data.Size()) { 288 *_length = 0; 289 return B_NO_ERROR; 290 } 291 292 size_t length = *_length; 293 294 if (pos + length > data.Size()) 295 length = data.Size() - pos; 296 297 block_run run; 298 off_t offset; 299 if (FindBlockRun(pos, run, offset) < B_OK) { 300 *_length = 0; 301 return B_BAD_VALUE; 302 } 303 304 uint32 bytesRead = 0; 305 uint32 blockSize = fVolume.BlockSize(); 306 uint32 blockShift = fVolume.BlockShift(); 307 uint8 *block; 308 309 // the first block_run we read could not be aligned to the block_size boundary 310 // (read partial block at the beginning) 311 312 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 313 if (pos % blockSize != 0) { 314 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); 315 run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); 316 317 CachedBlock cached(fVolume, run); 318 if ((block = cached.Block()) == NULL) { 319 *_length = 0; 320 return B_BAD_VALUE; 321 } 322 323 bytesRead = blockSize - (pos % blockSize); 324 if (length < bytesRead) 325 bytesRead = length; 326 327 memcpy(buffer, block + (pos % blockSize), bytesRead); 328 pos += bytesRead; 329 330 length -= bytesRead; 331 if (length == 0) { 332 *_length = bytesRead; 333 return B_OK; 334 } 335 336 if (FindBlockRun(pos, run, offset) < B_OK) { 337 *_length = bytesRead; 338 return B_BAD_VALUE; 339 } 340 } 341 342 // the first block_run is already filled in at this point 343 // read the following complete blocks using cached_read(), 344 // the last partial block is read using the generic Cache class 345 346 bool partial = false; 347 348 while (length > 0) { 349 // offset is the offset to the current pos in the block_run 350 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); 351 run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); 352 353 if (uint32(run.Length() << blockShift) > length) { 354 if (length < blockSize) { 355 CachedBlock cached(fVolume, run); 356 if ((block = cached.Block()) == NULL) { 357 *_length = bytesRead; 358 return B_BAD_VALUE; 359 } 360 memcpy(buffer + bytesRead, block, length); 361 bytesRead += length; 362 break; 363 } 364 run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift); 365 partial = true; 366 } 367 368 if (read_pos(fVolume.Device(), fVolume.ToOffset(run), buffer + bytesRead, run.Length() << fVolume.BlockShift()) < B_OK) { 369 *_length = bytesRead; 370 return B_BAD_VALUE; 371 } 372 373 int32 bytes = run.Length() << blockShift; 374 length -= bytes; 375 bytesRead += bytes; 376 if (length == 0) 377 break; 378 379 pos += bytes; 380 381 if (partial) { 382 // if the last block was read only partially, point block_run 383 // to the remaining part 384 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length()); 385 run.length = 1; 386 offset = pos; 387 } else if (FindBlockRun(pos, run, offset) < B_OK) { 388 *_length = bytesRead; 389 return B_BAD_VALUE; 390 } 391 } 392 393 *_length = bytesRead; 394 return B_NO_ERROR; 395 } 396 397 398 Node * 399 Stream::NodeFactory(Volume &volume, off_t id) 400 { 401 Stream stream(volume, id); 402 if (stream.InitCheck() != B_OK) 403 return NULL; 404 405 if (stream.IsContainer()) 406 return new(nothrow) Directory(stream); 407 408 if (stream.IsSymlink()) 409 return new(nothrow) Link(stream); 410 411 return new(nothrow) File(stream); 412 } 413 414 415 // #pragma mark - 416 417 418 status_t 419 bfs_inode::InitCheck(Volume *volume) 420 { 421 if (Flags() & INODE_NOT_READY) { 422 // the other fields may not yet contain valid values 423 return B_BUSY; 424 } 425 426 if (Magic1() != INODE_MAGIC1 427 || !(Flags() & INODE_IN_USE) 428 || inode_num.Length() != 1 429 // matches inode size? 430 || (uint32)InodeSize() != volume->InodeSize() 431 // parent resides on disk? 432 || parent.AllocationGroup() > int32(volume->AllocationGroups()) 433 || parent.AllocationGroup() < 0 434 || parent.Start() > (1L << volume->AllocationGroupShift()) 435 || parent.Length() != 1 436 // attributes, too? 437 || attributes.AllocationGroup() > int32(volume->AllocationGroups()) 438 || attributes.AllocationGroup() < 0 439 || attributes.Start() > (1L << volume->AllocationGroupShift())) 440 return B_BAD_DATA; 441 442 // ToDo: Add some tests to check the integrity of the other stuff here, 443 // especially for the data_stream! 444 445 return B_OK; 446 } 447 448