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