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