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