122 lines
3.5 KiB
Markdown
122 lines
3.5 KiB
Markdown
# Dataset Mountpoint Validation
|
|
|
|
## Issue
|
|
User meminta validasi bahwa mount point untuk dataset dan volume harus berada di dalam directory pool yang terkait.
|
|
|
|
## Solution
|
|
Menambahkan validasi untuk memastikan mount point dataset berada di dalam pool mount point directory (`/opt/calypso/data/pool/<pool-name>/`).
|
|
|
|
## Changes Made
|
|
|
|
### Updated `backend/internal/storage/zfs.go`
|
|
**File**: `backend/internal/storage/zfs.go` (line 728-814)
|
|
|
|
**Key Changes:**
|
|
|
|
1. **Mount Point Validation**
|
|
- Validasi bahwa mount point yang user berikan harus berada di dalam pool directory
|
|
- Menggunakan `filepath.Rel()` untuk memastikan mount point tidak keluar dari pool directory
|
|
|
|
2. **Default Mount Point**
|
|
- Jika mount point tidak disediakan, set default ke `/opt/calypso/data/pool/<pool-name>/<dataset-name>/`
|
|
- Memastikan semua dataset mount point berada di dalam pool directory
|
|
|
|
3. **Mount Point Always Set**
|
|
- Untuk filesystem datasets, mount point selalu di-set (baik user-provided atau default)
|
|
- Tidak lagi conditional pada `req.MountPoint != ""`
|
|
|
|
**Before:**
|
|
```go
|
|
if req.Type == "filesystem" && req.MountPoint != "" {
|
|
mountPath := filepath.Clean(req.MountPoint)
|
|
// ... create directory ...
|
|
}
|
|
|
|
// Later:
|
|
if req.Type == "filesystem" && req.MountPoint != "" {
|
|
args = append(args, "-o", fmt.Sprintf("mountpoint=%s", req.MountPoint))
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```go
|
|
poolMountPoint := fmt.Sprintf("/opt/calypso/data/pool/%s", poolName)
|
|
var mountPath string
|
|
|
|
if req.Type == "filesystem" {
|
|
if req.MountPoint != "" {
|
|
// Validate mount point is within pool directory
|
|
mountPath = filepath.Clean(req.MountPoint)
|
|
// ... validation logic ...
|
|
} else {
|
|
// Use default mount point
|
|
mountPath = filepath.Join(poolMountPoint, req.Name)
|
|
}
|
|
// ... create directory ...
|
|
}
|
|
|
|
// Later:
|
|
if req.Type == "filesystem" {
|
|
args = append(args, "-o", fmt.Sprintf("mountpoint=%s", mountPath))
|
|
}
|
|
```
|
|
|
|
## Mount Point Structure
|
|
|
|
### Pool Mount Point
|
|
```
|
|
/opt/calypso/data/pool/<pool-name>/
|
|
```
|
|
|
|
### Dataset Mount Point (Default)
|
|
```
|
|
/opt/calypso/data/pool/<pool-name>/<dataset-name>/
|
|
```
|
|
|
|
### Dataset Mount Point (Custom - must be within pool)
|
|
```
|
|
/opt/calypso/data/pool/<pool-name>/<custom-path>/
|
|
```
|
|
|
|
## Validation Rules
|
|
|
|
1. **User-provided mount point**:
|
|
- Must be within `/opt/calypso/data/pool/<pool-name>/`
|
|
- Cannot use `..` to escape pool directory
|
|
- Must be a valid directory path
|
|
|
|
2. **Default mount point**:
|
|
- Automatically set to `/opt/calypso/data/pool/<pool-name>/<dataset-name>/`
|
|
- Always within pool directory
|
|
|
|
3. **Volumes**:
|
|
- Volumes cannot have mount points (already validated in handler)
|
|
|
|
## Error Messages
|
|
|
|
- `mount point must be within pool directory: <path> (pool mount: <pool-mount>)` - Jika mount point di luar pool directory
|
|
- `mount point path exists but is not a directory: <path>` - Jika path sudah ada tapi bukan directory
|
|
- `failed to create mount directory <path>` - Jika gagal membuat directory
|
|
|
|
## Testing
|
|
|
|
1. **Create dataset without mount point**:
|
|
- Should use default: `/opt/calypso/data/pool/<pool-name>/<dataset-name>/`
|
|
|
|
2. **Create dataset with valid mount point**:
|
|
- Mount point: `/opt/calypso/data/pool/<pool-name>/custom-path/`
|
|
- Should succeed
|
|
|
|
3. **Create dataset with invalid mount point**:
|
|
- Mount point: `/opt/calypso/data/other-path/`
|
|
- Should fail with validation error
|
|
|
|
4. **Create volume**:
|
|
- Should not set mount point (volumes don't have mount points)
|
|
|
|
## Status
|
|
✅ **COMPLETED** - Mount point validation untuk dataset sudah diterapkan
|
|
|
|
## Date
|
|
2026-01-09
|