diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 1bc291cf95..b8dd92b651 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -826,7 +826,7 @@ std::string_view GetPathWithoutTop(std::string_view path) {
     }
 
     while (path[0] == '\\' || path[0] == '/') {
-        path.remove_suffix(1);
+        path.remove_prefix(1);
         if (path.empty()) {
             return path;
         }
@@ -870,6 +870,15 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
     return path;
 }
 
+std::string SanitizePath(std::string_view path_) {
+    std::string path(path_);
+    std::replace(path.begin(), path.end(), '\\', '/');
+    path.erase(std::unique(path.begin(), path.end(),
+                           [](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
+               path.end());
+    return std::string(RemoveTrailingSlash(path));
+}
+
 IOFile::IOFile() {}
 
 IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
diff --git a/src/common/file_util.h b/src/common/file_util.h
index abfa79eaea..bc9272d89d 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -178,6 +178,9 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
     return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
 }
 
+// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
+std::string SanitizePath(std::string_view path);
+
 // simple wrapper for cstdlib file functions to
 // hopefully will make error checking easier
 // and make forgetting an fclose() harder
diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h
index b4363152a3..c952056682 100644
--- a/src/core/file_sys/mode.h
+++ b/src/core/file_sys/mode.h
@@ -11,7 +11,13 @@ namespace FileSys {
 enum class Mode : u32 {
     Read = 1,
     Write = 2,
+    ReadWrite = 3,
     Append = 4,
+    WriteAppend = 6,
 };
 
+inline u32 operator&(Mode lhs, Mode rhs) {
+    return static_cast<u32>(lhs) & static_cast<u32>(rhs);
+}
+
 } // namespace FileSys
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 095fec77e5..9ce2e1efa0 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -13,24 +13,31 @@
 
 namespace FileSys {
 
-static std::string PermissionsToCharArray(Mode perms) {
-    std::string out;
-    switch (perms) {
-    case Mode::Read:
-        out += "r";
-        break;
-    case Mode::Write:
-        out += "r+";
-        break;
-    case Mode::Append:
-        out += "a";
-        break;
+static std::string ModeFlagsToString(Mode mode) {
+    std::string mode_str;
+
+    // Calculate the correct open mode for the file.
+    if (mode & Mode::Read && mode & Mode::Write) {
+        if (mode & Mode::Append)
+            mode_str = "a+";
+        else
+            mode_str = "r+";
+    } else {
+        if (mode & Mode::Read)
+            mode_str = "r";
+        else if (mode & Mode::Append)
+            mode_str = "a";
+        else if (mode & Mode::Write)
+            mode_str = "w";
     }
-    return out + "b";
+
+    mode_str += "b";
+
+    return mode_str;
 }
 
 RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
-    : backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_),
+    : backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
       parent_path(FileUtil::GetParentPath(path_)),
       path_components(FileUtil::SplitPathComponents(path_)),
       parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
@@ -53,11 +60,11 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
 }
 
 bool RealVfsFile::IsWritable() const {
-    return perms == Mode::Append || perms == Mode::Write;
+    return (perms & Mode::WriteAppend) != 0;
 }
 
 bool RealVfsFile::IsReadable() const {
-    return perms == Mode::Read || perms == Mode::Write;
+    return (perms & Mode::ReadWrite) != 0;
 }
 
 size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
@@ -79,7 +86,7 @@ bool RealVfsFile::Rename(std::string_view name) {
     path = (parent_path + DIR_SEP).append(name);
     path_components = parent_components;
     path_components.push_back(std::move(name_str));
-    backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
+    backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
 
     return out;
 }
@@ -93,7 +100,7 @@ RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
       path_components(FileUtil::SplitPathComponents(path)),
       parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
       perms(perms_) {
-    if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append))
+    if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
         FileUtil::CreateDir(path);
 
     if (perms == Mode::Append)
@@ -120,11 +127,11 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories()
 }
 
 bool RealVfsDirectory::IsWritable() const {
-    return perms == Mode::Write || perms == Mode::Append;
+    return (perms & Mode::WriteAppend) != 0;
 }
 
 bool RealVfsDirectory::IsReadable() const {
-    return perms == Mode::Read || perms == Mode::Write;
+    return (perms & Mode::ReadWrite) != 0;
 }
 
 std::string RealVfsDirectory::GetName() const {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index dbfe06cbc8..fdd2fda182 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -24,7 +24,8 @@ namespace Service::FileSystem {
 constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
 
 static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
-                                                       std::string_view dir_name) {
+                                                       std::string_view dir_name_) {
+    std::string dir_name(FileUtil::SanitizePath(dir_name_));
     if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
         return base;
 
@@ -38,7 +39,8 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
     return backing->GetName();
 }
 
-ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
+ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
+    std::string path(FileUtil::SanitizePath(path_));
     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
     auto file = dir->CreateFile(FileUtil::GetFilename(path));
     if (file == nullptr) {
@@ -52,7 +54,8 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 s
     return RESULT_SUCCESS;
 }
 
-ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
+ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
+    std::string path(FileUtil::SanitizePath(path_));
     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
     if (path == "/" || path == "\\") {
         // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
@@ -60,14 +63,15 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const
     }
     if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
         return FileSys::ERROR_PATH_NOT_FOUND;
-    if (!backing->DeleteFile(FileUtil::GetFilename(path))) {
+    if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
         // TODO(DarkLordZach): Find a better error code for this
         return ResultCode(-1);
     }
     return RESULT_SUCCESS;
 }
 
-ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
+ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
+    std::string path(FileUtil::SanitizePath(path_));
     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
     if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
         dir = backing;
@@ -79,7 +83,8 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path)
     return RESULT_SUCCESS;
 }
 
-ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
+ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
+    std::string path(FileUtil::SanitizePath(path_));
     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
     if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
         // TODO(DarkLordZach): Find a better error code for this
@@ -88,7 +93,8 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path)
     return RESULT_SUCCESS;
 }
 
-ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
+ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
+    std::string path(FileUtil::SanitizePath(path_));
     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
     if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
         // TODO(DarkLordZach): Find a better error code for this
@@ -97,8 +103,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
     return RESULT_SUCCESS;
 }
 
-ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
-                                                  const std::string& dest_path) const {
+ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
+                                                  const std::string& dest_path_) const {
+    std::string src_path(FileUtil::SanitizePath(src_path_));
+    std::string dest_path(FileUtil::SanitizePath(dest_path_));
     auto src = backing->GetFileRelative(src_path);
     if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
         // Use more-optimized vfs implementation rename.
@@ -130,8 +138,10 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
     return RESULT_SUCCESS;
 }
 
-ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
-                                                       const std::string& dest_path) const {
+ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
+                                                       const std::string& dest_path_) const {
+    std::string src_path(FileUtil::SanitizePath(src_path_));
+    std::string dest_path(FileUtil::SanitizePath(dest_path_));
     auto src = GetDirectoryRelativeWrapped(backing, src_path);
     if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
         // Use more-optimized vfs implementation rename.
@@ -154,8 +164,9 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
     return ResultCode(-1);
 }
 
-ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
+ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
                                                                      FileSys::Mode mode) const {
+    std::string path(FileUtil::SanitizePath(path_));
     auto npath = path;
     while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
         npath = npath.substr(1);
@@ -171,7 +182,8 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
     return MakeResult<FileSys::VirtualFile>(file);
 }
 
-ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
+ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
+    std::string path(FileUtil::SanitizePath(path_));
     auto dir = GetDirectoryRelativeWrapped(backing, path);
     if (dir == nullptr) {
         // TODO(DarkLordZach): Find a better error code for this
@@ -188,7 +200,8 @@ u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
 }
 
 ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
-    const std::string& path) const {
+    const std::string& path_) const {
+    std::string path(FileUtil::SanitizePath(path_));
     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
     if (dir == nullptr)
         return FileSys::ERROR_PATH_NOT_FOUND;
@@ -272,9 +285,9 @@ void RegisterFileSystems() {
     sdmc_factory = nullptr;
 
     auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
-        FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::Write);
+        FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite);
     auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
-        FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::Write);
+        FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite);
 
     auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
     save_data_factory = std::move(savedata);