headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / boot / loader / file_systems / bfs / Stream.cpp
blobd134e6f03842a432c0ad5ec7a71a8b03b11a8501
1 /*
2 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
3 * This file may be used under the terms of the MIT License.
4 */
7 //! Inode stream access functions
10 #include "Stream.h"
11 #include "Directory.h"
12 #include "File.h"
13 #include "Link.h"
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <string.h>
20 using namespace BFS;
21 using std::nothrow;
24 class CachedBlock {
25 public:
26 CachedBlock(Volume& volume);
27 CachedBlock(Volume& volume, block_run run);
28 ~CachedBlock();
30 uint8* SetTo(block_run run);
31 uint8* SetTo(off_t offset);
33 void Unset();
35 uint8* Block() const { return fBlock; }
36 off_t BlockNumber() const { return fBlockNumber; }
37 uint32 BlockSize() const { return fVolume.BlockSize(); }
38 uint32 BlockShift() const
39 { return fVolume.BlockShift(); }
41 private:
42 Volume& fVolume;
43 off_t fBlockNumber;
44 uint8* fBlock;
48 CachedBlock::CachedBlock(Volume& volume)
50 fVolume(volume),
51 fBlockNumber(-1LL),
52 fBlock(NULL)
57 CachedBlock::CachedBlock(Volume &volume, block_run run)
59 fVolume(volume),
60 fBlockNumber(-1LL),
61 fBlock(NULL)
63 SetTo(run);
67 CachedBlock::~CachedBlock()
69 free(fBlock);
73 inline void
74 CachedBlock::Unset()
76 fBlockNumber = -1;
80 inline uint8*
81 CachedBlock::SetTo(off_t block)
83 if (block == fBlockNumber)
84 return fBlock;
85 if (fBlock == NULL) {
86 fBlock = (uint8*)malloc(BlockSize());
87 if (fBlock == NULL)
88 return NULL;
91 fBlockNumber = block;
92 if (read_pos(fVolume.Device(), block << BlockShift(), fBlock, BlockSize())
93 < (ssize_t)BlockSize())
94 return NULL;
96 return fBlock;
100 inline uint8*
101 CachedBlock::SetTo(block_run run)
103 return SetTo(fVolume.ToBlock(run));
107 // #pragma mark -
110 Stream::Stream(Volume& volume, block_run run)
112 fVolume(volume)
114 if (read_pos(volume.Device(), volume.ToOffset(run), this, sizeof(bfs_inode))
115 != sizeof(bfs_inode))
116 return;
120 Stream::Stream(Volume& volume, off_t id)
122 fVolume(volume)
124 if (read_pos(volume.Device(), volume.ToOffset(id), this, sizeof(bfs_inode))
125 != sizeof(bfs_inode))
126 return;
130 Stream::~Stream()
135 status_t
136 Stream::InitCheck()
138 return bfs_inode::InitCheck(&fVolume);
142 status_t
143 Stream::GetNextSmallData(const small_data** _smallData) const
145 // TODO: Stream derives from bfs_inode and we read only sizeof(bfs_inode)
146 // bytes from disk, i.e. the small data region is not in memory.
147 panic("Stream::GetNextSmallData(): small data region is not loaded!");
149 const small_data* smallData = *_smallData;
151 // begin from the start?
152 if (smallData == NULL)
153 smallData = small_data_start;
154 else
155 smallData = smallData->Next();
157 // is already last item?
158 if (smallData->IsLast(this))
159 return B_ENTRY_NOT_FOUND;
161 *_smallData = smallData;
163 return B_OK;
167 status_t
168 Stream::GetName(char* name, size_t size) const
170 const small_data* smallData = NULL;
171 while (GetNextSmallData(&smallData) == B_OK) {
172 if (*smallData->Name() == FILE_NAME_NAME
173 && smallData->NameSize() == FILE_NAME_NAME_LENGTH) {
174 strlcpy(name, (const char*)smallData->Data(), size);
175 return B_OK;
178 return B_ERROR;
182 status_t
183 Stream::ReadLink(char* buffer, size_t bufferSize)
185 // link in the stream
187 if (Flags() & INODE_LONG_SYMLINK)
188 return ReadAt(0, (uint8*)buffer, &bufferSize);
190 // link in the inode
192 strlcpy(buffer, short_symlink, bufferSize);
193 return B_OK;
197 status_t
198 Stream::FindBlockRun(off_t pos, block_run& run, off_t& offset)
200 // find matching block run
202 if (data.MaxDirectRange() > 0 && pos >= data.MaxDirectRange()) {
203 if (data.MaxDoubleIndirectRange() > 0
204 && pos >= data.MaxIndirectRange()) {
205 // access to double indirect blocks
207 CachedBlock cached(fVolume);
209 int32 runsPerBlock;
210 int32 directSize;
211 int32 indirectSize;
212 get_double_indirect_sizes(data.double_indirect.Length(),
213 cached.BlockSize(), runsPerBlock, directSize, indirectSize);
215 off_t start = pos - data.MaxIndirectRange();
216 int32 index = start / indirectSize;
218 block_run* indirect = (block_run*)cached.SetTo(
219 fVolume.ToBlock(data.double_indirect) + index / runsPerBlock);
220 if (indirect == NULL)
221 return B_ERROR;
223 //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index);
224 //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start);
226 int32 current = (start % indirectSize) / directSize;
228 indirect = (block_run*)cached.SetTo(fVolume.ToBlock(indirect[
229 index % runsPerBlock]) + current / runsPerBlock);
230 if (indirect == NULL)
231 return B_ERROR;
233 run = indirect[current % runsPerBlock];
234 offset = data.MaxIndirectRange() + (index * indirectSize)
235 + (current * directSize);
236 //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
237 } else {
238 // access to indirect blocks
240 int32 runsPerBlock = fVolume.BlockSize() / sizeof(block_run);
241 off_t runBlockEnd = data.MaxDirectRange();
243 CachedBlock cached(fVolume);
244 off_t block = fVolume.ToBlock(data.indirect);
246 for (int32 i = 0; i < data.indirect.Length(); i++) {
247 block_run* indirect = (block_run *)cached.SetTo(block + i);
248 if (indirect == NULL)
249 return B_IO_ERROR;
251 int32 current = -1;
252 while (++current < runsPerBlock) {
253 if (indirect[current].IsZero())
254 break;
256 runBlockEnd
257 += (uint32)indirect[current].Length() << cached.BlockShift();
258 if (runBlockEnd > pos) {
259 run = indirect[current];
260 offset = runBlockEnd
261 - ((uint32)run.Length() << cached.BlockShift());
262 //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start);
263 //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
264 return fVolume.ValidateBlockRun(run);
268 return B_ERROR;
270 } else {
271 // access from direct blocks
273 off_t runBlockEnd = 0LL;
274 int32 current = -1;
276 while (++current < NUM_DIRECT_BLOCKS) {
277 if (data.direct[current].IsZero())
278 break;
280 runBlockEnd += (uint32)data.direct[current].Length() << fVolume.BlockShift();
281 if (runBlockEnd > pos) {
282 run = data.direct[current];
283 offset = runBlockEnd - ((uint32)run.Length() << fVolume.BlockShift());
284 //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
285 return fVolume.ValidateBlockRun(run);
288 //PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data.size,pos));
289 return B_ENTRY_NOT_FOUND;
291 return fVolume.ValidateBlockRun(run);
295 status_t
296 Stream::ReadAt(off_t pos, uint8* buffer, size_t* _length)
298 // set/check boundaries for pos/length
300 if (pos < 0)
301 return B_BAD_VALUE;
302 if (pos >= data.Size()) {
303 *_length = 0;
304 return B_NO_ERROR;
307 size_t length = *_length;
309 if (pos + (off_t)length > data.Size())
310 length = data.Size() - pos;
312 block_run run;
313 off_t offset;
314 if (FindBlockRun(pos, run, offset) < B_OK) {
315 *_length = 0;
316 return B_BAD_VALUE;
319 uint32 bytesRead = 0;
320 uint32 blockSize = fVolume.BlockSize();
321 uint32 blockShift = fVolume.BlockShift();
322 uint8* block;
324 // the first block_run we read could not be aligned to the block_size boundary
325 // (read partial block at the beginning)
327 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0
328 if (pos % blockSize != 0) {
329 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start()
330 + ((pos - offset) >> blockShift));
331 run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length()
332 - ((pos - offset) >> blockShift));
334 CachedBlock cached(fVolume, run);
335 if ((block = cached.Block()) == NULL) {
336 *_length = 0;
337 return B_BAD_VALUE;
340 bytesRead = blockSize - (pos % blockSize);
341 if (length < bytesRead)
342 bytesRead = length;
344 memcpy(buffer, block + (pos % blockSize), bytesRead);
345 pos += bytesRead;
347 length -= bytesRead;
348 if (length == 0) {
349 *_length = bytesRead;
350 return B_OK;
353 if (FindBlockRun(pos, run, offset) < B_OK) {
354 *_length = bytesRead;
355 return B_BAD_VALUE;
359 // the first block_run is already filled in at this point
360 // read the following complete blocks using cached_read(),
361 // the last partial block is read using the generic Cache class
363 bool partial = false;
365 while (length > 0) {
366 // offset is the offset to the current pos in the block_run
367 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start()
368 + ((pos - offset) >> blockShift));
369 run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length()
370 - ((pos - offset) >> blockShift));
372 if (uint32(run.Length() << blockShift) > length) {
373 if (length < blockSize) {
374 CachedBlock cached(fVolume, run);
375 if ((block = cached.Block()) == NULL) {
376 *_length = bytesRead;
377 return B_BAD_VALUE;
379 memcpy(buffer + bytesRead, block, length);
380 bytesRead += length;
381 break;
383 run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift);
384 partial = true;
387 if (read_pos(fVolume.Device(), fVolume.ToOffset(run), buffer + bytesRead,
388 run.Length() << fVolume.BlockShift()) < B_OK) {
389 *_length = bytesRead;
390 return B_BAD_VALUE;
393 int32 bytes = run.Length() << blockShift;
394 length -= bytes;
395 bytesRead += bytes;
396 if (length == 0)
397 break;
399 pos += bytes;
401 if (partial) {
402 // if the last block was read only partially, point block_run
403 // to the remaining part
404 run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length());
405 run.length = 1;
406 offset = pos;
407 } else if (FindBlockRun(pos, run, offset) < B_OK) {
408 *_length = bytesRead;
409 return B_BAD_VALUE;
413 *_length = bytesRead;
414 return B_NO_ERROR;
418 Node*
419 Stream::NodeFactory(Volume& volume, off_t id)
421 Stream stream(volume, id);
422 if (stream.InitCheck() != B_OK)
423 return NULL;
425 if (stream.IsContainer())
426 return new(nothrow) Directory(stream);
428 if (stream.IsSymlink())
429 return new(nothrow) Link(stream);
431 return new(nothrow) File(stream);
435 // #pragma mark -
438 status_t
439 bfs_inode::InitCheck(Volume* volume) const
441 if ((Flags() & INODE_NOT_READY) != 0) {
442 // the other fields may not yet contain valid values
443 return B_BUSY;
446 if (Magic1() != INODE_MAGIC1
447 || !(Flags() & INODE_IN_USE)
448 || inode_num.Length() != 1
449 // matches inode size?
450 || (uint32)InodeSize() != volume->InodeSize()
451 // parent resides on disk?
452 || parent.AllocationGroup() > int32(volume->AllocationGroups())
453 || parent.AllocationGroup() < 0
454 || parent.Start() > (1L << volume->AllocationGroupShift())
455 || parent.Length() != 1
456 // attributes, too?
457 || attributes.AllocationGroup() > int32(volume->AllocationGroups())
458 || attributes.AllocationGroup() < 0
459 || attributes.Start() > (1L << volume->AllocationGroupShift()))
460 return B_BAD_DATA;
462 // TODO: Add some tests to check the integrity of the other stuff here,
463 // especially for the data_stream!
465 return B_OK;