Skip to content

Commit

Permalink
Merge pull request #1109 from rommapp/rename-replace-illegal-chars
Browse files Browse the repository at this point in the history
Replace illegal fs chars in filenames
  • Loading branch information
gantoine authored Aug 26, 2024
2 parents e55401f + 38d5ec1 commit 2ce3282
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 28 deletions.
31 changes: 19 additions & 12 deletions backend/endpoints/rom.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from starlette.requests import ClientDisconnect
from streaming_form_data import StreamingFormDataParser
from streaming_form_data.targets import FileTarget, NullTarget
from utils.filesystem import sanitize_filename
from utils.hashing import crc32_to_hex
from utils.nginx import ZipContentLine, ZipResponse
from utils.router import APIRouter
Expand Down Expand Up @@ -337,19 +338,25 @@ async def update_rom(
}
)

fs_safe_file_name = data.get("file_name", rom.file_name).strip().replace("/", "-")
fs_safe_name = cleaned_data["name"].strip().replace("/", "-")

if rename_as_source:
fs_safe_file_name = rom.file_name.replace(
rom.file_name_no_tags or rom.file_name_no_ext, fs_safe_name
)
new_file_name = data.get("file_name", rom.file_name)

try:
if rom.file_name != fs_safe_file_name:
if rename_as_source:
new_file_name = rom.file_name.replace(
rom.file_name_no_tags or rom.file_name_no_ext,
data.get("name", rom.name),
)
new_file_name = sanitize_filename(new_file_name)
fs_rom_handler.rename_file(
old_name=rom.file_name,
new_name=new_file_name,
file_path=rom.file_path,
)
elif rom.file_name != new_file_name:
new_file_name = sanitize_filename(new_file_name)
fs_rom_handler.rename_file(
old_name=rom.file_name,
new_name=fs_safe_file_name,
new_name=new_file_name,
file_path=rom.file_path,
)
except RomAlreadyExistsException as exc:
Expand All @@ -360,12 +367,12 @@ async def update_rom(

cleaned_data.update(
{
"file_name": fs_safe_file_name,
"file_name": new_file_name,
"file_name_no_tags": fs_rom_handler.get_file_name_with_no_tags(
fs_safe_file_name
new_file_name
),
"file_name_no_ext": fs_rom_handler.get_file_name_with_no_extension(
fs_safe_file_name
new_file_name
),
}
)
Expand Down
34 changes: 34 additions & 0 deletions backend/utils/filesystem.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import re
from collections.abc import Iterator
from pathlib import Path

Expand Down Expand Up @@ -27,3 +28,36 @@ def iter_directories(path: str, recursive: bool = False) -> Iterator[tuple[Path,
yield Path(root), directory
if not recursive:
break


INVALID_CHARS_HYPHENS = re.compile(r"[\\/:|]")
INVALUD_CHARS_EMPTY = re.compile(r'[*?"<>]')


def sanitize_filename(filename):
"""
Replace invalid characters in the filename to make it valid across common filesystems
Args:
- filename (str): The filename to sanitize.
Returns:
- str: The sanitized filename.
"""
# Replace some invalid characters with hyphen
sanitized_filename = INVALID_CHARS_HYPHENS.sub("-", filename)

# Remove other invalid characters
sanitized_filename = INVALUD_CHARS_EMPTY.sub("", sanitized_filename)

# Ensure null bytes are not included (ZFS allows any characters except null bytes)
sanitized_filename = sanitized_filename.replace("\0", "")

# Remove leading/trailing whitespace
sanitized_filename = sanitized_filename.strip()

# Ensure the filename is not empty
if not sanitized_filename:
raise ValueError("Filename cannot be empty after sanitization")

return sanitized_filename
22 changes: 6 additions & 16 deletions frontend/src/components/common/Game/Dialog/EditRom.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ const rom = ref<UpdateRom>();
const romsStore = storeRoms();
const imagePreviewUrl = ref<string | undefined>("");
const removeCover = ref(false);
const fileNameInputRules = {
required: (value: string) => !!value || "Required",
newFileName: (value: string) => !value.includes("/") || "Invalid characters",
};
const emitter = inject<Emitter<Events>>("emitter");
emitter?.on("showEditRomDialog", (romToEdit: UpdateRom | undefined) => {
show.value = true;
Expand Down Expand Up @@ -62,16 +58,9 @@ async function removeArtwork() {
async function updateRom() {
if (!rom.value) return;
if (rom.value.file_name.includes("/")) {
if (!rom.value.file_name) {
emitter?.emit("snackbarShow", {
msg: "Couldn't edit rom: invalid file name characters",
icon: "mdi-close-circle",
color: "red",
});
return;
} else if (!rom.value.file_name) {
emitter?.emit("snackbarShow", {
msg: "Couldn't edit rom: file name required",
msg: "Cannot save: file name is required",
icon: "mdi-close-circle",
color: "red",
});
Expand Down Expand Up @@ -145,8 +134,7 @@ function closeDialog() {
v-model="rom.file_name"
class="py-2"
:rules="[
fileNameInputRules.newFileName,
fileNameInputRules.required,
(value: string) => !!value
]"
label="File name"
variant="outlined"
Expand Down Expand Up @@ -177,7 +165,9 @@ function closeDialog() {
<template #append-inner-right>
<v-btn-group rounded="0" divided density="compact">
<v-btn
:disabled="!heartbeat.value.METADATA_SOURCES?.STEAMGRIDDB_ENABLED"
:disabled="
!heartbeat.value.METADATA_SOURCES?.STEAMGRIDDB_ENABLED
"
size="small"
class="translucent-dark"
@click="
Expand Down

0 comments on commit 2ce3282

Please sign in to comment.