From 0e26ed99bc22d5b4e9c89988d46ba9e7ff9669c0 Mon Sep 17 00:00:00 2001 From: "Othman H. Suseno" Date: Thu, 18 Dec 2025 12:16:00 +0700 Subject: [PATCH] add directory structure for /storage/* --- installer/install.sh | 11 ++++++++ internal/zfs/service.go | 58 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/installer/install.sh b/installer/install.sh index 54f6537..77675ae 100755 --- a/installer/install.sh +++ b/installer/install.sh @@ -524,11 +524,17 @@ create_directories() { mkdir -p "$BACKUP_DIR" mkdir -p "$CONFIG_DIR/tls" + # Create dedicated storage directories for ZFS pools, datasets, and shares + mkdir -p "/storage/pools" + mkdir -p "/storage/datasets" + mkdir -p "/storage/shares" + # Set ownership chown -R "$SERVICE_USER:$SERVICE_USER" "$DATA_DIR" chown -R "$SERVICE_USER:$SERVICE_USER" "$LOG_DIR" chown -R "$SERVICE_USER:$SERVICE_USER" "$BACKUP_DIR" chown -R "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR" + chown -R "$SERVICE_USER:$SERVICE_USER" "/storage" # Set permissions chmod 755 "$INSTALL_DIR" @@ -537,8 +543,13 @@ create_directories() { chmod 700 "$CONFIG_DIR" chmod 750 "$LOG_DIR" chmod 750 "$BACKUP_DIR" + chmod 755 "/storage" + chmod 755 "/storage/pools" + chmod 755 "/storage/datasets" + chmod 755 "/storage/shares" echo -e "${GREEN}Directories created${NC}" + echo " Storage directories: /storage/{pools,datasets,shares}" } # Build binaries diff --git a/internal/zfs/service.go b/internal/zfs/service.go index 82cc225..349fd82 100644 --- a/internal/zfs/service.go +++ b/internal/zfs/service.go @@ -180,6 +180,23 @@ func (s *Service) GetPool(name string) (*models.Pool, error) { func (s *Service) CreatePool(name string, vdevs []string, options map[string]string) error { args := []string{"create"} + if options == nil { + options = make(map[string]string) + } + + // If mountpoint is not explicitly set, use dedicated storage directory + mountpoint := options["mountpoint"] + if mountpoint == "" { + // Default mountpoint: /storage/pools/{poolname} + mountpoint = "/storage/pools/" + name + options["mountpoint"] = mountpoint + // Pre-create the mountpoint directory with sudo + _ = s.createMountpointWithSudo(mountpoint) + } else if mountpoint != "none" { + // Ensure mountpoint directory exists with sudo + _ = s.createMountpointWithSudo(mountpoint) + } + // Add options for k, v := range options { args = append(args, "-o", fmt.Sprintf("%s=%s", k, v)) @@ -192,6 +209,26 @@ func (s *Service) CreatePool(name string, vdevs []string, options map[string]str return err } +// createMountpointWithSudo creates a mountpoint directory using sudo +// This allows ZFS to mount pools even if root filesystem appears read-only +func (s *Service) createMountpointWithSudo(path string) error { + // Use sudo to create the directory with proper permissions + cmd := exec.Command("sudo", "-n", "mkdir", "-p", path) + var stderr bytes.Buffer + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + // If sudo mkdir fails, try without sudo (might already be root or have permissions) + directCmd := exec.Command("mkdir", "-p", path) + if directErr := directCmd.Run(); directErr != nil { + // Both failed, but don't return error - ZFS might handle it + // Log but continue, as ZFS might create it or mountpoint might already exist + return fmt.Errorf("failed to create mountpoint %s: %v: %s", path, err, stderr.String()) + } + } + return nil +} + // DestroyPool destroys a ZFS pool func (s *Service) DestroyPool(name string) error { _, err := s.execCommand(s.zpoolPath, "destroy", name) @@ -440,6 +477,27 @@ func (s *Service) ListDatasets(pool string) ([]models.Dataset, error) { func (s *Service) CreateDataset(name string, options map[string]string) error { args := []string{"create"} + if options == nil { + options = make(map[string]string) + } + + // If mountpoint is not explicitly set, use dedicated storage directory + mountpoint := options["mountpoint"] + if mountpoint == "" { + // Extract dataset name (last part after /) + parts := strings.Split(name, "/") + datasetName := parts[len(parts)-1] + // Default mountpoint: /storage/datasets/{datasetname} + mountpoint = "/storage/datasets/" + datasetName + options["mountpoint"] = mountpoint + // Pre-create the mountpoint directory with sudo + _ = s.createMountpointWithSudo(mountpoint) + } else if mountpoint != "none" { + // Ensure mountpoint directory exists with sudo + _ = s.createMountpointWithSudo(mountpoint) + } + + // Add options for k, v := range options { args = append(args, "-o", fmt.Sprintf("%s=%s", k, v)) }