[FS] Add app data folder, add path safety checks

This commit is contained in:
wheremyfoodat 2023-01-16 01:06:43 +02:00
parent c16bf5c8aa
commit b1f2be98fa
6 changed files with 77 additions and 12 deletions

View file

@ -1,6 +1,8 @@
#pragma once
#include <cassert>
#include <optional>
#include <string>
#include <type_traits>
#include <vector>
#include "helpers.hpp"
#include "memory.hpp"
@ -93,6 +95,51 @@ protected:
using Handle = u32;
Memory& mem;
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory tree
// And access files outside of the emulator's app data folder
template <u32 format>
bool isPathSafe(const FSPath& path) {
static_assert(format == PathType::ASCII || format == PathType::UTF16);
using String = std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
using Char = String::value_type; // Char type for the path
String pathString, dots;
if constexpr (std::is_same<String, std::u16string>()) {
pathString = path.utf16_string;
dots = u"..";
} else {
pathString = path.string;
dots = "..";
}
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
if (pathString[0] != Char('/')) return false;
// Counts how many folders sans the root our file is nested under.
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
// If it's 0 then this is the FS root.
// If it's > 0 then we're in a subdirectory of the root.
int level = 0;
// Split the string on / characters and see how many of the substrings are ".."
size_t pos = 0;
while ((pos = pathString.find(Char('/'))) != String::npos) {
String token = pathString.substr(0, pos);
pathString.erase(0, pos + 1);
if (token == dots) {
level--;
if (level < 0) return false;
} else {
level++;
}
}
return true;
}
public:
virtual std::string name() = 0;
virtual u64 getFreeBytes() = 0;