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