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