20#include "Ext2Directory.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/stddef.h"
24#include "pedigree/kernel/syscallError.h"
25#include "pedigree/kernel/utilities/Pointers.h"
26#include "pedigree/kernel/utilities/Vector.h"
27#include "pedigree/kernel/utilities/assert.h"
28#include "pedigree/kernel/utilities/utility.h"
31#include "Ext2Filesystem.h"
32#include "Ext2Symlink.h"
34#include "modules/system/vfs/File.h"
40 :
Directory(name, LITTLE_TO_HOST32(inode->i_atime), LITTLE_TO_HOST32(inode->i_mtime),
41 LITTLE_TO_HOST32(inode->i_ctime), inode_num, static_cast<
Filesystem*>(pFs),
42 LITTLE_TO_HOST32(inode->i_size),
47 uint32_t mode = LITTLE_TO_HOST32(inode->i_mode);
57 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
68 bool succeeded =
true;
69 for (
size_t i = 0; i < m_Blocks.
count(); ++i) {
70 if (!ensureBlockLoaded(i)) {
74 succeeded = syncDirectoryBlock(m_Blocks[i]) && succeeded;
76 for (
size_t i = 0; i < m_State->namespaceSyncBlocks.
count();) {
77 if (syncDirectoryBlock(m_State->namespaceSyncBlocks[i])) {
78 m_State->namespaceSyncBlocks.
erase(i);
84 return m_pExt2Fs->syncInode(getInodeNumber(), *
this,
true) && succeeded;
87bool Ext2Directory::syncDirectoryBlock(uint32_t block) {
88 if (!block || !m_pExt2Fs->
readBlock(block)) {
91 const bool succeeded = m_pExt2Fs->syncBlock(block,
false);
92 m_pExt2Fs->unpinBlock(block);
96void Ext2Directory::queueSyncDependency(uint32_t block) {
98 for (uint32_t dependency : m_State->namespaceSyncBlocks) {
99 if (dependency == block) {
105 m_State->namespaceSyncBlocks.
pushBack(block);
109 if (!filename.length() || filename.length() > 255) {
110 SYSCALL_ERROR(InvalidArgument);
116 SYSCALL_ERROR(FileExists);
122 SYSCALL_ERROR(DoesNotExist);
126 uint64_t existingOffset = 0;
127 while (existingOffset < m_nSize) {
129 if (readEntry(existingOffset, entry) != ReadStatus::Complete) {
130 SYSCALL_ERROR(IoError);
133 if (entry.inode && filename.length() == entry.nameLength &&
134 !StringCompareN(filename.cstr(), entry.name, entry.nameLength)) {
135 SYSCALL_ERROR(FileExists);
138 existingOffset += entry.recordLength;
142 size_t length = offsetof(
Dir, d_name) + filename.length();
144 length += 4 - (length % 4);
153 uint16_t splitLength = 0;
154 uint16_t splitRemainder = 0;
155 for (i = 0; i < m_Blocks.
count(); i++) {
156 if (!ensureBlockLoaded(i)) {
159 uintptr_t buffer = m_pExt2Fs->
readBlock(m_Blocks[i]);
163 pDir =
reinterpret_cast<Dir*
>(buffer);
164 pBlockEnd = adjust_pointer(pDir, m_pExt2Fs->
m_BlockSize);
165 while (pDir < pBlockEnd) {
166 const size_t remaining = pointer_diff(pDir, pBlockEnd);
167 if (remaining < offsetof(
Dir, d_name)) {
168 m_pExt2Fs->unpinBlock(m_Blocks[i]);
169 SYSCALL_ERROR(IoError);
173 uint16_t entryReclen = LITTLE_TO_HOST16(pDir->d_reclen);
174 if (entryReclen < offsetof(
Dir, d_name) || entryReclen > remaining || (entryReclen % 4)) {
175 m_pExt2Fs->unpinBlock(m_Blocks[i]);
176 SYSCALL_ERROR(IoError);
181 size_t currentNameLength = pDir->d_namelen;
182 if (!m_pExt2Fs->checkRequiredFeature(2)) {
183 currentNameLength |=
static_cast<size_t>(pDir->d_file_type) << 8;
185 if (currentNameLength > 255 || currentNameLength > entryReclen - offsetof(
Dir, d_name)) {
186 m_pExt2Fs->unpinBlock(m_Blocks[i]);
187 SYSCALL_ERROR(IoError);
190 size_t thisReclen = offsetof(
Dir, d_name) + currentNameLength;
192 if (thisReclen % 4) {
193 thisReclen += 4 - (thisReclen % 4);
197 if (pDir->d_inode > 0) {
200 if (entryReclen - thisReclen >= length) {
203 splitLength = thisReclen;
204 splitRemainder = entryReclen - thisReclen;
205 pDir = adjust_pointer(pDir, thisReclen);
208 }
else if (entryReclen >= length) {
216 pDir = adjust_pointer(pDir, entryReclen);
220 m_pExt2Fs->unpinBlock(m_Blocks[i]);
223 if (!bFound || !pDir) {
225 uint32_t block = m_pExt2Fs->findFreeBlock(getInodeNumber());
230 const uintptr_t buffer = m_pExt2Fs->
readBlock(block);
232 m_pExt2Fs->releaseBlock(block, getInodeNumber());
233 SYSCALL_ERROR(IoError);
237 ByteSet(
reinterpret_cast<void*
>(buffer), 0, m_pExt2Fs->
m_BlockSize);
238 pDir =
reinterpret_cast<Dir*
>(buffer);
239 pDir->d_reclen = HOST_TO_LITTLE16(m_pExt2Fs->
m_BlockSize);
241 if (!addBlock(block)) {
242 const int failure = m_pExt2Fs->currentIoError();
243 m_pExt2Fs->unpinBlock(block);
244 m_pExt2Fs->releaseBlock(block, getInodeNumber());
245 syscallError(failure);
248 i = m_Blocks.
count() - 1;
254 const bool special = filename.
compare(
".") || filename.
compare(
"..");
256 if (!special && type == EXT2_S_IFDIR) {
258 for (
size_t block = 0; block < child->m_Blocks.
count(); ++block) {
259 if (!child->ensureBlockLoaded(block)) {
260 m_pExt2Fs->unpinBlock(m_Blocks[i]);
261 SYSCALL_ERROR(IoError);
264 queueSyncDependency(child->m_Blocks[block]);
269 pSplitDir->d_reclen = HOST_TO_LITTLE16(splitLength);
270 pDir->d_reclen = HOST_TO_LITTLE16(splitRemainder);
274 uint32_t entryInode = pFile->getInode();
275 pDir->d_inode = HOST_TO_LITTLE32(entryInode);
276 m_pExt2Fs->increaseInodeRefcount(entryInode);
278 if (m_pExt2Fs->checkRequiredFeature(2)) {
282 pDir->d_file_type = EXT2_FILE;
285 pDir->d_file_type = EXT2_DIRECTORY;
288 pDir->d_file_type = EXT2_SYMLINK;
291 ERROR(
"Unrecognised filetype.");
292 pDir->d_file_type = EXT2_UNKNOWN;
296 pDir->d_file_type = 0;
299 pDir->d_namelen =
static_cast<uint8_t
>(filename.length());
300 MemoryCopy(pDir->d_name,
static_cast<const char*
>(filename), filename.length());
304 m_pExt2Fs->unpinBlock(m_Blocks[i]);
310 reservation.complete(LookupStatus::Found);
325 size_t fileInode = pFile->getInodeNumber();
331 for (i = 0; i < m_Blocks.
count(); i++) {
332 if (!ensureBlockLoaded(i)) {
335 uintptr_t buffer = m_pExt2Fs->
readBlock(m_Blocks[i]);
339 pDir =
reinterpret_cast<Dir*
>(buffer);
340 while (
reinterpret_cast<uintptr_t
>(pDir) < buffer + m_pExt2Fs->
m_BlockSize) {
341 const uintptr_t current =
reinterpret_cast<uintptr_t
>(pDir);
342 const size_t remaining = buffer + m_pExt2Fs->
m_BlockSize - current;
343 if (remaining < offsetof(
Dir, d_name)) {
344 m_pExt2Fs->unpinBlock(m_Blocks[i]);
345 SYSCALL_ERROR(IoError);
349 const uint16_t recordLength = LITTLE_TO_HOST16(pDir->d_reclen);
350 if (recordLength < offsetof(
Dir, d_name) || recordLength > remaining || (recordLength % 4)) {
351 m_pExt2Fs->unpinBlock(m_Blocks[i]);
352 SYSCALL_ERROR(IoError);
356 size_t nameLength = pDir->d_namelen;
357 if (!m_pExt2Fs->checkRequiredFeature(2)) {
358 nameLength |=
static_cast<size_t>(pDir->d_file_type) << 8;
360 if (nameLength > 255 || nameLength > recordLength - offsetof(
Dir, d_name)) {
361 m_pExt2Fs->unpinBlock(m_Blocks[i]);
362 SYSCALL_ERROR(IoError);
366 if (LITTLE_TO_HOST32(pDir->d_inode) == fileInode) {
367 if (nameLength == filename.length()) {
368 if (!StringCompareN(pDir->d_name,
static_cast<const char*
>(filename), nameLength)) {
370 uint16_t old_reclen = recordLength;
371 ByteSet(pDir, 0, old_reclen);
379 pDir->d_reclen = HOST_TO_LITTLE16(old_reclen);
388 pDir =
reinterpret_cast<Dir*
>(
reinterpret_cast<uintptr_t
>(pDir) + recordLength);
391 m_pExt2Fs->unpinBlock(m_Blocks[i]);
403 SYSCALL_ERROR(DoesNotExist);
411 LITTLE_TO_HOST32(m_pInode->i_mtime),
412 LITTLE_TO_HOST32(m_pInode->i_ctime));
416 return inodeAttributes();
419void Ext2Directory::updateAttributes(
const Attributes& attributes, uint32_t mask) {
420 updateInodeAttributes(attributes, mask);
423bool Ext2Directory::readBytes(uint64_t offset,
size_t length,
void* output) {
424 if (offset > m_nSize || length > (m_nSize - offset)) {
428 uint8_t* destination =
reinterpret_cast<uint8_t*
>(output);
430 const size_t block = offset / m_pExt2Fs->
m_BlockSize;
431 const size_t blockOffset = offset % m_pExt2Fs->
m_BlockSize;
432 if (block >= m_Blocks.
count() || !ensureBlockLoaded(block)) {
436 const DiskReadView buffer = m_pExt2Fs->readBlockView(m_Blocks[block]);
441 size_t available = m_pExt2Fs->
m_BlockSize - blockOffset;
442 if (available > length) {
445 if (!buffer.copyTo(destination, available, blockOffset))
448 destination += available;
456Directory::ReadStatus Ext2Directory::readEntry(uint64_t offset, ParsedEntry& entry) {
458 return ReadStatus::IoError;
462 if (!readBytes(offset, offsetof(
Dir, d_name), &header)) {
463 return ReadStatus::IoError;
466 entry.inode = LITTLE_TO_HOST32(header.d_inode);
467 entry.recordLength = LITTLE_TO_HOST16(header.d_reclen);
468 entry.nameLength = header.d_namelen;
469 entry.fileType = header.d_file_type;
470 if (!m_pExt2Fs->checkRequiredFeature(2)) {
471 entry.nameLength |=
static_cast<uint16_t
>(header.d_file_type) << 8;
472 entry.fileType = EXT2_UNKNOWN;
476 if (entry.recordLength < offsetof(
Dir, d_name) || (entry.recordLength % 4) ||
477 entry.recordLength > (m_nSize - offset) || entry.recordLength > bytesRemainingInBlock ||
478 entry.nameLength > 255 || entry.nameLength > entry.recordLength - offsetof(
Dir, d_name)) {
479 return ReadStatus::IoError;
484 return ReadStatus::Complete;
487 const uint32_t inodeCount = LITTLE_TO_HOST32(m_pExt2Fs->
m_pSuperblock->s_inodes_count);
488 if (!entry.nameLength || entry.inode > inodeCount || entry.fileType >= EXT2_MAX ||
489 !readBytes(offset + offsetof(
Dir, d_name), entry.nameLength, entry.name)) {
490 return ReadStatus::IoError;
492 entry.name[entry.nameLength] = 0;
493 return ReadStatus::Complete;
496Directory::LookupStatus Ext2Directory::resolveEntry(
const ParsedEntry& entry,
499 if (!entry.inode || !name.compare(entry.name, entry.nameLength)) {
500 return LookupStatus::NotFound;
503 Inode* inode = m_pExt2Fs->getInode(entry.inode);
505 return LookupStatus::IoError;
508 uint8_t fileType = entry.fileType;
509 if (!m_pExt2Fs->checkRequiredFeature(2) || fileType == EXT2_UNKNOWN) {
510 switch (LITTLE_TO_HOST16(inode->i_mode) & 0xF000) {
512 fileType = EXT2_FILE;
515 fileType = EXT2_DIRECTORY;
518 fileType = EXT2_SYMLINK;
524 return LookupStatus::NotFound;
526 return LookupStatus::IoError;
530 const String filename(entry.name, entry.nameLength);
533 Ext2File* file =
new Ext2File(filename, entry.inode, inode, m_pExt2Fs,
this);
534 if (!file || !file->valid()) {
536 return LookupStatus::IoError;
542 child =
new Ext2Directory(filename, entry.inode, inode, m_pExt2Fs,
this);
545 child =
new Ext2Symlink(filename, entry.inode, inode, m_pExt2Fs,
this);
551 return LookupStatus::NotFound;
553 return LookupStatus::IoError;
556 return LookupStatus::Found;
559Directory::LookupStatus Ext2Directory::resolveChildLocked(
const StringView& name,
File*& child) {
562 while (offset < m_nSize) {
564 if (readEntry(offset, entry) != ReadStatus::Complete) {
565 return LookupStatus::IoError;
567 if (entry.inode && name.compare(entry.name, entry.nameLength)) {
568 return resolveEntry(entry, name, child);
570 offset += entry.recordLength;
572 return LookupStatus::NotFound;
577 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
578 return LookupStatus::IoError;
581 if (name.compare(
".", 1) || name.compare(
"..", 2)) {
582 return LookupStatus::NotFound;
587 return LookupStatus::NotFound;
589 return resolveChildLocked(name, child);
595 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
596 return LookupStatus::IoError;
599 if (name.compare(
".", 1) || name.compare(
"..", 2)) {
600 return LookupStatus::NotFound;
605 return LookupStatus::NotFound;
607 if (cookie < m_nSize) {
609 if (readEntry(cookie, entry) == ReadStatus::Complete && entry.inode &&
610 name.compare(entry.name, entry.nameLength)) {
611 return resolveEntry(entry, name, child);
617 return resolveChildLocked(name, child);
623 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
624 return ReadStatus::IoError;
628 return ReadStatus::Complete;
630 if (cookie > m_nSize) {
631 return ReadStatus::IoError;
634 while (cookie < m_nSize) {
636 if (readEntry(cookie, parsed) != ReadStatus::Complete) {
637 return ReadStatus::IoError;
640 const uint64_t currentCookie = cookie;
641 const uint64_t nextCookie = currentCookie + parsed.recordLength;
647 EntryType type = EntryType::Unknown;
648 switch (parsed.fileType) {
650 type = EntryType::Unknown;
653 type = EntryType::Regular;
656 type = EntryType::Directory;
659 type = EntryType::Symlink;
662 type = EntryType::CharacterDevice;
665 type = EntryType::BlockDevice;
668 type = EntryType::Fifo;
671 type = EntryType::Socket;
674 return ReadStatus::IoError;
678 currentCookie, nextCookie};
679 if (!emitter(context, entry)) {
680 return ReadStatus::Stopped;
685 return ReadStatus::Complete;
689 if (parent ==
this) {
690 SYSCALL_ERROR(InvalidArgument);
696 SYSCALL_ERROR(IoError);
700 SYSCALL_ERROR(NotEmpty);
706 SYSCALL_ERROR(DoesNotExist);
710 bool foundDot =
false;
711 bool foundDotDot =
false;
713 while (offset < m_nSize) {
715 if (readEntry(offset, entry) != ReadStatus::Complete) {
716 SYSCALL_ERROR(IoError);
721 const StringView name(entry.name, entry.nameLength);
722 if (name.compare(
".", 1)) {
723 foundDot = entry.inode == getInodeNumber();
725 SYSCALL_ERROR(IoError);
728 }
else if (name.compare(
"..", 2)) {
729 foundDotDot = entry.inode == parent->getInodeNumber();
731 SYSCALL_ERROR(IoError);
735 SYSCALL_ERROR(NotEmpty);
739 offset += entry.recordLength;
742 if (!foundDot || !foundDotDot) {
743 SYSCALL_ERROR(IoError);
754 ERROR(
"Ext2 directory was unlinked, but its parent link could not be retired");
758 ERROR(
"Ext2 directory was unlinked, but its inode could not be retired");
void invalidateDirectoryEntry(const HashedStringView &name)
Mutex & namespaceMutationLock()
ReadStatus isEmpty(bool &empty)
bool addCachedDirectoryEntry(NameReservation &reservation, File *pTarget)
bool reserveDirectoryEntry(const HashedStringView &name, NameReservation &reservation)
ReadStatus readDirectory(uint64_t &cookie, DirectoryEntryEmitter emitter, void *context) override
Ext2Directory(const Ext2Directory &file)
virtual bool removeEntry(const String &filename, Ext2Node *pFile)
~Ext2Directory() override
LookupStatus resolveChildAt(uint64_t cookie, const StringView &name, File *&child) override
void fileAttributeChanged() override
bool removeEntryLocked(const String &filename, Ext2Node *pFile)
LookupStatus resolveChild(const StringView &name, File *&child) override
virtual bool addEntry(const String &filename, File *pFile, size_t type)
void writeBlock(uint32_t block)
uintptr_t readBlock(uint32_t block)
bool releaseInode(uint32_t inode, Ext2Node *retiringNode=nullptr)
Superblock * m_pSuperblock
void fileAttributeChanged(size_t size, size_t atime, size_t mtime, size_t ctime)
virtual bool isDirectory()
void setGidOnly(size_t gid)
void publishEvent(FileEventMask mask, const StringView &name=StringView(), bool targetIsDirectory=false)
void setPermissionsOnly(uint32_t perms)
void setUidOnly(size_t uid)
bool compare(const char *s, size_t len) const
void pushBack(const T &value)