The Pedigree Project 0.1
Ext2Directory.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 "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"
29
30#include "Ext2File.h"
31#include "Ext2Filesystem.h"
32#include "Ext2Symlink.h"
33#include "ext2.h"
34#include "modules/system/vfs/File.h"
35
36class Filesystem;
37
38Ext2Directory::Ext2Directory(const String& name, uintptr_t inode_num, Inode* inode,
39 Ext2Filesystem* pFs, File* pParent)
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),
43 pParent),
44 Ext2Node(inode_num, inode, pFs),
45 m_DirectoryLock(),
46 m_Removed(false) {
47 uint32_t mode = LITTLE_TO_HOST32(inode->i_mode);
48 setPermissionsOnly(modeToPermissions(mode));
49 setUidOnly(Ext2Owner::uid(*inode));
50 setGidOnly(Ext2Owner::gid(*inode));
51}
52
54
57 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
58 return false;
59 }
60 LockGuard<Mutex> directoryGuard(m_DirectoryLock);
61 LockGuard<Mutex> writebackGuard(m_State->writebackLock);
62 if (!m_pExt2Fs->m_BlockSize) {
63 return false;
64 }
65
66 // Directory records bypass File's page cache, so even an empty cache must
67 // submit their backing blocks before reporting a completed namespace change.
68 bool succeeded = true;
69 for (size_t i = 0; i < m_Blocks.count(); ++i) {
70 if (!ensureBlockLoaded(i)) {
71 succeeded = false;
72 continue;
73 }
74 succeeded = syncDirectoryBlock(m_Blocks[i]) && succeeded;
75 }
76 for (size_t i = 0; i < m_State->namespaceSyncBlocks.count();) {
77 if (syncDirectoryBlock(m_State->namespaceSyncBlocks[i])) {
78 m_State->namespaceSyncBlocks.erase(i);
79 } else {
80 succeeded = false;
81 ++i;
82 }
83 }
84 return m_pExt2Fs->syncInode(getInodeNumber(), *this, true) && succeeded;
85}
86
87bool Ext2Directory::syncDirectoryBlock(uint32_t block) {
88 if (!block || !m_pExt2Fs->readBlock(block)) {
89 return false;
90 }
91 const bool succeeded = m_pExt2Fs->syncBlock(block, false);
92 m_pExt2Fs->unpinBlock(block);
93 return succeeded;
94}
95
96void Ext2Directory::queueSyncDependency(uint32_t block) {
97 LockGuard<Mutex> guard(m_State->writebackLock);
98 for (uint32_t dependency : m_State->namespaceSyncBlocks) {
99 if (dependency == block) {
100 return;
101 }
102 }
103 // Remember the block identity, not a cache address: removal may retire and
104 // reuse it before this parent is synced. Resolve current contents at sync.
105 m_State->namespaceSyncBlocks.pushBack(block);
106}
107
108bool Ext2Directory::addEntry(const String& filename, File* pFile, size_t type) {
109 if (!filename.length() || filename.length() > 255) {
110 SYSCALL_ERROR(InvalidArgument);
111 return false;
112 }
113
114 NameReservation reservation;
115 if (!reserveDirectoryEntry(HashedStringView(filename), reservation)) {
116 SYSCALL_ERROR(FileExists);
117 return false;
118 }
119
120 LockGuard<Mutex> guard(m_DirectoryLock);
121 if (m_Removed) {
122 SYSCALL_ERROR(DoesNotExist);
123 return false;
124 }
125
126 uint64_t existingOffset = 0;
127 while (existingOffset < m_nSize) {
128 ParsedEntry entry;
129 if (readEntry(existingOffset, entry) != ReadStatus::Complete) {
130 SYSCALL_ERROR(IoError);
131 return false;
132 }
133 if (entry.inode && filename.length() == entry.nameLength &&
134 !StringCompareN(filename.cstr(), entry.name, entry.nameLength)) {
135 SYSCALL_ERROR(FileExists);
136 return false;
137 }
138 existingOffset += entry.recordLength;
139 }
140
141 // Calculate the size of our Dir* entry.
142 size_t length = offsetof(Dir, d_name) + filename.length();
143 if (length % 4) {
144 length += 4 - (length % 4);
145 }
146
147 bool bFound = false;
148
149 uint32_t i;
150 Dir* pDir = 0;
151 Dir* pBlockEnd = 0;
152 Dir* pSplitDir = 0;
153 uint16_t splitLength = 0;
154 uint16_t splitRemainder = 0;
155 for (i = 0; i < m_Blocks.count(); i++) {
156 if (!ensureBlockLoaded(i)) {
157 return false;
158 }
159 uintptr_t buffer = m_pExt2Fs->readBlock(m_Blocks[i]);
160 if (!buffer) {
161 return false;
162 }
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);
170 return false;
171 }
172
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);
177 return false;
178 }
179
180 // What's the minimum length of this directory entry?
181 size_t currentNameLength = pDir->d_namelen;
182 if (!m_pExt2Fs->checkRequiredFeature(2)) {
183 currentNameLength |= static_cast<size_t>(pDir->d_file_type) << 8;
184 }
185 if (currentNameLength > 255 || currentNameLength > entryReclen - offsetof(Dir, d_name)) {
186 m_pExt2Fs->unpinBlock(m_Blocks[i]);
187 SYSCALL_ERROR(IoError);
188 return false;
189 }
190 size_t thisReclen = offsetof(Dir, d_name) + currentNameLength;
191 // Align to 4-byte boundary.
192 if (thisReclen % 4) {
193 thisReclen += 4 - (thisReclen % 4);
194 }
195
196 // Valid directory entry?
197 if (pDir->d_inode > 0) {
198 // Is there enough space to add this dirent?
200 if (entryReclen - thisReclen >= length) {
201 bFound = true;
202 pSplitDir = pDir;
203 splitLength = thisReclen;
204 splitRemainder = entryReclen - thisReclen;
205 pDir = adjust_pointer(pDir, thisReclen);
206 break;
207 }
208 } else if (entryReclen >= length) {
209 // We can use this unused entry - we fit into it.
210 // The record length does not need to be adjusted.
211 bFound = true;
212 break;
213 }
214
215 // Next.
216 pDir = adjust_pointer(pDir, entryReclen);
217 }
218 if (bFound)
219 break;
220 m_pExt2Fs->unpinBlock(m_Blocks[i]);
221 }
222
223 if (!bFound || !pDir) {
224 // Need to make a new block.
225 uint32_t block = m_pExt2Fs->findFreeBlock(getInodeNumber());
226 if (block == 0) {
227 // We had a problem.
228 return false;
229 }
230 const uintptr_t buffer = m_pExt2Fs->readBlock(block);
231 if (!buffer) {
232 m_pExt2Fs->releaseBlock(block, getInodeNumber());
233 SYSCALL_ERROR(IoError);
234 return false;
235 }
236 // Publish a valid empty directory block only after its contents are ready.
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);
240 m_pExt2Fs->writeBlock(block);
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);
246 return false;
247 }
248 i = m_Blocks.count() - 1;
249 m_nSize = m_Blocks.count() * m_pExt2Fs->m_BlockSize;
250 m_Size = m_nSize;
252 }
253
254 const bool special = filename.compare(".") || filename.compare("..");
255
256 if (!special && type == EXT2_S_IFDIR) {
257 Ext2Directory* child = static_cast<Ext2Directory*>(pFile);
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);
262 return false;
263 }
264 queueSyncDependency(child->m_Blocks[block]);
265 }
266 }
267
268 if (pSplitDir) {
269 pSplitDir->d_reclen = HOST_TO_LITTLE16(splitLength);
270 pDir->d_reclen = HOST_TO_LITTLE16(splitRemainder);
271 }
272
273 // Set the directory contents.
274 uint32_t entryInode = pFile->getInode();
275 pDir->d_inode = HOST_TO_LITTLE32(entryInode);
276 m_pExt2Fs->increaseInodeRefcount(entryInode);
277
278 if (m_pExt2Fs->checkRequiredFeature(2)) {
279 // File type in directory entry.
280 switch (type) {
281 case EXT2_S_IFREG:
282 pDir->d_file_type = EXT2_FILE;
283 break;
284 case EXT2_S_IFDIR:
285 pDir->d_file_type = EXT2_DIRECTORY;
286 break;
287 case EXT2_S_IFLNK:
288 pDir->d_file_type = EXT2_SYMLINK;
289 break;
290 default:
291 ERROR("Unrecognised filetype.");
292 pDir->d_file_type = EXT2_UNKNOWN;
293 }
294 } else {
295 // No file type in directory entries.
296 pDir->d_file_type = 0;
297 }
298
299 pDir->d_namelen = static_cast<uint8_t>(filename.length());
300 MemoryCopy(pDir->d_name, static_cast<const char*>(filename), filename.length());
301
302 // Trigger write back to disk.
303 m_pExt2Fs->writeBlock(m_Blocks[i]);
304 m_pExt2Fs->unpinBlock(m_Blocks[i]);
305
306 if (!special) {
307 const bool published = addCachedDirectoryEntry(reservation, pFile);
308 assert(published);
309 publishEvent(FileEvents::Created, filename.view(), pFile->isDirectory());
310 reservation.complete(LookupStatus::Found);
311 }
312
313 m_Size = m_nSize;
314
315 return true;
316}
317
318bool Ext2Directory::removeEntry(const String& filename, Ext2Node* pFile) {
319 LockGuard<Mutex> guard(m_DirectoryLock);
320 return removeEntryLocked(filename, pFile);
321}
322
323bool Ext2Directory::removeEntryLocked(const String& filename, Ext2Node* pFile) {
324 // Find this file in the directory.
325 size_t fileInode = pFile->getInodeNumber();
326
327 bool bFound = false;
328
329 uint32_t i;
330 Dir* pDir;
331 for (i = 0; i < m_Blocks.count(); i++) {
332 if (!ensureBlockLoaded(i)) {
333 return false;
334 }
335 uintptr_t buffer = m_pExt2Fs->readBlock(m_Blocks[i]);
336 if (!buffer) {
337 return false;
338 }
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);
346 return false;
347 }
348
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);
353 return false;
354 }
355
356 size_t nameLength = pDir->d_namelen;
357 if (!m_pExt2Fs->checkRequiredFeature(2)) {
358 nameLength |= static_cast<size_t>(pDir->d_file_type) << 8;
359 }
360 if (nameLength > 255 || nameLength > recordLength - offsetof(Dir, d_name)) {
361 m_pExt2Fs->unpinBlock(m_Blocks[i]);
362 SYSCALL_ERROR(IoError);
363 return false;
364 }
365
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)) {
369 // Wipe out the directory entry.
370 uint16_t old_reclen = recordLength;
371 ByteSet(pDir, 0, old_reclen);
372
378
379 pDir->d_reclen = HOST_TO_LITTLE16(old_reclen);
380
381 m_pExt2Fs->writeBlock(m_Blocks[i]);
382 bFound = true;
383 break;
384 }
385 }
386 }
387
388 pDir = reinterpret_cast<Dir*>(reinterpret_cast<uintptr_t>(pDir) + recordLength);
389 }
390
391 m_pExt2Fs->unpinBlock(m_Blocks[i]);
392 if (bFound)
393 break;
394 }
395
396 m_Size = m_nSize;
397
398 if (bFound) {
399 m_pExt2Fs->releaseInode(fileInode, pFile);
401 return true;
402 } else {
403 SYSCALL_ERROR(DoesNotExist);
404 return false;
405 }
406}
407
409 LockGuard<Mutex> guard(m_State->writebackLock);
410 Ext2Node::fileAttributeChanged(m_Size, LITTLE_TO_HOST32(m_pInode->i_atime),
411 LITTLE_TO_HOST32(m_pInode->i_mtime),
412 LITTLE_TO_HOST32(m_pInode->i_ctime));
413}
414
415File::Attributes Ext2Directory::getAttributes() const {
416 return inodeAttributes();
417}
418
419void Ext2Directory::updateAttributes(const Attributes& attributes, uint32_t mask) {
420 updateInodeAttributes(attributes, mask);
421}
422
423bool Ext2Directory::readBytes(uint64_t offset, size_t length, void* output) {
424 if (offset > m_nSize || length > (m_nSize - offset)) {
425 return false;
426 }
427
428 uint8_t* destination = reinterpret_cast<uint8_t*>(output);
429 while (length) {
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)) {
433 return false;
434 }
435
436 const DiskReadView buffer = m_pExt2Fs->readBlockView(m_Blocks[block]);
437 if (!buffer) {
438 return false;
439 }
440
441 size_t available = m_pExt2Fs->m_BlockSize - blockOffset;
442 if (available > length) {
443 available = length;
444 }
445 if (!buffer.copyTo(destination, available, blockOffset))
446 return false;
447
448 destination += available;
449 offset += available;
450 length -= available;
451 }
452
453 return true;
454}
455
456Directory::ReadStatus Ext2Directory::readEntry(uint64_t offset, ParsedEntry& entry) {
457 if (!m_pExt2Fs->m_BlockSize) {
458 return ReadStatus::IoError;
459 }
460
461 Dir header;
462 if (!readBytes(offset, offsetof(Dir, d_name), &header)) {
463 return ReadStatus::IoError;
464 }
465
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;
473 }
474
475 const size_t bytesRemainingInBlock = m_pExt2Fs->m_BlockSize - (offset % m_pExt2Fs->m_BlockSize);
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;
480 }
481
482 if (!entry.inode) {
483 entry.name[0] = 0;
484 return ReadStatus::Complete;
485 }
486
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;
491 }
492 entry.name[entry.nameLength] = 0;
493 return ReadStatus::Complete;
494}
495
496Directory::LookupStatus Ext2Directory::resolveEntry(const ParsedEntry& entry,
497 const StringView& name, File*& child) {
498 child = nullptr;
499 if (!entry.inode || !name.compare(entry.name, entry.nameLength)) {
500 return LookupStatus::NotFound;
501 }
502
503 Inode* inode = m_pExt2Fs->getInode(entry.inode);
504 if (!inode) {
505 return LookupStatus::IoError;
506 }
507
508 uint8_t fileType = entry.fileType;
509 if (!m_pExt2Fs->checkRequiredFeature(2) || fileType == EXT2_UNKNOWN) {
510 switch (LITTLE_TO_HOST16(inode->i_mode) & 0xF000) {
511 case EXT2_S_IFREG:
512 fileType = EXT2_FILE;
513 break;
514 case EXT2_S_IFDIR:
515 fileType = EXT2_DIRECTORY;
516 break;
517 case EXT2_S_IFLNK:
518 fileType = EXT2_SYMLINK;
519 break;
520 case EXT2_S_IFCHR:
521 case EXT2_S_IFBLK:
522 case EXT2_S_IFIFO:
523 case EXT2_S_IFSOCK:
524 return LookupStatus::NotFound;
525 default:
526 return LookupStatus::IoError;
527 }
528 }
529
530 const String filename(entry.name, entry.nameLength);
531 switch (fileType) {
532 case EXT2_FILE: {
533 Ext2File* file = new Ext2File(filename, entry.inode, inode, m_pExt2Fs, this);
534 if (!file || !file->valid()) {
535 delete file;
536 return LookupStatus::IoError;
537 }
538 child = file;
539 break;
540 }
541 case EXT2_DIRECTORY:
542 child = new Ext2Directory(filename, entry.inode, inode, m_pExt2Fs, this);
543 break;
544 case EXT2_SYMLINK:
545 child = new Ext2Symlink(filename, entry.inode, inode, m_pExt2Fs, this);
546 break;
547 case EXT2_CHAR_DEV:
548 case EXT2_BLOCK_DEV:
549 case EXT2_FIFO:
550 case EXT2_SOCKET:
551 return LookupStatus::NotFound;
552 default:
553 return LookupStatus::IoError;
554 }
555
556 return LookupStatus::Found;
557}
558
559Directory::LookupStatus Ext2Directory::resolveChildLocked(const StringView& name, File*& child) {
560 child = nullptr;
561 uint64_t offset = 0;
562 while (offset < m_nSize) {
563 ParsedEntry entry;
564 if (readEntry(offset, entry) != ReadStatus::Complete) {
565 return LookupStatus::IoError;
566 }
567 if (entry.inode && name.compare(entry.name, entry.nameLength)) {
568 return resolveEntry(entry, name, child);
569 }
570 offset += entry.recordLength;
571 }
572 return LookupStatus::NotFound;
573}
574
575Directory::LookupStatus Ext2Directory::resolveChild(const StringView& name, File*& child) {
576 OperationBarrier::Lease operation;
577 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
578 return LookupStatus::IoError;
579 }
580 child = nullptr;
581 if (name.compare(".", 1) || name.compare("..", 2)) {
582 return LookupStatus::NotFound;
583 }
584
585 LockGuard<Mutex> guard(m_DirectoryLock);
586 if (m_Removed) {
587 return LookupStatus::NotFound;
588 }
589 return resolveChildLocked(name, child);
590}
591
592Directory::LookupStatus Ext2Directory::resolveChildAt(uint64_t cookie, const StringView& name,
593 File*& child) {
594 OperationBarrier::Lease operation;
595 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
596 return LookupStatus::IoError;
597 }
598 child = nullptr;
599 if (name.compare(".", 1) || name.compare("..", 2)) {
600 return LookupStatus::NotFound;
601 }
602
603 LockGuard<Mutex> guard(m_DirectoryLock);
604 if (m_Removed) {
605 return LookupStatus::NotFound;
606 }
607 if (cookie < m_nSize) {
608 ParsedEntry entry;
609 if (readEntry(cookie, entry) == ReadStatus::Complete && entry.inode &&
610 name.compare(entry.name, entry.nameLength)) {
611 return resolveEntry(entry, name, child);
612 }
613 }
614
615 // A directory mutation may make a previously returned cookie stale. Fall
616 // back to a name lookup rather than turning that race into a false miss.
617 return resolveChildLocked(name, child);
618}
619
620Directory::ReadStatus Ext2Directory::readDirectory(uint64_t& cookie, DirectoryEntryEmitter emitter,
621 void* context) {
622 OperationBarrier::Lease operation;
623 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
624 return ReadStatus::IoError;
625 }
626 LockGuard<Mutex> guard(m_DirectoryLock);
627 if (m_Removed) {
628 return ReadStatus::Complete;
629 }
630 if (cookie > m_nSize) {
631 return ReadStatus::IoError;
632 }
633
634 while (cookie < m_nSize) {
635 ParsedEntry parsed;
636 if (readEntry(cookie, parsed) != ReadStatus::Complete) {
637 return ReadStatus::IoError;
638 }
639
640 const uint64_t currentCookie = cookie;
641 const uint64_t nextCookie = currentCookie + parsed.recordLength;
642 if (!parsed.inode) {
643 cookie = nextCookie;
644 continue;
645 }
646
647 EntryType type = EntryType::Unknown;
648 switch (parsed.fileType) {
649 case EXT2_UNKNOWN:
650 type = EntryType::Unknown;
651 break;
652 case EXT2_FILE:
653 type = EntryType::Regular;
654 break;
655 case EXT2_DIRECTORY:
656 type = EntryType::Directory;
657 break;
658 case EXT2_SYMLINK:
659 type = EntryType::Symlink;
660 break;
661 case EXT2_CHAR_DEV:
662 type = EntryType::CharacterDevice;
663 break;
664 case EXT2_BLOCK_DEV:
665 type = EntryType::BlockDevice;
666 break;
667 case EXT2_FIFO:
668 type = EntryType::Fifo;
669 break;
670 case EXT2_SOCKET:
671 type = EntryType::Socket;
672 break;
673 default:
674 return ReadStatus::IoError;
675 }
676
677 DirectoryEntryView entry = {StringView(parsed.name, parsed.nameLength), parsed.inode, type,
678 currentCookie, nextCookie};
679 if (!emitter(context, entry)) {
680 return ReadStatus::Stopped;
681 }
682 cookie = nextCookie;
683 }
684
685 return ReadStatus::Complete;
686}
687
688bool Ext2Directory::removeFromParent(Ext2Directory* parent, const String& filename) {
689 if (parent == this) {
690 SYSCALL_ERROR(InvalidArgument);
691 return false;
692 }
693 LockGuard<Mutex> namespaceGuard(namespaceMutationLock());
694 bool empty = false;
695 if (isEmpty(empty) != ReadStatus::Complete) {
696 SYSCALL_ERROR(IoError);
697 return false;
698 }
699 if (!empty) {
700 SYSCALL_ERROR(NotEmpty);
701 return false;
702 }
703
704 LockGuard<Mutex> guard(m_DirectoryLock);
705 if (m_Removed) {
706 SYSCALL_ERROR(DoesNotExist);
707 return false;
708 }
709
710 bool foundDot = false;
711 bool foundDotDot = false;
712 uint64_t offset = 0;
713 while (offset < m_nSize) {
714 ParsedEntry entry;
715 if (readEntry(offset, entry) != ReadStatus::Complete) {
716 SYSCALL_ERROR(IoError);
717 return false;
718 }
719
720 if (entry.inode) {
721 const StringView name(entry.name, entry.nameLength);
722 if (name.compare(".", 1)) {
723 foundDot = entry.inode == getInodeNumber();
724 if (!foundDot) {
725 SYSCALL_ERROR(IoError);
726 return false;
727 }
728 } else if (name.compare("..", 2)) {
729 foundDotDot = entry.inode == parent->getInodeNumber();
730 if (!foundDotDot) {
731 SYSCALL_ERROR(IoError);
732 return false;
733 }
734 } else {
735 SYSCALL_ERROR(NotEmpty);
736 return false;
737 }
738 }
739 offset += entry.recordLength;
740 }
741
742 if (!foundDot || !foundDotDot) {
743 SYSCALL_ERROR(IoError);
744 return false;
745 }
746
747 LockGuard<Mutex> parentGuard(parent->m_DirectoryLock);
748 if (!parent->removeEntryLocked(filename, this)) {
749 return false;
750 }
751 m_Removed = true;
752 markDetached();
753 if (!removeEntryLocked(String(".."), parent)) {
754 ERROR("Ext2 directory was unlinked, but its parent link could not be retired");
755 return true;
756 }
757 if (!removeEntryLocked(String("."), this)) {
758 ERROR("Ext2 directory was unlinked, but its inode could not be retired");
759 }
760
761 return true;
762}
bool empty()
void invalidateDirectoryEntry(const HashedStringView &name)
Definition Directory.cc:876
Mutex & namespaceMutationLock()
Definition Directory.h:416
ReadStatus isEmpty(bool &empty)
Definition Directory.cc:318
bool addCachedDirectoryEntry(NameReservation &reservation, File *pTarget)
Definition Directory.cc:856
bool reserveDirectoryEntry(const HashedStringView &name, NameReservation &reservation)
Definition Directory.cc:627
ReadStatus readDirectory(uint64_t &cookie, DirectoryEntryEmitter emitter, void *context) override
Ext2Directory(const Ext2Directory &file)
virtual bool removeEntry(const String &filename, Ext2Node *pFile)
bool sync() override
~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)
Definition Ext2Node.cc:567
Definition File.h:75
virtual bool isDirectory()
Definition File.cc:804
void setGidOnly(size_t gid)
Definition File.cc:1448
void publishEvent(FileEventMask mask, const StringView &name=StringView(), bool targetIsDirectory=false)
Definition File.cc:861
void setPermissionsOnly(uint32_t perms)
Definition File.cc:1440
void setUidOnly(size_t uid)
Definition File.cc:1444
StringView view() const
Definition String.cc:783
bool compare(const char *s, size_t len) const
Definition String.cc:202
void erase(size_t index)
Definition Vector.h:389
void pushBack(const T &value)
Definition Vector.h:275
size_t count() const
Definition Vector.h:270
Definition ext2.h:201
Definition ext2.h:152