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 */
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 <stdlib.h>
16#include <unistd.h>
17#include <string.h>
18
19
20using namespace BFS;
21using std::nothrow;
22
23
24class CachedBlock {
25public:
26								CachedBlock(Volume& volume);
27								CachedBlock(Volume& volume, block_run run);
28								~CachedBlock();
29
30			uint8*				SetTo(block_run run);
31			uint8*				SetTo(off_t offset);
32
33			void				Unset();
34
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(); }
40
41private:
42			Volume&				fVolume;
43			off_t				fBlockNumber;
44			uint8*				fBlock;
45};
46
47
48CachedBlock::CachedBlock(Volume& volume)
49	:
50	fVolume(volume),
51	fBlockNumber(-1LL),
52	fBlock(NULL)
53{
54}
55
56
57CachedBlock::CachedBlock(Volume &volume, block_run run)
58	:
59	fVolume(volume),
60	fBlockNumber(-1LL),
61	fBlock(NULL)
62{
63	SetTo(run);
64}
65
66
67CachedBlock::~CachedBlock()
68{
69	free(fBlock);
70}
71
72
73inline void
74CachedBlock::Unset()
75{
76	fBlockNumber = -1;
77}
78
79
80inline uint8*
81CachedBlock::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())
93			< (ssize_t)BlockSize())
94		return NULL;
95
96	return fBlock;
97}
98
99
100inline uint8*
101CachedBlock::SetTo(block_run run)
102{
103	return SetTo(fVolume.ToBlock(run));
104}
105
106
107//	#pragma mark -
108
109
110Stream::Stream(Volume& volume, block_run run)
111	:
112	fVolume(volume)
113{
114	if (read_pos(volume.Device(), volume.ToOffset(run), this, sizeof(bfs_inode))
115			!= sizeof(bfs_inode))
116		return;
117}
118
119
120Stream::Stream(Volume& volume, off_t id)
121	:
122	fVolume(volume)
123{
124	if (read_pos(volume.Device(), volume.ToOffset(id), this, sizeof(bfs_inode))
125			!= sizeof(bfs_inode))
126		return;
127}
128
129
130Stream::~Stream()
131{
132}
133
134
135status_t
136Stream::InitCheck()
137{
138	return bfs_inode::InitCheck(&fVolume);
139}
140
141
142status_t
143Stream::GetNextSmallData(const small_data** _smallData) const
144{
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!");
148
149	const small_data* smallData = *_smallData;
150
151	// begin from the start?
152	if (smallData == NULL)
153		smallData = small_data_start;
154	else
155		smallData = smallData->Next();
156
157	// is already last item?
158	if (smallData->IsLast(this))
159		return B_ENTRY_NOT_FOUND;
160
161	*_smallData = smallData;
162
163	return B_OK;
164}
165
166
167status_t
168Stream::GetName(char* name, size_t size) const
169{
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;
176		}
177	}
178	return B_ERROR;
179}
180
181
182status_t
183Stream::ReadLink(char* buffer, size_t bufferSize)
184{
185	// link in the stream
186
187	if (Flags() & INODE_LONG_SYMLINK)
188		return ReadAt(0, (uint8*)buffer, &bufferSize);
189
190	// link in the inode
191
192	strlcpy(buffer, short_symlink, bufferSize);
193	return B_OK;
194}
195
196
197status_t
198Stream::FindBlockRun(off_t pos, block_run& run, off_t& offset)
199{
200	// find matching block run
201
202	if (data.MaxDirectRange() > 0 && pos >= data.MaxDirectRange()) {
203		if (data.MaxDoubleIndirectRange() > 0
204			&& pos >= data.MaxIndirectRange()) {
205			// access to double indirect blocks
206
207			CachedBlock cached(fVolume);
208
209			int32 runsPerBlock;
210			int32 directSize;
211			int32 indirectSize;
212			get_double_indirect_sizes(data.double_indirect.Length(),
213				cached.BlockSize(), runsPerBlock, directSize, indirectSize);
214
215			off_t start = pos - data.MaxIndirectRange();
216			int32 index = start / indirectSize;
217
218			block_run* indirect = (block_run*)cached.SetTo(
219				fVolume.ToBlock(data.double_indirect) + index / runsPerBlock);
220			if (indirect == NULL)
221				return B_ERROR;
222
223			//printf("\tstart = %lld, 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);
225
226			int32 current = (start % indirectSize) / directSize;
227
228			indirect = (block_run*)cached.SetTo(fVolume.ToBlock(indirect[
229					index % runsPerBlock]) + current / runsPerBlock);
230			if (indirect == NULL)
231				return B_ERROR;
232
233			run = indirect[current % runsPerBlock];
234			offset = data.MaxIndirectRange() + (index * indirectSize)
235				+ (current * directSize);
236			//printf("\tfCurrent = %ld, fRunFileOffset = %lld, fRunBlockEnd = %lld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
237		} else {
238			// access to indirect blocks
239
240			int32 runsPerBlock = fVolume.BlockSize() / sizeof(block_run);
241			off_t runBlockEnd = data.MaxDirectRange();
242
243			CachedBlock cached(fVolume);
244			off_t block = fVolume.ToBlock(data.indirect);
245
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;
250
251				int32 current = -1;
252				while (++current < runsPerBlock) {
253					if (indirect[current].IsZero())
254						break;
255
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 = %lld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
264						return fVolume.ValidateBlockRun(run);
265					}
266				}
267			}
268			return B_ERROR;
269		}
270	} else {
271		// access from direct blocks
272
273		off_t runBlockEnd = 0LL;
274		int32 current = -1;
275
276		while (++current < NUM_DIRECT_BLOCKS) {
277			if (data.direct[current].IsZero())
278				break;
279
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 = %lld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
285				return fVolume.ValidateBlockRun(run);
286			}
287		}
288		//PRINT(("FindBlockRun() failed in direct range: size = %lld, pos = %lld\n",data.size,pos));
289		return B_ENTRY_NOT_FOUND;
290	}
291	return fVolume.ValidateBlockRun(run);
292}
293
294
295status_t
296Stream::ReadAt(off_t pos, uint8* buffer, size_t* _length)
297{
298	// set/check boundaries for pos/length
299
300	if (pos < 0)
301		return B_BAD_VALUE;
302	if (pos >= data.Size()) {
303		*_length = 0;
304		return B_NO_ERROR;
305	}
306
307	size_t length = *_length;
308
309	if (pos + (off_t)length > data.Size())
310		length = data.Size() - pos;
311
312	block_run run;
313	off_t offset;
314	if (FindBlockRun(pos, run, offset) < B_OK) {
315		*_length = 0;
316		return B_BAD_VALUE;
317	}
318
319	uint32 bytesRead = 0;
320	uint32 blockSize = fVolume.BlockSize();
321	uint32 blockShift = fVolume.BlockShift();
322	uint8* block;
323
324	// the first block_run we read could not be aligned to the block_size boundary
325	// (read partial block at the beginning)
326
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));
333
334		CachedBlock cached(fVolume, run);
335		if ((block = cached.Block()) == NULL) {
336			*_length = 0;
337			return B_BAD_VALUE;
338		}
339
340		bytesRead = blockSize - (pos % blockSize);
341		if (length < bytesRead)
342			bytesRead = length;
343
344		memcpy(buffer, block + (pos % blockSize), bytesRead);
345		pos += bytesRead;
346
347		length -= bytesRead;
348		if (length == 0) {
349			*_length = bytesRead;
350			return B_OK;
351		}
352
353		if (FindBlockRun(pos, run, offset) < B_OK) {
354			*_length = bytesRead;
355			return B_BAD_VALUE;
356		}
357	}
358
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
362
363	bool partial = false;
364
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));
371
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;
378				}
379				memcpy(buffer + bytesRead, block, length);
380				bytesRead += length;
381				break;
382			}
383			run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift);
384			partial = true;
385		}
386
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;
391		}
392
393		int32 bytes = run.Length() << blockShift;
394		length -= bytes;
395		bytesRead += bytes;
396		if (length == 0)
397			break;
398
399		pos += bytes;
400
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;
410		}
411	}
412
413	*_length = bytesRead;
414	return B_NO_ERROR;
415}
416
417
418Node*
419Stream::NodeFactory(Volume& volume, off_t id)
420{
421	Stream stream(volume, id);
422	if (stream.InitCheck() != B_OK)
423		return NULL;
424
425	if (stream.IsContainer())
426		return new(nothrow) Directory(stream);
427
428	if (stream.IsSymlink())
429		return new(nothrow) Link(stream);
430
431	return new(nothrow) File(stream);
432}
433
434
435//	#pragma mark -
436
437
438status_t
439bfs_inode::InitCheck(Volume* volume) const
440{
441	if ((Flags() & INODE_NOT_READY) != 0) {
442		// the other fields may not yet contain valid values
443		return B_BUSY;
444	}
445
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;
461
462	// TODO: Add some tests to check the integrity of the other stuff here,
463	// especially for the data_stream!
464
465	return B_OK;
466}
467