2#include "DevFs-block.h"
3#include "pedigree/kernel/machine/Disk.h"
4#include "pedigree/kernel/process/TerminationDeferral.h"
5#include "pedigree/kernel/syscallError.h"
6#include "pedigree/kernel/utilities/StaticString.h"
7#include "pedigree/kernel/utilities/utility.h"
10#include "modules/system/vfs/Symlink.h"
11#include "modules/system/vfs/VFS.h"
14bool parseId(
const StringView& name,
size_t start, uint32_t&
id) {
15 if (name.length() <= start || name[start] ==
'0')
18 for (
size_t i = start; i < name.length(); ++i) {
19 const char ch = name[i];
20 if (ch <
'0' || ch >
'9' ||
id > (PosixBlock::MaximumMinor - (ch -
'0')) / 10)
22 id =
id * 10 + ch -
'0';
27class BlockFile final :
public File {
29 BlockFile(
DevFs& filesystem,
File* parent,
const String& name, uint32_t
id, uint64_t bytes,
31 :
File(name, 0, 0, 0, filesystem.getNextInode(), &filesystem, bytes, parent),
38 bool isBlockDevice()
const override {
41 uint64_t deviceNumber()
const override {
42 return PosixBlock::encode(m_Mount ? PosixBlock::MountedMajor : PosixBlock::PhysicalMajor, m_Id);
44 uint64_t
readBytewise(uint64_t offset, uint64_t length, uintptr_t buffer,
45 bool canBlock =
true)
override {
46 return transfer(
false, offset, length, buffer, canBlock);
48 uint64_t
writeBytewise(uint64_t offset, uint64_t length, uintptr_t buffer,
49 bool canBlock =
true)
override {
50 return transfer(
true, offset, length, buffer, canBlock);
57 uint64_t transfer(
bool write, uint64_t offset, uint64_t length, uintptr_t buffer,
bool canBlock) {
61 SYSCALL_ERROR(NoMoreProcesses);
70 if (m_Mount.acquire(mounted) &&
71 mounted.filesystem()->tryAcquireOperation(filesystemOperation)) {
72 disk = mounted.filesystem()->
getDisk();
74 }
else if (DiskEndpoints::acquire(m_Id, use)) {
80 SYSCALL_ERROR(DeviceDoesNotExist);
82 syscallError(DiskEndpoints::describe(m_Id, bytes) ? Error::DeviceBusy
83 : Error::NoSuchDevice);
87 const uint64_t bytes = disk->
getSize();
90 if (length > bytes - offset)
91 length = bytes - offset;
93 while (done < length) {
94 const uint64_t position = offset + done;
95 const uint64_t aligned = position - position % 512;
96 const size_t displacement = position - aligned;
98 if (!view || view.size() <= displacement) {
100 disk->
unpin(aligned);
101 SYSCALL_ERROR(IoError);
104 const size_t amount = min(
static_cast<uint64_t
>(view.size() - displacement), length - done);
105 auto* page =
reinterpret_cast<uint8_t*
>(view.address()) + displacement;
107 MemoryCopy(page,
reinterpret_cast<void*
>(buffer + done), amount);
109 MemoryCopy(
reinterpret_cast<void*
>(buffer + done), page, amount);
110 const bool completed = !write || disk->
sync(aligned,
false);
111 disk->
unpin(aligned);
113 SYSCALL_ERROR(IoError);
126 BlockDirectory(
DevFs& filesystem,
File* parent)
141 const bool physical =
142 name.length() > 4 && name[0] ==
'd' && name[1] ==
'i' && name[2] ==
's' && name[3] ==
'k';
143 if (!parseId(name, physical ? 4 : 0, id))
144 return LookupStatus::NotFound;
148 if (!DiskEndpoints::describe(
id, bytes))
149 return LookupStatus::NotFound;
153 if (!
VFS::instance().diskMount(
id, mount) || !mount.acquire(pin) ||
154 !pin.filesystem()->tryAcquireOperation(filesystemOperation) ||
155 !pin.filesystem()->
getDisk()) {
156 return LookupStatus::NotFound;
161 new BlockFile(*
static_cast<DevFs*
>(getFilesystem()),
this,
String(name),
id, bytes, mount);
162 return child ? LookupStatus::Found : LookupStatus::IoError;
164 ReadStatus
readDirectory(uint64_t& cookie, DirectoryEntryEmitter emitter,
165 void* context)
override {
168 return ReadStatus::IoError;
169 uint32_t physical[DiskEndpoints::Capacity];
170 const size_t physicalCount = DiskEndpoints::snapshot(physical, DiskEndpoints::Capacity);
172 uint64_t next = ~uint64_t(0);
173 for (
const auto& mount : mounts) {
174 if (mount.id() >= cookie && mount.id() < next)
177 for (
size_t i = 0; i < physicalCount; ++i) {
178 const uint64_t key =
static_cast<uint64_t
>(PosixBlock::MaximumMinor) + 1 + physical[i];
179 if (key >= cookie && key < next)
182 if (next == ~uint64_t(0))
183 return ReadStatus::Complete;
185 if (next > PosixBlock::MaximumMinor) {
187 name.append(next - PosixBlock::MaximumMinor - 1);
191 const DirectoryEntryView entry{
StringView(name, name.length()),
static_cast<uintptr_t
>(next),
192 EntryType::BlockDevice, next, next + 1};
193 if (!emitter(context, entry))
194 return ReadStatus::Stopped;
200enum class AliasKind { FilesystemUuid, FilesystemLabel, PartitionUuid, PartitionLabel };
202struct AliasCandidate {
203 AliasCandidate() : key(0), name(), target() {}
204 AliasCandidate(uint32_t key,
const String& name,
const String& target)
205 : key(key), name(name), target(target) {}
213 static constexpr char digits[] =
"0123456789abcdef";
214 for (
size_t i = 0; i < value.length(); ++i) {
215 const uint8_t character = value[i];
216 const bool safe = (character >=
'a' && character <=
'z') ||
217 (character >=
'A' && character <=
'Z') ||
218 (character >=
'0' && character <=
'9') || character ==
'#' ||
219 character ==
'+' || character ==
'-' || character ==
'.' ||
220 character ==
':' || character ==
'=' || character ==
'@' || character ==
'_';
222 char plain[2] = {
static_cast<char>(character), 0};
225 char escaped[5] = {
'\\',
'x', digits[character >> 4], digits[character & 0xf], 0};
232class BlockAlias final :
public Symlink {
235 :
Symlink(name, 0, 0, 0, filesystem.getNextInode(), &filesystem, target.length(), parent) {
237 setPermissions(FILE_UR | FILE_UW | FILE_UX | FILE_GR | FILE_GW | FILE_GX | FILE_OR | FILE_OW |
244 AliasDirectory(
DevFs& filesystem,
File* parent,
const char* name, AliasKind kind)
258 if (!snapshot(candidates))
259 return LookupStatus::IoError;
260 const AliasCandidate* selected =
nullptr;
261 for (
const auto& candidate : candidates) {
262 if (candidate.name.view() == name && (!selected || candidate.key < selected->key))
263 selected = &candidate;
266 return LookupStatus::NotFound;
267 child =
new BlockAlias(*
static_cast<DevFs*
>(getFilesystem()),
this, selected->name,
269 return child ? LookupStatus::Found : LookupStatus::IoError;
272 ReadStatus
readDirectory(uint64_t& cookie, DirectoryEntryEmitter emitter,
273 void* context)
override {
275 if (!snapshot(candidates))
276 return ReadStatus::IoError;
278 const AliasCandidate* selected =
nullptr;
279 for (
const auto& candidate : candidates) {
280 if (candidate.key < cookie || (selected && candidate.key >= selected->key))
282 bool shadowed =
false;
283 for (
const auto& other : candidates) {
284 if (other.key < candidate.key && other.name == candidate.name) {
290 selected = &candidate;
293 return ReadStatus::Complete;
294 const DirectoryEntryView entry{selected->name.view(), selected->key, EntryType::Symlink,
295 selected->key,
static_cast<uint64_t
>(selected->key) + 1};
296 if (!emitter(context, entry))
297 return ReadStatus::Stopped;
298 cookie =
static_cast<uint64_t
>(selected->key) + 1;
307 for (
const auto& mount : mounts) {
310 if (!mount.acquire(pin) || !pin.filesystem()->tryAcquireOperation(filesystemOperation)) {
314 Disk* disk = filesystem ? filesystem->
getDisk() :
nullptr;
318 bool available =
false;
320 case AliasKind::FilesystemUuid:
323 case AliasKind::FilesystemLabel:
327 case AliasKind::PartitionUuid:
330 case AliasKind::PartitionLabel:
331 available = disk->getPartitionLabel(
identity);
334 if (!available || !
identity.length())
337 target.Format(
"/dev/block/%u", mount.id());
348 DiskDirectory(
DevFs& filesystem,
File* parent)
354File* posix_make_block_directory(
DevFs& filesystem,
File* parent) {
355 return new BlockDirectory(filesystem, parent);
358File* posix_make_disk_directory(
DevFs& filesystem,
File* parent) {
359 auto* disk =
new DiskDirectory(filesystem, parent);
362 struct DirectorySpec {
365 } specs[] = {{
"by-uuid", AliasKind::FilesystemUuid},
366 {
"by-label", AliasKind::FilesystemLabel},
367 {
"by-partuuid", AliasKind::PartitionUuid},
368 {
"by-partlabel", AliasKind::PartitionLabel}};
369 for (
const auto& spec : specs) {
370 auto* child =
new AliasDirectory(filesystem, disk, spec.name, spec.kind);
375 disk->addEntry(child->
getName(), child);
virtual bool cacheResolvedChildren() const
virtual ReadStatus readDirectory(uint64_t &cookie, DirectoryEntryEmitter emitter, void *context)
virtual LookupStatus resolveChild(const StringView &name, File *&child)
virtual bool sync(uint64_t location, bool async)
virtual bool getPartitionUuid(String &uuid) const
virtual BufferView read(uint64_t location)
virtual size_t getSize() const
Gets the size of the disk.
virtual void unpin(uint64_t location)=0
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual bool isBytewise() const
void setGidOnly(size_t gid)
void setPermissionsOnly(uint32_t perms)
void setUidOnly(size_t uid)
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual const String & getVolumeLabel() const =0
virtual bool getUuid(String &uuid) const
A vector / dynamic array.
void createBack(Args &&... args)