xref: /haiku/src/system/boot/loader/file_systems/amiga_ffs/File.cpp (revision 4e3137c085bae361922078f123dceb92da700640)
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 using std::nothrow;
15 
16 
17 namespace FFS {
18 
19 
20 class 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 
41 Stream::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 
55 Stream::~Stream()
56 {
57 	free(fBlock.BlockData());
58 }
59 
60 
61 status_t
62 Stream::InitCheck()
63 {
64 	return fBlock.BlockData() != NULL ? B_OK : B_NO_MEMORY;
65 }
66 
67 
68 int32
69 Stream::BlockOffset(off_t offset) const
70 {
71 	return offset % fNode.BlockSize();
72 }
73 
74 
75 int32
76 Stream::BlockIndex(off_t offset) const
77 {
78 	return (offset % (fNode.BlockSize() * fNode.NumDataBlocks())) / fNode.BlockSize();
79 }
80 
81 
82 int32
83 Stream::ExtensionBlockOffset(off_t offset) const
84 {
85 	return offset / (fNode.BlockSize() * fNode.NumDataBlocks());
86 }
87 
88 
89 status_t
90 Stream::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 
105 ssize_t
106 Stream::ReadAt(off_t offset, uint8 *buffer, size_t size)
107 {
108 	if (offset < 0)
109 		return B_BAD_VALUE;
110 	if (offset + 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 
160 File::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 
173 File::~File()
174 {
175 }
176 
177 
178 status_t
179 File::InitCheck()
180 {
181 	if (!fNode.IsFile())
182 		return B_BAD_TYPE;
183 
184 	return fNode.ValidateCheckSum();
185 }
186 
187 
188 status_t
189 File::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 
205 status_t
206 File::Close(void *cookie)
207 {
208 	Stream *stream = (Stream *)cookie;
209 
210 	delete stream;
211 	return B_OK;
212 }
213 
214 
215 ssize_t
216 File::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 
226 ssize_t
227 File::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
228 {
229 	return EROFS;
230 }
231 
232 
233 status_t
234 File::GetName(char *nameBuffer, size_t bufferSize) const
235 {
236 	return fNode.GetName(nameBuffer, bufferSize);
237 }
238 
239 
240 int32
241 File::Type() const
242 {
243 	return S_IFREG;
244 }
245 
246 
247 off_t
248 File::Size() const
249 {
250 	return fNode.Size();
251 }
252 
253 
254 ino_t
255 File::Inode() const
256 {
257 	return fNode.HeaderKey();
258 }
259 
260 
261 }	// namespace FFS
262