Skip to content

Commit

Permalink
selftests/filesystems: add a vfat RENAME_EXCHANGE test
Browse files Browse the repository at this point in the history
Add a test for the renameat2 RENAME_EXCHANGE support in vfat, but split it
in a tool that just does the rename exchange and a script that is run by
the kselftests framework on `make TARGETS="filesystems/fat" kselftest`.

That way the script can be easily extended to test other file operations.

The script creates a 1 MiB disk image, that is then formated with a vfat
filesystem and mounted using a loop device. That way all file operations
are done on an ephemeral filesystem.

Signed-off-by: Javier Martinez Canillas <[email protected]>
Series-changes: 2
- Call sync to flush the page cache before checking the file contents
  (Alex Larsson).

Series-cc: Alexander Larsson <[email protected]>
Series-cc: Peter Jones <[email protected]>
Series-cc: Lennart Poettering <[email protected]>
Series-cc: Colin Walters <[email protected]>
Series-cc: Alberto Ruiz <[email protected]>
Series-cc: Christian Kellner <[email protected]>
Series-cc: Chung-Chiang Cheng <[email protected]>
Series-version: 2
Cover-letter:
fat: add support for the renameat2 RENAME_EXCHANGE flag
Hello,

This series add support for the renameat2 system call RENAME_EXCHANGE flag
(which allows to atomically replace two paths) to the vfat filesystem code.

There are many use cases for this, but we are particularly interested in
making possible for vfat filesystems to be part of OSTree [0] deployments.

Currently OSTree relies on symbolic links to make the deployment updates
an atomic transactional operation. But RENAME_EXCHANGE could be used [1]
to achieve a similar level of robustness when using a vfat filesystem.

Patch #1 is just a preparatory patch to introduce the RENAME_EXCHANGE
support in patch #2 and finally patch #3 adds some kselftests to test it.

This is my first contribution to the fs/* subsystem, so I'm marking this
set as RFC, in case I got anything wrong with the patches. But they work
correctly on my local testing.

[0]: https://github.com/ostreedev/ostree
[1]: ostreedev/ostree#1649
END
  • Loading branch information
martinezjavier committed May 19, 2022
1 parent 0628841 commit fdbcb6c
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 0 deletions.
1 change: 1 addition & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -20841,6 +20841,7 @@ M: OGAWA Hirofumi <[email protected]>
S: Maintained
F: Documentation/filesystems/vfat.rst
F: fs/fat/
F: tools/testing/selftests/filesystems/fat/

VFIO DRIVER
M: Alex Williamson <[email protected]>
Expand Down
1 change: 1 addition & 0 deletions tools/testing/selftests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ TARGETS += exec
TARGETS += filesystems
TARGETS += filesystems/binderfs
TARGETS += filesystems/epoll
TARGETS += filesystems/fat
TARGETS += firmware
TARGETS += fpu
TARGETS += ftrace
Expand Down
7 changes: 7 additions & 0 deletions tools/testing/selftests/filesystems/fat/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0

TEST_PROGS := run_fat_tests.sh
TEST_GEN_PROGS_EXTENDED := rename_exchange
CFLAGS += -O2 -g -Wall -I../../../../usr/include/

include ../../lib.mk
2 changes: 2 additions & 0 deletions tools/testing/selftests/filesystems/fat/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_VFAT_FS=y
37 changes: 37 additions & 0 deletions tools/testing/selftests/filesystems/fat/rename_exchange.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Program that atomically exchanges two paths using
* the renameat2() system call RENAME_EXCHANGE flag.
*
* Copyright 2022 Red Hat Inc.
* Author: Javier Martinez Canillas <[email protected]>
*/

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

void print_usage(const char *program)
{
printf("Usage: %s [oldpath] [newpath]\n", program);
printf("Atomically exchange oldpath and newpath\n");
}

int main(int argc, char *argv[])
{
int ret;

if (argc != 3) {
print_usage(argv[0]);
exit(EXIT_FAILURE);
}

ret = renameat2(AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_EXCHANGE);
if (ret) {
perror("rename exchange failed");
exit(EXIT_FAILURE);
}

exit(EXIT_SUCCESS);
}
82 changes: 82 additions & 0 deletions tools/testing/selftests/filesystems/fat/run_fat_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Run filesystem operations tests on an 1 MiB disk image that is formatted with
# a vfat filesystem and mounted in a temporary directory using a loop device.
#
# Copyright 2022 Red Hat Inc.
# Author: Javier Martinez Canillas <[email protected]>

set -e
set -u
set -o pipefail

BASE_DIR="$(dirname $0)"
TMP_DIR="$(mktemp -d /tmp/fat_tests_tmp.XXXX)"
IMG_PATH="${TMP_DIR}/fat.img"
MNT_PATH="${TMP_DIR}/mnt"

cleanup()
{
mountpoint -q "${MNT_PATH}" && unmount_image
rm -rf "${TMP_DIR}"
}
trap cleanup SIGINT SIGTERM EXIT

create_loopback()
{
touch "${IMG_PATH}"
chattr +C "${IMG_PATH}" >/dev/null 2>&1 || true

truncate -s 1M "${IMG_PATH}"
mkfs.vfat "${IMG_PATH}" >/dev/null 2>&1
}

mount_image()
{
mkdir -p "${MNT_PATH}"
sudo mount -o loop "${IMG_PATH}" "${MNT_PATH}"
}

rename_exchange_test()
{
local rename_exchange="${BASE_DIR}/rename_exchange"
local old_path="${MNT_PATH}/old_file"
local new_path="${MNT_PATH}/new_file"

echo old | sudo tee "${old_path}" >/dev/null 2>&1
echo new | sudo tee "${new_path}" >/dev/null 2>&1
sudo "${rename_exchange}" "${old_path}" "${new_path}" >/dev/null 2>&1
sudo sync -f "${MNT_PATH}"
grep new "${old_path}" >/dev/null 2>&1
grep old "${new_path}" >/dev/null 2>&1
}

rename_exchange_subdir_test()
{
local rename_exchange="${BASE_DIR}/rename_exchange"
local dir_path="${MNT_PATH}/subdir"
local old_path="${MNT_PATH}/old_file"
local new_path="${dir_path}/new_file"

sudo mkdir -p "${dir_path}"
echo old | sudo tee "${old_path}" >/dev/null 2>&1
echo new | sudo tee "${new_path}" >/dev/null 2>&1
sudo "${rename_exchange}" "${old_path}" "${new_path}" >/dev/null 2>&1
sudo sync -f "${MNT_PATH}"
grep new "${old_path}" >/dev/null 2>&1
grep old "${new_path}" >/dev/null 2>&1
}

unmount_image()
{
sudo umount "${MNT_PATH}" &> /dev/null
}

create_loopback
mount_image
rename_exchange_test
rename_exchange_subdir_test
unmount_image

exit 0

0 comments on commit fdbcb6c

Please sign in to comment.