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