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