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