The Pedigree Project 0.1
Ext2Node.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "Ext2Node.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/compiler.h"
24#include "pedigree/kernel/machine/Disk.h"
25#include "pedigree/kernel/syscallError.h"
26#include "pedigree/kernel/utilities/assert.h"
27#include "pedigree/kernel/utilities/utility.h"
28
29#include "Ext2Filesystem.h"
30#include "ext2.h"
31#include "modules/system/vfs/File.h"
32
33Ext2InodeState::Ext2InodeState(Inode* pInode, Ext2Filesystem* filesystem)
34 : blocks(),
35 metadataBlocks(0),
36 allocatedDataBlocks(0),
37 size(LITTLE_TO_HOST32(pInode->i_size)),
38 dataLock(),
39 writeLock(),
40 writebackLock(),
41 pageLoans(0),
42 references(1),
43 orphan(false),
44 futexIdentity(0),
45 files(),
46 metadata(pInode),
47 filesystem(filesystem) {
48 reloadMappings(pInode, filesystem);
49}
50
51Ext2InodeState::~Ext2InodeState() {
52 delete cache;
53}
54
55void Ext2InodeState::reloadMappings(Inode* pInode, Ext2Filesystem* filesystem) {
56 assert(blocks.count() == 0);
57 size = LITTLE_TO_HOST32(pInode->i_size);
58 const size_t blockSize = filesystem->m_BlockSize;
59 uint32_t totalBlocks = 0;
60 bool inlineSymlink = false;
61 allocationValid = Ext2Node::decodeAllocation(*pInode, blockSize, totalBlocks, inlineSymlink);
62 if (!allocationValid) {
63 metadataBlocks = allocatedDataBlocks = 0;
64 return;
65 }
66 size_t dataBlockCount = 0;
67 if (!inlineSymlink) {
68 dataBlockCount = size / blockSize;
69 if (size % blockSize) {
70 ++dataBlockCount;
71 }
72 }
73
74 blocks.reserve(dataBlockCount, false);
75 metadataBlocks = 0;
76
77 for (size_t i = 0; i < 12 && i < dataBlockCount; i++)
78 blocks.pushBack(LITTLE_TO_HOST32(pInode->i_block[i]));
79
80 // We'll read these later.
81 for (size_t i = 12; i < dataBlockCount; ++i) {
82 blocks.pushBack(~0);
83 }
84 if (!inlineSymlink) {
85 for (size_t i = dataBlockCount; i < 12; ++i) {
86 const uint32_t block = LITTLE_TO_HOST32(pInode->i_block[i]);
87 if (block) {
88 while (blocks.count() <= i) {
89 blocks.pushBack(0);
90 }
91 blocks[i] = block;
92 }
93 }
94 const size_t entries = blockSize / sizeof(uint32_t);
95 loadMappings(filesystem, LITTLE_TO_HOST32(pInode->i_block[12]), 1, 12, entries);
96 loadMappings(filesystem, LITTLE_TO_HOST32(pInode->i_block[13]), 2, 12 + entries,
97 entries * entries);
98 loadMappings(filesystem, LITTLE_TO_HOST32(pInode->i_block[14]), 3,
99 12 + entries + entries * entries, entries * entries * entries);
100 }
101 allocationValid = totalBlocks >= metadataBlocks;
102 allocatedDataBlocks = allocationValid ? totalBlocks - metadataBlocks : 0;
103}
104
105void Ext2InodeState::loadMappings(Ext2Filesystem* filesystem, uint32_t block, unsigned depth,
106 size_t first, size_t span) {
107 if (!block) {
108 const size_t end = first + span < blocks.count() ? first + span : blocks.count();
109 for (size_t i = first; i < end; ++i) {
110 blocks[i] = 0;
111 }
112 return;
113 }
114 ++metadataBlocks;
115 const DiskReadView buffer = filesystem->readBlockView(block);
116 if (!buffer) {
117 return;
118 }
119 const size_t entries = filesystem->m_BlockSize / sizeof(uint32_t);
120 for (size_t i = 0; i < entries; ++i) {
121 uint32_t child;
122 if (!buffer.readAt(child, i * sizeof(child)))
123 return;
124 child = LITTLE_TO_HOST32(child);
125 if (depth > 1) {
126 loadMappings(filesystem, child, depth - 1, first + i * (span / entries), span / entries);
127 } else if (child || first + i < blocks.count()) {
128 while (blocks.count() <= first + i) {
129 blocks.pushBack(0);
130 }
131 blocks[first + i] = child;
132 }
133 }
134}
135
136Ext2Node::Ext2Node(uintptr_t inode_num, Inode* pInode, Ext2Filesystem* pFs)
137 : m_State(pFs->acquireInodeState(inode_num, pInode)),
138 m_pInode(m_State->metadata),
139 m_InodeNumber(inode_num),
140 m_pExt2Fs(pFs),
141 m_Blocks(m_State->blocks),
142 m_nMetadataBlocks(m_State->metadataBlocks),
143 m_nSize(m_State->size) {}
144
145Ext2Node::Ext2Node(uintptr_t inode, Inode* metadata, Ext2Filesystem* filesystem,
146 Ext2InodeState& admitted)
147 : m_State(&admitted),
148 m_pInode(admitted.metadata),
149 m_InodeNumber(inode),
150 m_pExt2Fs(filesystem),
151 m_Blocks(admitted.blocks),
152 m_nMetadataBlocks(admitted.metadataBlocks),
153 m_nSize(admitted.size) {}
154
156 m_pExt2Fs->releaseInodeState(m_InodeNumber, m_State, this);
157}
158
159uintptr_t Ext2Node::readBlock(uint64_t location) {
160 if (!m_State->allocationValid) {
161 SYSCALL_ERROR(IoError);
162 return 0;
163 }
164 // Sanity check.
165 uint32_t nBlock = location / m_pExt2Fs->m_BlockSize;
166 if (nBlock >= m_Blocks.count()) {
167 ERROR("Ext2Node::readBlock beyond blocks [" << nBlock << ", " << m_Blocks.count() << "]");
168 return 0;
169 }
170 if (location >= m_nSize) {
171 ERROR("Ext2Node::readBlock beyond size [" << location << ", " << m_nSize << "]");
172 return 0;
173 }
174
175 if (!ensureBlockLoaded(nBlock)) {
176 return 0;
177 }
178 uintptr_t result = m_pExt2Fs->readBlock(m_Blocks[nBlock]);
179 if (!result) {
180 return 0;
181 }
182
183 // Add any remaining offset we chopped off.
184 result += location % m_pExt2Fs->m_BlockSize;
185 return result;
186}
187
188void Ext2Node::writeBlock(uint64_t location) {
189 // Sanity check.
190 uint32_t nBlock = location / m_pExt2Fs->m_BlockSize;
191 if (nBlock >= m_Blocks.count())
192 return;
193 if (location >= m_nSize)
194 return;
195
196 // Update on disk.
197 if (!ensureBlockLoaded(nBlock)) {
198 return;
199 }
200 m_pExt2Fs->writeBlock(m_Blocks[nBlock]);
201}
202
203void Ext2Node::trackBlock(uint32_t block, bool writeInode) {
204 m_Blocks.pushBack(block);
205 if (block) {
206 ++m_State->allocatedDataBlocks;
207 }
208
209 updateAllocatedSectorCount();
210
211 if (writeInode) {
212 // Write updated inode.
213 m_pExt2Fs->writeInode(getInodeNumber());
214 }
215}
216
217bool Ext2Node::wipe(bool allocationLockHeld) {
218 OperationBarrier::Lease operation;
219 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
220 return false;
221 }
222 if (!m_State->allocationValid) {
223 SYSCALL_ERROR(IoError);
224 return false;
225 }
226 if (isInlineSymlink()) {
227 ByteSet(m_pInode->i_block, 0, sizeof(m_pInode->i_block));
228 m_nSize = 0;
229 m_pInode->i_size = 0;
230 m_pExt2Fs->writeInode(getInodeNumber());
231 return true;
232 }
233 if (!trimToBlocks(0, allocationLockHeld)) {
234 return false;
235 }
236 m_nSize = 0;
237 m_pInode->i_size = 0;
238 m_pExt2Fs->writeInode(getInodeNumber());
239 return true;
240}
241
242void Ext2Node::extend(size_t newSize) {
243 ensureLargeEnough(newSize, 0, 0);
244}
245
246void Ext2Node::extend(size_t newSize, uint64_t location, uint64_t size) {
247 ensureLargeEnough(newSize, location, size);
248}
249
250uint64_t Ext2Node::maximumFileSize() const {
251 const uint64_t blockSize = m_pExt2Fs->m_BlockSize;
252 const uint64_t entries = blockSize / sizeof(uint32_t);
253 const uint64_t maximumBlocks = 12 + entries + entries * entries;
254 constexpr uint64_t InodeSizeLimit = 0xffffffffULL;
255 return maximumBlocks > InodeSizeLimit / blockSize ? InodeSizeLimit : maximumBlocks * blockSize;
256}
257
258bool Ext2Node::ensureLargeEnough(size_t size, uint64_t location, uint64_t opsize, bool onlyBlocks,
259 bool nozeroblocks) {
260 if (!m_State->allocationValid) {
261 SYSCALL_ERROR(IoError);
262 return false;
263 }
264 const size_t blockSize = m_pExt2Fs->m_BlockSize;
265 if (size > maximumFileSize()) {
266 SYSCALL_ERROR(FileTooLarge);
267 return false;
268 }
269 const size_t oldBlocks = m_Blocks.count();
270 const size_t currentMaxSize = oldBlocks * blockSize;
271 if (size <= currentMaxSize) {
272 if (size > m_nSize && !onlyBlocks) {
273 if (!zeroRange(m_nSize, size)) {
274 return false;
275 }
276 m_nSize = size;
277 m_pInode->i_size = HOST_TO_LITTLE32(size);
278 m_pExt2Fs->writeInode(getInodeNumber());
279 }
280 return true;
281 }
282
283 const size_t delta = size - currentMaxSize;
284 const size_t deltaBlocks = delta / blockSize + (delta % blockSize != 0);
285 Vector<uint32_t> newBlocks;
286 if (!m_pExt2Fs->findFreeBlocks(m_InodeNumber, deltaBlocks, newBlocks)) {
287 return false;
288 }
289 Vector<uint32_t> pendingWrites;
290 bool success = true;
291 size_t attached = 0;
292 for (uint32_t block : newBlocks) {
293 if (!addBlock(block, &pendingWrites)) {
294 success = false;
295 break;
296 }
297 ++attached;
298 if (nozeroblocks) {
299 continue;
300 }
301 if (!m_pExt2Fs->m_pDisk->zero(static_cast<uint64_t>(block) * blockSize, blockSize)) {
302 SYSCALL_ERROR(IoError);
303 success = false;
304 break;
305 }
306 }
307 for (uint32_t block : pendingWrites) {
308 m_pExt2Fs->writeBlock(block);
309 m_pExt2Fs->unpinBlock(block);
310 }
311 if (success && !onlyBlocks && m_nSize < currentMaxSize) {
312 success = zeroRange(m_nSize, currentMaxSize);
313 }
314 if (!success) {
315 for (size_t i = attached; i < newBlocks.count(); ++i) {
316 m_pExt2Fs->releaseBlock(newBlocks[i], m_InodeNumber);
317 }
318 if (!trimToBlocks(oldBlocks)) {
319 ERROR("Ext2: unable to retire partially allocated extension blocks");
320 }
321 return false;
322 }
323 if (!onlyBlocks) {
324 m_nSize = size;
325 m_pInode->i_size = HOST_TO_LITTLE32(size);
326 }
327 m_pExt2Fs->writeInode(getInodeNumber());
328 return true;
329}
330
331bool Ext2Node::ensureBlockLoaded(size_t nBlock) {
332 if (nBlock >= m_Blocks.count()) {
333 FATAL("EXT2: ensureBlockLoaded: Algorithmic error [block " << nBlock << " > "
334 << m_Blocks.count() << "].");
335 }
336 if (m_Blocks[nBlock] == ~0U) {
337 return getBlockNumber(nBlock);
338 }
339
340 return true;
341}
342
343bool Ext2Node::getBlockNumber(size_t nBlock) {
344 size_t nPerBlock = m_pExt2Fs->m_BlockSize / 4;
345
346 assert(nBlock >= 12);
347
348 if (nBlock < nPerBlock + 12) {
349 return getBlockNumberIndirect(LITTLE_TO_HOST32(m_pInode->i_block[12]), 12, nBlock);
350 }
351
352 if (nBlock < (nPerBlock * nPerBlock) + nPerBlock + 12) {
353 return getBlockNumberBiindirect(LITTLE_TO_HOST32(m_pInode->i_block[13]), nPerBlock + 12,
354 nBlock);
355 }
356
357 return getBlockNumberTriindirect(LITTLE_TO_HOST32(m_pInode->i_block[14]),
358 (nPerBlock * nPerBlock) + nPerBlock + 12, nBlock);
359}
360
361bool Ext2Node::getBlockNumberIndirect(uint32_t inode_block, size_t nBlocks, size_t nBlock) {
362 if (!inode_block) {
363 m_Blocks[nBlock] = 0;
364 return true;
365 }
366 const DiskReadView buffer = m_pExt2Fs->readBlockView(inode_block);
367 if (!buffer) {
368 return false;
369 }
370
371 for (size_t i = 0; i < m_pExt2Fs->m_BlockSize / 4 && nBlocks < m_Blocks.count(); i++) {
372 uint32_t block;
373 if (!buffer.readAt(block, i * sizeof(block)))
374 return false;
375 m_Blocks[nBlocks++] = LITTLE_TO_HOST32(block);
376 }
377
378 return true;
379}
380
381bool Ext2Node::getBlockNumberBiindirect(uint32_t inode_block, size_t nBlocks, size_t nBlock) {
382 size_t nPerBlock = m_pExt2Fs->m_BlockSize / 4;
383
384 if (!inode_block) {
385 m_Blocks[nBlock] = 0;
386 return true;
387 }
388 DiskReadView buffer = m_pExt2Fs->readBlockView(inode_block);
389 if (!buffer) {
390 return false;
391 }
392
393 // What indirect block does nBlock exist on?
394 size_t nIndirectBlock = (nBlock - nBlocks) / nPerBlock;
395
396 uint32_t indirectBlock;
397 if (!buffer.readAt(indirectBlock, nIndirectBlock * sizeof(indirectBlock)))
398 return false;
399 buffer.reset();
400 return getBlockNumberIndirect(LITTLE_TO_HOST32(indirectBlock),
401 nBlocks + nIndirectBlock * nPerBlock, nBlock);
402}
403
404bool Ext2Node::getBlockNumberTriindirect(uint32_t inode_block, size_t nBlocks, size_t nBlock) {
405 size_t nPerBlock = m_pExt2Fs->m_BlockSize / 4;
406
407 if (!inode_block) {
408 m_Blocks[nBlock] = 0;
409 return true;
410 }
411 DiskReadView buffer = m_pExt2Fs->readBlockView(inode_block);
412 if (!buffer) {
413 return false;
414 }
415
416 // What biindirect block does nBlock exist on?
417 size_t nBiBlock = (nBlock - nBlocks) / (nPerBlock * nPerBlock);
418
419 uint32_t biBlock;
420 if (!buffer.readAt(biBlock, nBiBlock * sizeof(biBlock)))
421 return false;
422 buffer.reset();
423 return getBlockNumberBiindirect(LITTLE_TO_HOST32(biBlock),
424 nBlocks + nBiBlock * nPerBlock * nPerBlock, nBlock);
425}
426
427void Ext2Node::writeBlockOrQueue(uint32_t block, Vector<uint32_t>* pendingWrites) {
428 if (!pendingWrites) {
429 m_pExt2Fs->writeBlock(block);
430 return;
431 }
432
433 for (auto pending : *pendingWrites) {
434 if (pending == block) {
435 return;
436 }
437 }
438 // Keep the page resident until the batched write is submitted. The normal
439 // caller reference is released immediately after addBlock(), so without
440 // this pin a memory-pressure eviction could discard a deferred update.
441 if (!m_pExt2Fs->pinBlock(block)) {
442 // Fall back to the original write-through behaviour if the device cannot
443 // acquire a second reference to the mapping page.
444 m_pExt2Fs->writeBlock(block);
445 return;
446 }
447 pendingWrites->pushBack(block);
448}
449
450bool Ext2Node::setBlockNumber(size_t blockNum, uint32_t blockValue,
451 Vector<uint32_t>* pendingWrites) {
452 if (blockNum < 12) {
453 m_pInode->i_block[blockNum] = HOST_TO_LITTLE32(blockValue);
454 return true;
455 }
456
457 const size_t entries = m_pExt2Fs->m_BlockSize / sizeof(uint32_t);
458 size_t index = blockNum - 12;
459 const unsigned depth = index < entries ? 1 : 2;
460 if (depth == 2) {
461 index -= entries;
462 if (index >= entries * entries) {
463 SYSCALL_ERROR(FileTooLarge);
464 return false;
465 }
466 }
467 const size_t indices[2] = {depth == 1 ? index : index / entries, index % entries};
468 const size_t inodeIndex = depth == 1 ? 12 : 13;
469 uint32_t numbers[2] = {};
470 uintptr_t buffers[2] = {};
471 bool allocated[2] = {};
472 uint32_t next = LITTLE_TO_HOST32(m_pInode->i_block[inodeIndex]);
473 bool ready = true;
474 for (unsigned level = 0; level < depth; ++level) {
475 numbers[level] = next;
476 if (!next) {
477 numbers[level] = m_pExt2Fs->findFreeBlock(m_InodeNumber);
478 allocated[level] = numbers[level] != 0;
479 if (!allocated[level]) {
480 ready = false;
481 break;
482 }
483 }
484 buffers[level] = m_pExt2Fs->readBlock(numbers[level]);
485 if (!buffers[level]) {
486 SYSCALL_ERROR(IoError);
487 ready = false;
488 break;
489 }
490 if (allocated[level]) {
491 ByteSet(reinterpret_cast<void*>(buffers[level]), 0, m_pExt2Fs->m_BlockSize);
492 }
493 next = LITTLE_TO_HOST32(reinterpret_cast<uint32_t*>(buffers[level])[indices[level]]);
494 }
495 if (ready) {
496 // All pages and allocations exist before any old mapping is changed.
497 for (unsigned level = depth; level-- > 0;) {
498 uint32_t* table = reinterpret_cast<uint32_t*>(buffers[level]);
499 table[indices[level]] =
500 HOST_TO_LITTLE32(level + 1 == depth ? blockValue : numbers[level + 1]);
501 writeBlockOrQueue(numbers[level], pendingWrites);
502 if (allocated[level]) {
503 ++m_nMetadataBlocks;
504 }
505 }
506 m_pInode->i_block[inodeIndex] = HOST_TO_LITTLE32(numbers[0]);
507 }
508 for (unsigned level = 0; level < depth; ++level) {
509 if (buffers[level]) {
510 m_pExt2Fs->unpinBlock(numbers[level]);
511 }
512 if (!ready && allocated[level]) {
513 m_pExt2Fs->releaseBlock(numbers[level], m_InodeNumber);
514 }
515 }
516 return ready;
517}
518
519bool Ext2Node::addBlock(uint32_t blockValue, Vector<uint32_t>* pendingWrites) {
520 if (!setBlockNumber(m_Blocks.count(), blockValue, pendingWrites)) {
521 return false;
522 }
523 trackBlock(blockValue, !pendingWrites);
524 return true;
525}
526
527bool Ext2Node::ensureWritableRange(size_t location, size_t length) {
528 if (!m_State->allocationValid) {
529 SYSCALL_ERROR(IoError);
530 return false;
531 }
532 if (!length) {
533 return true;
534 }
535 const size_t blockSize = m_pExt2Fs->m_BlockSize;
536 const size_t first = location / blockSize;
537 const size_t last = (location + length - 1) / blockSize;
538 for (size_t index = first; index <= last; ++index) {
539 if (index >= m_Blocks.count() || !ensureBlockLoaded(index)) {
540 SYSCALL_ERROR(IoError);
541 return false;
542 }
543 if (m_Blocks[index]) {
544 continue;
545 }
546 const uint32_t block = m_pExt2Fs->findFreeBlock(m_InodeNumber);
547 if (!block) {
548 return false;
549 }
550 if (!m_pExt2Fs->m_pDisk->zero(static_cast<uint64_t>(block) * blockSize, blockSize)) {
551 m_pExt2Fs->releaseBlock(block, m_InodeNumber);
552 SYSCALL_ERROR(IoError);
553 return false;
554 }
555 if (!setBlockNumber(index, block)) {
556 m_pExt2Fs->releaseBlock(block, m_InodeNumber);
557 return false;
558 }
559 m_Blocks[index] = block;
560 ++m_State->allocatedDataBlocks;
561 updateAllocatedSectorCount();
562 m_pExt2Fs->writeInode(getInodeNumber());
563 }
564 return true;
565}
566
567void Ext2Node::fileAttributeChanged(size_t size, size_t atime, size_t mtime, size_t ctime) {
568 OperationBarrier::Lease operation;
569 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
570 return;
571 }
572 m_pInode->i_size = HOST_TO_LITTLE32(size);
573 m_pInode->i_atime = HOST_TO_LITTLE32(atime);
574 m_pInode->i_mtime = HOST_TO_LITTLE32(mtime);
575 m_pInode->i_ctime = HOST_TO_LITTLE32(ctime);
576
577 // Update our internal record of the file size accordingly.
578 m_nSize = size;
579
580 // Write updated inode.
581 m_pExt2Fs->writeInode(getInodeNumber());
582}
583
584File::Attributes Ext2Node::inodeAttributes() const {
585 LockGuard<Mutex> guard(m_State->writebackLock);
586 File::Attributes attributes;
587 attributes.accessed = LITTLE_TO_HOST32(m_pInode->i_atime);
588 attributes.modified = LITTLE_TO_HOST32(m_pInode->i_mtime);
589 attributes.changed = LITTLE_TO_HOST32(m_pInode->i_ctime);
590 attributes.uid = Ext2Owner::uid(*m_pInode);
591 attributes.gid = Ext2Owner::gid(*m_pInode);
592 attributes.permissions = modeToPermissions(LITTLE_TO_HOST16(m_pInode->i_mode));
593 attributes.size = LITTLE_TO_HOST32(m_pInode->i_size);
594 attributes.links = LITTLE_TO_HOST16(m_pInode->i_links_count);
595 attributes.blocks = LITTLE_TO_HOST32(m_pInode->i_blocks);
596 return attributes;
597}
598
599void Ext2Node::updateInodeAttributes(const File::Attributes& attributes, uint32_t mask) {
600 OperationBarrier::Lease operation;
601 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
602 return;
603 }
604 if (mask & (File::Owner | File::Group)) {
605 if (!changeInodeOwnership(attributes.uid, attributes.gid, mask & File::Owner,
606 mask & File::Group))
607 return;
608 mask &= ~(File::Owner | File::Group);
609 if (!mask)
610 return;
611 }
612 LockGuard<Mutex> guard(m_State->writebackLock);
613 if (mask & File::AccessTime) {
614 m_pInode->i_atime = HOST_TO_LITTLE32(attributes.accessed);
615 }
616 if (mask & File::ModifyTime) {
617 m_pInode->i_mtime = HOST_TO_LITTLE32(attributes.modified);
618 }
619 m_pInode->i_ctime =
620 HOST_TO_LITTLE32((mask & File::ChangeTime) ? attributes.changed : Time::getTime());
621 if (mask & File::Permissions) {
622 const uint16_t mode = LITTLE_TO_HOST16(m_pInode->i_mode);
623 m_pInode->i_mode =
624 HOST_TO_LITTLE16((mode & ~01777U) | permissionsToMode(attributes.permissions));
625 }
626 m_pExt2Fs->writeInode(getInodeNumber());
627}
628
629bool Ext2Node::sync(size_t offset, bool async) {
630 OperationBarrier::Lease operation;
631 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
632 return false;
633 }
634 const size_t nBlock = offset / m_pExt2Fs->m_BlockSize;
635 if (offset >= m_nSize) {
636 return true;
637 }
638 if (nBlock >= m_Blocks.count() || !ensureBlockLoaded(nBlock)) {
639 return false;
640 }
641 return m_pExt2Fs->syncBlock(m_Blocks[nBlock], async);
642}
643
644bool Ext2Node::pinBlock(uint64_t location) {
645 uint32_t nBlock = location / m_pExt2Fs->m_BlockSize;
646 if (nBlock >= m_Blocks.count())
647 return false;
648 if (location >= m_nSize)
649 return false;
650
651 if (!ensureBlockLoaded(nBlock))
652 return false;
653 if (!m_Blocks[nBlock])
654 return true;
655 return m_pExt2Fs->pinBlock(m_Blocks[nBlock]);
656}
657
658void Ext2Node::unpinBlock(uint64_t location) {
659 uint32_t nBlock = location / m_pExt2Fs->m_BlockSize;
660 if (nBlock >= m_Blocks.count())
661 return;
662 if (location >= m_nSize)
663 return;
664
665 if (!ensureBlockLoaded(nBlock))
666 return;
667 if (!m_Blocks[nBlock])
668 return;
669 m_pExt2Fs->unpinBlock(m_Blocks[nBlock]);
670}
671
672uint32_t Ext2Node::modeToPermissions(uint32_t mode) const {
673 uint32_t permissions = 0;
674 if (mode & 01000)
675 permissions |= FILE_STICKY;
676 if (mode & EXT2_S_IRUSR)
677 permissions |= FILE_UR;
678 if (mode & EXT2_S_IWUSR)
679 permissions |= FILE_UW;
680 if (mode & EXT2_S_IXUSR)
681 permissions |= FILE_UX;
682 if (mode & EXT2_S_IRGRP)
683 permissions |= FILE_GR;
684 if (mode & EXT2_S_IWGRP)
685 permissions |= FILE_GW;
686 if (mode & EXT2_S_IXGRP)
687 permissions |= FILE_GX;
688 if (mode & EXT2_S_IROTH)
689 permissions |= FILE_OR;
690 if (mode & EXT2_S_IWOTH)
691 permissions |= FILE_OW;
692 if (mode & EXT2_S_IXOTH)
693 permissions |= FILE_OX;
694 return permissions;
695}
696
697uint32_t Ext2Node::permissionsToMode(uint32_t permissions) const {
698 uint32_t mode = 0;
699 if (permissions & FILE_STICKY)
700 mode |= 01000;
701 if (permissions & FILE_UR)
702 mode |= EXT2_S_IRUSR;
703 if (permissions & FILE_UW)
704 mode |= EXT2_S_IWUSR;
705 if (permissions & FILE_UX)
706 mode |= EXT2_S_IXUSR;
707 if (permissions & FILE_GR)
708 mode |= EXT2_S_IRGRP;
709 if (permissions & FILE_GW)
710 mode |= EXT2_S_IWGRP;
711 if (permissions & FILE_GX)
712 mode |= EXT2_S_IXGRP;
713 if (permissions & FILE_OR)
714 mode |= EXT2_S_IROTH;
715 if (permissions & FILE_OW)
716 mode |= EXT2_S_IWOTH;
717 if (permissions & FILE_OX)
718 mode |= EXT2_S_IXOTH;
719 return mode;
720}
virtual MUST_USE_RESULT bool zero(uint64_t location, size_t length)
Definition Disk.cc:387
void writeBlock(uint32_t block)
uintptr_t readBlock(uint32_t block)
bool wipe(bool allocationLockHeld=false)
Definition Ext2Node.cc:217
Ext2Node(uintptr_t inode, Inode *metadata, Ext2Filesystem *filesystem, Ext2InodeState &admitted)
Definition Ext2Node.cc:145
bool ensureLargeEnough(size_t size, uint64_t location, uint64_t opsize, bool onlyBlocks=false, bool nozeroblocks=false)
Definition Ext2Node.cc:258
void fileAttributeChanged(size_t size, size_t atime, size_t mtime, size_t ctime)
Definition Ext2Node.cc:567
virtual ~Ext2Node()
Definition Ext2Node.cc:155
Disk * m_pDisk
Definition Filesystem.h:188
A vector / dynamic array.
Definition Vector.h:33
void reserve(size_t size, bool copy)
Definition Vector.h:453
void pushBack(const T &value)
Definition Vector.h:275
size_t count() const
Definition Vector.h:270
Definition ext2.h:152