1 /*
2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2008-2014, Axel Dörfler, axeld@pinc-software.de.
4 * This file may be used under the terms of the MIT License.
5 */
6
7
8 #include "Inode.h"
9
10 #include <string.h>
11 #include <util/AutoLock.h>
12 #include <NodeMonitor.h>
13
14 #include "CachedBlock.h"
15 #include "CRCTable.h"
16 #include "DataStream.h"
17 #include "DirectoryIterator.h"
18 #include "ExtentStream.h"
19 #include "HTree.h"
20 #include "Utility.h"
21
22
23 #undef ASSERT
24 //#define TRACE_EXT2
25 #ifdef TRACE_EXT2
26 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
27 # define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); }
28 #else
29 # define TRACE(x...) ;
30 # define ASSERT(x) ;
31 #endif
32 #define ERROR(x...) dprintf("\33[34mext2:\33[0m " x)
33
34
35 #define EXT2_EA_CHECKSUM_SIZE (offsetof(ext2_inode, checksum_high) \
36 + sizeof(uint16) - EXT2_INODE_NORMAL_SIZE)
37
38
Inode(Volume * volume,ino_t id)39 Inode::Inode(Volume* volume, ino_t id)
40 :
41 fVolume(volume),
42 fID(id),
43 fCache(NULL),
44 fMap(NULL),
45 fUnlinked(false),
46 fHasExtraAttributes(false)
47 {
48 rw_lock_init(&fLock, "ext2 inode");
49 recursive_lock_init(&fSmallDataLock, "ext2 inode small data");
50 memset(&fNode, 0, sizeof(fNode));
51
52 TRACE("Inode::Inode(): ext2_inode: %lu, disk inode: %" B_PRIu32
53 "\n", sizeof(ext2_inode), fVolume->InodeSize());
54 fNodeSize = sizeof(ext2_inode) > fVolume->InodeSize()
55 ? fVolume->InodeSize() : sizeof(ext2_inode);
56
57 fInitStatus = UpdateNodeFromDisk();
58 if (fInitStatus == B_OK) {
59 fHasExtraAttributes = (fNodeSize == sizeof(ext2_inode)
60 && fNode.ExtraInodeSize() + EXT2_INODE_NORMAL_SIZE
61 == sizeof(ext2_inode));
62
63 if (IsDirectory() || (IsSymLink() && Size() < 60)) {
64 TRACE("Inode::Inode(): Not creating the file cache\n");
65 fInitStatus = B_OK;
66 } else
67 fInitStatus = CreateFileCache();
68 } else
69 TRACE("Inode: Failed initialization\n");
70 }
71
72
Inode(Volume * volume)73 Inode::Inode(Volume* volume)
74 :
75 fVolume(volume),
76 fID(0),
77 fCache(NULL),
78 fMap(NULL),
79 fUnlinked(false),
80 fInitStatus(B_NO_INIT)
81 {
82 rw_lock_init(&fLock, "ext2 inode");
83 recursive_lock_init(&fSmallDataLock, "ext2 inode small data");
84 memset(&fNode, 0, sizeof(fNode));
85
86 TRACE("Inode::Inode(): ext2_inode: %lu, disk inode: %" B_PRIu32 "\n",
87 sizeof(ext2_inode), fVolume->InodeSize());
88 fNodeSize = sizeof(ext2_inode) > fVolume->InodeSize()
89 ? fVolume->InodeSize() : sizeof(ext2_inode);
90 }
91
92
~Inode()93 Inode::~Inode()
94 {
95 TRACE("Inode destructor\n");
96
97 DeleteFileCache();
98
99 TRACE("Inode destructor: Done\n");
100 }
101
102
103 status_t
InitCheck()104 Inode::InitCheck()
105 {
106 return fInitStatus;
107 }
108
109
110 void
WriteLockInTransaction(Transaction & transaction)111 Inode::WriteLockInTransaction(Transaction& transaction)
112 {
113 acquire_vnode(fVolume->FSVolume(), ID());
114
115 TRACE("Inode::WriteLockInTransaction(): Locking\n");
116 rw_lock_write_lock(&fLock);
117
118 transaction.AddListener(this);
119 }
120
121
122 status_t
WriteBack(Transaction & transaction)123 Inode::WriteBack(Transaction& transaction)
124 {
125 off_t blockNum;
126
127 status_t status = fVolume->GetInodeBlock(fID, blockNum);
128 if (status != B_OK)
129 return status;
130
131 if (Node().Size() > 0x7fffffffLL) {
132 status = fVolume->ActivateLargeFiles(transaction);
133 if (status != B_OK)
134 return status;
135 }
136
137 CachedBlock cached(fVolume);
138 uint8* inodeBlockData = cached.SetToWritable(transaction, blockNum);
139 if (inodeBlockData == NULL)
140 return B_IO_ERROR;
141
142 TRACE("Inode::WriteBack(): Inode ID: %" B_PRIdINO ", inode block: %"
143 B_PRIdOFF ", data: %p, index: %" B_PRIu32 ", inode size: %" B_PRIu32
144 ", node size: %" B_PRIu32 ", this: %p, node: %p\n",
145 fID, blockNum, inodeBlockData, fVolume->InodeBlockIndex(fID),
146 fVolume->InodeSize(), fNodeSize, this, &fNode);
147 ext2_inode* inode = (ext2_inode*)(inodeBlockData +
148 fVolume->InodeBlockIndex(fID) * fVolume->InodeSize());
149 memcpy(inode, (uint8*)&fNode, fNodeSize);
150
151 if (fVolume->HasMetaGroupChecksumFeature()) {
152 uint32 checksum = _InodeChecksum(inode);
153 inode->checksum = checksum & 0xffff;
154 if (fNodeSize > EXT2_INODE_NORMAL_SIZE
155 && fNode.ExtraInodeSize() >= EXT2_EA_CHECKSUM_SIZE) {
156 inode->checksum_high = checksum >> 16;
157 }
158 }
159 TRACE("Inode::WriteBack() finished %" B_PRId32 "\n", Node().stream.direct[0]);
160
161 return B_OK;
162 }
163
164
165 status_t
UpdateNodeFromDisk()166 Inode::UpdateNodeFromDisk()
167 {
168 off_t blockNum;
169
170 status_t status = fVolume->GetInodeBlock(fID, blockNum);
171 if (status != B_OK)
172 return status;
173
174 TRACE("inode %" B_PRIdINO " at block %" B_PRIdOFF "\n", fID, blockNum);
175
176 CachedBlock cached(fVolume);
177 const uint8* inodeBlock = cached.SetTo(blockNum);
178
179 if (inodeBlock == NULL)
180 return B_IO_ERROR;
181
182 TRACE("Inode size: %" B_PRIu32 ", inode index: %" B_PRIu32 "\n",
183 fVolume->InodeSize(), fVolume->InodeBlockIndex(fID));
184 ext2_inode* inode = (ext2_inode*)(inodeBlock
185 + fVolume->InodeBlockIndex(fID) * fVolume->InodeSize());
186
187 TRACE("Attempting to copy inode data from %p to %p, ext2_inode "
188 "size: %" B_PRIu32 "\n", inode, &fNode, fNodeSize);
189
190 memcpy(&fNode, inode, fNodeSize);
191
192 if (fVolume->HasMetaGroupChecksumFeature()) {
193 uint32 checksum = _InodeChecksum(inode);
194 uint32 provided = fNode.checksum;
195 if (fNodeSize > EXT2_INODE_NORMAL_SIZE
196 && fNode.ExtraInodeSize() >= EXT2_EA_CHECKSUM_SIZE) {
197 provided |= ((uint32)fNode.checksum_high << 16);
198 } else
199 checksum &= 0xffff;
200 if (provided != checksum) {
201 ERROR("Inode::UpdateNodeFromDisk(%" B_PRIdOFF "): "
202 "verification failed\n", blockNum);
203 return B_BAD_DATA;
204 }
205 }
206
207 uint32 numLinks = fNode.NumLinks();
208 fUnlinked = numLinks == 0 || (IsDirectory() && numLinks == 1);
209
210 return B_OK;
211 }
212
213
214 status_t
CheckPermissions(int accessMode) const215 Inode::CheckPermissions(int accessMode) const
216 {
217 // you never have write access to a read-only volume
218 if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly())
219 return B_READ_ONLY_DEVICE;
220
221 return check_access_permissions(accessMode, Mode(), (gid_t)fNode.GroupID(),
222 (uid_t)fNode.UserID());
223 }
224
225
226 status_t
FindBlock(off_t offset,fsblock_t & block,uint32 * _count)227 Inode::FindBlock(off_t offset, fsblock_t& block, uint32 *_count)
228 {
229 if (Flags() & EXT2_INODE_EXTENTS) {
230 ExtentStream stream(fVolume, this, &fNode.extent_stream, Size());
231 return stream.FindBlock(offset, block, _count);
232 }
233 DataStream stream(fVolume, &fNode.stream, Size());
234 return stream.FindBlock(offset, block, _count);
235 }
236
237
238 status_t
ReadAt(off_t pos,uint8 * buffer,size_t * _length)239 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
240 {
241 return file_cache_read(FileCache(), NULL, pos, buffer, _length);
242 }
243
244
245 status_t
WriteAt(Transaction & transaction,off_t pos,const uint8 * buffer,size_t * _length)246 Inode::WriteAt(Transaction& transaction, off_t pos, const uint8* buffer,
247 size_t* _length)
248 {
249 TRACE("Inode::WriteAt(%" B_PRIdOFF ", %p, *(%p) = %" B_PRIuSIZE ")\n", pos,
250 buffer, _length, *_length);
251 ReadLocker readLocker(fLock);
252
253 if (!HasFileCache())
254 return B_BAD_VALUE;
255
256 if (pos < 0)
257 return B_BAD_VALUE;
258
259 readLocker.Unlock();
260
261 TRACE("Inode::WriteAt(): Starting transaction\n");
262 transaction.Start(fVolume->GetJournal());
263
264 WriteLocker writeLocker(fLock);
265
266 TRACE("Inode::WriteAt(): Updating modification time\n");
267 struct timespec timespec;
268 _BigtimeToTimespec(real_time_clock_usecs(), ×pec);
269 SetModificationTime(×pec);
270
271 // NOTE: Debugging info to find why sometimes resize doesn't happen
272 size_t length = *_length;
273 #ifdef TRACE_EXT2
274 off_t oldEnd = pos + length;
275 TRACE("Inode::WriteAt(): Old calc for end? %x:%x\n",
276 (int)(oldEnd >> 32), (int)(oldEnd & 0xFFFFFFFF));
277 #endif
278
279 off_t end = pos + (off_t)length;
280 off_t oldSize = Size();
281
282 TRACE("Inode::WriteAt(): Old size: %" B_PRIdOFF ":%" B_PRIdOFF
283 ", new size: %" B_PRIdOFF ":%" B_PRIdOFF "\n",
284 oldSize >> 32, oldSize & 0xFFFFFFFF,
285 end >> 32, end & 0xFFFFFFFF);
286
287 if (end > oldSize) {
288 status_t status = Resize(transaction, end);
289 if (status != B_OK) {
290 *_length = 0;
291 WriteLockInTransaction(transaction);
292 return status;
293 }
294
295 status = WriteBack(transaction);
296 if (status != B_OK) {
297 *_length = 0;
298 WriteLockInTransaction(transaction);
299 return status;
300 }
301 }
302
303 writeLocker.Unlock();
304
305 if (oldSize < pos)
306 FillGapWithZeros(oldSize, pos);
307
308 if (length == 0) {
309 // Probably just changed the file size with the pos parameter
310 return B_OK;
311 }
312
313 TRACE("Inode::WriteAt(): Performing write: %p, %" B_PRIdOFF ", %p, %"
314 B_PRIuSIZE "\n", FileCache(), pos, buffer, *_length);
315 status_t status = file_cache_write(FileCache(), NULL, pos, buffer,
316 _length);
317
318 WriteLockInTransaction(transaction);
319
320 TRACE("Inode::WriteAt(): Done\n");
321
322 return status;
323 }
324
325
326 status_t
FillGapWithZeros(off_t start,off_t end)327 Inode::FillGapWithZeros(off_t start, off_t end)
328 {
329 TRACE("Inode::FileGapWithZeros(%" B_PRIdOFF " - %" B_PRIdOFF ")\n", start,
330 end);
331
332 while (start < end) {
333 size_t size;
334
335 if (end > start + 1024 * 1024 * 1024)
336 size = 1024 * 1024 * 1024;
337 else
338 size = end - start;
339
340 TRACE("Inode::FillGapWithZeros(): Calling file_cache_write(%p, NULL, "
341 "%" B_PRIdOFF ", NULL, &(%" B_PRIuSIZE ") = %p)\n", fCache, start,
342 size, &size);
343 status_t status = file_cache_write(fCache, NULL, start, NULL,
344 &size);
345 if (status != B_OK)
346 return status;
347
348 start += size;
349 }
350
351 return B_OK;
352 }
353
354
355 status_t
Resize(Transaction & transaction,off_t size)356 Inode::Resize(Transaction& transaction, off_t size)
357 {
358 TRACE("Inode::Resize() ID:%" B_PRIdINO " size: %" B_PRIdOFF "\n", ID(),
359 size);
360 if (size < 0)
361 return B_BAD_VALUE;
362
363 off_t oldSize = Size();
364
365 if (size == oldSize)
366 return B_OK;
367
368 TRACE("Inode::Resize(): old size: %" B_PRIdOFF ", new size: %" B_PRIdOFF
369 "\n", oldSize, size);
370
371 status_t status;
372 if (size > oldSize) {
373 status = _EnlargeDataStream(transaction, size);
374 if (status != B_OK) {
375 // Restore original size
376 _ShrinkDataStream(transaction, oldSize);
377 }
378 } else
379 status = _ShrinkDataStream(transaction, size);
380
381 TRACE("Inode::Resize(): Updating file map and cache\n");
382
383 if (status != B_OK)
384 return status;
385
386 file_cache_set_size(FileCache(), size);
387 file_map_set_size(Map(), size);
388
389 TRACE("Inode::Resize(): Writing back inode changes. Size: %" B_PRIdOFF
390 "\n", Size());
391
392 return WriteBack(transaction);
393 }
394
395
396 status_t
InitDirectory(Transaction & transaction,Inode * parent)397 Inode::InitDirectory(Transaction& transaction, Inode* parent)
398 {
399 TRACE("Inode::InitDirectory()\n");
400 uint32 blockSize = fVolume->BlockSize();
401
402 status_t status = Resize(transaction, blockSize);
403 if (status != B_OK)
404 return status;
405
406 fsblock_t blockNum;
407 if (Flags() & EXT2_INODE_EXTENTS) {
408 ExtentStream stream(fVolume, this, &fNode.extent_stream, Size());
409 status = stream.FindBlock(0, blockNum);
410 } else {
411 DataStream stream(fVolume, &fNode.stream, Size());
412 status = stream.FindBlock(0, blockNum);
413 }
414 if (status != B_OK)
415 return status;
416
417 CachedBlock cached(fVolume);
418 uint8* block = cached.SetToWritable(transaction, blockNum, true);
419
420 HTreeRoot* root = (HTreeRoot*)block;
421 root->dot.inode_id = fID;
422 root->dot.entry_length = 12;
423 root->dot.name_length = 1;
424 root->dot.file_type = EXT2_TYPE_DIRECTORY;
425 root->dot_entry_name[0] = '.';
426
427 root->dotdot.inode_id = parent == NULL ? fID : parent->ID();
428 uint32 dotdotlength = blockSize - 12;
429 if (fVolume->HasMetaGroupChecksumFeature())
430 dotdotlength -= sizeof(ext2_dir_entry_tail);
431 root->dotdot.entry_length = dotdotlength;
432 root->dotdot.name_length = 2;
433 root->dotdot.file_type = EXT2_TYPE_DIRECTORY;
434 root->dotdot_entry_name[0] = '.';
435 root->dotdot_entry_name[1] = '.';
436
437 parent->IncrementNumLinks(transaction);
438
439 SetDirEntryChecksum((uint8*)root);
440
441 return parent->WriteBack(transaction);
442 }
443
444
445 status_t
Unlink(Transaction & transaction)446 Inode::Unlink(Transaction& transaction)
447 {
448 uint32 numLinks = fNode.NumLinks();
449 TRACE("Inode::Unlink(): Current links: %" B_PRIu32 "\n", numLinks);
450
451 if (numLinks == 0) {
452 ERROR("Inode::Unlink(): no links\n");
453 return B_BAD_VALUE;
454 }
455
456 if ((IsDirectory() && numLinks == 2) || (numLinks == 1)) {
457 fUnlinked = true;
458
459 TRACE("Inode::Unlink(): Putting inode in orphan list\n");
460 ino_t firstOrphanID;
461 status_t status = fVolume->SaveOrphan(transaction, fID, firstOrphanID);
462 if (status != B_OK)
463 return status;
464
465 if (firstOrphanID != 0) {
466 Vnode firstOrphan(fVolume, firstOrphanID);
467 Inode* nextOrphan;
468
469 status = firstOrphan.Get(&nextOrphan);
470 if (status != B_OK)
471 return status;
472
473 fNode.SetNextOrphan(nextOrphan->ID());
474 } else {
475 // Next orphan link is stored in deletion time
476 fNode.deletion_time = 0;
477 }
478
479 fNode.num_links = 0;
480
481 status = remove_vnode(fVolume->FSVolume(), fID);
482 if (status != B_OK)
483 return status;
484 } else if (!IsDirectory() || numLinks > 2)
485 fNode.SetNumLinks(--numLinks);
486
487 return WriteBack(transaction);
488 }
489
490
491 /*static*/ status_t
Create(Transaction & transaction,Inode * parent,const char * name,int32 mode,int openMode,uint8 type,bool * _created,ino_t * _id,Inode ** _inode,fs_vnode_ops * vnodeOps,uint32 publishFlags)492 Inode::Create(Transaction& transaction, Inode* parent, const char* name,
493 int32 mode, int openMode, uint8 type, bool* _created, ino_t* _id,
494 Inode** _inode, fs_vnode_ops* vnodeOps, uint32 publishFlags)
495 {
496 TRACE("Inode::Create()\n");
497 Volume* volume = transaction.GetVolume();
498
499 DirectoryIterator* entries = NULL;
500 ObjectDeleter<DirectoryIterator> entriesDeleter;
501
502 if (parent != NULL) {
503 parent->WriteLockInTransaction(transaction);
504
505 // don't create anything in removed directories
506 bool removed;
507 if (get_vnode_removed(volume->FSVolume(), parent->ID(), &removed)
508 == B_OK && removed) {
509 return B_ENTRY_NOT_FOUND;
510 }
511
512 TRACE("Inode::Create(): Looking up entry destination\n");
513 HTree htree(volume, parent);
514
515 status_t status = htree.Lookup(name, &entries);
516 if (status == B_ENTRY_NOT_FOUND) {
517 panic("We need to add the first node.\n");
518 return B_ERROR;
519 }
520 if (status != B_OK)
521 return status;
522 entriesDeleter.SetTo(entries);
523
524 TRACE("Inode::Create(): Looking up to see if file already exists\n");
525 ino_t entryID;
526
527 status = entries->FindEntry(name, &entryID);
528 if (status == B_OK) {
529 // File already exists
530 TRACE("Inode::Create(): File already exists\n");
531 if (S_ISDIR(mode) || S_ISLNK(mode) || (openMode & O_EXCL) != 0)
532 return B_FILE_EXISTS;
533
534 Vnode vnode(volume, entryID);
535 Inode* inode;
536
537 status = vnode.Get(&inode);
538 if (status != B_OK) {
539 TRACE("Inode::Create() Failed to get the inode from the "
540 "vnode\n");
541 return B_ENTRY_NOT_FOUND;
542 }
543
544 if (inode->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY)
545 return B_IS_A_DIRECTORY;
546 if ((openMode & O_DIRECTORY) != 0 && !inode->IsDirectory())
547 return B_NOT_A_DIRECTORY;
548
549 if (inode->CheckPermissions(open_mode_to_access(openMode)
550 | ((openMode & O_TRUNC) != 0 ? W_OK : 0)) != B_OK)
551 return B_NOT_ALLOWED;
552
553 if ((openMode & O_TRUNC) != 0) {
554 // Truncate requested
555 TRACE("Inode::Create(): Truncating file\n");
556 inode->WriteLockInTransaction(transaction);
557
558 status = inode->Resize(transaction, 0);
559 if (status != B_OK)
560 return status;
561 }
562
563 if (_created != NULL)
564 *_created = false;
565 if (_id != NULL)
566 *_id = inode->ID();
567 if (_inode != NULL)
568 *_inode = inode;
569
570 if (_id != NULL || _inode != NULL)
571 vnode.Keep();
572
573 TRACE("Inode::Create(): Done opening file\n");
574 return B_OK;
575 /*} else if ((mode & S_ATTR_DIR) == 0) {
576 TRACE("Inode::Create(): (mode & S_ATTR_DIR) == 0\n");
577 return B_BAD_VALUE;*/
578 } else if ((openMode & O_DIRECTORY) != 0) {
579 TRACE("Inode::Create(): (openMode & O_DIRECTORY) != 0\n");
580 return B_ENTRY_NOT_FOUND;
581 }
582
583 // Return to initial position
584 TRACE("Inode::Create(): Restarting iterator\n");
585 entries->Restart();
586 }
587
588 status_t status;
589 if (parent != NULL) {
590 status = parent->CheckPermissions(W_OK);
591 if (status != B_OK)
592 return status;
593 }
594
595 TRACE("Inode::Create(): Allocating inode\n");
596 ino_t id;
597 status = volume->AllocateInode(transaction, parent, mode, id);
598 if (status != B_OK) {
599 ERROR("Inode::Create(): AllocateInode() failed\n");
600 return status;
601 }
602
603 if (entries != NULL) {
604 size_t nameLength = strlen(name);
605 status = entries->AddEntry(transaction, name, nameLength, id, type);
606 if (status != B_OK) {
607 ERROR("Inode::Create(): AddEntry() failed\n");
608 return status;
609 }
610 }
611
612 TRACE("Inode::Create(): Creating inode\n");
613 Inode* inode = new(std::nothrow) Inode(volume);
614 if (inode == NULL)
615 return B_NO_MEMORY;
616
617 TRACE("Inode::Create(): Getting node structure\n");
618 ext2_inode& node = inode->Node();
619 TRACE("Inode::Create(): Initializing inode data\n");
620 memset(&node, 0, sizeof(ext2_inode));
621 node.SetMode(mode);
622 node.SetUserID(geteuid());
623 node.SetGroupID(parent != NULL ? parent->Node().GroupID() : getegid());
624 node.SetNumLinks(inode->IsDirectory() ? 2 : 1);
625 TRACE("Inode::Create(): Updating time\n");
626 struct timespec timespec;
627 _BigtimeToTimespec(real_time_clock_usecs(), ×pec);
628 inode->SetAccessTime(×pec);
629 inode->SetCreationTime(×pec);
630 inode->SetModificationTime(×pec);
631 if (parent != NULL)
632 node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED);
633 if (volume->HasExtentsFeature()
634 && (inode->IsDirectory() || inode->IsFile())) {
635 node.SetFlag(EXT2_INODE_EXTENTS);
636 ExtentStream stream(volume, inode, &node.extent_stream, 0);
637 stream.Init();
638 ASSERT(stream.Check());
639 }
640
641 if (sizeof(ext2_inode) < volume->InodeSize())
642 node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE);
643
644 TRACE("Inode::Create(): Updating ID\n");
645 inode->fID = id;
646
647 if (inode->IsDirectory()) {
648 TRACE("Inode::Create(): Initializing directory\n");
649 status = inode->InitDirectory(transaction, parent);
650 if (status != B_OK) {
651 ERROR("Inode::Create(): InitDirectory() failed\n");
652 delete inode;
653 return status;
654 }
655 }
656
657 // TODO: Maybe it can be better
658 /*if (volume->HasExtendedAttributes()) {
659 TRACE("Inode::Create(): Initializing extended attributes\n");
660 uint32 blockGroup = 0;
661 uint32 pos = 0;
662 uint32 allocated;
663
664 status = volume->AllocateBlocks(transaction, 1, 1, blockGroup, pos,
665 allocated);
666 if (status != B_OK)
667 return status;
668
669 // Clear the new block
670 uint32 blockNum = volume->FirstDataBlock() + pos +
671 volume->BlocksPerGroup() * blockGroup;
672 CachedBlock cached(volume);
673 cached.SetToWritable(transaction, blockNum, true);
674
675 node.SetExtendedAttributesBlock(blockNum);
676 }*/
677
678 TRACE("Inode::Create(): Saving inode\n");
679 status = inode->WriteBack(transaction);
680 if (status != B_OK) {
681 delete inode;
682 return status;
683 }
684
685 TRACE("Inode::Create(): Creating vnode\n");
686
687 Vnode vnode;
688 status = vnode.Publish(transaction, inode, vnodeOps, publishFlags);
689 if (status != B_OK)
690 return status;
691
692 if (!inode->IsSymLink()) {
693 // Vnode::Publish doesn't publish symlinks
694 if (!inode->IsDirectory()) {
695 status = inode->CreateFileCache();
696 if (status != B_OK)
697 return status;
698 }
699
700 inode->WriteLockInTransaction(transaction);
701 }
702
703 if (_created)
704 *_created = true;
705 if (_id != NULL)
706 *_id = id;
707 if (_inode != NULL)
708 *_inode = inode;
709
710 if (_id != NULL || _inode != NULL)
711 vnode.Keep();
712
713 TRACE("Inode::Create(): Deleting entries iterator\n");
714 DirectoryIterator* iterator = entriesDeleter.Detach();
715 TRACE("Inode::Create(): Entries iterator: %p\n", entries);
716 delete iterator;
717 TRACE("Inode::Create(): Done\n");
718
719 return B_OK;
720 }
721
722
723 status_t
CreateFileCache()724 Inode::CreateFileCache()
725 {
726 TRACE("Inode::CreateFileCache()\n");
727
728 if (fCache != NULL)
729 return B_OK;
730
731 TRACE("Inode::CreateFileCache(): Creating file cache: %" B_PRIu32 ", %"
732 B_PRIdINO ", %" B_PRIdOFF "\n", fVolume->ID(), ID(), Size());
733
734 fCache = file_cache_create(fVolume->ID(), ID(), Size());
735 if (fCache == NULL) {
736 ERROR("Inode::CreateFileCache(): Failed to create file cache\n");
737 return B_ERROR;
738 }
739
740 fMap = file_map_create(fVolume->ID(), ID(), Size());
741 if (fMap == NULL) {
742 ERROR("Inode::CreateFileCache(): Failed to create file map\n");
743 file_cache_delete(fCache);
744 fCache = NULL;
745 return B_ERROR;
746 }
747
748 TRACE("Inode::CreateFileCache(): Done\n");
749
750 return B_OK;
751 }
752
753
754 void
DeleteFileCache()755 Inode::DeleteFileCache()
756 {
757 TRACE("Inode::DeleteFileCache()\n");
758
759 if (fCache == NULL)
760 return;
761
762 file_cache_delete(fCache);
763 file_map_delete(fMap);
764
765 fCache = NULL;
766 fMap = NULL;
767 }
768
769
770 status_t
EnableFileCache()771 Inode::EnableFileCache()
772 {
773 if (fCache == NULL)
774 return B_BAD_VALUE;
775
776 file_cache_enable(fCache);
777 return B_OK;
778 }
779
780
781 status_t
DisableFileCache()782 Inode::DisableFileCache()
783 {
784 status_t error = file_cache_disable(fCache);
785 if (error != B_OK)
786 return error;
787
788 return B_OK;
789 }
790
791
792 status_t
Sync()793 Inode::Sync()
794 {
795 if (HasFileCache())
796 return file_cache_sync(fCache);
797
798 return B_OK;
799 }
800
801
802 void
TransactionDone(bool success)803 Inode::TransactionDone(bool success)
804 {
805 if (!success) {
806 // Revert any changes to the inode
807 if (UpdateNodeFromDisk() != B_OK)
808 panic("Failed to reload inode from disk!\n");
809 }
810 }
811
812
813 void
RemovedFromTransaction()814 Inode::RemovedFromTransaction()
815 {
816 TRACE("Inode::RemovedFromTransaction(): Unlocking\n");
817 rw_lock_write_unlock(&fLock);
818
819 put_vnode(fVolume->FSVolume(), ID());
820 }
821
822
823 status_t
_EnlargeDataStream(Transaction & transaction,off_t size)824 Inode::_EnlargeDataStream(Transaction& transaction, off_t size)
825 {
826 if (size < 0)
827 return B_BAD_VALUE;
828
829 TRACE("Inode::_EnlargeDataStream()\n");
830
831 uint32 blockSize = fVolume->BlockSize();
832 off_t oldSize = Size();
833 off_t maxSize = oldSize;
834 if (maxSize % blockSize != 0)
835 maxSize += blockSize - maxSize % blockSize;
836
837 if (size <= maxSize) {
838 // No need to allocate more blocks
839 TRACE("Inode::_EnlargeDataStream(): No need to allocate more blocks\n");
840 TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n",
841 size);
842 fNode.SetSize(size);
843 return B_OK;
844 }
845
846 off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1;
847 if (Flags() & EXT2_INODE_EXTENTS) {
848 ExtentStream stream(fVolume, this, &fNode.extent_stream, Size());
849 stream.Enlarge(transaction, end);
850 ASSERT(stream.Check());
851 } else {
852 DataStream stream(fVolume, &fNode.stream, oldSize);
853 stream.Enlarge(transaction, end);
854 }
855 TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n",
856 size);
857 fNode.SetSize(size);
858 TRACE("Inode::_EnlargeDataStream(): Setting allocated block count to %"
859 B_PRIdOFF "\n", end);
860 return _SetNumBlocks(NumBlocks() + end * (fVolume->BlockSize() / 512));
861 }
862
863
864 status_t
_ShrinkDataStream(Transaction & transaction,off_t size)865 Inode::_ShrinkDataStream(Transaction& transaction, off_t size)
866 {
867 TRACE("Inode::_ShrinkDataStream()\n");
868
869 if (size < 0)
870 return B_BAD_VALUE;
871
872 uint32 blockSize = fVolume->BlockSize();
873 off_t oldSize = Size();
874 off_t lastByte = oldSize == 0 ? 0 : oldSize - 1;
875 off_t minSize = (lastByte / blockSize + 1) * blockSize;
876 // Minimum size that doesn't require freeing blocks
877
878 if (size > minSize) {
879 // No need to allocate more blocks
880 TRACE("Inode::_ShrinkDataStream(): No need to allocate more blocks\n");
881 TRACE("Inode::_ShrinkDataStream(): Setting size to %" B_PRIdOFF "\n",
882 size);
883 fNode.SetSize(size);
884 return B_OK;
885 }
886
887 off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1;
888 if (Flags() & EXT2_INODE_EXTENTS) {
889 ExtentStream stream(fVolume, this, &fNode.extent_stream, Size());
890 stream.Shrink(transaction, end);
891 ASSERT(stream.Check());
892 } else {
893 DataStream stream(fVolume, &fNode.stream, oldSize);
894 stream.Shrink(transaction, end);
895 }
896
897 fNode.SetSize(size);
898 return _SetNumBlocks(NumBlocks() - end * (fVolume->BlockSize() / 512));
899 }
900
901
902 uint64
NumBlocks()903 Inode::NumBlocks()
904 {
905 if (fVolume->HugeFiles()) {
906 if (fNode.Flags() & EXT2_INODE_HUGE_FILE)
907 return fNode.NumBlocks64() * (fVolume->BlockSize() / 512);
908 else
909 return fNode.NumBlocks64();
910 } else
911 return fNode.NumBlocks();
912 }
913
914
915 status_t
_SetNumBlocks(uint64 numBlocks)916 Inode::_SetNumBlocks(uint64 numBlocks)
917 {
918 if (numBlocks <= 0xffffffff) {
919 fNode.SetNumBlocks(numBlocks);
920 fNode.ClearFlag(EXT2_INODE_HUGE_FILE);
921 return B_OK;
922 }
923 if (!fVolume->HugeFiles())
924 return E2BIG;
925
926 if (numBlocks > 0xffffffffffffULL) {
927 fNode.SetFlag(EXT2_INODE_HUGE_FILE);
928 numBlocks /= (fVolume->BlockSize() / 512);
929 } else
930 fNode.ClearFlag(EXT2_INODE_HUGE_FILE);
931
932 fNode.SetNumBlocks64(numBlocks);
933 return B_OK;
934 }
935
936
937 void
IncrementNumLinks(Transaction & transaction)938 Inode::IncrementNumLinks(Transaction& transaction)
939 {
940 fNode.SetNumLinks(fNode.NumLinks() + 1);
941 if (IsIndexed() && (fNode.NumLinks() >= EXT2_INODE_MAX_LINKS
942 || fNode.NumLinks() == 2)) {
943 fNode.SetNumLinks(1);
944 fVolume->ActivateDirNLink(transaction);
945 }
946 }
947
948
949 uint32
_InodeChecksum(ext2_inode * inode)950 Inode::_InodeChecksum(ext2_inode* inode)
951 {
952 size_t offset = offsetof(ext2_inode, checksum);
953 uint32 number = fID;
954 uint32 checksum = calculate_crc32c(fVolume->ChecksumSeed(),
955 (uint8*)&number, sizeof(number));
956 uint32 gen = fNode.generation;
957 checksum = calculate_crc32c(checksum, (uint8*)&gen, sizeof(gen));
958 checksum = calculate_crc32c(checksum, (uint8*)inode, offset);
959 uint16 dummy = 0;
960 checksum = calculate_crc32c(checksum, (uint8*)&dummy, sizeof(dummy));
961 offset += sizeof(dummy);
962 checksum = calculate_crc32c(checksum, (uint8*)inode + offset,
963 EXT2_INODE_NORMAL_SIZE - offset);
964 if (fNodeSize > EXT2_INODE_NORMAL_SIZE) {
965 offset = offsetof(ext2_inode, checksum_high);
966 checksum = calculate_crc32c(checksum, (uint8*)inode
967 + EXT2_INODE_NORMAL_SIZE, offset - EXT2_INODE_NORMAL_SIZE);
968 if (fNode.ExtraInodeSize() >= EXT2_EA_CHECKSUM_SIZE) {
969 checksum = calculate_crc32c(checksum, (uint8*)&dummy,
970 sizeof(dummy));
971 offset += sizeof(dummy);
972 }
973 checksum = calculate_crc32c(checksum, (uint8*)inode + offset,
974 fVolume->InodeSize() - offset);
975 }
976 return checksum;
977 }
978
979
980 ext2_dir_entry_tail*
_DirEntryTail(uint8 * block) const981 Inode::_DirEntryTail(uint8* block) const
982 {
983 return (ext2_dir_entry_tail*)(block + fVolume->BlockSize()
984 - sizeof(ext2_dir_entry_tail));
985 }
986
987
988 uint32
_DirEntryChecksum(uint8 * block,uint32 id,uint32 gen) const989 Inode::_DirEntryChecksum(uint8* block, uint32 id, uint32 gen) const
990 {
991 uint32 number = id;
992 uint32 checksum = calculate_crc32c(fVolume->ChecksumSeed(),
993 (uint8*)&number, sizeof(number));
994 checksum = calculate_crc32c(checksum, (uint8*)&gen, sizeof(gen));
995 checksum = calculate_crc32c(checksum, block,
996 fVolume->BlockSize() - sizeof(ext2_dir_entry_tail));
997 return checksum;
998 }
999
1000
1001 void
SetDirEntryChecksum(uint8 * block,uint32 id,uint32 gen)1002 Inode::SetDirEntryChecksum(uint8* block, uint32 id, uint32 gen)
1003 {
1004 if (fVolume->HasMetaGroupChecksumFeature()) {
1005 ext2_dir_entry_tail* tail = _DirEntryTail(block);
1006 tail->twelve = 12;
1007 tail->hexade = 0xde;
1008 tail->checksum = _DirEntryChecksum(block, id, gen);
1009 }
1010 }
1011
1012
1013 void
SetDirEntryChecksum(uint8 * block)1014 Inode::SetDirEntryChecksum(uint8* block)
1015 {
1016 SetDirEntryChecksum(block, fID, fNode.generation);
1017 }
1018
1019
1020 uint32
_ExtentLength(ext2_extent_stream * stream) const1021 Inode::_ExtentLength(ext2_extent_stream* stream) const
1022 {
1023 return sizeof(struct ext2_extent_header)
1024 + stream->extent_header.MaxEntries()
1025 * sizeof(struct ext2_extent_entry);
1026 }
1027
1028
1029 uint32
_ExtentChecksum(ext2_extent_stream * stream) const1030 Inode::_ExtentChecksum(ext2_extent_stream* stream) const
1031 {
1032 uint32 number = fID;
1033 uint32 checksum = calculate_crc32c(fVolume->ChecksumSeed(),
1034 (uint8*)&number, sizeof(number));
1035 checksum = calculate_crc32c(checksum, (uint8*)&fNode.generation,
1036 sizeof(fNode.generation));
1037 checksum = calculate_crc32c(checksum, (uint8*)stream,
1038 _ExtentLength(stream));
1039 return checksum;
1040 }
1041
1042
1043 void
SetExtentChecksum(ext2_extent_stream * stream)1044 Inode::SetExtentChecksum(ext2_extent_stream* stream)
1045 {
1046 if (fVolume->HasMetaGroupChecksumFeature()) {
1047 uint32 checksum = _ExtentChecksum(stream);
1048 struct ext2_extent_tail *tail = (struct ext2_extent_tail *)
1049 ((uint8*)stream + _ExtentLength(stream));
1050 tail->checksum = checksum;
1051 }
1052 }
1053
1054
1055 bool
VerifyExtentChecksum(ext2_extent_stream * stream)1056 Inode::VerifyExtentChecksum(ext2_extent_stream* stream)
1057 {
1058 if (fVolume->HasMetaGroupChecksumFeature()) {
1059 uint32 checksum = _ExtentChecksum(stream);
1060 struct ext2_extent_tail *tail = (struct ext2_extent_tail *)
1061 ((uint8*)stream + _ExtentLength(stream));
1062 return tail->checksum == checksum;
1063 }
1064 return true;
1065 }
1066
1067