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