1/*
2** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3** Distributed under the terms of the OpenBeOS License.
4*/
5
6
7#include "File.h"
8
9#include <errno.h>
10#include <sys/stat.h>
11#include <unistd.h>
12
13#include <util/kernel_cpp.h>
14
15
16namespace FFS {
17
18class Stream {
19	public:
20		Stream(int device, FileBlock &node);
21		~Stream();
22
23		status_t InitCheck();
24		ssize_t ReadAt(off_t offset, uint8 *buffer, size_t size);
25
26	private:
27		int32 BlockOffset(off_t offset) const;
28		int32 BlockIndex(off_t offset) const;
29		int32 ExtensionBlockOffset(off_t offset) const;
30		status_t ReadNextExtension();
31
32		int			fDevice;
33		FileBlock	&fNode;
34		FileBlock	fBlock;
35		int32		fExtensionBlockOffset;
36};
37
38
39Stream::Stream(int device, FileBlock &node)
40	:
41	fDevice(device),
42	fNode(node)
43{
44	void *buffer = malloc(fNode.BlockSize());
45	if (buffer == NULL)
46		return;
47
48	fExtensionBlockOffset = 0;
49	fBlock.SetTo(buffer, fNode.BlockSize());
50}
51
52
53Stream::~Stream()
54{
55	free(fBlock.BlockData());
56}
57
58
59status_t
60Stream::InitCheck()
61{
62	return fBlock.BlockData() != NULL ? B_OK : B_NO_MEMORY;
63}
64
65
66int32
67Stream::BlockOffset(off_t offset) const
68{
69	return offset % fNode.BlockSize();
70}
71
72
73int32
74Stream::BlockIndex(off_t offset) const
75{
76	return (offset % (fNode.BlockSize() * fNode.NumDataBlocks())) / fNode.BlockSize();
77}
78
79
80int32
81Stream::ExtensionBlockOffset(off_t offset) const
82{
83	return offset / (fNode.BlockSize() * fNode.NumDataBlocks());
84}
85
86
87status_t
88Stream::ReadNextExtension()
89{
90	int32 next;
91	if (fExtensionBlockOffset == 0)
92		next = fNode.NextExtension();
93	else
94		next = fBlock.NextExtension();
95
96	if (read_pos(fDevice, next * fNode.BlockSize(), fBlock.BlockData(), fNode.BlockSize()) < B_OK)
97		return B_ERROR;
98
99	return fBlock.ValidateCheckSum();
100}
101
102
103ssize_t
104Stream::ReadAt(off_t offset, uint8 *buffer, size_t size)
105{
106	if (offset < 0)
107		return B_BAD_VALUE;
108	if (offset + size > fNode.Size())
109		size = fNode.Size() - offset;
110
111	ssize_t bytesLeft = (ssize_t)size;
112
113	while (bytesLeft != 0) {
114		int32 extensionBlock = ExtensionBlockOffset(offset);
115
116		// get the right extension block
117
118		if (extensionBlock < fExtensionBlockOffset)
119			fExtensionBlockOffset = 1;
120
121		while (fExtensionBlockOffset < extensionBlock) {
122			if (ReadNextExtension() != B_OK)
123				return B_ERROR;
124
125			fExtensionBlockOffset++;
126		}
127
128		// read the data block into memory
129
130		int32 block;
131		if (extensionBlock == 0)
132			block = fNode.DataBlock(BlockIndex(offset));
133		else
134			block = fBlock.DataBlock(BlockIndex(offset));
135
136		int32 blockOffset = BlockOffset(offset);
137		int32 toRead = fNode.BlockSize() - blockOffset;
138		if (toRead > bytesLeft)
139			toRead = bytesLeft;
140
141		ssize_t bytesRead = read_pos(fDevice, block * fNode.BlockSize() + blockOffset,
142			buffer, toRead);
143		if (bytesRead < 0)
144			return errno;
145
146		bytesLeft -= bytesRead;
147		buffer += bytesRead;
148		offset += fNode.BlockSize() - blockOffset;
149	}
150
151	return size;
152}
153
154
155//	#pragma mark -
156
157
158File::File(Volume &volume, int32 block)
159	:
160	fVolume(volume)
161{
162	void *data = malloc(volume.BlockSize());
163	if (data == NULL)
164		return;
165
166	if (read_pos(volume.Device(), block * volume.BlockSize(), data, volume.BlockSize()) == volume.BlockSize())
167		fNode.SetTo(data, volume.BlockSize());
168}
169
170
171File::~File()
172{
173}
174
175
176status_t
177File::InitCheck()
178{
179	if (!fNode.IsFile())
180		return B_BAD_TYPE;
181
182	return fNode.ValidateCheckSum();
183}
184
185
186status_t
187File::Open(void **_cookie, int mode)
188{
189	Stream *stream = new(nothrow) Stream(fVolume.Device(), fNode);
190	if (stream == NULL)
191		return B_NO_MEMORY;
192
193	if (stream->InitCheck() != B_OK) {
194		delete stream;
195		return B_NO_MEMORY;
196	}
197
198	*_cookie = (void *)stream;
199	return B_OK;
200}
201
202
203status_t
204File::Close(void *cookie)
205{
206	Stream *stream = (Stream *)cookie;
207
208	delete stream;
209	return B_OK;
210}
211
212
213ssize_t
214File::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
215{
216	Stream *stream = (Stream *)cookie;
217	if (stream == NULL)
218		return B_BAD_VALUE;
219
220	return stream->ReadAt(pos, (uint8 *)buffer, bufferSize);
221}
222
223
224ssize_t
225File::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
226{
227	return EROFS;
228}
229
230
231status_t
232File::GetName(char *nameBuffer, size_t bufferSize) const
233{
234	return fNode.GetName(nameBuffer, bufferSize);
235}
236
237
238int32
239File::Type() const
240{
241	return S_IFREG;
242}
243
244
245off_t
246File::Size() const
247{
248	return fNode.Size();
249}
250
251
252ino_t
253File::Inode() const
254{
255	return fNode.HeaderKey();
256}
257
258}	// namespace FFS
259