From dffdbb4acc84d567c6e1c97d6c89a93ca9386184 Mon Sep 17 00:00:00 2001 From: Boyang Xue Date: Mon, 2 Dec 2024 18:09:07 +0800 Subject: [PATCH] gating: sync with the gating test for c10s Resolves: RHEL-69609 Signed-off-by: Boyang Xue --- gating.yaml | 4 +- plan.fmf | 8 + plans/gating.fmf | 14 - tests/fuse-sanity-test/Makefile | 70 ++ tests/fuse-sanity-test/PURPOSE | 3 + tests/fuse-sanity-test/fusetest.c | 1492 +++++++++++++++++++++++++++ tests/fuse-sanity-test/fusexmp_fh.c | 539 ++++++++++ tests/fuse-sanity-test/main.fmf | 17 + tests/fuse-sanity-test/runtest.sh | 136 +++ 9 files changed, 2266 insertions(+), 17 deletions(-) create mode 100644 plan.fmf delete mode 100644 plans/gating.fmf create mode 100644 tests/fuse-sanity-test/Makefile create mode 100644 tests/fuse-sanity-test/PURPOSE create mode 100644 tests/fuse-sanity-test/fusetest.c create mode 100644 tests/fuse-sanity-test/fusexmp_fh.c create mode 100644 tests/fuse-sanity-test/main.fmf create mode 100755 tests/fuse-sanity-test/runtest.sh diff --git a/gating.yaml b/gating.yaml index ad4b29d..dd6ed2b 100644 --- a/gating.yaml +++ b/gating.yaml @@ -1,7 +1,5 @@ --- !Policy -product_versions: - - rhel-10 + decision_context: osci_compose_gate rules: - !PassingTestCaseRule {test_case_name: osci.brew-build.tier0.functional} - diff --git a/plan.fmf b/plan.fmf new file mode 100644 index 0000000..ddb26fa --- /dev/null +++ b/plan.fmf @@ -0,0 +1,8 @@ +summary: fuse gating test +execute: + how: tmt +discover: + how: fmf + test: [ + "fuse-sanity-test", + ] diff --git a/plans/gating.fmf b/plans/gating.fmf deleted file mode 100644 index ec5fa4f..0000000 --- a/plans/gating.fmf +++ /dev/null @@ -1,14 +0,0 @@ -summary: fuse gating test -discover: - how: fmf - url: https://gitlab.cee.redhat.com/kernel-qe/kernel.git - test: [ - "filesystems/fuse/gating/fuse2", - ] -environment: - FSTYP: ext4 - TEST_GROUP: fuse - AVC_ERROR: +no_avc_check -execute: - how: tmt - diff --git a/tests/fuse-sanity-test/Makefile b/tests/fuse-sanity-test/Makefile new file mode 100644 index 0000000..a404aeb --- /dev/null +++ b/tests/fuse-sanity-test/Makefile @@ -0,0 +1,70 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Makefile of /kernel/filesystems/fuse/gating/fuse2 +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2022 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Author and description of the test +AUTHOR=Zorro Lang +DESCRIPTION=The gating test case of fuse-2.x + +# The name of the test. +export TEST=/kernel/filesystems/fuse/gating/fuse2 + +# Version of the test. Used with make tag. +export TESTVERSION=1.0 + +# data files, .c files, scripts anything needed to either compile the test and/or run it. +FILES=$(METADATA) runtest.sh PURPOSE fusexmp_fh.c fusetest.c + +run: $(FILES) build + ./runtest.sh + +build: $(BUILT_FILES) $(FILES) + chmod a+x ./runtest.sh + +clean: + rm -f *~ + +# Include Common Makefile +include /usr/share/rhts/lib/rhts-make.include + +# Generate the testinfo.desc here: +$(METADATA): Makefile + @echo "Owner: $(AUTHOR)" > $(METADATA) + @echo "Name: $(TEST)" >> $(METADATA) + @echo "Path: $(TEST_DIR)" >> $(METADATA) + @echo "License: GPLv2" >> $(METADATA) + @echo "TestVersion: $(TESTVERSION)" >> $(METADATA) + @echo "Description: $(DESCRIPTION)" >> $(METADATA) + @echo "TestTime: 12h" >> $(METADATA) + @echo "RunFor: fuse" >> $(METADATA) + @echo "Type: Regression" >> $(METADATA) + @echo "Requires: fuse" >> $(METADATA) + @echo "Requires: fuse-devel" >> $(METADATA) + @echo "Requires: fuse-libs" >> $(METADATA) + @echo "Requires: pkgconf-pkg-config" >> $(METADATA) + @echo "Requires: gcc" >> $(METADATA) + @echo "Requires: glibc-headers" >> $(METADATA) + @echo "Requires: procps-ng" >> $(METADATA) + rhts-lint $(METADATA) +# The include package trys to install all the potential dependencies. +# Not each them are available on all RHEL old/new systems. diff --git a/tests/fuse-sanity-test/PURPOSE b/tests/fuse-sanity-test/PURPOSE new file mode 100644 index 0000000..f0632c4 --- /dev/null +++ b/tests/fuse-sanity-test/PURPOSE @@ -0,0 +1,3 @@ +Description: + +This's a simple gating test case for fuse-2.x. \ No newline at end of file diff --git a/tests/fuse-sanity-test/fusetest.c b/tests/fuse-sanity-test/fusetest.c new file mode 100644 index 0000000..3d84016 --- /dev/null +++ b/tests/fuse-sanity-test/fusetest.c @@ -0,0 +1,1492 @@ +/* + * Copy from upstream libfuse/test/test.c (fuse_2_9_bugfix branch), then + * make some changes. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static char testfile[1024]; +static char testfile2[1024]; +static char testdir[1024]; +static char testdir2[1024]; +static char subfile[1024]; +static char testname[256]; +static char testdata[] = "abcdefghijklmnopqrstuvwxyz"; +static char testdata2[] = "1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./"; +static const char *testdir_files[] = { "f1", "f2", NULL}; +static long seekdir_offsets[4]; +static char zerodata[4096]; +static int testdatalen = sizeof(testdata) - 1; +static int testdata2len = sizeof(testdata2) - 1; +static unsigned int testnum = 1; +static unsigned int select_test = 0; +static unsigned int skip_test = 0; + +#define MAX_ENTRIES 1024 + +static void test_perror(const char *func, const char *msg) +{ + fprintf(stderr, "%s %s() - %s: %s\n", testname, func, msg, + strerror(errno)); +} + +static void test_error(const char *func, const char *msg, ...) + __attribute__ ((format (printf, 2, 3))); + +static void __start_test(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +static void test_error(const char *func, const char *msg, ...) +{ + va_list ap; + fprintf(stderr, "%s %s() - ", testname, func); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void success(void) +{ + fprintf(stderr, "%s OK\n", testname); +} + +static void __start_test(const char *fmt, ...) +{ + unsigned int n; + va_list ap; + n = sprintf(testname, "%3i [", testnum++); + va_start(ap, fmt); + n += vsprintf(testname + n, fmt, ap); + va_end(ap); + sprintf(testname + n, "]"); +} + +#define start_test(msg, args...) { \ + if ((select_test && testnum != select_test) || \ + (testnum == skip_test)) { \ + testnum++; \ + return 0; \ + } \ + __start_test(msg, ##args); \ +} + +#define PERROR(msg) test_perror(__FUNCTION__, msg) +#define ERROR(msg, args...) test_error(__FUNCTION__, msg, ##args) + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static int check_size(const char *path, int len) +{ + struct stat stbuf; + int res = stat(path, &stbuf); + if (res == -1) { + PERROR("stat"); + return -1; + } + if (stbuf.st_size != len) { + ERROR("length %u instead of %u", (int) stbuf.st_size, + (int) len); + return -1; + } + return 0; +} + +static int fcheck_size(int fd, int len) +{ + struct stat stbuf; + int res = fstat(fd, &stbuf); + if (res == -1) { + PERROR("fstat"); + return -1; + } + if (stbuf.st_size != len) { + ERROR("length %u instead of %u", (int) stbuf.st_size, + (int) len); + return -1; + } + return 0; +} + +static int check_type(const char *path, mode_t type) +{ + struct stat stbuf; + int res = lstat(path, &stbuf); + if (res == -1) { + PERROR("lstat"); + return -1; + } + if ((stbuf.st_mode & S_IFMT) != type) { + ERROR("type 0%o instead of 0%o", stbuf.st_mode & S_IFMT, type); + return -1; + } + return 0; +} + +static int fcheck_type(int fd, mode_t type) +{ + struct stat stbuf; + int res = fstat(fd, &stbuf); + if (res == -1) { + PERROR("fstat"); + return -1; + } + if ((stbuf.st_mode & S_IFMT) != type) { + ERROR("type 0%o instead of 0%o", stbuf.st_mode & S_IFMT, type); + return -1; + } + return 0; +} + +static int check_mode(const char *path, mode_t mode) +{ + struct stat stbuf; + int res = lstat(path, &stbuf); + if (res == -1) { + PERROR("lstat"); + return -1; + } + if ((stbuf.st_mode & 07777) != mode) { + ERROR("mode 0%o instead of 0%o", stbuf.st_mode & 07777, mode); + return -1; + } + return 0; +} + +static int fcheck_mode(int fd, mode_t mode) +{ + struct stat stbuf; + int res = fstat(fd, &stbuf); + if (res == -1) { + PERROR("fstat"); + return -1; + } + if ((stbuf.st_mode & 07777) != mode) { + ERROR("mode 0%o instead of 0%o", stbuf.st_mode & 07777, mode); + return -1; + } + return 0; +} + +static int check_times(const char *path, time_t atime, time_t mtime) +{ + int err = 0; + struct stat stbuf; + int res = lstat(path, &stbuf); + if (res == -1) { + PERROR("lstat"); + return -1; + } + if (stbuf.st_atime != atime) { + ERROR("atime %li instead of %li", stbuf.st_atime, atime); + err--; + } + if (stbuf.st_mtime != mtime) { + ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime); + err--; + } + if (err) + return -1; + + return 0; +} + +#if 0 +static int fcheck_times(int fd, time_t atime, time_t mtime) +{ + int err = 0; + struct stat stbuf; + int res = fstat(fd, &stbuf); + if (res == -1) { + PERROR("fstat"); + return -1; + } + if (stbuf.st_atime != atime) { + ERROR("atime %li instead of %li", stbuf.st_atime, atime); + err--; + } + if (stbuf.st_mtime != mtime) { + ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime); + err--; + } + if (err) + return -1; + + return 0; +} +#endif + +static int check_nlink(const char *path, nlink_t nlink) +{ + struct stat stbuf; + int res = lstat(path, &stbuf); + if (res == -1) { + PERROR("lstat"); + return -1; + } + if (stbuf.st_nlink != nlink) { + ERROR("nlink %li instead of %li", (long) stbuf.st_nlink, + (long) nlink); + return -1; + } + return 0; +} + +static int fcheck_nlink(int fd, nlink_t nlink) +{ + struct stat stbuf; + int res = fstat(fd, &stbuf); + if (res == -1) { + PERROR("fstat"); + return -1; + } + if (stbuf.st_nlink != nlink) { + ERROR("nlink %li instead of %li", (long) stbuf.st_nlink, + (long) nlink); + return -1; + } + return 0; +} + +static int check_nonexist(const char *path) +{ + struct stat stbuf; + int res = lstat(path, &stbuf); + if (res == 0) { + ERROR("file should not exist"); + return -1; + } + if (errno != ENOENT) { + ERROR("file should not exist: %s", strerror(errno)); + return -1; + } + return 0; +} + +static int check_buffer(const char *buf, const char *data, unsigned len) +{ + if (memcmp(buf, data, len) != 0) { + ERROR("data mismatch"); + return -1; + } + return 0; +} + +static int check_data(const char *path, const char *data, int offset, + unsigned len) +{ + char buf[4096]; + int res; + int fd = open(path, O_RDONLY); + if (fd == -1) { + PERROR("open"); + return -1; + } + if (lseek(fd, offset, SEEK_SET) == (off_t) -1) { + PERROR("lseek"); + close(fd); + return -1; + } + while (len) { + int rdlen = len < sizeof(buf) ? len : sizeof(buf); + res = read(fd, buf, rdlen); + if (res == -1) { + PERROR("read"); + close(fd); + return -1; + } + if (res != rdlen) { + ERROR("short read: %u instead of %u", res, rdlen); + close(fd); + return -1; + } + if (check_buffer(buf, data, rdlen) != 0) { + close(fd); + return -1; + } + data += rdlen; + len -= rdlen; + } + res = close(fd); + if (res == -1) { + PERROR("close"); + return -1; + } + return 0; +} + +static int fcheck_data(int fd, const char *data, int offset, + unsigned len) +{ + char buf[4096]; + int res; + if (lseek(fd, offset, SEEK_SET) == (off_t) -1) { + PERROR("lseek"); + return -1; + } + while (len) { + int rdlen = len < sizeof(buf) ? len : sizeof(buf); + res = read(fd, buf, rdlen); + if (res == -1) { + PERROR("read"); + return -1; + } + if (res != rdlen) { + ERROR("short read: %u instead of %u", res, rdlen); + return -1; + } + if (check_buffer(buf, data, rdlen) != 0) { + return -1; + } + data += rdlen; + len -= rdlen; + } + return 0; +} + +static int check_dir_contents(const char *path, const char **contents) +{ + int i; + int res; + int err = 0; + int found[MAX_ENTRIES]; + const char *cont[MAX_ENTRIES]; + DIR *dp; + + for (i = 0; contents[i]; i++) { + assert(i < MAX_ENTRIES - 3); + found[i] = 0; + cont[i] = contents[i]; + } + found[i] = 0; + cont[i++] = "."; + found[i] = 0; + cont[i++] = ".."; + cont[i] = NULL; + + dp = opendir(path); + if (dp == NULL) { + PERROR("opendir"); + return -1; + } + memset(found, 0, sizeof(found)); + while(1) { + struct dirent *de; + errno = 0; + de = readdir(dp); + if (de == NULL) { + if (errno) { + PERROR("readdir"); + closedir(dp); + return -1; + } + break; + } + for (i = 0; cont[i] != NULL; i++) { + assert(i < MAX_ENTRIES); + if (strcmp(cont[i], de->d_name) == 0) { + if (found[i]) { + ERROR("duplicate entry <%s>", + de->d_name); + err--; + } else + found[i] = 1; + break; + } + } + if (!cont[i]) { + ERROR("unexpected entry <%s>", de->d_name); + err --; + } + } + for (i = 0; cont[i] != NULL; i++) { + if (!found[i]) { + ERROR("missing entry <%s>", cont[i]); + err--; + } + } + res = closedir(dp); + if (res == -1) { + PERROR("closedir"); + return -1; + } + if (err) + return -1; + + return 0; +} + +static int create_file(const char *path, const char *data, int len) +{ + int res; + int fd; + + unlink(path); + fd = creat(path, 0644); + if (fd == -1) { + PERROR("creat"); + return -1; + } + if (len) { + res = write(fd, data, len); + if (res == -1) { + PERROR("write"); + close(fd); + return -1; + } + if (res != len) { + ERROR("write is short: %u instead of %u", res, len); + close(fd); + return -1; + } + } + res = close(fd); + if (res == -1) { + PERROR("close"); + return -1; + } + res = check_type(path, S_IFREG); + if (res == -1) + return -1; + res = check_mode(path, 0644); + if (res == -1) + return -1; + res = check_nlink(path, 1); + if (res == -1) + return -1; + res = check_size(path, len); + if (res == -1) + return -1; + + if (len) { + res = check_data(path, data, 0, len); + if (res == -1) + return -1; + } + + return 0; +} + +static int cleanup_dir(const char *path, const char **dir_files, int quiet) +{ + int i; + int err = 0; + + for (i = 0; dir_files[i]; i++) { + int res; + char fpath[1024]; + sprintf(fpath, "%s/%s", path, dir_files[i]); + res = unlink(fpath); + if (res == -1 && !quiet) { + PERROR("unlink"); + err --; + } + } + if (err) + return -1; + + return 0; +} + +static int create_dir(const char *path, const char **dir_files) +{ + int res; + int i; + + rmdir(path); + res = mkdir(path, 0755); + if (res == -1) { + PERROR("mkdir"); + return -1; + } + res = check_type(path, S_IFDIR); + if (res == -1) + return -1; + res = check_mode(path, 0755); + if (res == -1) + return -1; + + for (i = 0; dir_files[i]; i++) { + char fpath[1024]; + sprintf(fpath, "%s/%s", path, dir_files[i]); + res = create_file(fpath, "", 0); + if (res == -1) { + cleanup_dir(path, dir_files, 1); + return -1; + } + } + res = check_dir_contents(path, dir_files); + if (res == -1) { + cleanup_dir(path, dir_files, 1); + return -1; + } + + return 0; +} + +static int test_truncate(int len) +{ + const char *data = testdata; + int datalen = testdatalen; + int res; + + start_test("truncate(%u)", (int) len); + res = create_file(testfile, data, datalen); + if (res == -1) + return -1; + + res = truncate(testfile, len); + if (res == -1) { + PERROR("truncate"); + return -1; + } + res = check_size(testfile, len); + if (res == -1) + return -1; + + if (len > 0) { + if (len <= datalen) { + res = check_data(testfile, data, 0, len); + if (res == -1) + return -1; + } else { + res = check_data(testfile, data, 0, datalen); + if (res == -1) + return -1; + res = check_data(testfile, zerodata, datalen, + len - datalen); + if (res == -1) + return -1; + } + } + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + + success(); + return 0; +} + +static int test_ftruncate(int len, int mode) +{ + const char *data = testdata; + int datalen = testdatalen; + int res; + int fd; + + start_test("ftruncate(%u) mode: 0%03o", len, mode); + res = create_file(testfile, data, datalen); + if (res == -1) + return -1; + + fd = open(testfile, O_WRONLY); + if (fd == -1) { + PERROR("open"); + return -1; + } + + res = fchmod(fd, mode); + if (res == -1) { + PERROR("fchmod"); + close(fd); + return -1; + } + res = check_mode(testfile, mode); + if (res == -1) { + close(fd); + return -1; + } + res = ftruncate(fd, len); + if (res == -1) { + PERROR("ftruncate"); + close(fd); + return -1; + } + close(fd); + res = check_size(testfile, len); + if (res == -1) + return -1; + + if (len > 0) { + if (len <= datalen) { + res = check_data(testfile, data, 0, len); + if (res == -1) + return -1; + } else { + res = check_data(testfile, data, 0, datalen); + if (res == -1) + return -1; + res = check_data(testfile, zerodata, datalen, + len - datalen); + if (res == -1) + return -1; + } + } + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + + success(); + return 0; +} + +static int test_utime(void) +{ + struct utimbuf utm; + time_t atime = 987631200; + time_t mtime = 123116400; + int res; + + start_test("utime"); + res = create_file(testfile, NULL, 0); + if (res == -1) + return -1; + + utm.actime = atime; + utm.modtime = mtime; + res = utime(testfile, &utm); + if (res == -1) { + PERROR("utime"); + return -1; + } + res = check_times(testfile, atime, mtime); + if (res == -1) { + return -1; + } + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + + success(); + return 0; +} + +static int test_create(void) +{ + const char *data = testdata; + int datalen = testdatalen; + int err = 0; + int res; + int fd; + + start_test("create"); + unlink(testfile); + fd = creat(testfile, 0644); + if (fd == -1) { + PERROR("creat"); + return -1; + } + res = write(fd, data, datalen); + if (res == -1) { + PERROR("write"); + close(fd); + return -1; + } + if (res != datalen) { + ERROR("write is short: %u instead of %u", res, datalen); + close(fd); + return -1; + } + res = close(fd); + if (res == -1) { + PERROR("close"); + return -1; + } + res = check_type(testfile, S_IFREG); + if (res == -1) + return -1; + err += check_mode(testfile, 0644); + err += check_nlink(testfile, 1); + err += check_size(testfile, datalen); + err += check_data(testfile, data, 0, datalen); + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + if (err) + return -1; + + success(); + return 0; +} + +static int test_create_unlink(void) +{ + const char *data = testdata; + int datalen = testdatalen; + int err = 0; + int res; + int fd; + + start_test("create+unlink"); + unlink(testfile); + fd = open(testfile, O_CREAT | O_RDWR | O_TRUNC, 0644); + if (fd == -1) { + PERROR("creat"); + return -1; + } + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + close(fd); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + res = write(fd, data, datalen); + if (res == -1) { + PERROR("write"); + close(fd); + return -1; + } + if (res != datalen) { + ERROR("write is short: %u instead of %u", res, datalen); + close(fd); + return -1; + } + err += fcheck_type(fd, S_IFREG); + err += fcheck_mode(fd, 0644); + err += fcheck_nlink(fd, 0); + err += fcheck_size(fd, datalen); + err += fcheck_data(fd, data, 0, datalen); + res = close(fd); + if (res == -1) { + PERROR("close"); + err--; + } + if (err) + return -1; + + success(); + return 0; +} + +static int test_mknod(void) +{ + int err = 0; + int res; + + start_test("mknod"); + unlink(testfile); + res = mknod(testfile, 0644, 0); + if (res == -1) { + PERROR("mknod"); + return -1; + } + res = check_type(testfile, S_IFREG); + if (res == -1) + return -1; + err += check_mode(testfile, 0644); + err += check_nlink(testfile, 1); + err += check_size(testfile, 0); + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + if (err) + return -1; + + success(); + return 0; +} + +#define test_open(exist, flags, mode) do_test_open(exist, flags, #flags, mode) + +static int do_test_open(int exist, int flags, const char *flags_str, int mode) +{ + char buf[4096]; + const char *data = testdata; + int datalen = testdatalen; + unsigned currlen = 0; + int err = 0; + int res; + int fd; + off_t off; + + start_test("open(%s, %s, 0%03o)", exist ? "+" : "-", flags_str, mode); + unlink(testfile); + if (exist) { + res = create_file(testfile, testdata2, testdata2len); + if (res == -1) + return -1; + + currlen = testdata2len; + } + + fd = open(testfile, flags, mode); + if ((flags & O_CREAT) && (flags & O_EXCL) && exist) { + if (fd != -1) { + ERROR("open should have failed"); + close(fd); + return -1; + } else if (errno == EEXIST) + goto succ; + } + if (!(flags & O_CREAT) && !exist) { + if (fd != -1) { + ERROR("open should have failed"); + close(fd); + return -1; + } else if (errno == ENOENT) + goto succ; + } + if (fd == -1) { + PERROR("open"); + return -1; + } + + if (flags & O_TRUNC) + currlen = 0; + + err += check_type(testfile, S_IFREG); + if (exist) + err += check_mode(testfile, 0644); + else + err += check_mode(testfile, mode); + err += check_nlink(testfile, 1); + err += check_size(testfile, currlen); + if (exist && !(flags & O_TRUNC) && (mode & 0400)) + err += check_data(testfile, testdata2, 0, testdata2len); + + res = write(fd, data, datalen); + if ((flags & O_ACCMODE) != O_RDONLY) { + if (res == -1) { + PERROR("write"); + err --; + } else if (res != datalen) { + ERROR("write is short: %u instead of %u", res, datalen); + err --; + } else { + if (datalen > (int) currlen) + currlen = datalen; + + err += check_size(testfile, currlen); + + if (mode & 0400) { + err += check_data(testfile, data, 0, datalen); + if (exist && !(flags & O_TRUNC) && + testdata2len > datalen) + err += check_data(testfile, + testdata2 + datalen, + datalen, + testdata2len - datalen); + } + } + } else { + if (res != -1) { + ERROR("write should have failed"); + err --; + } else if (errno != EBADF) { + PERROR("write"); + err --; + } + } + off = lseek(fd, SEEK_SET, 0); + if (off == (off_t) -1) { + PERROR("lseek"); + err--; + } else if (off != 0) { + ERROR("offset should have returned 0"); + err --; + } + res = read(fd, buf, sizeof(buf)); + if ((flags & O_ACCMODE) != O_WRONLY) { + if (res == -1) { + PERROR("read"); + err--; + } else { + int readsize = + currlen < sizeof(buf) ? currlen : sizeof(buf); + if (res != readsize) { + ERROR("read is short: %i instead of %u", + res, readsize); + err--; + } else { + if ((flags & O_ACCMODE) != O_RDONLY) { + err += check_buffer(buf, data, datalen); + if (exist && !(flags & O_TRUNC) && + testdata2len > datalen) + err += check_buffer(buf + datalen, + testdata2 + datalen, + testdata2len - datalen); + } else if (exist) + err += check_buffer(buf, testdata2, + testdata2len); + } + } + } else { + if (res != -1) { + ERROR("read should have failed"); + err --; + } else if (errno != EBADF) { + PERROR("read"); + err --; + } + } + + res = close(fd); + if (res == -1) { + PERROR("close"); + return -1; + } + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + if (err) + return -1; + +succ: + success(); + return 0; +} + +#define test_open_acc(flags, mode, err) \ + do_test_open_acc(flags, #flags, mode, err) + +static int do_test_open_acc(int flags, const char *flags_str, int mode, int err) +{ + const char *data = testdata; + int datalen = testdatalen; + int res; + int fd; + + start_test("open_acc(%s) mode: 0%03o error: '%s'", flags_str, mode, + strerror(err)); + unlink(testfile); + res = create_file(testfile, data, datalen); + if (res == -1) + return -1; + + res = chmod(testfile, mode); + if (res == -1) { + PERROR("chmod"); + return -1; + } + + res = check_mode(testfile, mode); + if (res == -1) + return -1; + + fd = open(testfile, flags); + if (fd == -1) { + if (err != errno) { + PERROR("open"); + return -1; + } + } else { + if (err) { + ERROR("open should have failed"); + close(fd); + return -1; + } + close(fd); + } + success(); + return 0; +} + +static int test_symlink(void) +{ + char buf[1024]; + const char *data = testdata; + int datalen = testdatalen; + int linklen = strlen(testfile); + int err = 0; + int res; + + start_test("symlink"); + res = create_file(testfile, data, datalen); + if (res == -1) + return -1; + + unlink(testfile2); + res = symlink(testfile, testfile2); + if (res == -1) { + PERROR("symlink"); + return -1; + } + res = check_type(testfile2, S_IFLNK); + if (res == -1) + return -1; + err += check_mode(testfile2, 0777); + err += check_nlink(testfile2, 1); + res = readlink(testfile2, buf, sizeof(buf)); + if (res == -1) { + PERROR("readlink"); + err--; + } + if (res != linklen) { + ERROR("short readlink: %u instead of %u", res, linklen); + err--; + } + if (memcmp(buf, testfile, linklen) != 0) { + ERROR("link mismatch"); + err--; + } + err += check_size(testfile2, datalen); + err += check_data(testfile2, data, 0, datalen); + res = unlink(testfile2); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile2); + if (res == -1) + return -1; + if (err) + return -1; + + success(); + return 0; +} + +static int test_link(void) +{ + const char *data = testdata; + int datalen = testdatalen; + int err = 0; + int res; + + start_test("link"); + res = create_file(testfile, data, datalen); + if (res == -1) + return -1; + + unlink(testfile2); + res = link(testfile, testfile2); + if (res == -1) { + PERROR("link"); + return -1; + } + res = check_type(testfile2, S_IFREG); + if (res == -1) + return -1; + err += check_mode(testfile2, 0644); + err += check_nlink(testfile2, 2); + err += check_size(testfile2, datalen); + err += check_data(testfile2, data, 0, datalen); + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + + err += check_nlink(testfile2, 1); + res = unlink(testfile2); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile2); + if (res == -1) + return -1; + if (err) + return -1; + + success(); + return 0; +} + +static int test_link2(void) +{ + const char *data = testdata; + int datalen = testdatalen; + int err = 0; + int res; + + start_test("link-unlink-link"); + res = create_file(testfile, data, datalen); + if (res == -1) + return -1; + + unlink(testfile2); + res = link(testfile, testfile2); + if (res == -1) { + PERROR("link"); + return -1; + } + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + res = link(testfile2, testfile); + if (res == -1) { + PERROR("link"); + } + res = check_type(testfile, S_IFREG); + if (res == -1) + return -1; + err += check_mode(testfile, 0644); + err += check_nlink(testfile, 2); + err += check_size(testfile, datalen); + err += check_data(testfile, data, 0, datalen); + + res = unlink(testfile2); + if (res == -1) { + PERROR("unlink"); + return -1; + } + err += check_nlink(testfile, 1); + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + if (err) + return -1; + + success(); + return 0; +} + +static int test_rename_file(void) +{ + const char *data = testdata; + int datalen = testdatalen; + int err = 0; + int res; + + start_test("rename file"); + res = create_file(testfile, data, datalen); + if (res == -1) + return -1; + + unlink(testfile2); + res = rename(testfile, testfile2); + if (res == -1) { + PERROR("rename"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + res = check_type(testfile2, S_IFREG); + if (res == -1) + return -1; + err += check_mode(testfile2, 0644); + err += check_nlink(testfile2, 1); + err += check_size(testfile2, datalen); + err += check_data(testfile2, data, 0, datalen); + res = unlink(testfile2); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile2); + if (res == -1) + return -1; + if (err) + return -1; + + success(); + return 0; +} + +static int test_rename_dir(void) +{ + int err = 0; + int res; + + start_test("rename dir"); + res = create_dir(testdir, testdir_files); + if (res == -1) + return -1; + + rmdir(testdir2); + res = rename(testdir, testdir2); + if (res == -1) { + PERROR("rename"); + cleanup_dir(testdir, testdir_files, 1); + return -1; + } + res = check_nonexist(testdir); + if (res == -1) { + cleanup_dir(testdir, testdir_files, 1); + return -1; + } + res = check_type(testdir2, S_IFDIR); + if (res == -1) { + cleanup_dir(testdir2, testdir_files, 1); + return -1; + } + err += check_mode(testdir2, 0755); + err += check_dir_contents(testdir2, testdir_files); + err += cleanup_dir(testdir2, testdir_files, 0); + res = rmdir(testdir2); + if (res == -1) { + PERROR("rmdir"); + return -1; + } + res = check_nonexist(testdir2); + if (res == -1) + return -1; + if (err) + return -1; + + success(); + return 0; +} + +static int test_mkfifo(void) +{ + int res; + int err = 0; + + start_test("mkfifo"); + unlink(testfile); + res = mkfifo(testfile, 0644); + if (res == -1) { + PERROR("mkfifo"); + return -1; + } + res = check_type(testfile, S_IFIFO); + if (res == -1) + return -1; + err += check_mode(testfile, 0644); + err += check_nlink(testfile, 1); + res = unlink(testfile); + if (res == -1) { + PERROR("unlink"); + return -1; + } + res = check_nonexist(testfile); + if (res == -1) + return -1; + if (err) + return -1; + + success(); + return 0; +} + +static int test_mkdir(void) +{ + int res; + int err = 0; + const char *dir_contents[] = {NULL}; + + start_test("mkdir"); + rmdir(testdir); + res = mkdir(testdir, 0755); + if (res == -1) { + PERROR("mkdir"); + return -1; + } + res = check_type(testdir, S_IFDIR); + if (res == -1) + return -1; + err += check_mode(testdir, 0755); + err += check_nlink(testdir, 2); + err += check_dir_contents(testdir, dir_contents); + res = rmdir(testdir); + if (res == -1) { + PERROR("rmdir"); + return -1; + } + res = check_nonexist(testdir); + if (res == -1) + return -1; + if (err) + return -1; + + success(); + return 0; +} + +#define test_create_ro_dir(flags) \ + do_test_create_ro_dir(flags, #flags) + +static int do_test_create_ro_dir(int flags, const char *flags_str) +{ + int res; + int err = 0; + int fd; + + start_test("open(%s) in read-only directory", flags_str); + rmdir(testdir); + res = mkdir(testdir, 0555); + if (res == -1) { + PERROR("mkdir"); + return -1; + } + fd = open(subfile, flags, 0644); + if (fd != -1) { + close(fd); + unlink(subfile); + ERROR("open should have failed"); + err--; + } else { + res = check_nonexist(subfile); + if (res == -1) + err--; + } + unlink(subfile); + res = rmdir(testdir); + if (res == -1) { + PERROR("rmdir"); + return -1; + } + res = check_nonexist(testdir); + if (res == -1) + return -1; + if (err) + return -1; + + success(); + return 0; +} + +int main(int argc, char *argv[]) +{ + const char *basepath; + int err = 0; + + umask(0); + if (argc < 2 || argc > 3) { + fprintf(stderr, "usage: %s testdir [test#]\n", argv[0]); + return 1; + } + basepath = argv[1]; + if (argc == 3) { + char *endptr; + char *arg = argv[2]; + if (arg[0] == '-') { + arg++; + skip_test = strtoul(arg, &endptr, 10); + } else { + select_test = strtoul(argv[2], &endptr, 10); + } + if (arg[0] == '\0' || *endptr != '\0') { + fprintf(stderr, "invalid number: '%s'\n", arg); + return 1; + } + } + assert(strlen(basepath) < 512); + if (basepath[0] != '/') { + fprintf(stderr, "testdir must be an absolute path\n"); + return 1; + } + + sprintf(testfile, "%s/testfile", basepath); + sprintf(testfile2, "%s/testfile2", basepath); + sprintf(testdir, "%s/testdir", basepath); + sprintf(testdir2, "%s/testdir2", basepath); + sprintf(subfile, "%s/subfile", testdir2); + err += test_create(); + err += test_create_unlink(); + err += test_mknod(); + err += test_symlink(); + err += test_link(); + err += test_link2(); + err += test_mkfifo(); + err += test_mkdir(); + err += test_rename_file(); + err += test_rename_dir(); + err += test_utime(); + err += test_truncate(0); + err += test_truncate(testdatalen / 2); + err += test_truncate(testdatalen); + err += test_truncate(testdatalen + 100); + err += test_ftruncate(0, 0600); + err += test_ftruncate(testdatalen / 2, 0600); + err += test_ftruncate(testdatalen, 0600); + err += test_ftruncate(testdatalen + 100, 0600); + err += test_ftruncate(0, 0400); + err += test_ftruncate(0, 0200); + err += test_ftruncate(0, 0000); + err += test_open(0, O_RDONLY, 0); + err += test_open(1, O_RDONLY, 0); + err += test_open(1, O_RDWR, 0); + err += test_open(1, O_WRONLY, 0); + err += test_open(0, O_RDWR | O_CREAT, 0600); + err += test_open(1, O_RDWR | O_CREAT, 0600); + err += test_open(0, O_RDWR | O_CREAT | O_TRUNC, 0600); + err += test_open(1, O_RDWR | O_CREAT | O_TRUNC, 0600); + err += test_open(0, O_RDONLY | O_CREAT, 0600); + err += test_open(0, O_RDONLY | O_CREAT, 0400); + err += test_open(0, O_RDONLY | O_CREAT, 0200); + err += test_open(0, O_RDONLY | O_CREAT, 0000); + err += test_open(0, O_WRONLY | O_CREAT, 0600); + err += test_open(0, O_WRONLY | O_CREAT, 0400); + err += test_open(0, O_WRONLY | O_CREAT, 0200); + err += test_open(0, O_WRONLY | O_CREAT, 0000); + err += test_open(0, O_RDWR | O_CREAT, 0400); + err += test_open(0, O_RDWR | O_CREAT, 0200); + err += test_open(0, O_RDWR | O_CREAT, 0000); + err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0600); + err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600); + err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000); + err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000); + err += test_open_acc(O_RDONLY, 0600, 0); + err += test_open_acc(O_WRONLY, 0600, 0); + err += test_open_acc(O_RDWR, 0600, 0); + err += test_open_acc(O_RDONLY, 0400, 0); + err += test_open_acc(O_WRONLY, 0200, 0); + err += test_create_ro_dir(O_CREAT); + err += test_create_ro_dir(O_CREAT | O_EXCL); + err += test_create_ro_dir(O_CREAT | O_WRONLY); + err += test_create_ro_dir(O_CREAT | O_TRUNC); + + unlink(testfile); + unlink(testfile2); + rmdir(testdir); + rmdir(testdir2); + + if (err) { + fprintf(stderr, "%i tests failed\n", -err); + return 1; + } + + return 0; +} diff --git a/tests/fuse-sanity-test/fusexmp_fh.c b/tests/fuse-sanity-test/fusexmp_fh.c new file mode 100644 index 0000000..d340a96 --- /dev/null +++ b/tests/fuse-sanity-test/fusexmp_fh.c @@ -0,0 +1,539 @@ +/* + Copy from upstream libfuse/example/fusexmp_fh.c, then make some + local changes. + + gcc -Wall fusexmp_fh.c `pkg-config fuse --cflags --libs` -lulockmgr -o fusexmp_fh +*/ + +#define FUSE_USE_VERSION 26 +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* flock(2) */ + +static int xmp_getattr(const char *path, struct stat *stbuf) +{ + int res; + + res = lstat(path, stbuf); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_fgetattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) +{ + int res; + + (void) path; + + res = fstat(fi->fh, stbuf); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_access(const char *path, int mask) +{ + int res; + + res = access(path, mask); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_readlink(const char *path, char *buf, size_t size) +{ + int res; + + res = readlink(path, buf, size - 1); + if (res == -1) + return -errno; + + buf[res] = '\0'; + return 0; +} + +struct xmp_dirp { + DIR *dp; + struct dirent *entry; + off_t offset; +}; + +static int xmp_opendir(const char *path, struct fuse_file_info *fi) +{ + int res; + struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp)); + if (d == NULL) + return -ENOMEM; + + d->dp = opendir(path); + if (d->dp == NULL) { + res = -errno; + free(d); + return res; + } + d->offset = 0; + d->entry = NULL; + + fi->fh = (unsigned long) d; + return 0; +} + +static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi) +{ + return (struct xmp_dirp *) (uintptr_t) fi->fh; +} + +static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + struct xmp_dirp *d = get_dirp(fi); + + (void) path; + if (offset != d->offset) { + seekdir(d->dp, offset); + d->entry = NULL; + d->offset = offset; + } + while (1) { + struct stat st; + off_t nextoff; + + if (!d->entry) { + d->entry = readdir(d->dp); + if (!d->entry) + break; + } + + memset(&st, 0, sizeof(st)); + st.st_ino = d->entry->d_ino; + st.st_mode = d->entry->d_type << 12; + nextoff = telldir(d->dp); + if (filler(buf, d->entry->d_name, &st, nextoff)) + break; + + d->entry = NULL; + d->offset = nextoff; + } + + return 0; +} + +static int xmp_releasedir(const char *path, struct fuse_file_info *fi) +{ + struct xmp_dirp *d = get_dirp(fi); + (void) path; + closedir(d->dp); + free(d); + return 0; +} + +static int xmp_mknod(const char *path, mode_t mode, dev_t rdev) +{ + int res; + + if (S_ISFIFO(mode)) + res = mkfifo(path, mode); + else + res = mknod(path, mode, rdev); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_mkdir(const char *path, mode_t mode) +{ + int res; + + res = mkdir(path, mode); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_unlink(const char *path) +{ + int res; + + res = unlink(path); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_rmdir(const char *path) +{ + int res; + + res = rmdir(path); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_symlink(const char *from, const char *to) +{ + int res; + + res = symlink(from, to); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_rename(const char *from, const char *to) +{ + int res; + + res = rename(from, to); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_link(const char *from, const char *to) +{ + int res; + + res = link(from, to); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_chmod(const char *path, mode_t mode) +{ + int res; + + res = chmod(path, mode); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_chown(const char *path, uid_t uid, gid_t gid) +{ + int res; + + res = lchown(path, uid, gid); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_truncate(const char *path, off_t size) +{ + int res; + + res = truncate(path, size); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_ftruncate(const char *path, off_t size, + struct fuse_file_info *fi) +{ + int res; + + (void) path; + + res = ftruncate(fi->fh, size); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_utimens(const char *path, const struct timespec ts[2]) +{ + int res; + + /* don't use utime/utimes since they follow symlinks */ + res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi) +{ + int fd; + + fd = open(path, fi->flags, mode); + if (fd == -1) + return -errno; + + fi->fh = fd; + return 0; +} + +static int xmp_open(const char *path, struct fuse_file_info *fi) +{ + int fd; + + fd = open(path, fi->flags); + if (fd == -1) + return -errno; + + fi->fh = fd; + return 0; +} + +static int xmp_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + int res; + + (void) path; + res = pread(fi->fh, buf, size, offset); + if (res == -1) + res = -errno; + + return res; +} + +static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp, + size_t size, off_t offset, struct fuse_file_info *fi) +{ + struct fuse_bufvec *src; + + (void) path; + + src = malloc(sizeof(struct fuse_bufvec)); + if (src == NULL) + return -ENOMEM; + + *src = FUSE_BUFVEC_INIT(size); + + src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; + src->buf[0].fd = fi->fh; + src->buf[0].pos = offset; + + *bufp = src; + + return 0; +} + +static int xmp_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + int res; + + (void) path; + res = pwrite(fi->fh, buf, size, offset); + if (res == -1) + res = -errno; + + return res; +} + +static int xmp_write_buf(const char *path, struct fuse_bufvec *buf, + off_t offset, struct fuse_file_info *fi) +{ + struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf)); + + (void) path; + + dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; + dst.buf[0].fd = fi->fh; + dst.buf[0].pos = offset; + + return fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK); +} + +static int xmp_statfs(const char *path, struct statvfs *stbuf) +{ + int res; + + res = statvfs(path, stbuf); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_flush(const char *path, struct fuse_file_info *fi) +{ + int res; + + (void) path; + /* This is called from every close on an open file, so call the + close on the underlying filesystem. But since flush may be + called multiple times for an open file, this must not really + close the file. This is important if used on a network + filesystem like NFS which flush the data/metadata on close() */ + res = close(dup(fi->fh)); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_release(const char *path, struct fuse_file_info *fi) +{ + (void) path; + close(fi->fh); + + return 0; +} + +static int xmp_fsync(const char *path, int isdatasync, + struct fuse_file_info *fi) +{ + int res; + (void) path; + + if (isdatasync) + res = fdatasync(fi->fh); + else + res = fsync(fi->fh); + if (res == -1) + return -errno; + + return 0; +} + +static int xmp_fallocate(const char *path, int mode, + off_t offset, off_t length, struct fuse_file_info *fi) +{ + (void) path; + + if (mode) + return -EOPNOTSUPP; + + return -posix_fallocate(fi->fh, offset, length); +} + +/* xattr operations are optional and can safely be left unimplemented */ +static int xmp_setxattr(const char *path, const char *name, const char *value, + size_t size, int flags) +{ + int res = lsetxattr(path, name, value, size, flags); + if (res == -1) + return -errno; + return 0; +} + +static int xmp_getxattr(const char *path, const char *name, char *value, + size_t size) +{ + int res = lgetxattr(path, name, value, size); + if (res == -1) + return -errno; + return res; +} + +static int xmp_listxattr(const char *path, char *list, size_t size) +{ + int res = llistxattr(path, list, size); + if (res == -1) + return -errno; + return res; +} + +static int xmp_removexattr(const char *path, const char *name) +{ + int res = lremovexattr(path, name); + if (res == -1) + return -errno; + return 0; +} + +static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd, + struct flock *lock) +{ + (void) path; + + return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner, + sizeof(fi->lock_owner)); +} + +static int xmp_flock(const char *path, struct fuse_file_info *fi, int op) +{ + int res; + (void) path; + + res = flock(fi->fh, op); + if (res == -1) + return -errno; + + return 0; +} + +static struct fuse_operations xmp_oper = { + .getattr = xmp_getattr, + .fgetattr = xmp_fgetattr, + .access = xmp_access, + .readlink = xmp_readlink, + .opendir = xmp_opendir, + .readdir = xmp_readdir, + .releasedir = xmp_releasedir, + .mknod = xmp_mknod, + .mkdir = xmp_mkdir, + .symlink = xmp_symlink, + .unlink = xmp_unlink, + .rmdir = xmp_rmdir, + .rename = xmp_rename, + .link = xmp_link, + .chmod = xmp_chmod, + .chown = xmp_chown, + .truncate = xmp_truncate, + .ftruncate = xmp_ftruncate, + .utimens = xmp_utimens, + .create = xmp_create, + .open = xmp_open, + .read = xmp_read, + .read_buf = xmp_read_buf, + .write = xmp_write, + .write_buf = xmp_write_buf, + .statfs = xmp_statfs, + .flush = xmp_flush, + .release = xmp_release, + .fsync = xmp_fsync, + .fallocate = xmp_fallocate, + .setxattr = xmp_setxattr, + .getxattr = xmp_getxattr, + .listxattr = xmp_listxattr, + .removexattr = xmp_removexattr, + .lock = xmp_lock, + .flock = xmp_flock, + + .flag_nullpath_ok = 1, + .flag_utime_omit_ok = 1, +}; + +int main(int argc, char *argv[]) +{ + umask(0); + return fuse_main(argc, argv, &xmp_oper, NULL); +} diff --git a/tests/fuse-sanity-test/main.fmf b/tests/fuse-sanity-test/main.fmf new file mode 100644 index 0000000..b94b261 --- /dev/null +++ b/tests/fuse-sanity-test/main.fmf @@ -0,0 +1,17 @@ +summary: The gating test case of fuse-2.x +description: This's a simple gating test case for fuse-2.x. +contact: Zorro Lang +component: + - fuse +test: ./runtest.sh +framework: shell +recommend: + - fuse + - fuse-devel + - fuse-libs + - pkgconf-pkg-config + - gcc + - glibc-headers + - procps-ng + - beakerlib +duration: 12h diff --git a/tests/fuse-sanity-test/runtest.sh b/tests/fuse-sanity-test/runtest.sh new file mode 100755 index 0000000..533ce26 --- /dev/null +++ b/tests/fuse-sanity-test/runtest.sh @@ -0,0 +1,136 @@ +#!/bin/bash +# vim: dict=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Description: Run for fuse-2.x CI gating test +# Author: Zorro Lang +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2022 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TEST_DIR=/mnt/fuse2-gating + +status=1 +trap "cleanup;" 0 1 2 3 9 15 + +# test exits here +cleanup() +{ + pkill fusetest + umount $TEST_DIR + pkill fusexmp_fh 2>/dev/null + if [ $status -eq 0 ];then + _report "CLEAN" PASS + else + _report "CLEAN" FAIL + fi + exit $status +} + +if [ -z "$RECIPEID" -o -z "$TASKID" ]; then + OUTPUTFILE=`pwd`/output.log + echo "No RECIPEID or TASKID, maybe local manual test" | tee $OUTPUTFILE +fi + +function echoo() +{ + echo $@ | tee -a $OUTPUTFILE +} + +# Wrapper to log the output of the command +function xlog() +{ + echoo "$@" + $@ 2>&1 | tee -a $OUTPUTFILE + return $? +} + +# Wrapper to report_result, clears $OUTPUTFILE +function _report() +{ + local what="$FSTYP:$1" + local status="$2" + local score="$3" + test -z "$score" && score=0 + echoo "---------------------------------------------" + echoo -e "|Report:\t" "$what\t" "$status\t" "$score|" + echoo "---------------------------------------------" + rhts-report-result "$what" "$status" "$OUTPUTFILE" "$score" + if [ -f "$OUTPUTFILE" ];then + rm -f $OUTPUTFILE + touch $OUTPUTFILE + fi +} + +function prepare_test() +{ + yum --enablerepo=* -y --nogpgcheck --setopt=skip_if_unavailable=True install fuse fuse-libs fuse-devel \ + pkgconf-pkg-config gcc glibc-headers procps-ng + if [ $? -ne 0 ];then + _report prepare_test FAIL + exit 1 + fi + + _report prepare_test PASS +} + +function build_test() +{ + xlog gcc -Wall fusexmp_fh.c `pkg-config fuse --cflags --libs` -lulockmgr -o fusexmp_fh + if [ $? -ne 0 ];then + _report build_test:fusexmp_fh FAIL + exit 1 + fi + xlog gcc -Wall fusetest.c -o fusetest + if [ $? -ne 0 ];then + _report build_test:fusetest FAIL + exit 1 + fi + + _report build_test PASS +} + +function do_test() +{ + mkdir -p $TEST_DIR 2>/dev/null + if [ ! -d $TEST_DIR ];then + _report do_test:TEST_DIR_missing FAIL + exit 1 + fi + xlog ./fusexmp_fh $TEST_DIR + if [ $? -ne 0 ] || (! findmnt $TEST_DIR >/dev/null 2>&1 );then + _report do_test:mount FAIL + exit 1 + fi + + xlog ./fusetest $TEST_DIR + if [ $? -eq 0 ];then + _report do_test:fusetest PASS + else + _report do_test:fusetest FAIL + exit 1 + fi +} + +prepare_test +build_test +do_test +status=0 +exit 0