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