xref: /haiku/src/bin/bfs_tools/lib/Inode.cpp (revision 040a81419dda83d1014e9dc94936a4cb3f027303)
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 
627 	object->AcquireBuffer();
628 		// this must not be deleted anymore!
629 	return object;
630 }
631 
632 
633 //	#pragma mark -
634 
635 
636 DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer)
637 	: Inode(disk,inode,ownBuffer),
638 	fCurrent(-1),
639 	fPosition(0LL)
640 {
641 }
642 
643 
644 DataStream::DataStream(const Inode &inode)
645 	: Inode(inode),
646 	fCurrent(-1),
647 	fPosition(0LL)
648 {
649 }
650 
651 
652 DataStream::~DataStream()
653 {
654 }
655 
656 
657 status_t
658 DataStream::FindBlockRun(off_t pos)
659 {
660 	NodeGetter _(this);
661 
662 	if (pos > fInode->data.size)
663 		return B_ENTRY_NOT_FOUND;
664 
665 	if (fCurrent < 0)
666 		fLevel = 0;
667 
668 	fRunBlockEnd = fCurrent >= 0
669 		? fRunFileOffset + (fRun.length << fDisk->BlockShift()) : 0LL;
670 
671 	// access in current block run?
672 
673 	if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd)
674 		return B_OK;
675 
676 	// find matching block run
677 
678 	if (fInode->data.max_direct_range > 0
679 		&& pos >= fInode->data.max_direct_range) {
680 		if (fInode->data.max_double_indirect_range > 0
681 			&& pos >= fInode->data.max_indirect_range) {
682 			// read from double indirect blocks
683 
684 			//printf("find double indirect block: %ld,%d!\n",fInode->data.double_indirect.allocation_group,fInode->data.double_indirect.start);
685 			block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.double_indirect);
686 			if (indirect == NULL)
687 				return B_ERROR;
688 
689 			off_t start = pos - fInode->data.max_indirect_range;
690 			int32 indirectSize = fDisk->BlockSize() * 16 * (fDisk->BlockSize() / sizeof(block_run));
691 			int32 directSize = fDisk->BlockSize() * 4;
692 			int32 index = start / indirectSize;
693 
694 			//printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index);
695 			//printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start);
696 			indirect = (block_run *)fDisk->ReadBlockRun(indirect[index]);
697 			if (indirect == NULL)
698 				return B_ERROR;
699 
700 			fCurrent = (start % indirectSize) / directSize;
701 			fRunFileOffset = fInode->data.max_indirect_range + (index * indirectSize) + (fCurrent * directSize);
702 			fRunBlockEnd = fRunFileOffset + directSize;
703 			fRun = indirect[fCurrent];
704 			//printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
705 		} else {
706 			// access from indirect blocks
707 
708 			block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.indirect);
709 			if (!indirect)
710 				return B_ERROR;
711 
712 			int32 indirectRuns = (fInode->data.indirect.length << fDisk->BlockShift()) / sizeof(block_run);
713 
714 			if (fLevel != 1 || pos < fRunFileOffset) {
715 				fRunBlockEnd = fInode->data.max_direct_range;
716 				fCurrent = -1;
717 				fLevel = 1;
718 			}
719 
720 			while (++fCurrent < indirectRuns) {
721 				if (indirect[fCurrent].IsZero())
722 					break;
723 
724 				fRunFileOffset = fRunBlockEnd;
725 				fRunBlockEnd += indirect[fCurrent].length << fDisk->BlockShift();
726 				if (fRunBlockEnd > pos)
727 					break;
728 			}
729 			if (fCurrent == indirectRuns || indirect[fCurrent].IsZero())
730 				return B_ERROR;
731 
732 			fRun = indirect[fCurrent];
733 			//printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start);
734 			//printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
735 		}
736 	} else {
737 		// access from direct blocks
738 		if (fRunFileOffset > pos) {
739 			fRunBlockEnd = 0LL;
740 			fCurrent = -1;
741 		}
742 		fLevel = 0;
743 
744 		while (++fCurrent < NUM_DIRECT_BLOCKS) {
745 			if (fInode->data.direct[fCurrent].IsZero())
746 				break;
747 
748 			fRunFileOffset = fRunBlockEnd;
749 			fRunBlockEnd += fInode->data.direct[fCurrent].length << fDisk->BlockShift();
750 			if (fRunBlockEnd > pos)
751 				break;
752 		}
753 		if (fCurrent == NUM_DIRECT_BLOCKS || fInode->data.direct[fCurrent].IsZero())
754 			return B_ERROR;
755 
756 		fRun = fInode->data.direct[fCurrent];
757 		//printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
758 	}
759 	return B_OK;
760 }
761 
762 
763 ssize_t
764 DataStream::ReadAt(off_t pos, void *buffer, size_t size)
765 {
766 	NodeGetter _(this);
767 
768 	//printf("DataStream::ReadAt(pos = %Ld,buffer = %p,size = %ld);\n",pos,buffer,size);
769 	// truncate size to read
770 	if (pos + (off_t)size > fInode->data.size) {
771 		if (pos > fInode->data.size)	// reading outside the file
772 			return B_ERROR;
773 
774 		size = fInode->data.size - pos;
775 		if (!size)	// there is nothing left to read
776 			return 0;
777 	}
778 	ssize_t read = 0;
779 
780 	//printf("### read %ld bytes at %Ld\n",size,pos);
781 	while (size > 0) {
782 		status_t status = FindBlockRun(pos);
783 		if (status < B_OK)
784 			return status;
785 
786 		ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
787 
788 		//printf("### read %ld bytes from %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset);
789 		bytes = fDisk->ReadAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,
790 			buffer, bytes);
791 		if (bytes <= 0) {
792 			if (bytes == 0) {
793 				printf("could not read bytes at: %" B_PRId32 ",%d\n",
794 					fRun.allocation_group, fRun.start);
795 			}
796 			return bytes < 0 ? bytes : B_BAD_DATA;
797 		}
798 
799 		buffer = (void *)((uint8 *)buffer + bytes);
800 		size -= bytes;
801 		pos += bytes;
802 		read += bytes;
803 	}
804 	if (read >= 0)
805 		return read;
806 
807 	return B_IO_ERROR;
808 }
809 
810 
811 ssize_t
812 DataStream::WriteAt(off_t pos, const void *buffer, size_t size)
813 {
814 	NodeGetter _(this);
815 
816 	// FIXME: truncate size -> should enlargen the file
817 	if (pos + (off_t)size > fInode->data.size) {
818 		if (pos > fInode->data.size)	// writing outside the file
819 			return B_ERROR;
820 
821 		size = fInode->data.size - pos;
822 		if (!size)	// there is nothing left to write
823 			return 0;
824 	}
825 	ssize_t written = 0;
826 
827 	//printf("### write %ld bytes at %Ld\n",size,pos);
828 	while (size > 0) {
829 		status_t status = FindBlockRun(pos);
830 		if (status < B_OK)
831 			return status;
832 
833 		ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
834 
835 		//printf("### write %ld bytes to %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset);
836 		bytes = fDisk->WriteAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,buffer,bytes);
837 		if (bytes < 0)
838 			return bytes;
839 
840 		buffer = (void *)((uint8 *)buffer + bytes);
841 		size -= bytes;
842 		pos += bytes;
843 		written += bytes;
844 	}
845 	if (written >= 0)
846 		return written;
847 
848 	return B_IO_ERROR;
849 }
850 
851 
852 off_t
853 DataStream::Seek(off_t position, uint32 seekMode)
854 {
855 	NodeGetter _(this);
856 
857 	if (seekMode == SEEK_SET)
858 		fPosition = position;
859 	else if (seekMode == SEEK_END)
860 		fPosition = fInode->data.size + position;
861 	else
862 		fPosition += position;
863 
864 	return fPosition;
865 }
866 
867 
868 off_t
869 DataStream::Position() const
870 {
871 	return fPosition;
872 }
873 
874 
875 status_t
876 DataStream::SetSize(off_t size)
877 {
878 	NodeGetter _(this);
879 
880 	// FIXME: not yet supported
881 	if (size > fInode->data.size || size > fInode->data.max_direct_range)
882 		return B_ERROR;
883 
884 	if (size == fInode->data.size)
885 		return B_OK;
886 
887 	BufferClobbered();
888 
889 	fInode->data.size = size;
890 	fInode->data.max_direct_range = size;
891 	fInode->data.max_indirect_range = 0;
892 	fInode->data.max_double_indirect_range = 0;
893 
894 	for (int32 i = 0;i < NUM_DIRECT_BLOCKS;i++) {
895 		if (size <= 0)
896 			fInode->data.direct[i].SetTo(0, 0, 0);
897 		else if ((fInode->data.direct[i].length << fDisk->BlockShift()) >= size) {
898 			off_t blocks = (size + fDisk->BlockSize() - 1) / fDisk->BlockSize();
899 			fInode->data.direct[i].length = blocks;
900 			size = 0;
901 		} else
902 			size -= fInode->data.direct[i].length << fDisk->BlockShift();
903 	}
904 
905 	return B_OK;
906 }
907 
908 
909 //	#pragma mark -
910 
911 
912 File::File(Disk *disk, bfs_inode *inode,bool ownBuffer)
913 	: DataStream(disk,inode,ownBuffer)
914 {
915 }
916 
917 
918 File::File(const Inode &inode)
919 	: DataStream(inode)
920 {
921 }
922 
923 
924 File::~File()
925 {
926 }
927 
928 
929 status_t
930 File::InitCheck()
931 {
932 	status_t status = DataStream::InitCheck();
933 	if (status == B_OK)
934 		return IsFile() ? B_OK : B_ERROR;
935 
936 	return status;
937 }
938 
939 
940 status_t
941 File::CopyTo(const char *root, bool fullPath, Inode::Source *source)
942 {
943 	status_t status = Inode::CopyTo(root, fullPath, source);
944 	if (status < B_OK)
945 		return status;
946 
947 	BPath path(root);
948 	if (fullPath && Path(source))
949 		path.Append(Path(source));
950 
951 	char *name = (char *)Name();
952 	if (name != NULL) {
953 		// changes the filename in the inode buffer (for deleted entries)
954 		if (!*name)
955 			*name = '_';
956 		path.Append(name);
957 	} else {
958 		BString sub;
959 		sub << "__untitled " << BlockRun().allocation_group << ":"
960 			<< (int32)BlockRun().start;
961 		path.Append(sub.String());
962 	}
963 	printf("%" B_PRId32 ",%d -> %s\n", BlockRun().allocation_group,
964 		BlockRun().start, path.Path());
965 
966 	BFile file;
967 	status = file.SetTo(path.Path(),
968 		B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS);
969 	if (status < B_OK)
970 		return status;
971 
972 	char buffer[fDisk->BlockSize()];
973 	ssize_t size;
974 	Seek(0, SEEK_SET);
975 
976 	while ((size = Read(buffer, sizeof(buffer))) > B_OK) {
977 		ssize_t written = file.Write(buffer, size);
978 		if (written < B_OK)
979 			return written;
980 	}
981 
982 	return CopyAttributesTo(&file);
983 }
984 
985 
986 //	#pragma mark -
987 
988 
989 Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer)
990 	: File(disk, inode, ownBuffer)
991 {
992 }
993 
994 
995 Attribute::Attribute(const Inode &inode)
996 	: File(inode)
997 {
998 }
999 
1000 
1001 Attribute::~Attribute()
1002 {
1003 }
1004 
1005 
1006 status_t
1007 Attribute::InitCheck()
1008 {
1009 	status_t status = DataStream::InitCheck();
1010 	if (status == B_OK)
1011 		return IsAttribute() ? B_OK : B_ERROR;
1012 
1013 	return status;
1014 }
1015 
1016 
1017 status_t
1018 Attribute::CopyTo(const char */*path*/, bool /*fullPath*/,
1019 	Inode::Source */*source*/)
1020 {
1021 	// files and directories already copy all attributes
1022 
1023 	// eventually, this method should be implemented to recover lost
1024 	// attributes on the disk
1025 
1026 	return B_OK;
1027 }
1028 
1029 
1030 //	#pragma mark -
1031 
1032 
1033 Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer)
1034 	: DataStream(disk, inode, ownBuffer),
1035 	fTree(NULL)
1036 {
1037 }
1038 
1039 
1040 Directory::Directory(const Inode &inode)
1041 	: DataStream(inode),
1042 	fTree(NULL)
1043 {
1044 }
1045 
1046 
1047 Directory::~Directory()
1048 {
1049 	delete fTree;
1050 }
1051 
1052 
1053 status_t
1054 Directory::InitCheck()
1055 {
1056 	status_t status = DataStream::InitCheck();
1057 	if (status == B_OK)
1058 		return (IsDirectory() || IsAttributeDirectory()) ? B_OK : B_ERROR;
1059 
1060 	return status;
1061 }
1062 
1063 
1064 status_t
1065 Directory::CopyTo(const char *root, bool fullPath, Inode::Source *source)
1066 {
1067 	// don't copy attributes or indices
1068 	// the recovery program should make empty files to recover lost attributes
1069 	if (IsAttributeDirectory() || IsIndex())
1070 		return B_OK;
1071 
1072 	status_t status = Inode::CopyTo(root, fullPath, source);
1073 	if (status < B_OK)
1074 		return status;
1075 
1076 	BPath path(root);
1077 	if (fullPath && Path(source))
1078 		path.Append(Path(source));
1079 
1080 	char *name = (char *)Name();
1081 	if (name != NULL) {
1082 		// changes the filename in the inode buffer (for deleted entries)
1083 		if (!*name)
1084 			*name = '_';
1085 		path.Append(name);
1086 	} else {
1087 		// create unique name
1088 		BString sub;
1089 		sub << "__untitled " << BlockRun().allocation_group << ":"
1090 			<< (int32)BlockRun().start;
1091 		path.Append(sub.String());
1092 	}
1093 
1094 	BEntry entry(path.Path());
1095 	BDirectory directory;
1096 	if ((status = entry.GetParent(&directory)) < B_OK)
1097 		return status;
1098 
1099 	status = directory.CreateDirectory(path.Leaf(), NULL);
1100 	if (status < B_OK && status != B_FILE_EXISTS)
1101 		return status;
1102 
1103 	if ((status = directory.SetTo(&entry)) < B_OK)
1104 		return status;
1105 
1106 	return CopyAttributesTo(&directory);
1107 }
1108 
1109 
1110 status_t
1111 Directory::Rewind()
1112 {
1113 	if (!fTree) {
1114 		status_t status = CreateTree();
1115 		if (status < B_OK)
1116 			return status;
1117 	}
1118 	return fTree->Rewind();
1119 }
1120 
1121 
1122 status_t
1123 Directory::GetNextEntry(char *name, block_run *run)
1124 {
1125 	status_t status;
1126 
1127 	if (!fTree) {
1128 		if ((status = Rewind()) < B_OK)
1129 			return status;
1130 	}
1131 	uint16 length;
1132 	off_t offset;
1133 
1134 	if ((status = fTree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH - 1,
1135 			&offset)) < B_OK)
1136 		return status;
1137 
1138 	*run = fDisk->ToBlockRun(offset);
1139 
1140 	return B_OK;
1141 }
1142 
1143 
1144 status_t
1145 Directory::GetNextEntry(block_run *run)
1146 {
1147 	char name[B_FILE_NAME_LENGTH];
1148 
1149 	return GetNextEntry(name, run);
1150 }
1151 
1152 
1153 status_t
1154 Directory::Contains(const block_run *run)
1155 {
1156 	status_t status;
1157 
1158 	if (!fTree) {
1159 		if ((status = Rewind()) < B_OK)
1160 			return status;
1161 	}
1162 
1163 	block_run searchRun;
1164 	while (GetNextEntry(&searchRun) == B_OK) {
1165 		if (searchRun == *run)
1166 			return B_OK;
1167 	}
1168 
1169 	return B_ENTRY_NOT_FOUND;
1170 }
1171 
1172 
1173 status_t
1174 Directory::Contains(const Inode *inode)
1175 {
1176 	status_t status;
1177 
1178 	if (!fTree) {
1179 		if ((status = CreateTree()) < B_OK)
1180 			return status;
1181 	}
1182 
1183 	off_t value;
1184 	const char *name = inode->Name();
1185 	status = B_ENTRY_NOT_FOUND;
1186 
1187 	if (name && (status = fTree->Find((uint8 *)name, (uint16)strlen(name),
1188 			&value)) == B_OK) {
1189 		if (fDisk->ToBlockRun(value) == inode->InodeBuffer()->inode_num)
1190 			return B_OK;
1191 
1192 		printf("inode address do not match (%s)!\n", inode->Name());
1193 	}
1194 
1195 	if (status != B_OK && status != B_ENTRY_NOT_FOUND)
1196 		return status;
1197 
1198 	return Contains(&inode->InodeBuffer()->inode_num);
1199 }
1200 
1201 
1202 status_t
1203 Directory::FindEntry(const char *name, block_run *run)
1204 {
1205 	status_t status;
1206 
1207 	if (!name)
1208 		return B_BAD_VALUE;
1209 
1210 	if (!fTree) {
1211 		if ((status = CreateTree()) < B_OK)
1212 			return status;
1213 	}
1214 
1215 	off_t value;
1216 
1217 	if ((status = fTree->Find((uint8 *)name, (uint16)strlen(name),
1218 			&value)) >= B_OK) {
1219 		if (run)
1220 			*run = fDisk->ToBlockRun(value);
1221 		return B_OK;
1222 	}
1223 	return status;
1224 }
1225 
1226 
1227 status_t
1228 Directory::AddEntry(Inode *inode)
1229 {
1230 	status_t status;
1231 	bool created = false;
1232 
1233 	if (!fTree) {
1234 		status = CreateTree();
1235 		if (status == B_OK)
1236 			status = fTree->Validate();
1237 
1238 		if (status == B_BAD_DATA) {
1239 			//puts("bplustree corrupted!");
1240 			fTree = new BPlusTree(BPLUSTREE_STRING_TYPE, BPLUSTREE_NODE_SIZE,
1241 				false);
1242 			if ((status = fTree->InitCheck()) < B_OK) {
1243 				delete fTree;
1244 				fTree = NULL;
1245 			} else
1246 				created = true;
1247 		}
1248 
1249 		if (status < B_OK)
1250 			return status;
1251 	}
1252 	// keep all changes in memory
1253 	fTree->SetHoldChanges(true);
1254 
1255 	if (created) {
1256 		// add . and ..
1257 		fTree->Insert(".", Block());
1258 		fTree->Insert("..", fDisk->ToBlock(Parent()));
1259 	}
1260 
1261 	if (inode->Flags() & INODE_DELETED)
1262 		return B_ENTRY_NOT_FOUND;
1263 
1264 	BString name = inode->Name();
1265 	if (name == "") {
1266 		name << "__file " << inode->BlockRun().allocation_group << ":"
1267 			<< (int32)inode->BlockRun().start;
1268 	}
1269 
1270 	return fTree->Insert(name.String(), inode->Block());
1271 }
1272 
1273 
1274 status_t
1275 Directory::CreateTree()
1276 {
1277 	fTree = new BPlusTree(this);
1278 
1279 	status_t status = fTree->InitCheck();
1280 	if (status < B_OK) {
1281 		delete fTree;
1282 		fTree = NULL;
1283 		return status;
1284 	}
1285 	return B_OK;
1286 }
1287 
1288 
1289 status_t
1290 Directory::GetTree(BPlusTree **tree)
1291 {
1292 	if (!fTree) {
1293 		status_t status = CreateTree();
1294 		if (status < B_OK)
1295 			return status;
1296 	}
1297 	*tree = fTree;
1298 	return B_OK;
1299 }
1300 
1301 
1302 //	#pragma mark -
1303 
1304 
1305 Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer)
1306 	: Inode(disk,inode,ownBuffer)
1307 {
1308 }
1309 
1310 
1311 Symlink::Symlink(const Inode &inode)
1312 	: Inode(inode)
1313 {
1314 }
1315 
1316 
1317 Symlink::~Symlink()
1318 {
1319 }
1320 
1321 
1322 status_t
1323 Symlink::InitCheck()
1324 {
1325 	status_t status = Inode::InitCheck();
1326 	if (status == B_OK)
1327 		return IsSymlink() ? B_OK : B_ERROR;
1328 
1329 	return status;
1330 }
1331 
1332 
1333 status_t
1334 Symlink::CopyTo(const char *root, bool fullPath,Inode::Source *source)
1335 {
1336 	status_t status = Inode::CopyTo(root,fullPath,source);
1337 	if (status < B_OK)
1338 		return status;
1339 
1340 	BPath path(root);
1341 	if (fullPath && Path(source))
1342 		path.Append(Path(source));
1343 
1344 	char *name = (char *)Name();
1345 	if (name != NULL) {
1346 		// changes the filename in the inode buffer (for deleted entries)
1347 		if (!*name)
1348 			*name = '_';
1349 		path.Append(name);
1350 	} else {
1351 		// create unique name
1352 		BString sub;
1353 		sub << "__symlink " << BlockRun().allocation_group << ":"
1354 			<< (int32)BlockRun().start;
1355 		path.Append(sub.String());
1356 	}
1357 
1358 	BEntry entry(path.Path());
1359 	BDirectory directory;
1360 	if ((status = entry.GetParent(&directory)) < B_OK)
1361 		return status;
1362 
1363 	char to[2048];
1364 	if (LinksTo(to,sizeof(to)) < B_OK)
1365 		return B_ERROR;
1366 
1367 	BSymLink link;
1368 	status = directory.CreateSymLink(path.Leaf(),to,&link);
1369 	if (status < B_OK && status != B_FILE_EXISTS)
1370 		return status;
1371 
1372 	if ((status = link.SetTo(&entry)) < B_OK)
1373 		return status;
1374 
1375 	return CopyAttributesTo(&link);
1376 }
1377 
1378 
1379 status_t
1380 Symlink::LinksTo(char *to,size_t maxLength)
1381 {
1382 	if ((fInode->flags & INODE_LONG_SYMLINK) == 0) {
1383 		strcpy(to,fInode->short_symlink);
1384 		return B_OK;
1385 	}
1386 
1387 	DataStream stream(*this);
1388 	status_t status = stream.InitCheck();
1389 	if (status < B_OK)
1390 		return status;
1391 
1392 	status = stream.Read(to,maxLength);
1393 
1394 	return status < B_OK ? status : B_OK;
1395 }
1396 
1397