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
Stream(int device,FileBlock & node)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
~Stream()55 Stream::~Stream()
56 {
57 free(fBlock.BlockData());
58 }
59
60
61 status_t
InitCheck()62 Stream::InitCheck()
63 {
64 return fBlock.BlockData() != NULL ? B_OK : B_NO_MEMORY;
65 }
66
67
68 int32
BlockOffset(off_t offset) const69 Stream::BlockOffset(off_t offset) const
70 {
71 return offset % fNode.BlockSize();
72 }
73
74
75 int32
BlockIndex(off_t offset) const76 Stream::BlockIndex(off_t offset) const
77 {
78 return (offset % (fNode.BlockSize() * fNode.NumDataBlocks())) / fNode.BlockSize();
79 }
80
81
82 int32
ExtensionBlockOffset(off_t offset) const83 Stream::ExtensionBlockOffset(off_t offset) const
84 {
85 return offset / (fNode.BlockSize() * fNode.NumDataBlocks());
86 }
87
88
89 status_t
ReadNextExtension()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
ReadAt(off_t offset,uint8 * buffer,size_t size)106 Stream::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
File(Volume & volume,int32 block)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
~File()173 File::~File()
174 {
175 }
176
177
178 status_t
InitCheck()179 File::InitCheck()
180 {
181 if (!fNode.IsFile())
182 return B_BAD_TYPE;
183
184 return fNode.ValidateCheckSum();
185 }
186
187
188 status_t
Open(void ** _cookie,int mode)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
Close(void * cookie)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
ReadAt(void * cookie,off_t pos,void * buffer,size_t bufferSize)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
WriteAt(void * cookie,off_t pos,const void * buffer,size_t bufferSize)227 File::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
228 {
229 return EROFS;
230 }
231
232
233 status_t
GetName(char * nameBuffer,size_t bufferSize) const234 File::GetName(char *nameBuffer, size_t bufferSize) const
235 {
236 return fNode.GetName(nameBuffer, bufferSize);
237 }
238
239
240 int32
Type() const241 File::Type() const
242 {
243 return S_IFREG;
244 }
245
246
247 off_t
Size() const248 File::Size() const
249 {
250 return fNode.Size();
251 }
252
253
254 ino_t
Inode() const255 File::Inode() const
256 {
257 return fNode.HeaderKey();
258 }
259
260
261 } // namespace FFS
262