The Pedigree Project 0.1
FatFilesystem-resize.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "pedigree/kernel/LockGuard.h"
9#include "pedigree/kernel/syscallError.h"
10
11#include "FatFilesystem.h"
12
14 public:
15 ShrinkPlan(FatFilesystem& filesystem, FatFile& file, size_t size)
16 : m_Filesystem(filesystem), m_File(file), m_Size(size) {}
17
18 void commit() override {
19 LockGuard<Mutex> guard(m_Filesystem.m_FileMutationLock);
20 m_Filesystem.publishSize(&m_File, m_Size);
21 m_File.m_MetadataDirty = true;
22 // Reclaim only after the smaller directory size reaches durable storage.
23 m_File.m_TrimPending = true;
24 }
25
26 private:
27 FatFilesystem& m_Filesystem;
28 FatFile& m_File;
29 size_t m_Size;
30};
31
32bool FatFilesystem::prepareFileShrink(FatFile* file, size_t size,
35 if (m_bReadOnly) {
36 SYSCALL_ERROR(ReadOnlyFilesystem);
37 return false;
38 }
39 uint32_t count = 0, last = 0;
40 if (!m_BlockSize || size >= file->getSize() || !chainExtent(file, count, last) ||
41 uint64_t(count) * m_BlockSize < file->getSize()) {
42 SYSCALL_ERROR(IoError);
43 return false;
44 }
45 if (!isNodeUnlinked(file)) {
46 Dir* entry = getDirectoryEntry(file->getDirCluster(), file->getDirOffset());
47 if (!entry) {
48 SYSCALL_ERROR(IoError);
49 return false;
50 }
51 delete entry;
52 }
53 auto plan = UniquePointer<File::PreparedShrink>::adopt(new ShrinkPlan(*this, *file, size));
54 if (!plan) {
55 SYSCALL_ERROR(OutOfMemory);
56 return false;
57 }
58 prepared = pedigree_std::move(plan);
59 return true;
60}
61
62bool FatFilesystem::trimFileAllocation(FatFile* file) {
63 if (!file->m_TrimPending)
64 return true;
65 bool succeeded = true;
66 while (file->m_RetiredClusters.count()) {
67 const uint32_t cluster = file->m_RetiredClusters[file->m_RetiredClusters.count() - 1];
68 const bool staged = setClusterEntry(cluster, 0, true, false);
69 if (!staged && getClusterEntry(cluster) != 0) {
70 SYSCALL_ERROR(IoError);
71 return false;
72 }
73 // A staged free may be reused after the allocator flushes the FAT. Never
74 // keep its number in a journal that could free that future allocation.
75 file->m_RetiredClusters.popBack();
76 succeeded = staged && succeeded;
77 }
78 if (!syncFat() || !succeeded) {
79 SYSCALL_ERROR(IoError);
80 return false;
81 }
82 uint32_t count = 0, last = 0;
83 if (!chainExtent(file, count, last)) {
84 SYSCALL_ERROR(IoError);
85 return false;
86 }
87 const size_t size = file->getSize();
88 const size_t keep = size / m_BlockSize + (size % m_BlockSize != 0);
89 if (count <= keep) {
90 file->m_TrimPending = false;
91 return true;
92 }
93 uint32_t retained = file->getInode();
94 for (size_t i = 1; i < keep; ++i)
95 retained = getClusterEntry(retained);
96 const uint32_t retired = keep ? getClusterEntry(retained) : retained;
97 if (!file->m_RetiredClusters.tryReserve(count - keep)) {
98 SYSCALL_ERROR(OutOfMemory);
99 return false;
100 }
101 uint32_t cluster = retired;
102 for (size_t i = keep; i < count; ++i) {
103 file->m_RetiredClusters.pushBack(cluster);
104 cluster = getClusterEntry(cluster);
105 }
106 if (!keep) {
107 // The directory entry already has a zero start cluster. The retirement
108 // journal owns its former chain until each free is staged for retry.
109 file->setInode(0);
110 } else if (!setClusterEntry(retained, eofValue())) {
111 // A failed mirror write cannot surrender ownership of the detached suffix.
112 setClusterEntry(retained, retired);
113 file->m_RetiredClusters.clear();
114 SYSCALL_ERROR(IoError);
115 return false;
116 }
117 return trimFileAllocation(file);
118}
119
120void FatFilesystem::truncate(File* file) {
121 file->resize(0);
122}
123
124bool FatFilesystem::truncateFile(File* file) {
125 return file->resize(0);
126}
127
128void FatFilesystem::extend(File* file, size_t size) {
130 if (file->getSize() >= size)
131 return;
132 if (m_bReadOnly) {
133 SYSCALL_ERROR(ReadOnlyFilesystem);
134 return;
135 }
136 if (size > UINT32_MAX) {
137 SYSCALL_ERROR(FileTooLarge);
138 return;
139 }
140 if (!syncFat() || !ensureCapacity(file, size) || !zeroRange(file, file->getSize(), size) ||
141 !updateFileMetadata(file, size)) {
142 SYSCALL_ERROR(IoError);
143 return;
144 }
145 publishSize(file, size);
146}
uint32_t eofValue() const
Mutex m_FileMutationLock
uint32_t m_BlockSize
Dir * getDirectoryEntry(uint32_t clus, uint32_t offset) const
uint32_t getClusterEntry(uint32_t cluster, bool bLock=true)
bool setClusterEntry(uint32_t cluster, uint32_t value, bool bLock=true, bool persist=true)
Definition File.h:74
bool resize(size_t size)
Definition File.cc:1085
bool m_bReadOnly
Definition Filesystem.h:178
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
void clear(bool freeMem=false)
Definition Vector.h:378
size_t count() const
Definition Vector.h:270
Definition ext2.h:201