mirror of
https://github.com/linux-apfs/apfstests.git
synced 2026-05-01 15:01:44 -07:00
src/open_by_handle: flexible usage options
More usage options for testing open_by_handle, which are needed for testing stable handles across copy up in overlayfs. usage: open_by_handle [-c|-l|-u|-d] <test_dir> [num_files] Examples: 1. Create test set of N files and try to get their NFS handles: open_by_handle -c <test_dir> [N] This is used by new helper _require_exportfs() to check if filesystem supports exportfs 2. Get file handles for existing test set, drop caches and try to open all files by handle: open_by_handle <test_dir> [N] 3. Get file handles for existing test set, unlink all test files, drop caches, try to open all files by handle and expect ESTALE: open_by_handle -d <test_dir> [N] 4. Get file handles for existing test set, hardlink all test files, then unlink the original files, drop caches and try to open all files by handle (should work): open_by_handle -l <test_dir> [N] open_by_handle -u <test_dir> [N] This test is done with 2 invocations of the program, first to hardlink (-l) and then to unlink the originals (-u), because we would like to be able to perform the hardlinks on overlay lower layer and unlink on upper layer. NOTE that open_by_handle -u doesn't check if the files are hardlinked, it just assumes that they are. If they are not then the test will fail, because file handles would be stale. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Eryu Guan <eguan@redhat.com> Signed-off-by: Eryu Guan <eguan@redhat.com>
This commit is contained in:
committed by
Eryu Guan
parent
b34b378566
commit
5210726732
@@ -2857,6 +2857,16 @@ _require_freeze()
|
|||||||
[ $result -eq 0 ] || _notrun "$FSTYP does not support freezing"
|
[ $result -eq 0 ] || _notrun "$FSTYP does not support freezing"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Does NFS export work on this fs?
|
||||||
|
_require_exportfs()
|
||||||
|
{
|
||||||
|
_require_test_program "open_by_handle"
|
||||||
|
mkdir -p "$TEST_DIR"/exportfs_test
|
||||||
|
$here/src/open_by_handle -c "$TEST_DIR"/exportfs_test 2>&1 \
|
||||||
|
|| _notrun "$FSTYP does not support NFS export"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Does shutdown work on this fs?
|
# Does shutdown work on this fs?
|
||||||
_require_scratch_shutdown()
|
_require_scratch_shutdown()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ note the dependency with:
|
|||||||
Contents:
|
Contents:
|
||||||
|
|
||||||
- af_unix -- Create an AF_UNIX socket
|
- af_unix -- Create an AF_UNIX socket
|
||||||
|
- open_by_handle -- open_by_handle_at syscall exercise
|
||||||
- stat_test -- statx syscall exercise
|
- stat_test -- statx syscall exercise
|
||||||
- xfs_io -- General I/O operation exercise
|
- xfs_io -- General I/O operation exercise
|
||||||
|
|
||||||
@@ -28,6 +29,15 @@ af_unix
|
|||||||
|
|
||||||
The af_unix program creates an AF_UNIX socket at the given location.
|
The af_unix program creates an AF_UNIX socket at the given location.
|
||||||
|
|
||||||
|
open_by_handle
|
||||||
|
|
||||||
|
The open_by_handle program exercises the open_by_handle_at() system
|
||||||
|
call. It can check if file handles are valid or stale after certain
|
||||||
|
filesystem operations.
|
||||||
|
|
||||||
|
See also:
|
||||||
|
_require_exportfs
|
||||||
|
|
||||||
stat_test
|
stat_test
|
||||||
|
|
||||||
The stat_test program is primarily designed to exercise the statx()
|
The stat_test program is primarily designed to exercise the statx()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ they have. This is done with _require_<xxx> macros, which may take parameters.
|
|||||||
(2) Filesystem capability requirements.
|
(2) Filesystem capability requirements.
|
||||||
|
|
||||||
_require_chattr <letters>
|
_require_chattr <letters>
|
||||||
|
_require_exportfs
|
||||||
|
|
||||||
(3) System call requirements.
|
(3) System call requirements.
|
||||||
|
|
||||||
@@ -86,6 +87,12 @@ _require_chattr <letters>
|
|||||||
tests to see if setting the append-only and immutable attributes on a file
|
tests to see if setting the append-only and immutable attributes on a file
|
||||||
(chattr +a +i) is supported.
|
(chattr +a +i) is supported.
|
||||||
|
|
||||||
|
_require_exportfs
|
||||||
|
|
||||||
|
The test requires that the $TEST_DEV filesystem supports NFS export.
|
||||||
|
The test also requires the use of the open_by_handle_at() system call and
|
||||||
|
will be skipped if it isn't available in the kernel.
|
||||||
|
|
||||||
|
|
||||||
========================
|
========================
|
||||||
SYSTEM CALL REQUIREMENTS
|
SYSTEM CALL REQUIREMENTS
|
||||||
|
|||||||
+138
-20
@@ -25,6 +25,46 @@
|
|||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
usage: open_by_handle [-c|-l|-u|-d] <test_dir> [num_files]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
1. Create test set of N files and try to get their NFS handles:
|
||||||
|
|
||||||
|
open_by_handle -c <test_dir> [N]
|
||||||
|
|
||||||
|
This is used by new helper _require_exportfs() to check
|
||||||
|
if filesystem supports exportfs
|
||||||
|
|
||||||
|
2. Get file handles for existing test set, drop caches and try to
|
||||||
|
open all files by handle:
|
||||||
|
|
||||||
|
open_by_handle <test_dir> [N]
|
||||||
|
|
||||||
|
3. Get file handles for existing test set, unlink all test files,
|
||||||
|
drop caches, try to open all files by handle and expect ESTALE:
|
||||||
|
|
||||||
|
open_by_handle -d <test_dir> [N]
|
||||||
|
|
||||||
|
4. Get file handles for existing test set, hardlink all test files,
|
||||||
|
then unlink the original files, drop caches and try to open all
|
||||||
|
files by handle (should work):
|
||||||
|
|
||||||
|
open_by_handle -l <test_dir> [N]
|
||||||
|
open_by_handle -u <test_dir> [N]
|
||||||
|
|
||||||
|
This test is done with 2 invocations of the program, first to
|
||||||
|
hardlink (-l) and then to unlink the originals (-u), because
|
||||||
|
we would like to be able to perform the hardlinks on overlay
|
||||||
|
lower layer and unlink on upper layer.
|
||||||
|
|
||||||
|
NOTE that open_by_handle -u doesn't check if the files are
|
||||||
|
hardlinked, it just assumes that they are. If they are not
|
||||||
|
then the test will fail, because file handles would be stale.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -35,41 +75,85 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
|
|
||||||
#define NUMFILES 1024
|
#define MAXFILES 1024
|
||||||
|
|
||||||
struct handle {
|
struct handle {
|
||||||
struct file_handle fh;
|
struct file_handle fh;
|
||||||
unsigned char fid[MAX_HANDLE_SZ];
|
unsigned char fid[MAX_HANDLE_SZ];
|
||||||
} handle[NUMFILES];
|
} handle[MAXFILES];
|
||||||
|
|
||||||
|
void usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "usage: open_by_handle [-c|-l|-u|-d] <test_dir> [num_files]\n");
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n");
|
||||||
|
fprintf(stderr, "open_by_handle <test_dir> [N] - get file handles of test files, drop caches and try to open by handle\n");
|
||||||
|
fprintf(stderr, "open_by_handle -l <test_dir> [N] - create hardlinks to test files, drop caches and try to open by handle\n");
|
||||||
|
fprintf(stderr, "open_by_handle -u <test_dir> [N] - unlink (hardlinked) test files, drop caches and try to open by handle\n");
|
||||||
|
fprintf(stderr, "open_by_handle -d <test_dir> [N] - unlink test files and hardlinks, drop caches and try to open by handle\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int i;
|
int i, c;
|
||||||
int fd;
|
int fd;
|
||||||
int ret;
|
int ret;
|
||||||
int failed = 0;
|
int failed = 0;
|
||||||
char fname[PATH_MAX];
|
char fname[PATH_MAX];
|
||||||
|
char fname2[PATH_MAX];
|
||||||
char *test_dir;
|
char *test_dir;
|
||||||
int mount_fd, mount_id;
|
int mount_fd, mount_id;
|
||||||
|
int numfiles = 1;
|
||||||
|
int create = 0, delete = 0, nlink = 1;
|
||||||
|
|
||||||
if (argc != 2) {
|
if (argc < 2 || argc > 4)
|
||||||
fprintf(stderr, "usage: open_by_handle <test_dir>\n");
|
usage();
|
||||||
return EXIT_FAILURE;
|
|
||||||
|
while ((c = getopt(argc, argv, "clud")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
create = 1;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
nlink = 2;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
delete = 1;
|
||||||
|
nlink = 1;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
delete = 1;
|
||||||
|
nlink = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "illegal option '%s'\n", argv[optind]);
|
||||||
|
case 'h':
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (optind == argc || optind > 2)
|
||||||
|
usage();
|
||||||
|
test_dir = argv[optind++];
|
||||||
|
if (optind < argc)
|
||||||
|
numfiles = atoi(argv[optind]);
|
||||||
|
if (!numfiles || numfiles > MAXFILES) {
|
||||||
|
fprintf(stderr, "illegal value '%s' for num_files\n", argv[optind]);
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
test_dir = argv[1];
|
|
||||||
mount_fd = open(test_dir, O_RDONLY|O_DIRECTORY);
|
mount_fd = open(test_dir, O_RDONLY|O_DIRECTORY);
|
||||||
if (mount_fd < 0) {
|
if (mount_fd < 0) {
|
||||||
perror("open test_dir");
|
perror(test_dir);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* create a large number of files to force allocation of new inode
|
* Create the test files and remove leftover hardlinks from previous run
|
||||||
* chunks on disk.
|
|
||||||
*/
|
*/
|
||||||
for (i=0; i < NUMFILES; i++) {
|
for (i=0; create && i < numfiles; i++) {
|
||||||
sprintf(fname, "%s/file%06d", test_dir, i);
|
sprintf(fname, "%s/file%06d", test_dir, i);
|
||||||
|
sprintf(fname2, "%s/link%06d", test_dir, i);
|
||||||
fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
|
fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
printf("Warning (%s,%d), open(%s) failed.\n", __FILE__, __LINE__, fname);
|
printf("Warning (%s,%d), open(%s) failed.\n", __FILE__, __LINE__, fname);
|
||||||
@@ -77,13 +161,19 @@ int main(int argc, char **argv)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
|
/* blow up leftovers hardlinks if they exist */
|
||||||
|
ret = unlink(fname2);
|
||||||
|
if (ret < 0 && errno != ENOENT) {
|
||||||
|
perror("unlink");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sync to get the new inodes to hit the disk */
|
/* sync to get the new inodes to hit the disk */
|
||||||
sync();
|
sync();
|
||||||
|
|
||||||
/* create the handles */
|
/* create the handles */
|
||||||
for (i=0; i < NUMFILES; i++) {
|
for (i=0; i < numfiles; i++) {
|
||||||
sprintf(fname, "%s/file%06d", test_dir, i);
|
sprintf(fname, "%s/file%06d", test_dir, i);
|
||||||
handle[i].fh.handle_bytes = MAX_HANDLE_SZ;
|
handle[i].fh.handle_bytes = MAX_HANDLE_SZ;
|
||||||
ret = name_to_handle_at(AT_FDCWD, fname, &handle[i].fh, &mount_id, 0);
|
ret = name_to_handle_at(AT_FDCWD, fname, &handle[i].fh, &mount_id, 0);
|
||||||
@@ -93,14 +183,37 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unlink the files */
|
/* after creating test set only check that fs supports exportfs */
|
||||||
for (i=0; i < NUMFILES; i++) {
|
if (create)
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
/* hardlink the files */
|
||||||
|
for (i=0; nlink > 1 && i < numfiles; i++) {
|
||||||
sprintf(fname, "%s/file%06d", test_dir, i);
|
sprintf(fname, "%s/file%06d", test_dir, i);
|
||||||
|
sprintf(fname2, "%s/link%06d", test_dir, i);
|
||||||
|
ret = link(fname, fname2);
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("link");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unlink the files */
|
||||||
|
for (i=0; delete && i < numfiles; i++) {
|
||||||
|
sprintf(fname, "%s/file%06d", test_dir, i);
|
||||||
|
sprintf(fname2, "%s/link%06d", test_dir, i);
|
||||||
ret = unlink(fname);
|
ret = unlink(fname);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
perror("unlink");
|
perror("unlink");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
/* with -d flag, delete the hardlink if it exists */
|
||||||
|
if (!nlink)
|
||||||
|
ret = unlink(fname2);
|
||||||
|
if (ret < 0 && errno != ENOENT) {
|
||||||
|
perror("unlink");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sync to get log forced for unlink transactions to hit the disk */
|
/* sync to get log forced for unlink transactions to hit the disk */
|
||||||
@@ -121,20 +234,25 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* now try to open the files by the stored handles. Expecting ENOENT
|
* now try to open the files by the stored handles. Expecting ESTALE
|
||||||
* for all of them.
|
* if all files and their hardlinks have been unlinked.
|
||||||
*/
|
*/
|
||||||
for (i=0; i < NUMFILES; i++) {
|
for (i=0; i < numfiles; i++) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
fd = open_by_handle_at(mount_fd, &handle[i].fh, O_RDWR);
|
fd = open_by_handle_at(mount_fd, &handle[i].fh, O_RDWR);
|
||||||
if (fd < 0 && (errno == ENOENT || errno == ESTALE)) {
|
if (nlink && fd >= 0) {
|
||||||
|
close(fd);
|
||||||
|
continue;
|
||||||
|
} else if (!nlink && fd < 0 && (errno == ENOENT || errno == ESTALE)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
printf("open_by_handle(%d) opened an unlinked file!\n", i);
|
printf("open_by_handle(%d) opened an unlinked file!\n", i);
|
||||||
close(fd);
|
close(fd);
|
||||||
} else
|
} else {
|
||||||
printf("open_by_handle(%d) returned %d incorrectly on an unlinked file!\n", i, errno);
|
printf("open_by_handle(%d) returned %d incorrectly on %s file!\n", i, errno,
|
||||||
|
nlink ? "a linked" : "an unlinked");
|
||||||
|
}
|
||||||
failed++;
|
failed++;
|
||||||
}
|
}
|
||||||
if (failed)
|
if (failed)
|
||||||
|
|||||||
Reference in New Issue
Block a user