285 lines
11 KiB
Markdown
285 lines
11 KiB
Markdown
---
|
|
name: storage-management
|
|
description: Detect, partition, format, mount, and back up storage drives on Linux servers — including fstab setup, rsync backup workflows, and permission management.
|
|
version: 1.0.0
|
|
author: Hermes Agent
|
|
license: MIT
|
|
platforms: [linux]
|
|
metadata:
|
|
hermes:
|
|
tags: [storage, disks, drives, rsync, backup, fstab, mount]
|
|
related_skills: [server-health-check]
|
|
---
|
|
|
|
# Storage & Drive Management
|
|
|
|
Manage physical storage drives on Linux — detection, partitioning, formatting, persistent mounting, and full-drive backup via rsync.
|
|
|
|
## Detecting New Drives
|
|
|
|
```bash
|
|
# Full overview: include TRAN(transport) to distinguish USB from SATA/NVMe
|
|
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,MODEL,TRAN
|
|
|
|
# Unmounted drives (device listed but no mountpoint)
|
|
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,MODEL,TRAN | grep -E 'disk$|part$' | awk '$5 == ""'
|
|
|
|
# Get UUIDs for fstab
|
|
sudo blkid
|
|
|
|
# Find by USB vendor/model (useful for identifying thumb drives)
|
|
ls -la /dev/disk/by-id/usb* 2>/dev/null
|
|
```
|
|
|
|
The `TRAN` column shows `usb`, `sata`, or `nvme` — immediately tells you how the drive is connected.
|
|
A drive showing in `lsblk` with no partitions and no mount point is ready to be set up.
|
|
|
|
## Partitioning & Formatting
|
|
|
|
### Step 1 — Create GPT partition table + single partition
|
|
|
|
```bash
|
|
sudo parted /dev/sdX mklabel gpt
|
|
sudo parted /dev/sdX mkpart primary ext4 0% 100%
|
|
sudo partprobe /dev/sdX # reload partition table
|
|
```
|
|
|
|
### Step 2 — Format with ext4 (label it for easy identification)
|
|
|
|
```bash
|
|
sudo mkfs.ext4 /dev/sdX1 -L DriveLabel
|
|
```
|
|
|
|
**Note:** `mkfs` is on the unconditional blocklist in Hermes. The agent cannot run it. Ask the user to run the command directly, then proceed with mounting and fstab.
|
|
|
|
### Step 3 — Create mount point and mount
|
|
|
|
```bash
|
|
sudo mkdir -p /mnt/drive-label
|
|
sudo mount /dev/sdX1 /mnt/drive-label
|
|
```
|
|
|
|
### Step 4 — Add to fstab for persistence
|
|
|
|
```bash
|
|
# Get UUID
|
|
sudo blkid /dev/sdX1
|
|
|
|
# Add entry (ext4 example)
|
|
echo 'UUID=xxxx-xxxx /mnt/drive-label ext4 defaults,noatime 0 2' | sudo tee -a /etc/fstab
|
|
```
|
|
|
|
**fstab format:** `<file_system> <mount_point> <type> <options> <dump> <pass>`
|
|
|
|
| Field | Meaning |
|
|
|-------|---------|
|
|
| `UUID=...` | Use UUID, not `/dev/sdX` — survives re-enumeration |
|
|
| mount point | Directory to mount at |
|
|
| type | `ext4`, `ntfs-3g`, `exfat`, `vfat` |
|
|
| options | `defaults`, `noatime` (skip access time updates), `uid=1000,gid=1000,umask=000` (permissions for NTFS/exFAT) |
|
|
| dump | 0 (no backup) |
|
|
| pass | `2` (non-root fsck), `1` (root fsck), `0` (no fsck) |
|
|
|
|
### External/Removable Drives
|
|
|
|
**ALWAYS add `nofail` to external drives in fstab.** Without it, systemd waits for the drive at boot and either hangs or drops to emergency mode if the drive is unplugged.
|
|
|
|
```bash
|
|
# WRONG — hangs at boot if drive missing
|
|
UUID=xxxx /mnt/external ext4 defaults,noatime 0 2
|
|
|
|
# RIGHT — skips silently if drive not present
|
|
UUID=xxxx /mnt/external ext4 defaults,noatime,nofail 0 0
|
|
```
|
|
|
|
Also use `pass=0` (last column) for external drives — a missing drive that's flagged for fsck (`0 2`) will cause a boot stall even with `nofail`.
|
|
|
|
### Step 5 — Verify
|
|
|
|
```bash
|
|
sudo mount -a # re-read fstab, mount any missing entries
|
|
df -h /mnt/drive-label
|
|
lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT
|
|
```
|
|
|
|
## Unmounting Drives
|
|
|
|
Always unmount before physically disconnecting a drive to avoid data corruption.
|
|
|
|
```bash
|
|
sudo umount /mnt/drive-label
|
|
```
|
|
|
|
### When umount hangs or times out
|
|
|
|
If `umount` hangs (e.g., a process still has open file handles or I/O is stalled), use lazy unmount:
|
|
|
|
```bash
|
|
sudo umount -l /mnt/drive-label
|
|
```
|
|
|
|
`-l` (lazy) detaches the filesystem from the mount tree immediately and cleans up references once they're no longer in use. The drive is safe to disconnect after `umount -l` returns successfully — you'll either get a clean detach or `not mounted` (meaning the original umount actually completed despite the timeout).
|
|
|
|
If even `umount -l` reports `not mounted`, the drive is already detached — safe to remove.
|
|
|
|
### Filesystem-Specific Notes
|
|
|
|
| FS | Install Package | Mount Type | Special Options |
|
|
|----|----------------|------------|-----------------|
|
|
| **ext4** | built-in | default | `defaults,noatime` |
|
|
| **NTFS** | `ntfs-3g` | `ntfs-3g` | `uid=1000,gid=1000,umask=000` (full user access) |
|
|
| **exFAT** | `exfatprogs` | `exfat` | `uid=1000,gid=1000,umask=000` |
|
|
|
|
> ⚠️ **NTFS on Linux:** Always specify `-t ntfs-3g` explicitly. The kernel `ntfs3` driver (in-tree) is experimental and can corrupt data. Use the userspace `ntfs-3g` which is battle-tested.
|
|
|
|
## Full-Drive Backup with rsync
|
|
|
|
For copying an entire drive (or large directories) to another local drive:
|
|
|
|
### Single-Instance rsync (Recommended for USB Drives)
|
|
|
|
Multiple parallel rsync instances on the same USB source cause I/O contention errors (`error in socket IO`, `Broken pipe`, `errors selecting input/output files`). Always use a **single instance** when the source is a USB/external drive.
|
|
|
|
```bash
|
|
# Local disk-to-disk copy (fastest approach)
|
|
sudo rsync -avhW --progress /mnt/source/ /mnt/dest/backup-folder/ \
|
|
--exclude='$RECYCLE.BIN' --exclude='System Volume Information'
|
|
```
|
|
|
|
| Flag | Meaning |
|
|
|------|---------|
|
|
| `-a` | Archive mode (preserves permissions, timestamps, symlinks) |
|
|
| `-v` | Verbose |
|
|
| `-h` | Human-readable sizes |
|
|
| `-W` | **Whole-file** (skip delta checksum) — faster for local copies |
|
|
| `--progress` | Show per-file transfer progress |
|
|
|
|
**Why `-W` for local copies:** rsync normally reads blocks of each file to compute checksums for delta transfer. On a local copy there's no network benefit — `-W` just copies the whole file, eliminating the checksum overhead.
|
|
|
|
For deep rsync best practices (USB I/O contention, ionice scheduling, progress monitoring, post-backup reorganization, performance expectations), see `references/rsync-best-practices.md` (absorbed from `data-migration`).
|
|
|
|
### Permission Handling
|
|
|
|
- **Before running non-sudo rsync, check destination ownership:** `ls -la /mnt/dest/` — if the parent directory is root-owned (e.g., `drwxr-xr-x root root`), rsync will fail with `Permission denied` on `mkdir`. Fix: `sudo chown user:user /mnt/dest/` first.
|
|
- If rsync runs with `sudo`, dest dirs are owned by root
|
|
- Subsequent non-sudo rsync runs fail with `Permission denied` on mkdir
|
|
- **Fix:** `sudo chown -R user:user /mnt/dest/` or chmod on target dirs
|
|
- Or just always run the rsync with `sudo`
|
|
|
|
### Progress Monitoring
|
|
|
|
```bash
|
|
# Check size transferred so far
|
|
df -h /mnt/dest-drive
|
|
|
|
# Check process is alive
|
|
ps aux | grep rsync | grep -v grep
|
|
|
|
# Recent log lines
|
|
tail -5 /path/to/rsync.log
|
|
|
|
# File count (slow for large dirs)
|
|
find /mnt/dest/backup-folder/ -type f | wc -l
|
|
```
|
|
|
|
### Expected Speeds
|
|
|
|
| Source Type | Typical Speed | Notes |
|
|
|-------------|--------------|-------|
|
|
| USB 3.0 HDD → SATA SSD | 80-120 MB/s | Source-limited |
|
|
| USB 2.0 HDD → any | 20-40 MB/s | Interface bottleneck |
|
|
| NVMe → NVMe | 500+ MB/s | Only if both are internal |
|
|
|
|
### Resume Behavior
|
|
|
|
rsync automatically resumes partial transfers. Files with matching name, size, and mtime are skipped. You can safely kill and restart — only new/changed files transfer.
|
|
|
|
## Exposing Drives Over the Network (Samba)
|
|
|
|
After mounting a drive, you can share it over the network via Samba (SMB/CIFS) so Windows, macOS, and Linux machines on the LAN can access it as a network drive.
|
|
|
|
### Check Samba Status
|
|
|
|
```bash
|
|
which smbd && smbd --version # check if installed
|
|
cat /etc/samba/smb.conf # existing config
|
|
```
|
|
|
|
### Add a New Share
|
|
|
|
Samba uses a simple INI-style config at `/etc/samba/smb.conf`. Append a new share section:
|
|
|
|
```bash
|
|
sudo tee -a /etc/samba/smb.conf << 'EOF'
|
|
|
|
[share-name]
|
|
path = /mnt/drive
|
|
browseable = yes
|
|
read only = no
|
|
guest ok = yes
|
|
force user = ray
|
|
create mask = 0777
|
|
directory mask = 0777
|
|
EOF
|
|
```
|
|
|
|
| Option | Meaning |
|
|
|--------|---------|
|
|
| `guest ok = yes` | No password required on connect |
|
|
| `force user = ray` | All files written over SMB owned by `ray` |
|
|
| `create mask = 0777` | New files get full permissions |
|
|
| `directory mask = 0777` | New directories get full permissions |
|
|
|
|
### Restart & Verify
|
|
|
|
```bash
|
|
sudo systemctl restart smbd
|
|
|
|
# Verify the share shows up
|
|
smbclient -L //localhost -U ray --no-pass | grep share-name
|
|
|
|
# Find server LAN IP
|
|
ip -4 addr show | grep -oP 'inet \K[^/]+' | grep -v 127.0.0.1
|
|
```
|
|
|
|
### Connecting from Clients
|
|
|
|
| OS | How |
|
|
|----|-----|
|
|
| **Windows** | `\\192.168.x.x\share-name` in File Explorer |
|
|
| **macOS** | ⌘K → `smb://192.168.x.x/share-name` |
|
|
| **Linux** | `smb://192.168.x.x/share-name` in file manager |
|
|
|
|
### Password-Protected Shares (Alternative)
|
|
|
|
```bash
|
|
sudo smbpasswd -a ray # set SMB password (interactive)
|
|
# or from a script:
|
|
echo 'password' | sudo smbpasswd -a -s ray
|
|
```
|
|
|
|
Then use `valid users = ray` and `guest ok = no` in the share section.
|
|
|
|
### Pitfalls
|
|
|
|
- **SMB password ≠ system password.** Setting a Samba password with `smbpasswd` is a separate step from the user's Linux login password.
|
|
- **`$` in share paths:** The `$RECYCLE.BIN` directory on NTFS drives contains a literal `$`. When writing Samba configs or rsync excludes, the `$` must be quoted or escaped to prevent shell variable expansion.
|
|
- **Firewall:** If clients can't connect, check Samba ports: `sudo ufw allow samba` or `sudo ufw allow 139,445/tcp`.
|
|
|
|
## Pitfalls
|
|
|
|
- **Mounted drives are root-owned by default** — `cp`, `rsync`, `mkdir`, and any write to a freshly mounted drive will fail with `Permission denied` unless you use `sudo`. After sudo operations, subdirectories are owned by root and non-sudo writes keep failing. Either always use `sudo` for writes to mounted drives, or `sudo chown user:user /mnt/mountpoint/` after mounting to grant user-level access.
|
|
- **fstab doesn't evaluate shell commands** — `UUID=$(sudo blkid ...)` doesn't work in fstab. Use the literal UUID from `sudo blkid`.
|
|
- **External drives MUST have `nofail`** — missing external drives without `nofail` cause systemd to hang at boot (waits 90s per drive) or drop to emergency mode. Always add `nofail` and `pass=0` to fstab entries for removable/external drives.
|
|
- **Parallel rsync on USB drives** causes `I/O error` / `Broken pipe` / `socket IO error`. The USB controller can't handle concurrent reads. Use a single instance.
|
|
- **Drive re-enumeration** — `/dev/sdb` may become `/dev/sdc` after reboot or replug. Always use UUIDs in fstab.
|
|
- **Permissions gap after sudo rsync** — subsequent non-sudo runs fail on directories the first run created as root. Either always use sudo, or chown after the first run.
|
|
- **NTFS write support** — `ntfs-3g` not `ntfs3`. The kernel ntfs3 driver is experimental. Install `ntfs-3g` explicitly.
|
|
- **exFAT on modern Ubuntu** — package is `exfatprogs` (not `exfat-utils` which is deprecated). Ubuntu 24.04+ has kernel exFAT support, but still needs the tools package.
|
|
|
|
## References
|
|
|
|
- `references/drive-selection-guide.md` — SMR vs CMR guidance, speed tiers, physical/electrical compatibility checklist, OEM RAM upgrade assessment (for when the user asks "will this drive fit?" or "how does this compare to my current drive?")
|
|
- `references/rsync-best-practices.md` — rsync pattern reference for USB I/O
|
|
- `references/rayserver-shares.md` — rayserver-specific Samba share inventory (absorbed from `samba-nas`)
|