btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / bin / bfs_tools / lib / Inode.cpp
blobaef986e9d84b24da56f0b877d98e79f82e547bf3
1 /*
2 * Copyright 2001-2008 pinc Software. All Rights Reserved.
3 * Released under the terms of the MIT license.
4 */
6 //! BFS Inode classes
9 #include "Inode.h"
10 #include "BPlusTree.h"
12 #include <Directory.h>
13 #include <SymLink.h>
14 #include <Entry.h>
15 #include <Path.h>
16 #include <String.h>
18 #include <new>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
24 class NodeGetter {
25 public:
26 NodeGetter(Inode* inode)
28 fInode(inode)
30 fInode->AcquireBuffer();
33 ~NodeGetter()
35 fInode->ReleaseBuffer();
38 private:
39 Inode* fInode;
43 // #pragma mark -
46 Inode::Inode(Disk* disk, bfs_inode* inode, bool ownBuffer)
48 fDisk(disk),
49 fInode(inode),
50 fOwnBuffer(ownBuffer),
51 fPath(NULL),
52 fRefCount(1),
53 fCurrentSmallData(NULL),
54 fAttributes(NULL),
55 fAttributeBuffer(NULL)
57 if (inode != NULL)
58 fBlockRun = inode->inode_num;
62 Inode::Inode(const Inode& inode)
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)
77 Inode::~Inode()
79 _Unset();
83 void
84 Inode::_Unset()
86 if (fOwnBuffer)
87 free(fInode);
89 fInode = NULL;
90 fBlockRun.SetTo(0, 0, 0);
92 free(fPath);
93 fPath = NULL;
95 delete fAttributes;
96 fAttributes = NULL;
100 status_t
101 Inode::SetTo(bfs_inode *inode)
103 _Unset();
105 fInode = inode;
106 fBlockRun = inode->inode_num;
107 return B_OK;
111 status_t
112 Inode::InitCheck() const
114 if (!fInode)
115 return B_ERROR;
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;
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;
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;
154 status_t
155 Inode::CopyBuffer()
157 if (!fInode)
158 return B_ERROR;
160 bfs_inode *buffer = (bfs_inode *)malloc(fInode->inode_size);
161 if (!buffer)
162 return B_NO_MEMORY;
164 memcpy(buffer, fInode, fInode->inode_size);
165 fInode = buffer;
166 fOwnBuffer = true;
167 BufferClobbered();
168 // this must not be deleted anymore
170 return B_OK;
174 /*static*/ bool
175 Inode::_LowMemory()
177 static bigtime_t lastChecked;
178 static int32 percentUsed;
180 if (system_time() > lastChecked + 1000000LL) {
181 system_info info;
182 get_system_info(&info);
183 percentUsed = 100 * info.used_pages / info.max_pages;
186 return percentUsed > 75;
190 void
191 Inode::ReleaseBuffer()
193 if (atomic_add(&fRefCount, -1) != 1)
194 return;
196 if (fOwnBuffer) {
197 if (!_LowMemory())
198 return;
200 free(fInode);
201 fInode = NULL;
206 status_t
207 Inode::AcquireBuffer()
209 if (atomic_add(&fRefCount, 1) != 0)
210 return B_OK;
212 if (!fOwnBuffer || fInode != NULL)
213 return B_OK;
215 fInode = (bfs_inode*)malloc(fDisk->BlockSize());
216 if (fInode == NULL)
217 return B_NO_MEMORY;
219 ssize_t bytesRead = fDisk->ReadAt(Offset(), fInode, fDisk->BlockSize());
220 if (bytesRead < B_OK)
221 return bytesRead;
223 return B_OK;
227 void
228 Inode::BufferClobbered()
230 AcquireBuffer();
234 void
235 Inode::SetParent(const block_run& run)
237 fInode->parent = run;
238 BufferClobbered();
242 void
243 Inode::SetBlockRun(const block_run& run)
245 fInode->inode_num = run;
246 fBlockRun = run;
247 BufferClobbered();
251 void
252 Inode::SetMode(uint32 mode)
254 fInode->mode = mode;
255 BufferClobbered();
259 status_t
260 Inode::SetName(const char *name)
262 if (name == NULL || *name == '\0')
263 return B_BAD_VALUE;
265 small_data *data = fInode->small_data_start, *nameData = NULL;
266 BufferClobbered();
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;
274 data = data->Next();
277 int32 oldLength = nameData == NULL ? 0 : nameData->data_size;
278 int32 newLength = strlen(name) + (nameData == NULL ? sizeof(small_data) + 5 : 0);
280 if ((addr_t)data + newLength - oldLength >= (addr_t)(fInode
281 + fDisk->BlockSize()))
282 return B_NO_MEMORY;
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);
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);
301 return B_OK;
305 const char *
306 Inode::Name() const
308 if (InitCheck() != B_OK) {
309 puts("Not getting name because node is invalid");
310 return NULL;
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();
319 data = data->Next();
321 return NULL;
325 status_t
326 Inode::GetNextSmallData(small_data **smallData)
328 if (!fInode)
329 return B_ERROR;
331 small_data *data = *smallData;
333 // begin from the start?
334 if (data == NULL)
335 data = fInode->small_data_start;
336 else
337 data = data->Next();
339 // is already last item?
340 if (data->IsLast(fInode))
341 return B_ENTRY_NOT_FOUND;
343 *smallData = data;
344 return B_OK;
348 status_t
349 Inode::RewindAttributes()
351 fCurrentSmallData = NULL;
353 if (fAttributes != NULL)
354 fAttributes->Rewind();
356 return B_OK;
360 status_t
361 Inode::GetNextAttribute(char *name, uint32 *type, void **data, size_t *length)
363 // read attributes out of the small data section
365 if (fCurrentSmallData == NULL || !fCurrentSmallData->IsLast(fInode)) {
366 if (fCurrentSmallData == NULL)
367 fCurrentSmallData = fInode->small_data_start;
368 else
369 fCurrentSmallData = fCurrentSmallData->Next();
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();
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;
383 return B_OK;
387 // read attributes out of the attribute directory
389 if (Attributes().IsZero())
390 return B_ENTRY_NOT_FOUND;
392 if (fAttributes == NULL)
393 fAttributes = (Directory *)Inode::Factory(fDisk, Attributes());
395 status_t status = fAttributes ? fAttributes->InitCheck() : B_ERROR;
396 if (status < B_OK)
397 return status;
399 block_run run;
400 status = fAttributes->GetNextEntry(name, &run);
401 if (status < B_OK) {
402 free(fAttributeBuffer);
403 fAttributeBuffer = NULL;
404 return status;
407 Attribute *attribute = (Attribute *)Inode::Factory(fDisk, run);
408 if (attribute == NULL || attribute->InitCheck() < B_OK)
409 return B_IO_ERROR;
411 *type = attribute->Type();
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;
420 fAttributeBuffer = buffer;
422 ssize_t size = attribute->Read(fAttributeBuffer, attribute->Size());
423 delete attribute;
425 *length = size;
426 *data = fAttributeBuffer;
428 return size < B_OK ? size : B_OK;
432 status_t
433 Inode::_FindPath(Inode::Source *source)
435 BString path;
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);
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);
454 delete inode;
455 break;
457 parent = inode->Parent();
458 path.Prepend("/");
459 path.Prepend(inode->Name());
461 delete inode;
463 fPath = strdup(path.String());
465 return B_OK;
469 const char *
470 Inode::Path(Inode::Source *source)
472 if (fPath == NULL)
473 _FindPath(source);
475 return fPath;
479 status_t
480 Inode::CopyTo(const char *root, bool fullPath, Inode::Source *source)
482 if (root == NULL)
483 return B_ENTRY_NOT_FOUND;
485 BString path;
487 if (fullPath)
488 path.Append(Path(source));
490 if (*(root + strlen(root) - 1) != '/')
491 path.Prepend("/");
492 path.Prepend(root);
494 return create_directory(path.String(), 0777);
498 status_t
499 Inode::CopyAttributesTo(BNode *node)
501 // copy attributes
503 RewindAttributes();
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;
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;
522 continue;
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);
535 // copy stats
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);
543 return B_OK;
547 Inode *
548 Inode::Factory(Disk *disk, bfs_inode *inode, bool ownBuffer)
550 // attributes (of a file)
551 if ((inode->mode & (S_ATTR | S_ATTR_DIR)) == S_ATTR)
552 return new Attribute(disk, inode, ownBuffer);
554 // directories, attribute directories, indices
555 if (S_ISDIR(inode->mode) || inode->mode & S_ATTR_DIR)
556 return new Directory(disk, inode, ownBuffer);
558 // regular files
559 if (S_ISREG(inode->mode))
560 return new File(disk, inode, ownBuffer);
562 // symlinks (short and link in data-stream)
563 if (S_ISLNK(inode->mode))
564 return new Symlink(disk, inode, ownBuffer);
566 return NULL;
570 Inode *
571 Inode::Factory(Disk *disk, block_run run)
573 bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize());
574 if (!inode)
575 return NULL;
577 if (disk->ReadAt(disk->ToOffset(run), inode, disk->BlockSize()) <= 0)
578 return NULL;
580 Inode *object = Factory(disk, inode);
581 if (object == NULL)
582 free(inode);
584 return object;
588 Inode *
589 Inode::Factory(Disk *disk, Inode *inode, bool copyBuffer)
591 bfs_inode *inodeBuffer = inode->fInode;
593 if (copyBuffer) {
594 bfs_inode *inodeCopy = (bfs_inode *)malloc(inodeBuffer->inode_size);
595 if (!inodeCopy)
596 return NULL;
598 memcpy(inodeCopy, inodeBuffer, inodeBuffer->inode_size);
599 inodeBuffer = inodeCopy;
601 return Factory(disk, inodeBuffer, copyBuffer);
605 Inode *
606 Inode::EmptyInode(Disk *disk, const char *name, int32 mode)
608 bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize());
609 if (!inode)
610 return NULL;
612 memset(inode, 0, sizeof(bfs_inode));
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);
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);
628 Inode *object = new (std::nothrow) Inode(disk, inode);
629 if (object == NULL) {
630 free(inode);
631 return NULL;
634 object->AcquireBuffer();
635 // this must not be deleted anymore!
636 return object;
640 // #pragma mark -
643 DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer)
644 : Inode(disk,inode,ownBuffer),
645 fCurrent(-1),
646 fPosition(0LL)
651 DataStream::DataStream(const Inode &inode)
652 : Inode(inode),
653 fCurrent(-1),
654 fPosition(0LL)
659 DataStream::~DataStream()
664 status_t
665 DataStream::FindBlockRun(off_t pos)
667 NodeGetter _(this);
669 if (pos > fInode->data.size)
670 return B_ENTRY_NOT_FOUND;
672 if (fCurrent < 0)
673 fLevel = 0;
675 fRunBlockEnd = fCurrent >= 0
676 ? fRunFileOffset + (fRun.length << fDisk->BlockShift()) : 0LL;
678 // access in current block run?
680 if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd)
681 return B_OK;
683 // find matching block run
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
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;
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;
701 //printf("\tstart = %Ld, 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;
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 = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
712 } else {
713 // access from indirect blocks
715 block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.indirect);
716 if (!indirect)
717 return B_ERROR;
719 int32 indirectRuns = (fInode->data.indirect.length << fDisk->BlockShift()) / sizeof(block_run);
721 if (fLevel != 1 || pos < fRunFileOffset) {
722 fRunBlockEnd = fInode->data.max_direct_range;
723 fCurrent = -1;
724 fLevel = 1;
727 while (++fCurrent < indirectRuns) {
728 if (indirect[fCurrent].IsZero())
729 break;
731 fRunFileOffset = fRunBlockEnd;
732 fRunBlockEnd += indirect[fCurrent].length << fDisk->BlockShift();
733 if (fRunBlockEnd > pos)
734 break;
736 if (fCurrent == indirectRuns || indirect[fCurrent].IsZero())
737 return B_ERROR;
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 = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
743 } else {
744 // access from direct blocks
745 if (fRunFileOffset > pos) {
746 fRunBlockEnd = 0LL;
747 fCurrent = -1;
749 fLevel = 0;
751 while (++fCurrent < NUM_DIRECT_BLOCKS) {
752 if (fInode->data.direct[fCurrent].IsZero())
753 break;
755 fRunFileOffset = fRunBlockEnd;
756 fRunBlockEnd += fInode->data.direct[fCurrent].length << fDisk->BlockShift();
757 if (fRunBlockEnd > pos)
758 break;
760 if (fCurrent == NUM_DIRECT_BLOCKS || fInode->data.direct[fCurrent].IsZero())
761 return B_ERROR;
763 fRun = fInode->data.direct[fCurrent];
764 //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
766 return B_OK;
770 ssize_t
771 DataStream::ReadAt(off_t pos, void *buffer, size_t size)
773 NodeGetter _(this);
775 //printf("DataStream::ReadAt(pos = %Ld,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;
781 size = fInode->data.size - pos;
782 if (!size) // there is nothing left to read
783 return 0;
785 ssize_t read = 0;
787 //printf("### read %ld bytes at %Ld\n",size,pos);
788 while (size > 0) {
789 status_t status = FindBlockRun(pos);
790 if (status < B_OK)
791 return status;
793 ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
795 //printf("### read %ld bytes from %Ld\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);
803 return bytes < 0 ? bytes : B_BAD_DATA;
806 buffer = (void *)((uint8 *)buffer + bytes);
807 size -= bytes;
808 pos += bytes;
809 read += bytes;
811 if (read >= 0)
812 return read;
814 return B_IO_ERROR;
818 ssize_t
819 DataStream::WriteAt(off_t pos, const void *buffer, size_t size)
821 NodeGetter _(this);
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;
828 size = fInode->data.size - pos;
829 if (!size) // there is nothing left to write
830 return 0;
832 ssize_t written = 0;
834 //printf("### write %ld bytes at %Ld\n",size,pos);
835 while (size > 0) {
836 status_t status = FindBlockRun(pos);
837 if (status < B_OK)
838 return status;
840 ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
842 //printf("### write %ld bytes to %Ld\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;
847 buffer = (void *)((uint8 *)buffer + bytes);
848 size -= bytes;
849 pos += bytes;
850 written += bytes;
852 if (written >= 0)
853 return written;
855 return B_IO_ERROR;
859 off_t
860 DataStream::Seek(off_t position, uint32 seekMode)
862 NodeGetter _(this);
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;
871 return fPosition;
875 off_t
876 DataStream::Position() const
878 return fPosition;
882 status_t
883 DataStream::SetSize(off_t size)
885 NodeGetter _(this);
887 // FIXME: not yet supported
888 if (size > fInode->data.size || size > fInode->data.max_direct_range)
889 return B_ERROR;
891 if (size == fInode->data.size)
892 return B_OK;
894 BufferClobbered();
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;
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();
912 return B_OK;
916 // #pragma mark -
919 File::File(Disk *disk, bfs_inode *inode,bool ownBuffer)
920 : DataStream(disk,inode,ownBuffer)
925 File::File(const Inode &inode)
926 : DataStream(inode)
931 File::~File()
936 status_t
937 File::InitCheck() const
939 status_t status = DataStream::InitCheck();
940 if (status == B_OK)
941 return IsFile() ? B_OK : B_ERROR;
943 return status;
947 status_t
948 File::CopyTo(const char *root, bool fullPath, Inode::Source *source)
950 status_t status = Inode::CopyTo(root, fullPath, source);
951 if (status < B_OK)
952 return status;
954 BPath path(root);
955 if (fullPath && Path(source))
956 path.Append(Path(source));
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());
970 printf("%" B_PRId32 ",%d -> %s\n", BlockRun().allocation_group,
971 BlockRun().start, path.Path());
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;
979 char buffer[fDisk->BlockSize()];
980 ssize_t size;
981 Seek(0, SEEK_SET);
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;
989 return CopyAttributesTo(&file);
993 // #pragma mark -
996 Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer)
997 : File(disk, inode, ownBuffer)
1002 Attribute::Attribute(const Inode &inode)
1003 : File(inode)
1008 Attribute::~Attribute()
1013 status_t
1014 Attribute::InitCheck() const
1016 status_t status = DataStream::InitCheck();
1017 if (status == B_OK)
1018 return IsAttribute() ? B_OK : B_ERROR;
1020 return status;
1024 status_t
1025 Attribute::CopyTo(const char */*path*/, bool /*fullPath*/,
1026 Inode::Source */*source*/)
1028 // files and directories already copy all attributes
1030 // eventually, this method should be implemented to recover lost
1031 // attributes on the disk
1033 return B_OK;
1037 // #pragma mark -
1040 Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer)
1041 : DataStream(disk, inode, ownBuffer),
1042 fTree(NULL)
1047 Directory::Directory(const Inode &inode)
1048 : DataStream(inode),
1049 fTree(NULL)
1054 Directory::~Directory()
1056 delete fTree;
1060 status_t
1061 Directory::InitCheck() const
1063 status_t status = DataStream::InitCheck();
1064 if (status == B_OK)
1065 return (IsDirectory() || IsAttributeDirectory()) ? B_OK : B_ERROR;
1067 return status;
1071 status_t
1072 Directory::CopyTo(const char *root, bool fullPath, Inode::Source *source)
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;
1079 status_t status = Inode::CopyTo(root, fullPath, source);
1080 if (status < B_OK)
1081 return status;
1083 BPath path(root);
1084 if (fullPath && Path(source))
1085 path.Append(Path(source));
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());
1101 BEntry entry(path.Path());
1102 BDirectory directory;
1103 if ((status = entry.GetParent(&directory)) < B_OK)
1104 return status;
1106 status = directory.CreateDirectory(path.Leaf(), NULL);
1107 if (status < B_OK && status != B_FILE_EXISTS)
1108 return status;
1110 if ((status = directory.SetTo(&entry)) < B_OK)
1111 return status;
1113 return CopyAttributesTo(&directory);
1117 status_t
1118 Directory::Rewind()
1120 if (!fTree) {
1121 status_t status = CreateTree();
1122 if (status < B_OK)
1123 return status;
1125 return fTree->Rewind();
1129 status_t
1130 Directory::GetNextEntry(char *name, block_run *run)
1132 status_t status;
1134 if (!fTree) {
1135 if ((status = Rewind()) < B_OK)
1136 return status;
1138 uint16 length;
1139 off_t offset;
1141 if ((status = fTree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH - 1,
1142 &offset)) < B_OK)
1143 return status;
1145 *run = fDisk->ToBlockRun(offset);
1147 return B_OK;
1151 status_t
1152 Directory::GetNextEntry(block_run *run)
1154 char name[B_FILE_NAME_LENGTH];
1156 return GetNextEntry(name, run);
1160 status_t
1161 Directory::Contains(const block_run *run)
1163 status_t status;
1165 if (!fTree) {
1166 if ((status = Rewind()) < B_OK)
1167 return status;
1170 block_run searchRun;
1171 while (GetNextEntry(&searchRun) == B_OK) {
1172 if (searchRun == *run)
1173 return B_OK;
1176 return B_ENTRY_NOT_FOUND;
1180 status_t
1181 Directory::Contains(const Inode *inode)
1183 status_t status;
1185 if (!fTree) {
1186 if ((status = CreateTree()) < B_OK)
1187 return status;
1190 off_t value;
1191 const char *name = inode->Name();
1192 status = B_ENTRY_NOT_FOUND;
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;
1199 printf("inode address do not match (%s)!\n", inode->Name());
1202 if (status != B_OK && status != B_ENTRY_NOT_FOUND)
1203 return status;
1205 return Contains(&inode->InodeBuffer()->inode_num);
1209 status_t
1210 Directory::FindEntry(const char *name, block_run *run)
1212 status_t status;
1214 if (!name)
1215 return B_BAD_VALUE;
1217 if (!fTree) {
1218 if ((status = CreateTree()) < B_OK)
1219 return status;
1222 off_t value;
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;
1230 return status;
1234 status_t
1235 Directory::AddEntry(Inode *inode)
1237 status_t status;
1238 bool created = false;
1240 if (!fTree) {
1241 status = CreateTree();
1242 if (status == B_OK)
1243 status = fTree->Validate();
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;
1256 if (status < B_OK)
1257 return status;
1259 // keep all changes in memory
1260 fTree->SetHoldChanges(true);
1262 if (created) {
1263 // add . and ..
1264 fTree->Insert(".", Block());
1265 fTree->Insert("..", fDisk->ToBlock(Parent()));
1268 if (inode->Flags() & INODE_DELETED)
1269 return B_ENTRY_NOT_FOUND;
1271 BString name = inode->Name();
1272 if (name == "") {
1273 name << "__file " << inode->BlockRun().allocation_group << ":"
1274 << (int32)inode->BlockRun().start;
1277 return fTree->Insert(name.String(), inode->Block());
1281 status_t
1282 Directory::CreateTree()
1284 fTree = new BPlusTree(this);
1286 status_t status = fTree->InitCheck();
1287 if (status < B_OK) {
1288 delete fTree;
1289 fTree = NULL;
1290 return status;
1292 return B_OK;
1296 status_t
1297 Directory::GetTree(BPlusTree **tree)
1299 if (!fTree) {
1300 status_t status = CreateTree();
1301 if (status < B_OK)
1302 return status;
1304 *tree = fTree;
1305 return B_OK;
1309 // #pragma mark -
1312 Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer)
1313 : Inode(disk,inode,ownBuffer)
1318 Symlink::Symlink(const Inode &inode)
1319 : Inode(inode)
1324 Symlink::~Symlink()
1329 status_t
1330 Symlink::InitCheck() const
1332 status_t status = Inode::InitCheck();
1333 if (status == B_OK)
1334 return IsSymlink() ? B_OK : B_ERROR;
1336 return status;
1340 status_t
1341 Symlink::CopyTo(const char *root, bool fullPath,Inode::Source *source)
1343 status_t status = Inode::CopyTo(root,fullPath,source);
1344 if (status < B_OK)
1345 return status;
1347 BPath path(root);
1348 if (fullPath && Path(source))
1349 path.Append(Path(source));
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());
1365 BEntry entry(path.Path());
1366 BDirectory directory;
1367 if ((status = entry.GetParent(&directory)) < B_OK)
1368 return status;
1370 char to[2048];
1371 if (LinksTo(to,sizeof(to)) < B_OK)
1372 return B_ERROR;
1374 BSymLink link;
1375 status = directory.CreateSymLink(path.Leaf(),to,&link);
1376 if (status < B_OK && status != B_FILE_EXISTS)
1377 return status;
1379 if ((status = link.SetTo(&entry)) < B_OK)
1380 return status;
1382 return CopyAttributesTo(&link);
1386 status_t
1387 Symlink::LinksTo(char *to,size_t maxLength)
1389 if ((fInode->flags & INODE_LONG_SYMLINK) == 0) {
1390 strcpy(to,fInode->short_symlink);
1391 return B_OK;
1394 DataStream stream(*this);
1395 status_t status = stream.InitCheck();
1396 if (status < B_OK)
1397 return status;
1399 status = stream.Read(to,maxLength);
1401 return status < B_OK ? status : B_OK;