Modifying the code to simulate a crash before resizing.
/* Only after log entry is committed, we can truncate size */
if ((ia_valid & ATTR_SIZE) && (attr->ia_size != oldsize ||
pi->i_flags & cpu_to_le32(NOVA_EOFBLOCKS_FL))) {
// nova_set_blocksize_hint(sb, inode, pi, attr->ia_size);
/* now we can freely truncate the inode */
if (oldsize >= attr_ia_size) // simulate a crash if we truncate the file to a smaller size
nova_setsize(inode, oldsize, attr->ia_size, epoch_id);
}
insmod nova.ko metadata_csum=1 data_csum=1 data_parity=1 dram_struct_csum=1
mount -t NOVA -o init,dbgmask=255 /dev/pmem0 /mnt/pmem0
touch /mnt/pmem0/foo
echo 123456789 > /mnt/pmem0/foo
# truncate the file to a smaller size
# since we modified the code, the `nova_setsize` function will not be invoked.
truncate -s 4 /mnt/pmem0/foo
# cat the image to represent the image in case of a crash
cat /dev/pmem0 > img
# remount the crash image
umount /mnt/pmem0
cat img > /dev/pmem0
mount -t NOVA -o dbgmask=255 /dev/pmem0 /mnt/pmem0
# the stat shows the size is 4 bytes and the cat shows '1234'
stat /mnt/pmem0/foo
cat /mnt/pmem0/foo
# truncate the file to a larger size
truncate -s 20 /mnt/pmem0/foo
# the cat shows '123456789', data leak.
cat /mnt/pmem0/foo
Reason
The truncate function (nova_notify_change) is not atomic.
Fix
Journaling the attribute change log and the zeroout of the last page tail, then committing them together. This fix might introduce a performance issue due to the logging of the data block in the undo-journal. We cannot solo log the attribute change log, since the resize (zeroout) could occur before the journal is committed, leading to data lost while the file is the old size.
Alternatively, declare a special log entry to indicate the completion of the resize. In recovery, if seeing the truncate log, redo the resize. It sounds like a redo log in an undo journal.
Issue
The data could leak if the system crashes during truncate.
Reproduce
The blow code snippet shows the operation to resize data blocks after appending an attribute log. https://github.com/NVSL/linux-nova/blob/976a4d1f3d5282863b23aa834e02012167be6ee2/fs/nova/inode.c#L1335-L1342
Modifying the code to simulate a crash before resizing.
Reason
The truncate function (nova_notify_change) is not atomic.
Fix
Journaling the attribute change log and the zeroout of the last page tail, then committing them together. This fix might introduce a performance issue due to the logging of the data block in the undo-journal. We cannot solo log the attribute change log, since the resize (zeroout) could occur before the journal is committed, leading to data lost while the file is the old size. Alternatively, declare a special log entry to indicate the completion of the resize. In recovery, if seeing the truncate log, redo the resize. It sounds like a redo log in an undo journal.