Use correct dataset folder in dataset nodes

This commit is contained in:
Kohaku-Blueleaf 2026-07-07 19:46:33 +08:00
parent ac5569f2a0
commit 990dded2c1

View File

@ -42,6 +42,62 @@ def load_and_process_images(image_files, input_dir):
return output_images return output_images
def secure_subfolder_path(base_dir, folder_name):
"""Resolve folder_name inside base_dir, rejecting anything that escapes it.
Blocks '..', absolute paths, drive letters and symlink escapes using the
same realpath containment check as the core file endpoints.
"""
target = os.path.abspath(os.path.join(base_dir, folder_name))
if not folder_paths.is_within_directory(base_dir, target):
raise ValueError(f"Invalid folder name {folder_name!r}: resolves outside of {base_dir}")
return target
def list_dataset_folders():
"""Relative paths of dataset folders found under all dataset roots.
Any subfolder containing a metadata.json or *.safetensors shard counts as
a dataset; the walk doesn't descend into matched folders.
"""
found = set()
for root in folder_paths.get_folder_paths("datasets"):
if not os.path.isdir(root):
continue
for dirpath, subdirs, filenames in os.walk(root, followlinks=True):
if dirpath != root and (
"metadata.json" in filenames
or any(f.endswith(".safetensors") for f in filenames)
):
found.add(os.path.relpath(dirpath, root).replace(os.sep, "/"))
subdirs[:] = []
return sorted(found)
def get_dataset_save_dir(folder_name):
"""Resolve the folder to save a new dataset into, inside the default root.
The folder is not created here; callers makedirs after validation.
"""
root = folder_paths.get_folder_paths("datasets")[0]
target = secure_subfolder_path(root, folder_name)
if os.path.realpath(target) == os.path.realpath(root):
raise ValueError("folder_name must name a subfolder of the datasets directory, e.g. 'my_dataset'.")
return target
def get_dataset_dir(folder_name):
"""Find an existing dataset folder by relative name across all dataset roots."""
roots = folder_paths.get_folder_paths("datasets")
for root in roots:
target = secure_subfolder_path(root, folder_name)
if os.path.realpath(target) == os.path.realpath(root):
raise ValueError("folder_name must name a subfolder of the datasets directory, e.g. 'my_dataset'.")
if os.path.isdir(target):
return target
raise ValueError(f"Dataset folder {folder_name!r} not found in: {', '.join(roots)}")
class LoadImageDataSetFromFolderNode(io.ComfyNode): class LoadImageDataSetFromFolderNode(io.ComfyNode):
@classmethod @classmethod
def define_schema(cls): def define_schema(cls):
@ -252,7 +308,7 @@ class SaveImageDataSetToFolderNode(io.ComfyNode):
filename_prefix = filename_prefix[0] filename_prefix = filename_prefix[0]
mode = mode[0] mode = mode[0]
output_dir = os.path.join(folder_paths.get_output_directory(), folder_name) output_dir = secure_subfolder_path(folder_paths.get_output_directory(), folder_name)
saved_files = save_images_to_folder(images, output_dir, filename_prefix, mode=='overwrite') saved_files = save_images_to_folder(images, output_dir, filename_prefix, mode=='overwrite')
logging.info(f"Saved {len(saved_files)} images to {output_dir}.") logging.info(f"Saved {len(saved_files)} images to {output_dir}.")
@ -306,7 +362,7 @@ class SaveImageTextDataSetToFolderNode(io.ComfyNode):
filename_prefix = filename_prefix[0] filename_prefix = filename_prefix[0]
mode = mode[0] mode = mode[0]
output_dir = os.path.join(folder_paths.get_output_directory(), folder_name) output_dir = secure_subfolder_path(folder_paths.get_output_directory(), folder_name)
saved_files = save_images_to_folder(images, output_dir, filename_prefix, mode=='overwrite') saved_files = save_images_to_folder(images, output_dir, filename_prefix, mode=='overwrite')
# Save captions # Save captions
@ -1443,7 +1499,7 @@ class SaveTrainingDataset(io.ComfyNode):
io.String.Input( io.String.Input(
"folder_name", "folder_name",
default="training_dataset", default="training_dataset",
tooltip="Name of folder to save dataset (inside output directory).", tooltip="Name of folder to save the dataset into, inside the datasets directory. Subfolders like 'project/run1' are allowed.",
), ),
io.Int.Input( io.Int.Input(
"shard_size", "shard_size",
@ -1473,8 +1529,8 @@ class SaveTrainingDataset(io.ComfyNode):
f"Something went wrong in dataset preparation." f"Something went wrong in dataset preparation."
) )
# Create output directory # Create output directory (inside the datasets root, traversal-safe)
output_dir = os.path.join(folder_paths.get_output_directory(), folder_name) output_dir = get_dataset_save_dir(folder_name)
os.makedirs(output_dir, exist_ok=True) os.makedirs(output_dir, exist_ok=True)
# Prepare data pairs # Prepare data pairs
@ -1533,10 +1589,10 @@ class LoadTrainingDataset(io.ComfyNode):
description="Load encoded training dataset (latents + conditioning) from disk for use in training.", description="Load encoded training dataset (latents + conditioning) from disk for use in training.",
is_experimental=True, is_experimental=True,
inputs=[ inputs=[
io.String.Input( io.Combo.Input(
"folder_name", "folder_name",
default="training_dataset", options=list_dataset_folders(),
tooltip="Name of folder containing the saved dataset (inside output directory).", tooltip="Saved dataset to load, from the datasets directory.",
), ),
], ],
outputs=[ outputs=[
@ -1555,11 +1611,8 @@ class LoadTrainingDataset(io.ComfyNode):
@classmethod @classmethod
def execute(cls, folder_name): def execute(cls, folder_name):
# Get dataset directory # Get dataset directory (searched across all dataset roots, traversal-safe)
dataset_dir = os.path.join(folder_paths.get_output_directory(), folder_name) dataset_dir = get_dataset_dir(folder_name)
if not os.path.exists(dataset_dir):
raise ValueError(f"Dataset directory not found: {dataset_dir}")
# Find all shard files # Find all shard files
shard_files = sorted( shard_files = sorted(