xref: /haiku/src/bin/bfs_tools/lib/Inode.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
1 /*
2  * Copyright 2001-2008 pinc Software. All Rights Reserved.
3  * Released under the terms of the MIT license.
4  */
5 
6 //!	BFS Inode classes
7 
8 
9 #include "Inode.h"
10 #include "BPlusTree.h"
11 
12 #include <Directory.h>
13 #include <SymLink.h>
14 #include <Entry.h>
15 #include <Path.h>
16 #include <String.h>
17 
18 #include <new>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 
23 
24 class NodeGetter {
25 	public:
26 		NodeGetter(Inode* inode)
27 			:
28 			fInode(inode)
29 		{
30 			fInode->AcquireBuffer();
31 		}
32 
33 		~NodeGetter()
34 		{
35 			fInode->ReleaseBuffer();
36 		}
37 
38 	private:
39 		Inode*	fInode;
40 };
41 
42 
43 //	#pragma mark -
44 
45 
46 Inode::Inode(Disk* disk, bfs_inode* inode, bool ownBuffer)
47 	:
48 	fDisk(disk),
49 	fInode(inode),
50 	fOwnBuffer(ownBuffer),
51 	fPath(NULL),
52 	fRefCount(1),
53 	fCurrentSmallData(NULL),
54 	fAttributes(NULL),
55 	fAttributeBuffer(NULL)
56 {
57 	if (inode != NULL)
58 		fBlockRun = inode->inode_num;
59 }
60 
61 
62 Inode::Inode(const Inode& inode)
63 	:
64 	fDisk(inode.fDisk),
65 	fInode(inode.fInode),
66 	fOwnBuffer(false),
67 	fPath(NULL),
68 	fBlockRun(inode.fBlockRun),
69 	fRefCount(1),
70 	fCurrentSmallData(NULL),
71 	fAttributes(NULL),
72 	fAttributeBuffer(NULL)
73 {
74 }
75 
76 
77 Inode::~Inode()
78 {
79 	_Unset();
80 }
81 
82 
83 void
84 Inode::_Unset()
85 {
86 	if (fOwnBuffer)
87 		free(fInode);
88 
89 	fInode = NULL;
90 	fBlockRun.SetTo(0, 0, 0);
91 
92 	free(fPath);
93 	fPath = NULL;
94 
95 	delete fAttributes;
96 	fAttributes = NULL;
97 }
98 
99 
100 status_t
101 Inode::SetTo(bfs_inode *inode)
102 {
103 	_Unset();
104 
105 	fInode = inode;
106 	fBlockRun = inode->inode_num;
107 	return B_OK;
108 }
109 
110 
111 status_t
112 Inode::InitCheck() const
113 {
114 	if (!fInode)
115 		return B_ERROR;
116 
117 	// test inode magic and flags
118 	if (fInode->magic1 != INODE_MAGIC1
119 		|| !(fInode->flags & INODE_IN_USE)
120 		|| fInode->inode_num.length != 1)
121 		return B_ERROR;
122 
123 	if (fDisk->BlockSize()) {
124 			// matches known block size?
125 		if (fInode->inode_size != fDisk->SuperBlock()->inode_size
126 			// parent resides on disk?
127 			|| fInode->parent.allocation_group > fDisk->SuperBlock()->num_ags
128 			|| fInode->parent.allocation_group < 0
129 			|| fInode->parent.start > (1L << fDisk->SuperBlock()->ag_shift)
130 			|| fInode->parent.length != 1
131 			// attributes, too?
132 			|| fInode->attributes.allocation_group > fDisk->SuperBlock()->num_ags
133 			|| fInode->attributes.allocation_group < 0
134 			|| fInode->attributes.start > (1L << fDisk->SuperBlock()->ag_shift))
135 			return B_ERROR;
136 	} else {
137 		// is inode size one of the valid values?
138 		switch (fInode->inode_size) {
139 			case 1024:
140 			case 2048:
141 			case 4096:
142 			case 8192:
143 				break;
144 			default:
145 				return B_ERROR;
146 		}
147 	}
148 	return B_OK;
149 	// is inode on a boundary matching it's size?
150 	//return (Offset() % fInode->inode_size) == 0 ? B_OK : B_ERROR;
151 }
152 
153 
154 status_t
155 Inode::CopyBuffer()
156 {
157 	if (!fInode)
158 		return B_ERROR;
159 
160 	bfs_inode *buffer = (bfs_inode *)malloc(fInode->inode_size);
161 	if (!buffer)
162 		return B_NO_MEMORY;
163 
164 	memcpy(buffer, fInode, fInode->inode_size);
165 	fInode = buffer;
166 	fOwnBuffer = true;
167 	BufferClobbered();
168 		// this must not be deleted anymore
169 
170 	return B_OK;
171 }
172 
173 
174 /*static*/ bool
175 Inode::_LowMemory()
176 {
177 	static bigtime_t lastChecked;
178 	static int32 percentUsed;
179 
180 	if (system_time() > lastChecked + 1000000LL) {
181 		system_info info;
182 		get_system_info(&info);
183 		percentUsed = 100 * info.used_pages / info.max_pages;
184 	}
185 
186 	return percentUsed > 75;
187 }
188 
189 
190 void
191 Inode::ReleaseBuffer()
192 {
193 	if (atomic_add(&fRefCount, -1) != 1)
194 		return;
195 
196 	if (fOwnBuffer) {
197 		if (!_LowMemory())
198 			return;
199 
200 		free(fInode);
201 		fInode = NULL;
202 	}
203 }
204 
205 
206 status_t
207 Inode::AcquireBuffer()
208 {
209 	if (atomic_add(&fRefCount, 1) != 0)
210 		return B_OK;
211 
212 	if (!fOwnBuffer || fInode != NULL)
213 		return B_OK;
214 
215 	fInode = (bfs_inode*)malloc(fDisk->BlockSize());
216 	if (fInode == NULL)
217 		return B_NO_MEMORY;
218 
219 	ssize_t bytesRead = fDisk->ReadAt(Offset(), fInode, fDisk->BlockSize());
220 	if (bytesRead < B_OK)
221 		return bytesRead;
222 
223 	return B_OK;
224 }
225 
226 
227 void
228 Inode::BufferClobbered()
229 {
230 	AcquireBuffer();
231 }
232 
233 
234 void
235 Inode::SetParent(const block_run& run)
236 {
237 	fInode->parent = run;
238 	BufferClobbered();
239 }
240 
241 
242 void
243 Inode::SetBlockRun(const block_run& run)
244 {
245 	fInode->inode_num = run;
246 	fBlockRun = run;
247 	BufferClobbered();
248 }
249 
250 
251 void
252 Inode::SetMode(uint32 mode)
253 {
254 	fInode->mode = mode;
255 	BufferClobbered();
256 }
257 
258 
259 status_t
260 Inode::SetName(const char *name)
261 {
262 	if (name == NULL || *name == '\0')
263 		return B_BAD_VALUE;
264 
265 	small_data *data = fInode->small_data_start, *nameData = NULL;
266 	BufferClobbered();
267 
268 	while (!data->IsLast(fInode)) {
269 		if (data->type == FILE_NAME_TYPE
270 			&& data->name_size == FILE_NAME_NAME_LENGTH
271 			&& *data->Name() == FILE_NAME_NAME)
272 			nameData = data;
273 
274 		data = data->Next();
275 	}
276 
277 	int32 oldLength = nameData == NULL ? 0 : nameData->data_size;
278 	int32 newLength = strlen(name) + (nameData == NULL ? sizeof(small_data) + 5 : 0);
279 
280 	if ((addr_t)data + newLength - oldLength >= (addr_t)(fInode
281 			+ fDisk->BlockSize()))
282 		return B_NO_MEMORY;
283 
284 	if (nameData == NULL) {
285 		memmove(newLength + (uint8 *)fInode->small_data_start,
286 			fInode->small_data_start,
287 			(addr_t)data - (addr_t)fInode->small_data_start);
288 		nameData = fInode->small_data_start;
289 	} else {
290 		memmove(newLength + (uint8 *)nameData, nameData,
291 			(addr_t)data - (addr_t)fInode->small_data_start);
292 	}
293 
294 	memset(nameData, 0, sizeof(small_data) + 5 + strlen(name));
295 	nameData->type = FILE_NAME_TYPE;
296 	nameData->name_size = FILE_NAME_NAME_LENGTH;
297 	nameData->data_size = strlen(name);
298 	*nameData->Name() = FILE_NAME_NAME;
299 	strcpy((char *)nameData->Data(),name);
300 
301 	return B_OK;
302 }
303 
304 
305 const char *
306 Inode::Name() const
307 {
308 	if (InitCheck() != B_OK) {
309 		puts("Not getting name because node is invalid");
310 		return NULL;
311 	}
312 	small_data *data = fInode->small_data_start;
313 	while (!data->IsLast(fInode)) {
314 		if (data->type == FILE_NAME_TYPE
315 			&& data->name_size == FILE_NAME_NAME_LENGTH
316 			&& *data->Name() == FILE_NAME_NAME)
317 			return (const char *)data->Data();
318 
319 		data = data->Next();
320 	}
321 	return NULL;
322 }
323 
324 
325 status_t
326 Inode::GetNextSmallData(small_data **smallData)
327 {
328 	if (!fInode)
329 		return B_ERROR;
330 
331 	small_data *data = *smallData;
332 
333 	// begin from the start?
334 	if (data == NULL)
335 		data = fInode->small_data_start;
336 	else
337 		data = data->Next();
338 
339 	// is already last item?
340 	if (data->IsLast(fInode))
341 		return B_ENTRY_NOT_FOUND;
342 
343 	*smallData = data;
344 	return B_OK;
345 }
346 
347 
348 status_t
349 Inode::RewindAttributes()
350 {
351 	fCurrentSmallData = NULL;
352 
353 	if (fAttributes != NULL)
354 		fAttributes->Rewind();
355 
356 	return B_OK;
357 }
358 
359 
360 status_t
361 Inode::GetNextAttribute(char *name, uint32 *type, void **data, size_t *length)
362 {
363 	// read attributes out of the small data section
364 
365 	if (fCurrentSmallData == NULL || !fCurrentSmallData->IsLast(fInode)) {
366 		if (fCurrentSmallData == NULL)
367 			fCurrentSmallData = fInode->small_data_start;
368 		else
369 			fCurrentSmallData = fCurrentSmallData->Next();
370 
371 		// skip name attribute
372 		if (!fCurrentSmallData->IsLast(fInode)
373 			&& fCurrentSmallData->name_size == FILE_NAME_NAME_LENGTH
374 			&& *fCurrentSmallData->Name() == FILE_NAME_NAME)
375 			fCurrentSmallData = fCurrentSmallData->Next();
376 
377 		if (!fCurrentSmallData->IsLast(fInode)) {
378 			strncpy(name,fCurrentSmallData->Name(), B_FILE_NAME_LENGTH);
379 			*type = fCurrentSmallData->type;
380 			*data = fCurrentSmallData->Data();
381 			*length = fCurrentSmallData->data_size;
382 
383 			return B_OK;
384 		}
385 	}
386 
387 	// read attributes out of the attribute directory
388 
389 	if (Attributes().IsZero())
390 		return B_ENTRY_NOT_FOUND;
391 
392 	if (fAttributes == NULL)
393 		fAttributes = (Directory *)Inode::Factory(fDisk, Attributes());
394 
395 	status_t status = fAttributes ? fAttributes->InitCheck() : B_ERROR;
396 	if (status < B_OK)
397 		return status;
398 
399 	block_run run;
400 	status = fAttributes->GetNextEntry(name, &run);
401 	if (status < B_OK) {
402 		free(fAttributeBuffer);
403 		fAttributeBuffer = NULL;
404 		return status;
405 	}
406 
407 	Attribute *attribute = (Attribute *)Inode::Factory(fDisk, run);
408 	if (attribute == NULL || attribute->InitCheck() < B_OK)
409 		return B_IO_ERROR;
410 
411 	*type = attribute->Type();
412 
413 	void *buffer = realloc(fAttributeBuffer, attribute->Size());
414 	if (buffer == NULL) {
415 		free(fAttributeBuffer);
416 		fAttributeBuffer = NULL;
417 		delete attribute;
418 		return B_NO_MEMORY;
419 	}
420 	fAttributeBuffer = buffer;
421 
422 	ssize_t size =  attribute->Read(fAttributeBuffer, attribute->Size());
423 	delete attribute;
424 
425 	*length = size;
426 	*data = fAttributeBuffer;
427 
428 	return size < B_OK ? size : B_OK;
429 }
430 
431 
432 status_t
433 Inode::_FindPath(Inode::Source *source)
434 {
435 	BString path;
436 
437 	block_run parent = Parent();
438 	while (!parent.IsZero() && parent != fDisk->Root()) {
439 		Inode *inode;
440 		if (source)
441 			inode = source->InodeAt(parent);
442 		else
443 			inode = Inode::Factory(fDisk, parent);
444 
445 		if (inode == NULL
446 			|| inode->InitCheck() < B_OK
447 			|| inode->Name() == NULL
448 			|| !*inode->Name()) {
449 			BString sub;
450 			sub << "__recovered " << parent.allocation_group << ":"
451 				<< (int32)parent.start << "/";
452 			path.Prepend(sub);
453 
454 			delete inode;
455 			break;
456 		}
457 		parent = inode->Parent();
458 		path.Prepend("/");
459 		path.Prepend(inode->Name());
460 
461 		delete inode;
462 	}
463 	fPath = strdup(path.String());
464 
465 	return B_OK;
466 }
467 
468 
469 const char *
470 Inode::Path(Inode::Source *source)
471 {
472 	if (fPath == NULL)
473 		_FindPath(source);
474 
475 	return fPath;
476 }
477 
478 
479 status_t
480 Inode::CopyTo(const char *root, bool fullPath, Inode::Source *source)
481 {
482 	if (root == NULL)
483 		return B_ENTRY_NOT_FOUND;
484 
485 	BString path;
486 
487 	if (fullPath)
488 		path.Append(Path(source));
489 
490 	if (*(root + strlen(root) - 1) != '/')
491 		path.Prepend("/");
492 	path.Prepend(root);
493 
494 	return create_directory(path.String(), 0777);
495 }
496 
497 
498 status_t
499 Inode::CopyAttributesTo(BNode *node)
500 {
501 	// copy attributes
502 
503 	RewindAttributes();
504 
505 	char name[B_FILE_NAME_LENGTH];
506 	const uint32 kMaxBrokenAttributes = 64;
507 		// sanity max value
508 	uint32 count = 0;
509 	uint32 type;
510 	void *data;
511 	size_t size;
512 
513 	status_t status;
514 	while ((status = GetNextAttribute(name, &type, &data, &size))
515 			!= B_ENTRY_NOT_FOUND) {
516 		if (status != B_OK) {
517 			printf("could not open attribute (possibly: %s): %s!\n",
518 				name, strerror(status));
519 			if (count++ > kMaxBrokenAttributes)
520 				break;
521 
522 			continue;
523 		}
524 
525 		ssize_t written = node->WriteAttr(name, type, 0, data, size);
526 		if (written < B_OK) {
527 			printf("could not write attribute \"%s\": %s\n", name,
528 				strerror(written));
529 		} else if ((size_t)written < size) {
530 			printf("could only write %ld bytes (from %ld) at attribute \"%s\"\n",
531 				written, size, name);
532 		}
533 	}
534 
535 	// copy stats
536 
537 	node->SetPermissions(fInode->mode);
538 	node->SetOwner(fInode->uid);
539 	node->SetGroup(fInode->gid);
540 	node->SetModificationTime(fInode->last_modified_time >> 16);
541 	node->SetCreationTime(fInode->create_time >> 16);
542 
543 	return B_OK;
544 }
545 
546 
547 Inode *
548 Inode::Factory(Disk *disk, bfs_inode *inode, bool ownBuffer)
549 {
550 	// attributes (of a file)
551 	if ((inode->mode & (S_ATTR | S_ATTR_DIR)) == S_ATTR)
552 		return new Attribute(disk, inode, ownBuffer);
553 
554 	// directories, attribute directories, indices
555 	if (S_ISDIR(inode->mode) || inode->mode & S_ATTR_DIR)
556 		return new Directory(disk, inode, ownBuffer);
557 
558 	// regular files
559 	if (S_ISREG(inode->mode))
560 		return new File(disk, inode, ownBuffer);
561 
562 	// symlinks (short and link in data-stream)
563 	if (S_ISLNK(inode->mode))
564 		return new Symlink(disk, inode, ownBuffer);
565 
566 	return NULL;
567 }
568 
569 
570 Inode *
571 Inode::Factory(Disk *disk, block_run run)
572 {
573 	bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize());
574 	if (!inode)
575 		return NULL;
576 
577 	if (disk->ReadAt(disk->ToOffset(run), inode, disk->BlockSize()) <= 0)
578 		return NULL;
579 
580 	Inode *object = Factory(disk, inode);
581 	if (object == NULL)
582 		free(inode);
583 
584 	return object;
585 }
586 
587 
588 Inode *
589 Inode::Factory(Disk *disk, Inode *inode, bool copyBuffer)
590 {
591 	bfs_inode *inodeBuffer = inode->fInode;
592 
593 	if (copyBuffer) {
594 		bfs_inode *inodeCopy = (bfs_inode *)malloc(inodeBuffer->inode_size);
595 		if (!inodeCopy)
596 			return NULL;
597 
598 		memcpy(inodeCopy, inodeBuffer, inodeBuffer->inode_size);
599 		inodeBuffer = inodeCopy;
600 	}
601 	return Factory(disk, inodeBuffer, copyBuffer);
602 }
603 
604 
605 Inode *
606 Inode::EmptyInode(Disk *disk, const char *name, int32 mode)
607 {
608 	bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize());
609 	if (!inode)
610 		return NULL;
611 
612 	memset(inode, 0, sizeof(bfs_inode));
613 
614 	inode->magic1 = INODE_MAGIC1;
615 	inode->inode_size = disk->BlockSize();
616 	inode->mode = mode;
617 	inode->flags = INODE_IN_USE | (mode & S_IFDIR ? INODE_LOGGED : 0);
618 
619 	if (name) {
620 		small_data *data = inode->small_data_start;
621 		data->type = FILE_NAME_TYPE;
622 		data->name_size = FILE_NAME_NAME_LENGTH;
623 		*data->Name() = FILE_NAME_NAME;
624 		data->data_size = strlen(name);
625 		strcpy((char *)data->Data(), name);
626 	}
627 
628 	Inode *object = new (std::nothrow) Inode(disk, inode);
629 	if (object == NULL) {
630 		free(inode);
631 		return NULL;
632 	}
633 
634 	object->AcquireBuffer();
635 		// this must not be deleted anymore!
636 	return object;
637 }
638 
639 
640 //	#pragma mark -
641 
642 
643 DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer)
644 	: Inode(disk,inode,ownBuffer),
645 	fCurrent(-1),
646 	fPosition(0LL)
647 {
648 }
649 
650 
651 DataStream::DataStream(const Inode &inode)
652 	: Inode(inode),
653 	fCurrent(-1),
654 	fPosition(0LL)
655 {
656 }
657 
658 
659 DataStream::~DataStream()
660 {
661 }
662 
663 
664 status_t
665 DataStream::FindBlockRun(off_t pos)
666 {
667 	NodeGetter _(this);
668 
669 	if (pos > fInode->data.size)
670 		return B_ENTRY_NOT_FOUND;
671 
672 	if (fCurrent < 0)
673 		fLevel = 0;
674 
675 	fRunBlockEnd = fCurrent >= 0
676 		? fRunFileOffset + (fRun.length << fDisk->BlockShift()) : 0LL;
677 
678 	// access in current block run?
679 
680 	if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd)
681 		return B_OK;
682 
683 	// find matching block run
684 
685 	if (fInode->data.max_direct_range > 0
686 		&& pos >= fInode->data.max_direct_range) {
687 		if (fInode->data.max_double_indirect_range > 0
688 			&& pos >= fInode->data.max_indirect_range) {
689 			// read from double indirect blocks
690 
691 			//printf("find double indirect block: %ld,%d!\n",fInode->data.double_indirect.allocation_group,fInode->data.double_indirect.start);
692 			block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.double_indirect);
693 			if (indirect == NULL)
694 				return B_ERROR;
695 
696 			off_t start = pos - fInode->data.max_indirect_range;
697 			int32 indirectSize = fDisk->BlockSize() * 16 * (fDisk->BlockSize() / sizeof(block_run));
698 			int32 directSize = fDisk->BlockSize() * 4;
699 			int32 index = start / indirectSize;
700 
701 			//printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index);
702 			//printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start);
703 			indirect = (block_run *)fDisk->ReadBlockRun(indirect[index]);
704 			if (indirect == NULL)
705 				return B_ERROR;
706 
707 			fCurrent = (start % indirectSize) / directSize;
708 			fRunFileOffset = fInode->data.max_indirect_range + (index * indirectSize) + (fCurrent * directSize);
709 			fRunBlockEnd = fRunFileOffset + directSize;
710 			fRun = indirect[fCurrent];
711 			//printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
712 		} else {
713 			// access from indirect blocks
714 
715 			block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.indirect);
716 			if (!indirect)
717 				return B_ERROR;
718 
719 			int32 indirectRuns = (fInode->data.indirect.length << fDisk->BlockShift()) / sizeof(block_run);
720 
721 			if (fLevel != 1 || pos < fRunFileOffset) {
722 				fRunBlockEnd = fInode->data.max_direct_range;
723 				fCurrent = -1;
724 				fLevel = 1;
725 			}
726 
727 			while (++fCurrent < indirectRuns) {
728 				if (indirect[fCurrent].IsZero())
729 					break;
730 
731 				fRunFileOffset = fRunBlockEnd;
732 				fRunBlockEnd += indirect[fCurrent].length << fDisk->BlockShift();
733 				if (fRunBlockEnd > pos)
734 					break;
735 			}
736 			if (fCurrent == indirectRuns || indirect[fCurrent].IsZero())
737 				return B_ERROR;
738 
739 			fRun = indirect[fCurrent];
740 			//printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start);
741 			//printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
742 		}
743 	} else {
744 		// access from direct blocks
745 		if (fRunFileOffset > pos) {
746 			fRunBlockEnd = 0LL;
747 			fCurrent = -1;
748 		}
749 		fLevel = 0;
750 
751 		while (++fCurrent < NUM_DIRECT_BLOCKS) {
752 			if (fInode->data.direct[fCurrent].IsZero())
753 				break;
754 
755 			fRunFileOffset = fRunBlockEnd;
756 			fRunBlockEnd += fInode->data.direct[fCurrent].length << fDisk->BlockShift();
757 			if (fRunBlockEnd > pos)
758 				break;
759 		}
760 		if (fCurrent == NUM_DIRECT_BLOCKS || fInode->data.direct[fCurrent].IsZero())
761 			return B_ERROR;
762 
763 		fRun = fInode->data.direct[fCurrent];
764 		//printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
765 	}
766 	return B_OK;
767 }
768 
769 
770 ssize_t
771 DataStream::ReadAt(off_t pos, void *buffer, size_t size)
772 {
773 	NodeGetter _(this);
774 
775 	//printf("DataStream::ReadAt(pos = %Ld,buffer = %p,size = %ld);\n",pos,buffer,size);
776 	// truncate size to read
777 	if (pos + (off_t)size > fInode->data.size) {
778 		if (pos > fInode->data.size)	// reading outside the file
779 			return B_ERROR;
780 
781 		size = fInode->data.size - pos;
782 		if (!size)	// there is nothing left to read
783 			return 0;
784 	}
785 	ssize_t read = 0;
786 
787 	//printf("### read %ld bytes at %Ld\n",size,pos);
788 	while (size > 0) {
789 		status_t status = FindBlockRun(pos);
790 		if (status < B_OK)
791 			return status;
792 
793 		ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
794 
795 		//printf("### read %ld bytes from %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset);
796 		bytes = fDisk->ReadAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,
797 			buffer, bytes);
798 		if (bytes <= 0) {
799 			if (bytes == 0) {
800 				printf("could not read bytes at: %" B_PRId32 ",%d\n",
801 					fRun.allocation_group, fRun.start);
802 			}
803 			return bytes < 0 ? bytes : B_BAD_DATA;
804 		}
805 
806 		buffer = (void *)((uint8 *)buffer + bytes);
807 		size -= bytes;
808 		pos += bytes;
809 		read += bytes;
810 	}
811 	if (read >= 0)
812 		return read;
813 
814 	return B_IO_ERROR;
815 }
816 
817 
818 ssize_t
819 DataStream::WriteAt(off_t pos, const void *buffer, size_t size)
820 {
821 	NodeGetter _(this);
822 
823 	// FIXME: truncate size -> should enlargen the file
824 	if (pos + (off_t)size > fInode->data.size) {
825 		if (pos > fInode->data.size)	// writing outside the file
826 			return B_ERROR;
827 
828 		size = fInode->data.size - pos;
829 		if (!size)	// there is nothing left to write
830 			return 0;
831 	}
832 	ssize_t written = 0;
833 
834 	//printf("### write %ld bytes at %Ld\n",size,pos);
835 	while (size > 0) {
836 		status_t status = FindBlockRun(pos);
837 		if (status < B_OK)
838 			return status;
839 
840 		ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
841 
842 		//printf("### write %ld bytes to %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset);
843 		bytes = fDisk->WriteAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,buffer,bytes);
844 		if (bytes < 0)
845 			return bytes;
846 
847 		buffer = (void *)((uint8 *)buffer + bytes);
848 		size -= bytes;
849 		pos += bytes;
850 		written += bytes;
851 	}
852 	if (written >= 0)
853 		return written;
854 
855 	return B_IO_ERROR;
856 }
857 
858 
859 off_t
860 DataStream::Seek(off_t position, uint32 seekMode)
861 {
862 	NodeGetter _(this);
863 
864 	if (seekMode == SEEK_SET)
865 		fPosition = position;
866 	else if (seekMode == SEEK_END)
867 		fPosition = fInode->data.size + position;
868 	else
869 		fPosition += position;
870 
871 	return fPosition;
872 }
873 
874 
875 off_t
876 DataStream::Position() const
877 {
878 	return fPosition;
879 }
880 
881 
882 status_t
883 DataStream::SetSize(off_t size)
884 {
885 	NodeGetter _(this);
886 
887 	// FIXME: not yet supported
888 	if (size > fInode->data.size || size > fInode->data.max_direct_range)
889 		return B_ERROR;
890 
891 	if (size == fInode->data.size)
892 		return B_OK;
893 
894 	BufferClobbered();
895 
896 	fInode->data.size = size;
897 	fInode->data.max_direct_range = size;
898 	fInode->data.max_indirect_range = 0;
899 	fInode->data.max_double_indirect_range = 0;
900 
901 	for (int32 i = 0;i < NUM_DIRECT_BLOCKS;i++) {
902 		if (size <= 0)
903 			fInode->data.direct[i].SetTo(0, 0, 0);
904 		else if ((fInode->data.direct[i].length << fDisk->BlockShift()) >= size) {
905 			off_t blocks = (size + fDisk->BlockSize() - 1) / fDisk->BlockSize();
906 			fInode->data.direct[i].length = blocks;
907 			size = 0;
908 		} else
909 			size -= fInode->data.direct[i].length << fDisk->BlockShift();
910 	}
911 
912 	return B_OK;
913 }
914 
915 
916 //	#pragma mark -
917 
918 
919 File::File(Disk *disk, bfs_inode *inode,bool ownBuffer)
920 	: DataStream(disk,inode,ownBuffer)
921 {
922 }
923 
924 
925 File::File(const Inode &inode)
926 	: DataStream(inode)
927 {
928 }
929 
930 
931 File::~File()
932 {
933 }
934 
935 
936 status_t
937 File::InitCheck() const
938 {
939 	status_t status = DataStream::InitCheck();
940 	if (status == B_OK)
941 		return IsFile() ? B_OK : B_ERROR;
942 
943 	return status;
944 }
945 
946 
947 status_t
948 File::CopyTo(const char *root, bool fullPath, Inode::Source *source)
949 {
950 	status_t status = Inode::CopyTo(root, fullPath, source);
951 	if (status < B_OK)
952 		return status;
953 
954 	BPath path(root);
955 	if (fullPath && Path(source))
956 		path.Append(Path(source));
957 
958 	char *name = (char *)Name();
959 	if (name != NULL) {
960 		// changes the filename in the inode buffer (for deleted entries)
961 		if (!*name)
962 			*name = '_';
963 		path.Append(name);
964 	} else {
965 		BString sub;
966 		sub << "__untitled " << BlockRun().allocation_group << ":"
967 			<< (int32)BlockRun().start;
968 		path.Append(sub.String());
969 	}
970 	printf("%" B_PRId32 ",%d -> %s\n", BlockRun().allocation_group,
971 		BlockRun().start, path.Path());
972 
973 	BFile file;
974 	status = file.SetTo(path.Path(),
975 		B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS);
976 	if (status < B_OK)
977 		return status;
978 
979 	char buffer[fDisk->BlockSize()];
980 	ssize_t size;
981 	Seek(0, SEEK_SET);
982 
983 	while ((size = Read(buffer, sizeof(buffer))) > B_OK) {
984 		ssize_t written = file.Write(buffer, size);
985 		if (written < B_OK)
986 			return written;
987 	}
988 
989 	return CopyAttributesTo(&file);
990 }
991 
992 
993 //	#pragma mark -
994 
995 
996 Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer)
997 	: File(disk, inode, ownBuffer)
998 {
999 }
1000 
1001 
1002 Attribute::Attribute(const Inode &inode)
1003 	: File(inode)
1004 {
1005 }
1006 
1007 
1008 Attribute::~Attribute()
1009 {
1010 }
1011 
1012 
1013 status_t
1014 Attribute::InitCheck() const
1015 {
1016 	status_t status = DataStream::InitCheck();
1017 	if (status == B_OK)
1018 		return IsAttribute() ? B_OK : B_ERROR;
1019 
1020 	return status;
1021 }
1022 
1023 
1024 status_t
1025 Attribute::CopyTo(const char */*path*/, bool /*fullPath*/,
1026 	Inode::Source */*source*/)
1027 {
1028 	// files and directories already copy all attributes
1029 
1030 	// eventually, this method should be implemented to recover lost
1031 	// attributes on the disk
1032 
1033 	return B_OK;
1034 }
1035 
1036 
1037 //	#pragma mark -
1038 
1039 
1040 Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer)
1041 	: DataStream(disk, inode, ownBuffer),
1042 	fTree(NULL)
1043 {
1044 }
1045 
1046 
1047 Directory::Directory(const Inode &inode)
1048 	: DataStream(inode),
1049 	fTree(NULL)
1050 {
1051 }
1052 
1053 
1054 Directory::~Directory()
1055 {
1056 	delete fTree;
1057 }
1058 
1059 
1060 status_t
1061 Directory::InitCheck() const
1062 {
1063 	status_t status = DataStream::InitCheck();
1064 	if (status == B_OK)
1065 		return (IsDirectory() || IsAttributeDirectory()) ? B_OK : B_ERROR;
1066 
1067 	return status;
1068 }
1069 
1070 
1071 status_t
1072 Directory::CopyTo(const char *root, bool fullPath, Inode::Source *source)
1073 {
1074 	// don't copy attributes or indices
1075 	// the recovery program should make empty files to recover lost attributes
1076 	if (IsAttributeDirectory() || IsIndex())
1077 		return B_OK;
1078 
1079 	status_t status = Inode::CopyTo(root, fullPath, source);
1080 	if (status < B_OK)
1081 		return status;
1082 
1083 	BPath path(root);
1084 	if (fullPath && Path(source))
1085 		path.Append(Path(source));
1086 
1087 	char *name = (char *)Name();
1088 	if (name != NULL) {
1089 		// changes the filename in the inode buffer (for deleted entries)
1090 		if (!*name)
1091 			*name = '_';
1092 		path.Append(name);
1093 	} else {
1094 		// create unique name
1095 		BString sub;
1096 		sub << "__untitled " << BlockRun().allocation_group << ":"
1097 			<< (int32)BlockRun().start;
1098 		path.Append(sub.String());
1099 	}
1100 
1101 	BEntry entry(path.Path());
1102 	BDirectory directory;
1103 	if ((status = entry.GetParent(&directory)) < B_OK)
1104 		return status;
1105 
1106 	status = directory.CreateDirectory(path.Leaf(), NULL);
1107 	if (status < B_OK && status != B_FILE_EXISTS)
1108 		return status;
1109 
1110 	if ((status = directory.SetTo(&entry)) < B_OK)
1111 		return status;
1112 
1113 	return CopyAttributesTo(&directory);
1114 }
1115 
1116 
1117 status_t
1118 Directory::Rewind()
1119 {
1120 	if (!fTree) {
1121 		status_t status = CreateTree();
1122 		if (status < B_OK)
1123 			return status;
1124 	}
1125 	return fTree->Rewind();
1126 }
1127 
1128 
1129 status_t
1130 Directory::GetNextEntry(char *name, block_run *run)
1131 {
1132 	status_t status;
1133 
1134 	if (!fTree) {
1135 		if ((status = Rewind()) < B_OK)
1136 			return status;
1137 	}
1138 	uint16 length;
1139 	off_t offset;
1140 
1141 	if ((status = fTree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH - 1,
1142 			&offset)) < B_OK)
1143 		return status;
1144 
1145 	*run = fDisk->ToBlockRun(offset);
1146 
1147 	return B_OK;
1148 }
1149 
1150 
1151 status_t
1152 Directory::GetNextEntry(block_run *run)
1153 {
1154 	char name[B_FILE_NAME_LENGTH];
1155 
1156 	return GetNextEntry(name, run);
1157 }
1158 
1159 
1160 status_t
1161 Directory::Contains(const block_run *run)
1162 {
1163 	status_t status;
1164 
1165 	if (!fTree) {
1166 		if ((status = Rewind()) < B_OK)
1167 			return status;
1168 	}
1169 
1170 	block_run searchRun;
1171 	while (GetNextEntry(&searchRun) == B_OK) {
1172 		if (searchRun == *run)
1173 			return B_OK;
1174 	}
1175 
1176 	return B_ENTRY_NOT_FOUND;
1177 }
1178 
1179 
1180 status_t
1181 Directory::Contains(const Inode *inode)
1182 {
1183 	status_t status;
1184 
1185 	if (!fTree) {
1186 		if ((status = CreateTree()) < B_OK)
1187 			return status;
1188 	}
1189 
1190 	off_t value;
1191 	const char *name = inode->Name();
1192 	status = B_ENTRY_NOT_FOUND;
1193 
1194 	if (name && (status = fTree->Find((uint8 *)name, (uint16)strlen(name),
1195 			&value)) == B_OK) {
1196 		if (fDisk->ToBlockRun(value) == inode->InodeBuffer()->inode_num)
1197 			return B_OK;
1198 
1199 		printf("inode address do not match (%s)!\n", inode->Name());
1200 	}
1201 
1202 	if (status != B_OK && status != B_ENTRY_NOT_FOUND)
1203 		return status;
1204 
1205 	return Contains(&inode->InodeBuffer()->inode_num);
1206 }
1207 
1208 
1209 status_t
1210 Directory::FindEntry(const char *name, block_run *run)
1211 {
1212 	status_t status;
1213 
1214 	if (!name)
1215 		return B_BAD_VALUE;
1216 
1217 	if (!fTree) {
1218 		if ((status = CreateTree()) < B_OK)
1219 			return status;
1220 	}
1221 
1222 	off_t value;
1223 
1224 	if ((status = fTree->Find((uint8 *)name, (uint16)strlen(name),
1225 			&value)) >= B_OK) {
1226 		if (run)
1227 			*run = fDisk->ToBlockRun(value);
1228 		return B_OK;
1229 	}
1230 	return status;
1231 }
1232 
1233 
1234 status_t
1235 Directory::AddEntry(Inode *inode)
1236 {
1237 	status_t status;
1238 	bool created = false;
1239 
1240 	if (!fTree) {
1241 		status = CreateTree();
1242 		if (status == B_OK)
1243 			status = fTree->Validate();
1244 
1245 		if (status == B_BAD_DATA) {
1246 			//puts("bplustree corrupted!");
1247 			fTree = new BPlusTree(BPLUSTREE_STRING_TYPE, BPLUSTREE_NODE_SIZE,
1248 				false);
1249 			if ((status = fTree->InitCheck()) < B_OK) {
1250 				delete fTree;
1251 				fTree = NULL;
1252 			} else
1253 				created = true;
1254 		}
1255 
1256 		if (status < B_OK)
1257 			return status;
1258 	}
1259 	// keep all changes in memory
1260 	fTree->SetHoldChanges(true);
1261 
1262 	if (created) {
1263 		// add . and ..
1264 		fTree->Insert(".", Block());
1265 		fTree->Insert("..", fDisk->ToBlock(Parent()));
1266 	}
1267 
1268 	if (inode->Flags() & INODE_DELETED)
1269 		return B_ENTRY_NOT_FOUND;
1270 
1271 	BString name = inode->Name();
1272 	if (name == "") {
1273 		name << "__file " << inode->BlockRun().allocation_group << ":"
1274 			<< (int32)inode->BlockRun().start;
1275 	}
1276 
1277 	return fTree->Insert(name.String(), inode->Block());
1278 }
1279 
1280 
1281 status_t
1282 Directory::CreateTree()
1283 {
1284 	fTree = new BPlusTree(this);
1285 
1286 	status_t status = fTree->InitCheck();
1287 	if (status < B_OK) {
1288 		delete fTree;
1289 		fTree = NULL;
1290 		return status;
1291 	}
1292 	return B_OK;
1293 }
1294 
1295 
1296 status_t
1297 Directory::GetTree(BPlusTree **tree)
1298 {
1299 	if (!fTree) {
1300 		status_t status = CreateTree();
1301 		if (status < B_OK)
1302 			return status;
1303 	}
1304 	*tree = fTree;
1305 	return B_OK;
1306 }
1307 
1308 
1309 //	#pragma mark -
1310 
1311 
1312 Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer)
1313 	: Inode(disk,inode,ownBuffer)
1314 {
1315 }
1316 
1317 
1318 Symlink::Symlink(const Inode &inode)
1319 	: Inode(inode)
1320 {
1321 }
1322 
1323 
1324 Symlink::~Symlink()
1325 {
1326 }
1327 
1328 
1329 status_t
1330 Symlink::InitCheck() const
1331 {
1332 	status_t status = Inode::InitCheck();
1333 	if (status == B_OK)
1334 		return IsSymlink() ? B_OK : B_ERROR;
1335 
1336 	return status;
1337 }
1338 
1339 
1340 status_t
1341 Symlink::CopyTo(const char *root, bool fullPath,Inode::Source *source)
1342 {
1343 	status_t status = Inode::CopyTo(root,fullPath,source);
1344 	if (status < B_OK)
1345 		return status;
1346 
1347 	BPath path(root);
1348 	if (fullPath && Path(source))
1349 		path.Append(Path(source));
1350 
1351 	char *name = (char *)Name();
1352 	if (name != NULL) {
1353 		// changes the filename in the inode buffer (for deleted entries)
1354 		if (!*name)
1355 			*name = '_';
1356 		path.Append(name);
1357 	} else {
1358 		// create unique name
1359 		BString sub;
1360 		sub << "__symlink " << BlockRun().allocation_group << ":"
1361 			<< (int32)BlockRun().start;
1362 		path.Append(sub.String());
1363 	}
1364 
1365 	BEntry entry(path.Path());
1366 	BDirectory directory;
1367 	if ((status = entry.GetParent(&directory)) < B_OK)
1368 		return status;
1369 
1370 	char to[2048];
1371 	if (LinksTo(to,sizeof(to)) < B_OK)
1372 		return B_ERROR;
1373 
1374 	BSymLink link;
1375 	status = directory.CreateSymLink(path.Leaf(),to,&link);
1376 	if (status < B_OK && status != B_FILE_EXISTS)
1377 		return status;
1378 
1379 	if ((status = link.SetTo(&entry)) < B_OK)
1380 		return status;
1381 
1382 	return CopyAttributesTo(&link);
1383 }
1384 
1385 
1386 status_t
1387 Symlink::LinksTo(char *to,size_t maxLength)
1388 {
1389 	if ((fInode->flags & INODE_LONG_SYMLINK) == 0) {
1390 		strcpy(to,fInode->short_symlink);
1391 		return B_OK;
1392 	}
1393 
1394 	DataStream stream(*this);
1395 	status_t status = stream.InitCheck();
1396 	if (status < B_OK)
1397 		return status;
1398 
1399 	status = stream.Read(to,maxLength);
1400 
1401 	return status < B_OK ? status : B_OK;
1402 }
1403 
1404