This patch (even though called RFC) provides as functional
implementation of a read-only subset of SMB2/3 networking
filesystem and allows mountimg remote Windows SMB2/3 shares
as well as Samba shares exposed on Linux instances. Please
note that it does not support mounting old SMB 1 nor CIFS shares.
This patch is heavily based on the work done by Benoit
Canet who implemented NFS filesystem. It also uses lower
level 3rd-party library libsmb2. It just happens that libsmb2
has been implemented by the same author as libnfs uses by
OSv NFS implementation so the interface of libsmb2 is actually
quite similar.
Unlike NFS implementation which requires compiling appropriate
NFS-specific VFS code into kernel and linking libnfs library,
this one is mostly outside of kernel in form of a shared object
(see modules/smbfs) which dynamically links libsmb2 shared object.
This allows for following benefits:
- adding smb2 filesystem is as easy as building and adding smbfs module
- the smbfs object gets dynamically recognized and corresponding vfsops registered
- it is easy to upgrade libsmb2 library to newer version and it can be
also added from host filesystem
It is hoped that this implementation may become a model for future
optional/modular filesystems supported by OSv. Also it should not be
difficult to tweak NFS implementation to follow same model.
Smbfs modules uses high-level sync API of Libsmb2. However
libsmb2 also provides lower level async (callback based) API
which might be beneficial to provide more efficient implementation
of smbfs but given synchronous nature of most of VFS api it is
not clear if async API would help us. Also the fact that VFS provides
coarse-grained locking around individual files may also hinder any
performance/parallelism improvements.
In order to use it one needs to as smbfs to an image and then
mount desired share by using REST app API (see mount-samba.sh)
or add an init script to call mount-fs.so.
Signed-off-by: Waldemar Kozaczuk <
jwkoz...@gmail.com>
---
Makefile | 3 +-
fs/smbfs/smbfs_null_vfsops.cc | 38 ++
fs/vfs/main.cc | 19 +
fs/vfs/vfs_conf.cc | 3 +
modules/libsmb2/Makefile | 19 +
modules/libsmb2/usr.manifest | 9 +
modules/smbfs/.gitignore | 4 +
modules/smbfs/Makefile | 49 +++
modules/smbfs/module.py | 3 +
modules/smbfs/smbfs.cc | 86 +++++
modules/smbfs/smbfs.hh | 68 ++++
modules/smbfs/smbfs_vfsops.cc | 66 ++++
modules/smbfs/smbfs_vnops.cc | 416 ++++++++++++++++++++++
mount-samba.sh | 1 +
tools/mount/{mount-nfs.cc => mount-fs.cc} | 13 +-
usr.manifest.skel | 2 +-
usr_rofs.manifest.skel | 3 +-
17 files changed, 795 insertions(+), 7 deletions(-)
create mode 100644 fs/smbfs/smbfs_null_vfsops.cc
create mode 100644 modules/libsmb2/Makefile
create mode 100644 modules/libsmb2/usr.manifest
create mode 100644 modules/smbfs/.gitignore
create mode 100644 modules/smbfs/Makefile
create mode 100644 modules/smbfs/module.py
create mode 100644 modules/smbfs/smbfs.cc
create mode 100644 modules/smbfs/smbfs.hh
create mode 100644 modules/smbfs/smbfs_vfsops.cc
create mode 100644 modules/smbfs/smbfs_vnops.cc
create mode 100644 mount-samba.sh
rename tools/mount/{mount-nfs.cc => mount-fs.cc} (70%)
diff --git a/Makefile b/Makefile
index 68043485..afb43acd 100644
--- a/Makefile
+++ b/Makefile
@@ -396,7 +396,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)
@@ -1853,6 +1853,7 @@ else
endif
objects += $(addprefix fs/nfs/, $(nfs_o))
+objects += fs/smbfs/smbfs_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
diff --git a/fs/smbfs/smbfs_null_vfsops.cc b/fs/smbfs/smbfs_null_vfsops.cc
new file mode 100644
index 00000000..287f6388
--- /dev/null
+++ b/fs/smbfs/smbfs_null_vfsops.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 Waldemar Kozaczuk.
+ *
+ * Based on ramfs code Copyright (c) 2006-2007, Kohsuke Ohtani
+ *
+ * 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.
+ */
+
+#include <dlfcn.h>
+#include <osv/mount.h>
+#include <osv/debug.h>
+
+#define smbfs_mount ((vfsop_mount_t)vfs_nullop)
+#define smbfs_umount ((vfsop_umount_t)vfs_nullop)
+#define smbfs_sync ((vfsop_sync_t)vfs_nullop)
+#define smbfs_vget ((vfsop_vget_t)vfs_nullop)
+#define smbfs_statfs ((vfsop_statfs_t)vfs_nullop)
+
+/*
+ * File system operations
+ *
+ * This provides a "null" vfsops that is replaced when libsmbfs.so is
+ * loaded upon VFS initialization.
+ */
+struct vfsops smbfs_vfsops = {
+ smbfs_mount, /* mount */
+ smbfs_umount, /* umount */
+ smbfs_sync, /* sync */
+ smbfs_vget, /* vget */
+ smbfs_statfs, /* statfs */
+ nullptr, /* vnops */
+};
+
+int smbfs_init(void)
+{
+ return 0;
+}
diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
index 7cc4291d..c48c52b4 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>
@@ -2267,6 +2268,24 @@ void pivot_rootfs(const char* path)
if (ret)
kprintf("failed to pivot root, error = %s\n", strerror(ret));
+ // Initialize other filesystem libraries if present
+ //TODO: Is this the best place on a filesystem to look for these libraries
+ 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);
+ //TODO: It would be nice to have ends_with() in std::string ;-)
+ 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 4ac695b7..928fd5a0 100644
--- a/fs/vfs/vfs_conf.cc
+++ b/fs/vfs/vfs_conf.cc
@@ -51,6 +51,7 @@ extern struct vfsops devfs_vfsops;
extern struct vfsops nfs_vfsops;
extern struct vfsops procfs_vfsops;
extern struct vfsops zfs_vfsops;
+extern struct vfsops smbfs_vfsops;
extern int ramfs_init(void);
extern int rofs_init(void);
@@ -58,6 +59,7 @@ extern int devfs_init(void);
extern int nfs_init(void);
extern int procfs_init(void);
extern "C" int zfs_init(void);
+extern int smbfs_init(void);
/*
* VFS switch table
@@ -69,5 +71,6 @@ const struct vfssw vfssw[] = {
{"procfs", procfs_init, &procfs_vfsops},
{"zfs", zfs_init, &zfs_vfsops},
{"rofs", rofs_init, &rofs_vfsops},
+ {"smbfs", smbfs_init, &smbfs_vfsops},
{nullptr, fs_noop, nullptr},
};
diff --git a/modules/libsmb2/Makefile b/modules/libsmb2/Makefile
new file mode 100644
index 00000000..f8b87596
--- /dev/null
+++ b/modules/libsmb2/Makefile
@@ -0,0 +1,19 @@
+src = $(shell readlink -f ../..)
+module-dir = $(src)/modules/libsmb2
+
+all: module
+module: libsmb2
+
+libsmb2: upstream/libsmb2/lib/libsmb2.so.2.0.0
+
+.PHONY: libsmb2
+
+upstream/libsmb2/.git:
+ mkdir -p $(module-dir)/upstream && cd $(module-dir)/upstream && \
+ git clone --depth 1
https://github.com/sahlberg/libsmb2.git
+
+upstream/libsmb2/lib/libsmb2.so.2.0.0: upstream/libsmb2/.git
+ cd $(module-dir)/upstream/libsmb2 && cmake . && make
+
+clean:
+ cd $(module-dir) && rm -rf upstream
diff --git a/modules/libsmb2/usr.manifest b/modules/libsmb2/usr.manifest
new file mode 100644
index 00000000..64315911
--- /dev/null
+++ b/modules/libsmb2/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/libsmb2.so.1: ${MODULE_DIR}/upstream/libsmb2/lib/libsmb2.so.2.0.0
diff --git a/modules/smbfs/.gitignore b/modules/smbfs/.gitignore
new file mode 100644
index 00000000..35e4c593
--- /dev/null
+++ b/modules/smbfs/.gitignore
@@ -0,0 +1,4 @@
+obj
+ntlm_user_file
+*.so
+usr.manifest
diff --git a/modules/smbfs/Makefile b/modules/smbfs/Makefile
new file mode 100644
index 00000000..b9b2bcb7
--- /dev/null
+++ b/modules/smbfs/Makefile
@@ -0,0 +1,49 @@
+#TODO: 1. Verify if we need to set all these includes and that the correct flags are set
+#TODO: 2. Understand why smbfs.so is almost 100K (I think it should be smaller)?
+INCLUDES = -I. -I../libsmb2/upstream/libsmb2/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 = smbfs
+CPP_FILES := $(wildcard *.cc)
+OBJ_FILES := $(addprefix obj/,$(CPP_FILES:.cc=.o))
+DEPS := $(OBJ_FILES:.o=.d)
+
+LIBS = -L../libsmb2/upstream/libsmb2/lib -lsmb2
+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/smbfs.so: $${MODULE_DIR}/smbfs.so' > usr.manifest
+ echo '/ntlm_user_file: $${MODULE_DIR}/ntlm_user_file' >> usr.manifest
+
+clean:
+ rm -f $(TARGET)*.so usr.manifest
+ $(call very-quiet, $(RM) -rf obj)
diff --git a/modules/smbfs/module.py b/modules/smbfs/module.py
new file mode 100644
index 00000000..70d017fe
--- /dev/null
+++ b/modules/smbfs/module.py
@@ -0,0 +1,3 @@
+from osv.modules import api
+
+api.require('libsmb2')
diff --git a/modules/smbfs/smbfs.cc b/modules/smbfs/smbfs.cc
new file mode 100644
index 00000000..60d38ef2
--- /dev/null
+++ b/modules/smbfs/smbfs.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 Waldemar Kozaczuk
+ * Based on NFS implementation by Benoit Canet
+ *
+ * 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.
+ */
+
+#include <osv/debug.hh>
+
+#include "smbfs.hh"
+
+namespace smbfs {
+
+mount_context::mount_context(const char *url)
+ : _valid(false),
+ _errno(0),
+ _raw_url(url),
+ _smbfs(nullptr, smb2_destroy_context),
+ _url(nullptr, smb2_destroy_url) {
+
+ /* Create SMB context */
+ _smbfs.reset(smb2_init_context());
+ if (!_smbfs) {
+ debug("mount_context(): failed to create SMB2/3 context\n");
+ _errno = ENOMEM;
+ return;
+ }
+
+ // parse the url while taking care of freeing it when needed
+ _url.reset(smb2_parse_url(_smbfs.get(), url));
+ if (!_url) {
+ debug(std::string("mount_context(): failed to parse URL: ") +
+ std::string(url) + ":" +
+ smb2_get_error(_smbfs.get()) + "\n");
+ _errno = EINVAL;
+ return;
+ }
+
+ smb2_set_security_mode(_smbfs.get(), SMB2_NEGOTIATE_SIGNING_ENABLED);
+
+ _errno = -smb2_connect_share(_smbfs.get(), _url->server, _url->share, _url->user);
+ if (_errno) {
+ debug(std::string("mount_context(): failed to mount share:g: ") +
+ smb2_get_error(_smbfs.get()) + "\n");
+ return;
+ }
+
+ _valid = true;
+}
+
+// __thread is not used for the following because it would give the error
+// non-local variable ‘_lock’ declared ‘__thread’ has a non-trivial destructor
+thread_local mutex _lock;
+thread_local std::unordered_map <std::string,
+std::unique_ptr<mount_context>> _map;
+
+struct mount_context *get_mount_context(struct mount *mp, int &err_no) {
+ auto m_path = static_cast<const char *>(mp->m_path);
+ std::string mount_point(m_path);
+ err_no = 0; //TODO Check when err_no is set as not zero
+
+ SCOPE_LOCK(_lock);
+
+ // if not mounted at all mount it
+ if (!_map.count(mount_point)) {
+ _map[mount_point] =
+ std::unique_ptr<mount_context>(new mount_context(mp->m_special));
+ }
+
+ // if we remounted with a different url change the mount point
+ if (!_map[mount_point]->same_url(mp->m_special)) {
+ _map.erase(mount_point);
+ _map[mount_point] =
+ std::unique_ptr<mount_context>(new mount_context(mp->m_special));
+ }
+
+ // clear the mess if the mount point is invalid
+ if (!_map[mount_point]->is_valid(err_no)) {
+ _map.erase(mount_point);
+ return nullptr;
+ }
+
+ return _map[mount_point].get();
+}
+}
diff --git a/modules/smbfs/smbfs.hh b/modules/smbfs/smbfs.hh
new file mode 100644
index 00000000..0aaef722
--- /dev/null
+++ b/modules/smbfs/smbfs.hh
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 Waldemar Kozaczuk
+ * Based on NFS implementation by Benoit Canet
+ *
+ * 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.
+ */
+
+#ifndef __INCLUDE_SMBFS_H__
+#define __INCLUDE_SMBFS_H__
+
+#include <string.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <thread>
+#include <unordered_map>
+
+#include <osv/mutex.h>
+#include <osv/vnode.h>
+#include <osv/mount.h>
+#include <osv/dentry.h>
+#include <osv/prex.h>
+
+#include "smb2/smb2.h"
+#include "smb2/libsmb2.h"
+
+extern struct vfsops smbfs_vfsops;
+extern struct vnops smbfs_vnops;
+
+namespace smbfs {
+ class mount_context {
+ public:
+ mount_context(const char *url);
+ struct smb2_context *smbfs() {
+ return _smbfs.get();
+ };
+ // Due to my particular hatred of exceptions the is_valid() method
+ // can be used to now if something failed in the constructor.
+ bool is_valid(int &err_no) {
+ err_no = _errno;
+ return _valid;
+ };
+ std::string server() {
+ return std::string(_url->server);
+ }
+ std::string share() {
+ return std::string(_url->path);
+ }
+ bool same_url(std::string url) {
+ return url == _raw_url;
+ }
+ private:
+ bool _valid;
+ int _errno;
+ std::string _raw_url;
+ std::unique_ptr<struct smb2_context,
+ void (*)(struct smb2_context *)> _smbfs;
+ std::unique_ptr<struct smb2_url, void (*)(struct smb2_url *)> _url;
+ };
+
+ struct mount_point *get_mount_point(struct mount *mp);
+
+ struct mount_context *get_mount_context(struct mount *mp, int &err_no);
+}
+
+#endif
diff --git a/modules/smbfs/smbfs_vfsops.cc b/modules/smbfs/smbfs_vfsops.cc
new file mode 100644
index 00000000..af4962a9
--- /dev/null
+++ b/modules/smbfs/smbfs_vfsops.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 Waldemar Kozaczuk
+ * Based on NFS implementation by Benoit Canet respectively
+ *
+ * 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.
+ */
+
+#include <osv/vnode.h>
+#include <osv/mount.h>
+#include <osv/dentry.h>
+
+#include "smbfs.hh"
+
+using namespace smbfs;
+
+/*
+ * Mount a file system.
+ */
+static int
+smbfs_mount(struct mount *mp, const char *dev, int flags, const void *data)
+{
+ assert(mp);
+
+ // build a temporary mount context to check the SMB2/3 server is alive
+ int err_no;
+ std::unique_ptr<mount_context> ctx(new mount_context(mp->m_special));
+
+ if (!ctx->is_valid(err_no)) {
+ return err_no;
+ }
+
+ return 0;
+}
+
+static int
+smbfs_unmount(struct mount *mp, int flags)
+{
+ assert(mp);
+
+ // Make sure nothing is used under this mount point.
+ if (mp->m_count > 1) {
+ return EBUSY;
+ }
+
+ return 0;
+}
+
+// For the following let's rely on operations on individual files
+#define smbfs_sync ((vfsop_sync_t)vfs_nullop)
+#define smbfs_vget ((vfsop_vget_t)vfs_nullop)
+#define smbfs_statfs ((vfsop_statfs_t)vfs_nullop)
+
+// We are relying on vfsops structure defined in kernel
+extern struct vfsops smbfs_vfsops;
+
+// Overwrite "null" vfsops structure fields with "real"
+// functions upon loading smbfs.so shared object
+void __attribute__((constructor)) initialize_vfsops() {
+ smbfs_vfsops.vfs_mount = smbfs_mount;
+ smbfs_vfsops.vfs_unmount = smbfs_unmount;
+ smbfs_vfsops.vfs_sync = smbfs_sync;
+ smbfs_vfsops.vfs_vget = smbfs_vget;
+ smbfs_vfsops.vfs_statfs = smbfs_statfs;
+ smbfs_vfsops.vfs_vnops = &smbfs_vnops;
+}
diff --git a/modules/smbfs/smbfs_vnops.cc b/modules/smbfs/smbfs_vnops.cc
new file mode 100644
index 00000000..992a93e7
--- /dev/null
+++ b/modules/smbfs/smbfs_vnops.cc
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2018 Waldemar Kozaczuk
+ * Based on NFS implementation Benoit Canet
+ *
+ * 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.
+ */
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include <osv/prex.h>
+#include <osv/vnode.h>
+#include <osv/file.h>
+#include <osv/debug.h>
+
+#include <sys/types.h>
+#include "smbfs.hh"
+
+static inline struct smb2_context *get_smb2_context(struct vnode *node,
+ int &err_no)
+{
+ return smbfs::get_mount_context(node->v_mount, err_no)->smbfs();
+}
+
+static inline struct smb2fh *get_file_handle(struct vnode *node)
+{
+ return static_cast<struct smb2fh *>(node->v_data);
+}
+
+static inline struct smb2dir *get_dir_handle(struct vnode *node)
+{
+ return static_cast<struct smb2dir *>(node->v_data);
+}
+
+static const char *get_node_name(struct vnode *node)
+{
+ if (LIST_EMPTY(&node->v_names) == 1) {
+ return nullptr;
+ }
+
+ auto name = LIST_FIRST(&node->v_names)->d_path;
+ if (name && *name == '/')
+ return name + 1;
+ else
+ return name;
+}
+
+static inline std::string mkpath(struct vnode *node, const char *name)
+{
+ std::string path(get_node_name(node));
+ if (path.length() > 0)
+ return path + "/" + name;
+ else
+ return name;
+}
+
+static inline struct timespec to_timespec(uint64_t sec, uint64_t nsec)
+{
+ struct timespec t;
+
+ t.tv_sec = sec;
+ t.tv_nsec = nsec;
+
+ return t;
+}
+
+static int smbfs_open(struct file *fp)
+{
+ struct vnode *vp = file_dentry(fp)->d_vnode;
+
+ // already opened reuse the nfs handle
+ if (vp->v_data) {
+ return 0;
+ }
+
+ // clear read write flags
+ int flags = file_flags(fp);
+ flags &= ~(O_RDONLY | O_WRONLY | O_RDWR);
+
+ // check our rights
+ bool read = !vn_access(vp, VREAD);
+ bool write = !vn_access(vp, VWRITE);
+
+ // Set updated flags
+ if (read && write) {
+ flags |= O_RDWR;
+ } else if (read) {
+ flags |= O_RDONLY;
+ } else if (write) {
+ flags |= O_WRONLY;
+ }
+
+ int err_no;
+ auto smb2 = get_smb2_context(vp, err_no);
+ if (err_no) {
+ return err_no;
+ }
+
+ std::string path(fp->f_dentry->d_path);
+ //
+ // It's a directory or a file.
+ int type = vp->v_type;
+ if (type == VDIR) {
+ struct smb2dir *handle = smb2_opendir(smb2, path.c_str() + 1);
+ if (handle) {
+ vp->v_data = handle;
+ }
+ else {
+ debugf("smbfs_open [%s]: smb2_opendir failed\n", path.c_str());
+ return EIO;
+ }
+ } else if (type == VREG) {
+ struct smb2fh *handle = smb2_open(smb2, path.c_str() + 1, flags);
+ if (handle) {
+ vp->v_data = handle;
+ }
+ else {
+ debugf("smbfs_open [%s]: smb2_open failed\n", path.c_str());
+ return EIO;
+ }
+ } else {
+ return EIO;
+ }
+
+ return 0;
+}
+
+//TODO: Understand why nfs_close does not do anything - was it for a reason?
+static int smbfs_close(struct vnode *vp, struct file *fp)
+{
+ // not opened - ignore
+ if (!vp->v_data) {
+ return 0;
+ }
+
+ int err_no;
+ auto smb2 = get_smb2_context(vp, err_no);
+ if (err_no) {
+ return err_no;
+ }
+
+ int type = vp->v_type;
+ //
+ // It's a directory or a file.
+ if (type == VDIR) {
+ smb2_closedir(smb2, get_dir_handle(vp));
+ vp->v_data = nullptr;
+ } else if (type == VREG) {
+ smb2_close(smb2, get_file_handle(vp));
+ vp->v_data = nullptr;
+ } else {
+ return EIO;
+ }
+
+ return 0;
+}
+
+//
+// This function reads as much data as requested per uio
+static int smbfs_read(struct vnode *vp, struct file *fp, struct uio *uio, int ioflag)
+{
+ if (vp->v_type == VDIR) {
+ return EISDIR;
+ }
+ if (vp->v_type != VREG) {
+ return EINVAL;
+ }
+ if (uio->uio_offset < 0) {
+ return EINVAL;
+ }
+ if (uio->uio_resid == 0) {
+ return 0;
+ }
+
+ if (uio->uio_offset >= (off_t)vp->v_size) {
+ return 0;
+ }
+
+ size_t len;
+ if (vp->v_size - uio->uio_offset < uio->uio_resid)
+ len = vp->v_size - uio->uio_offset;
+ else
+ len = uio->uio_resid;
+
+ int err_no;
+ auto smb2 = get_smb2_context(vp, err_no);
+ if (err_no) {
+ return err_no;
+ }
+
+ auto handle = get_file_handle(vp);
+
+ // FIXME: remove this temporary buffer
+ //TODO: libsmb2 claims zero-copy capability so I wonder if can advantage of
+ //it somehow and avoid this memory copy
+ auto buf = std::unique_ptr<unsigned char>(new unsigned char[len + 1]());
+ int ret = smb2_pread(smb2, handle, buf.get(), len, uio->uio_offset);
+ if (ret < 0) {
+ return -ret;
+ }
+
+ return uiomove(buf.get(), ret, uio);
+}
+
+//
+// This functions reads directory information (dentries) based on information in memory
+// under rofs->dir_entries table
+static int smbfs_readdir(struct vnode *vp, struct file *fp, struct dirent *dir)
+{
+ if (vp->v_type != VDIR) {
+ return ENOTDIR;
+ }
+
+ int err_no;
+ auto smb2 = get_smb2_context(vp, err_no);
+ if (err_no) {
+ return err_no;
+ }
+
+ // query the SMBFS server about this directory entry.
+ auto handle = get_dir_handle(vp);
+ auto smb2dirent = smb2_readdir(smb2, handle);
+
+ //TODO: Please note that smb2_readdir adds full stat information
+ //for each dirent. Would it be possible to somehow cache stat info
+ //so that we do not have to make network trip for each smbfs_lookup?
+
+ // We finished iterating on the directory.
+ if (!smb2dirent) {
+ return ENOENT;
+ }
+
+ // Fill dirent infos
+ assert(sizeof(ino_t) == sizeof(smb2dirent->st.smb2_ino));
+ dir->d_ino = smb2dirent->st.smb2_ino;
+ if (smb2dirent->st.smb2_type == SMB2_TYPE_DIRECTORY ) {
+ dir->d_type = DT_DIR;
+ }
+ else {
+ dir->d_type = DT_REG;
+ }
+ // FIXME: not filling dir->d_off
+ // FIXME: not filling dir->d_reclen
+
+ strlcpy((char *) &dir->d_name, smb2dirent->name, sizeof(dir->d_name));
+
+ // iterate
+ fp->f_offset++;
+
+ return 0;
+}
+
+//
+// This functions looks up directory entry
+static int smbfs_lookup(struct vnode *dvp, char *name, struct vnode **vpp)
+{
+ int err_no;
+ auto smb2 = get_smb2_context(dvp, err_no);
+ if (err_no) {
+ return err_no;
+ }
+
+ std::string path = mkpath(dvp, name);
+ struct vnode *vp;
+
+ // Make sure we don't accidentally return garbage.
+ *vpp = nullptr;
+
+ // Following 4 checks inspired by ZFS code
+ if (!path.size())
+ return ENOENT;
+
+ if (dvp->v_type != VDIR)
+ return ENOTDIR;
+
+ assert(path != ".");
+ assert(path != "..");
+
+ // We must get the inode number so we query the NFS server.
+ struct smb2_stat_64 st;
+ int ret = smb2_stat(smb2, path.c_str(), &st);
+ if (ret) {
+ debugf("smbfs_lookup [%s]: smb2_stat failed with %d\n", name, ret);
+ return -ret;
+ }
+
+ // Filter by inode type: only keep files and directories
+ // Symbolic links for now do not seem to be supported by smb2/3 or this library
+ if (st.smb2_type != SMB2_TYPE_DIRECTORY && st.smb2_type != SMB2_TYPE_FILE) {
+ debugf("smbfs_lookup [%s]: wrong type\n", name);
+ // FIXME: Not sure it's the right error code.
+ return EINVAL;
+ }
+
+ // Create the new vnode or get it from the cache.
+ if (vget(dvp->v_mount, st.smb2_ino, &vp)) {
+ // Present in the cache
+ *vpp = vp;
+ return 0;
+ }
+
+ if (!vp) {
+ return ENOMEM;
+ }
+
+ // Fill in the new vnode informations.
+ vp->v_size = st.smb2_size;
+ vp->v_mount = dvp->v_mount;
+ vp->v_data = nullptr;
+
+ // Get the entry type.
+ if (st.smb2_type == SMB2_TYPE_DIRECTORY) {
+ vp->v_type = VDIR;
+ } else if (st.smb2_type == SMB2_TYPE_FILE) {
+ vp->v_type = VREG;
+ }
+
+ //TODO: vp->v_mode -> It looks like high level API does not have
+ // a way to retrieve flags or any other security info
+ // May need to use raw API
+
+ *vpp = vp;
+
+ return 0;
+}
+
+static int smbfs_getattr(struct vnode *vp, struct vattr *attr)
+{
+ int err_no;
+ auto smb2 = get_smb2_context(vp, err_no);
+ if (err_no) {
+ return err_no;
+ }
+
+ auto path = get_node_name(vp);
+ if (!path) {
+ return ENOENT;
+ }
+
+ // Get the file infos.
+ struct smb2_stat_64 st;
+ int ret = smb2_stat(smb2, path, &st);
+ if (ret) {
+ debugf("smbfs_getattr [%s]: smb2_stat failed with %d\n", path, ret);
+ return -ret;
+ }
+
+ if (st.smb2_type == SMB2_TYPE_DIRECTORY) {
+ attr->va_type = VDIR;
+ } else if (st.smb2_type == SMB2_TYPE_FILE) {
+ attr->va_type = VREG;
+ }
+
+ //TODO: attr->va_mode -> It looks like high level API does not have a way to rerieve flags or any other security info
+ // needs to use raw API
+ attr->va_mode = 0777;
+ attr->va_nlink = st.smb2_nlink;
+ attr->va_nodeid = st.smb2_ino;
+ attr->va_atime = to_timespec(st.smb2_atime, st.smb2_atime_nsec);
+ attr->va_mtime = to_timespec(st.smb2_mtime, st.smb2_mtime_nsec);
+ attr->va_ctime = to_timespec(st.smb2_ctime, st.smb2_ctime_nsec);
+ attr->va_size = st.smb2_size;
+
+ return 0;
+}
+
+#define smbfs_write ((vnop_write_t)vop_erofs)
+#define smbfs_seek ((vnop_seek_t)vop_nullop)
+#define smbfs_ioctl ((vnop_ioctl_t)vop_nullop)
+#define smbfs_create ((vnop_create_t)vop_erofs)
+#define smbfs_remove ((vnop_remove_t)vop_erofs)
+#define smbfs_rename ((vnop_rename_t)vop_erofs)
+#define smbfs_mkdir ((vnop_mkdir_t)vop_erofs)
+#define smbfs_rmdir ((vnop_rmdir_t)vop_erofs)
+#define smbfs_setattr ((vnop_setattr_t)vop_erofs)
+#define smbfs_inactive ((vnop_inactive_t)vop_nullop)
+#define smbfs_truncate ((vnop_truncate_t)vop_erofs)
+#define smbfs_link ((vnop_link_t)vop_erofs)
+#define smbfs_arc ((vnop_cache_t) nullptr)
+#define smbfs_fallocate ((vnop_fallocate_t)vop_erofs)
+#define smbfs_fsync ((vnop_fsync_t)vop_nullop)
+#define smbfs_symlink ((vnop_symlink_t)vop_erofs)
+#define smbfs_readlink ((vnop_readlink_t)vop_nullop)
+
+struct vnops smbfs_vnops = {
+ smbfs_open, /* open */
+ smbfs_close, /* close */
+ smbfs_read, /* read */
+ smbfs_write, /* write - returns error when called */
+ smbfs_seek, /* seek */
+ smbfs_ioctl, /* ioctl */
+ smbfs_fsync, /* fsync */
+ smbfs_readdir, /* readdir */
+ smbfs_lookup, /* lookup */
+ smbfs_create, /* create - returns error when called */
+ smbfs_remove, /* remove - returns error when called */
+ smbfs_rename, /* rename - returns error when called */
+ smbfs_mkdir, /* mkdir - returns error when called */
+ smbfs_rmdir, /* rmdir - returns error when called */
+ smbfs_getattr, /* getattr */
+ smbfs_setattr, /* setattr - returns error when called */
+ smbfs_inactive, /* inactive */
+ smbfs_truncate, /* truncate - returns error when called*/
+ smbfs_link, /* link - returns error when called*/
+ smbfs_arc, /* arc */
+ smbfs_fallocate, /* fallocate - returns error when called*/
+ smbfs_readlink, /* read link */
+ smbfs_symlink /* symbolic link - returns error when called*/
+};
diff --git a/mount-samba.sh b/mount-samba.sh
new file mode 100644
index 00000000..181d30ba
--- /dev/null
+++ b/mount-samba.sh
@@ -0,0 +1 @@
+curl -X PUT --data-urlencode "command=/tools/mount-fs.so smbfs smb://<user>@<host_name>/<share_name> /samba"
http://localhost:8000/app/
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 159b73d5..798a1451 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: %(gccbase)s/lib64/libgcc_s.so.1
/&/etc/hosts: ../../static/&
diff --git a/usr_rofs.manifest.skel b/usr_rofs.manifest.skel
index fde87736..bf71bd32 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: %(gccbase)s/lib64/libgcc_s.so.1
/&/etc/hosts: ../../static/&
@@ -11,3 +11,4 @@
/dev: ../../static
/proc: ../../static
/tmp: ../../static
+/samba: ../../static
--
2.19.1