The Pedigree Project 0.1
RamFs.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 "RamFs.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/process/Process.h"
23#include "pedigree/kernel/process/TerminationDeferral.h"
24#include "pedigree/kernel/process/Thread.h"
25#include "pedigree/kernel/processor/Processor.h"
26#include "pedigree/kernel/processor/ProcessorInformation.h"
27#include "pedigree/kernel/syscallError.h"
28#include "pedigree/kernel/utilities/new"
29
30#include "modules/Module.h"
31#include "modules/system/vfs/Symlink.h"
32
33String RamFs::m_VolumeLabel("ramfs");
34
35namespace {
36class RamSymlink final : public Symlink {
37 public:
38 RamSymlink(const String& name, uintptr_t inode, RamFs& filesystem, File* parent,
39 const String& target)
40 : Symlink(name, 0, 0, 0, inode, &filesystem, target.length(), parent), m_OwnerPid(0) {
41 // A stored target is immutable and already loaded; generic link following
42 // must never reload and trim its trailing pathname characters.
43 m_sTarget = target;
44#if THREADS
45 m_OwnerPid = Processor::information().getCurrentThread()->getParent()->getId();
46#endif
47 }
48
49 bool canWrite() {
50 if (!static_cast<RamFs*>(getFilesystem())->getProcessOwnership())
51 return true;
52#if THREADS
53 return Processor::information().getCurrentThread()->getParent()->getId() == m_OwnerPid;
54#else
55 return true;
56#endif
57 }
58
59 protected:
60 uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool) override {
61 if (location >= m_sTarget.length())
62 return 0;
63 size_t amount = m_sTarget.length() - location;
64 if (amount > size)
65 amount = size;
66 MemoryCopy(reinterpret_cast<void*>(buffer), m_sTarget.cstr() + location, amount);
67 return amount;
68 }
69
70 private:
71 size_t m_OwnerPid;
72};
73
74bool canModifyRamNode(File* file) {
75 if (file->isDirectory())
76 return true;
77 if (file->isSymlink())
78 return static_cast<RamSymlink*>(file)->canWrite();
79 return static_cast<RamFile*>(file)->canWrite();
80}
81
82void initialiseCreatedNode(File& file, uint32_t mode) {
83 // Creation attributes must be complete before another process can find the node.
84 // VFS permissions place owner rights in the low bits, unlike Unix modes.
85 uint32_t permissions = mode & FILE_AMASK;
86 if (mode & 0400)
87 permissions |= FILE_UR;
88 if (mode & 0200)
89 permissions |= FILE_UW;
90 if (mode & 0100)
91 permissions |= FILE_UX;
92 if (mode & 0040)
93 permissions |= FILE_GR;
94 if (mode & 0020)
95 permissions |= FILE_GW;
96 if (mode & 0010)
97 permissions |= FILE_GX;
98 if (mode & 0004)
99 permissions |= FILE_OR;
100 if (mode & 0002)
101 permissions |= FILE_OW;
102 if (mode & 0001)
103 permissions |= FILE_OX;
104 file.setPermissions(permissions);
105#if THREADS
106 Thread* thread = Processor::information().getCurrentThread();
107 if (thread) {
108 FilesystemCredentials credentials;
109 if (Process::currentFilesystemCredentials(credentials)) {
110 file.setUid(credentials.uid);
111 file.setGid(credentials.gid);
112 }
113 }
114#endif
115}
116} // namespace
117
118RamFile::RamFile(const String& name, uintptr_t inode, Filesystem* pParentFS, File* pParent)
119 : File(name, 0, 0, 0, inode, pParentFS, 0, pParent),
120 m_FileBlocks(),
121 m_FileBlocksLock(),
122 m_nOwnerPid(0),
123 m_LinkCount(1) {
124 // Full permissions.
125 setPermissions(0777);
126
127#if THREADS
128 m_nOwnerPid = Processor::information().getCurrentThread()->getParent()->getId();
129#else
130 m_nOwnerPid = 0;
131#endif
132}
133
134RamFile::~RamFile() {
135 truncate();
136}
137
138File::Attributes RamFile::getAttributes() const {
139 LockGuard<Mutex> guard(m_FileBlocksLock);
140 Attributes attributes = File::getAttributes();
141 attributes.links = m_LinkCount;
142 attributes.blocks = static_cast<uint64_t>(m_BlockOffsets.count()) * (getBlockSize() / 512);
143 return attributes;
144}
145
146bool RamFile::addLink() {
147 for (;;) {
148 size_t count = m_LinkCount;
149 if (count == ~static_cast<size_t>(0)) {
150 SYSCALL_ERROR(TooManyLinks);
151 return false;
152 }
153 if (m_LinkCount.compareAndSwap(count, count + 1)) {
154 return true;
155 }
156 }
157}
158
159void RamFile::removeLink() {
160 for (;;) {
161 size_t count = m_LinkCount;
162 assert(count);
163 if (m_LinkCount.compareAndSwap(count, count - 1)) {
164 return;
165 }
166 }
167}
168
169XattrStatus RamFile::getExtendedAttribute(const StringView& name, void* buffer, size_t capacity,
170 size_t& required) {
171 return m_ExtendedAttributes.get(name, buffer, capacity, required);
172}
173
174XattrStatus RamFile::listExtendedAttributes(void* buffer, size_t capacity, size_t& required) {
175 return m_ExtendedAttributes.list(buffer, capacity, required);
176}
177
178XattrStatus RamFile::setExtendedAttribute(const StringView& name, const void* value, size_t length,
179 unsigned flags) {
180 TerminationDeferral lifetime;
181 if (!canWrite())
182 return XattrStatus::Denied;
183 const auto status = m_ExtendedAttributes.set(name, value, length, flags);
184 if (status == XattrStatus::Success)
185 setCreationTime(Time::getTime());
186 return status;
187}
188
189XattrStatus RamFile::removeExtendedAttribute(const StringView& name) {
190 TerminationDeferral lifetime;
191 if (!canWrite())
192 return XattrStatus::Denied;
193 const auto status = m_ExtendedAttributes.remove(name);
194 if (status == XattrStatus::Success)
195 setCreationTime(Time::getTime());
196 return status;
197}
198
200 resize(0);
201}
202
204 public:
205 ShrinkPlan(RamFile& file, size_t size)
206 : file(file), size(size), boundary(size - size % file.getBlockSize()), tail(0) {}
207 ~ShrinkPlan() override {
208 if (tail)
209 file.m_FileBlocks.release(boundary);
210 }
211 void commit() override {
212 LockGuard<Mutex> guard(file.m_FileBlocksLock);
213 discarded.get()->commit();
214 const size_t cutoff = boundary + (size % file.getBlockSize() ? file.getBlockSize() : 0);
215 for (size_t i = 0; i < file.m_BlockOffsets.count();) {
216 if (file.m_BlockOffsets[i] >= cutoff) {
217 file.m_BlockOffsets[i] = file.m_BlockOffsets[file.m_BlockOffsets.count() - 1];
218 file.m_BlockOffsets.popBack();
219 } else {
220 ++i;
221 }
222 }
223 if (tail)
224 ByteSet(reinterpret_cast<void*>(tail + size - boundary), 0,
225 file.getBlockSize() - (size - boundary));
226 file.setSize(size);
227 }
228 RamFile& file;
229 size_t size;
230 size_t boundary;
231 uintptr_t tail;
233};
234
235bool RamFile::prepareShrink(const ShrinkContext& context, UniquePointer<PreparedShrink>& prepared) {
236 if (!canWrite()) {
237 SYSCALL_ERROR(PermissionDenied);
238 return false;
239 }
240 ShrinkPlan* plan = new ShrinkPlan(*this, context.newSize);
242 if (!plan) {
243 SYSCALL_ERROR(OutOfMemory);
244 return false;
245 }
246 LockGuard<Mutex> guard(m_FileBlocksLock);
247 const size_t blockSize = getBlockSize();
248 const size_t cutoff = plan->boundary + (context.newSize % blockSize ? blockSize : 0);
249 const auto status = m_FileBlocks.prepareDiscardFrom(cutoff, context.mappingLoans,
250 context.mappingLoanCount, plan->discarded);
251 if (status != Cache::DiscardStatus::Ready) {
252 syscallError(status == Cache::DiscardStatus::NoMemory ? Error::OutOfMemory
253 : status == Cache::DiscardStatus::Busy ? Error::DeviceBusy
254 : status == Cache::DiscardStatus::Invalid ? Error::InvalidArgument
255 : Error::IoError);
256 return false;
257 }
258 if (context.newSize % blockSize)
259 plan->tail = m_FileBlocks.lookup(plan->boundary);
260 // The cache plan retains its rollback state until the generic mapping journal
261 // has returned the suffix loans.
262 prepared = pedigree_std::move(owner);
263 return true;
264}
265
266bool RamFile::resizeFile(size_t size) {
267 if (!canWrite()) {
268 SYSCALL_ERROR(PermissionDenied);
269 return false;
270 }
271 LockGuard<Mutex> guard(m_FileBlocksLock);
272 const size_t oldSize = getSize();
273 const size_t blockSize = getBlockSize();
274 for (size_t i = 0; i < m_BlockOffsets.count();) {
275 const uint64_t offset = m_BlockOffsets[i];
276 const uintptr_t buffer = m_FileBlocks.lookup(offset);
277 if (size <= oldSize && offset >= size) {
278 if (buffer) {
279 m_FileBlocks.release(offset);
280 m_FileBlocks.release(offset);
281 m_FileBlocks.evict(offset);
282 }
283 m_BlockOffsets[i] = m_BlockOffsets[m_BlockOffsets.count() - 1];
284 m_BlockOffsets.popBack();
285 continue;
286 }
287 const size_t boundary = size < oldSize ? size : oldSize;
288 if (buffer) {
289 if (offset <= boundary && boundary - offset < blockSize) {
290 const size_t within = boundary - offset;
291 ByteSet(reinterpret_cast<void*>(buffer + within), 0, blockSize - within);
292 }
293 m_FileBlocks.release(offset);
294 }
295 ++i;
296 }
297 setSize(size);
298 return true;
299}
300
301bool RamFile::canWrite() {
302 RamFs* pParent = static_cast<RamFs*>(getFilesystem());
303 if (!pParent->getProcessOwnership()) {
304 return true;
305 }
306
307#if THREADS
308 size_t pid = Processor::information().getCurrentThread()->getParent()->getId();
309 return pid == m_nOwnerPid;
310#else
311 return true;
312#endif
313}
314
315uintptr_t RamFile::readBlock(uint64_t location) {
316 LockGuard<Mutex> guard(m_FileBlocksLock);
317 uintptr_t buffer = m_FileBlocks.lookup(location);
318 if (!buffer) {
319 // Super trivial. But we are a ram filesystem... can't compact.
320 bool didExist = false;
321 buffer = m_FileBlocks.insert(location, &didExist);
322 if (!buffer) {
323 return 0;
324 }
325 if (!didExist) {
326 ByteSet(reinterpret_cast<void*>(buffer), 0, getBlockSize());
327 m_BlockOffsets.pushBack(location);
328 m_FileBlocks.markNoLongerEditing(location);
329 }
330 buffer = m_FileBlocks.lookup(location);
331 }
332 return buffer;
333}
334
335bool RamFile::pinBlock(uint64_t location) {
336 return m_FileBlocks.pin(location);
337}
338
339uintptr_t RamFile::acquireCachedBlock(uint64_t location, bool) {
340 return m_FileBlocks.lookup(location);
341}
342
343void RamFile::unpinBlock(uint64_t location) {
344 m_FileBlocks.release(location);
345}
346
347RamDir::RamDir(const String& name, size_t inode, class Filesystem* pFs, File* pParent)
348 : Directory(name, 0, 0, 0, inode, pFs, 0, pParent), m_DirectoryLock() {
349 // Full permissions.
350 setPermissions(0777);
351}
352
353RamDir::~RamDir() {};
354
355XattrStatus RamDir::getExtendedAttribute(const StringView& name, void* buffer, size_t capacity,
356 size_t& required) {
357 return m_ExtendedAttributes.get(name, buffer, capacity, required);
358}
359
360XattrStatus RamDir::listExtendedAttributes(void* buffer, size_t capacity, size_t& required) {
361 return m_ExtendedAttributes.list(buffer, capacity, required);
362}
363
364XattrStatus RamDir::setExtendedAttribute(const StringView& name, const void* value, size_t length,
365 unsigned flags) {
366 TerminationDeferral lifetime;
367 const auto status = m_ExtendedAttributes.set(name, value, length, flags);
368 if (status == XattrStatus::Success)
369 setCreationTime(Time::getTime());
370 return status;
371}
372
373XattrStatus RamDir::removeExtendedAttribute(const StringView& name) {
374 TerminationDeferral lifetime;
375 const auto status = m_ExtendedAttributes.remove(name);
376 if (status == XattrStatus::Success)
377 setCreationTime(Time::getTime());
378 return status;
379}
380
381bool RamDir::addEntry(String filename, File* pFile) {
382 return addDirectoryEntry(filename, pFile);
383}
384
385bool RamDir::removeEntry(const String& filename, File* pFile) {
386 LockGuard<Mutex> guard(m_DirectoryLock);
387 if (!canModifyRamNode(pFile)) {
388 SYSCALL_ERROR(PermissionDenied);
389 return false;
390 }
391
392 return removeDirectoryEntry(filename, pFile);
393}
394
395bool RamDir::removeFromParent(RamDir* parent, const String& filename) {
396 if (parent == this) {
397 SYSCALL_ERROR(InvalidArgument);
398 return false;
399 }
400 LockGuard<Mutex> namespaceGuard(namespaceMutationLock());
401 LockGuard<Mutex> guard(m_DirectoryLock);
402 bool empty = false;
403 if (isEmpty(empty) != ReadStatus::Complete) {
404 SYSCALL_ERROR(IoError);
405 return false;
406 }
407 if (!empty) {
408 SYSCALL_ERROR(NotEmpty);
409 return false;
410 }
411 if (!parent->removeEntry(filename, this))
412 return false;
413 markDetached();
414 return true;
415}
416
417RamFs::RamFs() : m_pRoot(0), m_bProcessOwners(false), m_NextInode(0) {}
418
419RamFs::~RamFs() {
420 if (m_pRoot)
421 delete m_pRoot;
422}
423
424Filesystem::SyncStatus RamFs::sync() {
425 // Files and shared mappings already modify the authoritative memory pages.
426 return SyncStatus::Success;
427}
428
429uintptr_t RamFs::allocateInode() {
430 for (;;) {
431 uintptr_t previous = m_NextInode;
432 if (previous == ~static_cast<uintptr_t>(0)) {
433 SYSCALL_ERROR(NoSpaceLeftOnDevice);
434 return 0;
435 }
436 if (m_NextInode.compareAndSwap(previous, previous + 1))
437 return previous + 1;
438 }
439}
440
442 uintptr_t inode = allocateInode();
443 if (!inode)
444 return false;
445 // Root directory with ./.. entries
446 m_pRoot = new RamDir(String(""), inode, this, 0);
447 return m_pRoot != nullptr;
448}
449
450bool RamFs::createFile(File* parent, const String& filename, uint32_t mask) {
451 if (!parent->isDirectory()) {
452 SYSCALL_ERROR(NotADirectory);
453 return false;
454 }
455
456 uintptr_t inode = allocateInode();
457 if (!inode)
458 return false;
459 File* f = new RamFile(filename, inode, this, parent);
460 if (!f) {
461 SYSCALL_ERROR(OutOfMemory);
462 return false;
463 }
464 initialiseCreatedNode(*f, mask);
465
466 RamDir* p = static_cast<RamDir*>(parent);
467 if (!p->addEntry(filename, f)) {
468 delete f;
469 return false;
470 }
471 return true;
472}
473
474bool RamFs::createDirectory(File* parent, const String& filename, uint32_t mask) {
475 if (!parent->isDirectory()) {
476 SYSCALL_ERROR(NotADirectory);
477 return false;
478 }
479
480 uintptr_t inode = allocateInode();
481 if (!inode)
482 return false;
483 RamDir* pDir = new RamDir(filename, inode, this, parent);
484 if (!pDir) {
485 SYSCALL_ERROR(OutOfMemory);
486 return false;
487 }
488 initialiseCreatedNode(*pDir, mask);
489
490 RamDir* pParent = static_cast<RamDir*>(parent);
491 if (!pParent->addEntry(filename, pDir)) {
492 delete pDir;
493 return false;
494 }
495 return true;
496}
497
498bool RamFs::createSymlink(File* parent, const String& filename, const String& value) {
499 if (!parent->isDirectory()) {
500 SYSCALL_ERROR(NotADirectory);
501 return false;
502 }
503 if (!value.length()) {
504 SYSCALL_ERROR(DoesNotExist);
505 return false;
506 }
507 uintptr_t inode = allocateInode();
508 if (!inode)
509 return false;
510 auto* link = new RamSymlink(filename, inode, *this, parent, value);
511 if (!link) {
512 SYSCALL_ERROR(OutOfMemory);
513 return false;
514 }
515 initialiseCreatedNode(*link, 0777);
516 if (!static_cast<RamDir*>(parent)->addEntry(filename, link)) {
517 delete link;
518 return false;
519 }
520 return true;
521}
522
523bool RamFs::createLink(File* parent, const String& filename, File* target) {
524 if (!parent->isDirectory()) {
525 SYSCALL_ERROR(NotADirectory);
526 return false;
527 }
528 if (target->isDirectory()) {
529 SYSCALL_ERROR(PermissionDenied);
530 return false;
531 }
532 if (target->isSymlink() || target->getFilesystem() != this) {
533 SYSCALL_ERROR(OperationNotSupported);
534 return false;
535 }
536
537 auto* file = static_cast<RamFile*>(target);
538 if (!file->addLink()) {
539 return false;
540 }
541 if (!static_cast<RamDir*>(parent)->addEntry(filename, target)) {
542 file->removeLink();
543 return false;
544 }
545 file->setCreationTime(Time::getTime());
546 return true;
547}
548
549bool RamFs::removeNode(File* parent, const String& filename, File* file) {
550 RamDir* p = static_cast<RamDir*>(parent);
551 if (file->isDirectory()) {
552 return static_cast<RamDir*>(file)->removeFromParent(p, filename);
553 }
554 if (!p->removeEntry(filename, file)) {
555 return false;
556 }
557 if (!file->isSymlink()) {
558 static_cast<RamFile*>(file)->removeLink();
559 file->setCreationTime(Time::getTime());
560 }
561 return true;
562}
563
564bool RamFs::renameNode(Directory*, const String&, File* source, Directory*, const String&,
565 File* replaced) {
566 if (m_bProcessOwners &&
567 (!canModifyRamNode(source) || (replaced && !canModifyRamNode(replaced)))) {
568 SYSCALL_ERROR(PermissionDenied);
569 return false;
570 }
571 return true;
572}
573
574static bool entry() {
575 return true;
576}
577
578static void destroy() {}
579
580MODULE_INFO_NON_UNLOADABLE("ramfs", &entry, &destroy, "vfs");
An in-RAM filesystem.
Definition Cache.h:207
bool evict(uintptr_t key)
Definition Cache.cc:1122
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 markNoLongerEditing(uintptr_t key, size_t length=0)
Definition Cache.cc:2646
uintptr_t lookup(uintptr_t key)
Definition Cache.cc:737
MUST_USE_RESULT bool pin(uintptr_t key)
Definition Cache.cc:1544
bool addDirectoryEntry(const String &name, File *pTarget)
Definition Directory.cc:835
bool empty()
Mutex & namespaceMutationLock()
Definition Directory.h:416
bool removeDirectoryEntry(const HashedStringView &name, File *expected)
Definition Directory.cc:894
ReadStatus isEmpty(bool &empty)
Definition Directory.cc:318
Definition Disk.h:35
Definition File.h:75
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition File.cc:1369
bool resize(size_t size)
Definition File.cc:1213
void setCreationTime(Time::Timestamp t)
Definition File.cc:725
virtual bool isSymlink()
Definition File.cc:800
virtual bool isDirectory()
Definition File.cc:804
virtual size_t getBlockSize() const
Definition File.cc:1077
static ProcessorInformation & information()
Definition RamFs.h:88
XattrStatus getExtendedAttribute(const StringView &name, void *buffer, size_t capacity, size_t &required) override
Definition RamFs.cc:355
void commit() override
Definition RamFs.cc:211
XattrStatus getExtendedAttribute(const StringView &name, void *buffer, size_t capacity, size_t &required) override
Definition RamFs.cc:169
virtual uintptr_t readBlock(uint64_t location) override
Definition RamFs.cc:315
uintptr_t acquireCachedBlock(uint64_t location, bool retryChanged) override
Definition RamFs.cc:339
virtual void truncate() override
Definition RamFs.cc:199
virtual bool pinBlock(uint64_t location) override
Definition RamFs.cc:335
virtual void unpinBlock(uint64_t location) override
Definition RamFs.cc:343
Definition RamFs.h:118
SyncStatus sync() override
Definition RamFs.cc:424
virtual bool renameNode(Directory *oldParent, const String &oldName, File *source, Directory *newParent, const String &newName, File *replaced) override
Definition RamFs.cc:564
virtual bool removeNode(File *parent, const String &filename, File *file) override
Definition RamFs.cc:549
virtual bool createFile(File *parent, const String &filename, uint32_t mask) override
Definition RamFs.cc:450
virtual bool createDirectory(File *parent, const String &filename, uint32_t mask) override
Definition RamFs.cc:474
File * m_pRoot
Definition RamFs.h:159
virtual bool initialise(Disk *pDisk) override
Definition RamFs.cc:441
virtual bool createSymlink(File *parent, const String &filename, const String &value) override
Definition RamFs.cc:498
virtual bool createLink(File *parent, const String &filename, File *target) override
Definition RamFs.cc:523
static UniquePointer< T > adopt(T *pointer)
Definition Pointers.h:101
T popBack()
Definition Vector.h:303
void pushBack(const T &value)
Definition Vector.h:275
size_t count() const
Definition Vector.h:270