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