[PATCH 1/2] fs: move nfs support out of kernel in lieu of a separate pluggable module

24 views
Skip to first unread message

Waldemar Kozaczuk

unread,
Mar 22, 2020, 12:33:39 PM3/22/20
to osv...@googlegroups.com, Waldemar Kozaczuk
This patch removes external/fs/libnfs module and makes nfs support pluggable
by moving it into a module (shared library) that can be added to the image
instead of being compiled into the kernel using nfs=true build option.
The nfs support can be added by adding nfs module to the image.

More specifically:
- external/fs/libnfs is removed and equivalent modules/libnfs gets created
- most fs/nfs code except for fs_null_vfsops.cc gets moved out of a kernel
into new modules/nfs that is built as a shared library
- vfs mount logic is able to dynamically load extra filesystem
libraries from /usr/lib/fs

Completes #1078

Signed-off-by: Waldemar Kozaczuk <jwkoz...@gmail.com>
---
.gitmodules | 3 --
Makefile | 32 ++--------------
external/fs/libnfs | 1 -
fs/nfs/nfs_null_vfsops.cc | 11 +++++-
fs/vfs/main.cc | 17 +++++++++
fs/vfs/vfs_conf.cc | 2 +-
modules/libnfs/.gitignore | 1 +
modules/libnfs/Makefile | 19 ++++++++++
modules/libnfs/usr.manifest | 9 +++++
modules/nfs/.gitignore | 3 ++
modules/nfs/Makefile | 46 +++++++++++++++++++++++
modules/nfs/module.py | 3 ++
{fs => modules}/nfs/nfs.cc | 2 +-
{fs => modules}/nfs/nfs.hh | 2 +-
{fs => modules}/nfs/nfs_vfsops.cc | 24 ++++++------
{fs => modules}/nfs/nfs_vnops.cc | 0
tools/mount/{mount-nfs.cc => mount-fs.cc} | 13 +++++--
usr.manifest.skel | 2 +-
usr_ramfs.manifest.skel | 2 +-
usr_rofs.manifest.skel | 2 +-
20 files changed, 139 insertions(+), 55 deletions(-)
delete mode 160000 external/fs/libnfs
create mode 100644 modules/libnfs/.gitignore
create mode 100644 modules/libnfs/Makefile
create mode 100644 modules/libnfs/usr.manifest
create mode 100644 modules/nfs/.gitignore
create mode 100644 modules/nfs/Makefile
create mode 100644 modules/nfs/module.py
rename {fs => modules}/nfs/nfs.cc (97%)
rename {fs => modules}/nfs/nfs.hh (96%)
rename {fs => modules}/nfs/nfs_vfsops.cc (74%)
rename {fs => modules}/nfs/nfs_vnops.cc (100%)
rename tools/mount/{mount-nfs.cc => mount-fs.cc} (70%)

diff --git a/.gitmodules b/.gitmodules
index b6ae6ac1..17a2c18e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -15,6 +15,3 @@
[submodule "modules/httpserver/osv-gui"]
path = modules/httpserver-html5-gui/osv-gui
url = ../../cloudius-systems/osv-gui.git
-[submodule "external/fs/libnfs"]
- path = external/fs/libnfs
- url = https://github.com/sahlberg/libnfs.git
diff --git a/Makefile b/Makefile
index 03a17920..9401e809 100644
--- a/Makefile
+++ b/Makefile
@@ -4,8 +4,6 @@
# This work is open source software, licensed under the terms of the
# BSD license as described in the LICENSE file in the top-level directory.

-# The nfs=true flag will build in the NFS client filesystem support
-
# Delete the builtin make rules, as if "make -r" was used.
.SUFFIXES:

@@ -142,25 +140,10 @@ check:
./scripts/build check
.PHONY: check

-libnfs-path = external/fs/libnfs/
-
-$(out)/libnfs.a:
- cd $(libnfs-path) && \
- $(call quiet, ./bootstrap) && \
- $(call quiet, ./configure --enable-shared=no --enable-static=yes --enable-silent-rules) && \
- $(call quiet, make)
- $(call quiet, cp -a $(libnfs-path)/lib/.libs/libnfs.a $(out)/libnfs.a)
-
-clean-libnfs:
- if [ -f $(out)/libnfs.a ] ; then \
- cd $(libnfs-path) && \
- make distclean; \
- fi
-
# Remember that "make clean" needs the same parameters that set $(out) in
# the first place, so to clean the output of "make mode=debug" you need to
# do "make mode=debug clean".
-clean: clean-libnfs
+clean:
rm -rf $(out)
rm -f $(outlink) $(outlink2)
.PHONY: clean
@@ -374,7 +357,7 @@ tools += tools/uush/uush.so
tools += tools/uush/ls.so
tools += tools/uush/mkdir.so

-tools += tools/mount/mount-nfs.so
+tools += tools/mount/mount-fs.so
tools += tools/mount/umount.so

ifeq ($(arch),aarch64)
@@ -1849,14 +1832,7 @@ endif

boost-libs := $(boost-lib-dir)/libboost_system$(boost-mt).a

-ifeq ($(nfs), true)
- nfs-lib = $(out)/libnfs.a
- nfs_o = nfs.o nfs_vfsops.o nfs_vnops.o
-else
- nfs_o = nfs_null_vfsops.o
-endif
-
-objects += $(addprefix fs/nfs/, $(nfs_o))
+objects += fs/nfs/nfs_null_vfsops.o

# ld has a known bug (https://sourceware.org/bugzilla/show_bug.cgi?id=6468)
# where if the executable doesn't use shared libraries, its .dynamic section
@@ -1865,7 +1841,7 @@ objects += $(addprefix fs/nfs/, $(nfs_o))
$(out)/dummy-shlib.so: $(out)/dummy-shlib.o
$(call quiet, $(CXX) -nodefaultlibs -shared $(gcc-sysroot) -o $@ $^, LINK $@)

-stage1_targets = $(out)/arch/$(arch)/boot.o $(out)/loader.o $(out)/runtime.o $(drivers:%=$(out)/%) $(objects:%=$(out)/%) $(out)/dummy-shlib.so $(nfs-lib)
+stage1_targets = $(out)/arch/$(arch)/boot.o $(out)/loader.o $(out)/runtime.o $(drivers:%=$(out)/%) $(objects:%=$(out)/%) $(out)/dummy-shlib.so
stage1: $(stage1_targets) links
.PHONY: stage1

diff --git a/external/fs/libnfs b/external/fs/libnfs
deleted file mode 160000
index dc8d8662..00000000
--- a/external/fs/libnfs
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit dc8d86628d2f67cb4b7a9e5e5d7a1259f065b3c7
diff --git a/fs/nfs/nfs_null_vfsops.cc b/fs/nfs/nfs_null_vfsops.cc
index eef09fd1..99befba6 100644
--- a/fs/nfs/nfs_null_vfsops.cc
+++ b/fs/nfs/nfs_null_vfsops.cc
@@ -15,6 +15,13 @@
#define nfs_vget ((vfsop_vget_t)vfs_nullop)
#define nfs_statfs ((vfsop_statfs_t)vfs_nullop)

+static int nfs_noop_mount(struct mount *mp, const char *dev, int flags,
+ const void *data)
+{
+ printf("The nfs module is in-active!. Please add nfs module to the image.\n");
+ return -1;
+}
+
/*
* File system operations
*
@@ -22,8 +29,8 @@
*
*/
struct vfsops nfs_vfsops = {
- nfs_mount, /* mount */
- nfs_umount, /* umount */
+ nfs_noop_mount, /* mount */
+ nfs_umount, /* umount */
nfs_sync, /* sync */
nfs_vget, /* vget */
nfs_statfs, /* statfs */
diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
index 6f224347..46dcb62f 100644
--- a/fs/vfs/main.cc
+++ b/fs/vfs/main.cc
@@ -53,6 +53,7 @@
#include <fcntl.h>
#undef open
#undef fcntl
+#include <dlfcn.h>

#include <osv/prex.h>
#include <osv/vnode.h>
@@ -2305,6 +2306,22 @@ void pivot_rootfs(const char* path)
if (ret)
kprintf("failed to pivot root, error = %s\n", strerror(ret));

+ // Initialize other filesystem libraries if present
+ auto fs_lib_dir = opendir("/usr/lib/fs");
+ if (fs_lib_dir) {
+ while (auto dirent = readdir(fs_lib_dir)) {
+ auto len = strlen(dirent->d_name);
+ if (len >= 3 && strcmp(dirent->d_name + (len - 3), ".so") == 0) {
+ auto lib_path = std::string("/usr/lib/fs/") + dirent->d_name;
+ auto module = dlopen(lib_path.c_str(), RTLD_LAZY);
+ if (module)
+ debugf("VFS: Initialized filesystem library: %s\n", lib_path.c_str());
+ }
+ }
+
+ closedir(fs_lib_dir);
+ }
+
auto ent = setmntent("/etc/fstab", "r");
if (!ent) {
return;
diff --git a/fs/vfs/vfs_conf.cc b/fs/vfs/vfs_conf.cc
index a23ea48f..4a54cb97 100644
--- a/fs/vfs/vfs_conf.cc
+++ b/fs/vfs/vfs_conf.cc
@@ -71,7 +71,7 @@ const struct vfssw vfssw[] = {
{"devfs", devfs_init, &devfs_vfsops},
{"nfs", nfs_init, &nfs_vfsops},
{"procfs", procfs_init, &procfs_vfsops},
- {"sysfs", sysfs_init, &sysfs_vfsops},
+ {"sysfs", sysfs_init, &sysfs_vfsops},
{"zfs", zfs_init, &zfs_vfsops},
{"rofs", rofs_init, &rofs_vfsops},
{"virtiofs", virtiofs_init, &virtiofs_vfsops},
diff --git a/modules/libnfs/.gitignore b/modules/libnfs/.gitignore
new file mode 100644
index 00000000..04595130
--- /dev/null
+++ b/modules/libnfs/.gitignore
@@ -0,0 +1 @@
+upstream
diff --git a/modules/libnfs/Makefile b/modules/libnfs/Makefile
new file mode 100644
index 00000000..66a5a230
--- /dev/null
+++ b/modules/libnfs/Makefile
@@ -0,0 +1,19 @@
+src = $(shell readlink -f ../..)
+module-dir = $(src)/modules/libnfs
+
+all: module
+module: libnfs
+
+libnfs: upstream/libnfs/.git upstream/libnfs/lib/libnfs.so.4.0.0
+
+.PHONY: libnfs
+
+upstream/libnfs/.git:
+ mkdir -p $(module-dir)/upstream && cd $(module-dir)/upstream && \
+ git clone --depth 1 https://github.com/sahlberg/libnfs.git
+
+upstream/libnfs/lib/libnfs.so.4.0.0:
+ cd $(module-dir)/upstream/libnfs && cmake . && make
+
+clean:
+ cd $(module-dir) && rm -rf upstream
diff --git a/modules/libnfs/usr.manifest b/modules/libnfs/usr.manifest
new file mode 100644
index 00000000..847ae537
--- /dev/null
+++ b/modules/libnfs/usr.manifest
@@ -0,0 +1,9 @@
+#
+# Copyright (C) 2018 Waldemar Kozaczuk
+#
+# This work is open source software, licensed under the terms of the
+# BSD license as described in the LICENSE file in the top-level directory.
+#
+
+[manifest]
+/usr/lib/libnfs.so.11.0.0: ${MODULE_DIR}/upstream/libnfs/lib/libnfs.so.4.0.0
diff --git a/modules/nfs/.gitignore b/modules/nfs/.gitignore
new file mode 100644
index 00000000..db4f6ea7
--- /dev/null
+++ b/modules/nfs/.gitignore
@@ -0,0 +1,3 @@
+obj
+*.so
+usr.manifest
diff --git a/modules/nfs/Makefile b/modules/nfs/Makefile
new file mode 100644
index 00000000..fa86780d
--- /dev/null
+++ b/modules/nfs/Makefile
@@ -0,0 +1,46 @@
+INCLUDES = -I. -I../libnfs/upstream/libnfs/include -I../../include
+INCLUDES += -I../../arch/$(ARCH) -I../.. -I../../build/$(mode)/gen/include
+INCLUDES += -isystem ../../include/glibc-compat
+#Only for host, not external
+INCLUDES += $(shell $(CXX) -E -xc++ - -v </dev/null 2>&1 | awk '/^End/ {exit} /^ .*c\+\+/ {print "-isystem" $$0}')
+#
+INCLUDES += -isystem ../../include/api -isystem ../../include/api/$(ARCH) -isystem ../../build/$(mode)/gen/include
+INCLUDES += -isystem ../../bsd/sys -isystem ../../bsd/ -isystem ../../bsd/$(ARCH)
+
+autodepend = -MD -MT $@ -MP
+CXXFLAGS = -g -rdynamic -Wall -std=c++11 -fPIC $(INCLUDES) -D_KERNEL -D_GNU_SOURCE $(autodepend)
+
+# the build target executable:
+TARGET = nfs
+CPP_FILES := $(wildcard *.cc)
+OBJ_FILES := $(addprefix obj/,$(CPP_FILES:.cc=.o))
+DEPS := $(OBJ_FILES:.o=.d)
+
+LIBS = -L../libnfs/upstream/libnfs/lib -lnfs
+ifndef ARCH
+ ARCH = x64
+endif
+ifndef mode
+ mode = release
+endif
+
+quiet = $(if $V, $1, @echo " $2"; $1)
+very-quiet = $(if $V, $1, @$1)
+
+$(TARGET).so: $(OBJ_FILES)
+ $(call quiet, $(CXX) $(CXXFLAGS) -shared -o $(TARGET).so $^ $(LIBS), LINK $@)
+
+obj/%.o: %.cc
+ $(call quiet, $(CXX) $(CXXFLAGS) -c -o $@ $<, CXX $@)
+
+init:
+ @echo " MKDIRS"
+ $(call very-quiet, mkdir -p obj)
+.PHONY: init
+
+module: init $(TARGET).so
+ echo '/usr/lib/fs/nfs.so: $${MODULE_DIR}/nfs.so' > usr.manifest
+
+clean:
+ rm -f $(TARGET)*.so usr.manifest
+ $(call very-quiet, $(RM) -rf obj)
diff --git a/modules/nfs/module.py b/modules/nfs/module.py
new file mode 100644
index 00000000..e3379492
--- /dev/null
+++ b/modules/nfs/module.py
@@ -0,0 +1,3 @@
+from osv.modules import api
+
+api.require('libnfs')
diff --git a/fs/nfs/nfs.cc b/modules/nfs/nfs.cc
similarity index 97%
rename from fs/nfs/nfs.cc
rename to modules/nfs/nfs.cc
index 61740251..6fd8bd16 100644
--- a/fs/nfs/nfs.cc
+++ b/modules/nfs/nfs.cc
@@ -31,7 +31,7 @@ mount_context::mount_context(const char *url)
// parse the url while taking care of freeing it when needed
_url.reset(nfs_parse_url_dir(_nfs.get(), url));
if (!_url) {
- debug(std::string("mount_context():g: ") +
+ debug(std::string("mount_context(): ") +
nfs_get_error(_nfs.get()) + "\n");
_errno = EINVAL;
return;
diff --git a/fs/nfs/nfs.hh b/modules/nfs/nfs.hh
similarity index 96%
rename from fs/nfs/nfs.hh
rename to modules/nfs/nfs.hh
index a078c723..f38b13b1 100644
--- a/fs/nfs/nfs.hh
+++ b/modules/nfs/nfs.hh
@@ -27,7 +27,7 @@

#include <osv/mutex.h>

-#include "../../external/fs/libnfs/include/nfsc/libnfs.h"
+#include "nfsc/libnfs.h"

class mount_context {
public:
diff --git a/fs/nfs/nfs_vfsops.cc b/modules/nfs/nfs_vfsops.cc
similarity index 74%
rename from fs/nfs/nfs_vfsops.cc
rename to modules/nfs/nfs_vfsops.cc
index 8ad1c2dd..9d879b15 100644
--- a/fs/nfs/nfs_vfsops.cc
+++ b/modules/nfs/nfs_vfsops.cc
@@ -69,15 +69,17 @@ int nfs_init(void)
#define nfs_op_vget ((vfsop_vget_t)vfs_nullop)
#define nfs_op_statfs ((vfsop_statfs_t)vfs_nullop)

-/*
- * File system operations
- */
-struct vfsops nfs_vfsops = {
- nfs_op_mount, /* mount */
- nfs_op_unmount, /* unmount */
- nfs_op_sync, /* sync */
- nfs_op_vget, /* vget */
- nfs_op_statfs, /* statfs */
- &nfs_vnops, /* vnops */
-};
+// We are relying on vfsops structure defined in kernel
+extern struct vfsops nfs_vfsops;
+
+// Overwrite "null" vfsops structure fields with "real"
+// functions upon loading nfs.so shared object
+void __attribute__((constructor)) initialize_vfsops() {
+ nfs_vfsops.vfs_mount = nfs_op_mount;
+ nfs_vfsops.vfs_unmount = nfs_op_unmount;
+ nfs_vfsops.vfs_sync = nfs_op_sync;
+ nfs_vfsops.vfs_vget = nfs_op_vget;
+ nfs_vfsops.vfs_statfs = nfs_op_statfs;
+ nfs_vfsops.vfs_vnops = &nfs_vnops;
+}

diff --git a/fs/nfs/nfs_vnops.cc b/modules/nfs/nfs_vnops.cc
similarity index 100%
rename from fs/nfs/nfs_vnops.cc
rename to modules/nfs/nfs_vnops.cc
diff --git a/tools/mount/mount-nfs.cc b/tools/mount/mount-fs.cc
similarity index 70%
rename from tools/mount/mount-nfs.cc
rename to tools/mount/mount-fs.cc
index 9c3da84a..6e6324c0 100644
--- a/tools/mount/mount-nfs.cc
+++ b/tools/mount/mount-fs.cc
@@ -9,28 +9,33 @@
int main(int argc, char **argv)
{
// Check number of arguments
- if (argc != 3) {
+ if (argc != 4) {
std::cout << "Usage:" << std::endl;
std::cout << "\t" << argv[0] <<
+ " nfs" <<
" nfs://<server|ipv4|ipv6>/path[?arg=val[&arg=val]*]" <<
" /mount_point" << std::endl;
return(1);
}

// fetch arguments
- std::string url(argv[1]);
- std::string mount_point(argv[2]);
+ std::string fs_type(argv[1]);
+ std::string url(argv[2]);
+ std::string mount_point(argv[3]);

// create the mount point as a convenience if it does not already exists
mkdir(mount_point.c_str(), 0777);
// Mount and process error
- int ret = mount(url.c_str(), mount_point.c_str(), "nfs", 0, nullptr);
+ int ret = mount(url.c_str(), mount_point.c_str(), fs_type.c_str(), 0, nullptr);
if (ret) {
int my_errno = errno;
std::cout << "Error in mount(): " << strerror(my_errno) << "(" << my_errno << ")"
<< std::endl;
return(1);
}
+ else {
+ std::cout << "Mounted " << url << " at " << mount_point << std::endl;
+ }

return(0);
}
diff --git a/usr.manifest.skel b/usr.manifest.skel
index 32fa8056..3c072d01 100644
--- a/usr.manifest.skel
+++ b/usr.manifest.skel
@@ -7,7 +7,7 @@
/zfs.so: zfs.so
/tools/mkfs.so: tools/mkfs/mkfs.so
/tools/cpiod.so: tools/cpiod/cpiod.so
-/tools/mount-nfs.so: tools/mount/mount-nfs.so
+/tools/mount-fs.so: tools/mount/mount-fs.so
/tools/umount.so: tools/mount/umount.so
/usr/lib/libgcc_s.so.1: %(libgcc_s_dir)s/libgcc_s.so.1
/&/etc/hosts: ../../static/&
diff --git a/usr_ramfs.manifest.skel b/usr_ramfs.manifest.skel
index 36c5e662..4bdfdd32 100644
--- a/usr_ramfs.manifest.skel
+++ b/usr_ramfs.manifest.skel
@@ -1,7 +1,7 @@
[manifest]
/libenviron.so: libenviron.so
/libvdso.so: libvdso.so
-/tools/mount-nfs.so: tools/mount/mount-nfs.so
+/tools/mount-fs.so: tools/mount/mount-fs.so
/tools/umount.so: tools/mount/umount.so
/usr/lib/libgcc_s.so.1: %(libgcc_s_dir)s/libgcc_s.so.1
/&/etc/hosts: ../../static/&
diff --git a/usr_rofs.manifest.skel b/usr_rofs.manifest.skel
index ecca8110..b7d477ec 100644
--- a/usr_rofs.manifest.skel
+++ b/usr_rofs.manifest.skel
@@ -1,7 +1,7 @@
[manifest]
/libenviron.so: libenviron.so
/libvdso.so: libvdso.so
-/tools/mount-nfs.so: tools/mount/mount-nfs.so
+/tools/mount-fs.so: tools/mount/mount-fs.so
/tools/umount.so: tools/mount/umount.so
/usr/lib/libgcc_s.so.1: %(libgcc_s_dir)s/libgcc_s.so.1
/&/etc/hosts: ../../static/&
--
2.20.1

Waldemar Kozaczuk

unread,
Mar 22, 2020, 12:33:42 PM3/22/20
to osv...@googlegroups.com, Waldemar Kozaczuk
This patch moves nfs testing logic from ./scripts/test.py
into ./modules/nfs-tests/test.py.

It also refreshes nfs-tests to pull and build UNFS3 (NFS in user space)
from a new repo. Eventually it adds new 'check' target
that builds the test image (nfs,nfs-tests) and runs test.py against it.

Signed-off-by: Waldemar Kozaczuk <jwkoz...@gmail.com>
---
modules/nfs-tests/.gitignore | 4 ++
modules/nfs-tests/Makefile | 32 +++++++++----
modules/nfs-tests/test.py | 90 ++++++++++++++++++++++++++++++++++++
modules/nfs-tests/tst-nfs.cc | 64 ++++++++++++++-----------
scripts/test.py | 62 ++-----------------------
5 files changed, 157 insertions(+), 95 deletions(-)
create mode 100644 modules/nfs-tests/.gitignore
create mode 100755 modules/nfs-tests/test.py

diff --git a/modules/nfs-tests/.gitignore b/modules/nfs-tests/.gitignore
new file mode 100644
index 00000000..a2666565
--- /dev/null
+++ b/modules/nfs-tests/.gitignore
@@ -0,0 +1,4 @@
+*.so
+*.d
+unfsd.bin
+upstream
diff --git a/modules/nfs-tests/Makefile b/modules/nfs-tests/Makefile
index 852df8bd..86379e81 100644
--- a/modules/nfs-tests/Makefile
+++ b/modules/nfs-tests/Makefile
@@ -1,17 +1,18 @@
quiet = $(if $V, $1, @echo " $2"; $1)

-UNFS_URL="https://github.com/benoit-canet/unfsd.git"
+UNFS_URL="https://github.com/unfs3/unfs3.git"

autodepend = -MD -MT $@ -MP

-src := $(OSV_BASE)
-out := $(OSV_BUILD_PATH)
+src = $(shell readlink -f ../..)
arch := $(ARCH)

-INCLUDES = -I$(src)/arch/$(ARCH) -I$(src) -I$(src)/include \
- -I$(src)/arch/common -isystem $(src)/include/glibc-compat \
- -isystem $(src)/include/api -isystem $(src)/include/api/$(ARCH) \
- -isystem $(out)/gen/include
+INCLUDES = -I../../include
+INCLUDES += -I../../arch/$(ARCH) -I../.. -I../../build/$(mode)/gen/include
+INCLUDES += -isystem ../../include/glibc-compat
+INCLUDES += $(shell $(CXX) -E -xc++ - -v </dev/null 2>&1 | awk '/^End/ {exit} /^ .*c\+\+/ {print "-isystem" $$0}')
+INCLUDES += -isystem ../../include/api -isystem ../../include/api/$(ARCH) -isystem ../../build/$(mode)/gen/include
+INCLUDES += -isystem ../../bsd/sys -isystem ../../bsd/ -isystem ../../bsd/$(ARCH)

CORE = $(autodepend) $(INCLUDES) -g -O2 -fPIC
COMMON = $(CORE) -DBOOST_TEST_DYN_LINK \
@@ -24,14 +25,25 @@ CFLAGS = -std=gnu99 $(COMMON)
module: unfsd.bin tst-nfs fsx-linux

unfsd.bin:
- $(call quiet, git clone $(UNFS_URL)) && \
- $(call quiet, cd unfsd) && \
+ $(call quiet, mkdir -p upstream) && \
+ $(call quiet, git clone $(UNFS_URL) upstream/unfs3) && \
+ $(call quiet, cd upstream/unfs3) && \
+ $(call quiet, ./bootstrap) && \
$(call quiet, ./configure) && \
$(call quiet, make)
- $(call quiet, cp unfsd/unfsd unfsd.bin)
+ $(call quiet, cp upstream/unfs3/unfsd unfsd.bin)

tst-nfs:
$(call quiet, $(CXX) $(CXXFLAGS) -shared -o tst-nfs.so tst-nfs.cc, LINK $@)

fsx-linux:
$(call quiet, $(CC) $(CORE) -shared -o fsx-linux.so fsx-linux.c, LINK $@)
+
+check:
+ cd $(src) && \
+ make image=nfs,nfs-tests && \
+ PYTHONPATH=$(src)/scripts modules/nfs-tests/test.py
+
+clean:
+ $(call quiet, rm -rf upstream, RM upstream)
+ $(call quiet, rm -f *.so unfsd.bin *.d, RM *.so)
diff --git a/modules/nfs-tests/test.py b/modules/nfs-tests/test.py
new file mode 100755
index 00000000..b4797bf6
--- /dev/null
+++ b/modules/nfs-tests/test.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+
+import time
+import tempfile
+import atexit
+from tests.testing import *
+
+def make_export_and_conf():
+ export_dir = tempfile.mkdtemp(prefix='share')
+ os.chmod(export_dir, 0o777)
+ (conf_fd, conf_path) = tempfile.mkstemp(prefix='export')
+ conf = os.fdopen(conf_fd, "w")
+ conf.write("%s 127.0.0.1(insecure,rw)\n" % export_dir)
+ conf.flush()
+ conf.close()
+ return (conf_path, export_dir)
+
+proc = None
+conf_path = None
+export_dir = None
+
+def kill_unfsd():
+ global proc, conf_path, export_dir
+ proc.kill()
+ proc.wait()
+ if conf_path and os.path.exists(conf_path):
+ os.unlink(conf_path)
+ if export_dir and os.path.exists(export_dir):
+ import shutil
+ shutil.rmtree(export_dir, ignore_errors=True)
+
+dirname = os.path.dirname(os.path.abspath(__file__))
+UNFSD = dirname + "/unfsd.bin"
+
+def run_test():
+ global proc, conf_path, export_dir
+ start = time.time()
+
+ if not os.path.exists(UNFSD):
+ print("Please do:\n\tmake nfs-server")
+ sys.exit(1)
+
+ (conf_path, export_dir) = make_export_and_conf()
+
+ ret = subprocess.call(['rpcinfo'])
+ if ret != 0:
+ print('Please install rpcbind!')
+ exit(-1)
+
+ proc = subprocess.Popen([os.path.join(os.getcwd(), UNFSD),
+ "-t",
+ "-d",
+ "-s",
+ "-l", "127.0.0.1",
+ "-e", conf_path ],
+ stdin = sys.stdin,
+ stdout = subprocess.PIPE,
+ stderr = sys.stderr,
+ shell = False)
+ atexit.register(kill_unfsd)
+ test = SingleCommandTest('nfs-test',
+ "/tst-nfs.so --server 192.168.122.1 --share %s" %
+ export_dir)
+
+ line = proc.stdout.readline().decode()
+ while line:
+ print(line)
+ if "/tmp" in line:
+ break
+ line = proc.stdout.readline().decode()
+
+ sys.stdout.write("NFS Test \n")
+ sys.stdout.write("Shared directory: [%s]\n" % export_dir)
+ sys.stdout.flush()
+
+ try:
+ test.run()
+ except:
+ sys.stdout.write("NFS Test FAILED\n")
+ sys.stdout.flush()
+ raise
+ end = time.time()
+
+ duration = end - start
+ sys.stdout.write("OK (%.3f s)\n" % duration)
+ sys.stdout.flush()
+ kill_unfsd()
+
+set_verbose_output(True)
+run_test()
diff --git a/modules/nfs-tests/tst-nfs.cc b/modules/nfs-tests/tst-nfs.cc
index dbc0026b..f41c5f77 100644
--- a/modules/nfs-tests/tst-nfs.cc
+++ b/modules/nfs-tests/tst-nfs.cc
@@ -10,13 +10,11 @@
#include <fstream>
#include <string>
#include <thread>
-
-#include <boost/program_options.hpp>
+#include <set>

#include <osv/app.hh>
#include <osv/run.hh>
-
-namespace po = boost::program_options;
+#include <osv/options.hh>

void assert_mount_error(int ret)
{
@@ -121,7 +119,7 @@ static void test_rename(std::string mount_point, std::string src,
static void test_truncate(std::string mount_point, std::string path)
{
std::string full_path = mount_point + "/" + path;
- int fd = creat(full_path.c_str(), 0500);
+ int fd = creat(full_path.c_str(), 0700);
struct stat st;

assert(fd != -1);
@@ -305,37 +303,51 @@ void test_trunc(std::string mount_point, std::string path)
assert(0 == fclose(ff));
}

+static void usage()
+{
+ std::cout << "Allowed options:\n";
+ std::cout << " --help produce help message\n";
+ std::cout << " --server arg set server ip\n";
+ std::cout << " --share arg set remote share\n\n";
+}
+
+static void handle_parse_error(const std::string &message)
+{
+ std::cout << message << std::endl;
+ usage();
+ exit(1);
+}
+
int main(int argc, char **argv)
{
- po::options_description desc("Allowed options");
- desc.add_options()
- ("help", "produce help message")
- ("server", po::value<std::string>(), "set server ip")
- ("share", po::value<std::string>(), "set remote share")
- ;
-
- po::variables_map vm;
- po::store(po::parse_command_line(argc, argv, desc), vm);
- po::notify(vm);
-
- if (vm.count("help")) {
- std::cout << desc << std::endl;
+ auto options_values = options::parse_options_values(argc - 1, argv + 1, handle_parse_error);
+
+ if (options::extract_option_flag(options_values, "help", handle_parse_error)) {
+ usage();
return 1;
}

- std::string server;
- if (vm.count("server")) {
- server = vm["server"].as<std::string>();
+ std::string server, share;
+ if (options::option_value_exists(options_values, "server")) {
+ server = options::extract_option_value(options_values, "server");
} else {
- std::cout << desc << std::endl;
+ usage();
return 1;
}

- std::string share;
- if (vm.count("share")) {
- share = vm["share"].as<std::string>();
+ if (options::option_value_exists(options_values, "share")) {
+ share = options::extract_option_value(options_values, "share");
} else {
- std::cout << desc << std::endl;
+ usage();
+ return 1;
+ }
+
+ if (!options_values.empty()) {
+ for (auto other_option : options_values) {
+ std::cout << "Unrecognized option: " << other_option.first << std::endl;
+ }
+
+ usage();
return 1;
}

diff --git a/scripts/test.py b/scripts/test.py
index 1dea288a..0cf787fd 100755
--- a/scripts/test.py
+++ b/scripts/test.py
@@ -1,8 +1,6 @@
#!/usr/bin/env python3
-import atexit
import subprocess
import argparse
-import tempfile
import glob
import time
import sys
@@ -107,32 +105,10 @@ def pluralize(word, count):
return word
return word + 's'

-def make_export_and_conf():
- export_dir = tempfile.mkdtemp(prefix='share')
- os.chmod(export_dir, 0o777)
- (conf_fd, conf_path) = tempfile.mkstemp(prefix='export')
- conf = os.fdopen(conf_fd, "w")
- conf.write("%s 127.0.0.1(insecure,rw)\n" % export_dir)
- conf.flush()
- conf.close()
- return (conf_path, export_dir)
-
-proc = None
-
-def kill_unfsd():
- global proc
- subprocess.call(["sudo", "kill", str(proc.pid + 1)])
- proc.wait()
-
-UNFSD = "./modules/nfs-tests/unfsd.bin"
-
def run_tests():
- global proc
start = time.time()

- if cmdargs.nfs:
- pass
- elif cmdargs.name:
+ if cmdargs.name:
tests_to_run = list((t for t in tests if re.match('^' + cmdargs.name + '$', t.name)))
if not tests_to_run:
print('No test matches: ' + cmdargs.name)
@@ -140,38 +116,7 @@ def run_tests():
else:
tests_to_run = tests

- if cmdargs.nfs:
- if not os.path.exists(UNFSD):
- print("Please do:\n\tmake nfs-server")
- sys.exit(1)
- (conf_path, export_dir) = make_export_and_conf()
- proc = subprocess.Popen([ "sudo",
- os.path.join(os.getcwd(), UNFSD),
- "-t",
- "-d",
- "-s",
- "-l", "127.0.0.1",
- "-e", conf_path ],
- stdin = sys.stdin,
- stdout = subprocess.PIPE,
- stderr = sys.stderr,
- shell = False)
- atexit.register(kill_unfsd)
- tests_to_run = [ SingleCommandTest('nfs-test',
- "/tst-nfs.so --server 192.168.122.1 --share %s" %
- export_dir) ]
-
- line = proc.stdout.readline().decode()
- while line:
- print(line)
- if "/tmp" in line:
- break
- line = proc.stdout.readline().decode()
-
-
- run(tests_to_run)
- kill_unfsd()
- elif cmdargs.single:
+ if cmdargs.single:
if tests_to_run != tests:
print('Cannot restrict the set of tests when --single option is used')
exit(1)
@@ -196,9 +141,8 @@ if __name__ == "__main__":
parser.add_argument("-v", "--verbose", action="store_true", help="verbose test output")
parser.add_argument("-r", "--repeat", action="store_true", help="repeat until test fails")
parser.add_argument("-s", "--single", action="store_true", help="run as much tests as possible in a single OSv instance")
- parser.add_argument("-n", "--nfs", action="store_true", help="run nfs test in a single OSv instance")
parser.add_argument("-p", "--hypervisor", action="store", default="qemu", help="choose hypervisor to run: qemu, firecracker")
- parser.add_argument("--name", action="store", help="run all tests whose names match given regular expression")
+ parser.add_argument("-n", "--name", action="store", help="run all tests whose names match given regular expression")
parser.add_argument("--run_options", action="store", help="pass extra options to run.py")
parser.add_argument("-m", "--manifest", action="store", default="modules/tests/usr.manifest", help="test manifest")
parser.add_argument("-b", "--blacklist", action="append", help="test to be blacklisted", default=[])
--
2.20.1

Commit Bot

unread,
Mar 27, 2020, 11:35:06 AM3/27/20
to osv...@googlegroups.com, Waldemar Kozaczuk
From: Waldemar Kozaczuk <jwkoz...@gmail.com>
Committer: Waldemar Kozaczuk <jwkoz...@gmail.com>
Branch: master

tests: move nfs testing out to a separate module

This patch moves nfs testing logic from ./scripts/test.py
into ./modules/nfs-tests/test.py.

It also refreshes nfs-tests to pull and build UNFS3 (NFS in user space)
from a new repo. Eventually it adds new 'check' target
that builds the test image (nfs,nfs-tests) and runs test.py against it.

Signed-off-by: Waldemar Kozaczuk <jwkoz...@gmail.com>

---
diff --git a/modules/nfs-tests/.gitignore b/modules/nfs-tests/.gitignore
--- a/modules/nfs-tests/.gitignore
+++ b/modules/nfs-tests/.gitignore
@@ -0,0 +1,4 @@
+*.so
+*.d
+unfsd.bin
+upstream
diff --git a/modules/nfs-tests/Makefile b/modules/nfs-tests/Makefile
--- a/modules/nfs-tests/test.py
--- a/scripts/test.py
+++ b/scripts/test.py
@@ -1,8 +1,6 @@
#!/usr/bin/env python3
-import atexit
import subprocess
import argparse
-import tempfile
import glob
import time
import sys
@@ -107,71 +105,18 @@ def pluralize(word, count):
exit(1)
@@ -196,9 +141,8 @@ def main():

Commit Bot

unread,
Mar 27, 2020, 11:35:06 AM3/27/20
to osv...@googlegroups.com, Waldemar Kozaczuk
From: Waldemar Kozaczuk <jwkoz...@gmail.com>
Committer: Waldemar Kozaczuk <jwkoz...@gmail.com>
Branch: master

fs: move nfs support out of kernel in lieu of a separate pluggable module

This patch removes external/fs/libnfs module and makes nfs support pluggable
by moving it into a module (shared library) that can be added to the image
instead of being compiled into the kernel using nfs=true build option.
The nfs support can be added by adding nfs module to the image.

More specifically:
- external/fs/libnfs is removed and equivalent modules/libnfs gets created
- most fs/nfs code except for fs_null_vfsops.cc gets moved out of a kernel
into new modules/nfs that is built as a shared library
- vfs mount logic is able to dynamically load extra filesystem
libraries from /usr/lib/fs

Completes #1078

Signed-off-by: Waldemar Kozaczuk <jwkoz...@gmail.com>

---
diff --git a/.gitmodules b/.gitmodules
--- a/.gitmodules
+++ b/.gitmodules
@@ -15,6 +15,3 @@
[submodule "modules/httpserver/osv-gui"]
path = modules/httpserver-html5-gui/osv-gui
url = ../../cloudius-systems/osv-gui.git
-[submodule "external/fs/libnfs"]
- path = external/fs/libnfs
- url = https://github.com/sahlberg/libnfs.git
diff --git a/Makefile b/Makefile
--- a/external/fs/libnfs
+++ b/external/fs/libnfs
@@ -1 +0,0 @@
-Subproject commit dc8d86628d2f67cb4b7a9e5e5d7a1259f065b3c7
diff --git a/fs/nfs/nfs_null_vfsops.cc b/fs/nfs/nfs_null_vfsops.cc
--- a/fs/nfs/nfs_null_vfsops.cc
+++ b/fs/nfs/nfs_null_vfsops.cc
@@ -15,15 +15,22 @@
#define nfs_vget ((vfsop_vget_t)vfs_nullop)
#define nfs_statfs ((vfsop_statfs_t)vfs_nullop)

+static int nfs_noop_mount(struct mount *mp, const char *dev, int flags,
+ const void *data)
+{
+ printf("The nfs module is in-active!. Please add nfs module to the image.\n");
+ return -1;
+}
+
/*
* File system operations
*
* This desactivate the NFS file system when libnfs is not compiled in.
*
*/
struct vfsops nfs_vfsops = {
- nfs_mount, /* mount */
- nfs_umount, /* umount */
+ nfs_noop_mount, /* mount */
+ nfs_umount, /* umount */
nfs_sync, /* sync */
nfs_vget, /* vget */
nfs_statfs, /* statfs */
diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
--- a/fs/vfs/vfs_conf.cc
+++ b/fs/vfs/vfs_conf.cc
@@ -71,7 +71,7 @@ const struct vfssw vfssw[] = {
{"devfs", devfs_init, &devfs_vfsops},
{"nfs", nfs_init, &nfs_vfsops},
{"procfs", procfs_init, &procfs_vfsops},
- {"sysfs", sysfs_init, &sysfs_vfsops},
+ {"sysfs", sysfs_init, &sysfs_vfsops},
{"zfs", zfs_init, &zfs_vfsops},
{"rofs", rofs_init, &rofs_vfsops},
{"virtiofs", virtiofs_init, &virtiofs_vfsops},
diff --git a/modules/libnfs/.gitignore b/modules/libnfs/.gitignore
--- a/modules/libnfs/.gitignore
+++ b/modules/libnfs/.gitignore
@@ -0,0 +1 @@
+upstream
diff --git a/modules/libnfs/Makefile b/modules/libnfs/Makefile
--- a/modules/libnfs/Makefile
+++ b/modules/libnfs/Makefile
@@ -0,0 +1,19 @@
+src = $(shell readlink -f ../..)
+module-dir = $(src)/modules/libnfs
+
+all: module
+module: libnfs
+
+libnfs: upstream/libnfs/.git upstream/libnfs/lib/libnfs.so.4.0.0
+
+.PHONY: libnfs
+
+upstream/libnfs/.git:
+ mkdir -p $(module-dir)/upstream && cd $(module-dir)/upstream && \
+ git clone --depth 1 https://github.com/sahlberg/libnfs.git
+
+upstream/libnfs/lib/libnfs.so.4.0.0:
+ cd $(module-dir)/upstream/libnfs && cmake . && make
+
+clean:
+ cd $(module-dir) && rm -rf upstream
diff --git a/modules/libnfs/usr.manifest b/modules/libnfs/usr.manifest
--- a/modules/libnfs/usr.manifest
+++ b/modules/libnfs/usr.manifest
@@ -0,0 +1,9 @@
+#
+# Copyright (C) 2018 Waldemar Kozaczuk
+#
+# This work is open source software, licensed under the terms of the
+# BSD license as described in the LICENSE file in the top-level directory.
+#
+
+[manifest]
+/usr/lib/libnfs.so.11.0.0: ${MODULE_DIR}/upstream/libnfs/lib/libnfs.so.4.0.0
diff --git a/modules/nfs/.gitignore b/modules/nfs/.gitignore
--- a/modules/nfs/.gitignore
+++ b/modules/nfs/.gitignore
@@ -0,0 +1,3 @@
+obj
+*.so
+usr.manifest
diff --git a/modules/nfs/Makefile b/modules/nfs/Makefile
--- a/modules/nfs/Makefile
+++ b/modules/nfs/Makefile
@@ -0,0 +1,46 @@
+INCLUDES = -I. -I../libnfs/upstream/libnfs/include -I../../include
+INCLUDES += -I../../arch/$(ARCH) -I../.. -I../../build/$(mode)/gen/include
+INCLUDES += -isystem ../../include/glibc-compat
+#Only for host, not external
+INCLUDES += $(shell $(CXX) -E -xc++ - -v </dev/null 2>&1 | awk '/^End/ {exit} /^ .*c\+\+/ {print "-isystem" $$0}')
+#
+INCLUDES += -isystem ../../include/api -isystem ../../include/api/$(ARCH) -isystem ../../build/$(mode)/gen/include
+INCLUDES += -isystem ../../bsd/sys -isystem ../../bsd/ -isystem ../../bsd/$(ARCH)
--- a/modules/nfs/module.py
+++ b/modules/nfs/module.py
@@ -0,0 +1,3 @@
+from osv.modules import api
+
+api.require('libnfs')
diff --git a/modules/nfs/nfs.cc b/modules/nfs/nfs.cc
--- a/modules/nfs/nfs.cc
+++ b/modules/nfs/nfs.cc
@@ -31,7 +31,7 @@ mount_context::mount_context(const char *url)
// parse the url while taking care of freeing it when needed
_url.reset(nfs_parse_url_dir(_nfs.get(), url));
if (!_url) {
- debug(std::string("mount_context():g: ") +
+ debug(std::string("mount_context(): ") +
nfs_get_error(_nfs.get()) + "\n");
_errno = EINVAL;
return;
diff --git a/modules/nfs/nfs.hh b/modules/nfs/nfs.hh
--- a/modules/nfs/nfs.hh
+++ b/modules/nfs/nfs.hh
@@ -27,7 +27,7 @@

#include <osv/mutex.h>

-#include "../../external/fs/libnfs/include/nfsc/libnfs.h"
+#include "nfsc/libnfs.h"

class mount_context {
public:
diff --git a/modules/nfs/nfs_vfsops.cc b/modules/nfs/nfs_vfsops.cc
--- a/modules/nfs/nfs_vfsops.cc
diff --git a/modules/nfs/nfs_vnops.cc b/modules/nfs/nfs_vnops.cc
--- a/modules/nfs/nfs_vnops.cc
+++ b/modules/nfs/nfs_vnops.cc
null
diff --git a/tools/mount/mount-fs.cc b/tools/mount/mount-fs.cc
--- a/tools/mount/mount-fs.cc
+++ b/tools/mount/mount-fs.cc
@@ -9,28 +9,33 @@
int main(int argc, char **argv)
{
--- a/usr.manifest.skel
+++ b/usr.manifest.skel
@@ -7,7 +7,7 @@
/zfs.so: zfs.so
/tools/mkfs.so: tools/mkfs/mkfs.so
/tools/cpiod.so: tools/cpiod/cpiod.so
-/tools/mount-nfs.so: tools/mount/mount-nfs.so
+/tools/mount-fs.so: tools/mount/mount-fs.so
/tools/umount.so: tools/mount/umount.so
/usr/lib/libgcc_s.so.1: %(libgcc_s_dir)s/libgcc_s.so.1
/&/etc/hosts: ../../static/&
diff --git a/usr_ramfs.manifest.skel b/usr_ramfs.manifest.skel
--- a/usr_ramfs.manifest.skel
+++ b/usr_ramfs.manifest.skel
@@ -1,7 +1,7 @@
[manifest]
/libenviron.so: libenviron.so
/libvdso.so: libvdso.so
-/tools/mount-nfs.so: tools/mount/mount-nfs.so
+/tools/mount-fs.so: tools/mount/mount-fs.so
/tools/umount.so: tools/mount/umount.so
/usr/lib/libgcc_s.so.1: %(libgcc_s_dir)s/libgcc_s.so.1
/&/etc/hosts: ../../static/&
diff --git a/usr_rofs.manifest.skel b/usr_rofs.manifest.skel

Nadav Har'El

unread,
Mar 29, 2020, 5:20:29 AM3/29/20
to Waldemar Kozaczuk, Osv Dev
Looks nice.

I see you added a general technique whereby every .so in /usr/lib/fs is loaded automatically during boot (not only when mount() is actually called).
Make a lot of sense. We can even make it more general, e.g., /lib/modules (or any other name), indicating in general features loaded at boot-time, not just filesystems.

--
Nadav Har'El
n...@scylladb.com


--
You received this message because you are subscribed to the Google Groups "OSv Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to osv-dev+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/osv-dev/00000000000053cb0105a1d7d801%40google.com.
Reply all
Reply to author
Forward
0 new messages