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 <javierm@redhat.com>
Acked-by: Muhammad Usama Anjum <usama.anjum@collabora.com>
Series-changes: 4
- Add Muhammad Usama Anjum Acked-by tag.

Series-changes: 3
- Add a .gitignore for the rename_exchange binary (Muhammad Usama Anjum).
- Include $(KHDR_INCLUDES) instead of hardcoding a relative path in Makefile
  (Muhammad Usama Anjum).

Series-changes: 2
- Call sync to flush the page cache before checking the file contents
  (Alex Larsson).

Series-cc: Alexander Larsson <alexl@redhat.com>
Series-cc: Peter Jones <pjones@redhat.com>
Series-cc: Lennart Poettering <lennart@poettering.net>
Series-cc: Colin Walters <walters@verbum.org>
Series-cc: Alberto Ruiz <aruiz@redhat.com>
Series-cc: Christian Kellner <ckellner@redhat.com>
Series-cc: Chung-Chiang Cheng <cccheng@synology.com>
Series-cc: Muhammad Usama Anjum <usama.anjum@collabora.com>
Series-version: 4
Cover-changes: 2
- Drop RFC prefix since the patches already got some review.

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, patch #2 moves some code blocks in vfat_rename() to a set of
helper functions, that can be reused by tvfat_rename_exchange() that's
added by patch #3 and finally patch #4 adds some kselftests to test it.

This is a v4 that addresses issues pointed out in the third version posted:

https://lwn.net/Articles/896359/

[0]: https://github.com/ostreedev/ostree
[1]: ostreedev/ostree#1649
END
  • Loading branch information
martinezjavier committed Jun 1, 2022
1 parent edb4451 commit 13c528b
Show file tree
Hide file tree
Showing 7 changed files with 133 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 <hirofumi@mail.parknet.co.jp>
S: Maintained
F: Documentation/filesystems/vfat.rst
F: fs/fat/
F: tools/testing/selftests/filesystems/fat/

VFIO DRIVER
M: Alex Williamson <alex.williamson@redhat.com>
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
2 changes: 2 additions & 0 deletions tools/testing/selftests/filesystems/fat/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
rename_exchange
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 $(KHDR_INCLUDES)

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 <javierm@redhat.com>
*/

#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);
}
83 changes: 83 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,83 @@
#!/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 <javierm@redhat.com>

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_test
rename_exchange_test
rename_exchange_subdir_test
unmount_image

exit 0

0 comments on commit 13c528b

Please sign in to comment.