From b6650c57ce7f7f4c80403f17c2bf0f09cd97940e Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Sun, 6 Sep 2026 09:52:52 +0200 Subject: [PATCH 1/5] piscsi-web: enumerate files in shared dir for CD-ROM generation instead of enumerating the images dir for the dropdown where you select a file to create a CD-ROM image from, provide a list of all files in the root of the shared dir, typically /var/lib/piscsi/shared this is preferable since 1) the images dir enumerating logic filters by valid image or archive file types, and 2) conceptually the files under images are typically not used directly on host machines, while the ones in the shared dir are meant to be transferred to a host machine for users the workflow changes slightly: when uploading a file meant for an iso image, choose the shared dir instead of the images dir --- go/piscsi-web/internal/server/handlers.go | 23 +++++++++++++-- .../internal/server/handlers_image_test.go | 29 +++++++++++++++++-- go/piscsi-web/web/templates/index.html | 8 ++--- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/go/piscsi-web/internal/server/handlers.go b/go/piscsi-web/internal/server/handlers.go index 81262729e1..08d72dd78c 100644 --- a/go/piscsi-web/internal/server/handlers.go +++ b/go/piscsi-web/internal/server/handlers.go @@ -186,6 +186,7 @@ func (s *Server) handleIndex(c *gin.Context) { data["ShowUnits"] = showUnits data["Files"] = files data["FilesBySubdir"] = filesBySubdir + data["SharedFiles"] = sharedFiles(s.config.SharedDir) data["ImageDir"] = s.config.BaseDir data["ImageDirExists"] = imageDirExists data["ImageRootDir"] = s.config.BaseDir @@ -3128,6 +3129,24 @@ func transferSubdirectories(root string) []string { return subdirectories } +// sharedFiles returns the visible regular files directly in the shared +// directory. These are available as sources when creating CD-ROM images. +func sharedFiles(root string) []string { + entries, err := os.ReadDir(root) + if err != nil { + return []string{} + } + + files := make([]string, 0, len(entries)) + for _, entry := range entries { + if strings.HasPrefix(entry.Name(), ".") || !entry.Type().IsRegular() { + continue + } + files = append(files, entry.Name()) + } + return files +} + // displays disk image information func (s *Server) handleFilesDiskinfo(c *gin.Context) { fileName := c.PostForm("file_name") @@ -3502,9 +3521,9 @@ func (s *Server) handleFilesCreateISO(c *gin.Context) { isoPath, err = resolvePathWithin(s.config.BaseDir, fileName+".iso") } } else { - sourcePath, err = resolvePathWithin(s.config.BaseDir, localFile) + sourcePath, err = resolvePathWithin(s.config.SharedDir, localFile) if err == nil { - isoPath, err = resolvePathWithin(s.config.BaseDir, localFile+".iso") + isoPath, err = resolvePathWithin(s.config.BaseDir, filepath.Base(localFile)+".iso") } } if err != nil { diff --git a/go/piscsi-web/internal/server/handlers_image_test.go b/go/piscsi-web/internal/server/handlers_image_test.go index c6c47c5338..af0d1911e4 100644 --- a/go/piscsi-web/internal/server/handlers_image_test.go +++ b/go/piscsi-web/internal/server/handlers_image_test.go @@ -175,14 +175,18 @@ func TestHandleFilesCreateISOLocalFile(t *testing.T) { gin.SetMode(gin.TestMode) root := t.TempDir() imageDir := filepath.Join(root, "images") + sharedDir := filepath.Join(root, "shared") binDir := filepath.Join(root, "bin") if err := os.MkdirAll(imageDir, 0o755); err != nil { t.Fatal(err) } + if err := os.MkdirAll(sharedDir, 0o755); err != nil { + t.Fatal(err) + } if err := os.MkdirAll(binDir, 0o755); err != nil { t.Fatal(err) } - if err := os.WriteFile(filepath.Join(imageDir, "software.hds"), []byte("software"), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(sharedDir, "software.hds"), []byte("software"), 0o644); err != nil { t.Fatal(err) } fakeGenisoimage := "#!/bin/sh\nwhile [ \"$1\" != \"-o\" ]; do shift; done\nshift\n: > \"$1\"\n" @@ -192,7 +196,7 @@ func TestHandleFilesCreateISOLocalFile(t *testing.T) { t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH")) server := &Server{ - config: &config.Config{BaseDir: imageDir, TemplatesDir: filepath.Join(root, "web", "templates")}, + config: &config.Config{BaseDir: imageDir, SharedDir: sharedDir, TemplatesDir: filepath.Join(root, "web", "templates")}, logger: slog.New(slog.NewTextHandler(io.Discard, nil)), sessionStore: sessions.NewCookieStore([]byte("test-secret-key")), } @@ -217,6 +221,27 @@ func TestHandleFilesCreateISOLocalFile(t *testing.T) { } } +func TestSharedFilesListsVisibleRegularFiles(t *testing.T) { + sharedDir := t.TempDir() + for name, content := range map[string][]byte{ + "document.txt": []byte("document"), + ".hidden": []byte("hidden"), + "archive.zip": []byte("archive"), + } { + if err := os.WriteFile(filepath.Join(sharedDir, name), content, 0o644); err != nil { + t.Fatal(err) + } + } + if err := os.Mkdir(filepath.Join(sharedDir, "folder"), 0o755); err != nil { + t.Fatal(err) + } + + want := []string{"archive.zip", "document.txt"} + if got := sharedFiles(sharedDir); !reflect.DeepEqual(got, want) { + t.Fatalf("sharedFiles() = %v, want %v", got, want) + } +} + func TestGetImageFilesUsesLocalDirectoryAndDaemonMapping(t *testing.T) { imageDir := t.TempDir() nestedDir := filepath.Join(imageDir, "nested") diff --git a/go/piscsi-web/web/templates/index.html b/go/piscsi-web/web/templates/index.html index 9ea61eb672..dffd7bbab2 100644 --- a/go/piscsi-web/web/templates/index.html +++ b/go/piscsi-web/web/templates/index.html @@ -471,7 +471,7 @@ @@ -572,10 +572,10 @@
- + From 557f608f29a90524bfec63245db5d5c6e14775d4 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Sun, 6 Sep 2026 10:22:27 +0200 Subject: [PATCH 2/5] piscsi-web: improve help text and status messages for file upload --- go/piscsi-web/internal/server/handlers.go | 2 +- .../internal/server/handlers_upload_test.go | 2 +- go/piscsi-web/web/templates/upload.html | 27 ++++++++++++------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/go/piscsi-web/internal/server/handlers.go b/go/piscsi-web/internal/server/handlers.go index 08d72dd78c..3922f6a318 100644 --- a/go/piscsi-web/internal/server/handlers.go +++ b/go/piscsi-web/internal/server/handlers.go @@ -1309,7 +1309,7 @@ func (s *Server) handleFilesUpload(c *gin.Context) { s.logger.Info("File uploaded", "filename", filename, "size", fileSize, "destination", filepath.Dir(fullPath)) s.respond(c, ResponseOptions{ - Message: "File uploaded successfully", + Message: fmt.Sprintf("File %q uploaded successfully to %q", filename, filepath.Dir(fullPath)), RedirectURL: "/upload", }) } diff --git a/go/piscsi-web/internal/server/handlers_upload_test.go b/go/piscsi-web/internal/server/handlers_upload_test.go index 6999ac2c7c..ff8204b79b 100644 --- a/go/piscsi-web/internal/server/handlers_upload_test.go +++ b/go/piscsi-web/internal/server/handlers_upload_test.go @@ -155,7 +155,7 @@ func TestHandleFilesUploadReturnsJSONForProgressRequest(t *testing.T) { if contentType := recorder.Header().Get("Content-Type"); !strings.HasPrefix(contentType, "application/json") { t.Fatalf("Content-Type = %q, want JSON", contentType) } - if !strings.Contains(recorder.Body.String(), `"message":"File uploaded successfully"`) { + if !strings.Contains(recorder.Body.String(), `"message":"File \"disk.hda\" uploaded successfully to \"`+imageDir+`\""`) { t.Fatalf("response = %s", recorder.Body.String()) } if _, err := os.Stat(filepath.Join(imageDir, "disk.hda")); err != nil { diff --git a/go/piscsi-web/web/templates/upload.html b/go/piscsi-web/web/templates/upload.html index 437c736d36..e97afffa2d 100644 --- a/go/piscsi-web/web/templates/upload.html +++ b/go/piscsi-web/web/templates/upload.html @@ -5,6 +5,12 @@

Upload File from Local Computer

+

Select the destination directory for the uploaded file according to its purpose:

+
    +
  • images are disk images to be mounted as a SCSI storage device
  • +
  • shared is for files to be shared with the host through network file sharing or used to create an ISO
  • +
  • config is meant for PiSCSI configuration files
  • +
+ + + {{end}} {{else}} {{if .IsArchive}} {{if .ArchiveContents}}