3.8 KiB
U-NAS In-Pool Share Move
Use this procedure to move a large folder between UniFi Drive shares in the same Btrfs storage pool without temporarily duplicating all its data.
This uses Btrfs reflinks and metadata-only validation. It does not checksum file contents. Stop all writers first and keep a separate backup of important data.
1. Set the Paths
UniFi Drive user data is below each share's .data directory. Never operate on
the share-level .trashcan, .uploads, .snapshot, or other service folders.
BASE='/volume/POOL_UUID/.srv/.unifi-drive'
SRC="$BASE/SOURCE_SHARE/.data/PATH_TO_FOLDER"
DST="$BASE/DESTINATION_SHARE/.data/FOLDER_NAME"
Confirm the paths before continuing:
printf 'SOURCE: %s\nDESTINATION: %s\n' "$SRC" "$DST"
test -d "$SRC" || { echo 'ERROR: source does not exist'; false; }
test ! -e "$DST" || { echo 'ERROR: destination already exists'; false; }
du -sh "$SRC"
df -hT "$SRC" "$(dirname "$DST")"
fuser -vm "$SRC"
Stop applications, containers, SMB/NFS clients, downloaders, and scanners that may modify the source. Proceed only when the destination is absent and the source is idle.
2. Confirm Btrfs Reflinks Work
Select a test file and clone it into the destination share:
SAMPLE="$(find "$SRC" -type f -size +10M -print -quit)"
REFTEST="$(dirname "$DST")/.reflink-test-$$"
cp --reflink=always --preserve=all -- "$SAMPLE" "$REFTEST"
btrfs filesystem du -s -- "$SAMPLE" "$REFTEST"
rm -- "$REFTEST"
Continue only if cp succeeds and btrfs filesystem du reports the test files
as shared data with little or no exclusive allocation.
3. Reflink-Clone the Folder
Start a persistent session:
tmux new-session -s unas-move
Recreate BASE, SRC, and DST inside tmux, then run:
mkdir -- "$DST"
cp -a \
--reflink=always \
--one-file-system \
-- "$SRC/." "$DST/" \
2>/root/unas-reflink-errors.log
CP_RC=$?
echo "cp exit code: $CP_RC"
Detach with Ctrl-B, then D. Reattach with:
tmux attach-session -t unas-move
If interrupted, rerun the same cp command. Do not continue unless its exit
code is 0 and the error log is empty:
cat /root/unas-reflink-errors.log
btrfs filesystem du -s -- "$SRC" "$DST"
sync
4. Compare Metadata
Create type, path, size, modification-time, and symlink-target manifests. This is much faster than checksumming file contents:
(cd "$SRC" && find . ! -type d -printf '%y\t%P\t%s\t%T@\t%l\n' | LC_ALL=C sort) \
> /root/unas-source-manifest.txt
(cd "$DST" && find . ! -type d -printf '%y\t%P\t%s\t%T@\t%l\n' | LC_ALL=C sort) \
> /root/unas-destination-manifest.txt
diff -u \
/root/unas-source-manifest.txt \
/root/unas-destination-manifest.txt
Do not continue if diff prints differences.
5. Remove Synchronized Source Files
rsync -aXHvi \
--remove-source-files \
--info=progress2,stats2 \
--log-file=/root/unas-move.log \
-- "$SRC/" "$DST/"
RSYNC_RC=$?
echo "rsync exit code: $RSYNC_RC"
This metadata comparison avoids reading all file contents. On the tested U-NAS rsync version, matching reflinked files are treated as synchronized and removed from the source even when the transferred-byte count is zero.
Continue only if the exit code is 0.
6. Clean Up and Verify
Confirm that no source files or links remain:
find "$SRC" -mindepth 1 ! -type d -print
If that command prints nothing, remove empty source directories:
find "$SRC" -depth -type d -empty -delete
Verify the result:
test ! -e "$SRC" && echo 'Source removed successfully'
test -d "$DST" && echo 'Destination retained successfully'
du -sh "$DST"
Confirm the destination through UniFi Drive or a normal share client before
restarting applications. Never use rm -rf as a substitute for these checks.