xref: /haiku/src/system/boot/loader/file_systems/bfs/Stream.cpp (revision b55a57da7173b9af0432bd3e148d03f06161d036)
1 /*
2  * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de.
3  * This file may be used under the terms of the MIT License.
4  */
5 
6 
7 //!	Inode stream access functions
8 
9 
10 #include "Stream.h"
11 #include "Directory.h"
12 #include "File.h"
13 #include "Link.h"
14 
15 #include <util/kernel_cpp.h>
16 
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <string.h>
20 
21 
22 using namespace BFS;
23 
24 
25 class CachedBlock {
26 	public:
27 		CachedBlock(Volume &volume);
28 		CachedBlock(Volume &volume, block_run run);
29 		~CachedBlock();
30 
31 		uint8 *SetTo(block_run run);
32 		uint8 *SetTo(off_t offset);
33 
34 		void Unset();
35 
36 		uint8 *Block() const { return fBlock; }
37 		off_t BlockNumber() const { return fBlockNumber; }
38 		uint32 BlockSize() const { return fVolume.BlockSize(); }
39 		uint32 BlockShift() const { return fVolume.BlockShift(); }
40 
41 	private:
42 		Volume	&fVolume;
43 		off_t	fBlockNumber;
44 		uint8	*fBlock;
45 };
46 
47 
48 CachedBlock::CachedBlock(Volume &volume)
49 	:
50 	fVolume(volume),
51 	fBlockNumber(-1LL),
52 	fBlock(NULL)
53 {
54 }
55 
56 
57 CachedBlock::CachedBlock(Volume &volume, block_run run)
58 	:
59 	fVolume(volume),
60 	fBlockNumber(-1LL),
61 	fBlock(NULL)
62 {
63 	SetTo(run);
64 }
65 
66 
67 CachedBlock::~CachedBlock()
68 {
69 	free(fBlock);
70 }
71 
72 
73 inline void
74 CachedBlock::Unset()
75 {
76 	fBlockNumber = -1;
77 }
78 
79 
80 inline uint8 *
81 CachedBlock::SetTo(off_t block)
82 {
83 	if (block == fBlockNumber)
84 		return fBlock;
85 	if (fBlock == NULL) {
86 		fBlock = (uint8 *)malloc(BlockSize());
87 		if (fBlock == NULL)
88 			return NULL;
89 	}
90 
91 	fBlockNumber = block;
92 	if (read_pos(fVolume.Device(), block << BlockShift(), fBlock, BlockSize()) < (ssize_t)BlockSize())
93 		return NULL;
94 
95 	return fBlock;
96 }
97 
98 
99 inline uint8 *
100 CachedBlock::SetTo(block_run run)
101 {
102 	return SetTo(fVolume.ToBlock(run));
103 }
104 
105 
106 //	#pragma mark -
107 
108 
109 Stream::Stream(Volume &volume, block_run run)
110 	:
111 	fVolume(volume)
112 {
113 	if (read_pos(volume.Device(), volume.ToOffset(run), this, sizeof(bfs_inode)) != sizeof(bfs_inode))
114 		return;
115 }
116 
117 
118 Stream::Stream(Volume &volume, off_t id)
119 	:
120 	fVolume(volume)
121 {
122 	if (read_pos(volume.Device(), volume.ToOffset(id), this, sizeof(bfs_inode)) != sizeof(bfs_inode))
123 		return;
124 }
125 
126 
127 Stream::~Stream()
128 {
129 }
130 
131 
132 status_t
133 Stream::InitCheck()
134 {
135 	return bfs_inode::InitCheck(&fVolume);
136 }
137 
138 
139 status_t
140 Stream::GetNextSmallData(const small_data **_smallData) const
141 {
142 	// TODO: Stream derives from bfs_inode and we read only sizeof(bfs_inode)
143 	// bytes from disk, i.e. the small data region is not in memory.
144 	panic("Stream::GetNextSmallData(): small data region is not loaded!");
145 
146 	const small_data *smallData = *_smallData;
147 
148 	// begin from the start?
149 	if (smallData == NULL)
150 		smallData = small_data_start;
151 	else
152 		smallData = smallData->Next();
153 
154 	// is already last item?
155 	if (smallData->IsLast(this))
156 		return B_ENTRY_NOT_FOUND;
157 
158 	*_smallData = smallData;
159 
160 	return B_OK;
161 }
162 
163 
164 status_t
165 Stream::GetName(char *name, size_t size) const
166 {
167 	const small_data *smallData = NULL;
168 	while (GetNextSmallData(&smallData) == B_OK) {
169 		if (*smallData->Name() == FILE_NAME_NAME
170 			&& smallData->NameSize() == FILE_NAME_NAME_LENGTH) {
171 			strlcpy(name, (const char *)smallData->Data(), size);
172 			return B_OK;
173 		}
174 	}
175 	return B_ERROR;
176 }
177 
178 
179 status_t
180 Stream::ReadLink(char *buffer, size_t bufferSize)
181 {
182 	// link in the stream
183 
184 	if (Flags() & INODE_LONG_SYMLINK)
185 		return ReadAt(0, (uint8 *)buffer, &bufferSize);
186 
187 	// link in the inode
188 
189 	strlcpy(buffer, short_symlink, bufferSize);
190 	return B_OK;
191 }
192 
193 
194 status_t
195 Stream::FindBlockRun(off_t pos, block_run &run, off_t &offset)
196 {
197 	// find matching block run
198 
199 	if (data.MaxDirectRange() > 0 && pos >= data.MaxDirectRange()) {
200 		if (data.MaxDoubleIndirectRange() > 0 && pos >= data.MaxIndirectRange()) {
201 			// access to double indirect blocks
202 
203 			CachedBlock cached(fVolume);
204 
205 			int32 runsPerBlock;
206 			int32 directSize;
207 			int32 indirectSize;
208 			get_double_indirect_sizes(data.double_indirect.Length(),
209 				cached.BlockSize(), runsPerBlock, directSize, indirectSize);
210 
211 			off_t start = pos - data.MaxIndirectRange();
212 			int32 index = start / indirectSize;
213 
214 			block_run *indirect = (block_run *)cached.SetTo(
215 				fVolume.ToBlock(data.double_indirect) + index / runsPerBlock);
216 			if (indirect == NULL)
217 				return B_ERROR;
218 
219 			//printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index);
220 			//printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start);
221 
222 			int32 current = (start % indirectSize) / directSize;
223 
224 			indirect = (block_run *)cached.SetTo(
225 					fVolume.ToBlock(indirect[index % runsPerBlock]) + current / runsPerBlock);
226 			if (indirect == NULL)
227 				return B_ERROR;
228 
229 			run = indirect[current % runsPerBlock];
230 			offset = data.MaxIndirectRange() + (index * indirectSize) + (current * directSize);
231 			//printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
232 		} else {
233 			// access to indirect blocks
234 
235 			int32 runsPerBlock = fVolume.BlockSize() / sizeof(block_run);
236 			off_t runBlockEnd = data.MaxDirectRange();
237 
238 			CachedBlock cached(fVolume);
239 			off_t block = fVolume.ToBlock(data.indirect);
240 
241 			for (int32 i = 0;i < data.indirect.Length();i++) {
242 				block_run *indirect = (block_run *)cached.SetTo(block + i);
243 				if (indirect == NULL)
244 					return B_IO_ERROR;
245 
246 				int32 current = -1;
247 				while (++current < runsPerBlock) {
248 					if (indirect[current].IsZero())
249 						break;
250 
251 					runBlockEnd += indirect[current].Length() << cached.BlockShift();
252 					if (runBlockEnd > pos) {
253 						run = indirect[current];
254 						offset = runBlockEnd - (run.Length() << cached.BlockShift());
255 						//printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start);
256 						//printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
257 						return fVolume.ValidateBlockRun(run);
258 					}
259 				}
260 			}
261 			return B_ERROR;
262 		}
263 	} else {
264 		// access from direct blocks
265 
266 		off_t runBlockEnd = 0LL;
267 		int32 current = -1;
268 
269 		while (++current < NUM_DIRECT_BLOCKS) {
270 			if (data.direct[current].IsZero())
271 				break;
272 
273 			runBlockEnd += data.direct[current].Length() << fVolume.BlockShift();
274 			if (runBlockEnd > pos) {
275 				run = data.direct[current];
276 				offset = runBlockEnd - (run.Length() << fVolume.BlockShift());
277 				//printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
278 				return fVolume.ValidateBlockRun(run);
279 			}
280 		}
281 		//PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data.size,pos));
282 		return B_ENTRY_NOT_FOUND;
283 	}
284 	return fVolume.ValidateBlockRun(run);
285 }
286 
287 
288 status_t
289 Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
290 {
291 	// set/check boundaries for pos/length
292 
293 	if (pos < 0)
294 		return B_BAD_VALUE;
295 	if (pos >= data.Size()) {
296 		*_length = 0;
297 		return B_NO_ERROR;
298 	}
299 
300 	size_t length = *_length;
301 
302 	if (pos + length > data.Size())
303 		length = data.Size() - pos;
304 
305 	block_run run;
306 	off_t offset;
307 	if (FindBlockRun(pos, run, offset) < B_OK) {
308 		*_length = 0;
309 		return B_BAD_VALUE;
310 	}
311 
312 	uint32 bytesRead = 0;
313 	uint32 blockSize = fVolume.BlockSize();
314 	uint32 blockShift = fVolume.BlockShift();
315 	uint8 *block;
316 
317 	// the first block_run we read could not be aligned to the block_size boundary
318 	// (read partial block at the beginning)
319 
320 	// pos % block_size == (pos - offset) % block_size, offset % block_size == 0
321 	if (pos % blockSize != 0) {
322 		run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift));
323 		run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift));
324 
325 		CachedBlock cached(fVolume, run);
326 		if ((block = cached.Block()) == NULL) {
327 			*_length = 0;
328 			return B_BAD_VALUE;
329 		}
330 
331 		bytesRead = blockSize - (pos % blockSize);
332 		if (length < bytesRead)
333 			bytesRead = length;
334 
335 		memcpy(buffer, block + (pos % blockSize), bytesRead);
336 		pos += bytesRead;
337 
338 		length -= bytesRead;
339 		if (length == 0) {
340 			*_length = bytesRead;
341 			return B_OK;
342 		}
343 
344 		if (FindBlockRun(pos, run, offset) < B_OK) {
345 			*_length = bytesRead;
346 			return B_BAD_VALUE;
347 		}
348 	}
349 
350 	// the first block_run is already filled in at this point
351 	// read the following complete blocks using cached_read(),
352 	// the last partial block is read using the generic Cache class
353 
354 	bool partial = false;
355 
356 	while (length > 0) {
357 		// offset is the offset to the current pos in the block_run
358 		run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift));
359 		run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift));
360 
361 		if (uint32(run.Length() << blockShift) > length) {
362 			if (length < blockSize) {
363 				CachedBlock cached(fVolume, run);
364 				if ((block = cached.Block()) == NULL) {
365 					*_length = bytesRead;
366 					return B_BAD_VALUE;
367 				}
368 				memcpy(buffer + bytesRead, block, length);
369 				bytesRead += length;
370 				break;
371 			}
372 			run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift);
373 			partial = true;
374 		}
375 
376 		if (read_pos(fVolume.Device(), fVolume.ToOffset(run), buffer + bytesRead, run.Length() << fVolume.BlockShift()) < B_OK) {
377 			*_length = bytesRead;
378 			return B_BAD_VALUE;
379 		}
380 
381 		int32 bytes = run.Length() << blockShift;
382 		length -= bytes;
383 		bytesRead += bytes;
384 		if (length == 0)
385 			break;
386 
387 		pos += bytes;
388 
389 		if (partial) {
390 			// if the last block was read only partially, point block_run
391 			// to the remaining part
392 			run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length());
393 			run.length = 1;
394 			offset = pos;
395 		} else if (FindBlockRun(pos, run, offset) < B_OK) {
396 			*_length = bytesRead;
397 			return B_BAD_VALUE;
398 		}
399 	}
400 
401 	*_length = bytesRead;
402 	return B_NO_ERROR;
403 }
404 
405 
406 Node *
407 Stream::NodeFactory(Volume &volume, off_t id)
408 {
409 	Stream stream(volume, id);
410 	if (stream.InitCheck() != B_OK)
411 		return NULL;
412 
413 	if (stream.IsContainer())
414 		return new(nothrow) Directory(stream);
415 
416 	if (stream.IsSymlink())
417 		return new(nothrow) Link(stream);
418 
419 	return new(nothrow) File(stream);
420 }
421 
422 
423 //	#pragma mark -
424 
425 
426 status_t
427 bfs_inode::InitCheck(Volume *volume)
428 {
429 	if (Flags() & INODE_NOT_READY) {
430 		// the other fields may not yet contain valid values
431 		return B_BUSY;
432 	}
433 
434 	if (Magic1() != INODE_MAGIC1
435 		|| !(Flags() & INODE_IN_USE)
436 		|| inode_num.Length() != 1
437 		// matches inode size?
438 		|| (uint32)InodeSize() != volume->InodeSize()
439 		// parent resides on disk?
440 		|| parent.AllocationGroup() > int32(volume->AllocationGroups())
441 		|| parent.AllocationGroup() < 0
442 		|| parent.Start() > (1L << volume->AllocationGroupShift())
443 		|| parent.Length() != 1
444 		// attributes, too?
445 		|| attributes.AllocationGroup() > int32(volume->AllocationGroups())
446 		|| attributes.AllocationGroup() < 0
447 		|| attributes.Start() > (1L << volume->AllocationGroupShift()))
448 		return B_BAD_DATA;
449 
450 	// ToDo: Add some tests to check the integrity of the other stuff here,
451 	// especially for the data_stream!
452 
453 	return B_OK;
454 }
455 
456