File_SDMC::File_SDMC(const Archive_SDMC* archive, const Path& path, const Mode mode) { // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass // the root directory we set while opening the archive. // For example, opening /../../etc/passwd can give the emulated program your users list. this->path = archive->GetMountPoint() + path.AsString(); this->mode.hex = mode.hex; }
PathParser::PathParser(const Path& path) { if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) { is_valid = false; return; } auto path_string = path.AsString(); if (path_string.size() == 0 || path_string[0] != '/') { is_valid = false; return; } // Filter out invalid characters for the host system. // Although some of these characters are valid on 3DS, they are unlikely to be used by games. if (std::find_if(path_string.begin(), path_string.end(), [](char c) { static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'}; return invalid_chars.find(c) != invalid_chars.end(); }) != path_string.end()) { is_valid = false; return; } Common::SplitString(path_string, '/', path_sequence); auto begin = path_sequence.begin(); auto end = path_sequence.end(); end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; }); path_sequence = std::vector<std::string>(begin, end); // checks if the path is out of bounds. int level = 0; for (auto& node : path_sequence) { if (node == "..") { --level; if (level < 0) { is_valid = false; return; } } else { ++level; } } is_valid = true; is_root = level == 0; }
ResultCode SaveDataArchive::CreateDirectory(const Path& path) const { const PathParser path_parser(path); if (!path_parser.IsValid()) { LOG_ERROR(Service_FS, "Invalid path {}", path.DebugStr()); return ERROR_INVALID_PATH; } const auto full_path = path_parser.BuildHostPath(mount_point); switch (path_parser.GetHostStatus(mount_point)) { case PathParser::InvalidMountPoint: LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point {}", mount_point); return ERROR_FILE_NOT_FOUND; case PathParser::PathNotFound: LOG_ERROR(Service_FS, "Path not found {}", full_path); return ERROR_PATH_NOT_FOUND; case PathParser::FileInPath: LOG_ERROR(Service_FS, "Unexpected file in path {}", full_path); return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; case PathParser::DirectoryFound: case PathParser::FileFound: LOG_ERROR(Service_FS, "{} already exists", full_path); return ERROR_DIRECTORY_ALREADY_EXISTS; case PathParser::NotFound: break; // Expected 'success' case } if (FileUtil::CreateDir(mount_point + path.AsString())) { return RESULT_SUCCESS; } LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", mount_point); return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); }
/** * Create a directory specified by its path * @param path Path relative to the archive * @return Whether the directory could be created */ bool Archive_SDMC::CreateDirectory(const Path& path) const { return FileUtil::CreateDir(GetMountPoint() + path.AsString()); }
bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()); }
bool DiskArchive::CreateDirectory(const Path& path) const { return FileUtil::CreateDir(mount_point + path.AsString()); }
bool DiskArchive::DeleteFile(const Path& path) const { return FileUtil::Delete(mount_point + path.AsString()); }
DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) : directory() { // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass // the root directory we set while opening the archive. // For example, opening /../../usr/bin can give the emulated program your installed programs. this->path = archive.mount_point + path.AsString(); }