Feel free to comment on it. You will see numerous "//TODO"s with questions or considerations.
Waldek PS. I hope that Benoit Canet will see this patch and find some time to comment on it. On Wednesday, December 12, 2018 at 11:54:03 PM UTC-5, Waldek Kozaczuk wrote: > > 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 <[email protected]> > --- > 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 > > -- 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 [email protected]. For more options, visit https://groups.google.com/d/optout.
