From: Nikolaus Rath Date: Thu, 5 Jan 2017 17:37:00 +0000 (-0800) Subject: Added experimental support for building with Meson+Ninja X-Git-Tag: fuse-3.0.1~36 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=9f96db71252fc66b72c433e2ca0d49e031c6a5fd;p=qemu-gpiodev%2Flibfuse.git Added experimental support for building with Meson+Ninja --- diff --git a/.gitignore b/.gitignore index 619131a..0da3051 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ TAGS /GSYMS /GTAGS /test/test_setattr +/build/ diff --git a/ChangeLog.rst b/ChangeLog.rst index b8ad5e1..2cabd7d 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -2,7 +2,7 @@ libfuse 3.1.0 (UNRELEASED) ========================== * Re-introduced examples/null.c. - +* Added experimental support for building with Meson. libfuse 3.0.0 (2016-12-08) ========================== diff --git a/Makefile.am b/Makefile.am index 4e95a71..b2ba4b2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,26 +8,11 @@ EXTRA_DIST = \ fuse3.pc.in \ README* \ test/*.py \ - test/pytest.ini + test/pytest.ini \ + meson.build \ + meson_options.txt pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = fuse3.pc $(pkgconfig_DATA): config.status - -.PHONY: setuid_fusermount -setuid_fusermount: - @echo "Attempting to use sudo to make util/fusermount3 setuid root" - @echo "If this fails, set permissions manually and re-run make test" - test $$(ls -n util/fusermount3 | awk 'NR==1 {print $$3}') -eq 0 || \ - sudo chown root util/fusermount3 - test -u util/fusermount3 || \ - sudo chmod u+s util/fusermount3 - -# If we are not root, util/fusermount3 needs to be setuid root -# for tests to work. - -test_deps = $(shell [ "$${UID}" -eq 0 ] || echo setuid_fusermount) -.PHONY: test -test: all $(test_deps) - python3 -m pytest test/ diff --git a/README.md b/README.md index 8201eeb..be2e52e 100644 --- a/README.md +++ b/README.md @@ -28,33 +28,58 @@ Installation ------------ You can download libfuse from -https://github.com/libfuse/libfuse/releases. After extracting the -tarball, build and install with +https://github.com/libfuse/libfuse/releases. To build and install, we +recommend to use [Meson](http://mesonbuild.com/) and +[Ninja](https://ninja-build.org). After extracting the libfuse +tarball, create a (temporary) build directory and run Meson: - ./configure - make -j8 - make install + $ md build; cd build + $ meson .. -To run some self tests, you need a Python 3 environment with the -[py.test](http://www.pytest.org/) module installed. To run the tests, -execute +Normally, the default build options will work fine. If you +nevertheless want to adjust them, you can do so with the *mesonconf* +command: - python3 -m pytest test/ + $ mesonconf # list options + $ mesonconf -D disable-mtab=true # set an option -You may also need to add `/usr/local/lib` to `/etc/ld.so.conf` and/or -run *ldconfig*. If you're building from the git repository (instead of -using a release tarball), you also need to run `./makeconf.sh` to -create the `configure` script. +To build, test and install libfuse, you then use Ninja: + + $ ninja + $ sudo ninja tests # requires pytest, see below + $ sudo ninja install + +Running the tests requires the [py.test](http://www.pytest.org/) +Python module. Instead of running the tests as root, the majority of +tests can also be run as a regular user if *util/fusermount3* is +made setuid root first: + + $ sudo chown root:root util/fusermount3 + $ sudo chmod 4755 util/fusermount3 + $ ninja tests + + +Alternate Installation +---------------------- + +If you are not able to use Meson and Ninja, please report this to the +libfuse mailing list. Until the problem is resolved, you may fall back +to an in-source build using autotools: + + $ ./configure + $ make + $ sudo make install + +Note that support for building with autotools may disappear at some +point, so if you depend on using autotools for some reason please let +the libfuse developers know! -You'll also need a fuse kernel module (Linux kernels 2.6.14 or later -contain FUSE support). Security implications --------------------- -If you run `make install`, the *fusermount3* program is installed -set-user-id to root. This is done to allow normal users to mount -their own filesystem implementations. +The *fusermount3* program is installed setuid root. This is done to +allow normal users to mount their own filesystem implementations. To limit the harm that malicious users can do this way, *fusermount3* enforces the following limitations: diff --git a/doc/Makefile.am b/doc/Makefile.am index 8801da2..531a6c4 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -2,4 +2,4 @@ dist_man_MANS = fusermount3.1 mount.fuse.8 -EXTRA_DIST = kernel.txt Doxyfile html README.NFS +EXTRA_DIST = kernel.txt Doxyfile html README.NFS meson.build diff --git a/doc/meson.build b/doc/meson.build new file mode 100644 index 0000000..eb81f3d --- /dev/null +++ b/doc/meson.build @@ -0,0 +1,5 @@ +# Attention, emacs, please use -*- mode: python -*- +# (even though this isn't actually Python code) + +install_man('fusermount3.1', 'mount.fuse.8') + diff --git a/example/Makefile.am b/example/Makefile.am index 81b9555..c83c81f 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -17,3 +17,5 @@ ioctl_client_LDADD = poll_client_CPPFLAGS = poll_client_LDFLAGS = poll_client_LDADD = + +EXTRA_DIST = meson.build diff --git a/example/meson.build b/example/meson.build new file mode 100644 index 0000000..4497288 --- /dev/null +++ b/example/meson.build @@ -0,0 +1,29 @@ +# Attention, emacs, please use -*- mode: python -*- +# (even though this isn't actually Python code) + +examples = [ 'passthrough', 'passthrough_fh', 'null', 'hello', 'hello_ll', + 'ioctl', 'ioctl_client', 'poll_client', + 'passthrough_ll', 'cuse', 'cuse_client' ] + +threaded_examples = [ 'notify_inval_inode', + 'notify_store_retrieve', + 'notify_inval_entry', + 'poll' ] + +foreach ex : examples + executable(ex, ex + '.c', + include_directories: include_dirs, + link_with: [ libfuse ], + install: false) +endforeach + + +foreach ex : threaded_examples + executable(ex, ex + '.c', + include_directories: include_dirs, + link_with: [ libfuse ], + dependencies: thread_dep, + install: false) +endforeach + +# TODO: Link passthrough_fh with ulockmgr if available diff --git a/include/Makefile.am b/include/Makefile.am index ffbfafa..5072cc3 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -10,3 +10,5 @@ fuseinclude_HEADERS = \ cuse_lowlevel.h noinst_HEADERS = fuse_kernel.h + +EXTRA_DIST = meson.build diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 0000000..f1fa5f0 --- /dev/null +++ b/include/meson.build @@ -0,0 +1,8 @@ +# Attention, emacs, please use -*- mode: python -*- +# (even though this isn't actually Python code) + +libfuse_headers = [ 'fuse.h', 'fuse_common.h', 'fuse_lowlevel.h', + 'fuse_opt.h', 'cuse_lowlevel.h' ] + +install_headers(libfuse_headers, subdir: 'fuse3') + diff --git a/lib/Makefile.am b/lib/Makefile.am index e7f6fd4..cdacfd5 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -40,4 +40,4 @@ if NETBSD libfuse3_la_LIBADD = -lperfuse -lpuffs endif -EXTRA_DIST = fuse_versionscript +EXTRA_DIST = fuse_versionscript meson.build diff --git a/lib/meson.build b/lib/meson.build new file mode 100644 index 0000000..287dc86 --- /dev/null +++ b/lib/meson.build @@ -0,0 +1,47 @@ +# Attention, emacs, please use -*- mode: python -*- +# (even though this isn't actually Python code) + +libfuse_sources = ['fuse.c', 'fuse_i.h', 'fuse_loop.c', 'fuse_loop_mt.c', + 'fuse_lowlevel.c', 'fuse_misc.h', 'fuse_opt.c', + 'fuse_signals.c', 'buffer.c', 'cuse_lowlevel.c', + 'helper.c', 'modules/subdir.c' ] + +if host_machine.system().startswith('linux') + libfuse_sources += [ 'mount.c', 'mount_util.c' ] +else + libfuse_sources += [ 'mount_bsd.c' ] +endif + +if cfg.has('HAVE_ICONV') + libfuse_sources += [ 'modules/iconv.c' ] +endif + +deps = [ thread_dep ] +libdl = meson.get_compiler('c').find_library('dl') +if libdl.found() + deps += [ libdl ] +endif + +if host_machine.system().startswith('netbsd') + deps += [ cc.find_library('perfuse'), + cc.find_library('puffs') ] +endif + +fusermount_path = join_paths(get_option('prefix'), get_option('bindir')) +libfuse = library('fuse3', libfuse_sources, version: '3.0.0', install: true, + soversion: '3', include_directories: include_dirs, + dependencies: deps, + link_depends: 'fuse_versionscript', + c_args: [ '-DFUSE_USE_VERSION=30', + '-DFUSERMOUNT_DIR="{}"'.format(fusermount_path) ], + link_args: ['-Wl,--version-script,' + meson.current_source_dir() + + '/fuse_versionscript' ]) + +pkg = import('pkgconfig') +pkg.generate(libraries: [ libfuse, '-lpthread' ], + libraries_private: '-ldl', + version: meson.project_version(), + name: 'fuse3', + description: 'Filesystem in Userspace', + subdirs: 'fuse3') + diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..0cce4af --- /dev/null +++ b/meson.build @@ -0,0 +1,90 @@ +# Attention, emacs, please use -*- mode: python -*- +# (even though this isn't actually Python code) + +project('libfuse3', 'c', version: '3.1.0', + default_options: [ 'buildtype=plain' ]) + +# +# Feature detection +# +cfg = configuration_data() +cc = meson.get_compiler('c') + +# Default includes when checking for presence of functions and +# struct members +include_default = ' +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +' + +cfg.set_quoted('PACKAGE_VERSION', meson.project_version()) + +# Test for presence of some functions +test_funcs = [ 'fork', 'fstatat', 'openat', 'readlinkat', 'pipe2', + 'splice', 'vmsplice', 'posix_fallocate', 'fdatasync', + 'utimensat' ] +foreach func : test_funcs + cfg.set('HAVE_' + func.to_upper(), + cc.has_function(func, prefix: include_default)) +endforeach +cfg.set('HAVE_SETXATTR', + cc.has_function('setxattr', prefix: '#include ')) +cfg.set('HAVE_ICONV', + cc.has_function('iconv', prefix: '#include ')) + +# Test if structs have specific member +cfg.set('HAVE_STRUCT_STAT_ST_ATIM', + cc.has_member('struct stat', 'st_atim', + prefix: include_default)) +cfg.set('HAVE_STRUCT_STAT_ST_ATIMESPEC', + cc.has_member('struct stat', 'st_atimespec', + prefix: include_default)) + +# Write the test results into config.h (stored in build directory) +configure_file(output: 'config.h', + configuration : cfg) + + +# +# Compiler configuration +# +add_global_arguments('-D_REENTRANT', '-DHAVE_CONFIG_H', '-Wall', '-Wextra', '-Wno-sign-compare', + '-Wstrict-prototypes', '-Wmissing-declarations', '-Wwrite-strings', + '-O2', '-g', '-fno-strict-aliasing', language: 'c') + +# Some (stupid) GCC versions warn about unused return values even when they are +# casted to void. This makes -Wunused-result pretty useless, since there is no +# way to suppress the warning when we really *want* to ignore the value. +code = ''' +__attribute__((warn_unused_result)) int get_4() { + return 4; +} +int main(void) { + (void) get_4(); + return 0; +}''' +if not cc.compiles(code, args: [ '-O0', '-Werror=unused-result' ]) + message('Compiler warns about unused result even when casting to void') + add_global_arguments('-Wno-unused-result', language: 'c') +endif + +# current_build_dir() contains config.h +include_dirs = include_directories('include', 'lib', + meson.current_build_dir()) + +# Common dependencies +thread_dep = dependency('threads') + +# +# Read build files from sub-directories +# +subdirs = [ 'lib', 'include', 'util', 'example', 'doc', 'test' ] +foreach n : subdirs + subdir(n) +endforeach diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..e53bf5d --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,2 @@ +option('disable-mtab', type : 'boolean', value : false, + description: 'Disable and ignore usage of /etc/mtab') diff --git a/test/Makefile.am b/test/Makefile.am index 9eb0a73..6bcea43 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,7 +1,10 @@ ## Process this file with automake to produce Makefile.in AM_CPPFLAGS = -I$(top_srcdir)/include -D_REENTRANT -noinst_PROGRAMS = test test_write_cache test_setattr +noinst_PROGRAMS = test_syscalls test_write_cache test_setattr test_write_cache_LDADD = ../lib/libfuse3.la test_setattr_LDADD = ../lib/libfuse3.la + +EXTRA_DIST = meson.build wrong_command.c + diff --git a/test/meson.build b/test/meson.build new file mode 100644 index 0000000..971c64a --- /dev/null +++ b/test/meson.build @@ -0,0 +1,35 @@ +# Attention, emacs, please use -*- mode: python -*- +# (even though this isn't actually Python code) + +# Compile helper programs +td = [] +foreach prog: [ 'test_write_cache', 'test_setattr' ] + td += executable(prog, prog + '.c', + include_directories: include_dirs, + link_with: [ libfuse ], + dependencies: thread_dep, + install: false) +endforeach +td += executable('test_syscalls', 'test_syscalls.c', + include_directories: include_dirs, + install: false) + +# Actual tests are written in Python and can simply be copied. +foreach fname : [ 'conftest.py', 'pytest.ini', 'test_examples.py', + 'util.py' ] + td += custom_target(fname, input: fname, output: fname, + command: ['cp', '-fP', '--preserve=mode', + '@INPUT@', '@OUTPUT@']) +endforeach + +# Create a new 'tests' target that we can run with Ninja +run_target('tests', depends: td, + command: [ 'python3', '-m', 'pytest', + meson.current_build_dir() ]) + + +# Provide something helpful when running 'ninja test' +wrong_cmd = executable('wrong_command', 'wrong_command.c', + install: false) +test('wrong_cmd', wrong_cmd) + diff --git a/test/test.c b/test/test.c deleted file mode 100644 index 281f218..0000000 --- a/test/test.c +++ /dev/null @@ -1,1522 +0,0 @@ -#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 testfile_r[1024]; -static char testfile2_r[1024]; -static char testdir_r[1024]; -static char testdir2_r[1024]; -static char subfile_r[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 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) - -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_r, 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; - res = check_nonexist(testfile_r); - 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 message: '%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; - const char *realpath; - int err = 0; - int a; - int is_root; - - umask(0); - if (argc < 2 || argc > 4) { - fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#]\n", argv[0]); - return 1; - } - basepath = argv[1]; - realpath = basepath; - for (a = 2; a < argc; a++) { - char *endptr; - char *arg = argv[a]; - if (arg[0] == ':') { - realpath = arg + 1; - } else { - if (arg[0] == '-') { - arg++; - skip_test = strtoul(arg, &endptr, 10); - } else { - select_test = strtoul(arg, &endptr, 10); - } - if (arg[0] == '\0' || *endptr != '\0') { - fprintf(stderr, "invalid number: '%s'\n", arg); - return 1; - } - } - } - assert(strlen(basepath) < 512); - assert(strlen(realpath) < 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); - - sprintf(testfile_r, "%s/testfile", realpath); - sprintf(testfile2_r, "%s/testfile2", realpath); - sprintf(testdir_r, "%s/testdir", realpath); - sprintf(testdir2_r, "%s/testdir2", realpath); - sprintf(subfile_r, "%s/subfile", testdir2_r); - - is_root = (geteuid() == 0); - - 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); - if(!is_root) { - err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES); - err += test_open_acc(O_WRONLY, 0400, EACCES); - err += test_open_acc(O_RDWR, 0400, EACCES); - err += test_open_acc(O_RDONLY, 0200, EACCES); - err += test_open_acc(O_RDWR, 0200, EACCES); - err += test_open_acc(O_RDONLY, 0000, EACCES); - err += test_open_acc(O_WRONLY, 0000, EACCES); - err += test_open_acc(O_RDWR, 0000, EACCES); - } - 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/test/test_examples.py b/test/test_examples.py index cfd6734..92a67c0 100755 --- a/test/test_examples.py +++ b/test/test_examples.py @@ -77,7 +77,7 @@ def test_passthrough(tmpdir, name, debug): wait_for_mount(mount_process, mnt_dir) work_dir = pjoin(mnt_dir, src_dir) - subprocess.check_call([ os.path.join(basename, 'test', 'test'), + subprocess.check_call([ os.path.join(basename, 'test', 'test_syscalls'), work_dir, ':' + src_dir ]) tst_write(work_dir) diff --git a/test/test_syscalls.c b/test/test_syscalls.c new file mode 100644 index 0000000..281f218 --- /dev/null +++ b/test/test_syscalls.c @@ -0,0 +1,1522 @@ +#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 testfile_r[1024]; +static char testfile2_r[1024]; +static char testdir_r[1024]; +static char testdir2_r[1024]; +static char subfile_r[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 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) + +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_r, 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; + res = check_nonexist(testfile_r); + 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 message: '%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; + const char *realpath; + int err = 0; + int a; + int is_root; + + umask(0); + if (argc < 2 || argc > 4) { + fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#]\n", argv[0]); + return 1; + } + basepath = argv[1]; + realpath = basepath; + for (a = 2; a < argc; a++) { + char *endptr; + char *arg = argv[a]; + if (arg[0] == ':') { + realpath = arg + 1; + } else { + if (arg[0] == '-') { + arg++; + skip_test = strtoul(arg, &endptr, 10); + } else { + select_test = strtoul(arg, &endptr, 10); + } + if (arg[0] == '\0' || *endptr != '\0') { + fprintf(stderr, "invalid number: '%s'\n", arg); + return 1; + } + } + } + assert(strlen(basepath) < 512); + assert(strlen(realpath) < 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); + + sprintf(testfile_r, "%s/testfile", realpath); + sprintf(testfile2_r, "%s/testfile2", realpath); + sprintf(testdir_r, "%s/testdir", realpath); + sprintf(testdir2_r, "%s/testdir2", realpath); + sprintf(subfile_r, "%s/subfile", testdir2_r); + + is_root = (geteuid() == 0); + + 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); + if(!is_root) { + err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES); + err += test_open_acc(O_WRONLY, 0400, EACCES); + err += test_open_acc(O_RDWR, 0400, EACCES); + err += test_open_acc(O_RDONLY, 0200, EACCES); + err += test_open_acc(O_RDWR, 0200, EACCES); + err += test_open_acc(O_RDONLY, 0000, EACCES); + err += test_open_acc(O_WRONLY, 0000, EACCES); + err += test_open_acc(O_RDWR, 0000, EACCES); + } + 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/test/util.py b/test/util.py index 6bba9e2..48670bd 100644 --- a/test/util.py +++ b/test/util.py @@ -105,7 +105,7 @@ def fuse_test_marker(): return pytest.mark.uses_fuse() -# If valgrind and libtool are available, use them +# If valgrind is available, use it def has_program(name): try: ret = subprocess.call([name, '--version'], @@ -115,9 +115,8 @@ def has_program(name): return False return ret == 0 -if has_program('valgrind') and has_program('libtool'): - base_cmdline = [ 'libtool', '--mode=execute', - 'valgrind', '-q', '--' ] +if has_program('valgrind'): + base_cmdline = [ 'valgrind', '-q', '--' ] else: base_cmdline = [] diff --git a/test/wrong_command.c b/test/wrong_command.c new file mode 100644 index 0000000..ef835b3 --- /dev/null +++ b/test/wrong_command.c @@ -0,0 +1,10 @@ +#include + +int main(void) { + fprintf(stderr, "\x1B[31m\e[1m" + "This is not the command you are looking for.\n" + "You probably want to run 'ninja tests' instead " + "(note the 's' at the end).\n" + "\e[0m"); + return 1; +} diff --git a/util/Makefile.am b/util/Makefile.am index 756afea..1eb2ec3 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -23,7 +23,7 @@ install-exec-hook: mknod $(DESTDIR)/dev/fuse -m 0666 c 10 229 || true; \ fi -EXTRA_DIST = udev.rules init_script +EXTRA_DIST = udev.rules init_script meson.build install_helper.sh MOUNT_FUSE_PATH = @MOUNT_FUSE_PATH@ UDEV_RULES_PATH = @UDEV_RULES_PATH@ diff --git a/util/install_helper.sh b/util/install_helper.sh new file mode 100755 index 0000000..fe65c1c --- /dev/null +++ b/util/install_helper.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Don't call this script. It is used internally by the Meson +# build system. Thank you for your cooperation. +# + +set -e + +sysconfdir="$1" +bindir="$2" +prefix="${MESON_INSTALL_DESTDIR_PREFIX}" + +chown root:root "${prefix}/${bindir}/fusermount3" +chmod u+s "${prefix}/${bindir}/fusermount3" + +if test ! -e "${DESTDIR}/dev/fuse"; then + mkdir -p "${DESTDIR}/dev" + mknod "${DESTDIR}/dev/fuse" -m 0666 c 10 229 +fi + +install -D -m 644 "${MESON_SOURCE_ROOT}/util/udev.rules" \ + "${prefix}/lib/udev/rules.d/99-fuse3.rules" + +install -D -m 755 "${MESON_SOURCE_ROOT}/util/init_script" \ + "${prefix}/${sysconfdir}/init.d/fuse3" + +if test -x /usr/sbin/update-rc.d; then + # May fail for a DESTDIR installation + /usr/sbin/update-rc.d fuse3 start 34 S . start 41 0 6 . || /bin/true +fi + + diff --git a/util/meson.build b/util/meson.build new file mode 100644 index 0000000..43c4973 --- /dev/null +++ b/util/meson.build @@ -0,0 +1,27 @@ +# Attention, emacs, please use -*- mode: python -*- +# (even though this isn't actually Python code) + +# we re-use mount_util.c from the library, but do want to keep ourself +# as stand-alone as possible. in order to make an out-of-source build +# possible, we "generate" the file from its original location by +# copying it over. +mount_util_c = custom_target('mount_util', + input : '../lib/mount_util.c', + output : 'mount_util.c', + command : ['cp', '-a', '@INPUT@', '@OUTPUT@'], +) + +executable('fusermount3', ['fusermount.c', mount_util_c], + include_directories: include_dirs, + install: true, + install_dir: get_option('bindir')) + +executable('mount.fuse3', ['mount.fuse.c'], + include_directories: include_dirs, + install: true, + install_dir: get_option('sbindir')) + +meson.add_install_script('install_helper.sh', get_option('sysconfdir'), + get_option('bindir'), get_option('libdir')) + +