RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / bin / bfs_tools / lib / Inode.cpp
blobeaaf6919f4e5f3d23d638edce5b4232e20d2dab8
1 /*
2 * Copyright 2001-2008 pinc Software. All Rights Reserved.
3 */
5 //! BFS Inode classes
8 #include "Inode.h"
9 #include "BPlusTree.h"
11 #include <Directory.h>
12 #include <SymLink.h>
13 #include <Entry.h>
14 #include <Path.h>
15 #include <String.h>
17 #include <new>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
23 class NodeGetter {
24 public:
25 NodeGetter(Inode* inode)
27 fInode(inode)
29 fInode->AcquireBuffer();
32 ~NodeGetter()
34 fInode->ReleaseBuffer();
37 private:
38 Inode* fInode;
42 // #pragma mark -
45 Inode::Inode(Disk* disk, bfs_inode* inode, bool ownBuffer)
47 fDisk(disk),
48 fInode(inode),
49 fOwnBuffer(ownBuffer),
50 fPath(NULL),
51 fRefCount(1),
52 fCurrentSmallData(NULL),
53 fAttributes(NULL),
54 fAttributeBuffer(NULL)
56 if (inode != NULL)
57 fBlockRun = inode->inode_num;
61 Inode::Inode(const Inode& inode)
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)
76 Inode::~Inode()
78 _Unset();
82 void
83 Inode::_Unset()
85 if (fOwnBuffer)
86 free(fInode);
88 fInode = NULL;
89 fBlockRun.SetTo(0, 0, 0);
91 free(fPath);
92 fPath = NULL;
94 delete fAttributes;
95 fAttributes = NULL;
99 status_t
100 Inode::SetTo(bfs_inode *inode)
102 _Unset();
104 fInode = inode;
105 fBlockRun = inode->inode_num;
106 return B_OK;
110 status_t
111 Inode::InitCheck() const
113 if (!fInode)
114 return B_ERROR;
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;
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;
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;
153 status_t
154 Inode::CopyBuffer()
156 if (!fInode)
157 return B_ERROR;
159 bfs_inode *buffer = (bfs_inode *)malloc(fInode->inode_size);
160 if (!buffer)
161 return B_NO_MEMORY;
163 memcpy(buffer, fInode, fInode->inode_size);
164 fInode = buffer;
165 fOwnBuffer = true;
166 BufferClobbered();
167 // this must not be deleted anymore
169 return B_OK;
173 /*static*/ bool
174 Inode::_LowMemory()
176 static bigtime_t lastChecked;
177 static int32 percentUsed;
179 if (system_time() > lastChecked + 1000000LL) {
180 system_info info;
181 get_system_info(&info);
182 percentUsed = 100 * info.used_pages / info.max_pages;
185 return percentUsed > 75;
189 void
190 Inode::ReleaseBuffer()
192 if (atomic_add(&fRefCount, -1) != 1)
193 return;
195 if (fOwnBuffer) {
196 if (!_LowMemory())
197 return;
199 free(fInode);
200 fInode = NULL;
205 status_t
206 Inode::AcquireBuffer()
208 if (atomic_add(&fRefCount, 1) != 0)
209 return B_OK;
211 if (!fOwnBuffer || fInode != NULL)
212 return B_OK;
214 fInode = (bfs_inode*)malloc(fDisk->BlockSize());
215 if (fInode == NULL)
216 return B_NO_MEMORY;
218 ssize_t bytesRead = fDisk->ReadAt(Offset(), fInode, fDisk->BlockSize());
219 if (bytesRead < B_OK)
220 return bytesRead;
222 return B_OK;
226 void
227 Inode::BufferClobbered()
229 AcquireBuffer();
233 void
234 Inode::SetParent(const block_run& run)
236 fInode->parent = run;
237 BufferClobbered();
241 void
242 Inode::SetBlockRun(const block_run& run)
244 fInode->inode_num = run;
245 fBlockRun = run;
246 BufferClobbered();
250 void
251 Inode::SetMode(uint32 mode)
253 fInode->mode = mode;
254 BufferClobbered();
258 status_t
259 Inode::SetName(const char *name)
261 if (name == NULL || *name == '\0')
262 return B_BAD_VALUE;
264 small_data *data = fInode->small_data_start, *nameData = NULL;
265 BufferClobbered();
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;
273 data = data->Next();
276 int32 oldLength = nameData == NULL ? 0 : nameData->data_size;
277 int32 newLength = strlen(name) + (nameData == NULL ? sizeof(small_data) + 5 : 0);
279 if ((addr_t)data + newLength - oldLength >= (addr_t)(fInode
280 + fDisk->BlockSize()))
281 return B_NO_MEMORY;
283 if (nameData == NULL) {
284 memmove(newLength + (uint8 *)fInode->small_data_start,
285 fInode->small_data_start,
286 (addr_t)data - (addr_t)fInode->small_data_start);
287 nameData = fInode->small_data_start;
288 } else {
289 memmove(newLength + (uint8 *)nameData, nameData,
290 (addr_t)data - (addr_t)fInode->small_data_start);
293 memset(nameData, 0, sizeof(small_data) + 5 + strlen(name));
294 nameData->type = FILE_NAME_TYPE;
295 nameData->name_size = FILE_NAME_NAME_LENGTH;
296 nameData->data_size = strlen(name);
297 *nameData->Name() = FILE_NAME_NAME;
298 strcpy((char *)nameData->Data(),name);
300 return B_OK;
304 const char *
305 Inode::Name() const
307 if (InitCheck() != B_OK) {
308 puts("Not getting name because node is invalid");
309 return NULL;
311 small_data *data = fInode->small_data_start;
312 while (!data->IsLast(fInode)) {
313 if (data->type == FILE_NAME_TYPE
314 && data->name_size == FILE_NAME_NAME_LENGTH
315 && *data->Name() == FILE_NAME_NAME)
316 return (const char *)data->Data();
318 data = data->Next();
320 return NULL;
324 status_t
325 Inode::GetNextSmallData(small_data **smallData)
327 if (!fInode)
328 return B_ERROR;
330 small_data *data = *smallData;
332 // begin from the start?
333 if (data == NULL)
334 data = fInode->small_data_start;
335 else
336 data = data->Next();
338 // is already last item?
339 if (data->IsLast(fInode))
340 return B_ENTRY_NOT_FOUND;
342 *smallData = data;
343 return B_OK;
347 status_t
348 Inode::RewindAttributes()
350 fCurrentSmallData = NULL;
352 if (fAttributes != NULL)
353 fAttributes->Rewind();
355 return B_OK;
359 status_t
360 Inode::GetNextAttribute(char *name, uint32 *type, void **data, size_t *length)
362 // read attributes out of the small data section
364 if (fCurrentSmallData == NULL || !fCurrentSmallData->IsLast(fInode)) {
365 if (fCurrentSmallData == NULL)
366 fCurrentSmallData = fInode->small_data_start;
367 else
368 fCurrentSmallData = fCurrentSmallData->Next();
370 // skip name attribute
371 if (!fCurrentSmallData->IsLast(fInode)
372 && fCurrentSmallData->name_size == FILE_NAME_NAME_LENGTH
373 && *fCurrentSmallData->Name() == FILE_NAME_NAME)
374 fCurrentSmallData = fCurrentSmallData->Next();
376 if (!fCurrentSmallData->IsLast(fInode)) {
377 strncpy(name,fCurrentSmallData->Name(), B_FILE_NAME_LENGTH);
378 *type = fCurrentSmallData->type;
379 *data = fCurrentSmallData->Data();
380 *length = fCurrentSmallData->data_size;
382 return B_OK;
386 // read attributes out of the attribute directory
388 if (Attributes().IsZero())
389 return B_ENTRY_NOT_FOUND;
391 if (fAttributes == NULL)
392 fAttributes = (Directory *)Inode::Factory(fDisk, Attributes());
394 status_t status = fAttributes ? fAttributes->InitCheck() : B_ERROR;
395 if (status < B_OK)
396 return status;
398 block_run run;
399 status = fAttributes->GetNextEntry(name, &run);
400 if (status < B_OK) {
401 free(fAttributeBuffer);
402 fAttributeBuffer = NULL;
403 return status;
406 Attribute *attribute = (Attribute *)Inode::Factory(fDisk, run);
407 if (attribute == NULL || attribute->InitCheck() < B_OK)
408 return B_IO_ERROR;
410 *type = attribute->Type();
412 void *buffer = realloc(fAttributeBuffer, attribute->Size());
413 if (buffer == NULL) {
414 free(fAttributeBuffer);
415 fAttributeBuffer = NULL;
416 delete attribute;
417 return B_NO_MEMORY;
419 fAttributeBuffer = buffer;
421 ssize_t size = attribute->Read(fAttributeBuffer, attribute->Size());
422 delete attribute;
424 *length = size;
425 *data = fAttributeBuffer;
427 return size < B_OK ? size : B_OK;
431 status_t
432 Inode::_FindPath(Inode::Source *source)
434 BString path;
436 block_run parent = Parent();
437 while (!parent.IsZero() && parent != fDisk->Root()) {
438 Inode *inode;
439 if (source)
440 inode = source->InodeAt(parent);
441 else
442 inode = Inode::Factory(fDisk, parent);
444 if (inode == NULL
445 || inode->InitCheck() < B_OK
446 || inode->Name() == NULL
447 || !*inode->Name()) {
448 BString sub;
449 sub << "__recovered " << parent.allocation_group << ":"
450 << (int32)parent.start << "/";
451 path.Prepend(sub);
453 delete inode;
454 break;
456 parent = inode->Parent();
457 path.Prepend("/");
458 path.Prepend(inode->Name());
460 delete inode;
462 fPath = strdup(path.String());
464 return B_OK;
468 const char *
469 Inode::Path(Inode::Source *source)
471 if (fPath == NULL)
472 _FindPath(source);
474 return fPath;
478 status_t
479 Inode::CopyTo(const char *root, bool fullPath, Inode::Source *source)
481 if (root == NULL)
482 return B_ENTRY_NOT_FOUND;
484 BString path;
486 if (fullPath)
487 path.Append(Path(source));
489 if (*(root + strlen(root) - 1) != '/')
490 path.Prepend("/");
491 path.Prepend(root);
493 return create_directory(path.String(), 0777);
497 status_t
498 Inode::CopyAttributesTo(BNode *node)
500 // copy attributes
502 RewindAttributes();
504 char name[B_FILE_NAME_LENGTH];
505 const uint32 kMaxBrokenAttributes = 64;
506 // sanity max value
507 uint32 count = 0;
508 uint32 type;
509 void *data;
510 size_t size;
512 status_t status;
513 while ((status = GetNextAttribute(name, &type, &data, &size))
514 != B_ENTRY_NOT_FOUND) {
515 if (status != B_OK) {
516 printf("could not open attribute (possibly: %s): %s!\n",
517 name, strerror(status));
518 if (count++ > kMaxBrokenAttributes)
519 break;
521 continue;
524 ssize_t written = node->WriteAttr(name, type, 0, data, size);
525 if (written < B_OK) {
526 printf("could not write attribute \"%s\": %s\n", name,
527 strerror(written));
528 } else if ((size_t)written < size) {
529 printf("could only write %ld bytes (from %ld) at attribute \"%s\"\n",
530 written, size, name);
534 // copy stats
536 node->SetPermissions(fInode->mode);
537 node->SetOwner(fInode->uid);
538 node->SetGroup(fInode->gid);
539 node->SetModificationTime(fInode->last_modified_time >> 16);
540 node->SetCreationTime(fInode->create_time >> 16);
542 return B_OK;
546 Inode *
547 Inode::Factory(Disk *disk, bfs_inode *inode, bool ownBuffer)
549 // attributes (of a file)
550 if ((inode->mode & (S_ATTR | S_ATTR_DIR)) == S_ATTR)
551 return new Attribute(disk, inode, ownBuffer);
553 // directories, attribute directories, indices
554 if (S_ISDIR(inode->mode) || inode->mode & S_ATTR_DIR)
555 return new Directory(disk, inode, ownBuffer);
557 // regular files
558 if (S_ISREG(inode->mode))
559 return new File(disk, inode, ownBuffer);
561 // symlinks (short and link in data-stream)
562 if (S_ISLNK(inode->mode))
563 return new Symlink(disk, inode, ownBuffer);
565 return NULL;
569 Inode *
570 Inode::Factory(Disk *disk, block_run run)
572 bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize());
573 if (!inode)
574 return NULL;
576 if (disk->ReadAt(disk->ToOffset(run), inode, disk->BlockSize()) <= 0)
577 return NULL;
579 Inode *object = Factory(disk, inode);
580 if (object == NULL)
581 free(inode);
583 return object;
587 Inode *
588 Inode::Factory(Disk *disk, Inode *inode, bool copyBuffer)
590 bfs_inode *inodeBuffer = inode->fInode;
592 if (copyBuffer) {
593 bfs_inode *inodeCopy = (bfs_inode *)malloc(inodeBuffer->inode_size);
594 if (!inodeCopy)
595 return NULL;
597 memcpy(inodeCopy, inodeBuffer, inodeBuffer->inode_size);
598 inodeBuffer = inodeCopy;
600 return Factory(disk, inodeBuffer, copyBuffer);
604 Inode *
605 Inode::EmptyInode(Disk *disk, const char *name, int32 mode)
607 bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize());
608 if (!inode)
609 return NULL;
611 memset(inode, 0, sizeof(bfs_inode));
613 inode->magic1 = INODE_MAGIC1;
614 inode->inode_size = disk->BlockSize();
615 inode->mode = mode;
616 inode->flags = INODE_IN_USE | (mode & S_IFDIR ? INODE_LOGGED : 0);
618 if (name) {
619 small_data *data = inode->small_data_start;
620 data->type = FILE_NAME_TYPE;
621 data->name_size = FILE_NAME_NAME_LENGTH;
622 *data->Name() = FILE_NAME_NAME;
623 data->data_size = strlen(name);
624 strcpy((char *)data->Data(), name);
627 Inode *object = new (std::nothrow) Inode(disk, inode);
628 if (object == NULL) {
629 free(inode);
630 return NULL;
633 object->AcquireBuffer();
634 // this must not be deleted anymore!
635 return object;
639 // #pragma mark -
642 DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer)
643 : Inode(disk,inode,ownBuffer),
644 fCurrent(-1),
645 fPosition(0LL)
650 DataStream::DataStream(const Inode &inode)
651 : Inode(inode),
652 fCurrent(-1),
653 fPosition(0LL)
658 DataStream::~DataStream()
663 status_t
664 DataStream::FindBlockRun(off_t pos)
666 NodeGetter _(this);
668 if (pos > fInode->data.size)
669 return B_ENTRY_NOT_FOUND;
671 if (fCurrent < 0)
672 fLevel = 0;
674 fRunBlockEnd = fCurrent >= 0
675 ? fRunFileOffset + (fRun.length << fDisk->BlockShift()) : 0LL;
677 // access in current block run?
679 if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd)
680 return B_OK;
682 // find matching block run
684 if (fInode->data.max_direct_range > 0
685 && pos >= fInode->data.max_direct_range) {
686 if (fInode->data.max_double_indirect_range > 0
687 && pos >= fInode->data.max_indirect_range) {
688 // read from double indirect blocks
690 //printf("find double indirect block: %ld,%d!\n",fInode->data.double_indirect.allocation_group,fInode->data.double_indirect.start);
691 block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.double_indirect);
692 if (indirect == NULL)
693 return B_ERROR;
695 off_t start = pos - fInode->data.max_indirect_range;
696 int32 indirectSize = fDisk->BlockSize() * 16 * (fDisk->BlockSize() / sizeof(block_run));
697 int32 directSize = fDisk->BlockSize() * 4;
698 int32 index = start / indirectSize;
700 //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index);
701 //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start);
702 indirect = (block_run *)fDisk->ReadBlockRun(indirect[index]);
703 if (indirect == NULL)
704 return B_ERROR;
706 fCurrent = (start % indirectSize) / directSize;
707 fRunFileOffset = fInode->data.max_indirect_range + (index * indirectSize) + (fCurrent * directSize);
708 fRunBlockEnd = fRunFileOffset + directSize;
709 fRun = indirect[fCurrent];
710 //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
711 } else {
712 // access from indirect blocks
714 block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.indirect);
715 if (!indirect)
716 return B_ERROR;
718 int32 indirectRuns = (fInode->data.indirect.length << fDisk->BlockShift()) / sizeof(block_run);
720 if (fLevel != 1 || pos < fRunFileOffset) {
721 fRunBlockEnd = fInode->data.max_direct_range;
722 fCurrent = -1;
723 fLevel = 1;
726 while (++fCurrent < indirectRuns) {
727 if (indirect[fCurrent].IsZero())
728 break;
730 fRunFileOffset = fRunBlockEnd;
731 fRunBlockEnd += indirect[fCurrent].length << fDisk->BlockShift();
732 if (fRunBlockEnd > pos)
733 break;
735 if (fCurrent == indirectRuns || indirect[fCurrent].IsZero())
736 return B_ERROR;
738 fRun = indirect[fCurrent];
739 //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start);
740 //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
742 } else {
743 // access from direct blocks
744 if (fRunFileOffset > pos) {
745 fRunBlockEnd = 0LL;
746 fCurrent = -1;
748 fLevel = 0;
750 while (++fCurrent < NUM_DIRECT_BLOCKS) {
751 if (fInode->data.direct[fCurrent].IsZero())
752 break;
754 fRunFileOffset = fRunBlockEnd;
755 fRunBlockEnd += fInode->data.direct[fCurrent].length << fDisk->BlockShift();
756 if (fRunBlockEnd > pos)
757 break;
759 if (fCurrent == NUM_DIRECT_BLOCKS || fInode->data.direct[fCurrent].IsZero())
760 return B_ERROR;
762 fRun = fInode->data.direct[fCurrent];
763 //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
765 return B_OK;
769 ssize_t
770 DataStream::ReadAt(off_t pos, void *buffer, size_t size)
772 NodeGetter _(this);
774 //printf("DataStream::ReadAt(pos = %Ld,buffer = %p,size = %ld);\n",pos,buffer,size);
775 // truncate size to read
776 if (pos + (off_t)size > fInode->data.size) {
777 if (pos > fInode->data.size) // reading outside the file
778 return B_ERROR;
780 size = fInode->data.size - pos;
781 if (!size) // there is nothing left to read
782 return 0;
784 ssize_t read = 0;
786 //printf("### read %ld bytes at %Ld\n",size,pos);
787 while (size > 0) {
788 status_t status = FindBlockRun(pos);
789 if (status < B_OK)
790 return status;
792 ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
794 //printf("### read %ld bytes from %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset);
795 bytes = fDisk->ReadAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,
796 buffer, bytes);
797 if (bytes <= 0) {
798 if (bytes == 0) {
799 printf("could not read bytes at: %" B_PRId32 ",%d\n",
800 fRun.allocation_group, fRun.start);
802 return bytes < 0 ? bytes : B_BAD_DATA;
805 buffer = (void *)((uint8 *)buffer + bytes);
806 size -= bytes;
807 pos += bytes;
808 read += bytes;
810 if (read >= 0)
811 return read;
813 return B_IO_ERROR;
817 ssize_t
818 DataStream::WriteAt(off_t pos, const void *buffer, size_t size)
820 NodeGetter _(this);
822 // FIXME: truncate size -> should enlargen the file
823 if (pos + (off_t)size > fInode->data.size) {
824 if (pos > fInode->data.size) // writing outside the file
825 return B_ERROR;
827 size = fInode->data.size - pos;
828 if (!size) // there is nothing left to write
829 return 0;
831 ssize_t written = 0;
833 //printf("### write %ld bytes at %Ld\n",size,pos);
834 while (size > 0) {
835 status_t status = FindBlockRun(pos);
836 if (status < B_OK)
837 return status;
839 ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
841 //printf("### write %ld bytes to %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset);
842 bytes = fDisk->WriteAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,buffer,bytes);
843 if (bytes < 0)
844 return bytes;
846 buffer = (void *)((uint8 *)buffer + bytes);
847 size -= bytes;
848 pos += bytes;
849 written += bytes;
851 if (written >= 0)
852 return written;
854 return B_IO_ERROR;
858 off_t
859 DataStream::Seek(off_t position, uint32 seekMode)
861 NodeGetter _(this);
863 if (seekMode == SEEK_SET)
864 fPosition = position;
865 else if (seekMode == SEEK_END)
866 fPosition = fInode->data.size + position;
867 else
868 fPosition += position;
870 return fPosition;
874 off_t
875 DataStream::Position() const
877 return fPosition;
881 status_t
882 DataStream::SetSize(off_t size)
884 NodeGetter _(this);
886 // FIXME: not yet supported
887 if (size > fInode->data.size || size > fInode->data.max_direct_range)
888 return B_ERROR;
890 if (size == fInode->data.size)
891 return B_OK;
893 BufferClobbered();
895 fInode->data.size = size;
896 fInode->data.max_direct_range = size;
897 fInode->data.max_indirect_range = 0;
898 fInode->data.max_double_indirect_range = 0;
900 for (int32 i = 0;i < NUM_DIRECT_BLOCKS;i++) {
901 if (size <= 0)
902 fInode->data.direct[i].SetTo(0, 0, 0);
903 else if ((fInode->data.direct[i].length << fDisk->BlockShift()) >= size) {
904 off_t blocks = (size + fDisk->BlockSize() - 1) / fDisk->BlockSize();
905 fInode->data.direct[i].length = blocks;
906 size = 0;
907 } else
908 size -= fInode->data.direct[i].length << fDisk->BlockShift();
911 return B_OK;
915 // #pragma mark -
918 File::File(Disk *disk, bfs_inode *inode,bool ownBuffer)
919 : DataStream(disk,inode,ownBuffer)
924 File::File(const Inode &inode)
925 : DataStream(inode)
930 File::~File()
935 status_t
936 File::InitCheck() const
938 status_t status = DataStream::InitCheck();
939 if (status == B_OK)
940 return IsFile() ? B_OK : B_ERROR;
942 return status;
946 status_t
947 File::CopyTo(const char *root, bool fullPath, Inode::Source *source)
949 status_t status = Inode::CopyTo(root, fullPath, source);
950 if (status < B_OK)
951 return status;
953 BPath path(root);
954 if (fullPath && Path(source))
955 path.Append(Path(source));
957 char *name = (char *)Name();
958 if (name != NULL) {
959 // changes the filename in the inode buffer (for deleted entries)
960 if (!*name)
961 *name = '_';
962 path.Append(name);
963 } else {
964 BString sub;
965 sub << "__untitled " << BlockRun().allocation_group << ":"
966 << (int32)BlockRun().start;
967 path.Append(sub.String());
969 printf("%" B_PRId32 ",%d -> %s\n", BlockRun().allocation_group,
970 BlockRun().start, path.Path());
972 BFile file;
973 status = file.SetTo(path.Path(),
974 B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS);
975 if (status < B_OK)
976 return status;
978 char buffer[fDisk->BlockSize()];
979 ssize_t size;
980 Seek(0, SEEK_SET);
982 while ((size = Read(buffer, sizeof(buffer))) > B_OK) {
983 ssize_t written = file.Write(buffer, size);
984 if (written < B_OK)
985 return written;
988 return CopyAttributesTo(&file);
992 // #pragma mark -
995 Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer)
996 : File(disk, inode, ownBuffer)
1001 Attribute::Attribute(const Inode &inode)
1002 : File(inode)
1007 Attribute::~Attribute()
1012 status_t
1013 Attribute::InitCheck() const
1015 status_t status = DataStream::InitCheck();
1016 if (status == B_OK)
1017 return IsAttribute() ? B_OK : B_ERROR;
1019 return status;
1023 status_t
1024 Attribute::CopyTo(const char */*path*/, bool /*fullPath*/,
1025 Inode::Source */*source*/)
1027 // files and directories already copy all attributes
1029 // eventually, this method should be implemented to recover lost
1030 // attributes on the disk
1032 return B_OK;
1036 // #pragma mark -
1039 Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer)
1040 : DataStream(disk, inode, ownBuffer),
1041 fTree(NULL)
1046 Directory::Directory(const Inode &inode)
1047 : DataStream(inode),
1048 fTree(NULL)
1053 Directory::~Directory()
1055 delete fTree;
1059 status_t
1060 Directory::InitCheck() const
1062 status_t status = DataStream::InitCheck();
1063 if (status == B_OK)
1064 return (IsDirectory() || IsAttributeDirectory()) ? B_OK : B_ERROR;
1066 return status;
1070 status_t
1071 Directory::CopyTo(const char *root, bool fullPath, Inode::Source *source)
1073 // don't copy attributes or indices
1074 // the recovery program should make empty files to recover lost attributes
1075 if (IsAttributeDirectory() || IsIndex())
1076 return B_OK;
1078 status_t status = Inode::CopyTo(root, fullPath, source);
1079 if (status < B_OK)
1080 return status;
1082 BPath path(root);
1083 if (fullPath && Path(source))
1084 path.Append(Path(source));
1086 char *name = (char *)Name();
1087 if (name != NULL) {
1088 // changes the filename in the inode buffer (for deleted entries)
1089 if (!*name)
1090 *name = '_';
1091 path.Append(name);
1092 } else {
1093 // create unique name
1094 BString sub;
1095 sub << "__untitled " << BlockRun().allocation_group << ":"
1096 << (int32)BlockRun().start;
1097 path.Append(sub.String());
1100 BEntry entry(path.Path());
1101 BDirectory directory;
1102 if ((status = entry.GetParent(&directory)) < B_OK)
1103 return status;
1105 status = directory.CreateDirectory(path.Leaf(), NULL);
1106 if (status < B_OK && status != B_FILE_EXISTS)
1107 return status;
1109 if ((status = directory.SetTo(&entry)) < B_OK)
1110 return status;
1112 return CopyAttributesTo(&directory);
1116 status_t
1117 Directory::Rewind()
1119 if (!fTree) {
1120 status_t status = CreateTree();
1121 if (status < B_OK)
1122 return status;
1124 return fTree->Rewind();
1128 status_t
1129 Directory::GetNextEntry(char *name, block_run *run)
1131 status_t status;
1133 if (!fTree) {
1134 if ((status = Rewind()) < B_OK)
1135 return status;
1137 uint16 length;
1138 off_t offset;
1140 if ((status = fTree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH - 1,
1141 &offset)) < B_OK)
1142 return status;
1144 *run = fDisk->ToBlockRun(offset);
1146 return B_OK;
1150 status_t
1151 Directory::GetNextEntry(block_run *run)
1153 char name[B_FILE_NAME_LENGTH];
1155 return GetNextEntry(name, run);
1159 status_t
1160 Directory::Contains(const block_run *run)
1162 status_t status;
1164 if (!fTree) {
1165 if ((status = Rewind()) < B_OK)
1166 return status;
1169 block_run searchRun;
1170 while (GetNextEntry(&searchRun) == B_OK) {
1171 if (searchRun == *run)
1172 return B_OK;
1175 return B_ENTRY_NOT_FOUND;
1179 status_t
1180 Directory::Contains(const Inode *inode)
1182 status_t status;
1184 if (!fTree) {
1185 if ((status = CreateTree()) < B_OK)
1186 return status;
1189 off_t value;
1190 const char *name = inode->Name();
1191 status = B_ENTRY_NOT_FOUND;
1193 if (name && (status = fTree->Find((uint8 *)name, (uint16)strlen(name),
1194 &value)) == B_OK) {
1195 if (fDisk->ToBlockRun(value) == inode->InodeBuffer()->inode_num)
1196 return B_OK;
1198 printf("inode address do not match (%s)!\n", inode->Name());
1201 if (status != B_OK && status != B_ENTRY_NOT_FOUND)
1202 return status;
1204 return Contains(&inode->InodeBuffer()->inode_num);
1208 status_t
1209 Directory::FindEntry(const char *name, block_run *run)
1211 status_t status;
1213 if (!name)
1214 return B_BAD_VALUE;
1216 if (!fTree) {
1217 if ((status = CreateTree()) < B_OK)
1218 return status;
1221 off_t value;
1223 if ((status = fTree->Find((uint8 *)name, (uint16)strlen(name),
1224 &value)) >= B_OK) {
1225 if (run)
1226 *run = fDisk->ToBlockRun(value);
1227 return B_OK;
1229 return status;
1233 status_t
1234 Directory::AddEntry(Inode *inode)
1236 status_t status;
1237 bool created = false;
1239 if (!fTree) {
1240 status = CreateTree();
1241 if (status == B_OK)
1242 status = fTree->Validate();
1244 if (status == B_BAD_DATA) {
1245 //puts("bplustree corrupted!");
1246 fTree = new BPlusTree(BPLUSTREE_STRING_TYPE, BPLUSTREE_NODE_SIZE,
1247 false);
1248 if ((status = fTree->InitCheck()) < B_OK) {
1249 delete fTree;
1250 fTree = NULL;
1251 } else
1252 created = true;
1255 if (status < B_OK)
1256 return status;
1258 // keep all changes in memory
1259 fTree->SetHoldChanges(true);
1261 if (created) {
1262 // add . and ..
1263 fTree->Insert(".", Block());
1264 fTree->Insert("..", fDisk->ToBlock(Parent()));
1267 if (inode->Flags() & INODE_DELETED)
1268 return B_ENTRY_NOT_FOUND;
1270 BString name = inode->Name();
1271 if (name == "") {
1272 name << "__file " << inode->BlockRun().allocation_group << ":"
1273 << (int32)inode->BlockRun().start;
1276 return fTree->Insert(name.String(), inode->Block());
1280 status_t
1281 Directory::CreateTree()
1283 fTree = new BPlusTree(this);
1285 status_t status = fTree->InitCheck();
1286 if (status < B_OK) {
1287 delete fTree;
1288 fTree = NULL;
1289 return status;
1291 return B_OK;
1295 status_t
1296 Directory::GetTree(BPlusTree **tree)
1298 if (!fTree) {
1299 status_t status = CreateTree();
1300 if (status < B_OK)
1301 return status;
1303 *tree = fTree;
1304 return B_OK;
1308 // #pragma mark -
1311 Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer)
1312 : Inode(disk,inode,ownBuffer)
1317 Symlink::Symlink(const Inode &inode)
1318 : Inode(inode)
1323 Symlink::~Symlink()
1328 status_t
1329 Symlink::InitCheck() const
1331 status_t status = Inode::InitCheck();
1332 if (status == B_OK)
1333 return IsSymlink() ? B_OK : B_ERROR;
1335 return status;
1339 status_t
1340 Symlink::CopyTo(const char *root, bool fullPath,Inode::Source *source)
1342 status_t status = Inode::CopyTo(root,fullPath,source);
1343 if (status < B_OK)
1344 return status;
1346 BPath path(root);
1347 if (fullPath && Path(source))
1348 path.Append(Path(source));
1350 char *name = (char *)Name();
1351 if (name != NULL) {
1352 // changes the filename in the inode buffer (for deleted entries)
1353 if (!*name)
1354 *name = '_';
1355 path.Append(name);
1356 } else {
1357 // create unique name
1358 BString sub;
1359 sub << "__symlink " << BlockRun().allocation_group << ":"
1360 << (int32)BlockRun().start;
1361 path.Append(sub.String());
1364 BEntry entry(path.Path());
1365 BDirectory directory;
1366 if ((status = entry.GetParent(&directory)) < B_OK)
1367 return status;
1369 char to[2048];
1370 if (LinksTo(to,sizeof(to)) < B_OK)
1371 return B_ERROR;
1373 BSymLink link;
1374 status = directory.CreateSymLink(path.Leaf(),to,&link);
1375 if (status < B_OK && status != B_FILE_EXISTS)
1376 return status;
1378 if ((status = link.SetTo(&entry)) < B_OK)
1379 return status;
1381 return CopyAttributesTo(&link);
1385 status_t
1386 Symlink::LinksTo(char *to,size_t maxLength)
1388 if ((fInode->flags & INODE_LONG_SYMLINK) == 0) {
1389 strcpy(to,fInode->short_symlink);
1390 return B_OK;
1393 DataStream stream(*this);
1394 status_t status = stream.InitCheck();
1395 if (status < B_OK)
1396 return status;
1398 status = stream.Read(to,maxLength);
1400 return status < B_OK ? status : B_OK;