The Pedigree Project 0.1
File.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 "File.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/Metrics.h"
24#include "pedigree/kernel/machine/Disk.h"
25#include "pedigree/kernel/process/Scheduler.h"
26#include "pedigree/kernel/process/Thread.h"
27#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
28#include "pedigree/kernel/processor/Processor.h"
29#include "pedigree/kernel/processor/ProcessorInformation.h"
30#include "pedigree/kernel/processor/VirtualAddressSpace.h"
31#include "pedigree/kernel/processor/types.h"
32#include "pedigree/kernel/syscallError.h"
33#include "pedigree/kernel/utilities/Iterator.h"
34#include "pedigree/kernel/utilities/Pair.h"
35#include "pedigree/kernel/utilities/Result.h"
36#include "pedigree/kernel/utilities/Vector.h"
37#include "pedigree/kernel/utilities/assert.h"
38#include "pedigree/kernel/utilities/utility.h"
39
40#include "Filesystem.h"
41#include "MemoryMappedFile.h"
42#include "VFS.h"
43
44RetainedFile::RetainedFile() = default;
45
46RetainedFile::RetainedFile(RetainedFile&& other) noexcept : m_File(other.m_File) {
47 other.m_File = nullptr;
48}
49
50RetainedFile::~RetainedFile() {
51 reset();
52}
53
54RetainedFile& RetainedFile::operator=(RetainedFile&& other) noexcept {
55 if (this != &other) {
56 reset();
57 m_File = other.m_File;
58 other.m_File = nullptr;
59 }
60 return *this;
61}
62
63void RetainedFile::adopt(File* file) {
64 reset();
65 m_File = file;
66}
67
68void RetainedFile::reset() {
69 File* file = m_File;
70 m_File = nullptr;
71 if (file)
72 file->releaseVfsReference();
73}
74
76 uintptr_t identity = __atomic_load_n(&m_FutexIdentity, __ATOMIC_ACQUIRE);
77 if (!identity) {
78 static uintptr_t nextIdentity = 0;
79 const uintptr_t allocated = __atomic_add_fetch(&nextIdentity, uintptr_t(1), __ATOMIC_RELAXED);
80 // Futex keys tag these tokens. Never let wraparound alias a retired file.
81 assert(allocated && allocated <= (~uintptr_t(0) >> 1));
82 __atomic_compare_exchange_n(&m_FutexIdentity, &identity, allocated, false, __ATOMIC_ACQ_REL,
83 __ATOMIC_ACQUIRE);
84 if (!identity) {
85 identity = allocated;
86 }
87 }
88 return identity;
89}
90
91File::ParentLease::ParentLease()
92 : m_Parent(nullptr),
93 m_Retained(false)
94#if THREADS && !defined(STANDALONE_MUTEXES)
95 ,
96 m_TerminationDeferral(true)
97#endif
98{
99}
100
101File::ParentLease::~ParentLease() {
102 if (m_Retained) {
103 VFS::instance().untrackFile(m_Parent);
104 }
105}
106
107void File::ParentLease::swap(ParentLease& other) {
108 File* parent = m_Parent;
109 bool retained = m_Retained;
110 m_Parent = other.m_Parent;
111 m_Retained = other.m_Retained;
112 other.m_Parent = parent;
113 other.m_Retained = retained;
114}
115
116bool File::writeCallback(CacheConstants::CallbackCause cause, uintptr_t loc, uintptr_t page,
117 void* meta) {
118 File* pFile = reinterpret_cast<File*>(meta);
119
120 switch (cause) {
121 case CacheConstants::WriteBack: {
122 OperationBarrier::Lease operation;
123 if (!pFile->tryAcquireFilesystemOperation(operation)) {
124 return false;
125 }
127 } break;
128 case CacheConstants::Eviction:
129 // Remove this page from our data cache.
131 pFile->setCachedPage(loc / pFile->getBlockSize(), FILE_BAD_BLOCK);
132 break;
133 default:
134 WARNING(
135 "File: unknown cache callback -- could indicate potential "
136 "future I/O issues.");
137 break;
138 }
139 return true;
140}
141
142bool File::fillCacheCallback(CacheConstants::CallbackCause cause, uintptr_t loc, uintptr_t page,
143 void* meta) {
144 File* pFile = reinterpret_cast<File*>(meta);
145 if (cause == CacheConstants::WriteBack) {
146 OperationBarrier::Lease operation;
147 if (!pFile->tryAcquireFilesystemOperation(operation)) {
148 return false;
149 }
151 } else if (cause == CacheConstants::Eviction) {
152 pFile->setCachedPage(loc / PhysicalMemoryManager::getPageSize(), FILE_BAD_BLOCK);
153 } else if (cause != CacheConstants::Eviction) {
154 WARNING("File: unknown fill-cache callback cause.");
155 }
156 return true;
157}
158
159File::CacheState::CacheState() : data(FILE_BAD_BLOCK), indexLock(), fill(), fillLock() {}
160
161File::CacheState& File::cacheState() {
162 return m_CacheState;
163}
164
166 : m_Name(),
167 m_AccessedTime(0),
168 m_ModifiedTime(0),
169 m_CreationTime(0),
170 m_Inode(0),
171 m_pFilesystem(0),
172 m_Size(0),
173 m_pParent(0),
174 m_MetadataLock(),
175 m_pDetachedParent(0),
176 m_bDetachedParentHandled(false),
177 m_nWriters(0),
178 m_nReaders(0),
179 m_Uid(0),
180 m_Gid(0),
181 m_Permissions(0),
182 m_CacheState(),
183 m_bDirect(false),
184 m_WriteLock(),
185 m_Lock(),
186 m_MonitorTargets() {}
187
188File::File(const String& name, Time::Timestamp accessedTime, Time::Timestamp modifiedTime,
189 Time::Timestamp creationTime, uintptr_t inode, Filesystem* pFs, size_t size,
190 File* pParent)
191 : m_Name(name),
192 m_AccessedTime(accessedTime),
193 m_ModifiedTime(modifiedTime),
194 m_CreationTime(creationTime),
195 m_Inode(inode),
196 m_pFilesystem(pFs),
197 m_Size(size),
198 m_pParent(pParent),
199 m_MetadataLock(),
200 m_pDetachedParent(0),
201 m_bDetachedParentHandled(false),
202 m_nWriters(0),
203 m_nReaders(0),
204 m_Uid(0),
205 m_Gid(0),
206 m_Permissions(0),
207 m_CacheState(),
208 m_bDirect(false),
209 m_WriteLock(),
210 m_Lock(),
211 m_MonitorTargets() {
212 size_t maxBlock = size / getBlockSize();
213 if (size % getBlockSize()) {
214 ++maxBlock;
215 }
216
217 // Prefill the block cache with bad pages to avoid continuous resizes.
218 setCachedPage(maxBlock, FILE_BAD_BLOCK);
219}
220
222 closeFileEvents();
224
225 {
226 LockGuard<Mutex> guard(m_Lock);
227 for (auto target : m_MonitorTargets) {
228 delete target;
229 }
230 m_MonitorTargets.clear();
231 }
232
233 // Releasing the parent can run its destructor, so no File lock may be held.
234 if (m_pDetachedParent) {
236 }
237}
238
239uint64_t File::read(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock) {
240 OperationBarrier::Lease operation;
241 if (!tryAcquireFilesystemOperation(operation)) {
242 return 0;
243 }
244 Metrics::increment(Metrics::FileReadCalls);
245 if (isBytewise()) {
246 // Have to perform bytewise reads
248 const uint64_t read = readBytewise(location, size, buffer, bCanBlock);
249 Metrics::add(Metrics::FileReadBytes, read);
250 return read;
251 }
252
253 LockGuard<Mutex> guard(dataMutationLock());
254
255 const size_t fileSize = getSize();
256 if (!size || location >= fileSize) {
257 return 0;
258 }
259
260 const uint64_t remaining = fileSize - location;
261 if (size > remaining) {
262 size = remaining;
263 }
264
265 const bool filled = useFillCache();
266 const size_t blockSize = filled ? PhysicalMemoryManager::getPageSize() : getBlockSize();
267
268 size_t n = 0;
269 while (size) {
270 if (filled && !m_bDirect) {
271 const size_t cached = cacheState().fill.read(location, size, buffer);
272 if (cached) {
273 location += cached;
274 size -= cached;
275 n += cached;
276 if (buffer) {
277 buffer += cached;
278 }
279 continue;
280 }
281 }
282 if (location >= fileSize) {
283 Metrics::add(Metrics::FileReadBytes, n);
284 return n;
285 }
286
287 uintptr_t block = location / blockSize;
288 uintptr_t offs = location % blockSize;
289 uintptr_t sz = (size + offs > blockSize) ? blockSize - offs : size;
290
291 // Handle a possible early EOF.
292 if (sz > (fileSize - location))
293 sz = fileSize - location;
294
295 const size_t readAheadLimit = MaxReadPages * blockSize;
296 const size_t readAheadBytes =
297 size > blockSize ? (size < readAheadLimit - offs ? size + offs : readAheadLimit) : 0;
298 uintptr_t buff = readIntoCache(block, false, readAheadBytes);
299 if (buff == FILE_BAD_BLOCK) {
300 ERROR("File::read - failed to get page from cache, returning early");
301 Metrics::add(Metrics::FileReadBytes, n);
302 return n;
303 }
304
305 if (buffer) {
306 ForwardMemoryCopy(reinterpret_cast<void*>(buffer), reinterpret_cast<void*>(buff + offs), sz);
307 buffer += sz;
308 }
310
311 location += sz;
312 size -= sz;
313 n += sz;
314 }
315 Metrics::add(Metrics::FileReadBytes, n);
316 return n;
317}
318
319size_t File::readCached(uint64_t location, size_t size, uintptr_t buffer,
320 bool (*prepare)(uintptr_t, size_t)) {
321 OperationBarrier::Lease operation;
322 if (!tryAcquireFilesystemOperation(operation)) {
323 return 0;
324 }
325 Metrics::increment(Metrics::FileCachedReadCalls);
326 if (isBytewise() || !useFillCache() || m_bDirect) {
327 return 0;
328 }
329 LockGuard<Mutex> guard(dataMutationLock());
330 const size_t fileSize = getSize();
331 if (location >= fileSize) {
332 return 0;
333 }
334 if (size > fileSize - location) {
335 size = fileSize - location;
336 }
337 const size_t read = cacheState().fill.read(location, size, buffer, prepare);
338 Metrics::add(Metrics::FileReadBytes, read);
339 return read;
340}
341
342uint64_t File::write(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock) {
343 WriteGuard guard = lockWrites();
344 return guard.write(location, size, buffer, bCanBlock);
345}
346
347uint64_t File::writeUnlocked(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock) {
348 if (!size || location > (~static_cast<uint64_t>(0) - size)) {
349 return 0;
350 }
351
352 const uint64_t endLocation = location + size;
353 if (endLocation > static_cast<uint64_t>(~static_cast<size_t>(0))) {
354 return 0;
355 }
356
357 if (isBytewise()) {
358 // Have to perform bytewise reads
360 return writeBytewise(location, size, buffer, bCanBlock);
361 }
362
363 if (cacheState().executableMappings) {
364 SYSCALL_ERROR(TextFileBusy);
365 return 0;
366 }
367
368 const size_t filesystemBlockSize = getBlockSize();
369 const size_t blockSize =
370 useFillCache() ? PhysicalMemoryManager::getPageSize() : filesystemBlockSize;
371
372 if (!prepareWrite(location, size)) {
373 return 0;
374 }
375
376 size_t n = 0;
377 while (size) {
378 uintptr_t block = location / blockSize;
379 uintptr_t offs = location % blockSize;
380 uintptr_t sz = (size + offs > blockSize) ? blockSize - offs : size;
381
382 uintptr_t buff = readIntoCache(block, useFillCache() && !offs && sz == blockSize);
383 if (buff == FILE_BAD_BLOCK) {
384 ERROR("File::write - failed to get page from cache, returning early");
385 return n;
386 }
387
388 ForwardMemoryCopy(reinterpret_cast<void*>(buff + offs), reinterpret_cast<void*>(buffer), sz);
389
390 if (useFillCache()) {
391 const uint64_t pageOffset = block * blockSize;
392 const size_t firstBlock = offs / filesystemBlockSize;
393 const size_t endBlock = (offs + sz + filesystemBlockSize - 1) / filesystemBlockSize;
394 const size_t fileBlockOffset = firstBlock * filesystemBlockSize;
395 writeBlocks(pageOffset + fileBlockOffset, buff + fileBlockOffset,
396 (endBlock - firstBlock) * filesystemBlockSize);
397 } else {
398 writeBlock(block * blockSize, buff);
399 }
401
402 location += sz;
403 buffer += sz;
404 size -= sz;
405 n += sz;
406 }
407
408 if (location >= getSize()) {
409 m_Size = location;
410 fileAttributeChanged();
411 }
412 return n;
413}
414
415uint64_t File::append(uint64_t size, uintptr_t buffer, uint64_t& location, bool bCanBlock) {
416 WriteGuard guard = lockWrites();
417 return guard.append(size, buffer, location, bCanBlock);
418}
419
421 return WriteGuard(*this);
422}
423
424File::WriteGuard::WriteGuard(File& file) : m_File(file), m_Guard(file.writeSerializationLock()) {}
425
426File::WriteGuard::~WriteGuard() {
427 if (m_MetadataPending) {
428 m_File.publishWriteMetadata();
429 }
430}
431
432void File::publishWriteMetadata() {
433 OperationBarrier::Lease operation;
434 if (!tryAcquireFilesystemOperation(operation)) {
435 return;
436 }
437 if (!isBytewise() && !isDirectory() && !isSymlink() && isSeekable()) {
438 Attributes attributes;
439 attributes.modified = attributes.changed = Time::getTime();
440 updateAttributes(attributes, ModifyTime | ChangeTime);
441 }
442 publishEvent(FileEvents::Modify);
443}
444
445uint64_t File::WriteGuard::write(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock,
446 bool publishMetadata) {
447 OperationBarrier::Lease operation;
448 if (!m_File.tryAcquireFilesystemOperation(operation)) {
449 return 0;
450 }
451 Metrics::increment(Metrics::FileWriteCalls);
452 LockGuard<Mutex> guard(m_File.dataMutationLock());
453 const uint64_t written = m_File.writeUnlocked(location, size, buffer, bCanBlock);
454 Metrics::add(Metrics::FileWriteBytes, written);
455 if (written) {
456 if (publishMetadata) {
457 m_File.publishWriteMetadata();
458 } else {
459 m_MetadataPending = true;
460 }
461 }
462 return written;
463}
464
465uint64_t File::WriteGuard::append(uint64_t size, uintptr_t buffer, uint64_t& location,
466 bool bCanBlock, bool publishMetadata) {
467 OperationBarrier::Lease operation;
468 if (!m_File.tryAcquireFilesystemOperation(operation)) {
469 return 0;
470 }
471 Metrics::increment(Metrics::FileWriteCalls);
472 LockGuard<Mutex> guard(m_File.dataMutationLock());
473 location = m_File.getSize();
474 const uint64_t written = m_File.writeUnlocked(location, size, buffer, bCanBlock);
475 Metrics::add(Metrics::FileWriteBytes, written);
476 if (written) {
477 if (publishMetadata) {
478 m_File.publishWriteMetadata();
479 } else {
480 m_MetadataPending = true;
481 }
482 }
483 return written;
484}
485
486physical_uintptr_t File::getPhysicalPage(size_t offset) {
487 OperationBarrier::Lease operation;
488 if (!tryAcquireFilesystemOperation(operation)) {
489 return ~0UL;
490 }
491 LockGuard<Mutex> guard(dataMutationLock());
492 if (!allowPhysicalPage())
493 return ~0UL;
494 if (m_bDirect) {
495 WARNING("File in direct mode, cannot get backing page.");
496 return ~0UL;
497 }
498
499 EMIT_IF(VFS_NOMMU) {
500 return ~0UL;
501 }
502
503 // Sanitise input.
504 size_t blockSize = getBlockSize();
505 size_t nativeBlockSize = PhysicalMemoryManager::getPageSize();
506 if (useFillCache()) {
507 blockSize = nativeBlockSize;
508 }
509 offset &= ~(blockSize - 1);
510
511 // Quick and easy exit.
512 if (offset >= getSize()) {
513 return ~0UL;
514 }
515
516 // Check if we have this page in the cache and acquire the cache reference
517 // before translating it. A cache address observed before a successful pin
518 // may already be retiring.
519 uintptr_t vaddr = FILE_BAD_BLOCK;
520 bool pinned = false;
521 if (LIKELY(!useFillCache())) {
522 vaddr = acquireCachedBlock(offset, false);
523 if (!vaddr) {
524 return ~0UL;
525 }
526 pinned = true;
527 } else {
528 // Using the fill cache, because the filesystem has a block size
529 // smaller than our native page size. lookup() itself acquires the
530 // reference; taking a second pin here would leak one on every mmap.
531 vaddr = cacheState().fill.lookup(offset);
532 if (!vaddr) {
533 return ~0UL;
534 }
535 pinned = true;
536 }
537
538 // Translate only while the exact published page remains pinned.
539 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
540 if (va.isMapped(reinterpret_cast<void*>(vaddr))) {
541 physical_uintptr_t phys = 0;
542 size_t flags = 0;
543 va.getMapping(reinterpret_cast<void*>(vaddr), phys, flags);
544 __atomic_add_fetch(&physicalPageLoans(), 1, __ATOMIC_RELEASE);
545 return phys;
546 }
547
548 if (pinned) {
549 if (UNLIKELY(useFillCache()))
550 cacheState().fill.release(offset);
551 else
552 unpinBlock(offset);
553 }
554 return ~0UL;
555}
556
557void File::returnPhysicalPage(size_t offset) {
558 if (m_bDirect) {
559 return;
560 }
561
562 // Sanitise input.
563 size_t blockSize = getBlockSize();
564 size_t nativeBlockSize = PhysicalMemoryManager::getPageSize();
565 if (useFillCache()) {
566 blockSize = nativeBlockSize;
567 }
568 offset &= ~(blockSize - 1);
569
570 // Release the page. Beware - this could cause a cache evict, which will
571 // make the next read/write at this offset do real (slow) I/O.
572 if (UNLIKELY(useFillCache())) {
573 cacheState().fill.release(offset);
574 } else {
575 unpinBlock(offset);
576 }
577 assert(__atomic_load_n(&physicalPageLoans(), __ATOMIC_ACQUIRE));
578 __atomic_sub_fetch(&physicalPageLoans(), 1, __ATOMIC_RELEASE);
579}
580
581bool File::syncAndReturnPhysicalPage(size_t offset, bool async) {
582 const bool succeeded = sync(offset, async);
583 returnPhysicalPage(offset);
584 return succeeded;
585}
586
588 return dataMutationLock().tryAcquire();
589}
590
591void File::endMappingRelease() {
592 dataMutationLock().release();
593}
594
596 return syncRange(0, 0);
597}
598
599bool File::syncRange(size_t offset, size_t length) {
600 OperationBarrier::Lease operation;
601 if (!tryAcquireFilesystemOperation(operation)) {
602 return false;
603 }
604 if (length && length - 1 > ~size_t(0) - offset) {
605 SYSCALL_ERROR(InvalidArgument);
606 return false;
607 }
608 LockGuard<Mutex> dataGuard(dataMutationLock());
609 const bool filled = useFillCache();
610 const size_t blockSize = filled ? PhysicalMemoryManager::getPageSize() : getBlockSize();
611 const size_t firstBlock = offset / blockSize;
612 const size_t lastBlock = length ? (offset + length - 1) / blockSize : ~size_t(0);
613 struct SyncPage {
614 size_t block;
615 uintptr_t buffer;
616 };
617
618 size_t snapshotSize = 0;
619 {
620 LockGuard<Mutex> guard(cacheState().indexLock);
621 snapshotSize = cacheState().data.count();
622 }
623
624 // Reserve outside the index lock. Allocation can trigger cache pressure, whose
625 // eviction callback removes entries under this same File lock.
626 Vector<SyncPage> pages(snapshotSize);
627 {
628 LockGuard<Mutex> guard(cacheState().indexLock);
629 for (auto it = cacheState().data.begin();
630 it != cacheState().data.end() && pages.count() < snapshotSize; ++it) {
631 const uintptr_t buffer = it.__getNode()->value;
632 const size_t block = it.__getNode()->key.hash();
633 if (buffer != FILE_BAD_BLOCK && block >= firstBlock && block <= lastBlock) {
634 pages.pushBack({block, buffer});
635 }
636 }
637 }
638
639 bool succeeded = true;
640 uint64_t batch[Disk::MaxSyncPages];
641 size_t batchCount = 0;
642 auto flushBatch = [&] {
643 if (!batchCount)
644 return;
645 succeeded = syncPages(batch, batchCount) && succeeded;
646 for (size_t i = 0; i < batchCount; ++i) {
647 if (filled)
648 cacheState().fill.release(batch[i]);
649 else
650 unpinBlock(batch[i]);
651 }
652 batchCount = 0;
653 };
654 for (const SyncPage& page : pages) {
655 const uint64_t location = page.block * blockSize;
656 // The index holds weak identities. Pin each producer page before checking
657 // the snapshot, and retain that pin through the batch's durability result.
658 if (filled) {
659 const uintptr_t buffer = cacheState().fill.lookup(location);
660 if (!buffer)
661 continue;
662 if (buffer != page.buffer) {
663 cacheState().fill.release(location);
664 continue;
665 }
666 batch[batchCount++] = location;
667 if (batchCount == Disk::MaxSyncPages)
668 flushBatch();
669 continue;
670 }
671 if (!pinBlock(location)) {
672 continue;
673 }
674
675 if (getCachedPage(page.block) == page.buffer) {
676 writeBlock(location, page.buffer);
677 batch[batchCount++] = location;
678 if (batchCount == Disk::MaxSyncPages)
679 flushBatch();
680 } else {
681 unpinBlock(location);
682 }
683 }
684 flushBatch();
685 return succeeded;
686}
687
688bool File::syncPages(const uint64_t* offsets, size_t count) {
689 OperationBarrier::Lease operation;
690 if (!tryAcquireFilesystemOperation(operation)) {
691 return false;
692 }
693 if (count > Disk::MaxSyncPages || (count && !offsets))
694 return false;
695 for (size_t i = 0; i < count; ++i) {
696 if (offsets[i] >= getSize())
697 return false;
698 }
699 bool succeeded = true;
700 for (size_t i = 0; i < count; ++i)
701 succeeded = sync(offsets[i], false) && succeeded;
702 return succeeded;
703}
704
705bool File::sync(size_t offset, bool async) {
706 OperationBarrier::Lease operation;
707 if (!tryAcquireFilesystemOperation(operation)) {
708 return false;
709 }
710 return true;
711}
712
713bool File::tryAcquireFilesystemOperation(OperationBarrier::Lease& operation) const {
714 if (m_pFilesystem && !m_pFilesystem->tryAcquireOperation(operation)) {
715 SYSCALL_ERROR(DeviceDoesNotExist);
716 return false;
717 }
718 return true;
719}
720
721Time::Timestamp File::getCreationTime() {
722 return getAttributes().changed;
723}
724
725void File::setCreationTime(Time::Timestamp t) {
726 OperationBarrier::Lease operation;
727 if (!tryAcquireFilesystemOperation(operation)) {
728 return;
729 }
730 Attributes attributes;
731 attributes.changed = t;
732 updateAttributes(attributes, ChangeTime);
733 publishEvent(FileEvents::Attributes);
734}
735
736Time::Timestamp File::getAccessedTime() {
737 return getAttributes().accessed;
738}
739
740void File::setAccessedTime(Time::Timestamp t) {
741 OperationBarrier::Lease operation;
742 if (!tryAcquireFilesystemOperation(operation)) {
743 return;
744 }
745 Attributes attributes;
746 attributes.accessed = t;
747 updateAttributes(attributes, AccessTime);
748 publishEvent(FileEvents::Attributes);
749}
750
751void File::setTimes(Time::Timestamp accessed, Time::Timestamp modified, bool changeAccessed,
752 bool changeModified) {
753 OperationBarrier::Lease operation;
754 if (!tryAcquireFilesystemOperation(operation)) {
755 return;
756 }
757 // A caller may request a ctime-only update without changing atime or mtime.
758 Attributes attributes;
759 attributes.accessed = accessed;
760 attributes.modified = modified;
761 attributes.changed = Time::getTime();
762 updateAttributes(attributes, ChangeTime | (changeAccessed ? AccessTime : 0U) |
763 (changeModified ? ModifyTime : 0U));
764 publishEvent(FileEvents::Attributes);
765}
766
767Time::Timestamp File::getModifiedTime() {
768 return getAttributes().modified;
769}
770
771void File::setModifiedTime(Time::Timestamp t) {
772 OperationBarrier::Lease operation;
773 if (!tryAcquireFilesystemOperation(operation)) {
774 return;
775 }
776 Attributes attributes;
777 attributes.modified = t;
778 updateAttributes(attributes, ModifyTime);
779 publishEvent(FileEvents::Attributes);
780}
781
783 LockGuard<Mutex> guard(m_MetadataLock);
784 return m_Name;
785}
786
787void File::getName(String& s) const {
788 LockGuard<Mutex> guard(m_MetadataLock);
789 s = m_Name;
790}
791
792size_t File::getSize() {
793 return m_Size;
794}
795
796void File::setSize(size_t sz) {
797 m_Size = sz;
798}
799
801 return false;
802}
803
805 return false;
806}
807
808bool File::isPipe() const {
809 return false;
810}
811
812bool File::isFifo() const {
813 return false;
814}
815
816bool File::isSocket() const {
817 return false;
818}
819
820bool File::isSeekable() const {
821 return true;
822}
823
825 return !isDirectory() && !isSymlink() && !isPipe() && !isFifo() && !isSocket() && !isBytewise() &&
826 isSeekable() && !isDirectPhysicalMapping();
827}
828
829uint64_t File::maximumFileSize() const {
830 return ~uint64_t(0) >> 1;
831}
832
833uintptr_t File::getInode() const {
834 return m_Inode;
835}
836
837void File::setInode(uintptr_t inode) {
838 m_Inode = inode;
839}
840
841Filesystem* File::getFilesystem() const {
842 return m_pFilesystem;
843}
844
845void File::setFilesystem(Filesystem* pFs) {
846 m_pFilesystem = pFs;
847}
848
849void File::fileAttributeChanged() {}
850
851FileHandleStatus File::subscribeInodeEvents(FileEventMask, const SharedPointer<FileEventObserver>&,
852 FileEventSubscription& subscription) {
853 subscription.reset();
854 return FileHandleStatus::Unsupported;
855}
856
857void File::publishInodeEvent(const FileEvent&) {}
858
859void File::finishInodeRetirement() {}
860
861void File::publishEvent(FileEventMask mask, const StringView& name, bool targetIsDirectory) {
862 // Deletion must close admission even when no observer is currently registered.
863 if (!(mask & FileEvents::DeletedSelf) && !anyFileEventObservers()) {
864 return;
865 }
866 uint32_t producer = 0;
867#if THREADS && !defined(STANDALONE_MUTEXES)
868 Thread* thread = Processor::information().getCurrentThread();
869 if (thread && thread->getParent())
870 producer = static_cast<uint32_t>(thread->getParent()->getId());
871#endif
872 const FileEvent event(mask, name, targetIsDirectory, producer);
873 constexpr FileEventMask InodeEvents = FileEvents::Modify | FileEvents::Attributes |
874 FileEvents::Open | FileEvents::CloseWrite |
875 FileEvents::CloseNoWrite;
876 if (!name.length() && (mask & InodeEvents))
877 publishInodeEvent(FileEvent(mask & InodeEvents, name, isDirectory(), producer));
878 if (mask & FileEvents::DeletedSelf) {
880 } else {
881 notifyFileEvent(event);
882 }
883
884 constexpr FileEventMask ChildEvents = FileEvents::Access | FileEvents::Modify |
885 FileEvents::Attributes | FileEvents::CloseWrite |
886 FileEvents::CloseNoWrite | FileEvents::Open;
887 if (!name.length() && (mask & ChildEvents)) {
888 ParentLease parent;
889 String childName;
890 if (snapshotNamespace(parent, childName, mask)) {
891 parent.get()->notifyFileEvent(FileEvent(mask, childName.view(), isDirectory(), producer));
892 }
893 }
894}
895
896void File::increaseRefCount(bool bIsWriter) {
897 if (bIsWriter)
898 m_nWriters++;
899 else
900 m_nReaders++;
901}
902
903void File::decreaseRefCount(bool bIsWriter) {
904 if (bIsWriter)
905 m_nWriters--;
906 else
907 m_nReaders--;
908}
909
913
917
919 return m_pFilesystem && m_pFilesystem->getRoot() == this;
920}
921
922void File::setPermissions(uint32_t perms) {
923 OperationBarrier::Lease operation;
924 if (!tryAcquireFilesystemOperation(operation)) {
925 return;
926 }
927 Attributes attributes;
928 attributes.permissions = perms;
929 updateAttributes(attributes, Permissions);
930 publishEvent(FileEvents::Attributes);
931}
932
933uint32_t File::getPermissions() const {
934 return getAttributes().permissions;
935}
936
937bool File::setOwnership(size_t uid, size_t gid, bool changeUid, bool changeGid) {
938 OperationBarrier::Lease operation;
939 if (!tryAcquireFilesystemOperation(operation)) {
940 return false;
941 }
942 if (!changeUid && !changeGid)
943 return true;
944 if (!changeOwnership(uid, gid, changeUid, changeGid))
945 return false;
946 publishEvent(FileEvents::Attributes);
947 return true;
948}
949
950bool File::changeOwnership(size_t uid, size_t gid, bool changeUid, bool changeGid) {
951 Attributes attributes = getAttributes();
952 attributes.uid = uid;
953 attributes.gid = gid;
954 updateAttributes(attributes, (changeUid ? Owner : 0U) | (changeGid ? Group : 0U));
955 return true;
956}
957
958void File::setUid(size_t uid) {
959 OperationBarrier::Lease operation;
960 if (!tryAcquireFilesystemOperation(operation)) {
961 return;
962 }
963 Attributes attributes;
964 attributes.uid = uid;
965 updateAttributes(attributes, Owner);
966 publishEvent(FileEvents::Attributes);
967}
968
969size_t File::getUid() const {
970 return getAttributes().uid;
971}
972
973void File::setGid(size_t gid) {
974 OperationBarrier::Lease operation;
975 if (!tryAcquireFilesystemOperation(operation)) {
976 return;
977 }
978 Attributes attributes;
979 attributes.gid = gid;
980 updateAttributes(attributes, Group);
981 publishEvent(FileEvents::Attributes);
982}
983
984size_t File::getGid() const {
985 return getAttributes().gid;
986}
987
988File* File::getParent() const {
989 return __atomic_load_n(&m_pParent, __ATOMIC_ACQUIRE);
990}
991
992void File::getNamespace(ParentLease& parent, String& name) const {
993 snapshotNamespace(parent, name, FileEvents::None);
994}
995
996bool File::snapshotNamespace(ParentLease& parent, String& name, FileEventMask interest) const {
997 ParentLease replacement;
998 bool captured = false;
999 {
1000 LockGuard<Mutex> guard(m_MetadataLock);
1001 File* current = getParent();
1002 if (current) {
1003 replacement.m_Retained = VFS::instance().retainTrackedFile(current);
1004 if (replacement.m_Retained || (m_pFilesystem && current == m_pFilesystem->getRoot())) {
1005 replacement.m_Parent = current;
1006 }
1007 }
1008 // The raw parent pointer is only safe to inspect after acquiring its lease.
1009 if (!interest ||
1010 (replacement.m_Parent && replacement.m_Parent->hasFileEventObservers(interest))) {
1011 name = m_Name;
1012 captured = true;
1013 }
1014 }
1015 parent.swap(replacement);
1016 return captured;
1017}
1018
1019void File::moveNamespace(const String& name, File* parent) {
1020 LockGuard<Mutex> guard(m_MetadataLock);
1021 m_Name = name;
1022 __atomic_store_n(&m_pParent, parent, __ATOMIC_RELEASE);
1023}
1024
1026 LockGuard<Mutex> guard(m_MetadataLock);
1027 if (__atomic_exchange_n(&m_bDetachedParentHandled, true, __ATOMIC_ACQ_REL)) {
1028 return;
1029 }
1030
1031 File* parent = getParent();
1032 if (!parent) {
1033 return;
1034 }
1035
1036 File* root = m_pFilesystem ? m_pFilesystem->getRoot() : nullptr;
1037 if (parent == root) {
1038 return;
1039 }
1040
1041 if (VFS::instance().retainTrackedFile(parent)) {
1042 m_pDetachedParent = parent;
1043 return;
1044 }
1045
1046 // A failed retain for any non-root parent means it is being retired.
1047 __atomic_store_n(&m_pParent, nullptr, __ATOMIC_RELEASE);
1048}
1049
1050int File::select(bool bWriting, int timeout) {
1051 return 1;
1052}
1053
1054ReadyMask File::queryReady(bool reading, bool writing) {
1055 ReadyMask ready = ReadyNone;
1056 if (reading && select(false, 0)) {
1057 ready |= ReadyRead;
1058 }
1059 if (writing && select(true, 0)) {
1060 ready |= ReadyWrite;
1061 }
1062 return ready;
1063}
1064
1066 return false;
1067}
1068
1069bool File::supports(const size_t command) const {
1070 return false;
1071}
1072
1073int File::command(const size_t command, void* buffer) {
1074 return 0;
1075}
1076
1077size_t File::getBlockSize() const {
1079}
1080
1082 m_bDirect = true;
1083}
1084
1086 m_bDirect = false;
1087}
1088
1089void File::preallocate(size_t expectedSize, bool zero) {}
1090
1091File::Attributes File::getAttributes() const {
1092 Attributes attributes;
1093 {
1094 LockGuard<Mutex> guard(m_MetadataLock);
1095 attributes.accessed = m_AccessedTime;
1096 attributes.modified = m_ModifiedTime;
1097 attributes.changed = m_CreationTime;
1098 attributes.uid = m_Uid;
1099 attributes.gid = m_Gid;
1100 attributes.permissions = m_Permissions;
1101 }
1102 attributes.size = const_cast<File*>(this)->getSize();
1103 attributes.blocks = attributes.size / 512 + (attributes.size % 512 != 0);
1104 return attributes;
1105}
1106
1107XattrStatus File::getExtendedAttribute(const StringView&, void*, size_t, size_t& required) {
1108 required = 0;
1109 return XattrStatus::Unsupported;
1110}
1111
1112XattrStatus File::listExtendedAttributes(void*, size_t, size_t& required) {
1113 required = 0;
1114 return XattrStatus::Unsupported;
1115}
1116
1117XattrStatus File::setExtendedAttribute(const StringView&, const void*, size_t, unsigned) {
1118 return XattrStatus::Unsupported;
1119}
1120
1121XattrStatus File::removeExtendedAttribute(const StringView&) {
1122 return XattrStatus::Unsupported;
1123}
1124
1125void File::updateAttributes(const Attributes& attributes, uint32_t mask) {
1126 {
1127 LockGuard<Mutex> guard(m_MetadataLock);
1128 if (mask & AccessTime)
1129 m_AccessedTime = attributes.accessed;
1130 if (mask & ModifyTime)
1131 m_ModifiedTime = attributes.modified;
1132 m_CreationTime = (mask & ChangeTime) ? attributes.changed : Time::getTime();
1133 if (mask & Owner)
1134 m_Uid = attributes.uid;
1135 if (mask & Group)
1136 m_Gid = attributes.gid;
1137 if (mask & Permissions)
1138 m_Permissions = attributes.permissions;
1139 }
1140 fileAttributeChanged();
1141}
1142
1143bool File::prepareSharedMapping(size_t, size_t) {
1144 return true;
1145}
1146
1147bool File::prepareWrite(uint64_t location, uint64_t size) {
1148 const uint64_t end = location + size;
1149 extend(static_cast<size_t>(end), location, size);
1150 return getSize() >= end;
1151}
1152
1154
1155Mutex& File::writeSerializationLock() {
1156 return m_WriteLock;
1157}
1158
1159Mutex& File::dataMutationLock() {
1160 return m_DataMutationLock;
1161}
1162
1163size_t& File::physicalPageLoans() {
1164 return m_PhysicalPageLoans;
1165}
1166
1168 if (!cacheState().fill.empty()) {
1169 SYSCALL_ERROR(IoError);
1170 return false;
1171 }
1172 LockGuard<Mutex> guard(cacheState().indexLock);
1173 cacheState().data.clear();
1174 return true;
1175}
1176
1177bool File::allowMapping(bool, bool, bool&) {
1178 return true;
1179}
1180
1181bool File::acquireMappingUse(bool executable, bool sharedWrite) {
1182 LockGuard<Mutex> guard(dataMutationLock());
1183 CacheState& state = cacheState();
1184 if ((executable && (sharedWrite || state.sharedWriteMappings)) ||
1185 (sharedWrite && state.executableMappings)) {
1186 SYSCALL_ERROR(TextFileBusy);
1187 return false;
1188 }
1189 if (executable)
1190 ++state.executableMappings;
1191 if (sharedWrite)
1192 ++state.sharedWriteMappings;
1193 return true;
1194}
1195
1196void File::releaseMappingUse(bool executable, bool sharedWrite) {
1197 LockGuard<Mutex> guard(dataMutationLock());
1198 CacheState& state = cacheState();
1199 if (executable) {
1200 assert(state.executableMappings);
1201 --state.executableMappings;
1202 }
1203 if (sharedWrite) {
1204 assert(state.sharedWriteMappings);
1205 --state.sharedWriteMappings;
1206 }
1207}
1208
1209bool File::allowResize(size_t, size_t) {
1210 return true;
1211}
1212
1213bool File::resize(size_t size) {
1214 OperationBarrier::Lease operation;
1215 if (!tryAcquireFilesystemOperation(operation)) {
1216 return false;
1217 }
1219 syscallError(isDirectory() ? Error::IsADirectory : Error::InvalidArgument);
1220 return false;
1221 }
1222 LockGuard<Mutex> writeGuard(writeSerializationLock());
1223#if !VFS_NOMMU
1225 MemoryMapManager::OperationGuard mappingGuard(mappings);
1226#endif
1227 LockGuard<Mutex> guard(dataMutationLock());
1228 if (m_pFilesystem && m_pFilesystem->isReadOnly()) {
1229 SYSCALL_ERROR(ReadOnlyFilesystem);
1230 return false;
1231 }
1232 if (cacheState().executableMappings) {
1233 SYSCALL_ERROR(TextFileBusy);
1234 return false;
1235 }
1236 const size_t oldSize = getSize();
1237 // Reject backing policy before retiring cache loans or changing mappings.
1238 if (!allowResize(oldSize, size)) {
1239 return false;
1240 }
1241 if (size == oldSize) {
1242 return resizeFile(size);
1243 }
1244 if (size < oldSize) {
1245 ShrinkContext context{oldSize, size, nullptr, 0};
1246 size_t mappingLoans = 0;
1247#if !VFS_NOMMU
1249 const auto mappingStatus = mappings.prepareFileResize(this, oldSize, size, mappingPlan);
1250 if (mappingStatus != MemoryMapManager::ResizeStatus::Ready) {
1251 syscallError(mappingStatus == MemoryMapManager::ResizeStatus::NoMemory ? Error::OutOfMemory
1252 : mappingStatus == MemoryMapManager::ResizeStatus::Invalid
1253 ? Error::InvalidArgument
1254 : Error::OperationNotSupported);
1255 return false;
1256 }
1257 context.mappingLoans = mappingPlan.get()->loans();
1258 context.mappingLoanCount = mappingPlan.get()->loanCount();
1259 mappingLoans = mappingPlan.get()->totalLoans();
1260#endif
1261 // Prefix mappings remain valid borrowers; other physical-page users cannot
1262 // be revoked by the mapping journal.
1263 if (__atomic_load_n(&physicalPageLoans(), __ATOMIC_ACQUIRE) != mappingLoans) {
1264 SYSCALL_ERROR(DeviceBusy);
1265 return false;
1266 }
1267 const size_t pageSize = PhysicalMemoryManager::getPageSize();
1268 const size_t boundaryOffset = size - size % pageSize;
1269 const size_t cutoff = boundaryOffset + (size % pageSize ? pageSize : 0);
1270 if (cutoff < size) {
1271 SYSCALL_ERROR(InvalidArgument);
1272 return false;
1273 }
1275 struct BoundaryPage {
1276 Cache& cache;
1277 size_t offset;
1278 uintptr_t address;
1279 ~BoundaryPage() {
1280 if (address)
1281 cache.release(offset);
1282 }
1283 } boundary{cacheState().fill, boundaryOffset, 0};
1284 if (useFillCache()) {
1285 const auto status = cacheState().fill.prepareDiscardFrom(cutoff, context.mappingLoans,
1286 context.mappingLoanCount, fillPlan);
1287 if (status != Cache::DiscardStatus::Ready) {
1288 syscallError(status == Cache::DiscardStatus::NoMemory ? Error::OutOfMemory
1289 : status == Cache::DiscardStatus::Busy ? Error::DeviceBusy
1290 : status == Cache::DiscardStatus::Invalid ? Error::InvalidArgument
1291 : Error::IoError);
1292 return false;
1293 }
1294 if (size % pageSize)
1295 boundary.address = cacheState().fill.lookup(boundaryOffset);
1296 }
1298 if (!prepareShrink(context, backend))
1299 return false;
1300#if !VFS_NOMMU
1301 mappingPlan.get()->commit();
1302#endif
1303 backend.get()->commit();
1304 if (fillPlan)
1305 fillPlan.get()->commit();
1306 if (boundary.address) {
1307 ByteSet(reinterpret_cast<void*>(boundary.address + size % pageSize), 0,
1308 pageSize - size % pageSize);
1309 cacheState().fill.markDirty(boundaryOffset);
1310 }
1311 // Native-block backends also cache borrowed addresses in this index.
1312 // Preserve prefix entries while invalidating every detached suffix alias.
1313 const size_t cacheBlockSize = useFillCache() ? pageSize : getBlockSize();
1314 const size_t firstDiscard = cutoff / cacheBlockSize;
1315 LockGuard<Mutex> indexGuard(cacheState().indexLock);
1316 for (auto it = cacheState().data.begin(); it != cacheState().data.end();) {
1317 if (it.__getNode()->key.hash() >= firstDiscard)
1318 it = cacheState().data.erase(it);
1319 else
1320 ++it;
1321 }
1322 } else if (!resizeFile(size)) {
1323 return false;
1324 }
1325 if (size > oldSize && useFillCache()) {
1326 const size_t pageSize = PhysicalMemoryManager::getPageSize();
1327 const size_t offset = oldSize % pageSize;
1328 if (offset) {
1329 const size_t pageOffset = oldSize - offset;
1330 const uintptr_t buffer = cacheState().fill.lookup(pageOffset);
1331 if (buffer) {
1332 const size_t amount =
1333 (size - oldSize < pageSize - offset) ? size - oldSize : pageSize - offset;
1334 ByteSet(reinterpret_cast<void*>(buffer + offset), 0, amount);
1335 cacheState().fill.markDirty(pageOffset);
1336 cacheState().fill.release(pageOffset);
1337 }
1338 }
1339 }
1340 Attributes attributes;
1341 attributes.modified = attributes.changed = Time::getTime();
1342 updateAttributes(attributes, ModifyTime | ChangeTime);
1343 publishEvent(FileEvents::Modify);
1344 return true;
1345}
1346
1347bool File::prepareShrink(const ShrinkContext&, UniquePointer<PreparedShrink>&) {
1348 SYSCALL_ERROR(OperationNotSupported);
1349 return false;
1350}
1351
1352bool File::resizeFile(size_t) {
1353 SYSCALL_ERROR(OperationNotSupported);
1354 return false;
1355}
1356
1358 return this;
1359}
1360
1364
1365bool File::isBytewise() const {
1366 return false;
1367}
1368
1369uint64_t File::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock) {
1370 if (isBytewise()) {
1371 FATAL("A bytewise File subclass didn't implement readBytewise");
1372 }
1373 return 0;
1374}
1375
1376uint64_t File::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock) {
1377 if (isBytewise()) {
1378 FATAL("A bytewise File subclass didn't implement writeBytewise");
1379 }
1380 return 0;
1381}
1382
1383uintptr_t File::readBlock(uint64_t location) {
1384 String fullPath;
1385 getFullPath(fullPath);
1386 ERROR("File: base class readBlock() called for " << fullPath);
1387 // only truly breaks on debug-enabled builds - in release builds this will
1388 // just cause an error in the caller
1389 assert(false);
1390 return 0;
1391}
1392
1393void File::writeBlock(uint64_t location, uintptr_t addr) {}
1394
1395void File::writeBlocks(uint64_t location, uintptr_t addr, size_t length) {
1396 const size_t blockSize = getBlockSize();
1397 if (!blockSize) {
1398 return;
1399 }
1400
1401 for (size_t offset = 0; offset < length; offset += blockSize) {
1402 writeBlock(location + offset, addr + offset);
1403 }
1404}
1405
1406void File::extend(size_t newSize) {
1407 if (m_Size < newSize)
1408 m_Size = newSize;
1409}
1410
1411void File::extend(size_t newSize, uint64_t location, uint64_t size) {
1412 extend(newSize);
1413}
1414
1415bool File::pinBlock(uint64_t location) {
1416 return false;
1417}
1418
1419uintptr_t File::acquireCachedBlock(uint64_t location, bool retryChanged) {
1420 const size_t block = location / getBlockSize();
1421 do {
1422 const uintptr_t address = getCachedPage(block);
1423 if (!address || address == FILE_BAD_BLOCK || !pinBlock(location))
1424 return 0;
1425 // A key can be replaced between the address snapshot and pinBlock().
1426 // Only return an address which still names the pinned page.
1427 if (getCachedPage(block) == address)
1428 return address;
1429 unpinBlock(location);
1430 } while (retryChanged);
1431 return 0;
1432}
1433
1434void File::unpinBlock(uint64_t location) {}
1435
1436void File::evict(uint64_t location) {
1437 setCachedPage(location / getBlockSize(), FILE_BAD_BLOCK);
1438}
1439
1440void File::setPermissionsOnly(uint32_t perms) {
1441 m_Permissions = perms;
1442}
1443
1444void File::setUidOnly(size_t uid) {
1445 m_Uid = uid;
1446}
1447
1448void File::setGidOnly(size_t gid) {
1449 m_Gid = gid;
1450}
1451
1453 EMIT_IF(THREADS) {
1454 bool bAny = false;
1455 {
1456 LockGuard<Mutex> guard(m_Lock);
1457
1458 for (List<MonitorTarget*>::Iterator it = m_MonitorTargets.begin();
1459 it != m_MonitorTargets.end(); it++) {
1460 MonitorTarget* pMT = *it;
1461
1462 pMT->pThread->sendEvent(pMT->pEvent);
1463 delete pMT;
1464
1465 bAny = true;
1466 }
1467
1468 m_MonitorTargets.clear();
1469 }
1470
1471 // If anything was waiting on a change, wake it up now.
1472 if (bAny) {
1474 }
1475 }
1476
1477 // Readiness observers only receive a change hint and re-query the source.
1478 // Keep callbacks outside the File lock and the legacy Event registry.
1479 notifyReadiness(ReadyAll);
1480}
1481
1482void File::monitor(Thread* pThread, Event* pEvent) {
1483 assert(pThread);
1484 assert(pEvent);
1485
1486 EMIT_IF(THREADS) {
1487 LockGuard<Mutex> guard(m_Lock);
1488 Event::SendLease registration;
1489 if (pEvent->tryAcquireRegistration(registration)) {
1490 m_MonitorTargets.pushBack(
1491 new MonitorTarget(pThread, pEvent, pedigree_std::move(registration)));
1492 }
1493 }
1494}
1495
1497 EMIT_IF(THREADS) {
1498 LockGuard<Mutex> guard(m_Lock);
1499
1500 for (List<MonitorTarget*>::Iterator it = m_MonitorTargets.begin();
1501 it != m_MonitorTargets.end();) {
1502 MonitorTarget* pMT = *it;
1503
1504 if (pMT->pThread == pThread) {
1505 delete pMT;
1506 it = m_MonitorTargets.erase(it);
1507 } else {
1508 ++it;
1509 }
1510 }
1511 }
1512}
1513
1515 EMIT_IF(THREADS) {
1516 LockGuard<Mutex> guard(m_Lock);
1517
1518 for (List<MonitorTarget*>::Iterator it = m_MonitorTargets.begin();
1519 it != m_MonitorTargets.end();) {
1520 MonitorTarget* pMT = *it;
1521 if (pMT->pEvent == pEvent) {
1522 delete pMT;
1523 it = m_MonitorTargets.erase(it);
1524 } else {
1525 ++it;
1526 }
1527 }
1528 }
1529}
1530
1532 s = m_pFilesystem->getVolumeLabel();
1533}
1534
1535void File::getFullPath(String& result, bool bWithMount) {
1536 HugeStaticString str;
1537 HugeStaticString tmp;
1538 str.clear();
1539 tmp.clear();
1540
1541 File* f = this;
1542 ParentLease current;
1543 while (f) {
1544 ParentLease parent;
1545 String name;
1546 f->getNamespace(parent, name);
1547 if (!parent.get()) {
1548 break;
1549 }
1550 tmp = str;
1551 str = name;
1552 if (tmp.length()) {
1553 str += "/";
1554 str += tmp;
1555 }
1556 current.swap(parent);
1557 f = current.get();
1558 }
1559
1560 tmp = str;
1561 str = "/";
1562 str += tmp;
1563
1564 if (bWithMount && m_pFilesystem) {
1565 String mountPath;
1566 if (VFS::instance().getMountPath(m_pFilesystem, mountPath) && mountPath != "/") {
1567 tmp = str;
1568 str = mountPath;
1569 if (tmp != "/") {
1570 str += tmp;
1571 }
1572 }
1573 } else if (bWithMount && !m_pFilesystem) {
1574 ERROR("File::getFullPath called without a filesystem!");
1575 }
1576
1577 result.assign(str, str.length());
1578}
1579
1580String File::getFullPath(bool bWithMount) {
1581 String path;
1582 getFullPath(path, bWithMount);
1583 return path;
1584}
1585
1586uintptr_t File::getCachedPage(size_t block, bool locked) {
1587 LockGuard<Mutex> guard(cacheState().indexLock, locked);
1588
1589 DataCacheKey key(block);
1590 auto result = cacheState().data.lookup(key);
1591 if (result.hasValue()) {
1592 return result.value();
1593 } else {
1594 return FILE_BAD_BLOCK;
1595 }
1596}
1597
1598void File::setCachedPage(size_t block, uintptr_t value, bool locked) {
1599 LockGuard<Mutex> guard(cacheState().indexLock, locked);
1600
1601 assert(value);
1602
1603 DataCacheKey key(block);
1604 if (value == FILE_BAD_BLOCK) {
1605 cacheState().data.remove(key);
1606 } else if (!cacheState().data.update(key, value)) {
1607 cacheState().data.insert(key, value);
1608 }
1609}
1610
1612 if (useFillCache()) {
1613 const size_t pageSize = PhysicalMemoryManager::getPageSize();
1614 cacheState().fill.markExternallyWritable(offset - offset % pageSize);
1615 }
1616}
1617
1618size_t File::populateRange(size_t offset, size_t length) {
1619 OperationBarrier::Lease operation;
1620 if (!tryAcquireFilesystemOperation(operation)) {
1621 return 0;
1622 }
1623 if (!length || isBytewise())
1624 return 0;
1625 if (!useFillCache() || m_bDirect) {
1626 const size_t pageSize = PhysicalMemoryManager::getPageSize();
1627 const size_t limit = MaxReadPages * pageSize - offset % pageSize;
1628 return read(offset, length < limit ? length : limit, 0);
1629 }
1630 LockGuard<Mutex> guard(dataMutationLock());
1631 return populateRangeLocked(offset, length);
1632}
1633
1634size_t File::populateRangeLocked(size_t offset, size_t length) {
1635 const size_t fileSize = getSize();
1636 const size_t pageSize = PhysicalMemoryManager::getPageSize();
1637 if (!length || offset >= fileSize || !pageSize)
1638 return 0;
1639 const size_t within = offset % pageSize;
1640 const size_t first = offset - within;
1641 const size_t limit = MaxReadPages * pageSize - within;
1642 if (length > fileSize - offset)
1643 length = fileSize - offset;
1644 if (length > limit)
1645 length = limit;
1646 LockGuard<Mutex> fillGuard(cacheState().fillLock);
1647 Cache& cache = cacheState().fill;
1648 const uintptr_t demand = cache.lookup(first);
1649 if (demand) {
1650 setCachedPage(first / pageSize, demand);
1651 cache.release(first);
1652 return length < pageSize - within ? length : pageSize - within;
1653 }
1654
1655 const size_t count = (within + length - 1) / pageSize + 1;
1656 ReadPage pages[MaxReadPages] = {};
1657 ReadPage pending[MaxReadPages] = {};
1658 size_t pendingSlots[MaxReadPages] = {};
1659 bool inserted[MaxReadPages] = {};
1660 size_t pendingCount = 0;
1661 for (size_t i = 0; i < count; ++i) {
1662 const size_t location = first + i * pageSize;
1663 pages[i].offset = location;
1664 pages[i].buffer = cache.lookup(location);
1665 if (pages[i].buffer) {
1666 pages[i].complete = true;
1667 continue;
1668 }
1669 bool existed = false;
1670 const uintptr_t fresh = cache.insert(location, &existed);
1671 if (!fresh)
1672 continue;
1673 // An Editing page's publication reference alone does not protect it from
1674 // pressure-driven eviction while the rest of this batch is reserved.
1675 pages[i].buffer = cache.lookup(location);
1676 if (!pages[i].buffer)
1677 continue;
1678 inserted[i] = !existed;
1679 if (existed) {
1680 pages[i].complete = true;
1681 continue;
1682 }
1683 pendingSlots[pendingCount] = i;
1684 pending[pendingCount++] = pages[i];
1685 }
1686 if (pendingCount) {
1687 const bool allRead = readPages(pending, pendingCount);
1688 (void)allRead;
1689 for (size_t i = 0; i < pendingCount; ++i)
1690 pages[pendingSlots[i]].complete = pending[i].complete;
1691 }
1692
1693 size_t readyPages = 0;
1694 while (readyPages < count && pages[readyPages].complete)
1695 ++readyPages;
1696 for (size_t i = 0; i < count; ++i) {
1697 if (!pages[i].buffer)
1698 continue;
1699 if (pages[i].complete) {
1700 if (inserted[i])
1701 cache.markNoLongerEditing(pages[i].offset);
1702 setCachedPage(pages[i].offset / pageSize, pages[i].buffer);
1703 }
1704 cache.release(pages[i].offset);
1705 if (inserted[i] && !pages[i].complete) {
1706 const bool discarded = cache.discardEditing(pages[i].offset);
1707 (void)discarded;
1708 }
1709 }
1710 if (!readyPages)
1711 return 0;
1712 const size_t available = readyPages * pageSize - within;
1713 return available < length ? available : length;
1714}
1715
1716bool File::readPages(ReadPage* pages, size_t count) {
1717 if (count > MaxReadPages || (count && !pages))
1718 return false;
1719 bool succeeded = true;
1720 for (size_t i = 0; i < count; ++i) {
1721 pages[i].complete = readPage(pages[i].offset, pages[i].buffer);
1722 succeeded = pages[i].complete && succeeded;
1723 }
1724 return succeeded;
1725}
1726
1727bool File::readPage(uint64_t location, uintptr_t destination) {
1728 const size_t pageSize = PhysicalMemoryManager::getPageSize();
1729 const size_t blockSize = getBlockSize();
1730 if (!blockSize || pageSize % blockSize)
1731 return false;
1732 ByteSet(reinterpret_cast<void*>(destination), 0, pageSize);
1733 const size_t size = getSize();
1734 for (size_t i = 0; i < pageSize && location < size && i < size - location; i += blockSize) {
1735 const uintptr_t block = readBlock(location + i);
1736 if (!block || block == FILE_BAD_BLOCK)
1737 return false;
1738 const size_t remaining = size - location - i;
1739 ForwardMemoryCopy(reinterpret_cast<void*>(destination + i), reinterpret_cast<void*>(block),
1740 remaining < blockSize ? remaining : blockSize);
1741 unpinBlock(location + i);
1742 }
1743 return true;
1744}
1745
1747 EMIT_IF(VFS_NOMMU) {
1748#if defined(PEDIGREE_BUILDUTILS)
1749 if (m_bForceFillCache) {
1751 }
1752#endif
1753 // No fill cache in NOMMU builds.
1754 return false;
1755 }
1756 else {
1757 size_t blockSize = getBlockSize();
1758 size_t nativeBlockSize = PhysicalMemoryManager::getPageSize();
1759 return blockSize < nativeBlockSize;
1760 }
1761}
1762
1764 cacheState().fill.setCallback(fillCacheCallback, this);
1765}
1766
1767void File::shutdownFillCacheWriteback() {
1768 cacheState().fill.shutdown();
1769}
1770
1771bool File::syncFillCache(size_t offset, bool async, bool& present) {
1772 OperationBarrier::Lease operation;
1773 if (!tryAcquireFilesystemOperation(operation)) {
1774 present = false;
1775 return false;
1776 }
1777 const size_t pageSize = PhysicalMemoryManager::getPageSize();
1778 const size_t pageOffset = offset - (offset % pageSize);
1779 LockGuard<Mutex> guard(cacheState().fillLock);
1780 present = cacheState().fill.lookup(pageOffset) != 0;
1781 if (!present) {
1782 return true;
1783 }
1784
1785 const bool succeeded = cacheState().fill.sync(pageOffset, async);
1786 cacheState().fill.release(pageOffset);
1787 return succeeded;
1788}
1789
1790uintptr_t File::readIntoCache(uintptr_t block, bool overwriteWholePage, size_t readAheadBytes) {
1791 size_t blockSize = getBlockSize();
1792 size_t nativeBlockSize = PhysicalMemoryManager::getPageSize();
1793 const bool fillCache = useFillCache();
1794
1795 const size_t offset = block * (fillCache ? nativeBlockSize : blockSize);
1796
1797 if (fillCache) {
1798 LockGuard<Mutex> fillGuard(cacheState().fillLock);
1799
1800 // Resident reads must not enter insertion's memory-pressure eviction path.
1801 if (!m_bDirect) {
1802 const uintptr_t cached = cacheState().fill.lookup(offset);
1803 if (cached) {
1804 setCachedPage(block, cached);
1805 return cached;
1806 }
1807 if (readAheadBytes > nativeBlockSize) {
1808 // Population takes fillLock itself; the caller retains dataMutationLock
1809 // across this handoff and the final acquisition of the demand page.
1810 cacheState().fillLock.release();
1811 fillGuard.disown();
1812 if (!populateRangeLocked(offset, readAheadBytes))
1813 return FILE_BAD_BLOCK;
1814 return readIntoCache(block);
1815 }
1816 }
1817
1818 // Using Cache::insert() here is atomic compared to if we did a
1819 // lookup() followed by an insert() - means we don't need to lock the
1820 // File object to do this.
1821 bool didExist = false;
1822 uintptr_t vaddr = cacheState().fill.insert(offset, nativeBlockSize, &didExist);
1823 if (!vaddr) {
1824 return FILE_BAD_BLOCK;
1825 }
1826
1827 // If in direct mode we are required to read() again
1828 bool existingReference = false;
1829 if (didExist) {
1830 vaddr = cacheState().fill.lookup(offset);
1831 if (!vaddr) {
1832 return FILE_BAD_BLOCK;
1833 }
1834 if (!m_bDirect) {
1835 setCachedPage(block, vaddr);
1836 return vaddr;
1837 }
1838 existingReference = true;
1839 // A fresh backend read must not overwrite deferred writes in this page.
1840 if (!cacheState().fill.sync(offset, false)) {
1841 cacheState().fill.release(offset);
1842 return FILE_BAD_BLOCK;
1843 }
1844 }
1845
1846 // The write lock excludes readers until the caller replaces the whole page.
1847 // Initialising it still gives legacy checksum owners a defined clean image.
1848 if (overwriteWholePage) {
1849 ByteSet(reinterpret_cast<void*>(vaddr), 0, nativeBlockSize);
1850 } else if (!readPage(offset, vaddr)) {
1851 if (existingReference) {
1852 cacheState().fill.release(offset);
1853 }
1854 if (!didExist && !cacheState().fill.discardEditing(offset)) {
1855 WARNING("File::readIntoCache could not discard a failed fill for offset " << offset);
1856 }
1857 return FILE_BAD_BLOCK;
1858 }
1859
1860 cacheState().fill.markNoLongerEditing(offset, nativeBlockSize);
1861
1862 if (existingReference) {
1863 return vaddr;
1864 }
1865
1866 vaddr = cacheState().fill.lookup(offset);
1867 if (vaddr) {
1868 setCachedPage(block, vaddr);
1869 }
1870 return vaddr ? vaddr : FILE_BAD_BLOCK;
1871 }
1872
1873 if (!m_bDirect) {
1874 const uintptr_t cached = acquireCachedBlock(offset, true);
1875 if (cached)
1876 return cached;
1877 }
1878 const uintptr_t buff = readBlock(offset);
1879 if (!buff) {
1880 ERROR("File::readIntoCache - bad read (" << (block * blockSize) << " - block size is "
1881 << blockSize << ")");
1882 return FILE_BAD_BLOCK;
1883 }
1884
1885 if (!m_bDirect) {
1886 setCachedPage(block, buff);
1887 }
1888
1889 return buff;
1890}
1891
1892void File::releaseReadReference(uintptr_t block) {
1893 if (useFillCache()) {
1894 cacheState().fill.release(block * PhysicalMemoryManager::getPageSize());
1895 } else {
1896 unpinBlock(block * getBlockSize());
1897 }
1898}
Memory-mapped file interface.
Definition Cache.h:207
size_t read(uintptr_t offset, size_t length, uintptr_t buffer, bool(*prepare)(uintptr_t, size_t)=nullptr)
Definition Cache.cc:769
bool shutdown(ShutdownMode mode=ShutdownMode::WriteBack)
Definition Cache.cc:663
void setCallback(writeback_t newCallback, void *meta)
Definition Cache.cc:2390
void release(uintptr_t key)
Definition Cache.cc:1570
uintptr_t insert(uintptr_t key, bool *alreadyExisted=nullptr)
Definition Cache.cc:890
DiscardStatus prepareDiscardFrom(uintptr_t cutoff, const DiscardReference *references, size_t count, UniquePointer< PreparedDiscard > &result)
void markExternallyWritable(uintptr_t key)
Definition Cache.cc:2146
void markDirty(uintptr_t key)
Definition Cache.cc:2135
bool sync(uintptr_t key, bool async)
Definition Cache.cc:1627
void markNoLongerEditing(uintptr_t key, size_t length=0)
Definition Cache.cc:2646
uintptr_t lookup(uintptr_t key)
Definition Cache.cc:737
Definition Event.h:49
bool tryAcquireRegistration(SendLease &registration)
Definition Event.cc:392
void notifyFileEvent(const FileEvent &event)
Definition FileEvent.cc:289
void notifyFinalFileEvent(const FileEvent &event)
Definition FileEvent.cc:304
static bool anyFileEventObservers()
Definition FileEvent.cc:296
Definition File.h:75
virtual XattrStatus getExtendedAttribute(const StringView &name, void *buffer, size_t capacity, size_t &required)
Definition File.cc:1107
virtual void getFullPath(String &result, bool bWithMount=true)
Definition File.cc:1535
WriteGuard lockWrites()
Definition File.cc:420
virtual uintptr_t readBlock(uint64_t location)
Definition File.cc:1383
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition File.cc:1369
size_t readCached(uint64_t location, size_t size, uintptr_t buffer, bool(*prepare)(uintptr_t, size_t)=nullptr)
Definition File.cc:319
File * m_pDetachedParent
Definition File.h:631
bool resize(size_t size)
Definition File.cc:1213
void monitor(Thread *pThread, Event *pEvent)
Definition File.cc:1482
virtual bool retainVfsReference()
Definition File.cc:910
virtual bool isSeekable() const
Definition File.cc:820
virtual uint64_t read(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
Definition File.cc:239
virtual void unpinBlock(uint64_t location)
Definition File.cc:1434
virtual int command(const size_t command, void *buffer)
Definition File.cc:1073
Time::Timestamp getCreationTime()
Definition File.cc:721
void markPageExternallyWritable(size_t offset)
Definition File.cc:1611
virtual bool syncPages(const uint64_t *offsets, size_t count)
Definition File.cc:688
uint64_t writeUnlocked(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock)
Definition File.cc:347
virtual void extend(size_t newSize)
Definition File.cc:1406
bool supportsRegularFileOperations()
Definition File.cc:824
size_t populateRange(size_t offset, size_t length)
Definition File.cc:1618
String getName() const
Definition File.cc:782
static bool writeCallback(CacheConstants::CallbackCause cause, uintptr_t loc, uintptr_t page, void *meta)
Definition File.cc:116
void cullMonitorTargets(Thread *pThread)
Definition File.cc:1496
uint64_t append(uint64_t size, uintptr_t buffer, uint64_t &location, bool bCanBlock=true)
Definition File.cc:415
virtual void releaseVfsReference()
Definition File.cc:914
virtual bool tryBeginMappingRelease()
Definition File.cc:587
void getNamespace(ParentLease &parent, String &name) const
Definition File.cc:992
uintptr_t getCachedPage(size_t block, bool locked=true)
Definition File.cc:1586
virtual uint64_t write(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
Definition File.cc:342
void setCreationTime(Time::Timestamp t)
Definition File.cc:725
virtual void writeBlocks(uint64_t location, uintptr_t addr, size_t length)
Definition File.cc:1395
bool clearDataCache()
Definition File.cc:1167
bool syncRange(size_t offset, size_t length)
Definition File.cc:599
virtual bool isSocket() const
Definition File.cc:816
virtual bool isSymlink()
Definition File.cc:800
virtual void preallocate(size_t expectedSize, bool zero=true)
Definition File.cc:1089
void retainDetachedParent()
Definition File.cc:1025
virtual bool isStableVfsRoot() const
Definition File.cc:918
virtual bool isBytewise() const
Definition File.cc:1365
Time::Timestamp getAccessedTime()
Definition File.cc:736
void evict(uint64_t location)
Definition File.cc:1436
void disableDirect()
Definition File.cc:1085
virtual bool isDirectory()
Definition File.cc:804
virtual bool readPage(uint64_t location, uintptr_t destination)
Definition File.cc:1727
void setModifiedTime(Time::Timestamp t)
Definition File.cc:771
void setGidOnly(size_t gid)
Definition File.cc:1448
Time::Timestamp getModifiedTime()
Definition File.cc:767
virtual uintptr_t futexIdentity()
Definition File.cc:75
virtual physical_uintptr_t getPhysicalPage(size_t offset)
Definition File.cc:486
virtual bool sync()
Definition File.cc:595
void publishEvent(FileEventMask mask, const StringView &name=StringView(), bool targetIsDirectory=false)
Definition File.cc:861
void setPermissionsOnly(uint32_t perms)
Definition File.cc:1440
Mutex m_WriteLock
Definition File.h:680
void releaseReadReference(uintptr_t block)
Definition File.cc:1892
virtual bool isFifo() const
Definition File.cc:812
virtual bool isPipe() const
Definition File.cc:808
virtual bool readPages(ReadPage *pages, size_t count)
Definition File.cc:1716
virtual bool useFillCache() const
Definition File.cc:1746
void enableDirect()
Definition File.cc:1081
virtual MUST_USE_RESULT uintptr_t acquireCachedBlock(uint64_t location, bool retryChanged)
Definition File.cc:1419
bool syncAndReturnPhysicalPage(size_t offset, bool async)
Definition File.cc:581
void setAccessedTime(Time::Timestamp t)
Definition File.cc:740
void setUidOnly(size_t uid)
Definition File.cc:1444
virtual void truncate()
Definition File.cc:1153
virtual uint64_t maximumFileSize() const
Definition File.cc:829
bool syncFillCache(size_t offset, bool async, bool &present)
Definition File.cc:1771
virtual ReadyMask queryReady(bool reading, bool writing)
Definition File.cc:1054
void enableFillCacheWriteback()
Definition File.cc:1763
virtual bool supports(const size_t command) const
Definition File.cc:1069
bool acquireMappingUse(bool executable, bool sharedWrite)
Definition File.cc:1181
virtual ~File()
Definition File.cc:221
void setCachedPage(size_t block, uintptr_t value, bool locked=true)
Definition File.cc:1598
void dataChanged()
Definition File.cc:1452
virtual bool allowMapping(bool shared, bool writeRequested, bool &mayWrite)
Definition File.cc:1177
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition File.cc:1376
virtual bool prepareSharedMapping(size_t offset, size_t length)
Definition File.cc:1143
virtual size_t getBlockSize() const
Definition File.cc:1077
virtual void returnPhysicalPage(size_t offset)
Definition File.cc:557
void getFilesystemLabel(HugeStaticString &s)
Definition File.cc:1531
File()
Definition File.cc:165
virtual int select(bool bWriting=false, int timeout=0)
Definition File.cc:1050
virtual File * openForDescriptor(RetainedFile &owner)
Definition File.cc:1361
virtual bool supportsReadinessNotifications() const
Definition File.cc:1065
virtual File * open()
Definition File.cc:1357
uintptr_t readIntoCache(uintptr_t block, bool overwriteWholePage=false, size_t readAheadBytes=0)
Definition File.cc:1790
virtual MUST_USE_RESULT bool pinBlock(uint64_t location)
Definition File.cc:1415
virtual void writeBlock(uint64_t location, uintptr_t addr)
Definition File.cc:1393
virtual const String & getVolumeLabel() const =0
bool isReadOnly()
Definition Filesystem.h:150
virtual File * getRoot() const =0
Definition Group.h:32
::Iterator< T, node_t > Iterator
Definition List.h:67
void disown()
Definition LockGuard.h:69
ResizeStatus prepareFileResize(File *file, size_t oldSize, size_t newSize, UniquePointer< PreparedFileResize > &result)
static MemoryMapManager & instance()
Definition Mutex.h:56
size_t getId()
Definition Process.h:499
static ProcessorInformation & information()
void notifyReadiness(ReadyMask mask)
Definition Readiness.cc:201
void closeReadiness(ReadyMask mask=ReadyInvalid|ReadyHangup)
Definition Readiness.cc:208
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:236
void release(size_t n=1)
Definition Semaphore.cc:549
bool tryAcquire(size_t n=1)
Definition Semaphore.cc:484
StringView view() const
Definition String.cc:783
Process * getParent() const
Definition Thread.h:340
bool sendEvent(Event *pEvent)
Definition Thread.cc:1115
MUST_USE_RESULT bool retainTrackedFile(File *pFile)
Definition VFS.cc:1640
bool untrackFile(File *pFile, bool destroy=true)
Definition VFS.cc:1670
static VFS & instance()
Definition VFS.cc:311
A vector / dynamic array.
Definition Vector.h:33
virtual bool isMapped(void *virtualAddress)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
void pushBack(const T &value)
Definition Vector.h:275
size_t count() const
Definition Vector.h:270