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