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
24 class NodeGetter {
25 public:
NodeGetter(Inode * inode)26 NodeGetter(Inode* inode)
27 :
28 fInode(inode)
29 {
30 fInode->AcquireBuffer();
31 }
32
~NodeGetter()33 ~NodeGetter()
34 {
35 fInode->ReleaseBuffer();
36 }
37
38 private:
39 Inode* fInode;
40 };
41
42
43 // #pragma mark -
44
45
Inode(Disk * disk,bfs_inode * inode,bool ownBuffer)46 Inode::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
Inode(const Inode & inode)62 Inode::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
~Inode()77 Inode::~Inode()
78 {
79 _Unset();
80 }
81
82
83 void
_Unset()84 Inode::_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
100 status_t
SetTo(bfs_inode * inode)101 Inode::SetTo(bfs_inode *inode)
102 {
103 _Unset();
104
105 fInode = inode;
106 fBlockRun = inode->inode_num;
107 return B_OK;
108 }
109
110
111 status_t
InitCheck() const112 Inode::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
154 status_t
CopyBuffer()155 Inode::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
_LowMemory()175 Inode::_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
190 void
ReleaseBuffer()191 Inode::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
206 status_t
AcquireBuffer()207 Inode::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
227 void
BufferClobbered()228 Inode::BufferClobbered()
229 {
230 AcquireBuffer();
231 }
232
233
234 void
SetParent(const block_run & run)235 Inode::SetParent(const block_run& run)
236 {
237 fInode->parent = run;
238 BufferClobbered();
239 }
240
241
242 void
SetBlockRun(const block_run & run)243 Inode::SetBlockRun(const block_run& run)
244 {
245 fInode->inode_num = run;
246 fBlockRun = run;
247 BufferClobbered();
248 }
249
250
251 void
SetMode(uint32 mode)252 Inode::SetMode(uint32 mode)
253 {
254 fInode->mode = mode;
255 BufferClobbered();
256 }
257
258
259 status_t
SetName(const char * name)260 Inode::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
305 const char *
Name() const306 Inode::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
325 status_t
GetNextSmallData(small_data ** smallData)326 Inode::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
348 status_t
RewindAttributes()349 Inode::RewindAttributes()
350 {
351 fCurrentSmallData = NULL;
352
353 if (fAttributes != NULL)
354 fAttributes->Rewind();
355
356 return B_OK;
357 }
358
359
360 status_t
GetNextAttribute(char * name,uint32 * type,void ** data,size_t * length)361 Inode::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
432 status_t
_FindPath(Inode::Source * source)433 Inode::_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
469 const char *
Path(Inode::Source * source)470 Inode::Path(Inode::Source *source)
471 {
472 if (fPath == NULL)
473 _FindPath(source);
474
475 return fPath;
476 }
477
478
479 status_t
CopyTo(const char * root,bool fullPath,Inode::Source * source)480 Inode::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
498 status_t
CopyAttributesTo(BNode * node)499 Inode::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
547 Inode *
Factory(Disk * disk,bfs_inode * inode,bool ownBuffer)548 Inode::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
570 Inode *
Factory(Disk * disk,block_run run)571 Inode::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
588 Inode *
Factory(Disk * disk,Inode * inode,bool copyBuffer)589 Inode::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
605 Inode *
EmptyInode(Disk * disk,const char * name,int32 mode)606 Inode::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
DataStream(Disk * disk,bfs_inode * inode,bool ownBuffer)643 DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer)
644 : Inode(disk,inode,ownBuffer),
645 fCurrent(-1),
646 fPosition(0LL)
647 {
648 }
649
650
DataStream(const Inode & inode)651 DataStream::DataStream(const Inode &inode)
652 : Inode(inode),
653 fCurrent(-1),
654 fPosition(0LL)
655 {
656 }
657
658
~DataStream()659 DataStream::~DataStream()
660 {
661 }
662
663
664 status_t
FindBlockRun(off_t pos)665 DataStream::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
770 ssize_t
ReadAt(off_t pos,void * buffer,size_t size)771 DataStream::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
818 ssize_t
WriteAt(off_t pos,const void * buffer,size_t size)819 DataStream::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
859 off_t
Seek(off_t position,uint32 seekMode)860 DataStream::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
875 off_t
Position() const876 DataStream::Position() const
877 {
878 return fPosition;
879 }
880
881
882 status_t
SetSize(off_t size)883 DataStream::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
File(Disk * disk,bfs_inode * inode,bool ownBuffer)919 File::File(Disk *disk, bfs_inode *inode,bool ownBuffer)
920 : DataStream(disk,inode,ownBuffer)
921 {
922 }
923
924
File(const Inode & inode)925 File::File(const Inode &inode)
926 : DataStream(inode)
927 {
928 }
929
930
~File()931 File::~File()
932 {
933 }
934
935
936 status_t
InitCheck() const937 File::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
947 status_t
CopyTo(const char * root,bool fullPath,Inode::Source * source)948 File::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
Attribute(Disk * disk,bfs_inode * inode,bool ownBuffer)996 Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer)
997 : File(disk, inode, ownBuffer)
998 {
999 }
1000
1001
Attribute(const Inode & inode)1002 Attribute::Attribute(const Inode &inode)
1003 : File(inode)
1004 {
1005 }
1006
1007
~Attribute()1008 Attribute::~Attribute()
1009 {
1010 }
1011
1012
1013 status_t
InitCheck() const1014 Attribute::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
1024 status_t
CopyTo(const char *,bool,Inode::Source *)1025 Attribute::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
Directory(Disk * disk,bfs_inode * inode,bool ownBuffer)1040 Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer)
1041 : DataStream(disk, inode, ownBuffer),
1042 fTree(NULL)
1043 {
1044 }
1045
1046
Directory(const Inode & inode)1047 Directory::Directory(const Inode &inode)
1048 : DataStream(inode),
1049 fTree(NULL)
1050 {
1051 }
1052
1053
~Directory()1054 Directory::~Directory()
1055 {
1056 delete fTree;
1057 }
1058
1059
1060 status_t
InitCheck() const1061 Directory::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
1071 status_t
CopyTo(const char * root,bool fullPath,Inode::Source * source)1072 Directory::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
1117 status_t
Rewind()1118 Directory::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
1129 status_t
GetNextEntry(char * name,block_run * run)1130 Directory::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
1151 status_t
GetNextEntry(block_run * run)1152 Directory::GetNextEntry(block_run *run)
1153 {
1154 char name[B_FILE_NAME_LENGTH];
1155
1156 return GetNextEntry(name, run);
1157 }
1158
1159
1160 status_t
Contains(const block_run * run)1161 Directory::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
1180 status_t
Contains(const Inode * inode)1181 Directory::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
1209 status_t
FindEntry(const char * name,block_run * run)1210 Directory::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
1234 status_t
AddEntry(Inode * inode)1235 Directory::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
1281 status_t
CreateTree()1282 Directory::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
1296 status_t
GetTree(BPlusTree ** tree)1297 Directory::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
Symlink(Disk * disk,bfs_inode * inode,bool ownBuffer)1312 Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer)
1313 : Inode(disk,inode,ownBuffer)
1314 {
1315 }
1316
1317
Symlink(const Inode & inode)1318 Symlink::Symlink(const Inode &inode)
1319 : Inode(inode)
1320 {
1321 }
1322
1323
~Symlink()1324 Symlink::~Symlink()
1325 {
1326 }
1327
1328
1329 status_t
InitCheck() const1330 Symlink::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
1340 status_t
CopyTo(const char * root,bool fullPath,Inode::Source * source)1341 Symlink::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
1386 status_t
LinksTo(char * to,size_t maxLength)1387 Symlink::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