Given two mirrored drives:
/dev/loop1
ofzfs1.img
/dev/loop2
ofzfs2.img
If the drives contain different data, even if the copies in zfs2.img
are newer, or zfs2.img
contains extra files, ZFS always silently uses state of zfs1.img
, and overwrites all files in zfs2.img
.
In these situations, the most favourable behaviour would be to list out all the conflicting copies and let the user (or administrator) decide.
ZFS is designed to be a smart filesystem that perform automatic recovery with minimal user interference. However, ZFS should at least leave a log of its actions, especially when it decides to delete a file. I have searched extensively but found no mention of such a log.
Step to reproduce:
- Create a simple mirror.
- Write a common file to both drives, then export.
- Unmount
zfs2.img
; import with onlyzfs1.img
, modify the common file, write a new file one, export. - Mount
zfs2.img
, unmountzfs1.img
; import with onlyzfs2.img
, modify the common file, write a new file two, export.
At this stage:
zfs1.img
contains its own version of the common file, and the new file one.zfs2.img
contains its own version of the common file, and the new file two. The version of the common file inzfs2.img
is newer.zfs2.img
also contains an extra new file two.
Continuing with the reproduction:
Mount
zfs1.img
; import with bothzfs1.img
andzfs2.img
mounted, note that the version and files ofzfs1.img
are shown in the filesystem. The version and files ofzfs2.img
is not available. Export.Mount
zfs2.img
, unmountzfs1.img
; import with onlyzfs2.img
, note that the version and the new file two inzfs2.img
are silently overwritten. Export.
The issue is now reproduced. Even though the version in zfs2.img
is newer and there is a newer file in zfs2.img
, both are overwritten without any error messages.
I am still unable to find any logs mentioning this issue.
Additional step: If the first drive is replaced with a blank drive, ZFS does not overwrite drive two with the blank drive one, as normally expected. Instead, the blank drive one is populated with the data from drive two:
- Create a new blank drive, import, replace missing drive one with the newly created drive. Export.
Code to reproduce:
(1) Create a simple mirror.
print_status() {printf -- 'vvvvvvvvvvvvvvvvvvvvvvvvvvvv\n'ls -Al /zpool_test/ ; cat /zpool_test/* ; echo ; losetup ; zpool list -v ; zpool status -vprintf -- '^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'}printf '(1) : Create mirror\n'tmpdir=$(mktemp -d)cd "${tmpdir}"truncate -s 100M "${tmpdir}/zfs1.img"truncate -s 100M "${tmpdir}/zfs2.img"loop1=$(losetup -f --show "${tmpdir}/zfs1.img")loop2=$(losetup -f --show "${tmpdir}/zfs2.img")/sbin/modprobe zfszpool create zpool_test mirror "${loop1}" "${loop2}"
(2) Write the common file to both drives.
printf '(2) : Write common to both\n'echo 'common'> /zpool_test/file_commonprint_statuszpool export zpool_test
(3) Only in drive one: Modify the common file & Add a new file one.
printf '(3) : Modify & Add only to one\n'losetup -d "${loop2}"zpool import zpool_testecho 'append one'>> /zpool_test/file_commonecho 'new one'> /zpool_test/file_new_1print_statuszpool export zpool_test
(4) Only in drive two: Modify the common file & Add a new file two.
printf '(4) : Modify & Add only to two\n'loop2=$(losetup -f --show "${tmpdir}/zfs2.img")losetup -d "${loop1}"zpool import zpool_testecho 'append two'>> /zpool_test/file_commonecho 'new two'> /zpool_test/file_new_2print_statuszpool export zpool_test
(5) Import both. Shows only the version and files in zfs1.img
.
printf '(5) : import both one and two\n'loop1=$(losetup -f --show "${tmpdir}/zfs1.img")zpool import zpool_testprint_statuszpool export zpool_test
(6) Import only two. Check zfs2.img
is overwritten silently without any logs.
printf '(6) : import only two\n'losetup -d "${loop1}"zpool import zpool_testprint_statuszpool export zpool_test
(7) If drive one is replaced with blank drive, blank drive is overwritten.
printf '(7) : if one is replaced with blank\n'truncate -s 100M "${tmpdir}/zfs_new_1.img"loop4=$(losetup --show /dev/loop4 "${tmpdir}/zfs_new_1.img")zpool import zpool_testzpool replace zpool_test /dev/loop1 /dev/loop4sleep 1print_statuszpool export zpool_test
Last but not least, handy clean up script is also provided:
zpool import zpool_testzpool destroy zpool_test# losetup -d "${loop1}" "${loop2}"losetup -d "${loop2}" "${loop4}"rm -rf "${tmpdir}"