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