ksmbd-tools: update to 3.4.6
authorRosen Penev <rosenp@gmail.com>
Thu, 13 Oct 2022 19:27:07 +0000 (12:27 -0700)
committerRosen Penev <rosenp@gmail.com>
Sun, 6 Nov 2022 20:39:03 +0000 (12:39 -0800)
Switch to git tarball as the meson files did not get added to the
official one.

Backport busybox style binaries. Saves on size.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
net/ksmbd-tools/Makefile
net/ksmbd-tools/patches/010-meson.patch [deleted file]
net/ksmbd-tools/patches/010-muon.patch [new file with mode: 0644]
net/ksmbd-tools/patches/020-meson.patch [new file with mode: 0644]
net/ksmbd-tools/patches/030-glib.patch [new file with mode: 0644]

index e8a7482f78488b601ed190d35c2fceef14fcb672..8cb6981114bada2129c5719d82b7ce4a065d9f5c 100644 (file)
@@ -1,12 +1,12 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=ksmbd-tools
-PKG_VERSION:=3.4.5
 PKG_RELEASE:=$(AUTORELEASE)
 
-PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
-PKG_SOURCE_URL:=https://codeload.github.com/cifsd-team/ksmbd-tools/tar.gz/$(PKG_VERSION)?
-PKG_HASH:=e22e5bef29ffa2670fc82c490ad4dc6eb00963b4f963dd1852c811b437c77ee1
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_URL:=https://github.com/cifsd-team/ksmbd-tools
+PKG_SOURCE_VERSION:=3.4.6
+PKG_MIRROR_HASH:=c78dace3320cf8a273738b8f3e67bed24c812695f2fab2fbaeae06ec8a15cb77
 
 PKG_LICENSE:=GPL-2.0-or-later
 PKG_LICENSE_FILES:=COPYING
@@ -17,9 +17,6 @@ include $(INCLUDE_DIR)/package.mk
 include $(INCLUDE_DIR)/nls.mk
 include $(INCLUDE_DIR)/meson.mk
 
-TAR_OPTIONS+= --strip-components 1
-TAR_CMD=$(HOST_TAR) -C $(1) $(TAR_OPTIONS)
-
 define Package/ksmbd-tools/Default
   SECTION:=net
   CATEGORY:=Network
@@ -50,27 +47,6 @@ define Package/ksmbd-server/config
   select PACKAGE_wsdd2
 endef
 
-define Package/ksmbd-utils
-  $(call Package/ksmbd-tools/Default)
-  TITLE+= user management-util
-endef
-
-define Package/ksmbd-utils/description
-  installs: ksmbd.adduser (ksmbd.addshare)
-
-  Tool needed to create the ksmbdpwd.db, to manage per user share passwords.
-  NOTE: Not needed for 'guest only' shares.
-endef
-
-define Package/ksmbd-utils/config
-       config KSMBD_UTILS_SHAREADD
-               bool "Add ksmbd.addshare util"
-               depends on PACKAGE_ksmbd-utils
-               help
-                       Add the ksmbd.addshare tool, to directly manipulate the /etc/ksmbd/smb.conf.
-               default n
-endef
-
 define Package/ksmbd-avahi-service
   $(call Package/ksmbd-tools/Default)
   TITLE+= (Avahi service)
@@ -98,23 +74,19 @@ TARGET_CFLAGS += -ffunction-sections -fdata-sections
 TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed
 
 define Package/ksmbd-server/install
+       $(INSTALL_DIR) $(1)/usr/libexec
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/libexec/ksmbd.tools $(1)/usr/libexec/
        $(INSTALL_DIR) $(1)/usr/sbin
-       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.mountd $(1)/usr/sbin/
+       $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.addshare $(1)/usr/sbin/
+       $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.adduser $(1)/usr/sbin/
+       $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.control $(1)/usr/sbin/
+       $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.mountd $(1)/usr/sbin/
        $(INSTALL_DIR) $(1)/etc/config $(1)/etc/ksmbd $(1)/etc/init.d
        $(INSTALL_CONF) ./files/ksmbd.config $(1)/etc/config/ksmbd
        $(INSTALL_DATA) ./files/smb.conf.template $(1)/etc/ksmbd/
        $(INSTALL_BIN) ./files/ksmbd.init $(1)/etc/init.d/ksmbd
        # copy examples until we have a wiki page
        $(INSTALL_DATA) ./files/ksmbd.config.example $(1)/etc/ksmbd/
-       $(INSTALL_DATA) $(PKG_BUILD_DIR)/Documentation/configuration.txt $(1)/etc/ksmbd/
-endef
-
-define Package/ksmbd-utils/install
-       $(INSTALL_DIR) $(1)/usr/sbin
-       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.adduser $(1)/usr/sbin/
-ifeq ($(CONFIG_KSMBD_UTILS_SHAREADD),y)
-       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.addshare $(1)/usr/sbin/
-endif
 endef
 
 define Package/ksmbd-avahi-service/install
@@ -139,6 +111,5 @@ define Package/ksmbd-avahi-service/conffiles
 endef
 
 $(eval $(call BuildPackage,ksmbd-server))
-$(eval $(call BuildPackage,ksmbd-utils))
 $(eval $(call BuildPackage,ksmbd-avahi-service))
 $(eval $(call BuildPackage,ksmbd-hotplug))
diff --git a/net/ksmbd-tools/patches/010-meson.patch b/net/ksmbd-tools/patches/010-meson.patch
deleted file mode 100644 (file)
index 2fffcd4..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-From dc80281ba0d325e71ff6cd2c1d7d525c889b3996 Mon Sep 17 00:00:00 2001
-From: Rosen Penev <rosenp@gmail.com>
-Date: Sat, 2 Jan 2021 21:05:53 -0800
-Subject: [PATCH] add meson build
-
-meson compiles faster and is simpler than autotools.
-
-Signed-off-by: Rosen Penev <rosenp@gmail.com>
----
- addshare/meson.build | 11 +++++++++++
- adduser/meson.build  | 13 +++++++++++++
- control/meson.build  |  9 +++++++++
- lib/meson.build      | 18 ++++++++++++++++++
- meson.build          | 33 +++++++++++++++++++++++++++++++++
- meson_options.txt    |  4 ++++
- mountd/meson.build   | 17 +++++++++++++++++
- 7 files changed, 105 insertions(+)
- create mode 100644 addshare/meson.build
- create mode 100644 adduser/meson.build
- create mode 100644 control/meson.build
- create mode 100644 lib/meson.build
- create mode 100644 meson.build
- create mode 100644 meson_options.txt
- create mode 100644 mountd/meson.build
-
---- /dev/null
-+++ b/addshare/meson.build
-@@ -0,0 +1,11 @@
-+addshare = executable(
-+  'ksmbd.addshare',
-+  'share_admin.c',
-+  'addshare.c',
-+  'share_admin.h',
-+  dependencies: [glib_dep, netlink_dep],
-+  include_directories: tools_incdir,
-+  link_with: libksmbdtools,
-+  install: true,
-+  install_dir: get_option('sbindir'),
-+)
---- /dev/null
-+++ b/adduser/meson.build
-@@ -0,0 +1,13 @@
-+adduser = executable(
-+  'ksmbd.adduser',
-+  'md4_hash.c',
-+  'user_admin.c',
-+  'adduser.c',
-+  'md4_hash.h',
-+  'user_admin.h',
-+  dependencies: [glib_dep, netlink_dep],
-+  include_directories: tools_incdir,
-+  link_with: libksmbdtools,
-+  install: true,
-+  install_dir: get_option('sbindir'),
-+)
---- /dev/null
-+++ b/control/meson.build
-@@ -0,0 +1,9 @@
-+control = executable(
-+  'ksmbd.control',
-+  'control.c',
-+  dependencies: [glib_dep, netlink_dep],
-+  include_directories: tools_incdir,
-+  link_with: libksmbdtools,
-+  install: true,
-+  install_dir: get_option('sbindir'),
-+)
---- /dev/null
-+++ b/lib/meson.build
-@@ -0,0 +1,18 @@
-+core_files = [
-+  'management/tree_conn.c',
-+  'management/user.c',
-+  'management/share.c',
-+  'management/session.c',
-+  'config_parser.c',
-+  'ksmbdtools.c',
-+]
-+
-+if krb5_dep.found()
-+  core_files += [
-+    'management/spnego.c',
-+    'asn1.c',
-+    'management/spnego_krb5.c',
-+  ]
-+endif
-+
-+libksmbdtools = static_library('ksmbdtools', core_files, include_directories: tools_incdir, dependencies: [glib_dep, krb5_dep])
---- /dev/null
-+++ b/meson.build
-@@ -0,0 +1,33 @@
-+project('ksmbsd-tools', 'c',
-+  version: run_command(find_program('awk'), '''/define KSMBD_TOOLS_VERSION / \
-+    { gsub(/"/,"",$3); printf "%s", $3 }''', 'include/version.h', check: true).stdout(),
-+  default_options: 'c_std=gnu99')
-+
-+tools_incdir = include_directories(['include', '.'])
-+
-+glib_dep = dependency('glib-2.0', static: true)
-+netlink_dep = dependency('libnl-genl-3.0')
-+krb5_dep = dependency('krb5', required: get_option('krb5'))
-+
-+cc = meson.get_compiler('c')
-+
-+cdata = configuration_data()
-+add_project_arguments('-DHAVE_CONFIG_H', language: 'c')
-+if krb5_dep.found()
-+  cdata.set('CONFIG_KRB5', krb5_dep.found())
-+  cdata.set('HAVE_KRB5_KEYBLOCK_KEYVALUE', cc.has_member('krb5_keyblock', 'keyvalue', prefix: '#include <krb5.h>'))
-+  cdata.set('HAVE_KRB5_AUTHENTICATOR_CLIENT', cc.has_member('krb5_authenticator', 'client', prefix: '#include <krb5.h>'))
-+  cdata.set('HAVE_KRB5_AUTH_CON_GETRECVSUBKEY', cc.has_function('krb5_auth_con_getrecvsubkey', dependencies: krb5_dep))
-+  cdata.set('HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER', cc.compiles('''#include <krb5.h>
-+    krb5_error_code krb5_auth_con_getauthenticator(krb5_context, krb5_auth_context, krb5_authenticator**);''', dependencies: krb5_dep))
-+endif
-+cfile = configure_file(
-+  output: 'config.h',
-+  configuration: cdata,
-+)
-+
-+subdir('lib')
-+subdir('addshare')
-+subdir('adduser')
-+subdir('control')
-+subdir('mountd')
---- /dev/null
-+++ b/meson_options.txt
-@@ -0,0 +1,4 @@
-+option('krb5', type : 'feature',
-+  description : 'Build with Kerberos support',
-+  value : 'disabled',
-+)
---- /dev/null
-+++ b/mountd/meson.build
-@@ -0,0 +1,17 @@
-+mountd = executable(
-+  'ksmbd.mountd',
-+  'worker.c',
-+  'ipc.c',
-+  'rpc.c',
-+  'rpc_srvsvc.c',
-+  'rpc_wkssvc.c',
-+  'mountd.c',
-+  'smbacl.c',
-+  'rpc_samr.c',
-+  'rpc_lsarpc.c',
-+  dependencies: [glib_dep, netlink_dep],
-+  include_directories: tools_incdir,
-+  link_with: libksmbdtools,
-+  install: true,
-+  install_dir: get_option('sbindir'),
-+)
diff --git a/net/ksmbd-tools/patches/010-muon.patch b/net/ksmbd-tools/patches/010-muon.patch
new file mode 100644 (file)
index 0000000..967c927
--- /dev/null
@@ -0,0 +1,69 @@
+From 3281f325c820a72057ea639e0d11ad68d5703b43 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Thu, 6 Oct 2022 18:07:01 -0700
+Subject: [PATCH] ksmbd-tools: run meson through muon analyze
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Mostly unused variable removals. Removed pointless [] in
+include_directories.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Acked-by: Atte Heikkilä <atteh.mailbox@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+---
+ addshare/meson.build | 2 +-
+ adduser/meson.build  | 2 +-
+ control/meson.build  | 2 +-
+ meson.build          | 4 ++--
+ mountd/meson.build   | 2 +-
+ 5 files changed, 6 insertions(+), 6 deletions(-)
+
+--- a/addshare/meson.build
++++ b/addshare/meson.build
+@@ -1,4 +1,4 @@
+-addshare = executable(
++executable(
+   'ksmbd.addshare',
+   'share_admin.c',
+   'addshare.c',
+--- a/adduser/meson.build
++++ b/adduser/meson.build
+@@ -1,4 +1,4 @@
+-adduser = executable(
++executable(
+   'ksmbd.adduser',
+   'md4_hash.c',
+   'user_admin.c',
+--- a/control/meson.build
++++ b/control/meson.build
+@@ -1,4 +1,4 @@
+-control = executable(
++executable(
+   'ksmbd.control',
+   'control.c',
+   dependencies: [
+--- a/meson.build
++++ b/meson.build
+@@ -13,10 +13,10 @@ exec awk '/define KSMBD_TOOLS_VERSION /
+   meson_version: '>= 0.51.0',
+ )
+-tools_incdir = include_directories([
++tools_incdir = include_directories(
+   '.',
+   'include',
+-])
++)
+ glib_dep = dependency(
+   'glib-2.0',
+--- a/mountd/meson.build
++++ b/mountd/meson.build
+@@ -1,4 +1,4 @@
+-mountd = executable(
++executable(
+   'ksmbd.mountd',
+   'worker.c',
+   'ipc.c',
diff --git a/net/ksmbd-tools/patches/020-meson.patch b/net/ksmbd-tools/patches/020-meson.patch
new file mode 100644 (file)
index 0000000..adc01b6
--- /dev/null
@@ -0,0 +1,9190 @@
+From 6b74583bd62e2b6ed6b76ff72d8fc7c2d8f26510 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Atte=20Heikkil=C3=A4?= <atteh.mailbox@gmail.com>
+Date: Thu, 13 Oct 2022 20:08:47 +0300
+Subject: [PATCH] ksmbd-tools: build utilities as a single binary
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Rather than have four different binaries, i.e. ksmbd.addshare,
+ksmbd.adduser, ksmbd.control, and ksmbd.mountd, each one including
+its own libksmbdtools.a copy, build them instead as a single binary.
+This resulting ksmbd.tools acts like BusyBox in that its behavior
+depends on the name by which it is called. Then, each utility is made
+into a symlink to it, meaning that users keep using the utilities as
+usual. ksmbd.tools itself is installed to libexecdir as it is not
+something the user should run.
+
+Instead of libksmbdtools.a, each utility becomes its own static
+library, i.e. libaddshare.a, libadduser.a, libcontrol.a, libmountd.a,
+which are all linked to the single binary. Note that the single binary
+approach is also beneficial when statically linking to any of the
+external dependencies, e.g. GLib, as it greatly reduces the overall
+binary size when there is overlap in copies otherwise made separately
+for multiple utilities.
+
+Due to install_symlink(), minimum meson version is bumped to 0.62.1,
+meaning that it has to be installed using `pip' in Travis, since no
+Ubuntu release currently packages that version or newer. However, bump
+to Ubuntu Jammy anyways, just for the sake of building against newer
+versions of ksmbd-tools' dependencies.
+
+Signed-off-by: Atte Heikkilä <atteh.mailbox@gmail.com>
+---
+ .travis.yml                             |  6 ++++--
+ Makefile.am                             |  2 +-
+ addshare/Makefile.am                    | 15 ++++++++-----
+ addshare/addshare.c                     |  4 ++--
+ addshare/meson.build                    | 22 ++++++++++---------
+ addshare/share_admin.c                  |  2 +-
+ adduser/Makefile.am                     | 16 ++++++++------
+ adduser/adduser.c                       |  4 ++--
+ adduser/meson.build                     | 22 ++++++++++---------
+ adduser/user_admin.c                    |  2 +-
+ configure.ac                            |  8 ++++---
+ control/Makefile.am                     | 15 ++++++++-----
+ control/control.c                       |  4 ++--
+ control/meson.build                     | 22 ++++++++++---------
+ include/{ksmbdtools.h => tools.h}       | 11 +++++++---
+ ksmbd-tools.spec                        |  3 ++-
+ meson.build                             |  6 +++---
+ mountd/Makefile.am                      | 17 +++++++++------
+ mountd/ipc.c                            |  2 +-
+ mountd/meson.build                      | 23 +++++++++++---------
+ mountd/mountd.c                         |  4 ++--
+ mountd/rpc.c                            |  2 +-
+ mountd/rpc_lsarpc.c                     |  2 +-
+ mountd/rpc_samr.c                       |  2 +-
+ mountd/rpc_srvsvc.c                     |  2 +-
+ mountd/rpc_wkssvc.c                     |  2 +-
+ mountd/smbacl.c                         |  2 +-
+ mountd/worker.c                         |  2 +-
+ {lib => tools}/Makefile.am              | 15 +++++++------
+ {lib => tools}/asn1.c                   |  0
+ {lib => tools}/config_parser.c          |  2 +-
+ {lib => tools}/management/session.c     |  2 +-
+ {lib => tools}/management/share.c       |  2 +-
+ {lib => tools}/management/spnego.c      |  2 +-
+ {lib => tools}/management/spnego_krb5.c |  2 +-
+ {lib => tools}/management/spnego_mech.h |  0
+ {lib => tools}/management/tree_conn.c   |  2 +-
+ {lib => tools}/management/user.c        |  2 +-
+ {lib => tools}/meson.build              | 28 ++++++++++++++++---------
+ lib/ksmbdtools.c => tools/tools.c       | 27 +++++++++++++++++++++++-
+ 40 files changed, 191 insertions(+), 117 deletions(-)
+ rename include/{ksmbdtools.h => tools.h} (94%)
+ rename {lib => tools}/Makefile.am (53%)
+ rename {lib => tools}/asn1.c (100%)
+ rename {lib => tools}/config_parser.c (99%)
+ rename {lib => tools}/management/session.c (99%)
+ rename {lib => tools}/management/share.c (99%)
+ rename {lib => tools}/management/spnego.c (99%)
+ rename {lib => tools}/management/spnego_krb5.c (99%)
+ rename {lib => tools}/management/spnego_mech.h (100%)
+ rename {lib => tools}/management/tree_conn.c (99%)
+ rename {lib => tools}/management/user.c (99%)
+ rename {lib => tools}/meson.build (60%)
+ rename lib/ksmbdtools.c => tools/tools.c (89%)
+
+--- a/.travis.yml
++++ b/.travis.yml
+@@ -1,4 +1,4 @@
+-dist: focal
++dist: jammy
+ language: c
+@@ -6,9 +6,11 @@ notifications:
+  - email: true
+ before_install:
+- - sudo apt-get install libnl-3-dev libnl-genl-3-dev krb5-multidev heimdal-multidev meson
++ - sudo apt-get install libnl-3-dev libnl-genl-3-dev krb5-multidev heimdal-multidev ninja-build
+  - gcc --version
+  - g++ --version
++ - pip3 install --user meson
++ - PATH=$HOME/.local/bin:$PATH
+ jobs:
+  include:
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1,6 +1,6 @@
+ ACLOCAL_AMFLAGS = -I m4
+-SUBDIRS = lib mountd adduser addshare control
++SUBDIRS = addshare adduser control mountd tools
+ EXTRA_DIST = include \
+              README.md \
+--- a/addshare/Makefile.am
++++ b/addshare/Makefile.am
+@@ -1,11 +1,8 @@
+ AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
+             -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common
+-LIBS = $(GLIB_LIBS)
+-ksmbd_addshare_LDADD = $(top_builddir)/lib/libksmbdtools.a
+-sbin_PROGRAMS = ksmbd.addshare
+-
+-ksmbd_addshare_SOURCES = share_admin.c addshare.c share_admin.h
++noinst_LIBRARIES = libaddshare.a
++libaddshare_a_SOURCES = share_admin.c addshare.c share_admin.h
+ EXTRA_DIST = ksmbd.addshare.8.in
+@@ -13,3 +10,11 @@ man_MANS = ksmbd.addshare.8
+ $(man_MANS): %: %.in; @$(in_script) $< >$@
+ CLEANFILES = $(man_MANS)
++
++install-exec-hook: uninstall-hook
++      $(MKDIR_P) $(DESTDIR)$(sbindir)
++      ( cd $(DESTDIR)$(sbindir) && \
++        $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.addshare )
++
++uninstall-hook:
++      -rm $(DESTDIR)$(sbindir)/ksmbd.addshare
+--- a/addshare/addshare.c
++++ b/addshare/addshare.c
+@@ -18,7 +18,7 @@
+ #include <ctype.h>
+ #include "config_parser.h"
+-#include "ksmbdtools.h"
++#include "tools.h"
+ #include "management/share.h"
+ #include "linux/ksmbd_server.h"
+ #include "share_admin.h"
+@@ -111,7 +111,7 @@ static int sanity_check_share_name_simpl
+       return 0;
+ }
+-int main(int argc, char *argv[])
++int addshare_main(int argc, char **argv)
+ {
+       int ret = -EINVAL;
+       char *share = NULL, *options = NULL, *smbconf = NULL;
+--- a/addshare/meson.build
++++ b/addshare/meson.build
+@@ -1,20 +1,16 @@
+-executable(
+-  'ksmbd.addshare',
++addshare_lib = static_library(
++  'addshare',
+   'share_admin.c',
+   'addshare.c',
+   'share_admin.h',
+-  dependencies: [
+-    glib_dep,
+-    libnl_dep,
+-  ],
+-  include_directories: tools_incdir,
+-  link_with: libksmbdtools,
+-  install: true,
+-  install_dir: get_option('sbindir'),
++  include_directories: include_dirs,
+   c_args: [
+     '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
+     '-DRUNSTATEDIR="@0@"'.format(runstatedir),
+   ],
++  dependencies: [
++    glib_dep,
++  ],
+ )
+ configure_file(
+@@ -23,3 +19,9 @@ configure_file(
+   install_dir: get_option('mandir') / 'man8',
+   configuration: in_data,
+ )
++
++install_symlink(
++  'ksmbd.addshare',
++  install_dir: get_option('sbindir'),
++  pointing_to: '..' / get_option('libexecdir') / 'ksmbd.tools',
++)
+--- a/addshare/share_admin.c
++++ b/addshare/share_admin.c
+@@ -14,7 +14,7 @@
+ #include <fcntl.h>
+ #include <config_parser.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #include <management/share.h>
+--- a/adduser/Makefile.am
++++ b/adduser/Makefile.am
+@@ -1,12 +1,8 @@
+ AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
+             -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common
+-LIBS = $(GLIB_LIBS)
+-ksmbd_adduser_LDADD = $(top_builddir)/lib/libksmbdtools.a
+-sbin_PROGRAMS = ksmbd.adduser
+-
+-ksmbd_adduser_SOURCES = md4_hash.c user_admin.c adduser.c md4_hash.h \
+-                        user_admin.h
++noinst_LIBRARIES = libadduser.a
++libadduser_a_SOURCES = md4_hash.c user_admin.c adduser.c md4_hash.h user_admin.h
+ EXTRA_DIST = ksmbd.adduser.8.in
+@@ -14,3 +10,11 @@ man_MANS = ksmbd.adduser.8
+ $(man_MANS): %: %.in; @$(in_script) $< >$@
+ CLEANFILES = $(man_MANS)
++
++install-exec-hook: uninstall-hook
++      $(MKDIR_P) $(DESTDIR)$(sbindir)
++      ( cd $(DESTDIR)$(sbindir) && \
++        $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.adduser )
++
++uninstall-hook:
++      -rm $(DESTDIR)$(sbindir)/ksmbd.adduser
+--- a/adduser/adduser.c
++++ b/adduser/adduser.c
+@@ -18,7 +18,7 @@
+ #include <ctype.h>
+ #include "config_parser.h"
+-#include "ksmbdtools.h"
++#include "tools.h"
+ #include "management/user.h"
+ #include "management/share.h"
+ #include "user_admin.h"
+@@ -121,7 +121,7 @@ static int sanity_check_user_name_simple
+       return 0;
+ }
+-int main(int argc, char *argv[])
++int adduser_main(int argc, char **argv)
+ {
+       int ret = -EINVAL;
+       char *account = NULL, *password = NULL, *pwddb = NULL, *smbconf = NULL;
+--- a/adduser/meson.build
++++ b/adduser/meson.build
+@@ -1,22 +1,18 @@
+-executable(
+-  'ksmbd.adduser',
++adduser_lib = static_library(
++  'adduser',
+   'md4_hash.c',
+   'user_admin.c',
+   'adduser.c',
+   'md4_hash.h',
+   'user_admin.h',
+-  dependencies: [
+-    glib_dep,
+-    libnl_dep,
+-  ],
+-  include_directories: tools_incdir,
+-  link_with: libksmbdtools,
+-  install: true,
+-  install_dir: get_option('sbindir'),
++  include_directories: include_dirs,
+   c_args: [
+     '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
+     '-DRUNSTATEDIR="@0@"'.format(runstatedir),
+   ],
++  dependencies: [
++    glib_dep,
++  ],
+ )
+ configure_file(
+@@ -25,3 +21,9 @@ configure_file(
+   install_dir: get_option('mandir') / 'man8',
+   configuration: in_data,
+ )
++
++install_symlink(
++  'ksmbd.adduser',
++  install_dir: get_option('sbindir'),
++  pointing_to: '..' / get_option('libexecdir') / 'ksmbd.tools',
++)
+--- a/adduser/user_admin.c
++++ b/adduser/user_admin.c
+@@ -15,7 +15,7 @@
+ #include <termios.h>
+ #include <config_parser.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #include <md4_hash.h>
+ #include <user_admin.h>
+--- a/configure.ac
++++ b/configure.ac
+@@ -24,6 +24,8 @@ AC_PROG_CC_STDC
+ AM_SILENT_RULES([yes])
+ AC_PROG_LIBTOOL
+ AC_PROG_SED
++AC_PROG_MKDIR_P
++AC_PROG_LN_S
+ AC_SUBST([in_script], [[\
+ '$(SED) -e "s,[@]sbindir[@],$(sbindir),g" \
+@@ -143,11 +145,11 @@ AM_CONDITIONAL(HAVE_LIBKRB5, [test "x$en
+ AC_CONFIG_FILES([
+       Makefile
+-      lib/Makefile
+-      mountd/Makefile
+-      adduser/Makefile
+       addshare/Makefile
++      adduser/Makefile
+       control/Makefile
++      mountd/Makefile
++      tools/Makefile
+ ])
+ AC_OUTPUT
+--- a/control/Makefile.am
++++ b/control/Makefile.am
+@@ -1,11 +1,8 @@
+ AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
+             -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common
+-LIBS = $(GLIB_LIBS)
+-ksmbd_control_LDADD = $(top_builddir)/lib/libksmbdtools.a
+-sbin_PROGRAMS = ksmbd.control
+-
+-ksmbd_control_SOURCES = control.c
++noinst_LIBRARIES = libcontrol.a
++libcontrol_a_SOURCES = control.c
+ EXTRA_DIST = ksmbd.control.8.in
+@@ -13,3 +10,11 @@ man_MANS = ksmbd.control.8
+ $(man_MANS): %: %.in; @$(in_script) $< >$@
+ CLEANFILES = $(man_MANS)
++
++install-exec-hook: uninstall-hook
++      $(MKDIR_P) $(DESTDIR)$(sbindir)
++      ( cd $(DESTDIR)$(sbindir) && \
++        $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.control )
++
++uninstall-hook:
++      -rm $(DESTDIR)$(sbindir)/ksmbd.control
+--- a/control/control.c
++++ b/control/control.c
+@@ -9,7 +9,7 @@
+ #include <fcntl.h>
+ #include <errno.h>
+-#include "ksmbdtools.h"
++#include "tools.h"
+ #include "version.h"
+ static void usage(int status)
+@@ -146,7 +146,7 @@ out:
+       return ret;
+ }
+-int main(int argc, char *argv[])
++int control_main(int argc, char **argv)
+ {
+       int ret = -EINVAL;
+       int c;
+--- a/control/meson.build
++++ b/control/meson.build
+@@ -1,18 +1,14 @@
+-executable(
+-  'ksmbd.control',
++control_lib = static_library(
++  'control',
+   'control.c',
+-  dependencies: [
+-    glib_dep,
+-    libnl_dep,
+-  ],
+-  include_directories: tools_incdir,
+-  link_with: libksmbdtools,
+-  install: true,
+-  install_dir: get_option('sbindir'),
++  include_directories: include_dirs,
+   c_args: [
+     '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
+     '-DRUNSTATEDIR="@0@"'.format(runstatedir),
+   ],
++  dependencies: [
++    glib_dep,
++  ],
+ )
+ configure_file(
+@@ -21,3 +17,9 @@ configure_file(
+   install_dir: get_option('mandir') / 'man8',
+   configuration: in_data,
+ )
++
++install_symlink(
++  'ksmbd.control',
++  install_dir: get_option('sbindir'),
++  pointing_to: '..' / get_option('libexecdir') / 'ksmbd.tools',
++)
+--- a/include/ksmbdtools.h
++++ /dev/null
+@@ -1,172 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-or-later */
+-/*
+- *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- *   linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#ifndef __KSMBDTOOLS_H__
+-#define __KSMBDTOOLS_H__
+-
+-#ifndef _GNU_SOURCE
+-#define _GNU_SOURCE 1
+-#endif
+-
+-#include <errno.h>
+-#include <getopt.h>
+-#include <glib.h>
+-#include <poll.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-#include <string.h>
+-#include <sys/types.h>
+-#include <sys/wait.h>
+-#include <time.h>
+-#include <unistd.h>
+-
+-#ifdef HAVE_CONFIG_H
+-#include <config.h>
+-#endif
+-
+-struct smbconf_global {
+-      int                     flags;
+-      int                     map_to_guest;
+-      char                    *guest_account;
+-
+-      char                    *server_string;
+-      char                    *work_group;
+-      char                    *netbios_name;
+-      char                    *server_min_protocol;
+-      char                    *server_max_protocol;
+-      char                    *root_dir;
+-      int                     server_signing;
+-      int                     sessions_cap;
+-      int                     restrict_anon;
+-      unsigned short          tcp_port;
+-      unsigned short          ipc_timeout;
+-      unsigned int            deadtime;
+-      int                     bind_interfaces_only;
+-      char                    **interfaces;
+-      unsigned long           file_max;
+-      unsigned int            smb2_max_read;
+-      unsigned int            smb2_max_write;
+-      unsigned int            smb2_max_trans;
+-      unsigned int            smb2_max_credits;
+-      unsigned int            smbd_max_io_size;
+-      unsigned int            share_fake_fscaps;
+-      unsigned int            gen_subauth[3];
+-      char                    *krb5_keytab_file;
+-      char                    *krb5_service_name;
+-      char                    *pwddb;
+-      char                    *smbconf;
+-};
+-
+-#define KSMBD_LOCK_FILE               RUNSTATEDIR "/ksmbd.lock"
+-
+-#define KSMBD_RESTRICT_ANON_TYPE_1    1
+-#define KSMBD_RESTRICT_ANON_TYPE_2    2
+-
+-extern struct smbconf_global global_conf;
+-
+-#define KSMBD_CONF_MAP_TO_GUEST_NEVER         (0)
+-#define KSMBD_CONF_MAP_TO_GUEST_BAD_USER      (1 << 0)
+-#define KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD  (1 << 1)
+-#define KSMBD_CONF_MAP_TO_GUEST_BAD_UID               (1 << 2)
+-
+-#define KSMBD_CONF_DEFAULT_NETBIOS_NAME       "KSMBD SERVER"
+-#define KSMBD_CONF_DEFAULT_SERVER_STRING      "SMB SERVER"
+-#define KSMBD_CONF_DEFAULT_WORK_GROUP         "WORKGROUP"
+-
+-#define KSMBD_CONF_DEFAULT_GUEST_ACCOUNT      "nobody"
+-
+-#define KSMBD_CONF_DEFAULT_SESS_CAP   1024
+-#define KSMBD_CONF_DEFAULT_TCP_PORT   445
+-
+-#define KSMBD_CONF_FILE_MAX           10000
+-
+-#define PATH_PWDDB            SYSCONFDIR "/ksmbd/ksmbdpwd.db"
+-#define PATH_SMBCONF          SYSCONFDIR "/ksmbd/ksmbd.conf"
+-#define PATH_SMBCONF_FALLBACK SYSCONFDIR "/ksmbd/smb.conf"
+-#define PATH_SUBAUTH          SYSCONFDIR "/ksmbd/ksmbd.subauth"
+-
+-#define KSMBD_HEALTH_START            (0)
+-#define KSMBD_HEALTH_RUNNING          (1 << 0)
+-#define KSMBD_SHOULD_RELOAD_CONFIG    (1 << 1)
+-
+-extern int ksmbd_health_status;
+-
+-#define TRACING_DUMP_NL_MSG   0
+-
+-#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
+-
+-#define STR_HELPER(x) #x
+-#define STR(x) STR_HELPER(x)
+-
+-//---------------------------------------------------------------//
+-#define LOGAPP                "[%s/%d]:"
+-#define PRERR         LOGAPP" ERROR: "
+-#define PRINF         LOGAPP" INFO: "
+-#define PRDEBUG               LOGAPP" DEBUG: "
+-
+-#define PR_ERROR      0
+-#define PR_INFO               1
+-#define PR_DEBUG      2
+-
+-extern int log_level;
+-
+-#define PR_LOGGER_STDIO         0
+-#define PR_LOGGER_SYSLOG        1
+-
+-G_GNUC_PRINTF(2, 3)
+-extern void __pr_log(int level, const char *fmt, ...);
+-extern void set_logger_app_name(const char *an);
+-extern const char *get_logger_app_name(void);
+-extern void pr_logger_init(int flags);
+-extern int set_log_level(int level);
+-
+-#define pr_log(l, f, ...)                                             \
+-      do {                                                            \
+-              if ((l) <= log_level)                                   \
+-                      __pr_log((l), (f), get_logger_app_name(),       \
+-                                      getpid(),                       \
+-                                      ##__VA_ARGS__);                 \
+-      } while (0)
+-
+-#define pr_debug(f, ...)      \
+-      pr_log(PR_DEBUG, PRDEBUG f, ##__VA_ARGS__)
+-#define pr_info(f, ...)       \
+-      pr_log(PR_INFO, PRINF f, ##__VA_ARGS__)
+-#define pr_err(f, ...)        \
+-      pr_log(PR_ERROR, PRERR f, ##__VA_ARGS__)
+-
+-//---------------------------------------------------------------//
+-
+-void pr_hex_dump(const void *mem, size_t sz);
+-
+-char *base64_encode(unsigned char *src, size_t srclen);
+-unsigned char *base64_decode(char const *src, size_t *dstlen);
+-
+-gchar *ksmbd_gconvert(const gchar *str,
+-                    gssize       str_len,
+-                    int          to_codeset,
+-                    int          from_codeset,
+-                    gsize       *bytes_read,
+-                    gsize       *bytes_written);
+-
+-enum charset_idx {
+-      KSMBD_CHARSET_UTF8              = 0,
+-      KSMBD_CHARSET_UTF16LE,
+-      KSMBD_CHARSET_UCS2LE,
+-      KSMBD_CHARSET_UTF16BE,
+-      KSMBD_CHARSET_UCS2BE,
+-      KSMBD_CHARSET_MAX               = 5,
+-};
+-
+-#define KSMBD_CHARSET_DEFAULT         KSMBD_CHARSET_UTF8
+-
+-extern char *ksmbd_conv_charsets[KSMBD_CHARSET_MAX + 1];
+-
+-int send_signal_to_ksmbd_mountd(int signo);
+-int test_file_access(char *conf);
+-
+-#endif /* __KSMBDTOOLS_H__ */
+--- /dev/null
++++ b/include/tools.h
+@@ -0,0 +1,177 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ *   linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#ifndef __TOOLS_H__
++#define __TOOLS_H__
++
++#ifndef _GNU_SOURCE
++#define _GNU_SOURCE 1
++#endif
++
++#include <errno.h>
++#include <getopt.h>
++#include <glib.h>
++#include <poll.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <time.h>
++#include <unistd.h>
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++struct smbconf_global {
++      int                     flags;
++      int                     map_to_guest;
++      char                    *guest_account;
++
++      char                    *server_string;
++      char                    *work_group;
++      char                    *netbios_name;
++      char                    *server_min_protocol;
++      char                    *server_max_protocol;
++      char                    *root_dir;
++      int                     server_signing;
++      int                     sessions_cap;
++      int                     restrict_anon;
++      unsigned short          tcp_port;
++      unsigned short          ipc_timeout;
++      unsigned int            deadtime;
++      int                     bind_interfaces_only;
++      char                    **interfaces;
++      unsigned long           file_max;
++      unsigned int            smb2_max_read;
++      unsigned int            smb2_max_write;
++      unsigned int            smb2_max_trans;
++      unsigned int            smb2_max_credits;
++      unsigned int            smbd_max_io_size;
++      unsigned int            share_fake_fscaps;
++      unsigned int            gen_subauth[3];
++      char                    *krb5_keytab_file;
++      char                    *krb5_service_name;
++      char                    *pwddb;
++      char                    *smbconf;
++};
++
++#define KSMBD_LOCK_FILE               RUNSTATEDIR "/ksmbd.lock"
++
++#define KSMBD_RESTRICT_ANON_TYPE_1    1
++#define KSMBD_RESTRICT_ANON_TYPE_2    2
++
++extern struct smbconf_global global_conf;
++
++#define KSMBD_CONF_MAP_TO_GUEST_NEVER         (0)
++#define KSMBD_CONF_MAP_TO_GUEST_BAD_USER      (1 << 0)
++#define KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD  (1 << 1)
++#define KSMBD_CONF_MAP_TO_GUEST_BAD_UID               (1 << 2)
++
++#define KSMBD_CONF_DEFAULT_NETBIOS_NAME       "KSMBD SERVER"
++#define KSMBD_CONF_DEFAULT_SERVER_STRING      "SMB SERVER"
++#define KSMBD_CONF_DEFAULT_WORK_GROUP         "WORKGROUP"
++
++#define KSMBD_CONF_DEFAULT_GUEST_ACCOUNT      "nobody"
++
++#define KSMBD_CONF_DEFAULT_SESS_CAP   1024
++#define KSMBD_CONF_DEFAULT_TCP_PORT   445
++
++#define KSMBD_CONF_FILE_MAX           10000
++
++#define PATH_PWDDB            SYSCONFDIR "/ksmbd/ksmbdpwd.db"
++#define PATH_SMBCONF          SYSCONFDIR "/ksmbd/ksmbd.conf"
++#define PATH_SMBCONF_FALLBACK SYSCONFDIR "/ksmbd/smb.conf"
++#define PATH_SUBAUTH          SYSCONFDIR "/ksmbd/ksmbd.subauth"
++
++#define KSMBD_HEALTH_START            (0)
++#define KSMBD_HEALTH_RUNNING          (1 << 0)
++#define KSMBD_SHOULD_RELOAD_CONFIG    (1 << 1)
++
++extern int ksmbd_health_status;
++
++#define TRACING_DUMP_NL_MSG   0
++
++#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
++
++#define STR_HELPER(x) #x
++#define STR(x) STR_HELPER(x)
++
++//---------------------------------------------------------------//
++#define LOGAPP                "[%s/%d]:"
++#define PRERR         LOGAPP" ERROR: "
++#define PRINF         LOGAPP" INFO: "
++#define PRDEBUG               LOGAPP" DEBUG: "
++
++#define PR_ERROR      0
++#define PR_INFO               1
++#define PR_DEBUG      2
++
++extern int log_level;
++
++#define PR_LOGGER_STDIO         0
++#define PR_LOGGER_SYSLOG        1
++
++G_GNUC_PRINTF(2, 3)
++extern void __pr_log(int level, const char *fmt, ...);
++extern void set_logger_app_name(const char *an);
++extern const char *get_logger_app_name(void);
++extern void pr_logger_init(int flags);
++extern int set_log_level(int level);
++
++#define pr_log(l, f, ...)                                             \
++      do {                                                            \
++              if ((l) <= log_level)                                   \
++                      __pr_log((l), (f), get_logger_app_name(),       \
++                                      getpid(),                       \
++                                      ##__VA_ARGS__);                 \
++      } while (0)
++
++#define pr_debug(f, ...)      \
++      pr_log(PR_DEBUG, PRDEBUG f, ##__VA_ARGS__)
++#define pr_info(f, ...)       \
++      pr_log(PR_INFO, PRINF f, ##__VA_ARGS__)
++#define pr_err(f, ...)        \
++      pr_log(PR_ERROR, PRERR f, ##__VA_ARGS__)
++
++//---------------------------------------------------------------//
++
++void pr_hex_dump(const void *mem, size_t sz);
++
++char *base64_encode(unsigned char *src, size_t srclen);
++unsigned char *base64_decode(char const *src, size_t *dstlen);
++
++gchar *ksmbd_gconvert(const gchar *str,
++                    gssize       str_len,
++                    int          to_codeset,
++                    int          from_codeset,
++                    gsize       *bytes_read,
++                    gsize       *bytes_written);
++
++enum charset_idx {
++      KSMBD_CHARSET_UTF8              = 0,
++      KSMBD_CHARSET_UTF16LE,
++      KSMBD_CHARSET_UCS2LE,
++      KSMBD_CHARSET_UTF16BE,
++      KSMBD_CHARSET_UCS2BE,
++      KSMBD_CHARSET_MAX               = 5,
++};
++
++#define KSMBD_CHARSET_DEFAULT         KSMBD_CHARSET_UTF8
++
++extern char *ksmbd_conv_charsets[KSMBD_CHARSET_MAX + 1];
++
++int send_signal_to_ksmbd_mountd(int signo);
++int test_file_access(char *conf);
++
++int addshare_main(int argc, char **argv);
++int adduser_main(int argc, char **argv);
++int control_main(int argc, char **argv);
++int mountd_main(int argc, char **argv);
++
++#endif /* __TOOLS_H__ */
+--- a/ksmbd-tools.spec
++++ b/ksmbd-tools.spec
+@@ -16,7 +16,7 @@
+ #
+ Name:           ksmbd-tools
+-Version:        3.4.6
++Version:        master
+ Release:        0
+ Summary:        ksmbd kernel server userspace utilities
+ License:        GPL-2.0-or-later
+@@ -53,6 +53,7 @@ make %{?_smp_mflags}
+ %{_sbindir}/ksmbd.adduser
+ %{_sbindir}/ksmbd.control
+ %{_sbindir}/ksmbd.mountd
++%{_libexecdir}/ksmbd.tools
+ %{_mandir}/man8/ksmbd.addshare.8*
+ %{_mandir}/man8/ksmbd.adduser.8*
+ %{_mandir}/man8/ksmbd.control.8*
+--- a/meson.build
++++ b/meson.build
+@@ -10,10 +10,10 @@ exec awk '/define KSMBD_TOOLS_VERSION /
+     check: true,
+   ).stdout(),
+   default_options: 'c_std=gnu99',
+-  meson_version: '>= 0.51.0',
++  meson_version: '>= 0.61.5',
+ )
+-tools_incdir = include_directories(
++include_dirs = include_directories(
+   '.',
+   'include',
+ )
+@@ -151,8 +151,8 @@ configure_file(
+   configuration: in_data,
+ )
+-subdir('lib')
+ subdir('addshare')
+ subdir('adduser')
+ subdir('control')
+ subdir('mountd')
++subdir('tools')
+--- a/mountd/Makefile.am
++++ b/mountd/Makefile.am
+@@ -1,12 +1,9 @@
+ AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
+             -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common
+-LIBS = $(GLIB_LIBS) $(LIBNL_LIBS) $(LIBKRB5_LIBS)
+-ksmbd_mountd_LDADD = $(top_builddir)/lib/libksmbdtools.a
+-sbin_PROGRAMS = ksmbd.mountd
+-
+-ksmbd_mountd_SOURCES = worker.c ipc.c rpc.c rpc_srvsvc.c rpc_wkssvc.c \
+-                       mountd.c smbacl.c rpc_samr.c rpc_lsarpc.c
++noinst_LIBRARIES = libmountd.a
++libmountd_a_SOURCES = worker.c ipc.c rpc.c rpc_srvsvc.c rpc_wkssvc.c mountd.c \
++                      smbacl.c rpc_samr.c rpc_lsarpc.c
+ EXTRA_DIST = ksmbd.mountd.8.in
+@@ -14,3 +11,11 @@ man_MANS = ksmbd.mountd.8
+ $(man_MANS): %: %.in; @$(in_script) $< >$@
+ CLEANFILES = $(man_MANS)
++
++install-exec-hook: uninstall-hook
++      $(MKDIR_P) $(DESTDIR)$(sbindir)
++      ( cd $(DESTDIR)$(sbindir) && \
++        $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.mountd )
++
++uninstall-hook:
++      -rm $(DESTDIR)$(sbindir)/ksmbd.mountd
+--- a/mountd/ipc.c
++++ b/mountd/ipc.c
+@@ -17,7 +17,7 @@
+ #include <linux/ksmbd_server.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #include <ipc.h>
+ #include <worker.h>
+ #include <config_parser.h>
+--- a/mountd/meson.build
++++ b/mountd/meson.build
+@@ -1,5 +1,5 @@
+-executable(
+-  'ksmbd.mountd',
++mountd_lib = static_library(
++  'mountd',
+   'worker.c',
+   'ipc.c',
+   'rpc.c',
+@@ -9,18 +9,15 @@ executable(
+   'smbacl.c',
+   'rpc_samr.c',
+   'rpc_lsarpc.c',
+-  dependencies: [
+-    glib_dep,
+-    libnl_dep,
+-  ],
+-  include_directories: tools_incdir,
+-  link_with: libksmbdtools,
+-  install: true,
+-  install_dir: get_option('sbindir'),
++  include_directories: include_dirs,
+   c_args: [
+     '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
+     '-DRUNSTATEDIR="@0@"'.format(runstatedir),
+   ],
++  dependencies: [
++    glib_dep,
++    libnl_dep,
++  ],
+ )
+ configure_file(
+@@ -29,3 +26,9 @@ configure_file(
+   install_dir: get_option('mandir') / 'man8',
+   configuration: in_data,
+ )
++
++install_symlink(
++  'ksmbd.mountd',
++  install_dir: get_option('sbindir'),
++  pointing_to: '..' / get_option('libexecdir') / 'ksmbd.tools',
++)
+--- a/mountd/mountd.c
++++ b/mountd/mountd.c
+@@ -5,7 +5,7 @@
+  *   linux-cifsd-devel@lists.sourceforge.net
+  */
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #ifndef _GNU_SOURCE
+ #define _GNU_SOURCE
+@@ -509,7 +509,7 @@ out:
+       return 0;
+ }
+-int main(int argc, char *argv[])
++int mountd_main(int argc, char **argv)
+ {
+       int ret = -EINVAL;
+       int c;
+--- a/mountd/rpc.c
++++ b/mountd/rpc.c
+@@ -16,7 +16,7 @@
+ #include <rpc_wkssvc.h>
+ #include <rpc_samr.h>
+ #include <rpc_lsarpc.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ static GHashTable     *pipes_table;
+ static GRWLock                pipes_table_lock;
+--- a/mountd/rpc_lsarpc.c
++++ b/mountd/rpc_lsarpc.c
+@@ -16,7 +16,7 @@
+ #include <rpc.h>
+ #include <rpc_lsarpc.h>
+ #include <smbacl.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #define LSARPC_OPNUM_DS_ROLE_GET_PRIMARY_DOMAIN_INFO  0
+ #define LSARPC_OPNUM_OPEN_POLICY2                     44
+--- a/mountd/rpc_samr.c
++++ b/mountd/rpc_samr.c
+@@ -15,7 +15,7 @@
+ #include <rpc.h>
+ #include <rpc_samr.h>
+ #include <smbacl.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #define SAMR_OPNUM_CONNECT5           64
+ #define SAMR_OPNUM_ENUM_DOMAIN                6
+--- a/mountd/rpc_srvsvc.c
++++ b/mountd/rpc_srvsvc.c
+@@ -15,7 +15,7 @@
+ #include <rpc.h>
+ #include <rpc_srvsvc.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #define SHARE_TYPE_TEMP                       0x40000000
+ #define SHARE_TYPE_HIDDEN             0x80000000
+--- a/mountd/rpc_wkssvc.c
++++ b/mountd/rpc_wkssvc.c
+@@ -15,7 +15,7 @@
+ #include <rpc.h>
+ #include <rpc_wkssvc.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #define WKSSVC_NETWKSTA_GET_INFO      (0)
+--- a/mountd/smbacl.c
++++ b/mountd/smbacl.c
+@@ -7,7 +7,7 @@
+  */
+ #include <smbacl.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #include <glib.h>
+ static const struct smb_sid sid_domain = {1, 1, {0, 0, 0, 0, 0, 5},
+--- a/mountd/worker.c
++++ b/mountd/worker.c
+@@ -9,7 +9,7 @@
+ #include <errno.h>
+ #include <linux/ksmbd_server.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #include <worker.h>
+ #include <ipc.h>
+ #include <rpc.h>
+--- a/lib/Makefile.am
++++ /dev/null
+@@ -1,18 +0,0 @@
+-AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
+-            -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBKRB5_CFLAGS) -fno-common
+-LIBS = $(GLIB_LIBS)
+-
+-noinst_LIBRARIES = libksmbdtools.a
+-libksmbdtools_a_SOURCES = management/tree_conn.c \
+-                          management/user.c \
+-                          management/share.c \
+-                          management/session.c \
+-                          config_parser.c \
+-                          ksmbdtools.c
+-
+-if HAVE_LIBKRB5
+-libksmbdtools_a_SOURCES += management/spnego.c \
+-                           asn1.c \
+-                           management/spnego_krb5.c \
+-                           management/spnego_mech.h
+-endif
+--- /dev/null
++++ b/tools/Makefile.am
+@@ -0,0 +1,21 @@
++AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
++            -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBKRB5_CFLAGS) -fno-common
++LIBS = $(GLIB_LIBS) $(LIBNL_LIBS) $(LIBKRB5_LIBS)
++
++libexec_PROGRAMS = ksmbd.tools
++ksmbd_tools_SOURCES =     management/tree_conn.c \
++                          management/user.c \
++                          management/share.c \
++                          management/session.c \
++                          config_parser.c \
++                          tools.c
++if HAVE_LIBKRB5
++ksmbd_tools_SOURCES +=     management/spnego.c \
++                           asn1.c \
++                           management/spnego_krb5.c \
++                           management/spnego_mech.h
++endif
++ksmbd_tools_LDADD = $(top_builddir)/addshare/libaddshare.a \
++                    $(top_builddir)/adduser/libadduser.a \
++                    $(top_builddir)/control/libcontrol.a \
++                    $(top_builddir)/mountd/libmountd.a
+--- a/lib/config_parser.c
++++ /dev/null
+@@ -1,770 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- *   linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <glib.h>
+-#include <string.h>
+-#include <glib/gstdio.h>
+-#include <sys/stat.h>
+-#include <sys/types.h>
+-#include <unistd.h>
+-#include <fcntl.h>
+-#include <linux/ksmbd_server.h>
+-
+-#include <config_parser.h>
+-#include <ksmbdtools.h>
+-#include <management/user.h>
+-#include <management/share.h>
+-
+-struct smbconf_global global_conf;
+-struct smbconf_parser parser;
+-struct smbconf_group *global_group, *ipc_group;
+-
+-unsigned long long memparse(const char *v)
+-{
+-      char *eptr;
+-
+-      unsigned long long ret = strtoull(v, &eptr, 0);
+-
+-      switch (*eptr) {
+-      case 'E':
+-      case 'e':
+-              ret <<= 10;
+-      case 'P':
+-      case 'p':
+-              ret <<= 10;
+-      case 'T':
+-      case 't':
+-              ret <<= 10;
+-      case 'G':
+-      case 'g':
+-              ret <<= 10;
+-      case 'M':
+-      case 'm':
+-              ret <<= 10;
+-      case 'K':
+-      case 'k':
+-              ret <<= 10;
+-      }
+-
+-      return ret;
+-}
+-
+-static void kv_release_cb(gpointer p)
+-{
+-      g_free(p);
+-}
+-
+-static int is_ascii_space_tab(char c)
+-{
+-      return c == ' ' || c == '\t';
+-}
+-
+-static int is_a_comment(char *line)
+-{
+-      return (*line == 0x00 || *line == ';' || *line == '\n' || *line == '#');
+-}
+-
+-static int is_a_group(char *line)
+-{
+-      char *p = line;
+-
+-      if (*p != '[')
+-              return 0;
+-      p++;
+-      while (*p && *p != ']')
+-              p = g_utf8_find_next_char(p, NULL);
+-      if (*p != ']')
+-              return 0;
+-      return 1;
+-}
+-
+-static int add_new_group(char *line)
+-{
+-      char *begin = line;
+-      char *end = line;
+-      char *name = NULL;
+-      struct smbconf_group *group = NULL;
+-      struct smbconf_group *lookup;
+-
+-      while (*end && *end != ']')
+-              end = g_utf8_find_next_char(end, NULL);
+-
+-      name = g_strndup(begin + 1, end - begin - 1);
+-      if (!name)
+-              goto out_free;
+-
+-      lookup = g_hash_table_lookup(parser.groups, name);
+-      if (lookup) {
+-              parser.current = lookup;
+-              pr_info("Multiple definitions for group `%s'\n", name);
+-              g_free(name);
+-              return 0;
+-      }
+-
+-      group = g_malloc(sizeof(struct smbconf_group));
+-      group->cb_mode = GROUPS_CALLBACK_NONE;
+-      group->name = name;
+-      group->kv = g_hash_table_new_full(g_str_hash,
+-                                        g_str_equal,
+-                                        kv_release_cb,
+-                                        kv_release_cb);
+-      if (!group->kv)
+-              goto out_free;
+-
+-      parser.current = group;
+-      g_hash_table_insert(parser.groups, group->name, group);
+-      return 0;
+-
+-out_free:
+-      g_free(name);
+-      if (group && group->kv)
+-              g_hash_table_destroy(group->kv);
+-      g_free(group);
+-      return -ENOMEM;
+-}
+-
+-static int add_group_key_value(char *line)
+-{
+-      char *key, *value;
+-
+-      key = strchr(line, '=');
+-      if (!key)
+-              return -EINVAL;
+-
+-      value = key;
+-      *key = 0x00;
+-      key--;
+-      value++;
+-
+-      while (is_ascii_space_tab(*key))
+-              key--;
+-      while (is_ascii_space_tab(*value))
+-              value++;
+-
+-      if (is_a_comment(value))
+-              return 0;
+-
+-      if (g_hash_table_lookup(parser.current->kv, key)) {
+-              pr_info("Multiple key-value definitions [%s] %s\n",
+-                              parser.current->name, key);
+-              return 0;
+-      }
+-
+-      key = g_strndup(line, key - line + 1);
+-      value = g_strdup(value);
+-
+-      if (!key || !value) {
+-              g_free(key);
+-              g_free(value);
+-              return -ENOMEM;
+-      }
+-
+-      g_hash_table_insert(parser.current->kv, key, value);
+-      return 0;
+-}
+-
+-static int process_smbconf_entry(char *data)
+-{
+-      while (is_ascii_space_tab(*data))
+-              data++;
+-
+-      if (is_a_comment(data))
+-              return 0;
+-
+-      if (is_a_group(data))
+-              return add_new_group(data);
+-
+-      return add_group_key_value(data);
+-}
+-
+-static int __mmap_parse_file(const char *fname, int (*callback)(char *data))
+-{
+-      GMappedFile *file;
+-      GError *err = NULL;
+-      gchar *contents;
+-      int len;
+-      char *delim;
+-      int fd, ret = 0;
+-
+-      fd = g_open(fname, O_RDONLY, 0);
+-      if (fd == -1) {
+-              ret = -errno;
+-              pr_debug("Can't open `%s': %m\n", fname);
+-              return ret;
+-      }
+-
+-      file = g_mapped_file_new_from_fd(fd, FALSE, &err);
+-      if (err) {
+-              pr_err("Can't map `%s' to memory: %s\n", fname, err->message);
+-              g_error_free(err);
+-              ret = -EINVAL;
+-              goto out;
+-      }
+-
+-      contents = g_mapped_file_get_contents(file);
+-      if (!contents)
+-              goto out;
+-
+-      len = g_mapped_file_get_length(file);
+-      while (len > 0) {
+-              delim = strchr(contents, '\n');
+-              if (!delim)
+-                      delim = contents + len - 1;
+-
+-              if (delim) {
+-                      size_t sz = delim - contents;
+-                      char *data;
+-
+-                      if (delim == contents) {
+-                              contents = delim + 1;
+-                              len--;
+-                              continue;
+-                      }
+-
+-                      if (!sz)
+-                              break;
+-
+-                      data = g_strndup(contents, sz);
+-                      ret = callback(data);
+-                      if (ret) {
+-                              g_free(data);
+-                              goto out;
+-                      }
+-
+-                      g_free(data);
+-                      contents = delim + 1;
+-                      len -= (sz + 1);
+-              }
+-      }
+-
+-      ret = 0;
+-out:
+-      if (file)
+-              g_mapped_file_unref(file);
+-
+-      if (fd) {
+-              g_close(fd, &err);
+-              if (err) {
+-                      pr_err("Can't close `%s': %s\n", fname, err->message);
+-                      g_error_free(err);
+-              }
+-      }
+-      return ret;
+-}
+-
+-static int init_smbconf_parser(void)
+-{
+-      if (parser.groups)
+-              return 0;
+-
+-      parser.groups = g_hash_table_new(shm_share_name_hash,
+-                                       shm_share_name_equal);
+-      if (!parser.groups)
+-              return -ENOMEM;
+-      return 0;
+-}
+-
+-static void release_smbconf_group(gpointer k, gpointer v, gpointer user_data)
+-{
+-      struct smbconf_group *g = v;
+-
+-      g_hash_table_destroy(g->kv);
+-      g_free(g->name);
+-      g_free(g);
+-}
+-
+-static void release_smbconf_parser(void)
+-{
+-      if (!parser.groups)
+-              return;
+-
+-      g_hash_table_foreach(parser.groups, release_smbconf_group, NULL);
+-      g_hash_table_destroy(parser.groups);
+-      parser.groups = NULL;
+-}
+-
+-char *cp_ltrim(char *v)
+-{
+-      if (!v)
+-              return NULL;
+-
+-      while (*v && *v == ' ')
+-              v++;
+-      if (*v == 0x00)
+-              return NULL;
+-      return v;
+-}
+-
+-int cp_key_cmp(char *k, char *v)
+-{
+-      if (!k || !v)
+-              return -1;
+-      return g_ascii_strncasecmp(k, v, strlen(v));
+-}
+-
+-char *cp_get_group_kv_string(char *v)
+-{
+-      return g_strdup(v);
+-}
+-
+-int cp_get_group_kv_bool(char *v)
+-{
+-      if (!g_ascii_strncasecmp(v, "yes", 3) ||
+-              !g_ascii_strncasecmp(v, "1", 1) ||
+-              !g_ascii_strncasecmp(v, "true", 4) ||
+-              !g_ascii_strncasecmp(v, "enable", 6))
+-              return 1;
+-      return 0;
+-}
+-
+-int cp_get_group_kv_config_opt(char *v)
+-{
+-      if (!g_ascii_strncasecmp(v, "disabled", 8))
+-              return KSMBD_CONFIG_OPT_DISABLED;
+-      if (!g_ascii_strncasecmp(v, "enabled", 7))
+-              return KSMBD_CONFIG_OPT_ENABLED;
+-      if (!g_ascii_strncasecmp(v, "auto", 4))
+-              return KSMBD_CONFIG_OPT_AUTO;
+-      if (!g_ascii_strncasecmp(v, "mandatory", 9))
+-              return KSMBD_CONFIG_OPT_MANDATORY;
+-      return KSMBD_CONFIG_OPT_DISABLED;
+-}
+-
+-unsigned long cp_get_group_kv_long_base(char *v, int base)
+-{
+-      return strtoul(v, NULL, base);
+-}
+-
+-unsigned long cp_get_group_kv_long(char *v)
+-{
+-      return cp_get_group_kv_long_base(v, 10);
+-}
+-
+-char **cp_get_group_kv_list(char *v)
+-{
+-      /*
+-       * SMB conf lists are "tabs, spaces, commas" separated.
+-       */
+-      return g_strsplit_set(v, "\t ,", -1);
+-}
+-
+-void cp_group_kv_list_free(char **list)
+-{
+-      g_strfreev(list);
+-}
+-
+-static int cp_add_global_guest_account(gpointer _v)
+-{
+-      struct ksmbd_user *user;
+-
+-      if (usm_add_new_user(cp_get_group_kv_string(_v),
+-                           g_strdup("NULL"))) {
+-              pr_err("Unable to add guest account\n");
+-              return -ENOMEM;
+-      }
+-
+-      user = usm_lookup_user(_v);
+-      if (!user) {
+-              pr_err("Unable to find user `%s'\n", (char *) _v);
+-              return -EINVAL;
+-      }
+-
+-      set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT);
+-      put_ksmbd_user(user);
+-      global_conf.guest_account = cp_get_group_kv_string(_v);
+-      return 0;
+-}
+-
+-static gboolean global_group_kv(gpointer _k, gpointer _v, gpointer user_data)
+-{
+-      if (!cp_key_cmp(_k, "server string")) {
+-              global_conf.server_string = cp_get_group_kv_string(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "workgroup")) {
+-              global_conf.work_group = cp_get_group_kv_string(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "netbios name")) {
+-              global_conf.netbios_name = cp_get_group_kv_string(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "server min protocol")) {
+-              global_conf.server_min_protocol = cp_get_group_kv_string(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "server signing")) {
+-              global_conf.server_signing = cp_get_group_kv_config_opt(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "server max protocol")) {
+-              global_conf.server_max_protocol = cp_get_group_kv_string(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "guest account")) {
+-              cp_add_global_guest_account(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "max active sessions")) {
+-              global_conf.sessions_cap = cp_get_group_kv_long(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "tcp port")) {
+-              if (!global_conf.tcp_port)
+-                      global_conf.tcp_port = cp_get_group_kv_long(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "ipc timeout")) {
+-              global_conf.ipc_timeout = cp_get_group_kv_long(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "max open files")) {
+-              global_conf.file_max = cp_get_group_kv_long(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "restrict anonymous")) {
+-              global_conf.restrict_anon = cp_get_group_kv_long(_v);
+-              if (global_conf.restrict_anon > KSMBD_RESTRICT_ANON_TYPE_2 ||
+-                              global_conf.restrict_anon < 0) {
+-                      global_conf.restrict_anon = 0;
+-                      pr_err("Invalid restrict anonymous value\n");
+-              }
+-
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "map to guest")) {
+-              global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_NEVER;
+-              if (!cp_key_cmp(_v, "bad user"))
+-                      global_conf.map_to_guest =
+-                              KSMBD_CONF_MAP_TO_GUEST_BAD_USER;
+-              if (!cp_key_cmp(_v, "bad password"))
+-                      global_conf.map_to_guest =
+-                              KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD;
+-              if (!cp_key_cmp(_v, "bad uid"))
+-                      global_conf.map_to_guest =
+-                              KSMBD_CONF_MAP_TO_GUEST_BAD_UID;
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "bind interfaces only")) {
+-              global_conf.bind_interfaces_only = cp_get_group_kv_bool(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "interfaces")) {
+-              global_conf.interfaces = cp_get_group_kv_list(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "deadtime")) {
+-              global_conf.deadtime = cp_get_group_kv_long(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "smb2 leases")) {
+-              if (cp_get_group_kv_bool(_v))
+-                      global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB2_LEASES;
+-              else
+-                      global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB2_LEASES;
+-
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "root directory")) {
+-              global_conf.root_dir = cp_get_group_kv_string(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "smb2 max read")) {
+-              global_conf.smb2_max_read = memparse(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "smb2 max write")) {
+-              global_conf.smb2_max_write = memparse(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "smb2 max trans")) {
+-              global_conf.smb2_max_trans = memparse(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "smb3 encryption")) {
+-              if (cp_get_group_kv_bool(_v))
+-                      global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION;
+-              else
+-                      global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION;
+-
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "share:fake_fscaps")) {
+-              global_conf.share_fake_fscaps = cp_get_group_kv_long(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "kerberos service name")) {
+-              global_conf.krb5_service_name = cp_get_group_kv_string(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "kerberos keytab file")) {
+-              global_conf.krb5_keytab_file = cp_get_group_kv_string(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "server multi channel support")) {
+-              if (cp_get_group_kv_bool(_v))
+-                      global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL;
+-              else
+-                      global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL;
+-
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "smb2 max credits")) {
+-              global_conf.smb2_max_credits = memparse(_v);
+-              return TRUE;
+-      }
+-
+-      if (!cp_key_cmp(_k, "smbd max io size")) {
+-              global_conf.smbd_max_io_size = memparse(_v);
+-              return TRUE;
+-      }
+-
+-      /* At this point, this is an option that must be applied to all shares */
+-      return FALSE;
+-}
+-
+-static void global_conf_default(void)
+-{
+-      /* The SPARSE_FILES file system capability flag is set by default */
+-      global_conf.share_fake_fscaps = 64;
+-}
+-
+-static void global_conf_create(void)
+-{
+-      if (!global_group || global_group->cb_mode != GROUPS_CALLBACK_INIT)
+-              return;
+-
+-      /*
+-       * This will transfer server options to global_conf, and leave behind
+-       * in the global parser group, the options that must be applied to every
+-       * share
+-       */
+-      g_hash_table_foreach_remove(global_group->kv, global_group_kv, NULL);
+-}
+-
+-static void append_key_value(gpointer _k, gpointer _v, gpointer user_data)
+-{
+-      GHashTable *receiver = (GHashTable *)user_data;
+-
+-      /* Don't override local share options */
+-      if (!g_hash_table_lookup(receiver, _k))
+-              g_hash_table_insert(receiver, g_strdup(_k), g_strdup(_v));
+-}
+-
+-static void global_conf_update(struct smbconf_group *group)
+-{
+-      if (!global_group)
+-              return;
+-
+-      g_hash_table_foreach(global_group->kv, append_key_value, group->kv);
+-}
+-
+-static void global_conf_fixup_missing(void)
+-{
+-      /*
+-       * Set default global parameters which were not specified
+-       * in smb.conf
+-       */
+-      if (!global_conf.file_max)
+-              global_conf.file_max = KSMBD_CONF_FILE_MAX;
+-      if (!global_conf.server_string)
+-              global_conf.server_string =
+-                      cp_get_group_kv_string(
+-                                      KSMBD_CONF_DEFAULT_SERVER_STRING);
+-      if (!global_conf.netbios_name)
+-              global_conf.netbios_name =
+-                      cp_get_group_kv_string(KSMBD_CONF_DEFAULT_NETBIOS_NAME);
+-      if (!global_conf.work_group)
+-              global_conf.work_group =
+-                      cp_get_group_kv_string(KSMBD_CONF_DEFAULT_WORK_GROUP);
+-      if (!global_conf.tcp_port)
+-              global_conf.tcp_port = KSMBD_CONF_DEFAULT_TCP_PORT;
+-
+-      if (global_conf.sessions_cap <= 0)
+-              global_conf.sessions_cap = KSMBD_CONF_DEFAULT_SESS_CAP;
+-
+-      if (!global_conf.guest_account &&
+-          cp_add_global_guest_account(KSMBD_CONF_DEFAULT_GUEST_ACCOUNT))
+-              pr_err("Unable to add guest account\n");
+-}
+-
+-static void groups_callback(gpointer _k, gpointer _v, gpointer user_data)
+-{
+-      struct smbconf_group *group = (struct smbconf_group *)_v;
+-      unsigned short cb_mode = *(unsigned short *)user_data;
+-
+-      if (group == global_group)
+-              return;
+-
+-      group->cb_mode = cb_mode;
+-
+-      if (group != ipc_group)
+-              global_conf_update(group);
+-
+-      shm_add_new_share(group);
+-}
+-
+-static int cp_add_ipc_group(void)
+-{
+-      char *comment = NULL, *guest = NULL;
+-      int ret = 0;
+-
+-      if (ipc_group)
+-              return ret;
+-
+-      comment = g_strdup("comment = IPC share");
+-      guest = g_strdup("guest ok = yes");
+-      ret = add_new_group("[IPC$]");
+-      ret |= add_group_key_value(comment);
+-      ret |= add_group_key_value(guest);
+-      if (ret) {
+-              pr_err("Unable to add IPC$ share\n");
+-              ret = -EINVAL;
+-              goto out;
+-      }
+-
+-      ipc_group = g_hash_table_lookup(parser.groups, "ipc$");
+-out:
+-      g_free(comment);
+-      g_free(guest);
+-      return ret;
+-}
+-
+-static int __cp_parse_smbconfig(const char *smbconf, GHFunc cb,
+-                              unsigned short cb_mode)
+-{
+-      int ret;
+-
+-      global_conf_default();
+-
+-      ret = cp_smbconfig_hash_create(smbconf);
+-      if (ret)
+-              return ret;
+-
+-      ret = cp_add_ipc_group();
+-      if (ret)
+-              goto out;
+-
+-      global_group = g_hash_table_lookup(parser.groups, "global");
+-      if (global_group)
+-              global_group->cb_mode = cb_mode;
+-
+-      global_conf_create();
+-      g_hash_table_foreach(parser.groups, groups_callback, &cb_mode);
+-      global_conf_fixup_missing();
+-out:
+-      cp_smbconfig_destroy();
+-      return ret;
+-}
+-
+-int cp_parse_reload_smbconf(const char *smbconf)
+-{
+-      return __cp_parse_smbconfig(smbconf, groups_callback,
+-                                  GROUPS_CALLBACK_REINIT);
+-}
+-
+-int cp_parse_smbconf(const char *smbconf)
+-{
+-      return __cp_parse_smbconfig(smbconf, groups_callback,
+-                                  GROUPS_CALLBACK_INIT);
+-}
+-
+-int cp_parse_pwddb(const char *pwddb)
+-{
+-      return __mmap_parse_file(pwddb, usm_add_update_user_from_pwdentry);
+-}
+-
+-int cp_smbconfig_hash_create(const char *smbconf)
+-{
+-      int ret = init_smbconf_parser();
+-
+-      if (ret)
+-              return ret;
+-      return __mmap_parse_file(smbconf, process_smbconf_entry);
+-}
+-
+-int cp_parse_subauth(void)
+-{
+-      return __mmap_parse_file(PATH_SUBAUTH, usm_add_subauth_global_conf);
+-}
+-
+-void cp_smbconfig_destroy(void)
+-{
+-      release_smbconf_parser();
+-}
+-
+-int cp_parse_external_smbconf_group(char *name, char *opts)
+-{
+-      char *pos;
+-      int i, len;
+-
+-      if (init_smbconf_parser())
+-              return -EINVAL;
+-
+-      if (!opts || !name)
+-              return -EINVAL;
+-
+-      len = strlen(opts);
+-      /* fake smb.conf input */
+-      for (i = 0; i < KSMBD_SHARE_CONF_MAX; i++) {
+-              pos = strstr(opts, KSMBD_SHARE_CONF[i]);
+-              if (!pos)
+-                      continue;
+-              if (pos != opts)
+-                      *(pos - 1) = '\n';
+-      }
+-
+-      if (add_new_group(name))
+-              goto error;
+-
+-      /* split input and feed to normal process_smbconf_entry() */
+-      while (len) {
+-              char *delim = strchr(opts, '\n');
+-
+-              if (delim) {
+-                      *delim = 0x00;
+-                      len -= delim - opts;
+-              } else {
+-                      len = 0;
+-              }
+-
+-              process_smbconf_entry(opts);
+-              if (delim)
+-                      opts = delim + 1;
+-      }
+-      return 0;
+-
+-error:
+-      cp_smbconfig_destroy();
+-      return -EINVAL;
+-}
+--- /dev/null
++++ b/tools/config_parser.c
+@@ -0,0 +1,770 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ *   linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <glib.h>
++#include <string.h>
++#include <glib/gstdio.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <linux/ksmbd_server.h>
++
++#include <config_parser.h>
++#include <tools.h>
++#include <management/user.h>
++#include <management/share.h>
++
++struct smbconf_global global_conf;
++struct smbconf_parser parser;
++struct smbconf_group *global_group, *ipc_group;
++
++unsigned long long memparse(const char *v)
++{
++      char *eptr;
++
++      unsigned long long ret = strtoull(v, &eptr, 0);
++
++      switch (*eptr) {
++      case 'E':
++      case 'e':
++              ret <<= 10;
++      case 'P':
++      case 'p':
++              ret <<= 10;
++      case 'T':
++      case 't':
++              ret <<= 10;
++      case 'G':
++      case 'g':
++              ret <<= 10;
++      case 'M':
++      case 'm':
++              ret <<= 10;
++      case 'K':
++      case 'k':
++              ret <<= 10;
++      }
++
++      return ret;
++}
++
++static void kv_release_cb(gpointer p)
++{
++      g_free(p);
++}
++
++static int is_ascii_space_tab(char c)
++{
++      return c == ' ' || c == '\t';
++}
++
++static int is_a_comment(char *line)
++{
++      return (*line == 0x00 || *line == ';' || *line == '\n' || *line == '#');
++}
++
++static int is_a_group(char *line)
++{
++      char *p = line;
++
++      if (*p != '[')
++              return 0;
++      p++;
++      while (*p && *p != ']')
++              p = g_utf8_find_next_char(p, NULL);
++      if (*p != ']')
++              return 0;
++      return 1;
++}
++
++static int add_new_group(char *line)
++{
++      char *begin = line;
++      char *end = line;
++      char *name = NULL;
++      struct smbconf_group *group = NULL;
++      struct smbconf_group *lookup;
++
++      while (*end && *end != ']')
++              end = g_utf8_find_next_char(end, NULL);
++
++      name = g_strndup(begin + 1, end - begin - 1);
++      if (!name)
++              goto out_free;
++
++      lookup = g_hash_table_lookup(parser.groups, name);
++      if (lookup) {
++              parser.current = lookup;
++              pr_info("Multiple definitions for group `%s'\n", name);
++              g_free(name);
++              return 0;
++      }
++
++      group = g_malloc(sizeof(struct smbconf_group));
++      group->cb_mode = GROUPS_CALLBACK_NONE;
++      group->name = name;
++      group->kv = g_hash_table_new_full(g_str_hash,
++                                        g_str_equal,
++                                        kv_release_cb,
++                                        kv_release_cb);
++      if (!group->kv)
++              goto out_free;
++
++      parser.current = group;
++      g_hash_table_insert(parser.groups, group->name, group);
++      return 0;
++
++out_free:
++      g_free(name);
++      if (group && group->kv)
++              g_hash_table_destroy(group->kv);
++      g_free(group);
++      return -ENOMEM;
++}
++
++static int add_group_key_value(char *line)
++{
++      char *key, *value;
++
++      key = strchr(line, '=');
++      if (!key)
++              return -EINVAL;
++
++      value = key;
++      *key = 0x00;
++      key--;
++      value++;
++
++      while (is_ascii_space_tab(*key))
++              key--;
++      while (is_ascii_space_tab(*value))
++              value++;
++
++      if (is_a_comment(value))
++              return 0;
++
++      if (g_hash_table_lookup(parser.current->kv, key)) {
++              pr_info("Multiple key-value definitions [%s] %s\n",
++                              parser.current->name, key);
++              return 0;
++      }
++
++      key = g_strndup(line, key - line + 1);
++      value = g_strdup(value);
++
++      if (!key || !value) {
++              g_free(key);
++              g_free(value);
++              return -ENOMEM;
++      }
++
++      g_hash_table_insert(parser.current->kv, key, value);
++      return 0;
++}
++
++static int process_smbconf_entry(char *data)
++{
++      while (is_ascii_space_tab(*data))
++              data++;
++
++      if (is_a_comment(data))
++              return 0;
++
++      if (is_a_group(data))
++              return add_new_group(data);
++
++      return add_group_key_value(data);
++}
++
++static int __mmap_parse_file(const char *fname, int (*callback)(char *data))
++{
++      GMappedFile *file;
++      GError *err = NULL;
++      gchar *contents;
++      int len;
++      char *delim;
++      int fd, ret = 0;
++
++      fd = g_open(fname, O_RDONLY, 0);
++      if (fd == -1) {
++              ret = -errno;
++              pr_debug("Can't open `%s': %m\n", fname);
++              return ret;
++      }
++
++      file = g_mapped_file_new_from_fd(fd, FALSE, &err);
++      if (err) {
++              pr_err("Can't map `%s' to memory: %s\n", fname, err->message);
++              g_error_free(err);
++              ret = -EINVAL;
++              goto out;
++      }
++
++      contents = g_mapped_file_get_contents(file);
++      if (!contents)
++              goto out;
++
++      len = g_mapped_file_get_length(file);
++      while (len > 0) {
++              delim = strchr(contents, '\n');
++              if (!delim)
++                      delim = contents + len - 1;
++
++              if (delim) {
++                      size_t sz = delim - contents;
++                      char *data;
++
++                      if (delim == contents) {
++                              contents = delim + 1;
++                              len--;
++                              continue;
++                      }
++
++                      if (!sz)
++                              break;
++
++                      data = g_strndup(contents, sz);
++                      ret = callback(data);
++                      if (ret) {
++                              g_free(data);
++                              goto out;
++                      }
++
++                      g_free(data);
++                      contents = delim + 1;
++                      len -= (sz + 1);
++              }
++      }
++
++      ret = 0;
++out:
++      if (file)
++              g_mapped_file_unref(file);
++
++      if (fd) {
++              g_close(fd, &err);
++              if (err) {
++                      pr_err("Can't close `%s': %s\n", fname, err->message);
++                      g_error_free(err);
++              }
++      }
++      return ret;
++}
++
++static int init_smbconf_parser(void)
++{
++      if (parser.groups)
++              return 0;
++
++      parser.groups = g_hash_table_new(shm_share_name_hash,
++                                       shm_share_name_equal);
++      if (!parser.groups)
++              return -ENOMEM;
++      return 0;
++}
++
++static void release_smbconf_group(gpointer k, gpointer v, gpointer user_data)
++{
++      struct smbconf_group *g = v;
++
++      g_hash_table_destroy(g->kv);
++      g_free(g->name);
++      g_free(g);
++}
++
++static void release_smbconf_parser(void)
++{
++      if (!parser.groups)
++              return;
++
++      g_hash_table_foreach(parser.groups, release_smbconf_group, NULL);
++      g_hash_table_destroy(parser.groups);
++      parser.groups = NULL;
++}
++
++char *cp_ltrim(char *v)
++{
++      if (!v)
++              return NULL;
++
++      while (*v && *v == ' ')
++              v++;
++      if (*v == 0x00)
++              return NULL;
++      return v;
++}
++
++int cp_key_cmp(char *k, char *v)
++{
++      if (!k || !v)
++              return -1;
++      return g_ascii_strncasecmp(k, v, strlen(v));
++}
++
++char *cp_get_group_kv_string(char *v)
++{
++      return g_strdup(v);
++}
++
++int cp_get_group_kv_bool(char *v)
++{
++      if (!g_ascii_strncasecmp(v, "yes", 3) ||
++              !g_ascii_strncasecmp(v, "1", 1) ||
++              !g_ascii_strncasecmp(v, "true", 4) ||
++              !g_ascii_strncasecmp(v, "enable", 6))
++              return 1;
++      return 0;
++}
++
++int cp_get_group_kv_config_opt(char *v)
++{
++      if (!g_ascii_strncasecmp(v, "disabled", 8))
++              return KSMBD_CONFIG_OPT_DISABLED;
++      if (!g_ascii_strncasecmp(v, "enabled", 7))
++              return KSMBD_CONFIG_OPT_ENABLED;
++      if (!g_ascii_strncasecmp(v, "auto", 4))
++              return KSMBD_CONFIG_OPT_AUTO;
++      if (!g_ascii_strncasecmp(v, "mandatory", 9))
++              return KSMBD_CONFIG_OPT_MANDATORY;
++      return KSMBD_CONFIG_OPT_DISABLED;
++}
++
++unsigned long cp_get_group_kv_long_base(char *v, int base)
++{
++      return strtoul(v, NULL, base);
++}
++
++unsigned long cp_get_group_kv_long(char *v)
++{
++      return cp_get_group_kv_long_base(v, 10);
++}
++
++char **cp_get_group_kv_list(char *v)
++{
++      /*
++       * SMB conf lists are "tabs, spaces, commas" separated.
++       */
++      return g_strsplit_set(v, "\t ,", -1);
++}
++
++void cp_group_kv_list_free(char **list)
++{
++      g_strfreev(list);
++}
++
++static int cp_add_global_guest_account(gpointer _v)
++{
++      struct ksmbd_user *user;
++
++      if (usm_add_new_user(cp_get_group_kv_string(_v),
++                           g_strdup("NULL"))) {
++              pr_err("Unable to add guest account\n");
++              return -ENOMEM;
++      }
++
++      user = usm_lookup_user(_v);
++      if (!user) {
++              pr_err("Unable to find user `%s'\n", (char *) _v);
++              return -EINVAL;
++      }
++
++      set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT);
++      put_ksmbd_user(user);
++      global_conf.guest_account = cp_get_group_kv_string(_v);
++      return 0;
++}
++
++static gboolean global_group_kv(gpointer _k, gpointer _v, gpointer user_data)
++{
++      if (!cp_key_cmp(_k, "server string")) {
++              global_conf.server_string = cp_get_group_kv_string(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "workgroup")) {
++              global_conf.work_group = cp_get_group_kv_string(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "netbios name")) {
++              global_conf.netbios_name = cp_get_group_kv_string(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "server min protocol")) {
++              global_conf.server_min_protocol = cp_get_group_kv_string(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "server signing")) {
++              global_conf.server_signing = cp_get_group_kv_config_opt(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "server max protocol")) {
++              global_conf.server_max_protocol = cp_get_group_kv_string(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "guest account")) {
++              cp_add_global_guest_account(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "max active sessions")) {
++              global_conf.sessions_cap = cp_get_group_kv_long(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "tcp port")) {
++              if (!global_conf.tcp_port)
++                      global_conf.tcp_port = cp_get_group_kv_long(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "ipc timeout")) {
++              global_conf.ipc_timeout = cp_get_group_kv_long(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "max open files")) {
++              global_conf.file_max = cp_get_group_kv_long(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "restrict anonymous")) {
++              global_conf.restrict_anon = cp_get_group_kv_long(_v);
++              if (global_conf.restrict_anon > KSMBD_RESTRICT_ANON_TYPE_2 ||
++                              global_conf.restrict_anon < 0) {
++                      global_conf.restrict_anon = 0;
++                      pr_err("Invalid restrict anonymous value\n");
++              }
++
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "map to guest")) {
++              global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_NEVER;
++              if (!cp_key_cmp(_v, "bad user"))
++                      global_conf.map_to_guest =
++                              KSMBD_CONF_MAP_TO_GUEST_BAD_USER;
++              if (!cp_key_cmp(_v, "bad password"))
++                      global_conf.map_to_guest =
++                              KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD;
++              if (!cp_key_cmp(_v, "bad uid"))
++                      global_conf.map_to_guest =
++                              KSMBD_CONF_MAP_TO_GUEST_BAD_UID;
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "bind interfaces only")) {
++              global_conf.bind_interfaces_only = cp_get_group_kv_bool(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "interfaces")) {
++              global_conf.interfaces = cp_get_group_kv_list(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "deadtime")) {
++              global_conf.deadtime = cp_get_group_kv_long(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "smb2 leases")) {
++              if (cp_get_group_kv_bool(_v))
++                      global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB2_LEASES;
++              else
++                      global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB2_LEASES;
++
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "root directory")) {
++              global_conf.root_dir = cp_get_group_kv_string(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "smb2 max read")) {
++              global_conf.smb2_max_read = memparse(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "smb2 max write")) {
++              global_conf.smb2_max_write = memparse(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "smb2 max trans")) {
++              global_conf.smb2_max_trans = memparse(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "smb3 encryption")) {
++              if (cp_get_group_kv_bool(_v))
++                      global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION;
++              else
++                      global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION;
++
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "share:fake_fscaps")) {
++              global_conf.share_fake_fscaps = cp_get_group_kv_long(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "kerberos service name")) {
++              global_conf.krb5_service_name = cp_get_group_kv_string(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "kerberos keytab file")) {
++              global_conf.krb5_keytab_file = cp_get_group_kv_string(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "server multi channel support")) {
++              if (cp_get_group_kv_bool(_v))
++                      global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL;
++              else
++                      global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL;
++
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "smb2 max credits")) {
++              global_conf.smb2_max_credits = memparse(_v);
++              return TRUE;
++      }
++
++      if (!cp_key_cmp(_k, "smbd max io size")) {
++              global_conf.smbd_max_io_size = memparse(_v);
++              return TRUE;
++      }
++
++      /* At this point, this is an option that must be applied to all shares */
++      return FALSE;
++}
++
++static void global_conf_default(void)
++{
++      /* The SPARSE_FILES file system capability flag is set by default */
++      global_conf.share_fake_fscaps = 64;
++}
++
++static void global_conf_create(void)
++{
++      if (!global_group || global_group->cb_mode != GROUPS_CALLBACK_INIT)
++              return;
++
++      /*
++       * This will transfer server options to global_conf, and leave behind
++       * in the global parser group, the options that must be applied to every
++       * share
++       */
++      g_hash_table_foreach_remove(global_group->kv, global_group_kv, NULL);
++}
++
++static void append_key_value(gpointer _k, gpointer _v, gpointer user_data)
++{
++      GHashTable *receiver = (GHashTable *)user_data;
++
++      /* Don't override local share options */
++      if (!g_hash_table_lookup(receiver, _k))
++              g_hash_table_insert(receiver, g_strdup(_k), g_strdup(_v));
++}
++
++static void global_conf_update(struct smbconf_group *group)
++{
++      if (!global_group)
++              return;
++
++      g_hash_table_foreach(global_group->kv, append_key_value, group->kv);
++}
++
++static void global_conf_fixup_missing(void)
++{
++      /*
++       * Set default global parameters which were not specified
++       * in smb.conf
++       */
++      if (!global_conf.file_max)
++              global_conf.file_max = KSMBD_CONF_FILE_MAX;
++      if (!global_conf.server_string)
++              global_conf.server_string =
++                      cp_get_group_kv_string(
++                                      KSMBD_CONF_DEFAULT_SERVER_STRING);
++      if (!global_conf.netbios_name)
++              global_conf.netbios_name =
++                      cp_get_group_kv_string(KSMBD_CONF_DEFAULT_NETBIOS_NAME);
++      if (!global_conf.work_group)
++              global_conf.work_group =
++                      cp_get_group_kv_string(KSMBD_CONF_DEFAULT_WORK_GROUP);
++      if (!global_conf.tcp_port)
++              global_conf.tcp_port = KSMBD_CONF_DEFAULT_TCP_PORT;
++
++      if (global_conf.sessions_cap <= 0)
++              global_conf.sessions_cap = KSMBD_CONF_DEFAULT_SESS_CAP;
++
++      if (!global_conf.guest_account &&
++          cp_add_global_guest_account(KSMBD_CONF_DEFAULT_GUEST_ACCOUNT))
++              pr_err("Unable to add guest account\n");
++}
++
++static void groups_callback(gpointer _k, gpointer _v, gpointer user_data)
++{
++      struct smbconf_group *group = (struct smbconf_group *)_v;
++      unsigned short cb_mode = *(unsigned short *)user_data;
++
++      if (group == global_group)
++              return;
++
++      group->cb_mode = cb_mode;
++
++      if (group != ipc_group)
++              global_conf_update(group);
++
++      shm_add_new_share(group);
++}
++
++static int cp_add_ipc_group(void)
++{
++      char *comment = NULL, *guest = NULL;
++      int ret = 0;
++
++      if (ipc_group)
++              return ret;
++
++      comment = g_strdup("comment = IPC share");
++      guest = g_strdup("guest ok = yes");
++      ret = add_new_group("[IPC$]");
++      ret |= add_group_key_value(comment);
++      ret |= add_group_key_value(guest);
++      if (ret) {
++              pr_err("Unable to add IPC$ share\n");
++              ret = -EINVAL;
++              goto out;
++      }
++
++      ipc_group = g_hash_table_lookup(parser.groups, "ipc$");
++out:
++      g_free(comment);
++      g_free(guest);
++      return ret;
++}
++
++static int __cp_parse_smbconfig(const char *smbconf, GHFunc cb,
++                              unsigned short cb_mode)
++{
++      int ret;
++
++      global_conf_default();
++
++      ret = cp_smbconfig_hash_create(smbconf);
++      if (ret)
++              return ret;
++
++      ret = cp_add_ipc_group();
++      if (ret)
++              goto out;
++
++      global_group = g_hash_table_lookup(parser.groups, "global");
++      if (global_group)
++              global_group->cb_mode = cb_mode;
++
++      global_conf_create();
++      g_hash_table_foreach(parser.groups, groups_callback, &cb_mode);
++      global_conf_fixup_missing();
++out:
++      cp_smbconfig_destroy();
++      return ret;
++}
++
++int cp_parse_reload_smbconf(const char *smbconf)
++{
++      return __cp_parse_smbconfig(smbconf, groups_callback,
++                                  GROUPS_CALLBACK_REINIT);
++}
++
++int cp_parse_smbconf(const char *smbconf)
++{
++      return __cp_parse_smbconfig(smbconf, groups_callback,
++                                  GROUPS_CALLBACK_INIT);
++}
++
++int cp_parse_pwddb(const char *pwddb)
++{
++      return __mmap_parse_file(pwddb, usm_add_update_user_from_pwdentry);
++}
++
++int cp_smbconfig_hash_create(const char *smbconf)
++{
++      int ret = init_smbconf_parser();
++
++      if (ret)
++              return ret;
++      return __mmap_parse_file(smbconf, process_smbconf_entry);
++}
++
++int cp_parse_subauth(void)
++{
++      return __mmap_parse_file(PATH_SUBAUTH, usm_add_subauth_global_conf);
++}
++
++void cp_smbconfig_destroy(void)
++{
++      release_smbconf_parser();
++}
++
++int cp_parse_external_smbconf_group(char *name, char *opts)
++{
++      char *pos;
++      int i, len;
++
++      if (init_smbconf_parser())
++              return -EINVAL;
++
++      if (!opts || !name)
++              return -EINVAL;
++
++      len = strlen(opts);
++      /* fake smb.conf input */
++      for (i = 0; i < KSMBD_SHARE_CONF_MAX; i++) {
++              pos = strstr(opts, KSMBD_SHARE_CONF[i]);
++              if (!pos)
++                      continue;
++              if (pos != opts)
++                      *(pos - 1) = '\n';
++      }
++
++      if (add_new_group(name))
++              goto error;
++
++      /* split input and feed to normal process_smbconf_entry() */
++      while (len) {
++              char *delim = strchr(opts, '\n');
++
++              if (delim) {
++                      *delim = 0x00;
++                      len -= delim - opts;
++              } else {
++                      len = 0;
++              }
++
++              process_smbconf_entry(opts);
++              if (delim)
++                      opts = delim + 1;
++      }
++      return 0;
++
++error:
++      cp_smbconfig_destroy();
++      return -EINVAL;
++}
+--- a/lib/management/session.c
++++ /dev/null
+@@ -1,235 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- *   linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <glib.h>
+-
+-#include "linux/ksmbd_server.h"
+-#include "management/session.h"
+-#include "management/tree_conn.h"
+-#include "management/user.h"
+-#include "ksmbdtools.h"
+-
+-static GHashTable     *sessions_table;
+-static GRWLock                sessions_table_lock;
+-
+-static void __free_func(gpointer data, gpointer user_data)
+-{
+-      struct ksmbd_tree_conn *tree_conn;
+-
+-      tree_conn = (struct ksmbd_tree_conn *)data;
+-      tcm_tree_conn_free(tree_conn);
+-}
+-
+-static void kill_ksmbd_session(struct ksmbd_session *sess)
+-{
+-      g_list_foreach(sess->tree_conns, __free_func, NULL);
+-      g_list_free(sess->tree_conns);
+-      g_rw_lock_clear(&sess->update_lock);
+-      g_free(sess);
+-}
+-
+-static struct ksmbd_session *new_ksmbd_session(unsigned long long id,
+-                                             struct ksmbd_user *user)
+-{
+-      struct ksmbd_session *sess;
+-
+-      sess = g_try_malloc0(sizeof(struct ksmbd_session));
+-      if (!sess)
+-              return NULL;
+-
+-      g_rw_lock_init(&sess->update_lock);
+-      sess->ref_counter = 1;
+-      sess->id = id;
+-      sess->user = user;
+-      return sess;
+-}
+-
+-static void free_hash_entry(gpointer k, gpointer s, gpointer user_data)
+-{
+-      kill_ksmbd_session(s);
+-}
+-
+-static void sm_clear_sessions(void)
+-{
+-      g_hash_table_foreach(sessions_table, free_hash_entry, NULL);
+-}
+-
+-static int __sm_remove_session(struct ksmbd_session *sess)
+-{
+-      int ret = -EINVAL;
+-
+-      g_rw_lock_writer_lock(&sessions_table_lock);
+-      if (g_hash_table_remove(sessions_table, &sess->id))
+-              ret = 0;
+-      g_rw_lock_writer_unlock(&sessions_table_lock);
+-
+-      if (!ret)
+-              kill_ksmbd_session(sess);
+-      return ret;
+-}
+-
+-static struct ksmbd_session *__get_session(struct ksmbd_session *sess)
+-{
+-      struct ksmbd_session *ret = NULL;
+-
+-      g_rw_lock_writer_lock(&sess->update_lock);
+-      if (sess->ref_counter != 0) {
+-              sess->ref_counter++;
+-              ret = sess;
+-      } else {
+-              ret = NULL;
+-      }
+-      g_rw_lock_writer_unlock(&sess->update_lock);
+-      return ret;
+-}
+-
+-static void __put_session(struct ksmbd_session *sess)
+-{
+-      int drop = 0;
+-
+-      g_rw_lock_writer_lock(&sess->update_lock);
+-      sess->ref_counter--;
+-      drop = !sess->ref_counter;
+-      g_rw_lock_writer_unlock(&sess->update_lock);
+-
+-      if (drop)
+-              __sm_remove_session(sess);
+-}
+-
+-static struct ksmbd_session *__sm_lookup_session(unsigned long long id)
+-{
+-      return g_hash_table_lookup(sessions_table, &id);
+-}
+-
+-static struct ksmbd_session *sm_lookup_session(unsigned long long id)
+-{
+-      struct ksmbd_session *sess;
+-
+-      g_rw_lock_reader_lock(&sessions_table_lock);
+-      sess = __sm_lookup_session(id);
+-      if (sess)
+-              sess = __get_session(sess);
+-      g_rw_lock_reader_unlock(&sessions_table_lock);
+-      return sess;
+-}
+-
+-int sm_handle_tree_connect(unsigned long long id,
+-                         struct ksmbd_user *user,
+-                         struct ksmbd_tree_conn *tree_conn)
+-{
+-      struct ksmbd_session *sess, *lookup;
+-
+-retry:
+-      sess = sm_lookup_session(id);
+-      if (!sess) {
+-              sess = new_ksmbd_session(id, user);
+-              if (!sess)
+-                      return -EINVAL;
+-
+-              g_rw_lock_writer_lock(&sessions_table_lock);
+-              lookup = __sm_lookup_session(id);
+-              if (lookup)
+-                      lookup = __get_session(lookup);
+-              if (lookup) {
+-                      kill_ksmbd_session(sess);
+-                      sess = lookup;
+-              }
+-              if (!g_hash_table_insert(sessions_table, &(sess->id), sess)) {
+-                      kill_ksmbd_session(sess);
+-                      sess = NULL;
+-              }
+-              g_rw_lock_writer_unlock(&sessions_table_lock);
+-
+-              if (!sess)
+-                      goto retry;
+-      }
+-
+-      g_rw_lock_writer_lock(&sess->update_lock);
+-      sess->tree_conns = g_list_insert(sess->tree_conns, tree_conn, -1);
+-      g_rw_lock_writer_unlock(&sess->update_lock);
+-      return 0;
+-}
+-
+-int sm_check_sessions_capacity(unsigned long long id)
+-{
+-      int ret = 0;
+-      struct ksmbd_session *sess;
+-
+-      sess = sm_lookup_session(id);
+-      if (sess) {
+-              __put_session(sess);
+-              return ret;
+-      }
+-
+-      if (g_atomic_int_add(&global_conf.sessions_cap, -1) < 1) {
+-              ret = -EINVAL;
+-              g_atomic_int_inc(&global_conf.sessions_cap);
+-      }
+-      return ret;
+-}
+-
+-static gint lookup_tree_conn(gconstpointer data, gconstpointer user_data)
+-{
+-      struct ksmbd_tree_conn *tree_conn = (struct ksmbd_tree_conn *)data;
+-      struct ksmbd_tree_conn *dummy = (struct ksmbd_tree_conn *)user_data;
+-
+-      if (tree_conn->id == dummy->id)
+-              return 0;
+-      return 1;
+-}
+-
+-int sm_handle_tree_disconnect(unsigned long long sess_id,
+-                            unsigned long long tree_conn_id)
+-{
+-      struct ksmbd_tree_conn dummy;
+-      struct ksmbd_session *sess;
+-      GList *tc_list;
+-
+-      sess = sm_lookup_session(sess_id);
+-      if (!sess)
+-              return 0;
+-
+-      g_atomic_int_inc(&global_conf.sessions_cap);
+-      g_rw_lock_writer_lock(&sess->update_lock);
+-      dummy.id = tree_conn_id;
+-      tc_list = g_list_find_custom(sess->tree_conns,
+-                                   &dummy,
+-                                   lookup_tree_conn);
+-      if (tc_list) {
+-              struct ksmbd_tree_conn *tree_conn;
+-
+-              tree_conn = (struct ksmbd_tree_conn *)tc_list->data;
+-              sess->tree_conns = g_list_remove(sess->tree_conns, tree_conn);
+-              sess->ref_counter--;
+-              tcm_tree_conn_free(tree_conn);
+-      }
+-      g_rw_lock_writer_unlock(&sess->update_lock);
+-
+-      put_ksmbd_user(sess->user);
+-      __put_session(sess);
+-      return 0;
+-}
+-
+-void sm_destroy(void)
+-{
+-      if (sessions_table) {
+-              sm_clear_sessions();
+-              g_hash_table_destroy(sessions_table);
+-      }
+-      g_rw_lock_clear(&sessions_table_lock);
+-}
+-
+-int sm_init(void)
+-{
+-      sessions_table = g_hash_table_new(g_int64_hash, g_int64_equal);
+-      if (!sessions_table)
+-              return -ENOMEM;
+-      g_rw_lock_init(&sessions_table_lock);
+-      return 0;
+-}
+--- /dev/null
++++ b/tools/management/session.c
+@@ -0,0 +1,235 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ *   linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <glib.h>
++
++#include "linux/ksmbd_server.h"
++#include "management/session.h"
++#include "management/tree_conn.h"
++#include "management/user.h"
++#include "tools.h"
++
++static GHashTable     *sessions_table;
++static GRWLock                sessions_table_lock;
++
++static void __free_func(gpointer data, gpointer user_data)
++{
++      struct ksmbd_tree_conn *tree_conn;
++
++      tree_conn = (struct ksmbd_tree_conn *)data;
++      tcm_tree_conn_free(tree_conn);
++}
++
++static void kill_ksmbd_session(struct ksmbd_session *sess)
++{
++      g_list_foreach(sess->tree_conns, __free_func, NULL);
++      g_list_free(sess->tree_conns);
++      g_rw_lock_clear(&sess->update_lock);
++      g_free(sess);
++}
++
++static struct ksmbd_session *new_ksmbd_session(unsigned long long id,
++                                             struct ksmbd_user *user)
++{
++      struct ksmbd_session *sess;
++
++      sess = g_try_malloc0(sizeof(struct ksmbd_session));
++      if (!sess)
++              return NULL;
++
++      g_rw_lock_init(&sess->update_lock);
++      sess->ref_counter = 1;
++      sess->id = id;
++      sess->user = user;
++      return sess;
++}
++
++static void free_hash_entry(gpointer k, gpointer s, gpointer user_data)
++{
++      kill_ksmbd_session(s);
++}
++
++static void sm_clear_sessions(void)
++{
++      g_hash_table_foreach(sessions_table, free_hash_entry, NULL);
++}
++
++static int __sm_remove_session(struct ksmbd_session *sess)
++{
++      int ret = -EINVAL;
++
++      g_rw_lock_writer_lock(&sessions_table_lock);
++      if (g_hash_table_remove(sessions_table, &sess->id))
++              ret = 0;
++      g_rw_lock_writer_unlock(&sessions_table_lock);
++
++      if (!ret)
++              kill_ksmbd_session(sess);
++      return ret;
++}
++
++static struct ksmbd_session *__get_session(struct ksmbd_session *sess)
++{
++      struct ksmbd_session *ret = NULL;
++
++      g_rw_lock_writer_lock(&sess->update_lock);
++      if (sess->ref_counter != 0) {
++              sess->ref_counter++;
++              ret = sess;
++      } else {
++              ret = NULL;
++      }
++      g_rw_lock_writer_unlock(&sess->update_lock);
++      return ret;
++}
++
++static void __put_session(struct ksmbd_session *sess)
++{
++      int drop = 0;
++
++      g_rw_lock_writer_lock(&sess->update_lock);
++      sess->ref_counter--;
++      drop = !sess->ref_counter;
++      g_rw_lock_writer_unlock(&sess->update_lock);
++
++      if (drop)
++              __sm_remove_session(sess);
++}
++
++static struct ksmbd_session *__sm_lookup_session(unsigned long long id)
++{
++      return g_hash_table_lookup(sessions_table, &id);
++}
++
++static struct ksmbd_session *sm_lookup_session(unsigned long long id)
++{
++      struct ksmbd_session *sess;
++
++      g_rw_lock_reader_lock(&sessions_table_lock);
++      sess = __sm_lookup_session(id);
++      if (sess)
++              sess = __get_session(sess);
++      g_rw_lock_reader_unlock(&sessions_table_lock);
++      return sess;
++}
++
++int sm_handle_tree_connect(unsigned long long id,
++                         struct ksmbd_user *user,
++                         struct ksmbd_tree_conn *tree_conn)
++{
++      struct ksmbd_session *sess, *lookup;
++
++retry:
++      sess = sm_lookup_session(id);
++      if (!sess) {
++              sess = new_ksmbd_session(id, user);
++              if (!sess)
++                      return -EINVAL;
++
++              g_rw_lock_writer_lock(&sessions_table_lock);
++              lookup = __sm_lookup_session(id);
++              if (lookup)
++                      lookup = __get_session(lookup);
++              if (lookup) {
++                      kill_ksmbd_session(sess);
++                      sess = lookup;
++              }
++              if (!g_hash_table_insert(sessions_table, &(sess->id), sess)) {
++                      kill_ksmbd_session(sess);
++                      sess = NULL;
++              }
++              g_rw_lock_writer_unlock(&sessions_table_lock);
++
++              if (!sess)
++                      goto retry;
++      }
++
++      g_rw_lock_writer_lock(&sess->update_lock);
++      sess->tree_conns = g_list_insert(sess->tree_conns, tree_conn, -1);
++      g_rw_lock_writer_unlock(&sess->update_lock);
++      return 0;
++}
++
++int sm_check_sessions_capacity(unsigned long long id)
++{
++      int ret = 0;
++      struct ksmbd_session *sess;
++
++      sess = sm_lookup_session(id);
++      if (sess) {
++              __put_session(sess);
++              return ret;
++      }
++
++      if (g_atomic_int_add(&global_conf.sessions_cap, -1) < 1) {
++              ret = -EINVAL;
++              g_atomic_int_inc(&global_conf.sessions_cap);
++      }
++      return ret;
++}
++
++static gint lookup_tree_conn(gconstpointer data, gconstpointer user_data)
++{
++      struct ksmbd_tree_conn *tree_conn = (struct ksmbd_tree_conn *)data;
++      struct ksmbd_tree_conn *dummy = (struct ksmbd_tree_conn *)user_data;
++
++      if (tree_conn->id == dummy->id)
++              return 0;
++      return 1;
++}
++
++int sm_handle_tree_disconnect(unsigned long long sess_id,
++                            unsigned long long tree_conn_id)
++{
++      struct ksmbd_tree_conn dummy;
++      struct ksmbd_session *sess;
++      GList *tc_list;
++
++      sess = sm_lookup_session(sess_id);
++      if (!sess)
++              return 0;
++
++      g_atomic_int_inc(&global_conf.sessions_cap);
++      g_rw_lock_writer_lock(&sess->update_lock);
++      dummy.id = tree_conn_id;
++      tc_list = g_list_find_custom(sess->tree_conns,
++                                   &dummy,
++                                   lookup_tree_conn);
++      if (tc_list) {
++              struct ksmbd_tree_conn *tree_conn;
++
++              tree_conn = (struct ksmbd_tree_conn *)tc_list->data;
++              sess->tree_conns = g_list_remove(sess->tree_conns, tree_conn);
++              sess->ref_counter--;
++              tcm_tree_conn_free(tree_conn);
++      }
++      g_rw_lock_writer_unlock(&sess->update_lock);
++
++      put_ksmbd_user(sess->user);
++      __put_session(sess);
++      return 0;
++}
++
++void sm_destroy(void)
++{
++      if (sessions_table) {
++              sm_clear_sessions();
++              g_hash_table_destroy(sessions_table);
++      }
++      g_rw_lock_clear(&sessions_table_lock);
++}
++
++int sm_init(void)
++{
++      sessions_table = g_hash_table_new(g_int64_hash, g_int64_equal);
++      if (!sessions_table)
++              return -ENOMEM;
++      g_rw_lock_init(&sessions_table_lock);
++      return 0;
++}
+--- a/lib/management/share.c
++++ /dev/null
+@@ -1,868 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- *   linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <glib.h>
+-#include <sys/types.h>
+-#include <pwd.h>
+-#include <grp.h>
+-
+-#include "config_parser.h"
+-#include "linux/ksmbd_server.h"
+-#include "management/share.h"
+-#include "management/user.h"
+-#include "ksmbdtools.h"
+-
+-#define KSMBD_SHARE_STATE_FREEING     1
+-
+-/*
+- * WARNING:
+- *
+- * This must match KSMBD_SHARE_CONF enum 1:1.
+- * Add new entries ONLY to the bottom.
+- */
+-char *KSMBD_SHARE_CONF[KSMBD_SHARE_CONF_MAX] = {
+-      "comment",                              /* 0 */
+-      "path",
+-      "guest ok",
+-      "guest account",
+-      "read only",
+-      "browseable",                           /* 5 */
+-      "write ok",
+-      "writeable",
+-      "store dos attributes",
+-      "oplocks",
+-      "create mask",                          /* 10 */
+-      "directory mask",
+-      "force create mode",
+-      "force directory mode",
+-      "force group",
+-      "force user",                           /* 15 */
+-      "hide dot files",
+-      "valid users",
+-      "invalid users",
+-      "read list",
+-      "write list",                           /* 20 */
+-      "admin users",
+-      "hosts allow",
+-      "hosts deny",
+-      "max connections",
+-      "veto files",                           /* 25 */
+-      "inherit owner",
+-      "follow symlinks",
+-      "vfs objects",
+-      "writable",
+-};
+-
+-static GHashTable     *shares_table;
+-static GRWLock                shares_table_lock;
+-
+-int shm_share_config(char *k, enum KSMBD_SHARE_CONF c)
+-{
+-      if (c >= KSMBD_SHARE_CONF_MAX)
+-              return 0;
+-
+-      return !cp_key_cmp(k, KSMBD_SHARE_CONF[c]);
+-}
+-
+-static void list_hosts_callback(gpointer k, gpointer v, gpointer user_data)
+-{
+-      free(k);
+-      free(v);
+-}
+-
+-static void free_hosts_map(GHashTable *map)
+-{
+-      if (map) {
+-              g_hash_table_foreach(map, list_hosts_callback, NULL);
+-              g_hash_table_destroy(map);
+-      }
+-}
+-
+-static void list_user_callback(gpointer k, gpointer u, gpointer user_data)
+-{
+-      put_ksmbd_user((struct ksmbd_user *)u);
+-}
+-
+-static void free_user_map(GHashTable *map)
+-{
+-      if (map) {
+-              g_hash_table_foreach(map, list_user_callback, NULL);
+-              g_hash_table_destroy(map);
+-      }
+-}
+-
+-static void kill_ksmbd_share(struct ksmbd_share *share)
+-{
+-      int i;
+-
+-      pr_debug("Kill share `%s'\n", share->name);
+-
+-      for (i = 0; i < KSMBD_SHARE_USERS_MAX; i++)
+-              free_user_map(share->maps[i]);
+-
+-      free_hosts_map(share->hosts_allow_map);
+-      free_hosts_map(share->hosts_deny_map);
+-
+-      g_rw_lock_clear(&share->maps_lock);
+-
+-      free(share->name);
+-      free(share->path);
+-      free(share->comment);
+-      free(share->veto_list);
+-      free(share->guest_account);
+-      g_rw_lock_clear(&share->update_lock);
+-      g_free(share);
+-}
+-
+-static int __shm_remove_share(struct ksmbd_share *share)
+-{
+-      int ret = 0;
+-
+-      if (share->state != KSMBD_SHARE_STATE_FREEING) {
+-              g_rw_lock_writer_lock(&shares_table_lock);
+-              if (!g_hash_table_remove(shares_table, share->name))
+-                      ret = -EINVAL;
+-              g_rw_lock_writer_unlock(&shares_table_lock);
+-      }
+-      if (!ret)
+-              kill_ksmbd_share(share);
+-      return ret;
+-}
+-
+-struct ksmbd_share *get_ksmbd_share(struct ksmbd_share *share)
+-{
+-      g_rw_lock_writer_lock(&share->update_lock);
+-      if (share->ref_count != 0) {
+-              share->ref_count++;
+-              g_rw_lock_writer_unlock(&share->update_lock);
+-      } else {
+-              g_rw_lock_writer_unlock(&share->update_lock);
+-              share = NULL;
+-      }
+-
+-      return share;
+-}
+-
+-void put_ksmbd_share(struct ksmbd_share *share)
+-{
+-      int drop;
+-
+-      if (!share)
+-              return;
+-
+-      g_rw_lock_writer_lock(&share->update_lock);
+-      share->ref_count--;
+-      drop = !share->ref_count;
+-      g_rw_lock_writer_unlock(&share->update_lock);
+-
+-      if (!drop)
+-              return;
+-
+-      __shm_remove_share(share);
+-}
+-
+-static gboolean put_share_callback(gpointer _k, gpointer _v, gpointer data)
+-{
+-      struct ksmbd_share *share = (struct ksmbd_share *)_v;
+-
+-      share->state = KSMBD_SHARE_STATE_FREEING;
+-      put_ksmbd_share(share);
+-      return TRUE;
+-}
+-
+-void shm_remove_all_shares(void)
+-{
+-      g_rw_lock_writer_lock(&shares_table_lock);
+-      g_hash_table_foreach_remove(shares_table, put_share_callback, NULL);
+-      g_rw_lock_writer_unlock(&shares_table_lock);
+-}
+-
+-static struct ksmbd_share *new_ksmbd_share(void)
+-{
+-      struct ksmbd_share *share;
+-      int i;
+-
+-      share = g_try_malloc0(sizeof(struct ksmbd_share));
+-      if (!share)
+-              return NULL;
+-
+-      share->ref_count = 1;
+-      /*
+-       * Create maps as needed. NULL maps means that share
+-       * does not have a corresponding shmbconf entry.
+-       */
+-      for (i = 0; i < KSMBD_SHARE_USERS_MAX; i++)
+-              share->maps[i] = NULL;
+-
+-      share->hosts_allow_map = NULL;
+-      share->hosts_deny_map = NULL;
+-      g_rw_lock_init(&share->maps_lock);
+-      g_rw_lock_init(&share->update_lock);
+-
+-      return share;
+-}
+-
+-static void free_hash_entry(gpointer k, gpointer s, gpointer user_data)
+-{
+-      kill_ksmbd_share(s);
+-}
+-
+-static void shm_clear_shares(void)
+-{
+-      g_hash_table_foreach(shares_table, free_hash_entry, NULL);
+-}
+-
+-void shm_destroy(void)
+-{
+-      if (shares_table) {
+-              shm_clear_shares();
+-              g_hash_table_destroy(shares_table);
+-      }
+-      g_rw_lock_clear(&shares_table_lock);
+-}
+-
+-static char *shm_casefold_share_name(const char *name, size_t len)
+-{
+-      char *nfdi_name, *nfdicf_name;
+-
+-      nfdi_name = g_utf8_normalize(name, len, G_NORMALIZE_NFD);
+-      if (!nfdi_name)
+-              goto out_ascii;
+-
+-      nfdicf_name = g_utf8_casefold(nfdi_name, strlen(nfdi_name));
+-      g_free(nfdi_name);
+-      return nfdicf_name;
+-out_ascii:
+-      g_free(nfdi_name);
+-      return g_ascii_strdown(name, len);
+-}
+-
+-guint shm_share_name_hash(gconstpointer name)
+-{
+-      char *cf_name;
+-      guint hash;
+-
+-      cf_name = shm_casefold_share_name(name, strlen(name));
+-      hash = g_str_hash(cf_name);
+-      g_free(cf_name);
+-      return hash;
+-}
+-
+-gboolean shm_share_name_equal(gconstpointer lname, gconstpointer rname)
+-{
+-      char *cf_lname, *cf_rname;
+-      gboolean equal;
+-
+-      cf_lname = shm_casefold_share_name(lname, strlen(lname));
+-      cf_rname = shm_casefold_share_name(rname, strlen(rname));
+-      equal = g_str_equal(cf_lname, cf_rname);
+-      g_free(cf_lname);
+-      g_free(cf_rname);
+-      return equal;
+-}
+-
+-int shm_init(void)
+-{
+-      shares_table = g_hash_table_new(shm_share_name_hash,
+-                                      shm_share_name_equal);
+-      if (!shares_table)
+-              return -ENOMEM;
+-      g_rw_lock_init(&shares_table_lock);
+-      return 0;
+-}
+-
+-static struct ksmbd_share *__shm_lookup_share(char *name)
+-{
+-      return g_hash_table_lookup(shares_table, name);
+-}
+-
+-struct ksmbd_share *shm_lookup_share(char *name)
+-{
+-      struct ksmbd_share *share, *ret;
+-
+-      g_rw_lock_reader_lock(&shares_table_lock);
+-      share = __shm_lookup_share(name);
+-      if (share) {
+-              ret = get_ksmbd_share(share);
+-              if (!ret)
+-                      share = NULL;
+-      }
+-      g_rw_lock_reader_unlock(&shares_table_lock);
+-      return share;
+-}
+-
+-static GHashTable *parse_list(GHashTable *map, char **list, char grc)
+-{
+-      int i;
+-
+-      if (!list)
+-              return map;
+-
+-      if (!map)
+-              map = g_hash_table_new(g_str_hash, g_str_equal);
+-      if (!map)
+-              return map;
+-
+-      for (i = 0;  list[i] != NULL; i++) {
+-              struct ksmbd_user *user;
+-              char *p = list[i];
+-
+-              p = cp_ltrim(p);
+-              if (!p)
+-                      continue;
+-
+-              if (*p == grc) {
+-                      struct group *gr;
+-
+-                      gr = getgrnam(p + 1);
+-                      if (gr)
+-                              parse_list(map, gr->gr_mem, 0x00);
+-                      continue;
+-              }
+-
+-              user = usm_lookup_user(p);
+-              if (!user) {
+-                      pr_info("Drop non-existing user `%s'\n", p);
+-                      continue;
+-              }
+-
+-              if (g_hash_table_lookup(map, user->name)) {
+-                      pr_debug("User `%s' already exists in a map\n",
+-                               user->name);
+-                      continue;
+-              }
+-
+-              g_hash_table_insert(map, user->name, user);
+-      }
+-
+-      return map;
+-}
+-
+-static void make_veto_list(struct ksmbd_share *share)
+-{
+-      int i;
+-
+-      for (i = 0; i < share->veto_list_sz; i++) {
+-              if (share->veto_list[i] == '/')
+-                      share->veto_list[i] = 0x00;
+-      }
+-}
+-
+-static void force_group(struct ksmbd_share *share, char *name)
+-{
+-      struct group *grp;
+-
+-      grp = getgrnam(name);
+-      if (grp) {
+-              share->force_gid = grp->gr_gid;
+-              if (share->force_gid == KSMBD_SHARE_INVALID_GID)
+-                      pr_err("Invalid force GID: %u\n", share->force_gid);
+-      } else
+-              pr_err("Unable to lookup up `/etc/group' entry: %s\n", name);
+-}
+-
+-static void force_user(struct ksmbd_share *share, char *name)
+-{
+-      struct passwd *passwd;
+-
+-      passwd = getpwnam(name);
+-      if (passwd) {
+-              share->force_uid = passwd->pw_uid;
+-              /*
+-               * smb.conf 'force group' has higher priority than
+-               * 'force user'.
+-               */
+-              if (share->force_gid == KSMBD_SHARE_INVALID_GID)
+-                      share->force_gid = passwd->pw_gid;
+-              if (share->force_uid == KSMBD_SHARE_INVALID_UID ||
+-                              share->force_gid == KSMBD_SHARE_INVALID_GID)
+-                      pr_err("Invalid force UID/GID: %u/%u\n",
+-                                      share->force_uid, share->force_gid);
+-      } else {
+-              pr_err("Unable to lookup up `/etc/passwd' entry: %s\n", name);
+-      }
+-}
+-
+-static void process_group_kv(gpointer _k, gpointer _v, gpointer user_data)
+-{
+-      struct ksmbd_share *share = user_data;
+-      char *k = _k;
+-      char *v = _v;
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_COMMENT)) {
+-              share->comment = cp_get_group_kv_string(v);
+-              if (share->comment == NULL)
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_PATH)) {
+-              share->path = cp_get_group_kv_string(v);
+-              if (share->path == NULL)
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_OK)) {
+-              if (cp_get_group_kv_bool(v))
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_ACCOUNT)) {
+-              struct ksmbd_user *user;
+-
+-              if (usm_add_new_user(cp_get_group_kv_string(_v),
+-                                   g_strdup("NULL"))) {
+-                      pr_err("Unable to add guest account\n");
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-                      return;
+-              }
+-
+-              user = usm_lookup_user(_v);
+-              if (user) {
+-                      set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT);
+-                      put_ksmbd_user(user);
+-              }
+-              share->guest_account = cp_get_group_kv_string(_v);
+-              if (!share->guest_account)
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_READ_ONLY)) {
+-              if (cp_get_group_kv_bool(v)) {
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
+-                      clear_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
+-              } else {
+-                      clear_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
+-              }
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_BROWSEABLE)) {
+-              if (cp_get_group_kv_bool(v))
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
+-              else
+-                      clear_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_OK) ||
+-                      shm_share_config(k, KSMBD_SHARE_CONF_WRITEABLE) ||
+-                      shm_share_config(k, KSMBD_SHARE_CONF_WRITABLE)) {
+-              if (cp_get_group_kv_bool(v))
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
+-              else
+-                      clear_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_STORE_DOS_ATTRIBUTES)) {
+-              if (cp_get_group_kv_bool(v))
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
+-              else
+-                      clear_share_flag(share,
+-                                      KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_OPLOCKS)) {
+-              if (cp_get_group_kv_bool(v))
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
+-              else
+-                      clear_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_CREATE_MASK)) {
+-              share->create_mask = cp_get_group_kv_long_base(v, 8);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_DIRECTORY_MASK)) {
+-              share->directory_mask = cp_get_group_kv_long_base(v, 8);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_CREATE_MODE)) {
+-              share->force_create_mode = cp_get_group_kv_long_base(v, 8);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_DIRECTORY_MODE)) {
+-              share->force_directory_mode = cp_get_group_kv_long_base(v, 8);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_GROUP)) {
+-              force_group(share, v);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_USER)) {
+-              force_user(share, v);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_HIDE_DOT_FILES)) {
+-              if (cp_get_group_kv_bool(v))
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
+-              else
+-                      clear_share_flag(share,
+-                              KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_VALID_USERS)) {
+-              char **users_list;
+-
+-              users_list = cp_get_group_kv_list(v);
+-              share->maps[KSMBD_SHARE_VALID_USERS_MAP] =
+-                      parse_list(share->maps[KSMBD_SHARE_VALID_USERS_MAP],
+-                                 users_list, '@');
+-              if (share->maps[KSMBD_SHARE_VALID_USERS_MAP] == NULL)
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              cp_group_kv_list_free(users_list);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_INVALID_USERS)) {
+-              char **users_list;
+-
+-              users_list = cp_get_group_kv_list(v);
+-              share->maps[KSMBD_SHARE_INVALID_USERS_MAP] =
+-                      parse_list(share->maps[KSMBD_SHARE_INVALID_USERS_MAP],
+-                                 users_list, '@');
+-              if (share->maps[KSMBD_SHARE_INVALID_USERS_MAP] == NULL)
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              cp_group_kv_list_free(users_list);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_READ_LIST)) {
+-              char **users_list;
+-
+-              users_list = cp_get_group_kv_list(v);
+-              share->maps[KSMBD_SHARE_READ_LIST_MAP] =
+-                      parse_list(share->maps[KSMBD_SHARE_READ_LIST_MAP],
+-                                 users_list, '@');
+-              if (share->maps[KSMBD_SHARE_READ_LIST_MAP] == NULL)
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              cp_group_kv_list_free(users_list);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_LIST)) {
+-              char **users_list;
+-
+-              users_list = cp_get_group_kv_list(v);
+-              share->maps[KSMBD_SHARE_WRITE_LIST_MAP] =
+-                      parse_list(share->maps[KSMBD_SHARE_WRITE_LIST_MAP],
+-                                 users_list, '@');
+-              if (share->maps[KSMBD_SHARE_WRITE_LIST_MAP] == NULL)
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              cp_group_kv_list_free(users_list);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_ADMIN_USERS)) {
+-              char **users_list;
+-
+-              users_list = cp_get_group_kv_list(v);
+-              share->maps[KSMBD_SHARE_ADMIN_USERS_MAP] =
+-                      parse_list(share->maps[KSMBD_SHARE_ADMIN_USERS_MAP],
+-                                 users_list, '@');
+-              if (share->maps[KSMBD_SHARE_ADMIN_USERS_MAP] == NULL)
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              cp_group_kv_list_free(users_list);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_ALLOW)) {
+-              char **hosts_list;
+-
+-              hosts_list = cp_get_group_kv_list(v);
+-              share->hosts_allow_map = parse_list(share->hosts_allow_map,
+-                                                  hosts_list, 0x00);
+-              if (share->hosts_allow_map == NULL)
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              cp_group_kv_list_free(hosts_list);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_DENY)) {
+-              char **hosts_list;
+-
+-              hosts_list = cp_get_group_kv_list(v);
+-              share->hosts_deny_map = parse_list(share->hosts_deny_map,
+-                                                 hosts_list, 0x00);
+-              if (share->hosts_deny_map == NULL)
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              cp_group_kv_list_free(hosts_list);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_MAX_CONNECTIONS)) {
+-              share->max_connections = cp_get_group_kv_long_base(v, 10);
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_VETO_FILES)) {
+-              share->veto_list = cp_get_group_kv_string(v + 1);
+-              if (share->veto_list == NULL) {
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+-              } else {
+-                      share->veto_list_sz = strlen(share->veto_list);
+-                      make_veto_list(share);
+-              }
+-              return;
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_INHERIT_OWNER)) {
+-              if (cp_get_group_kv_bool(v))
+-                      set_share_flag(share, KSMBD_SHARE_FLAG_INHERIT_OWNER);
+-              else
+-                      clear_share_flag(share, KSMBD_SHARE_FLAG_INHERIT_OWNER);
+-      }
+-
+-      if (shm_share_config(k, KSMBD_SHARE_CONF_VFS_OBJECTS)) {
+-              char *p;
+-              int i;
+-              char **objects = cp_get_group_kv_list(v);
+-
+-              if (objects) {
+-                      clear_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR);
+-                      clear_share_flag(share, KSMBD_SHARE_FLAG_STREAMS);
+-                      for (i = 0;  objects[i] != NULL; i++) {
+-                              p = cp_ltrim(objects[i]);
+-                              if (!p)
+-                                      continue;
+-                              if (!strcmp(p, "acl_xattr"))
+-                                      set_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR);
+-                              else if (!strcmp(p, "streams_xattr"))
+-                                      set_share_flag(share, KSMBD_SHARE_FLAG_STREAMS);
+-                      }
+-                      cp_group_kv_list_free(objects);
+-              }
+-      }
+-
+-}
+-
+-static void fixup_missing_fields(struct ksmbd_share *share)
+-{
+-      if (!share->comment)
+-              share->comment = strdup("");
+-}
+-
+-static void init_share_from_group(struct ksmbd_share *share,
+-                               struct smbconf_group *group)
+-{
+-      share->name = g_strdup(group->name);
+-      share->create_mask = KSMBD_SHARE_DEFAULT_CREATE_MASK;
+-      share->directory_mask = KSMBD_SHARE_DEFAULT_DIRECTORY_MASK;
+-      share->force_create_mode = 0;
+-      share->force_directory_mode = 0;
+-
+-      share->force_uid = KSMBD_SHARE_INVALID_UID;
+-      share->force_gid = KSMBD_SHARE_INVALID_GID;
+-
+-      set_share_flag(share, KSMBD_SHARE_FLAG_AVAILABLE);
+-      set_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
+-      set_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
+-      set_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
+-      set_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
+-      set_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
+-
+-      if (!g_ascii_strcasecmp(share->name, "ipc$"))
+-              set_share_flag(share, KSMBD_SHARE_FLAG_PIPE);
+-
+-      if (group->cb_mode == GROUPS_CALLBACK_REINIT)
+-              set_share_flag(share, KSMBD_SHARE_FLAG_UPDATE);
+-
+-      g_hash_table_foreach(group->kv, process_group_kv, share);
+-
+-      fixup_missing_fields(share);
+-}
+-
+-int shm_add_new_share(struct smbconf_group *group)
+-{
+-      int ret = 0;
+-      struct ksmbd_share *share = new_ksmbd_share();
+-
+-      if (!share)
+-              return -ENOMEM;
+-
+-      init_share_from_group(share, group);
+-      if (test_share_flag(share, KSMBD_SHARE_FLAG_INVALID)) {
+-              pr_err("Share `%s' is invalid\n", share->name);
+-              kill_ksmbd_share(share);
+-              return 0;
+-      }
+-
+-      g_rw_lock_writer_lock(&shares_table_lock);
+-      if (__shm_lookup_share(share->name)) {
+-              g_rw_lock_writer_unlock(&shares_table_lock);
+-              pr_info("Share `%s' already exists\n", share->name);
+-              kill_ksmbd_share(share);
+-              return 0;
+-      }
+-
+-      pr_debug("New share `%s'\n", share->name);
+-      if (!g_hash_table_insert(shares_table, share->name, share)) {
+-              kill_ksmbd_share(share);
+-              ret = -EINVAL;
+-      }
+-      g_rw_lock_writer_unlock(&shares_table_lock);
+-      return ret;
+-}
+-
+-int shm_lookup_users_map(struct ksmbd_share *share,
+-                        enum share_users map,
+-                        char *name)
+-{
+-      int ret = -ENOENT;
+-
+-      if (map >= KSMBD_SHARE_USERS_MAX) {
+-              pr_err("Invalid users map index: %d\n", map);
+-              return 0;
+-      }
+-
+-      if (!share->maps[map])
+-              return -EINVAL;
+-
+-      g_rw_lock_reader_lock(&share->maps_lock);
+-      if (g_hash_table_lookup(share->maps[map], name))
+-              ret = 0;
+-      g_rw_lock_reader_unlock(&share->maps_lock);
+-
+-      return ret;
+-}
+-
+-/*
+- * FIXME
+- * Do a real hosts lookup. IP masks, etc.
+- */
+-int shm_lookup_hosts_map(struct ksmbd_share *share,
+-                        enum share_hosts map,
+-                        char *host)
+-{
+-      GHashTable *lookup_map = NULL;
+-      int ret = -ENOENT;
+-
+-      if (map >= KSMBD_SHARE_HOSTS_MAX) {
+-              pr_err("Invalid hosts map index: %d\n", map);
+-              return 0;
+-      }
+-
+-      if (map == KSMBD_SHARE_HOSTS_ALLOW_MAP)
+-              lookup_map = share->hosts_allow_map;
+-      if (map == KSMBD_SHARE_HOSTS_DENY_MAP)
+-              lookup_map = share->hosts_deny_map;
+-
+-      if (!lookup_map)
+-              return -EINVAL;
+-
+-      g_rw_lock_reader_lock(&share->maps_lock);
+-      if (g_hash_table_lookup(lookup_map, host))
+-              ret = 0;
+-      g_rw_lock_reader_unlock(&share->maps_lock);
+-
+-      return ret;
+-}
+-
+-int shm_open_connection(struct ksmbd_share *share)
+-{
+-      int ret = 0;
+-
+-      g_rw_lock_writer_lock(&share->update_lock);
+-      share->num_connections++;
+-      if (share->max_connections) {
+-              if (share->num_connections >= share->max_connections)
+-                      ret = -EINVAL;
+-      }
+-      g_rw_lock_writer_unlock(&share->update_lock);
+-      return ret;
+-}
+-
+-int shm_close_connection(struct ksmbd_share *share)
+-{
+-      if (!share)
+-              return 0;
+-
+-      g_rw_lock_writer_lock(&share->update_lock);
+-      share->num_connections--;
+-      g_rw_lock_writer_unlock(&share->update_lock);
+-      return 0;
+-}
+-
+-void for_each_ksmbd_share(walk_shares cb, gpointer user_data)
+-{
+-      g_rw_lock_reader_lock(&shares_table_lock);
+-      g_hash_table_foreach(shares_table, cb, user_data);
+-      g_rw_lock_reader_unlock(&shares_table_lock);
+-}
+-
+-int shm_share_config_payload_size(struct ksmbd_share *share)
+-{
+-      int sz = 1;
+-
+-      if (share && !test_share_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
+-              if (share->path)
+-                      sz += strlen(share->path);
+-              if (global_conf.root_dir)
+-                      sz += strlen(global_conf.root_dir) + 1;
+-              if (share->veto_list_sz)
+-                      sz += share->veto_list_sz + 1;
+-      }
+-
+-      return sz;
+-}
+-
+-int shm_handle_share_config_request(struct ksmbd_share *share,
+-                                  struct ksmbd_share_config_response *resp)
+-{
+-      unsigned char *config_payload;
+-
+-      if (!share)
+-              return -EINVAL;
+-
+-      resp->flags = share->flags;
+-      resp->create_mask = share->create_mask;
+-      resp->directory_mask = share->directory_mask;
+-      resp->force_create_mode = share->force_create_mode;
+-      resp->force_directory_mode = share->force_directory_mode;
+-      resp->force_uid = share->force_uid;
+-      resp->force_gid = share->force_gid;
+-      *resp->share_name = 0x00;
+-      strncat(resp->share_name, share->name, KSMBD_REQ_MAX_SHARE_NAME - 1);
+-      resp->veto_list_sz = share->veto_list_sz;
+-
+-      if (test_share_flag(share, KSMBD_SHARE_FLAG_PIPE))
+-              return 0;
+-
+-      if (!share->path)
+-              return 0;
+-
+-      config_payload = KSMBD_SHARE_CONFIG_VETO_LIST(resp);
+-      if (resp->veto_list_sz) {
+-              memcpy(config_payload,
+-                     share->veto_list,
+-                     resp->veto_list_sz);
+-              config_payload += resp->veto_list_sz + 1;
+-      }
+-      if (global_conf.root_dir)
+-              sprintf(config_payload,
+-                      "%s%s",
+-                      global_conf.root_dir,
+-                      share->path);
+-      else
+-              sprintf(config_payload, "%s", share->path);
+-      return 0;
+-}
+--- /dev/null
++++ b/tools/management/share.c
+@@ -0,0 +1,868 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ *   linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <glib.h>
++#include <sys/types.h>
++#include <pwd.h>
++#include <grp.h>
++
++#include "config_parser.h"
++#include "linux/ksmbd_server.h"
++#include "management/share.h"
++#include "management/user.h"
++#include "tools.h"
++
++#define KSMBD_SHARE_STATE_FREEING     1
++
++/*
++ * WARNING:
++ *
++ * This must match KSMBD_SHARE_CONF enum 1:1.
++ * Add new entries ONLY to the bottom.
++ */
++char *KSMBD_SHARE_CONF[KSMBD_SHARE_CONF_MAX] = {
++      "comment",                              /* 0 */
++      "path",
++      "guest ok",
++      "guest account",
++      "read only",
++      "browseable",                           /* 5 */
++      "write ok",
++      "writeable",
++      "store dos attributes",
++      "oplocks",
++      "create mask",                          /* 10 */
++      "directory mask",
++      "force create mode",
++      "force directory mode",
++      "force group",
++      "force user",                           /* 15 */
++      "hide dot files",
++      "valid users",
++      "invalid users",
++      "read list",
++      "write list",                           /* 20 */
++      "admin users",
++      "hosts allow",
++      "hosts deny",
++      "max connections",
++      "veto files",                           /* 25 */
++      "inherit owner",
++      "follow symlinks",
++      "vfs objects",
++      "writable",
++};
++
++static GHashTable     *shares_table;
++static GRWLock                shares_table_lock;
++
++int shm_share_config(char *k, enum KSMBD_SHARE_CONF c)
++{
++      if (c >= KSMBD_SHARE_CONF_MAX)
++              return 0;
++
++      return !cp_key_cmp(k, KSMBD_SHARE_CONF[c]);
++}
++
++static void list_hosts_callback(gpointer k, gpointer v, gpointer user_data)
++{
++      free(k);
++      free(v);
++}
++
++static void free_hosts_map(GHashTable *map)
++{
++      if (map) {
++              g_hash_table_foreach(map, list_hosts_callback, NULL);
++              g_hash_table_destroy(map);
++      }
++}
++
++static void list_user_callback(gpointer k, gpointer u, gpointer user_data)
++{
++      put_ksmbd_user((struct ksmbd_user *)u);
++}
++
++static void free_user_map(GHashTable *map)
++{
++      if (map) {
++              g_hash_table_foreach(map, list_user_callback, NULL);
++              g_hash_table_destroy(map);
++      }
++}
++
++static void kill_ksmbd_share(struct ksmbd_share *share)
++{
++      int i;
++
++      pr_debug("Kill share `%s'\n", share->name);
++
++      for (i = 0; i < KSMBD_SHARE_USERS_MAX; i++)
++              free_user_map(share->maps[i]);
++
++      free_hosts_map(share->hosts_allow_map);
++      free_hosts_map(share->hosts_deny_map);
++
++      g_rw_lock_clear(&share->maps_lock);
++
++      free(share->name);
++      free(share->path);
++      free(share->comment);
++      free(share->veto_list);
++      free(share->guest_account);
++      g_rw_lock_clear(&share->update_lock);
++      g_free(share);
++}
++
++static int __shm_remove_share(struct ksmbd_share *share)
++{
++      int ret = 0;
++
++      if (share->state != KSMBD_SHARE_STATE_FREEING) {
++              g_rw_lock_writer_lock(&shares_table_lock);
++              if (!g_hash_table_remove(shares_table, share->name))
++                      ret = -EINVAL;
++              g_rw_lock_writer_unlock(&shares_table_lock);
++      }
++      if (!ret)
++              kill_ksmbd_share(share);
++      return ret;
++}
++
++struct ksmbd_share *get_ksmbd_share(struct ksmbd_share *share)
++{
++      g_rw_lock_writer_lock(&share->update_lock);
++      if (share->ref_count != 0) {
++              share->ref_count++;
++              g_rw_lock_writer_unlock(&share->update_lock);
++      } else {
++              g_rw_lock_writer_unlock(&share->update_lock);
++              share = NULL;
++      }
++
++      return share;
++}
++
++void put_ksmbd_share(struct ksmbd_share *share)
++{
++      int drop;
++
++      if (!share)
++              return;
++
++      g_rw_lock_writer_lock(&share->update_lock);
++      share->ref_count--;
++      drop = !share->ref_count;
++      g_rw_lock_writer_unlock(&share->update_lock);
++
++      if (!drop)
++              return;
++
++      __shm_remove_share(share);
++}
++
++static gboolean put_share_callback(gpointer _k, gpointer _v, gpointer data)
++{
++      struct ksmbd_share *share = (struct ksmbd_share *)_v;
++
++      share->state = KSMBD_SHARE_STATE_FREEING;
++      put_ksmbd_share(share);
++      return TRUE;
++}
++
++void shm_remove_all_shares(void)
++{
++      g_rw_lock_writer_lock(&shares_table_lock);
++      g_hash_table_foreach_remove(shares_table, put_share_callback, NULL);
++      g_rw_lock_writer_unlock(&shares_table_lock);
++}
++
++static struct ksmbd_share *new_ksmbd_share(void)
++{
++      struct ksmbd_share *share;
++      int i;
++
++      share = g_try_malloc0(sizeof(struct ksmbd_share));
++      if (!share)
++              return NULL;
++
++      share->ref_count = 1;
++      /*
++       * Create maps as needed. NULL maps means that share
++       * does not have a corresponding shmbconf entry.
++       */
++      for (i = 0; i < KSMBD_SHARE_USERS_MAX; i++)
++              share->maps[i] = NULL;
++
++      share->hosts_allow_map = NULL;
++      share->hosts_deny_map = NULL;
++      g_rw_lock_init(&share->maps_lock);
++      g_rw_lock_init(&share->update_lock);
++
++      return share;
++}
++
++static void free_hash_entry(gpointer k, gpointer s, gpointer user_data)
++{
++      kill_ksmbd_share(s);
++}
++
++static void shm_clear_shares(void)
++{
++      g_hash_table_foreach(shares_table, free_hash_entry, NULL);
++}
++
++void shm_destroy(void)
++{
++      if (shares_table) {
++              shm_clear_shares();
++              g_hash_table_destroy(shares_table);
++      }
++      g_rw_lock_clear(&shares_table_lock);
++}
++
++static char *shm_casefold_share_name(const char *name, size_t len)
++{
++      char *nfdi_name, *nfdicf_name;
++
++      nfdi_name = g_utf8_normalize(name, len, G_NORMALIZE_NFD);
++      if (!nfdi_name)
++              goto out_ascii;
++
++      nfdicf_name = g_utf8_casefold(nfdi_name, strlen(nfdi_name));
++      g_free(nfdi_name);
++      return nfdicf_name;
++out_ascii:
++      g_free(nfdi_name);
++      return g_ascii_strdown(name, len);
++}
++
++guint shm_share_name_hash(gconstpointer name)
++{
++      char *cf_name;
++      guint hash;
++
++      cf_name = shm_casefold_share_name(name, strlen(name));
++      hash = g_str_hash(cf_name);
++      g_free(cf_name);
++      return hash;
++}
++
++gboolean shm_share_name_equal(gconstpointer lname, gconstpointer rname)
++{
++      char *cf_lname, *cf_rname;
++      gboolean equal;
++
++      cf_lname = shm_casefold_share_name(lname, strlen(lname));
++      cf_rname = shm_casefold_share_name(rname, strlen(rname));
++      equal = g_str_equal(cf_lname, cf_rname);
++      g_free(cf_lname);
++      g_free(cf_rname);
++      return equal;
++}
++
++int shm_init(void)
++{
++      shares_table = g_hash_table_new(shm_share_name_hash,
++                                      shm_share_name_equal);
++      if (!shares_table)
++              return -ENOMEM;
++      g_rw_lock_init(&shares_table_lock);
++      return 0;
++}
++
++static struct ksmbd_share *__shm_lookup_share(char *name)
++{
++      return g_hash_table_lookup(shares_table, name);
++}
++
++struct ksmbd_share *shm_lookup_share(char *name)
++{
++      struct ksmbd_share *share, *ret;
++
++      g_rw_lock_reader_lock(&shares_table_lock);
++      share = __shm_lookup_share(name);
++      if (share) {
++              ret = get_ksmbd_share(share);
++              if (!ret)
++                      share = NULL;
++      }
++      g_rw_lock_reader_unlock(&shares_table_lock);
++      return share;
++}
++
++static GHashTable *parse_list(GHashTable *map, char **list, char grc)
++{
++      int i;
++
++      if (!list)
++              return map;
++
++      if (!map)
++              map = g_hash_table_new(g_str_hash, g_str_equal);
++      if (!map)
++              return map;
++
++      for (i = 0;  list[i] != NULL; i++) {
++              struct ksmbd_user *user;
++              char *p = list[i];
++
++              p = cp_ltrim(p);
++              if (!p)
++                      continue;
++
++              if (*p == grc) {
++                      struct group *gr;
++
++                      gr = getgrnam(p + 1);
++                      if (gr)
++                              parse_list(map, gr->gr_mem, 0x00);
++                      continue;
++              }
++
++              user = usm_lookup_user(p);
++              if (!user) {
++                      pr_info("Drop non-existing user `%s'\n", p);
++                      continue;
++              }
++
++              if (g_hash_table_lookup(map, user->name)) {
++                      pr_debug("User `%s' already exists in a map\n",
++                               user->name);
++                      continue;
++              }
++
++              g_hash_table_insert(map, user->name, user);
++      }
++
++      return map;
++}
++
++static void make_veto_list(struct ksmbd_share *share)
++{
++      int i;
++
++      for (i = 0; i < share->veto_list_sz; i++) {
++              if (share->veto_list[i] == '/')
++                      share->veto_list[i] = 0x00;
++      }
++}
++
++static void force_group(struct ksmbd_share *share, char *name)
++{
++      struct group *grp;
++
++      grp = getgrnam(name);
++      if (grp) {
++              share->force_gid = grp->gr_gid;
++              if (share->force_gid == KSMBD_SHARE_INVALID_GID)
++                      pr_err("Invalid force GID: %u\n", share->force_gid);
++      } else
++              pr_err("Unable to lookup up `/etc/group' entry: %s\n", name);
++}
++
++static void force_user(struct ksmbd_share *share, char *name)
++{
++      struct passwd *passwd;
++
++      passwd = getpwnam(name);
++      if (passwd) {
++              share->force_uid = passwd->pw_uid;
++              /*
++               * smb.conf 'force group' has higher priority than
++               * 'force user'.
++               */
++              if (share->force_gid == KSMBD_SHARE_INVALID_GID)
++                      share->force_gid = passwd->pw_gid;
++              if (share->force_uid == KSMBD_SHARE_INVALID_UID ||
++                              share->force_gid == KSMBD_SHARE_INVALID_GID)
++                      pr_err("Invalid force UID/GID: %u/%u\n",
++                                      share->force_uid, share->force_gid);
++      } else {
++              pr_err("Unable to lookup up `/etc/passwd' entry: %s\n", name);
++      }
++}
++
++static void process_group_kv(gpointer _k, gpointer _v, gpointer user_data)
++{
++      struct ksmbd_share *share = user_data;
++      char *k = _k;
++      char *v = _v;
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_COMMENT)) {
++              share->comment = cp_get_group_kv_string(v);
++              if (share->comment == NULL)
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_PATH)) {
++              share->path = cp_get_group_kv_string(v);
++              if (share->path == NULL)
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_OK)) {
++              if (cp_get_group_kv_bool(v))
++                      set_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_ACCOUNT)) {
++              struct ksmbd_user *user;
++
++              if (usm_add_new_user(cp_get_group_kv_string(_v),
++                                   g_strdup("NULL"))) {
++                      pr_err("Unable to add guest account\n");
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++                      return;
++              }
++
++              user = usm_lookup_user(_v);
++              if (user) {
++                      set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT);
++                      put_ksmbd_user(user);
++              }
++              share->guest_account = cp_get_group_kv_string(_v);
++              if (!share->guest_account)
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_READ_ONLY)) {
++              if (cp_get_group_kv_bool(v)) {
++                      set_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
++                      clear_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
++              } else {
++                      clear_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
++                      set_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
++              }
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_BROWSEABLE)) {
++              if (cp_get_group_kv_bool(v))
++                      set_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
++              else
++                      clear_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_OK) ||
++                      shm_share_config(k, KSMBD_SHARE_CONF_WRITEABLE) ||
++                      shm_share_config(k, KSMBD_SHARE_CONF_WRITABLE)) {
++              if (cp_get_group_kv_bool(v))
++                      set_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
++              else
++                      clear_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_STORE_DOS_ATTRIBUTES)) {
++              if (cp_get_group_kv_bool(v))
++                      set_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
++              else
++                      clear_share_flag(share,
++                                      KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_OPLOCKS)) {
++              if (cp_get_group_kv_bool(v))
++                      set_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
++              else
++                      clear_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_CREATE_MASK)) {
++              share->create_mask = cp_get_group_kv_long_base(v, 8);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_DIRECTORY_MASK)) {
++              share->directory_mask = cp_get_group_kv_long_base(v, 8);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_CREATE_MODE)) {
++              share->force_create_mode = cp_get_group_kv_long_base(v, 8);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_DIRECTORY_MODE)) {
++              share->force_directory_mode = cp_get_group_kv_long_base(v, 8);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_GROUP)) {
++              force_group(share, v);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_USER)) {
++              force_user(share, v);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_HIDE_DOT_FILES)) {
++              if (cp_get_group_kv_bool(v))
++                      set_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
++              else
++                      clear_share_flag(share,
++                              KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_VALID_USERS)) {
++              char **users_list;
++
++              users_list = cp_get_group_kv_list(v);
++              share->maps[KSMBD_SHARE_VALID_USERS_MAP] =
++                      parse_list(share->maps[KSMBD_SHARE_VALID_USERS_MAP],
++                                 users_list, '@');
++              if (share->maps[KSMBD_SHARE_VALID_USERS_MAP] == NULL)
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              cp_group_kv_list_free(users_list);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_INVALID_USERS)) {
++              char **users_list;
++
++              users_list = cp_get_group_kv_list(v);
++              share->maps[KSMBD_SHARE_INVALID_USERS_MAP] =
++                      parse_list(share->maps[KSMBD_SHARE_INVALID_USERS_MAP],
++                                 users_list, '@');
++              if (share->maps[KSMBD_SHARE_INVALID_USERS_MAP] == NULL)
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              cp_group_kv_list_free(users_list);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_READ_LIST)) {
++              char **users_list;
++
++              users_list = cp_get_group_kv_list(v);
++              share->maps[KSMBD_SHARE_READ_LIST_MAP] =
++                      parse_list(share->maps[KSMBD_SHARE_READ_LIST_MAP],
++                                 users_list, '@');
++              if (share->maps[KSMBD_SHARE_READ_LIST_MAP] == NULL)
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              cp_group_kv_list_free(users_list);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_LIST)) {
++              char **users_list;
++
++              users_list = cp_get_group_kv_list(v);
++              share->maps[KSMBD_SHARE_WRITE_LIST_MAP] =
++                      parse_list(share->maps[KSMBD_SHARE_WRITE_LIST_MAP],
++                                 users_list, '@');
++              if (share->maps[KSMBD_SHARE_WRITE_LIST_MAP] == NULL)
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              cp_group_kv_list_free(users_list);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_ADMIN_USERS)) {
++              char **users_list;
++
++              users_list = cp_get_group_kv_list(v);
++              share->maps[KSMBD_SHARE_ADMIN_USERS_MAP] =
++                      parse_list(share->maps[KSMBD_SHARE_ADMIN_USERS_MAP],
++                                 users_list, '@');
++              if (share->maps[KSMBD_SHARE_ADMIN_USERS_MAP] == NULL)
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              cp_group_kv_list_free(users_list);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_ALLOW)) {
++              char **hosts_list;
++
++              hosts_list = cp_get_group_kv_list(v);
++              share->hosts_allow_map = parse_list(share->hosts_allow_map,
++                                                  hosts_list, 0x00);
++              if (share->hosts_allow_map == NULL)
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              cp_group_kv_list_free(hosts_list);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_DENY)) {
++              char **hosts_list;
++
++              hosts_list = cp_get_group_kv_list(v);
++              share->hosts_deny_map = parse_list(share->hosts_deny_map,
++                                                 hosts_list, 0x00);
++              if (share->hosts_deny_map == NULL)
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              cp_group_kv_list_free(hosts_list);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_MAX_CONNECTIONS)) {
++              share->max_connections = cp_get_group_kv_long_base(v, 10);
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_VETO_FILES)) {
++              share->veto_list = cp_get_group_kv_string(v + 1);
++              if (share->veto_list == NULL) {
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++              } else {
++                      share->veto_list_sz = strlen(share->veto_list);
++                      make_veto_list(share);
++              }
++              return;
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_INHERIT_OWNER)) {
++              if (cp_get_group_kv_bool(v))
++                      set_share_flag(share, KSMBD_SHARE_FLAG_INHERIT_OWNER);
++              else
++                      clear_share_flag(share, KSMBD_SHARE_FLAG_INHERIT_OWNER);
++      }
++
++      if (shm_share_config(k, KSMBD_SHARE_CONF_VFS_OBJECTS)) {
++              char *p;
++              int i;
++              char **objects = cp_get_group_kv_list(v);
++
++              if (objects) {
++                      clear_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR);
++                      clear_share_flag(share, KSMBD_SHARE_FLAG_STREAMS);
++                      for (i = 0;  objects[i] != NULL; i++) {
++                              p = cp_ltrim(objects[i]);
++                              if (!p)
++                                      continue;
++                              if (!strcmp(p, "acl_xattr"))
++                                      set_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR);
++                              else if (!strcmp(p, "streams_xattr"))
++                                      set_share_flag(share, KSMBD_SHARE_FLAG_STREAMS);
++                      }
++                      cp_group_kv_list_free(objects);
++              }
++      }
++
++}
++
++static void fixup_missing_fields(struct ksmbd_share *share)
++{
++      if (!share->comment)
++              share->comment = strdup("");
++}
++
++static void init_share_from_group(struct ksmbd_share *share,
++                               struct smbconf_group *group)
++{
++      share->name = g_strdup(group->name);
++      share->create_mask = KSMBD_SHARE_DEFAULT_CREATE_MASK;
++      share->directory_mask = KSMBD_SHARE_DEFAULT_DIRECTORY_MASK;
++      share->force_create_mode = 0;
++      share->force_directory_mode = 0;
++
++      share->force_uid = KSMBD_SHARE_INVALID_UID;
++      share->force_gid = KSMBD_SHARE_INVALID_GID;
++
++      set_share_flag(share, KSMBD_SHARE_FLAG_AVAILABLE);
++      set_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
++      set_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
++      set_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
++      set_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
++      set_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
++
++      if (!g_ascii_strcasecmp(share->name, "ipc$"))
++              set_share_flag(share, KSMBD_SHARE_FLAG_PIPE);
++
++      if (group->cb_mode == GROUPS_CALLBACK_REINIT)
++              set_share_flag(share, KSMBD_SHARE_FLAG_UPDATE);
++
++      g_hash_table_foreach(group->kv, process_group_kv, share);
++
++      fixup_missing_fields(share);
++}
++
++int shm_add_new_share(struct smbconf_group *group)
++{
++      int ret = 0;
++      struct ksmbd_share *share = new_ksmbd_share();
++
++      if (!share)
++              return -ENOMEM;
++
++      init_share_from_group(share, group);
++      if (test_share_flag(share, KSMBD_SHARE_FLAG_INVALID)) {
++              pr_err("Share `%s' is invalid\n", share->name);
++              kill_ksmbd_share(share);
++              return 0;
++      }
++
++      g_rw_lock_writer_lock(&shares_table_lock);
++      if (__shm_lookup_share(share->name)) {
++              g_rw_lock_writer_unlock(&shares_table_lock);
++              pr_info("Share `%s' already exists\n", share->name);
++              kill_ksmbd_share(share);
++              return 0;
++      }
++
++      pr_debug("New share `%s'\n", share->name);
++      if (!g_hash_table_insert(shares_table, share->name, share)) {
++              kill_ksmbd_share(share);
++              ret = -EINVAL;
++      }
++      g_rw_lock_writer_unlock(&shares_table_lock);
++      return ret;
++}
++
++int shm_lookup_users_map(struct ksmbd_share *share,
++                        enum share_users map,
++                        char *name)
++{
++      int ret = -ENOENT;
++
++      if (map >= KSMBD_SHARE_USERS_MAX) {
++              pr_err("Invalid users map index: %d\n", map);
++              return 0;
++      }
++
++      if (!share->maps[map])
++              return -EINVAL;
++
++      g_rw_lock_reader_lock(&share->maps_lock);
++      if (g_hash_table_lookup(share->maps[map], name))
++              ret = 0;
++      g_rw_lock_reader_unlock(&share->maps_lock);
++
++      return ret;
++}
++
++/*
++ * FIXME
++ * Do a real hosts lookup. IP masks, etc.
++ */
++int shm_lookup_hosts_map(struct ksmbd_share *share,
++                        enum share_hosts map,
++                        char *host)
++{
++      GHashTable *lookup_map = NULL;
++      int ret = -ENOENT;
++
++      if (map >= KSMBD_SHARE_HOSTS_MAX) {
++              pr_err("Invalid hosts map index: %d\n", map);
++              return 0;
++      }
++
++      if (map == KSMBD_SHARE_HOSTS_ALLOW_MAP)
++              lookup_map = share->hosts_allow_map;
++      if (map == KSMBD_SHARE_HOSTS_DENY_MAP)
++              lookup_map = share->hosts_deny_map;
++
++      if (!lookup_map)
++              return -EINVAL;
++
++      g_rw_lock_reader_lock(&share->maps_lock);
++      if (g_hash_table_lookup(lookup_map, host))
++              ret = 0;
++      g_rw_lock_reader_unlock(&share->maps_lock);
++
++      return ret;
++}
++
++int shm_open_connection(struct ksmbd_share *share)
++{
++      int ret = 0;
++
++      g_rw_lock_writer_lock(&share->update_lock);
++      share->num_connections++;
++      if (share->max_connections) {
++              if (share->num_connections >= share->max_connections)
++                      ret = -EINVAL;
++      }
++      g_rw_lock_writer_unlock(&share->update_lock);
++      return ret;
++}
++
++int shm_close_connection(struct ksmbd_share *share)
++{
++      if (!share)
++              return 0;
++
++      g_rw_lock_writer_lock(&share->update_lock);
++      share->num_connections--;
++      g_rw_lock_writer_unlock(&share->update_lock);
++      return 0;
++}
++
++void for_each_ksmbd_share(walk_shares cb, gpointer user_data)
++{
++      g_rw_lock_reader_lock(&shares_table_lock);
++      g_hash_table_foreach(shares_table, cb, user_data);
++      g_rw_lock_reader_unlock(&shares_table_lock);
++}
++
++int shm_share_config_payload_size(struct ksmbd_share *share)
++{
++      int sz = 1;
++
++      if (share && !test_share_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
++              if (share->path)
++                      sz += strlen(share->path);
++              if (global_conf.root_dir)
++                      sz += strlen(global_conf.root_dir) + 1;
++              if (share->veto_list_sz)
++                      sz += share->veto_list_sz + 1;
++      }
++
++      return sz;
++}
++
++int shm_handle_share_config_request(struct ksmbd_share *share,
++                                  struct ksmbd_share_config_response *resp)
++{
++      unsigned char *config_payload;
++
++      if (!share)
++              return -EINVAL;
++
++      resp->flags = share->flags;
++      resp->create_mask = share->create_mask;
++      resp->directory_mask = share->directory_mask;
++      resp->force_create_mode = share->force_create_mode;
++      resp->force_directory_mode = share->force_directory_mode;
++      resp->force_uid = share->force_uid;
++      resp->force_gid = share->force_gid;
++      *resp->share_name = 0x00;
++      strncat(resp->share_name, share->name, KSMBD_REQ_MAX_SHARE_NAME - 1);
++      resp->veto_list_sz = share->veto_list_sz;
++
++      if (test_share_flag(share, KSMBD_SHARE_FLAG_PIPE))
++              return 0;
++
++      if (!share->path)
++              return 0;
++
++      config_payload = KSMBD_SHARE_CONFIG_VETO_LIST(resp);
++      if (resp->veto_list_sz) {
++              memcpy(config_payload,
++                     share->veto_list,
++                     resp->veto_list_sz);
++              config_payload += resp->veto_list_sz + 1;
++      }
++      if (global_conf.root_dir)
++              sprintf(config_payload,
++                      "%s%s",
++                      global_conf.root_dir,
++                      share->path);
++      else
++              sprintf(config_payload, "%s", share->path);
++      return 0;
++}
+--- a/lib/management/spnego.c
++++ /dev/null
+@@ -1,348 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- *   Copyright (C) 2020 LG Electronics
+- *
+- *   linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include "ksmbdtools.h"
+-
+-#ifndef _GNU_SOURCE
+-#define _GNU_SOURCE
+-#endif
+-
+-#include <stdlib.h>
+-#include <stdio.h>
+-#include <unistd.h>
+-#include <sys/types.h>
+-#include <fcntl.h>
+-#include <stdint.h>
+-#include <stdbool.h>
+-
+-#include <linux/ksmbd_server.h>
+-#include <management/spnego.h>
+-#include <asn1.h>
+-#include "spnego_mech.h"
+-
+-static struct spnego_mech_ctx mech_ctxs[SPNEGO_MAX_MECHS];
+-
+-static struct spnego_mech_ctx *get_mech(int mech_type)
+-{
+-      if (mech_type >= SPNEGO_MAX_MECHS)
+-              return NULL;
+-      return &mech_ctxs[mech_type];
+-}
+-
+-int spnego_init(void)
+-{
+-      struct spnego_mech_ctx *mech_ctx;
+-      int i;
+-
+-      mech_ctx = &mech_ctxs[SPNEGO_MECH_MSKRB5];
+-      mech_ctx->ops = &spnego_mskrb5_operations;
+-      if (global_conf.krb5_service_name)
+-              mech_ctx->params.krb5.service_name =
+-                      strdup(global_conf.krb5_service_name);
+-      if (global_conf.krb5_keytab_file)
+-              mech_ctx->params.krb5.keytab_name =
+-                      strdup(global_conf.krb5_keytab_file);
+-
+-      mech_ctx = &mech_ctxs[SPNEGO_MECH_KRB5];
+-      mech_ctx->ops = &spnego_krb5_operations;
+-      if (global_conf.krb5_service_name)
+-              mech_ctx->params.krb5.service_name =
+-                      strdup(global_conf.krb5_service_name);
+-      if (global_conf.krb5_keytab_file)
+-              mech_ctx->params.krb5.keytab_name =
+-                      strdup(global_conf.krb5_keytab_file);
+-
+-      for (i = 0; i < SPNEGO_MAX_MECHS; i++) {
+-              if (mech_ctxs[i].ops->setup &&
+-                              mech_ctxs[i].ops->setup(&mech_ctxs[i])) {
+-                      pr_err("Failed to init Kerberos 5\n");
+-                      goto out_err;
+-              }
+-      }
+-
+-      return 0;
+-out_err:
+-      for (; i >= 0; i--) {
+-              if (mech_ctxs[i].ops->cleanup)
+-                      mech_ctxs[i].ops->cleanup(&mech_ctxs[i]);
+-      }
+-      return -ENOTSUP;
+-}
+-
+-void spnego_destroy(void)
+-{
+-      int i;
+-
+-      for (i = 0; i < SPNEGO_MAX_MECHS; i++) {
+-              if (mech_ctxs[i].ops && mech_ctxs[i].ops->cleanup)
+-                      mech_ctxs[i].ops->cleanup(&mech_ctxs[i]);
+-      }
+-}
+-
+-static int compare_oid(unsigned long *oid1, unsigned int oid1len,
+-                  unsigned long *oid2, unsigned int oid2len)
+-{
+-      unsigned int i;
+-
+-      if (oid1len != oid2len)
+-              return 1;
+-
+-      for (i = 0; i < oid1len; i++) {
+-              if (oid1[i] != oid2[i])
+-                      return 1;
+-      }
+-      return 0;
+-}
+-
+-static bool is_supported_mech(unsigned long *oid, unsigned int len,
+-                      int *mech_type)
+-{
+-      if (!compare_oid(oid, len, MSKRB5_OID, ARRAY_SIZE(MSKRB5_OID))) {
+-              *mech_type = SPNEGO_MECH_MSKRB5;
+-              return true;
+-      }
+-
+-      if (!compare_oid(oid, len, KRB5_OID, ARRAY_SIZE(KRB5_OID))) {
+-              *mech_type = SPNEGO_MECH_KRB5;
+-              return true;
+-      }
+-
+-      *mech_type = SPNEGO_MAX_MECHS;
+-      return false;
+-}
+-
+-static int decode_asn1_header(struct asn1_ctx *ctx, unsigned char **end,
+-              unsigned int cls, unsigned int con, unsigned int tag)
+-{
+-      unsigned int d_cls, d_con, d_tag;
+-
+-      if (asn1_header_decode(ctx, end, &d_cls, &d_con, &d_tag) == 0 ||
+-              (d_cls != cls || d_con != con || d_tag != tag))
+-              return -EINVAL;
+-      return 0;
+-}
+-
+-static int decode_negTokenInit(unsigned char *negToken, int token_len,
+-                      int *mech_type, unsigned char **krb5_ap_req,
+-                      unsigned int *req_len)
+-{
+-      struct asn1_ctx ctx;
+-      unsigned char *end, *mech_types_end, *id;
+-      unsigned long *oid = NULL;
+-      unsigned int len;
+-
+-      asn1_open(&ctx, negToken, token_len);
+-
+-      /* GSSAPI header */
+-      if (decode_asn1_header(&ctx, &end, ASN1_APL, ASN1_CON, ASN1_EOC)) {
+-              pr_debug("Error decoding SPNEGO application tag\n");
+-              return -EINVAL;
+-      }
+-
+-      /* SPNEGO oid */
+-      if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI) ||
+-                      asn1_oid_decode(&ctx, end, &oid, &len) == 0 ||
+-                      compare_oid(oid, len, SPNEGO_OID, SPNEGO_OID_LEN)) {
+-              pr_debug("Error decoding SPNEGO OID\n");
+-              g_free(oid);
+-              return -EINVAL;
+-      }
+-      g_free(oid);
+-
+-      /* negoTokenInit */
+-      if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 0) ||
+-                      decode_asn1_header(&ctx, &end,
+-                              ASN1_UNI, ASN1_CON, ASN1_SEQ)) {
+-              pr_debug("Error decoding negTokenInit tag\n");
+-              return -EINVAL;
+-      }
+-
+-      /* mechTypes */
+-      if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 0) ||
+-                      decode_asn1_header(&ctx, &end,
+-                              ASN1_UNI, ASN1_CON, ASN1_SEQ)) {
+-              pr_debug("Error decoding mechTypes tag\n");
+-              return -EINVAL;
+-      }
+-
+-      mech_types_end = end;
+-      if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI) ||
+-                      asn1_oid_decode(&ctx, end, &oid, &len) == 0) {
+-              pr_debug("Error decoding Kerberos 5 OIDs\n");
+-              return -EINVAL;
+-      }
+-
+-      if (!is_supported_mech(oid, len, mech_type)) {
+-              g_free(oid);
+-              pr_debug("Not a supported mechanism\n");
+-              return -EINVAL;
+-      }
+-      g_free(oid);
+-
+-      ctx.pointer = mech_types_end;
+-      /* mechToken */
+-      if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 2) ||
+-                      decode_asn1_header(&ctx, &end,
+-                              ASN1_UNI, ASN1_PRI, ASN1_OTS)) {
+-              pr_debug("Error decoding krb5_blob\n");
+-              return -EINVAL;
+-      }
+-
+-      if (decode_asn1_header(&ctx, &end, ASN1_APL, ASN1_CON, ASN1_EOC)) {
+-              pr_debug("Error decoding GSSAPI application tag\n");
+-              return -EINVAL;
+-      }
+-
+-      /* Kerberos 5 oid */
+-      if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI)) {
+-              pr_debug("Error decoding Kerberos 5 OID tag\n");
+-              return -EINVAL;
+-      }
+-
+-      if (asn1_oid_decode(&ctx, end, &oid, &len) == 0 ||
+-                      compare_oid(oid, len, KRB5_OID,
+-                              ARRAY_SIZE(KRB5_OID))) {
+-              pr_debug("Not a Kerberos 5 OID\n");
+-              g_free(oid);
+-              return -EINVAL;
+-      }
+-      g_free(oid);
+-
+-      /* AP_REQ id */
+-      if (asn1_read(&ctx, &id, 2) == 0 || id[0] != 1 || id[1] != 0) {
+-              if (id)
+-                      free(id);
+-              pr_debug("Error decoding AP_REQ ID\n");
+-              return -EINVAL;
+-      }
+-      free(id);
+-
+-      /* AP_REQ */
+-      *req_len = (unsigned int)(ctx.end - ctx.pointer);
+-      *krb5_ap_req = ctx.pointer;
+-      return 0;
+-}
+-
+-static int encode_negTokenTarg(char *in_blob, int in_len,
+-                      const unsigned long *oid, int oid_len,
+-                      char **out_blob, int *out_len)
+-{
+-      unsigned char *buf;
+-      unsigned char *sup_oid, *krb5_oid;
+-      int sup_oid_len, krb5_oid_len;
+-      unsigned int neg_result_len, sup_mech_len, rep_token_len, len;
+-
+-      if (asn1_oid_encode(oid, oid_len, &sup_oid, &sup_oid_len))
+-              return -ENOMEM;
+-      if (asn1_oid_encode(KRB5_OID, ARRAY_SIZE(KRB5_OID),
+-                      &krb5_oid, &krb5_oid_len)) {
+-              g_free(sup_oid);
+-              return -ENOMEM;
+-      }
+-
+-      neg_result_len = asn1_header_len(1, 2);
+-      sup_mech_len = asn1_header_len(sup_oid_len, 2);
+-      rep_token_len = asn1_header_len(krb5_oid_len, 1);
+-      rep_token_len += 2 + in_len;
+-      rep_token_len = asn1_header_len(rep_token_len, 3);
+-
+-      *out_len = asn1_header_len(
+-                      neg_result_len + sup_mech_len + rep_token_len, 2);
+-      *out_blob = g_try_malloc0(*out_len);
+-      if (*out_blob == NULL)
+-              return -ENOMEM;
+-      buf = *out_blob;
+-
+-      /* negTokenTarg */
+-      len = *out_len;
+-      asn1_header_encode(&buf,
+-                      ASN1_CTX, ASN1_CON, 1,
+-                      &len);
+-      asn1_header_encode(&buf,
+-                      ASN1_UNI, ASN1_CON, ASN1_SEQ,
+-                      &len);
+-
+-      /* negTokenTarg/negResult */
+-      len = neg_result_len;
+-      asn1_header_encode(&buf,
+-                      ASN1_CTX, ASN1_CON, 0,
+-                      &len);
+-      asn1_header_encode(&buf,
+-                      ASN1_UNI, ASN1_PRI, ASN1_ENUM,
+-                      &len);
+-      *buf++ = 0;
+-
+-      /* negTokenTarg/supportedMechType */
+-      len = sup_mech_len;
+-      asn1_header_encode(&buf,
+-                      ASN1_CTX, ASN1_CON, 1,
+-                      &len);
+-      asn1_header_encode(&buf,
+-                      ASN1_UNI, ASN1_PRI, ASN1_OJI,
+-                      &len);
+-      memcpy(buf, sup_oid, sup_oid_len);
+-      buf += len;
+-
+-      /* negTokenTarg/responseToken */
+-      len = rep_token_len;
+-      asn1_header_encode(&buf,
+-                      ASN1_CTX, ASN1_CON, 2,
+-                      &len);
+-      asn1_header_encode(&buf,
+-                      ASN1_UNI, ASN1_PRI, ASN1_OTS,
+-                      &len);
+-      asn1_header_encode(&buf,
+-                      ASN1_APL, ASN1_CON, 0,
+-                      &len);
+-      /* negTokenTarg/responseToken/OID */
+-      len = asn1_header_len(krb5_oid_len, 1);
+-      asn1_header_encode(&buf,
+-                      ASN1_UNI, ASN1_PRI, ASN1_OJI,
+-                      &len);
+-      /* negTokenTarg/responseToken/mechToken*/
+-      memcpy(buf, krb5_oid, krb5_oid_len);
+-      buf += len;
+-      /* AP_REP id */
+-      *buf++ = 2;
+-      *buf++ = 0;
+-      memcpy(buf, in_blob, in_len);
+-
+-      g_free(sup_oid);
+-      g_free(krb5_oid);
+-}
+-
+-int spnego_handle_authen_request(struct ksmbd_spnego_authen_request *req,
+-                      struct ksmbd_spnego_auth_out *auth_out)
+-{
+-      struct spnego_mech_ctx *mech_ctx;
+-      unsigned char *mech_token;
+-      int token_len, mech_type;
+-      int retval = 0;
+-
+-      if (decode_negTokenInit(req->spnego_blob, (int)req->spnego_blob_len,
+-                              &mech_type, &mech_token, &token_len)) {
+-              pr_info("Error decoding negTokenInit\n");
+-              return -EINVAL;
+-      }
+-
+-      mech_ctx = get_mech(mech_type);
+-      if (!mech_ctx) {
+-              retval = -ENOTSUP;
+-              pr_info("No support for Kerberos 5\n");
+-              goto out;
+-      }
+-
+-      if (mech_ctx->ops->handle_authen(mech_ctx,
+-                              mech_token, token_len,
+-                              auth_out, encode_negTokenTarg)) {
+-              retval = -EPERM;
+-              pr_info("Error authenticating\n");
+-              goto out;
+-      }
+-out:
+-      return retval;
+-}
+--- /dev/null
++++ b/tools/management/spnego.c
+@@ -0,0 +1,348 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ *   Copyright (C) 2020 LG Electronics
++ *
++ *   linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include "tools.h"
++
++#ifndef _GNU_SOURCE
++#define _GNU_SOURCE
++#endif
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <fcntl.h>
++#include <stdint.h>
++#include <stdbool.h>
++
++#include <linux/ksmbd_server.h>
++#include <management/spnego.h>
++#include <asn1.h>
++#include "spnego_mech.h"
++
++static struct spnego_mech_ctx mech_ctxs[SPNEGO_MAX_MECHS];
++
++static struct spnego_mech_ctx *get_mech(int mech_type)
++{
++      if (mech_type >= SPNEGO_MAX_MECHS)
++              return NULL;
++      return &mech_ctxs[mech_type];
++}
++
++int spnego_init(void)
++{
++      struct spnego_mech_ctx *mech_ctx;
++      int i;
++
++      mech_ctx = &mech_ctxs[SPNEGO_MECH_MSKRB5];
++      mech_ctx->ops = &spnego_mskrb5_operations;
++      if (global_conf.krb5_service_name)
++              mech_ctx->params.krb5.service_name =
++                      strdup(global_conf.krb5_service_name);
++      if (global_conf.krb5_keytab_file)
++              mech_ctx->params.krb5.keytab_name =
++                      strdup(global_conf.krb5_keytab_file);
++
++      mech_ctx = &mech_ctxs[SPNEGO_MECH_KRB5];
++      mech_ctx->ops = &spnego_krb5_operations;
++      if (global_conf.krb5_service_name)
++              mech_ctx->params.krb5.service_name =
++                      strdup(global_conf.krb5_service_name);
++      if (global_conf.krb5_keytab_file)
++              mech_ctx->params.krb5.keytab_name =
++                      strdup(global_conf.krb5_keytab_file);
++
++      for (i = 0; i < SPNEGO_MAX_MECHS; i++) {
++              if (mech_ctxs[i].ops->setup &&
++                              mech_ctxs[i].ops->setup(&mech_ctxs[i])) {
++                      pr_err("Failed to init Kerberos 5\n");
++                      goto out_err;
++              }
++      }
++
++      return 0;
++out_err:
++      for (; i >= 0; i--) {
++              if (mech_ctxs[i].ops->cleanup)
++                      mech_ctxs[i].ops->cleanup(&mech_ctxs[i]);
++      }
++      return -ENOTSUP;
++}
++
++void spnego_destroy(void)
++{
++      int i;
++
++      for (i = 0; i < SPNEGO_MAX_MECHS; i++) {
++              if (mech_ctxs[i].ops && mech_ctxs[i].ops->cleanup)
++                      mech_ctxs[i].ops->cleanup(&mech_ctxs[i]);
++      }
++}
++
++static int compare_oid(unsigned long *oid1, unsigned int oid1len,
++                  unsigned long *oid2, unsigned int oid2len)
++{
++      unsigned int i;
++
++      if (oid1len != oid2len)
++              return 1;
++
++      for (i = 0; i < oid1len; i++) {
++              if (oid1[i] != oid2[i])
++                      return 1;
++      }
++      return 0;
++}
++
++static bool is_supported_mech(unsigned long *oid, unsigned int len,
++                      int *mech_type)
++{
++      if (!compare_oid(oid, len, MSKRB5_OID, ARRAY_SIZE(MSKRB5_OID))) {
++              *mech_type = SPNEGO_MECH_MSKRB5;
++              return true;
++      }
++
++      if (!compare_oid(oid, len, KRB5_OID, ARRAY_SIZE(KRB5_OID))) {
++              *mech_type = SPNEGO_MECH_KRB5;
++              return true;
++      }
++
++      *mech_type = SPNEGO_MAX_MECHS;
++      return false;
++}
++
++static int decode_asn1_header(struct asn1_ctx *ctx, unsigned char **end,
++              unsigned int cls, unsigned int con, unsigned int tag)
++{
++      unsigned int d_cls, d_con, d_tag;
++
++      if (asn1_header_decode(ctx, end, &d_cls, &d_con, &d_tag) == 0 ||
++              (d_cls != cls || d_con != con || d_tag != tag))
++              return -EINVAL;
++      return 0;
++}
++
++static int decode_negTokenInit(unsigned char *negToken, int token_len,
++                      int *mech_type, unsigned char **krb5_ap_req,
++                      unsigned int *req_len)
++{
++      struct asn1_ctx ctx;
++      unsigned char *end, *mech_types_end, *id;
++      unsigned long *oid = NULL;
++      unsigned int len;
++
++      asn1_open(&ctx, negToken, token_len);
++
++      /* GSSAPI header */
++      if (decode_asn1_header(&ctx, &end, ASN1_APL, ASN1_CON, ASN1_EOC)) {
++              pr_debug("Error decoding SPNEGO application tag\n");
++              return -EINVAL;
++      }
++
++      /* SPNEGO oid */
++      if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI) ||
++                      asn1_oid_decode(&ctx, end, &oid, &len) == 0 ||
++                      compare_oid(oid, len, SPNEGO_OID, SPNEGO_OID_LEN)) {
++              pr_debug("Error decoding SPNEGO OID\n");
++              g_free(oid);
++              return -EINVAL;
++      }
++      g_free(oid);
++
++      /* negoTokenInit */
++      if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 0) ||
++                      decode_asn1_header(&ctx, &end,
++                              ASN1_UNI, ASN1_CON, ASN1_SEQ)) {
++              pr_debug("Error decoding negTokenInit tag\n");
++              return -EINVAL;
++      }
++
++      /* mechTypes */
++      if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 0) ||
++                      decode_asn1_header(&ctx, &end,
++                              ASN1_UNI, ASN1_CON, ASN1_SEQ)) {
++              pr_debug("Error decoding mechTypes tag\n");
++              return -EINVAL;
++      }
++
++      mech_types_end = end;
++      if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI) ||
++                      asn1_oid_decode(&ctx, end, &oid, &len) == 0) {
++              pr_debug("Error decoding Kerberos 5 OIDs\n");
++              return -EINVAL;
++      }
++
++      if (!is_supported_mech(oid, len, mech_type)) {
++              g_free(oid);
++              pr_debug("Not a supported mechanism\n");
++              return -EINVAL;
++      }
++      g_free(oid);
++
++      ctx.pointer = mech_types_end;
++      /* mechToken */
++      if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 2) ||
++                      decode_asn1_header(&ctx, &end,
++                              ASN1_UNI, ASN1_PRI, ASN1_OTS)) {
++              pr_debug("Error decoding krb5_blob\n");
++              return -EINVAL;
++      }
++
++      if (decode_asn1_header(&ctx, &end, ASN1_APL, ASN1_CON, ASN1_EOC)) {
++              pr_debug("Error decoding GSSAPI application tag\n");
++              return -EINVAL;
++      }
++
++      /* Kerberos 5 oid */
++      if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI)) {
++              pr_debug("Error decoding Kerberos 5 OID tag\n");
++              return -EINVAL;
++      }
++
++      if (asn1_oid_decode(&ctx, end, &oid, &len) == 0 ||
++                      compare_oid(oid, len, KRB5_OID,
++                              ARRAY_SIZE(KRB5_OID))) {
++              pr_debug("Not a Kerberos 5 OID\n");
++              g_free(oid);
++              return -EINVAL;
++      }
++      g_free(oid);
++
++      /* AP_REQ id */
++      if (asn1_read(&ctx, &id, 2) == 0 || id[0] != 1 || id[1] != 0) {
++              if (id)
++                      free(id);
++              pr_debug("Error decoding AP_REQ ID\n");
++              return -EINVAL;
++      }
++      free(id);
++
++      /* AP_REQ */
++      *req_len = (unsigned int)(ctx.end - ctx.pointer);
++      *krb5_ap_req = ctx.pointer;
++      return 0;
++}
++
++static int encode_negTokenTarg(char *in_blob, int in_len,
++                      const unsigned long *oid, int oid_len,
++                      char **out_blob, int *out_len)
++{
++      unsigned char *buf;
++      unsigned char *sup_oid, *krb5_oid;
++      int sup_oid_len, krb5_oid_len;
++      unsigned int neg_result_len, sup_mech_len, rep_token_len, len;
++
++      if (asn1_oid_encode(oid, oid_len, &sup_oid, &sup_oid_len))
++              return -ENOMEM;
++      if (asn1_oid_encode(KRB5_OID, ARRAY_SIZE(KRB5_OID),
++                      &krb5_oid, &krb5_oid_len)) {
++              g_free(sup_oid);
++              return -ENOMEM;
++      }
++
++      neg_result_len = asn1_header_len(1, 2);
++      sup_mech_len = asn1_header_len(sup_oid_len, 2);
++      rep_token_len = asn1_header_len(krb5_oid_len, 1);
++      rep_token_len += 2 + in_len;
++      rep_token_len = asn1_header_len(rep_token_len, 3);
++
++      *out_len = asn1_header_len(
++                      neg_result_len + sup_mech_len + rep_token_len, 2);
++      *out_blob = g_try_malloc0(*out_len);
++      if (*out_blob == NULL)
++              return -ENOMEM;
++      buf = *out_blob;
++
++      /* negTokenTarg */
++      len = *out_len;
++      asn1_header_encode(&buf,
++                      ASN1_CTX, ASN1_CON, 1,
++                      &len);
++      asn1_header_encode(&buf,
++                      ASN1_UNI, ASN1_CON, ASN1_SEQ,
++                      &len);
++
++      /* negTokenTarg/negResult */
++      len = neg_result_len;
++      asn1_header_encode(&buf,
++                      ASN1_CTX, ASN1_CON, 0,
++                      &len);
++      asn1_header_encode(&buf,
++                      ASN1_UNI, ASN1_PRI, ASN1_ENUM,
++                      &len);
++      *buf++ = 0;
++
++      /* negTokenTarg/supportedMechType */
++      len = sup_mech_len;
++      asn1_header_encode(&buf,
++                      ASN1_CTX, ASN1_CON, 1,
++                      &len);
++      asn1_header_encode(&buf,
++                      ASN1_UNI, ASN1_PRI, ASN1_OJI,
++                      &len);
++      memcpy(buf, sup_oid, sup_oid_len);
++      buf += len;
++
++      /* negTokenTarg/responseToken */
++      len = rep_token_len;
++      asn1_header_encode(&buf,
++                      ASN1_CTX, ASN1_CON, 2,
++                      &len);
++      asn1_header_encode(&buf,
++                      ASN1_UNI, ASN1_PRI, ASN1_OTS,
++                      &len);
++      asn1_header_encode(&buf,
++                      ASN1_APL, ASN1_CON, 0,
++                      &len);
++      /* negTokenTarg/responseToken/OID */
++      len = asn1_header_len(krb5_oid_len, 1);
++      asn1_header_encode(&buf,
++                      ASN1_UNI, ASN1_PRI, ASN1_OJI,
++                      &len);
++      /* negTokenTarg/responseToken/mechToken*/
++      memcpy(buf, krb5_oid, krb5_oid_len);
++      buf += len;
++      /* AP_REP id */
++      *buf++ = 2;
++      *buf++ = 0;
++      memcpy(buf, in_blob, in_len);
++
++      g_free(sup_oid);
++      g_free(krb5_oid);
++}
++
++int spnego_handle_authen_request(struct ksmbd_spnego_authen_request *req,
++                      struct ksmbd_spnego_auth_out *auth_out)
++{
++      struct spnego_mech_ctx *mech_ctx;
++      unsigned char *mech_token;
++      int token_len, mech_type;
++      int retval = 0;
++
++      if (decode_negTokenInit(req->spnego_blob, (int)req->spnego_blob_len,
++                              &mech_type, &mech_token, &token_len)) {
++              pr_info("Error decoding negTokenInit\n");
++              return -EINVAL;
++      }
++
++      mech_ctx = get_mech(mech_type);
++      if (!mech_ctx) {
++              retval = -ENOTSUP;
++              pr_info("No support for Kerberos 5\n");
++              goto out;
++      }
++
++      if (mech_ctx->ops->handle_authen(mech_ctx,
++                              mech_token, token_len,
++                              auth_out, encode_negTokenTarg)) {
++              retval = -EPERM;
++              pr_info("Error authenticating\n");
++              goto out;
++      }
++out:
++      return retval;
++}
+--- a/lib/management/spnego_krb5.c
++++ /dev/null
+@@ -1,408 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- *   Copyright (C) 2020 LG Electronics
+- *
+- *   linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include "ksmbdtools.h"
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <unistd.h>
+-#include <sys/types.h>
+-#include <sys/socket.h>
+-#include <netdb.h>
+-#include <krb5.h>
+-
+-#include <management/spnego.h>
+-#include <asn1.h>
+-#include "spnego_mech.h"
+-
+-#ifndef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY
+-krb5_error_code krb5_auth_con_getrecvsubkey(krb5_context context,
+-                      krb5_auth_context auth_context, krb5_keyblock **keyblock)
+-{
+-      return krb5_auth_con_getremotesubkey(context, auth_context, keyblock);
+-}
+-#endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
+-
+-#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
+-#define KRB5_KEY_TYPE(k)      ((k)->keytype)
+-#define KRB5_KEY_LENGTH(k)    ((k)->keyvalue.length)
+-#define KRB5_KEY_DATA(k)      ((k)->keyvalue.data)
+-#else
+-#define KRB5_KEY_TYPE(k)      ((k)->enctype)
+-#define KRB5_KEY_LENGTH(k)    ((k)->length)
+-#define KRB5_KEY_DATA(k)      ((k)->contents)
+-#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
+-
+-struct spnego_krb5_ctx {
+-      krb5_context    context;
+-      krb5_keytab     keytab;
+-      krb5_creds      creds;
+-};
+-
+-#define SERVICE_NAME  "cifs"
+-
+-#define pr_krb5_err(_context, _retval, _fmt, ...)             \
+-      do {                                                    \
+-              const char *msg = krb5_get_error_message(_context, _retval);    \
+-              pr_err("%s: " _fmt, msg, ##__VA_ARGS__);        \
+-              krb5_free_error_message(_context, msg);         \
+-      } while (0)
+-
+-static char *get_service_name(void)
+-{
+-      return strdup(SERVICE_NAME);
+-}
+-
+-static char *get_host_name(void)
+-{
+-      struct addrinfo hint, *ai;
+-      char *host_name;
+-      char hostname[NI_MAXHOST];
+-
+-      if (gethostname(hostname, sizeof(hostname)))
+-              return NULL;
+-
+-      memset(&hint, 0, sizeof(hint));
+-      hint.ai_family = AF_UNSPEC;
+-      hint.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+-      if (getaddrinfo(hostname, NULL, &hint, &ai))
+-              return NULL;
+-
+-      host_name = strdup(ai->ai_canonname);
+-      freeaddrinfo(ai);
+-      return host_name;
+-}
+-
+-/* Service full name is <service name>[/<host FQDN>[@REALM>]] */
+-static int parse_service_full_name(char *service_full_name,
+-                      char **service_name,
+-                      char **host_name)
+-{
+-      char *name, *delim;
+-
+-      *service_name = NULL;
+-      *host_name = NULL;
+-
+-      if (!service_full_name) {
+-              *service_name = get_service_name();
+-              *host_name = get_host_name();
+-              goto out;
+-      }
+-
+-      name = service_full_name;
+-      delim = strchr(name, '/');
+-      if (!delim) {
+-              *service_name = strdup(name);
+-              *host_name = get_host_name();
+-              goto out;
+-      }
+-      *service_name = strndup(name, delim - name);
+-      if (*service_name == NULL)
+-              return -ENOMEM;
+-
+-      name = delim + 1;
+-      delim = strchr(name, '@');
+-      if (!delim) {
+-              *host_name = strdup(name);
+-              goto out;
+-      }
+-      *host_name = strndup(name, delim - name);
+-      if (*host_name == NULL) {
+-              free(*service_name);
+-              *service_name = NULL;
+-              return -ENOMEM;
+-      }
+-out:
+-      /* we assume the host name is FQDN if it has "." */
+-      if (*host_name && strchr(*host_name, '.'))
+-              return 0;
+-
+-      free(*service_name);
+-      free(*host_name);
+-      *service_name = NULL;
+-      *host_name = NULL;
+-      return -EINVAL;
+-}
+-
+-static krb5_error_code acquire_creds_from_keytab(krb5_context context,
+-              char *service_full_name, char *keytab_name,
+-              krb5_creds *out_creds, krb5_keytab *keytab)
+-{
+-      krb5_error_code retval;
+-      krb5_principal sprinc = NULL;
+-      char *host_name = NULL, *service_name = NULL;
+-
+-      if (keytab_name)
+-              retval = krb5_kt_resolve(context, keytab_name, keytab);
+-      else
+-              retval = krb5_kt_default(context, keytab);
+-      if (retval) {
+-              pr_krb5_err(context, retval, "while resolving keytab\n");
+-              return retval;
+-      }
+-
+-      if (parse_service_full_name(service_full_name,
+-                              &service_name, &host_name)) {
+-              retval = KRB5_ERR_HOST_REALM_UNKNOWN;
+-              pr_krb5_err(context, retval, "while getting host name\n");
+-              goto out_err;
+-      }
+-
+-      retval = krb5_sname_to_principal(context, host_name, service_name,
+-                      KRB5_NT_UNKNOWN, &sprinc);
+-      if (retval) {
+-              pr_krb5_err(context, retval, "while generating service name\n");
+-              goto out_err;
+-      }
+-
+-      retval = krb5_get_init_creds_keytab(context, out_creds, sprinc,
+-                      *keytab, 0, NULL, NULL);
+-      if (retval) {
+-              char *name;
+-
+-              krb5_unparse_name(context, sprinc, &name);
+-              pr_krb5_err(context, retval,
+-                      "while getting credentials for %s\n", name);
+-              krb5_free_unparsed_name(context, name);
+-              goto out_err;
+-      }
+-
+-      free(host_name);
+-      free(service_name);
+-      return 0;
+-out_err:
+-      if (sprinc)
+-              krb5_free_principal(context, sprinc);
+-      if (service_name)
+-              free(service_name);
+-      if (host_name)
+-              free(host_name);
+-      if (*keytab)
+-              krb5_kt_close(context, *keytab);
+-      return retval;
+-}
+-
+-static int handle_krb5_authen(struct spnego_mech_ctx *mech_ctx,
+-                      char *in_blob, unsigned int in_len,
+-                      struct ksmbd_spnego_auth_out *auth_out,
+-                      spnego_encode_t spnego_encode)
+-{
+-      struct spnego_krb5_ctx *krb5_ctx;
+-      char *client_name;
+-      krb5_auth_context auth_context;
+-      krb5_data packet, ap_rep;
+-      krb5_ticket *ticket = NULL;
+-      krb5_keyblock *session_key;
+-#ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER
+-      krb5_authenticator *authenti;
+-#else
+-      krb5_authenticator authenti;
+-#endif /* HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER */
+-      krb5_principal client;
+-      int retval = -EINVAL;
+-      krb5_error_code krb_retval;
+-
+-      krb5_ctx = (struct spnego_krb5_ctx *)mech_ctx->private;
+-      if (!krb5_ctx)
+-              return -EINVAL;
+-
+-      krb_retval = krb5_auth_con_init(krb5_ctx->context, &auth_context);
+-      if (krb_retval) {
+-              pr_krb5_err(krb5_ctx->context, krb_retval,
+-                              "while initializing auth context\n");
+-              return -EINVAL;
+-      }
+-
+-      packet.length = in_len;
+-      packet.data = (krb5_pointer)in_blob;
+-      krb_retval = krb5_rd_req(krb5_ctx->context, &auth_context, &packet,
+-                              krb5_ctx->creds.client, krb5_ctx->keytab,
+-                              NULL, &ticket);
+-      if (krb_retval) {
+-              char *name;
+-
+-              krb5_unparse_name(krb5_ctx->context, krb5_ctx->creds.client,
+-                              &name);
+-              krb5_auth_con_free(krb5_ctx->context, auth_context);
+-              pr_krb5_err(krb5_ctx->context, krb_retval,
+-                      "while decoding AP_REQ with %s creds\n", name);
+-              krb5_free_unparsed_name(krb5_ctx->context, name);
+-              return -EINVAL;
+-      }
+-
+-      krb_retval = krb5_auth_con_getrecvsubkey(krb5_ctx->context,
+-                              auth_context, &session_key);
+-      if (krb_retval) {
+-              pr_krb5_err(krb5_ctx->context, krb_retval,
+-                              "while reading session key\n");
+-              goto out_free_con_auth;
+-      }
+-
+-      krb_retval = krb5_mk_rep(krb5_ctx->context, auth_context, &ap_rep);
+-      if (krb_retval) {
+-              pr_krb5_err(krb5_ctx->context, krb_retval,
+-                              "while making AP_REP\n");
+-              goto out_free_key;
+-      }
+-
+-      krb_retval = krb5_auth_con_getauthenticator(krb5_ctx->context,
+-                              auth_context, &authenti);
+-      if (krb_retval) {
+-              pr_krb5_err(krb5_ctx->context, krb_retval,
+-                              "while getting authenticator\n");
+-              goto out_free_rep;
+-      }
+-
+-#ifndef HAVE_KRB5_AUTHENTICATOR_CLIENT
+-      krb_retval = krb5_build_principal_ext(krb5_ctx->context, &client,
+-                      strlen(authenti->crealm), authenti->crealm, 0);
+-      if (krb_retval) {
+-              pr_krb5_err(krb5_ctx->context, krb_retval,
+-                              "while getting authenticator client\n");
+-              goto out_free_auth;
+-      }
+-      krb_retval = copy_PrincipalName(&authenti->cname, &client->name);
+-      if (krb_retval) {
+-              pr_krb5_err(krb5_ctx->context, krb_retval,
+-                              "while copying authenticator client name\n");
+-              goto out_free_client;
+-      }
+-#else
+-      client = authenti->client;
+-#endif /* HAVE_KRB5_AUTHENTICATOR_CLIENT */
+-
+-      krb_retval = krb5_unparse_name_flags(krb5_ctx->context,
+-                              client,
+-                              KRB5_PRINCIPAL_UNPARSE_NO_REALM, &client_name);
+-      if (krb_retval) {
+-              pr_krb5_err(krb5_ctx->context, krb_retval,
+-                              "while unparsing client name\n");
+-              goto out_free_client;
+-      }
+-
+-      memset(auth_out, 0, sizeof(*auth_out));
+-      auth_out->user_name = strdup(client_name);
+-      if (!auth_out->user_name) {
+-              krb5_free_unparsed_name(krb5_ctx->context, client_name);
+-              retval = -ENOMEM;
+-              goto out_free_client;
+-      }
+-      krb5_free_unparsed_name(krb5_ctx->context, client_name);
+-
+-      auth_out->sess_key = malloc(KRB5_KEY_LENGTH(session_key));
+-      if (!auth_out->sess_key) {
+-              free(auth_out->user_name);
+-              retval = -ENOMEM;
+-              goto out_free_client;
+-      }
+-      memcpy(auth_out->sess_key, KRB5_KEY_DATA(session_key), KRB5_KEY_LENGTH(session_key));
+-      auth_out->key_len = KRB5_KEY_LENGTH(session_key);
+-
+-      if (spnego_encode(ap_rep.data, ap_rep.length,
+-                      mech_ctx->oid, mech_ctx->oid_len,
+-                      &auth_out->spnego_blob, &auth_out->blob_len)) {
+-              free(auth_out->user_name);
+-              free(auth_out->sess_key);
+-              goto out_free_client;
+-      }
+-
+-      pr_info("Authenticated user `%s'\n", auth_out->user_name);
+-      retval = 0;
+-
+-out_free_client:
+-#ifndef HAVE_KRB5_AUTHENTICATOR_CLIENT
+-      krb5_free_principal(krb5_ctx->context, client);
+-#endif /* HAVE_KRB5_AUTHENTICATOR_CLIENT */
+-out_free_auth:
+-#ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER
+-      krb5_free_authenticator(krb5_ctx->context, authenti);
+-#else
+-      krb5_free_authenticator(krb5_ctx->context, &authenti);
+-#endif /* HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER */
+-out_free_rep:
+-      krb5_free_data_contents(krb5_ctx->context, &ap_rep);
+-out_free_key:
+-      krb5_free_keyblock(krb5_ctx->context, session_key);
+-out_free_con_auth:
+-      krb5_free_ticket(krb5_ctx->context, ticket);
+-      krb5_auth_con_free(krb5_ctx->context, auth_context);
+-      return retval;
+-}
+-
+-static int setup_krb5_ctx(struct spnego_mech_ctx *mech_ctx)
+-{
+-      struct spnego_krb5_ctx *krb5_ctx;
+-      krb5_error_code krb_retval;
+-
+-      krb5_ctx = g_try_malloc0(sizeof(*krb5_ctx));
+-      if (!krb5_ctx)
+-              return -ENOMEM;
+-
+-      krb_retval = krb5_init_context(&krb5_ctx->context);
+-      if (krb_retval) {
+-              g_free(krb5_ctx);
+-              pr_err("while initializing krb5 context\n");
+-              return -EINVAL;
+-      }
+-
+-      krb_retval = acquire_creds_from_keytab(krb5_ctx->context,
+-                      mech_ctx->params.krb5.service_name,
+-                      mech_ctx->params.krb5.keytab_name,
+-                      &krb5_ctx->creds, &krb5_ctx->keytab);
+-      if (krb_retval) {
+-              krb5_free_context(krb5_ctx->context);
+-              g_free(krb5_ctx);
+-              return -EINVAL;
+-      }
+-
+-      mech_ctx->private = krb5_ctx;
+-      return 0;
+-}
+-
+-static int setup_krb5(struct spnego_mech_ctx *mech_ctx)
+-{
+-      mech_ctx->oid = KRB5_OID;
+-      mech_ctx->oid_len = ARRAY_SIZE(KRB5_OID);
+-      return setup_krb5_ctx(mech_ctx);
+-}
+-
+-static int setup_mskrb5(struct spnego_mech_ctx *mech_ctx)
+-{
+-      mech_ctx->oid = MSKRB5_OID;
+-      mech_ctx->oid_len = ARRAY_SIZE(MSKRB5_OID);
+-      return setup_krb5_ctx(mech_ctx);
+-}
+-
+-static void cleanup_krb5(struct spnego_mech_ctx *mech_ctx)
+-{
+-      if (mech_ctx->private) {
+-              struct spnego_krb5_ctx *krb5_ctx;
+-
+-              krb5_ctx = (struct spnego_krb5_ctx *)mech_ctx->private;
+-              krb5_free_cred_contents(krb5_ctx->context, &krb5_ctx->creds);
+-              krb5_kt_close(krb5_ctx->context, krb5_ctx->keytab);
+-              krb5_free_context(krb5_ctx->context);
+-              g_free(krb5_ctx);
+-              mech_ctx->private = NULL;
+-      }
+-      if (mech_ctx->params.krb5.service_name)
+-              free(mech_ctx->params.krb5.service_name);
+-      if (mech_ctx->params.krb5.keytab_name)
+-              free(mech_ctx->params.krb5.keytab_name);
+-}
+-
+-struct spnego_mech_operations spnego_krb5_operations = {
+-      .setup          = setup_krb5,
+-      .cleanup        = cleanup_krb5,
+-      .handle_authen  = handle_krb5_authen,
+-};
+-
+-struct spnego_mech_operations spnego_mskrb5_operations = {
+-      .setup          = setup_mskrb5,
+-      .cleanup        = cleanup_krb5,
+-      .handle_authen  = handle_krb5_authen,
+-};
+--- /dev/null
++++ b/tools/management/spnego_krb5.c
+@@ -0,0 +1,408 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ *   Copyright (C) 2020 LG Electronics
++ *
++ *   linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include "tools.h"
++
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netdb.h>
++#include <krb5.h>
++
++#include <management/spnego.h>
++#include <asn1.h>
++#include "spnego_mech.h"
++
++#ifndef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY
++krb5_error_code krb5_auth_con_getrecvsubkey(krb5_context context,
++                      krb5_auth_context auth_context, krb5_keyblock **keyblock)
++{
++      return krb5_auth_con_getremotesubkey(context, auth_context, keyblock);
++}
++#endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
++
++#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
++#define KRB5_KEY_TYPE(k)      ((k)->keytype)
++#define KRB5_KEY_LENGTH(k)    ((k)->keyvalue.length)
++#define KRB5_KEY_DATA(k)      ((k)->keyvalue.data)
++#else
++#define KRB5_KEY_TYPE(k)      ((k)->enctype)
++#define KRB5_KEY_LENGTH(k)    ((k)->length)
++#define KRB5_KEY_DATA(k)      ((k)->contents)
++#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
++
++struct spnego_krb5_ctx {
++      krb5_context    context;
++      krb5_keytab     keytab;
++      krb5_creds      creds;
++};
++
++#define SERVICE_NAME  "cifs"
++
++#define pr_krb5_err(_context, _retval, _fmt, ...)             \
++      do {                                                    \
++              const char *msg = krb5_get_error_message(_context, _retval);    \
++              pr_err("%s: " _fmt, msg, ##__VA_ARGS__);        \
++              krb5_free_error_message(_context, msg);         \
++      } while (0)
++
++static char *get_service_name(void)
++{
++      return strdup(SERVICE_NAME);
++}
++
++static char *get_host_name(void)
++{
++      struct addrinfo hint, *ai;
++      char *host_name;
++      char hostname[NI_MAXHOST];
++
++      if (gethostname(hostname, sizeof(hostname)))
++              return NULL;
++
++      memset(&hint, 0, sizeof(hint));
++      hint.ai_family = AF_UNSPEC;
++      hint.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
++      if (getaddrinfo(hostname, NULL, &hint, &ai))
++              return NULL;
++
++      host_name = strdup(ai->ai_canonname);
++      freeaddrinfo(ai);
++      return host_name;
++}
++
++/* Service full name is <service name>[/<host FQDN>[@REALM>]] */
++static int parse_service_full_name(char *service_full_name,
++                      char **service_name,
++                      char **host_name)
++{
++      char *name, *delim;
++
++      *service_name = NULL;
++      *host_name = NULL;
++
++      if (!service_full_name) {
++              *service_name = get_service_name();
++              *host_name = get_host_name();
++              goto out;
++      }
++
++      name = service_full_name;
++      delim = strchr(name, '/');
++      if (!delim) {
++              *service_name = strdup(name);
++              *host_name = get_host_name();
++              goto out;
++      }
++      *service_name = strndup(name, delim - name);
++      if (*service_name == NULL)
++              return -ENOMEM;
++
++      name = delim + 1;
++      delim = strchr(name, '@');
++      if (!delim) {
++              *host_name = strdup(name);
++              goto out;
++      }
++      *host_name = strndup(name, delim - name);
++      if (*host_name == NULL) {
++              free(*service_name);
++              *service_name = NULL;
++              return -ENOMEM;
++      }
++out:
++      /* we assume the host name is FQDN if it has "." */
++      if (*host_name && strchr(*host_name, '.'))
++              return 0;
++
++      free(*service_name);
++      free(*host_name);
++      *service_name = NULL;
++      *host_name = NULL;
++      return -EINVAL;
++}
++
++static krb5_error_code acquire_creds_from_keytab(krb5_context context,
++              char *service_full_name, char *keytab_name,
++              krb5_creds *out_creds, krb5_keytab *keytab)
++{
++      krb5_error_code retval;
++      krb5_principal sprinc = NULL;
++      char *host_name = NULL, *service_name = NULL;
++
++      if (keytab_name)
++              retval = krb5_kt_resolve(context, keytab_name, keytab);
++      else
++              retval = krb5_kt_default(context, keytab);
++      if (retval) {
++              pr_krb5_err(context, retval, "while resolving keytab\n");
++              return retval;
++      }
++
++      if (parse_service_full_name(service_full_name,
++                              &service_name, &host_name)) {
++              retval = KRB5_ERR_HOST_REALM_UNKNOWN;
++              pr_krb5_err(context, retval, "while getting host name\n");
++              goto out_err;
++      }
++
++      retval = krb5_sname_to_principal(context, host_name, service_name,
++                      KRB5_NT_UNKNOWN, &sprinc);
++      if (retval) {
++              pr_krb5_err(context, retval, "while generating service name\n");
++              goto out_err;
++      }
++
++      retval = krb5_get_init_creds_keytab(context, out_creds, sprinc,
++                      *keytab, 0, NULL, NULL);
++      if (retval) {
++              char *name;
++
++              krb5_unparse_name(context, sprinc, &name);
++              pr_krb5_err(context, retval,
++                      "while getting credentials for %s\n", name);
++              krb5_free_unparsed_name(context, name);
++              goto out_err;
++      }
++
++      free(host_name);
++      free(service_name);
++      return 0;
++out_err:
++      if (sprinc)
++              krb5_free_principal(context, sprinc);
++      if (service_name)
++              free(service_name);
++      if (host_name)
++              free(host_name);
++      if (*keytab)
++              krb5_kt_close(context, *keytab);
++      return retval;
++}
++
++static int handle_krb5_authen(struct spnego_mech_ctx *mech_ctx,
++                      char *in_blob, unsigned int in_len,
++                      struct ksmbd_spnego_auth_out *auth_out,
++                      spnego_encode_t spnego_encode)
++{
++      struct spnego_krb5_ctx *krb5_ctx;
++      char *client_name;
++      krb5_auth_context auth_context;
++      krb5_data packet, ap_rep;
++      krb5_ticket *ticket = NULL;
++      krb5_keyblock *session_key;
++#ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER
++      krb5_authenticator *authenti;
++#else
++      krb5_authenticator authenti;
++#endif /* HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER */
++      krb5_principal client;
++      int retval = -EINVAL;
++      krb5_error_code krb_retval;
++
++      krb5_ctx = (struct spnego_krb5_ctx *)mech_ctx->private;
++      if (!krb5_ctx)
++              return -EINVAL;
++
++      krb_retval = krb5_auth_con_init(krb5_ctx->context, &auth_context);
++      if (krb_retval) {
++              pr_krb5_err(krb5_ctx->context, krb_retval,
++                              "while initializing auth context\n");
++              return -EINVAL;
++      }
++
++      packet.length = in_len;
++      packet.data = (krb5_pointer)in_blob;
++      krb_retval = krb5_rd_req(krb5_ctx->context, &auth_context, &packet,
++                              krb5_ctx->creds.client, krb5_ctx->keytab,
++                              NULL, &ticket);
++      if (krb_retval) {
++              char *name;
++
++              krb5_unparse_name(krb5_ctx->context, krb5_ctx->creds.client,
++                              &name);
++              krb5_auth_con_free(krb5_ctx->context, auth_context);
++              pr_krb5_err(krb5_ctx->context, krb_retval,
++                      "while decoding AP_REQ with %s creds\n", name);
++              krb5_free_unparsed_name(krb5_ctx->context, name);
++              return -EINVAL;
++      }
++
++      krb_retval = krb5_auth_con_getrecvsubkey(krb5_ctx->context,
++                              auth_context, &session_key);
++      if (krb_retval) {
++              pr_krb5_err(krb5_ctx->context, krb_retval,
++                              "while reading session key\n");
++              goto out_free_con_auth;
++      }
++
++      krb_retval = krb5_mk_rep(krb5_ctx->context, auth_context, &ap_rep);
++      if (krb_retval) {
++              pr_krb5_err(krb5_ctx->context, krb_retval,
++                              "while making AP_REP\n");
++              goto out_free_key;
++      }
++
++      krb_retval = krb5_auth_con_getauthenticator(krb5_ctx->context,
++                              auth_context, &authenti);
++      if (krb_retval) {
++              pr_krb5_err(krb5_ctx->context, krb_retval,
++                              "while getting authenticator\n");
++              goto out_free_rep;
++      }
++
++#ifndef HAVE_KRB5_AUTHENTICATOR_CLIENT
++      krb_retval = krb5_build_principal_ext(krb5_ctx->context, &client,
++                      strlen(authenti->crealm), authenti->crealm, 0);
++      if (krb_retval) {
++              pr_krb5_err(krb5_ctx->context, krb_retval,
++                              "while getting authenticator client\n");
++              goto out_free_auth;
++      }
++      krb_retval = copy_PrincipalName(&authenti->cname, &client->name);
++      if (krb_retval) {
++              pr_krb5_err(krb5_ctx->context, krb_retval,
++                              "while copying authenticator client name\n");
++              goto out_free_client;
++      }
++#else
++      client = authenti->client;
++#endif /* HAVE_KRB5_AUTHENTICATOR_CLIENT */
++
++      krb_retval = krb5_unparse_name_flags(krb5_ctx->context,
++                              client,
++                              KRB5_PRINCIPAL_UNPARSE_NO_REALM, &client_name);
++      if (krb_retval) {
++              pr_krb5_err(krb5_ctx->context, krb_retval,
++                              "while unparsing client name\n");
++              goto out_free_client;
++      }
++
++      memset(auth_out, 0, sizeof(*auth_out));
++      auth_out->user_name = strdup(client_name);
++      if (!auth_out->user_name) {
++              krb5_free_unparsed_name(krb5_ctx->context, client_name);
++              retval = -ENOMEM;
++              goto out_free_client;
++      }
++      krb5_free_unparsed_name(krb5_ctx->context, client_name);
++
++      auth_out->sess_key = malloc(KRB5_KEY_LENGTH(session_key));
++      if (!auth_out->sess_key) {
++              free(auth_out->user_name);
++              retval = -ENOMEM;
++              goto out_free_client;
++      }
++      memcpy(auth_out->sess_key, KRB5_KEY_DATA(session_key), KRB5_KEY_LENGTH(session_key));
++      auth_out->key_len = KRB5_KEY_LENGTH(session_key);
++
++      if (spnego_encode(ap_rep.data, ap_rep.length,
++                      mech_ctx->oid, mech_ctx->oid_len,
++                      &auth_out->spnego_blob, &auth_out->blob_len)) {
++              free(auth_out->user_name);
++              free(auth_out->sess_key);
++              goto out_free_client;
++      }
++
++      pr_info("Authenticated user `%s'\n", auth_out->user_name);
++      retval = 0;
++
++out_free_client:
++#ifndef HAVE_KRB5_AUTHENTICATOR_CLIENT
++      krb5_free_principal(krb5_ctx->context, client);
++#endif /* HAVE_KRB5_AUTHENTICATOR_CLIENT */
++out_free_auth:
++#ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER
++      krb5_free_authenticator(krb5_ctx->context, authenti);
++#else
++      krb5_free_authenticator(krb5_ctx->context, &authenti);
++#endif /* HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER */
++out_free_rep:
++      krb5_free_data_contents(krb5_ctx->context, &ap_rep);
++out_free_key:
++      krb5_free_keyblock(krb5_ctx->context, session_key);
++out_free_con_auth:
++      krb5_free_ticket(krb5_ctx->context, ticket);
++      krb5_auth_con_free(krb5_ctx->context, auth_context);
++      return retval;
++}
++
++static int setup_krb5_ctx(struct spnego_mech_ctx *mech_ctx)
++{
++      struct spnego_krb5_ctx *krb5_ctx;
++      krb5_error_code krb_retval;
++
++      krb5_ctx = g_try_malloc0(sizeof(*krb5_ctx));
++      if (!krb5_ctx)
++              return -ENOMEM;
++
++      krb_retval = krb5_init_context(&krb5_ctx->context);
++      if (krb_retval) {
++              g_free(krb5_ctx);
++              pr_err("while initializing krb5 context\n");
++              return -EINVAL;
++      }
++
++      krb_retval = acquire_creds_from_keytab(krb5_ctx->context,
++                      mech_ctx->params.krb5.service_name,
++                      mech_ctx->params.krb5.keytab_name,
++                      &krb5_ctx->creds, &krb5_ctx->keytab);
++      if (krb_retval) {
++              krb5_free_context(krb5_ctx->context);
++              g_free(krb5_ctx);
++              return -EINVAL;
++      }
++
++      mech_ctx->private = krb5_ctx;
++      return 0;
++}
++
++static int setup_krb5(struct spnego_mech_ctx *mech_ctx)
++{
++      mech_ctx->oid = KRB5_OID;
++      mech_ctx->oid_len = ARRAY_SIZE(KRB5_OID);
++      return setup_krb5_ctx(mech_ctx);
++}
++
++static int setup_mskrb5(struct spnego_mech_ctx *mech_ctx)
++{
++      mech_ctx->oid = MSKRB5_OID;
++      mech_ctx->oid_len = ARRAY_SIZE(MSKRB5_OID);
++      return setup_krb5_ctx(mech_ctx);
++}
++
++static void cleanup_krb5(struct spnego_mech_ctx *mech_ctx)
++{
++      if (mech_ctx->private) {
++              struct spnego_krb5_ctx *krb5_ctx;
++
++              krb5_ctx = (struct spnego_krb5_ctx *)mech_ctx->private;
++              krb5_free_cred_contents(krb5_ctx->context, &krb5_ctx->creds);
++              krb5_kt_close(krb5_ctx->context, krb5_ctx->keytab);
++              krb5_free_context(krb5_ctx->context);
++              g_free(krb5_ctx);
++              mech_ctx->private = NULL;
++      }
++      if (mech_ctx->params.krb5.service_name)
++              free(mech_ctx->params.krb5.service_name);
++      if (mech_ctx->params.krb5.keytab_name)
++              free(mech_ctx->params.krb5.keytab_name);
++}
++
++struct spnego_mech_operations spnego_krb5_operations = {
++      .setup          = setup_krb5,
++      .cleanup        = cleanup_krb5,
++      .handle_authen  = handle_krb5_authen,
++};
++
++struct spnego_mech_operations spnego_mskrb5_operations = {
++      .setup          = setup_mskrb5,
++      .cleanup        = cleanup_krb5,
++      .handle_authen  = handle_krb5_authen,
++};
+--- a/lib/management/tree_conn.c
++++ /dev/null
+@@ -1,231 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- *   linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <glib.h>
+-
+-#include "linux/ksmbd_server.h"
+-#include "management/tree_conn.h"
+-#include "management/session.h"
+-#include "management/share.h"
+-#include "management/user.h"
+-#include "ksmbdtools.h"
+-
+-static struct ksmbd_tree_conn *new_ksmbd_tree_conn(void)
+-{
+-      struct ksmbd_tree_conn *conn;
+-
+-      conn = g_try_malloc0(sizeof(struct ksmbd_tree_conn));
+-      if (!conn)
+-              return NULL;
+-
+-      conn->id = 0;
+-      return conn;
+-}
+-
+-void tcm_tree_conn_free(struct ksmbd_tree_conn *conn)
+-{
+-      shm_close_connection(conn->share);
+-      put_ksmbd_share(conn->share);
+-      g_free(conn);
+-}
+-
+-int tcm_handle_tree_connect(struct ksmbd_tree_connect_request *req,
+-                          struct ksmbd_tree_connect_response *resp)
+-{
+-      struct ksmbd_user *user = NULL;
+-      struct ksmbd_share *share = NULL;
+-      struct ksmbd_tree_conn *conn = new_ksmbd_tree_conn();
+-      int ret;
+-
+-      if (!conn) {
+-              resp->status = KSMBD_TREE_CONN_STATUS_NOMEM;
+-              return -ENOMEM;
+-      }
+-
+-      if (sm_check_sessions_capacity(req->session_id)) {
+-              resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS;
+-              pr_debug("treecon: Too many active sessions\n");
+-              goto out_error;
+-      }
+-
+-      if (global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_NEVER) {
+-              if (req->account_flags & KSMBD_USER_FLAG_BAD_PASSWORD) {
+-                      resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
+-                      pr_debug("treecon: Bad user password\n");
+-                      goto out_error;
+-              }
+-      }
+-
+-      share = shm_lookup_share(req->share);
+-      if (!share) {
+-              resp->status = KSMBD_TREE_CONN_STATUS_NO_SHARE;
+-              pr_err("treecon: Unknown net share: %s\n", req->share);
+-              goto out_error;
+-      }
+-
+-      if (test_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE))
+-              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
+-      if (test_share_flag(share, KSMBD_SHARE_FLAG_READONLY))
+-              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_READ_ONLY);
+-      if (test_share_flag(share, KSMBD_SHARE_FLAG_UPDATE))
+-              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_UPDATE);
+-
+-      if (shm_open_connection(share)) {
+-              resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS;
+-              pr_debug("treecon: Too many connections to net share\n");
+-              goto out_error;
+-      }
+-
+-      ret = shm_lookup_hosts_map(share,
+-                                 KSMBD_SHARE_HOSTS_ALLOW_MAP,
+-                                 req->peer_addr);
+-      if (ret == -ENOENT) {
+-              resp->status = KSMBD_TREE_CONN_STATUS_HOST_DENIED;
+-              pr_debug("treecon: Host denied: %s\n", req->peer_addr);
+-              goto out_error;
+-      }
+-
+-      if (ret != 0) {
+-              ret = shm_lookup_hosts_map(share,
+-                                         KSMBD_SHARE_HOSTS_DENY_MAP,
+-                                         req->peer_addr);
+-              if (ret == 0) {
+-                      resp->status = KSMBD_TREE_CONN_STATUS_HOST_DENIED;
+-                      pr_err("treecon: Host denied: %s\n", req->peer_addr);
+-                      goto out_error;
+-              }
+-      }
+-
+-      if (global_conf.restrict_anon >= KSMBD_RESTRICT_ANON_TYPE_1) {
+-              int deny;
+-
+-              deny = !test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK);
+-              deny |= test_share_flag(share, KSMBD_SHARE_FLAG_PIPE);
+-
+-              if (req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT &&
+-                              deny) {
+-                      pr_debug("treecon: Deny, restricted session\n");
+-                      resp->status = KSMBD_TREE_CONN_STATUS_ERROR;
+-                      goto out_error;
+-              }
+-      }
+-
+-      if ((req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT) &&
+-          !test_share_flag(share, KSMBD_SHARE_FLAG_PIPE) &&
+-          !test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK)) {
+-              pr_debug("treecon: Deny, guest not allowed\n");
+-              resp->status = KSMBD_TREE_CONN_STATUS_ERROR;
+-              goto out_error;
+-      }
+-
+-      if ((req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT) &&
+-           test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK)) {
+-              pr_debug("treecon: Net share permits guest login\n");
+-              user = usm_lookup_user(share->guest_account);
+-              if (user) {
+-                      set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
+-                      goto bind;
+-              }
+-
+-              user = usm_lookup_user(global_conf.guest_account);
+-              if (user) {
+-                      set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
+-                      goto bind;
+-              }
+-      }
+-
+-      user = usm_lookup_user(req->account);
+-      if (!user) {
+-              resp->status = KSMBD_TREE_CONN_STATUS_NO_USER;
+-              pr_err("treecon: User `%s' not found\n", req->account);
+-              goto out_error;
+-      }
+-
+-      user->failed_login_count = 0;
+-      user->flags &= ~KSMBD_USER_FLAG_DELAY_SESSION;
+-
+-      if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
+-              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
+-
+-      ret = shm_lookup_users_map(share,
+-                                 KSMBD_SHARE_ADMIN_USERS_MAP,
+-                                 req->account);
+-      if (ret == 0) {
+-              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT);
+-              goto bind;
+-      }
+-
+-      ret = shm_lookup_users_map(share,
+-                                 KSMBD_SHARE_INVALID_USERS_MAP,
+-                                 req->account);
+-      if (ret == 0) {
+-              resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
+-              pr_err("treecon: User is on invalid users list\n");
+-              goto out_error;
+-      }
+-
+-      ret = shm_lookup_users_map(share,
+-                                 KSMBD_SHARE_READ_LIST_MAP,
+-                                 req->account);
+-      if (ret == 0) {
+-              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_READ_ONLY);
+-              clear_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
+-              goto bind;
+-      }
+-
+-      ret = shm_lookup_users_map(share,
+-                                 KSMBD_SHARE_WRITE_LIST_MAP,
+-                                 req->account);
+-      if (ret == 0) {
+-              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
+-              goto bind;
+-      }
+-
+-      ret = shm_lookup_users_map(share,
+-                                 KSMBD_SHARE_VALID_USERS_MAP,
+-                                 req->account);
+-      if (ret == 0)
+-              goto bind;
+-      if (ret == -ENOENT) {
+-              resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
+-              pr_err("treecon: User is not on valid users list\n");
+-              goto out_error;
+-      }
+-
+-bind:
+-      conn->id = req->connect_id;
+-      conn->share = share;
+-      resp->status = KSMBD_TREE_CONN_STATUS_OK;
+-      resp->connection_flags = conn->flags;
+-
+-      if (sm_handle_tree_connect(req->session_id, user, conn)) {
+-              pr_err("treecon: Unable to bind tree connection\n");
+-              tcm_tree_conn_free(conn);
+-              put_ksmbd_user(user);
+-      }
+-
+-      g_rw_lock_writer_lock(&share->update_lock);
+-      clear_share_flag(share, KSMBD_SHARE_FLAG_UPDATE);
+-      g_rw_lock_writer_unlock(&share->update_lock);
+-
+-      return 0;
+-out_error:
+-      tcm_tree_conn_free(conn);
+-      shm_close_connection(share);
+-      put_ksmbd_share(share);
+-      put_ksmbd_user(user);
+-      return -EINVAL;
+-}
+-
+-int tcm_handle_tree_disconnect(unsigned long long sess_id,
+-                             unsigned long long tree_conn_id)
+-{
+-      sm_handle_tree_disconnect(sess_id, tree_conn_id);
+-      return 0;
+-}
+--- /dev/null
++++ b/tools/management/tree_conn.c
+@@ -0,0 +1,231 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ *   linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <glib.h>
++
++#include "linux/ksmbd_server.h"
++#include "management/tree_conn.h"
++#include "management/session.h"
++#include "management/share.h"
++#include "management/user.h"
++#include "tools.h"
++
++static struct ksmbd_tree_conn *new_ksmbd_tree_conn(void)
++{
++      struct ksmbd_tree_conn *conn;
++
++      conn = g_try_malloc0(sizeof(struct ksmbd_tree_conn));
++      if (!conn)
++              return NULL;
++
++      conn->id = 0;
++      return conn;
++}
++
++void tcm_tree_conn_free(struct ksmbd_tree_conn *conn)
++{
++      shm_close_connection(conn->share);
++      put_ksmbd_share(conn->share);
++      g_free(conn);
++}
++
++int tcm_handle_tree_connect(struct ksmbd_tree_connect_request *req,
++                          struct ksmbd_tree_connect_response *resp)
++{
++      struct ksmbd_user *user = NULL;
++      struct ksmbd_share *share = NULL;
++      struct ksmbd_tree_conn *conn = new_ksmbd_tree_conn();
++      int ret;
++
++      if (!conn) {
++              resp->status = KSMBD_TREE_CONN_STATUS_NOMEM;
++              return -ENOMEM;
++      }
++
++      if (sm_check_sessions_capacity(req->session_id)) {
++              resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS;
++              pr_debug("treecon: Too many active sessions\n");
++              goto out_error;
++      }
++
++      if (global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_NEVER) {
++              if (req->account_flags & KSMBD_USER_FLAG_BAD_PASSWORD) {
++                      resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
++                      pr_debug("treecon: Bad user password\n");
++                      goto out_error;
++              }
++      }
++
++      share = shm_lookup_share(req->share);
++      if (!share) {
++              resp->status = KSMBD_TREE_CONN_STATUS_NO_SHARE;
++              pr_err("treecon: Unknown net share: %s\n", req->share);
++              goto out_error;
++      }
++
++      if (test_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE))
++              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
++      if (test_share_flag(share, KSMBD_SHARE_FLAG_READONLY))
++              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_READ_ONLY);
++      if (test_share_flag(share, KSMBD_SHARE_FLAG_UPDATE))
++              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_UPDATE);
++
++      if (shm_open_connection(share)) {
++              resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS;
++              pr_debug("treecon: Too many connections to net share\n");
++              goto out_error;
++      }
++
++      ret = shm_lookup_hosts_map(share,
++                                 KSMBD_SHARE_HOSTS_ALLOW_MAP,
++                                 req->peer_addr);
++      if (ret == -ENOENT) {
++              resp->status = KSMBD_TREE_CONN_STATUS_HOST_DENIED;
++              pr_debug("treecon: Host denied: %s\n", req->peer_addr);
++              goto out_error;
++      }
++
++      if (ret != 0) {
++              ret = shm_lookup_hosts_map(share,
++                                         KSMBD_SHARE_HOSTS_DENY_MAP,
++                                         req->peer_addr);
++              if (ret == 0) {
++                      resp->status = KSMBD_TREE_CONN_STATUS_HOST_DENIED;
++                      pr_err("treecon: Host denied: %s\n", req->peer_addr);
++                      goto out_error;
++              }
++      }
++
++      if (global_conf.restrict_anon >= KSMBD_RESTRICT_ANON_TYPE_1) {
++              int deny;
++
++              deny = !test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK);
++              deny |= test_share_flag(share, KSMBD_SHARE_FLAG_PIPE);
++
++              if (req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT &&
++                              deny) {
++                      pr_debug("treecon: Deny, restricted session\n");
++                      resp->status = KSMBD_TREE_CONN_STATUS_ERROR;
++                      goto out_error;
++              }
++      }
++
++      if ((req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT) &&
++          !test_share_flag(share, KSMBD_SHARE_FLAG_PIPE) &&
++          !test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK)) {
++              pr_debug("treecon: Deny, guest not allowed\n");
++              resp->status = KSMBD_TREE_CONN_STATUS_ERROR;
++              goto out_error;
++      }
++
++      if ((req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT) &&
++           test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK)) {
++              pr_debug("treecon: Net share permits guest login\n");
++              user = usm_lookup_user(share->guest_account);
++              if (user) {
++                      set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
++                      goto bind;
++              }
++
++              user = usm_lookup_user(global_conf.guest_account);
++              if (user) {
++                      set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
++                      goto bind;
++              }
++      }
++
++      user = usm_lookup_user(req->account);
++      if (!user) {
++              resp->status = KSMBD_TREE_CONN_STATUS_NO_USER;
++              pr_err("treecon: User `%s' not found\n", req->account);
++              goto out_error;
++      }
++
++      user->failed_login_count = 0;
++      user->flags &= ~KSMBD_USER_FLAG_DELAY_SESSION;
++
++      if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
++              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
++
++      ret = shm_lookup_users_map(share,
++                                 KSMBD_SHARE_ADMIN_USERS_MAP,
++                                 req->account);
++      if (ret == 0) {
++              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT);
++              goto bind;
++      }
++
++      ret = shm_lookup_users_map(share,
++                                 KSMBD_SHARE_INVALID_USERS_MAP,
++                                 req->account);
++      if (ret == 0) {
++              resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
++              pr_err("treecon: User is on invalid users list\n");
++              goto out_error;
++      }
++
++      ret = shm_lookup_users_map(share,
++                                 KSMBD_SHARE_READ_LIST_MAP,
++                                 req->account);
++      if (ret == 0) {
++              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_READ_ONLY);
++              clear_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
++              goto bind;
++      }
++
++      ret = shm_lookup_users_map(share,
++                                 KSMBD_SHARE_WRITE_LIST_MAP,
++                                 req->account);
++      if (ret == 0) {
++              set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
++              goto bind;
++      }
++
++      ret = shm_lookup_users_map(share,
++                                 KSMBD_SHARE_VALID_USERS_MAP,
++                                 req->account);
++      if (ret == 0)
++              goto bind;
++      if (ret == -ENOENT) {
++              resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
++              pr_err("treecon: User is not on valid users list\n");
++              goto out_error;
++      }
++
++bind:
++      conn->id = req->connect_id;
++      conn->share = share;
++      resp->status = KSMBD_TREE_CONN_STATUS_OK;
++      resp->connection_flags = conn->flags;
++
++      if (sm_handle_tree_connect(req->session_id, user, conn)) {
++              pr_err("treecon: Unable to bind tree connection\n");
++              tcm_tree_conn_free(conn);
++              put_ksmbd_user(user);
++      }
++
++      g_rw_lock_writer_lock(&share->update_lock);
++      clear_share_flag(share, KSMBD_SHARE_FLAG_UPDATE);
++      g_rw_lock_writer_unlock(&share->update_lock);
++
++      return 0;
++out_error:
++      tcm_tree_conn_free(conn);
++      shm_close_connection(share);
++      put_ksmbd_share(share);
++      put_ksmbd_user(user);
++      return -EINVAL;
++}
++
++int tcm_handle_tree_disconnect(unsigned long long sess_id,
++                             unsigned long long tree_conn_id)
++{
++      sm_handle_tree_disconnect(sess_id, tree_conn_id);
++      return 0;
++}
+--- a/lib/management/user.c
++++ /dev/null
+@@ -1,412 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- *   linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <glib.h>
+-
+-#include "linux/ksmbd_server.h"
+-#include "management/user.h"
+-#include "ksmbdtools.h"
+-
+-#define KSMBD_USER_STATE_FREEING      1
+-
+-static GHashTable     *users_table;
+-static GRWLock                users_table_lock;
+-
+-static void kill_ksmbd_user(struct ksmbd_user *user)
+-{
+-      pr_debug("Kill user `%s'\n", user->name);
+-
+-      free(user->name);
+-      free(user->pass_b64);
+-      free(user->pass);
+-      g_rw_lock_clear(&user->update_lock);
+-      g_free(user);
+-}
+-
+-static int __usm_remove_user(struct ksmbd_user *user)
+-{
+-      int ret = 0;
+-
+-      if (user->state != KSMBD_USER_STATE_FREEING) {
+-              g_rw_lock_writer_lock(&users_table_lock);
+-              if (!g_hash_table_remove(users_table, user->name))
+-                      ret = -EINVAL;
+-              g_rw_lock_writer_unlock(&users_table_lock);
+-      }
+-      if (!ret)
+-              kill_ksmbd_user(user);
+-      return ret;
+-}
+-
+-struct ksmbd_user *get_ksmbd_user(struct ksmbd_user *user)
+-{
+-      g_rw_lock_writer_lock(&user->update_lock);
+-      if (user->ref_count != 0) {
+-              user->ref_count++;
+-              g_rw_lock_writer_unlock(&user->update_lock);
+-      } else {
+-              g_rw_lock_writer_unlock(&user->update_lock);
+-              user = NULL;
+-      }
+-      return user;
+-}
+-
+-void put_ksmbd_user(struct ksmbd_user *user)
+-{
+-      int drop;
+-
+-      if (!user)
+-              return;
+-
+-      g_rw_lock_writer_lock(&user->update_lock);
+-      user->ref_count--;
+-      drop = !user->ref_count;
+-      g_rw_lock_writer_unlock(&user->update_lock);
+-
+-      if (!drop)
+-              return;
+-
+-      __usm_remove_user(user);
+-}
+-
+-static gboolean put_user_callback(gpointer _k, gpointer _v, gpointer data)
+-{
+-      struct ksmbd_user *user = (struct ksmbd_user *)_v;
+-
+-      user->state = KSMBD_USER_STATE_FREEING;
+-      put_ksmbd_user(user);
+-      return TRUE;
+-}
+-
+-void usm_remove_all_users(void)
+-{
+-      g_rw_lock_writer_lock(&users_table_lock);
+-      g_hash_table_foreach_remove(users_table, put_user_callback, NULL);
+-      g_rw_lock_writer_unlock(&users_table_lock);
+-}
+-
+-static struct ksmbd_user *new_ksmbd_user(char *name, char *pwd)
+-{
+-      struct ksmbd_user *user;
+-      struct passwd *passwd;
+-      size_t pass_sz;
+-
+-      user = g_try_malloc0(sizeof(struct ksmbd_user));
+-      if (!user)
+-              return NULL;
+-
+-      g_rw_lock_init(&user->update_lock);
+-      user->name = name;
+-      user->pass_b64 = pwd;
+-      user->ref_count = 1;
+-      user->gid = 9999;
+-      user->uid = 9999;
+-      passwd = getpwnam(name);
+-      if (passwd) {
+-              user->uid = passwd->pw_uid;
+-              user->gid = passwd->pw_gid;
+-      }
+-
+-      user->pass = base64_decode(user->pass_b64, &pass_sz);
+-      user->pass_sz = (int)pass_sz;
+-      return user;
+-}
+-
+-static void free_hash_entry(gpointer k, gpointer u, gpointer user_data)
+-{
+-      kill_ksmbd_user(u);
+-}
+-
+-static void usm_clear_users(void)
+-{
+-      g_hash_table_foreach(users_table, free_hash_entry, NULL);
+-}
+-
+-void usm_destroy(void)
+-{
+-      if (users_table) {
+-              usm_clear_users();
+-              g_hash_table_destroy(users_table);
+-      }
+-      g_rw_lock_clear(&users_table_lock);
+-}
+-
+-int usm_init(void)
+-{
+-      users_table = g_hash_table_new(g_str_hash, g_str_equal);
+-      if (!users_table)
+-              return -ENOMEM;
+-      g_rw_lock_init(&users_table_lock);
+-      return 0;
+-}
+-
+-static struct ksmbd_user *__usm_lookup_user(char *name)
+-{
+-      return g_hash_table_lookup(users_table, name);
+-}
+-
+-struct ksmbd_user *usm_lookup_user(char *name)
+-{
+-      struct ksmbd_user *user, *ret;
+-
+-      if (!name)
+-              return NULL;
+-
+-      g_rw_lock_reader_lock(&users_table_lock);
+-      user = __usm_lookup_user(name);
+-      if (user) {
+-              ret = get_ksmbd_user(user);
+-              if (!ret)
+-                      user = NULL;
+-      }
+-      g_rw_lock_reader_unlock(&users_table_lock);
+-      return user;
+-}
+-
+-int usm_add_new_user(char *name, char *pwd)
+-{
+-      int ret = 0;
+-      struct ksmbd_user *user = new_ksmbd_user(name, pwd);
+-
+-      if (!user) {
+-              free(name);
+-              free(pwd);
+-              return -ENOMEM;
+-      }
+-
+-      g_rw_lock_writer_lock(&users_table_lock);
+-      if (__usm_lookup_user(name)) {
+-              g_rw_lock_writer_unlock(&users_table_lock);
+-              pr_info("User `%s' already exists\n", name);
+-              kill_ksmbd_user(user);
+-              return 0;
+-      }
+-
+-      pr_debug("New user `%s'\n", user->name);
+-      if (!g_hash_table_insert(users_table, user->name, user)) {
+-              kill_ksmbd_user(user);
+-              ret = -EINVAL;
+-      }
+-      g_rw_lock_writer_unlock(&users_table_lock);
+-      return ret;
+-}
+-
+-int usm_add_update_user_from_pwdentry(char *data)
+-{
+-      struct ksmbd_user *user;
+-      char *name;
+-      char *pwd;
+-      char *pos = strchr(data, ':');
+-      int ret;
+-
+-      if (!pos) {
+-              pr_err("Invalid pwd entry: %s\n", data);
+-              return -EINVAL;
+-      }
+-
+-      *pos = 0x00;
+-      name = g_strdup(data);
+-      pwd = g_strdup(pos + 1);
+-
+-      if (!name || !pwd) {
+-              free(name);
+-              free(pwd);
+-              return -ENOMEM;
+-      }
+-
+-      user = usm_lookup_user(name);
+-      if (user) {
+-              ret = usm_update_user_password(user, pwd);
+-              put_ksmbd_user(user);
+-
+-              free(name);
+-              free(pwd);
+-              return ret;
+-      }
+-      return usm_add_new_user(name, pwd);
+-}
+-
+-int usm_add_subauth_global_conf(char *data)
+-{
+-      char *pos = data;
+-      char *spos;
+-
+-      if (!pos)
+-              return -EINVAL;
+-
+-      spos = strchr(pos, ':');
+-      if (!spos) {
+-              pr_err("Invalid subauth entry: %s\n", data);
+-              return -EINVAL;
+-      }
+-
+-      *spos = 0x00;
+-      global_conf.gen_subauth[0] = atoi(pos);
+-      pos = spos + 1;
+-
+-      spos = strchr(pos, ':');
+-      if (!spos) {
+-              pr_err("Invalid subauth entry: %s\n", data);
+-              return -EINVAL;
+-      }
+-      *spos = 0x00;
+-      global_conf.gen_subauth[1] = atoi(pos);
+-      global_conf.gen_subauth[2] = atoi(spos + 1);
+-
+-      return 0;
+-}
+-
+-void for_each_ksmbd_user(walk_users cb, gpointer user_data)
+-{
+-      g_rw_lock_reader_lock(&users_table_lock);
+-      g_hash_table_foreach(users_table, cb, user_data);
+-      g_rw_lock_reader_unlock(&users_table_lock);
+-}
+-
+-int usm_update_user_password(struct ksmbd_user *user, char *pswd)
+-{
+-      size_t pass_sz;
+-      char *pass_b64 = g_strdup(pswd);
+-      char *pass = base64_decode(pass_b64, &pass_sz);
+-
+-      if (!pass_b64 || !pass) {
+-              free(pass_b64);
+-              free(pass);
+-              pr_err("Out of memory\n");
+-              return -ENOMEM;
+-      }
+-
+-      pr_debug("Update user password: %s\n", user->name);
+-      g_rw_lock_writer_lock(&user->update_lock);
+-      free(user->pass_b64);
+-      free(user->pass);
+-      user->pass_b64 = pass_b64;
+-      user->pass = pass;
+-      user->pass_sz = (int)pass_sz;
+-      g_rw_lock_writer_unlock(&user->update_lock);
+-
+-      return 0;
+-}
+-
+-static int usm_copy_user_passhash(struct ksmbd_user *user,
+-                                char *pass,
+-                                size_t sz)
+-{
+-      int ret = -ENOSPC;
+-
+-      if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
+-              return 0;
+-
+-      g_rw_lock_reader_lock(&user->update_lock);
+-      if (sz >= user->pass_sz) {
+-              memcpy(pass, user->pass, user->pass_sz);
+-              ret = user->pass_sz;
+-      }
+-      g_rw_lock_reader_unlock(&user->update_lock);
+-
+-      return ret;
+-}
+-
+-static int usm_copy_user_account(struct ksmbd_user *user,
+-                               char *account,
+-                               size_t sz)
+-{
+-      int account_sz;
+-
+-      if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
+-              return 0;
+-
+-      account_sz = strlen(user->name);
+-      if (sz >= account_sz) {
+-              memcpy(account, user->name, account_sz);
+-              return 0;
+-      }
+-      pr_err("Cannot copy user data, buffer overrun\n");
+-      return -ENOSPC;
+-}
+-
+-static void __handle_login_request(struct ksmbd_login_response *resp,
+-                                 struct ksmbd_user *user)
+-{
+-      int hash_sz;
+-
+-      resp->gid = user->gid;
+-      resp->uid = user->uid;
+-      resp->status = user->flags;
+-      resp->status |= KSMBD_USER_FLAG_OK;
+-
+-      hash_sz = usm_copy_user_passhash(user,
+-                                       resp->hash,
+-                                       sizeof(resp->hash));
+-      if (hash_sz < 0) {
+-              resp->status = KSMBD_USER_FLAG_INVALID;
+-      } else {
+-              resp->hash_sz = (unsigned short)hash_sz;
+-              if (usm_copy_user_account(user,
+-                                        resp->account,
+-                                        sizeof(resp->account)))
+-                      resp->status = KSMBD_USER_FLAG_INVALID;
+-      }
+-}
+-
+-int usm_handle_login_request(struct ksmbd_login_request *req,
+-                           struct ksmbd_login_response *resp)
+-{
+-      struct ksmbd_user *user = NULL;
+-      int null_session = 0;
+-
+-      if (req->account[0] == '\0')
+-              null_session = 1;
+-
+-      if (!null_session)
+-              user = usm_lookup_user(req->account);
+-      if (user) {
+-              __handle_login_request(resp, user);
+-              put_ksmbd_user(user);
+-              return 0;
+-      }
+-
+-      resp->status = KSMBD_USER_FLAG_BAD_USER;
+-      if (!null_session &&
+-              global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_NEVER)
+-              return 0;
+-
+-      if (null_session ||
+-              global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_BAD_USER)
+-              user = usm_lookup_user(global_conf.guest_account);
+-
+-      if (!user)
+-              return 0;
+-
+-      __handle_login_request(resp, user);
+-      put_ksmbd_user(user);
+-      return 0;
+-}
+-
+-int usm_handle_logout_request(struct ksmbd_logout_request *req)
+-{
+-      struct ksmbd_user *user;
+-
+-      user = usm_lookup_user(req->account);
+-      if (!user)
+-              return -ENOENT;
+-
+-      if (req->account_flags & KSMBD_USER_FLAG_BAD_PASSWORD) {
+-              if (user->failed_login_count < 10)
+-                      user->failed_login_count++;
+-              else
+-                      user->flags |= KSMBD_USER_FLAG_DELAY_SESSION;
+-      } else {
+-              user->failed_login_count = 0;
+-              user->flags &= ~KSMBD_USER_FLAG_DELAY_SESSION;
+-      }
+-
+-      put_ksmbd_user(user);
+-      return 0;
+-}
+--- /dev/null
++++ b/tools/management/user.c
+@@ -0,0 +1,412 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ *   linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <glib.h>
++
++#include "linux/ksmbd_server.h"
++#include "management/user.h"
++#include "tools.h"
++
++#define KSMBD_USER_STATE_FREEING      1
++
++static GHashTable     *users_table;
++static GRWLock                users_table_lock;
++
++static void kill_ksmbd_user(struct ksmbd_user *user)
++{
++      pr_debug("Kill user `%s'\n", user->name);
++
++      free(user->name);
++      free(user->pass_b64);
++      free(user->pass);
++      g_rw_lock_clear(&user->update_lock);
++      g_free(user);
++}
++
++static int __usm_remove_user(struct ksmbd_user *user)
++{
++      int ret = 0;
++
++      if (user->state != KSMBD_USER_STATE_FREEING) {
++              g_rw_lock_writer_lock(&users_table_lock);
++              if (!g_hash_table_remove(users_table, user->name))
++                      ret = -EINVAL;
++              g_rw_lock_writer_unlock(&users_table_lock);
++      }
++      if (!ret)
++              kill_ksmbd_user(user);
++      return ret;
++}
++
++struct ksmbd_user *get_ksmbd_user(struct ksmbd_user *user)
++{
++      g_rw_lock_writer_lock(&user->update_lock);
++      if (user->ref_count != 0) {
++              user->ref_count++;
++              g_rw_lock_writer_unlock(&user->update_lock);
++      } else {
++              g_rw_lock_writer_unlock(&user->update_lock);
++              user = NULL;
++      }
++      return user;
++}
++
++void put_ksmbd_user(struct ksmbd_user *user)
++{
++      int drop;
++
++      if (!user)
++              return;
++
++      g_rw_lock_writer_lock(&user->update_lock);
++      user->ref_count--;
++      drop = !user->ref_count;
++      g_rw_lock_writer_unlock(&user->update_lock);
++
++      if (!drop)
++              return;
++
++      __usm_remove_user(user);
++}
++
++static gboolean put_user_callback(gpointer _k, gpointer _v, gpointer data)
++{
++      struct ksmbd_user *user = (struct ksmbd_user *)_v;
++
++      user->state = KSMBD_USER_STATE_FREEING;
++      put_ksmbd_user(user);
++      return TRUE;
++}
++
++void usm_remove_all_users(void)
++{
++      g_rw_lock_writer_lock(&users_table_lock);
++      g_hash_table_foreach_remove(users_table, put_user_callback, NULL);
++      g_rw_lock_writer_unlock(&users_table_lock);
++}
++
++static struct ksmbd_user *new_ksmbd_user(char *name, char *pwd)
++{
++      struct ksmbd_user *user;
++      struct passwd *passwd;
++      size_t pass_sz;
++
++      user = g_try_malloc0(sizeof(struct ksmbd_user));
++      if (!user)
++              return NULL;
++
++      g_rw_lock_init(&user->update_lock);
++      user->name = name;
++      user->pass_b64 = pwd;
++      user->ref_count = 1;
++      user->gid = 9999;
++      user->uid = 9999;
++      passwd = getpwnam(name);
++      if (passwd) {
++              user->uid = passwd->pw_uid;
++              user->gid = passwd->pw_gid;
++      }
++
++      user->pass = base64_decode(user->pass_b64, &pass_sz);
++      user->pass_sz = (int)pass_sz;
++      return user;
++}
++
++static void free_hash_entry(gpointer k, gpointer u, gpointer user_data)
++{
++      kill_ksmbd_user(u);
++}
++
++static void usm_clear_users(void)
++{
++      g_hash_table_foreach(users_table, free_hash_entry, NULL);
++}
++
++void usm_destroy(void)
++{
++      if (users_table) {
++              usm_clear_users();
++              g_hash_table_destroy(users_table);
++      }
++      g_rw_lock_clear(&users_table_lock);
++}
++
++int usm_init(void)
++{
++      users_table = g_hash_table_new(g_str_hash, g_str_equal);
++      if (!users_table)
++              return -ENOMEM;
++      g_rw_lock_init(&users_table_lock);
++      return 0;
++}
++
++static struct ksmbd_user *__usm_lookup_user(char *name)
++{
++      return g_hash_table_lookup(users_table, name);
++}
++
++struct ksmbd_user *usm_lookup_user(char *name)
++{
++      struct ksmbd_user *user, *ret;
++
++      if (!name)
++              return NULL;
++
++      g_rw_lock_reader_lock(&users_table_lock);
++      user = __usm_lookup_user(name);
++      if (user) {
++              ret = get_ksmbd_user(user);
++              if (!ret)
++                      user = NULL;
++      }
++      g_rw_lock_reader_unlock(&users_table_lock);
++      return user;
++}
++
++int usm_add_new_user(char *name, char *pwd)
++{
++      int ret = 0;
++      struct ksmbd_user *user = new_ksmbd_user(name, pwd);
++
++      if (!user) {
++              free(name);
++              free(pwd);
++              return -ENOMEM;
++      }
++
++      g_rw_lock_writer_lock(&users_table_lock);
++      if (__usm_lookup_user(name)) {
++              g_rw_lock_writer_unlock(&users_table_lock);
++              pr_info("User `%s' already exists\n", name);
++              kill_ksmbd_user(user);
++              return 0;
++      }
++
++      pr_debug("New user `%s'\n", user->name);
++      if (!g_hash_table_insert(users_table, user->name, user)) {
++              kill_ksmbd_user(user);
++              ret = -EINVAL;
++      }
++      g_rw_lock_writer_unlock(&users_table_lock);
++      return ret;
++}
++
++int usm_add_update_user_from_pwdentry(char *data)
++{
++      struct ksmbd_user *user;
++      char *name;
++      char *pwd;
++      char *pos = strchr(data, ':');
++      int ret;
++
++      if (!pos) {
++              pr_err("Invalid pwd entry: %s\n", data);
++              return -EINVAL;
++      }
++
++      *pos = 0x00;
++      name = g_strdup(data);
++      pwd = g_strdup(pos + 1);
++
++      if (!name || !pwd) {
++              free(name);
++              free(pwd);
++              return -ENOMEM;
++      }
++
++      user = usm_lookup_user(name);
++      if (user) {
++              ret = usm_update_user_password(user, pwd);
++              put_ksmbd_user(user);
++
++              free(name);
++              free(pwd);
++              return ret;
++      }
++      return usm_add_new_user(name, pwd);
++}
++
++int usm_add_subauth_global_conf(char *data)
++{
++      char *pos = data;
++      char *spos;
++
++      if (!pos)
++              return -EINVAL;
++
++      spos = strchr(pos, ':');
++      if (!spos) {
++              pr_err("Invalid subauth entry: %s\n", data);
++              return -EINVAL;
++      }
++
++      *spos = 0x00;
++      global_conf.gen_subauth[0] = atoi(pos);
++      pos = spos + 1;
++
++      spos = strchr(pos, ':');
++      if (!spos) {
++              pr_err("Invalid subauth entry: %s\n", data);
++              return -EINVAL;
++      }
++      *spos = 0x00;
++      global_conf.gen_subauth[1] = atoi(pos);
++      global_conf.gen_subauth[2] = atoi(spos + 1);
++
++      return 0;
++}
++
++void for_each_ksmbd_user(walk_users cb, gpointer user_data)
++{
++      g_rw_lock_reader_lock(&users_table_lock);
++      g_hash_table_foreach(users_table, cb, user_data);
++      g_rw_lock_reader_unlock(&users_table_lock);
++}
++
++int usm_update_user_password(struct ksmbd_user *user, char *pswd)
++{
++      size_t pass_sz;
++      char *pass_b64 = g_strdup(pswd);
++      char *pass = base64_decode(pass_b64, &pass_sz);
++
++      if (!pass_b64 || !pass) {
++              free(pass_b64);
++              free(pass);
++              pr_err("Out of memory\n");
++              return -ENOMEM;
++      }
++
++      pr_debug("Update user password: %s\n", user->name);
++      g_rw_lock_writer_lock(&user->update_lock);
++      free(user->pass_b64);
++      free(user->pass);
++      user->pass_b64 = pass_b64;
++      user->pass = pass;
++      user->pass_sz = (int)pass_sz;
++      g_rw_lock_writer_unlock(&user->update_lock);
++
++      return 0;
++}
++
++static int usm_copy_user_passhash(struct ksmbd_user *user,
++                                char *pass,
++                                size_t sz)
++{
++      int ret = -ENOSPC;
++
++      if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
++              return 0;
++
++      g_rw_lock_reader_lock(&user->update_lock);
++      if (sz >= user->pass_sz) {
++              memcpy(pass, user->pass, user->pass_sz);
++              ret = user->pass_sz;
++      }
++      g_rw_lock_reader_unlock(&user->update_lock);
++
++      return ret;
++}
++
++static int usm_copy_user_account(struct ksmbd_user *user,
++                               char *account,
++                               size_t sz)
++{
++      int account_sz;
++
++      if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
++              return 0;
++
++      account_sz = strlen(user->name);
++      if (sz >= account_sz) {
++              memcpy(account, user->name, account_sz);
++              return 0;
++      }
++      pr_err("Cannot copy user data, buffer overrun\n");
++      return -ENOSPC;
++}
++
++static void __handle_login_request(struct ksmbd_login_response *resp,
++                                 struct ksmbd_user *user)
++{
++      int hash_sz;
++
++      resp->gid = user->gid;
++      resp->uid = user->uid;
++      resp->status = user->flags;
++      resp->status |= KSMBD_USER_FLAG_OK;
++
++      hash_sz = usm_copy_user_passhash(user,
++                                       resp->hash,
++                                       sizeof(resp->hash));
++      if (hash_sz < 0) {
++              resp->status = KSMBD_USER_FLAG_INVALID;
++      } else {
++              resp->hash_sz = (unsigned short)hash_sz;
++              if (usm_copy_user_account(user,
++                                        resp->account,
++                                        sizeof(resp->account)))
++                      resp->status = KSMBD_USER_FLAG_INVALID;
++      }
++}
++
++int usm_handle_login_request(struct ksmbd_login_request *req,
++                           struct ksmbd_login_response *resp)
++{
++      struct ksmbd_user *user = NULL;
++      int null_session = 0;
++
++      if (req->account[0] == '\0')
++              null_session = 1;
++
++      if (!null_session)
++              user = usm_lookup_user(req->account);
++      if (user) {
++              __handle_login_request(resp, user);
++              put_ksmbd_user(user);
++              return 0;
++      }
++
++      resp->status = KSMBD_USER_FLAG_BAD_USER;
++      if (!null_session &&
++              global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_NEVER)
++              return 0;
++
++      if (null_session ||
++              global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_BAD_USER)
++              user = usm_lookup_user(global_conf.guest_account);
++
++      if (!user)
++              return 0;
++
++      __handle_login_request(resp, user);
++      put_ksmbd_user(user);
++      return 0;
++}
++
++int usm_handle_logout_request(struct ksmbd_logout_request *req)
++{
++      struct ksmbd_user *user;
++
++      user = usm_lookup_user(req->account);
++      if (!user)
++              return -ENOENT;
++
++      if (req->account_flags & KSMBD_USER_FLAG_BAD_PASSWORD) {
++              if (user->failed_login_count < 10)
++                      user->failed_login_count++;
++              else
++                      user->flags |= KSMBD_USER_FLAG_DELAY_SESSION;
++      } else {
++              user->failed_login_count = 0;
++              user->flags &= ~KSMBD_USER_FLAG_DELAY_SESSION;
++      }
++
++      put_ksmbd_user(user);
++      return 0;
++}
+--- a/lib/meson.build
++++ /dev/null
+@@ -1,31 +0,0 @@
+-core_files = [
+-  'management/tree_conn.c',
+-  'management/user.c',
+-  'management/share.c',
+-  'management/session.c',
+-  'config_parser.c',
+-  'ksmbdtools.c',
+-]
+-
+-if krb5_dep.found()
+-  core_files += [
+-    'management/spnego.c',
+-    'asn1.c',
+-    'management/spnego_krb5.c',
+-  ]
+-endif
+-
+-libksmbdtools = static_library(
+-  'ksmbdtools',
+-  core_files,
+-  include_directories: tools_incdir,
+-  dependencies: [
+-    glib_dep,
+-    krb5_dep,
+-    asn1_lib,
+-  ],
+-  c_args: [
+-    '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
+-    '-DRUNSTATEDIR="@0@"'.format(runstatedir),
+-  ],
+-)
+--- /dev/null
++++ b/tools/meson.build
+@@ -0,0 +1,39 @@
++ksmbd_tools_files = [
++  'management/tree_conn.c',
++  'management/user.c',
++  'management/share.c',
++  'management/session.c',
++  'config_parser.c',
++  'tools.c',
++]
++
++if krb5_dep.found()
++  ksmbd_tools_files += [
++    'management/spnego.c',
++    'asn1.c',
++    'management/spnego_krb5.c',
++  ]
++endif
++
++executable(
++  'ksmbd.tools',
++  ksmbd_tools_files,
++  include_directories: include_dirs,
++  c_args: [
++    '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
++    '-DRUNSTATEDIR="@0@"'.format(runstatedir),
++  ],
++  dependencies: [
++    glib_dep,
++    krb5_dep,
++    asn1_lib,
++  ],
++  link_with: [
++    addshare_lib,
++    adduser_lib,
++    control_lib,
++    mountd_lib,
++  ],
++  install: true,
++  install_dir: get_option('libexecdir'),
++)
+--- a/lib/ksmbdtools.c
++++ /dev/null
+@@ -1,276 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- *   linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <syslog.h>
+-
+-#include <unistd.h>
+-#include <sys/stat.h>
+-#include <fcntl.h>
+-
+-#include <stdio.h>
+-#include <ksmbdtools.h>
+-
+-int log_level = PR_INFO;
+-int ksmbd_health_status;
+-
+-static const char *app_name = "unknown";
+-static int log_open;
+-
+-typedef void (*logger)(int level, const char *fmt, va_list list);
+-
+-char *ksmbd_conv_charsets[KSMBD_CHARSET_MAX + 1] = {
+-      "UTF-8",
+-      "UTF-16LE",
+-      "UCS-2LE",
+-      "UTF-16BE",
+-      "UCS-2BE",
+-      "OOPS"
+-};
+-
+-static int syslog_level(int level)
+-{
+-      if (level == PR_ERROR)
+-              return LOG_ERR;
+-      if (level == PR_INFO)
+-              return LOG_INFO;
+-      if (level == PR_DEBUG)
+-              return LOG_DEBUG;
+-
+-      return LOG_ERR;
+-}
+-
+-G_GNUC_PRINTF(2, 0)
+-static void __pr_log_stdio(int level, const char *fmt, va_list list)
+-{
+-      char buf[1024];
+-
+-      vsnprintf(buf, sizeof(buf), fmt, list);
+-      printf("%s", buf);
+-}
+-
+-G_GNUC_PRINTF(2, 0)
+-static void __pr_log_syslog(int level, const char *fmt, va_list list)
+-{
+-      vsyslog(syslog_level(level), fmt, list);
+-}
+-
+-static logger __logger = __pr_log_stdio;
+-
+-void set_logger_app_name(const char *an)
+-{
+-      app_name = an;
+-}
+-
+-const char *get_logger_app_name(void)
+-{
+-      return app_name;
+-}
+-
+-void __pr_log(int level, const char *fmt, ...)
+-{
+-      va_list list;
+-
+-      va_start(list, fmt);
+-      __logger(level, fmt, list);
+-      va_end(list);
+-}
+-
+-void pr_logger_init(int flag)
+-{
+-      if (flag == PR_LOGGER_SYSLOG) {
+-              if (log_open) {
+-                      closelog();
+-                      log_open = 0;
+-              }
+-              openlog("ksmbd", LOG_NDELAY, LOG_LOCAL5);
+-              __logger = __pr_log_syslog;
+-              log_open = 1;
+-      }
+-}
+-
+-int set_log_level(int level)
+-{
+-      int old_level;
+-
+-      if (log_level == PR_DEBUG)
+-              return log_level;
+-
+-      old_level = log_level;
+-      log_level = level;
+-      return old_level;
+-}
+-
+-#if TRACING_DUMP_NL_MSG
+-#define PR_HEX_DUMP_WIDTH     160
+-void pr_hex_dump(const void *mem, size_t sz)
+-{
+-      char xline[PR_HEX_DUMP_WIDTH];
+-      char sline[PR_HEX_DUMP_WIDTH];
+-      int xi = 0, si = 0, mi = 0;
+-
+-      while (mi < sz) {
+-              char c = *((char *)mem + mi);
+-
+-              mi++;
+-              xi += sprintf(xline + xi, "%02X ", 0xff & c);
+-              if (c > ' ' && c < '~')
+-                      si += sprintf(sline + si, "%c", c);
+-              else
+-                      si += sprintf(sline + si, ".");
+-              if (xi >= PR_HEX_DUMP_WIDTH / 2) {
+-                      pr_err("%s         %s\n", xline, sline);
+-                      xi = 0;
+-                      si = 0;
+-              }
+-      }
+-
+-      if (xi) {
+-              int sz = PR_HEX_DUMP_WIDTH / 2 - xi + 1;
+-
+-              if (sz > 0) {
+-                      memset(xline + xi, ' ', sz);
+-                      xline[PR_HEX_DUMP_WIDTH / 2 + 1] = 0x00;
+-              }
+-              pr_err("%s         %s\n", xline, sline);
+-      }
+-}
+-#else
+-void pr_hex_dump(const void *mem, size_t sz)
+-{
+-}
+-#endif
+-
+-char *base64_encode(unsigned char *src, size_t srclen)
+-{
+-      return g_base64_encode(src, srclen);
+-}
+-
+-unsigned char *base64_decode(char const *src, size_t *dstlen)
+-{
+-      unsigned char *ret = g_base64_decode(src, dstlen);
+-
+-      if (ret)
+-              ret[*dstlen] = 0x00;
+-      return ret;
+-}
+-
+-static int codeset_has_altname(int codeset)
+-{
+-      if (codeset == KSMBD_CHARSET_UTF16LE ||
+-                      codeset == KSMBD_CHARSET_UTF16BE)
+-              return 1;
+-      return 0;
+-}
+-
+-gchar *ksmbd_gconvert(const gchar *str,
+-                    gssize       str_len,
+-                    int          to_codeset,
+-                    int          from_codeset,
+-                    gsize       *bytes_read,
+-                    gsize       *bytes_written)
+-{
+-      gchar *converted;
+-      GError *err;
+-
+-retry:
+-      err = NULL;
+-      if (from_codeset >= KSMBD_CHARSET_MAX) {
+-              pr_err("Unknown source codeset: %d\n", from_codeset);
+-              return NULL;
+-      }
+-
+-      if (to_codeset >= KSMBD_CHARSET_MAX) {
+-              pr_err("Unknown target codeset: %d\n", to_codeset);
+-              return NULL;
+-      }
+-
+-      converted = g_convert(str,
+-                            str_len,
+-                            ksmbd_conv_charsets[to_codeset],
+-                            ksmbd_conv_charsets[from_codeset],
+-                            bytes_read,
+-                            bytes_written,
+-                            &err);
+-      if (err) {
+-              int has_altname = 0;
+-
+-              if (codeset_has_altname(to_codeset)) {
+-                      to_codeset++;
+-                      has_altname = 1;
+-              }
+-
+-              if (codeset_has_altname(from_codeset)) {
+-                      from_codeset++;
+-                      has_altname = 1;
+-              }
+-
+-              pr_info("%s\n", err->message);
+-              g_error_free(err);
+-
+-              if (has_altname) {
+-                      pr_info("Will try `%s' and `%s'\n",
+-                              ksmbd_conv_charsets[to_codeset],
+-                              ksmbd_conv_charsets[from_codeset]);
+-                      goto retry;
+-              }
+-
+-              pr_err("Can't convert string: %s\n", err->message);
+-              g_error_free(err);
+-              return NULL;
+-      }
+-
+-      return converted;
+-}
+-
+-int send_signal_to_ksmbd_mountd(int signo)
+-{
+-      int fd, ret = -EINVAL;
+-      char pid_buf[10] = {0};
+-      int pid;
+-
+-      fd = open(KSMBD_LOCK_FILE, O_RDONLY);
+-      if (fd < 0) {
+-              pr_debug("Can't open `%s': %m\n", KSMBD_LOCK_FILE);
+-              return ret;
+-      }
+-
+-      if (read(fd, &pid_buf, sizeof(pid_buf)) == -1) {
+-              pr_err("Can't read manager PID: %m\n");
+-              goto out;
+-      }
+-
+-      pid = strtol(pid_buf, NULL, 10);
+-      if (signo)
+-              pr_debug("Send signal %d (%s) to PID %d\n",
+-                       signo, strsignal(signo), pid);
+-      if (kill(pid, signo) == -1) {
+-              ret = -errno;
+-              if (signo)
+-                      pr_err("Unable to send signal %d (%s) to PID %d: %m\n",
+-                                      signo, strsignal(signo), pid);
+-              goto out;
+-      }
+-
+-      ret = 0;
+-out:
+-      close(fd);
+-      return ret;
+-}
+-
+-int test_file_access(char *conf)
+-{
+-      int fd;
+-
+-      fd = open(conf, O_RDWR | O_CREAT, S_IRWXU | S_IRGRP);
+-      if (fd < 0) {
+-              pr_debug("Can't open `%s': %m\n", conf);
+-              return -EINVAL;
+-      }
+-
+-      close(fd);
+-      return 0;
+-}
+--- /dev/null
++++ b/tools/tools.c
+@@ -0,0 +1,301 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ *   linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <syslog.h>
++
++#include <unistd.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++
++#include <stdio.h>
++#include <tools.h>
++
++int log_level = PR_INFO;
++int ksmbd_health_status;
++
++static const char *app_name = "unknown";
++static int log_open;
++
++typedef void (*logger)(int level, const char *fmt, va_list list);
++
++char *ksmbd_conv_charsets[KSMBD_CHARSET_MAX + 1] = {
++      "UTF-8",
++      "UTF-16LE",
++      "UCS-2LE",
++      "UTF-16BE",
++      "UCS-2BE",
++      "OOPS"
++};
++
++static int syslog_level(int level)
++{
++      if (level == PR_ERROR)
++              return LOG_ERR;
++      if (level == PR_INFO)
++              return LOG_INFO;
++      if (level == PR_DEBUG)
++              return LOG_DEBUG;
++
++      return LOG_ERR;
++}
++
++G_GNUC_PRINTF(2, 0)
++static void __pr_log_stdio(int level, const char *fmt, va_list list)
++{
++      char buf[1024];
++
++      vsnprintf(buf, sizeof(buf), fmt, list);
++      printf("%s", buf);
++}
++
++G_GNUC_PRINTF(2, 0)
++static void __pr_log_syslog(int level, const char *fmt, va_list list)
++{
++      vsyslog(syslog_level(level), fmt, list);
++}
++
++static logger __logger = __pr_log_stdio;
++
++void set_logger_app_name(const char *an)
++{
++      app_name = an;
++}
++
++const char *get_logger_app_name(void)
++{
++      return app_name;
++}
++
++void __pr_log(int level, const char *fmt, ...)
++{
++      va_list list;
++
++      va_start(list, fmt);
++      __logger(level, fmt, list);
++      va_end(list);
++}
++
++void pr_logger_init(int flag)
++{
++      if (flag == PR_LOGGER_SYSLOG) {
++              if (log_open) {
++                      closelog();
++                      log_open = 0;
++              }
++              openlog("ksmbd", LOG_NDELAY, LOG_LOCAL5);
++              __logger = __pr_log_syslog;
++              log_open = 1;
++      }
++}
++
++int set_log_level(int level)
++{
++      int old_level;
++
++      if (log_level == PR_DEBUG)
++              return log_level;
++
++      old_level = log_level;
++      log_level = level;
++      return old_level;
++}
++
++#if TRACING_DUMP_NL_MSG
++#define PR_HEX_DUMP_WIDTH     160
++void pr_hex_dump(const void *mem, size_t sz)
++{
++      char xline[PR_HEX_DUMP_WIDTH];
++      char sline[PR_HEX_DUMP_WIDTH];
++      int xi = 0, si = 0, mi = 0;
++
++      while (mi < sz) {
++              char c = *((char *)mem + mi);
++
++              mi++;
++              xi += sprintf(xline + xi, "%02X ", 0xff & c);
++              if (c > ' ' && c < '~')
++                      si += sprintf(sline + si, "%c", c);
++              else
++                      si += sprintf(sline + si, ".");
++              if (xi >= PR_HEX_DUMP_WIDTH / 2) {
++                      pr_err("%s         %s\n", xline, sline);
++                      xi = 0;
++                      si = 0;
++              }
++      }
++
++      if (xi) {
++              int sz = PR_HEX_DUMP_WIDTH / 2 - xi + 1;
++
++              if (sz > 0) {
++                      memset(xline + xi, ' ', sz);
++                      xline[PR_HEX_DUMP_WIDTH / 2 + 1] = 0x00;
++              }
++              pr_err("%s         %s\n", xline, sline);
++      }
++}
++#else
++void pr_hex_dump(const void *mem, size_t sz)
++{
++}
++#endif
++
++char *base64_encode(unsigned char *src, size_t srclen)
++{
++      return g_base64_encode(src, srclen);
++}
++
++unsigned char *base64_decode(char const *src, size_t *dstlen)
++{
++      unsigned char *ret = g_base64_decode(src, dstlen);
++
++      if (ret)
++              ret[*dstlen] = 0x00;
++      return ret;
++}
++
++static int codeset_has_altname(int codeset)
++{
++      if (codeset == KSMBD_CHARSET_UTF16LE ||
++                      codeset == KSMBD_CHARSET_UTF16BE)
++              return 1;
++      return 0;
++}
++
++gchar *ksmbd_gconvert(const gchar *str,
++                    gssize       str_len,
++                    int          to_codeset,
++                    int          from_codeset,
++                    gsize       *bytes_read,
++                    gsize       *bytes_written)
++{
++      gchar *converted;
++      GError *err;
++
++retry:
++      err = NULL;
++      if (from_codeset >= KSMBD_CHARSET_MAX) {
++              pr_err("Unknown source codeset: %d\n", from_codeset);
++              return NULL;
++      }
++
++      if (to_codeset >= KSMBD_CHARSET_MAX) {
++              pr_err("Unknown target codeset: %d\n", to_codeset);
++              return NULL;
++      }
++
++      converted = g_convert(str,
++                            str_len,
++                            ksmbd_conv_charsets[to_codeset],
++                            ksmbd_conv_charsets[from_codeset],
++                            bytes_read,
++                            bytes_written,
++                            &err);
++      if (err) {
++              int has_altname = 0;
++
++              if (codeset_has_altname(to_codeset)) {
++                      to_codeset++;
++                      has_altname = 1;
++              }
++
++              if (codeset_has_altname(from_codeset)) {
++                      from_codeset++;
++                      has_altname = 1;
++              }
++
++              pr_info("%s\n", err->message);
++              g_error_free(err);
++
++              if (has_altname) {
++                      pr_info("Will try `%s' and `%s'\n",
++                              ksmbd_conv_charsets[to_codeset],
++                              ksmbd_conv_charsets[from_codeset]);
++                      goto retry;
++              }
++
++              pr_err("Can't convert string: %s\n", err->message);
++              g_error_free(err);
++              return NULL;
++      }
++
++      return converted;
++}
++
++int send_signal_to_ksmbd_mountd(int signo)
++{
++      int fd, ret = -EINVAL;
++      char pid_buf[10] = {0};
++      int pid;
++
++      fd = open(KSMBD_LOCK_FILE, O_RDONLY);
++      if (fd < 0) {
++              pr_debug("Can't open `%s': %m\n", KSMBD_LOCK_FILE);
++              return ret;
++      }
++
++      if (read(fd, &pid_buf, sizeof(pid_buf)) == -1) {
++              pr_err("Can't read manager PID: %m\n");
++              goto out;
++      }
++
++      pid = strtol(pid_buf, NULL, 10);
++      if (signo)
++              pr_debug("Send signal %d (%s) to PID %d\n",
++                       signo, strsignal(signo), pid);
++      if (kill(pid, signo) == -1) {
++              ret = -errno;
++              if (signo)
++                      pr_err("Unable to send signal %d (%s) to PID %d: %m\n",
++                                      signo, strsignal(signo), pid);
++              goto out;
++      }
++
++      ret = 0;
++out:
++      close(fd);
++      return ret;
++}
++
++int test_file_access(char *conf)
++{
++      int fd;
++
++      fd = open(conf, O_RDWR | O_CREAT, S_IRWXU | S_IRGRP);
++      if (fd < 0) {
++              pr_debug("Can't open `%s': %m\n", conf);
++              return -EINVAL;
++      }
++
++      close(fd);
++      return 0;
++}
++
++int main(int argc, char **argv)
++{
++      char *base_name;
++
++      set_logger_app_name("ksmbd.tools");
++
++      if (!*argv)
++              return EXIT_FAILURE;
++
++      base_name = strrchr(*argv, '/');
++      base_name = base_name ? base_name + 1 : *argv;
++
++      if (!strcmp(base_name, "ksmbd.addshare"))
++              return addshare_main(argc, argv);
++      if (!strcmp(base_name, "ksmbd.adduser"))
++              return adduser_main(argc, argv);
++      if (!strcmp(base_name, "ksmbd.control"))
++              return control_main(argc, argv);
++      if (!strcmp(base_name, "ksmbd.mountd"))
++              return mountd_main(argc, argv);
++
++      pr_err("Unknown base name: %s\n", base_name);
++      return EXIT_FAILURE;
++}
+--- a/lib/asn1.c
++++ /dev/null
+@@ -1,391 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in
+- * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
+- *
+- * Copyright (c) 2000 RP Internet (www.rpi.net.au).
+- */
+-
+-/*****************************************************************************
+- *
+- * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse)
+- *
+- *****************************************************************************/
+-#include <stdlib.h>
+-#include <limits.h>
+-#include <string.h>
+-#include <errno.h>
+-#include <glib.h>
+-
+-#include "asn1.h"
+-
+-void
+-asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len)
+-{
+-      ctx->begin = buf;
+-      ctx->end = buf + len;
+-      ctx->pointer = buf;
+-      ctx->error = ASN1_ERR_NOERROR;
+-}
+-
+-static unsigned char
+-asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch)
+-{
+-      if (ctx->pointer >= ctx->end) {
+-              ctx->error = ASN1_ERR_DEC_EMPTY;
+-              return 0;
+-      }
+-      *ch = *(ctx->pointer)++;
+-      return 1;
+-}
+-
+-static unsigned char
+-asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag)
+-{
+-      unsigned char ch;
+-
+-      *tag = 0;
+-
+-      do {
+-              if (!asn1_octet_decode(ctx, &ch))
+-                      return 0;
+-              *tag <<= 7;
+-              *tag |= ch & 0x7F;
+-      } while ((ch & 0x80) == 0x80);
+-      return 1;
+-}
+-
+-static unsigned char
+-asn1_id_decode(struct asn1_ctx *ctx,
+-             unsigned int *cls, unsigned int *con, unsigned int *tag)
+-{
+-      unsigned char ch;
+-
+-      if (!asn1_octet_decode(ctx, &ch))
+-              return 0;
+-
+-      *cls = (ch & 0xC0) >> 6;
+-      *con = (ch & 0x20) >> 5;
+-      *tag = (ch & 0x1F);
+-
+-      if (*tag == 0x1F) {
+-              if (!asn1_tag_decode(ctx, tag))
+-                      return 0;
+-      }
+-      return 1;
+-}
+-
+-static unsigned char
+-asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len)
+-{
+-      unsigned char ch, cnt;
+-
+-      if (!asn1_octet_decode(ctx, &ch))
+-              return 0;
+-
+-      if (ch == 0x80)
+-              *def = 0;
+-      else {
+-              *def = 1;
+-
+-              if (ch < 0x80)
+-                      *len = ch;
+-              else {
+-                      cnt = (unsigned char) (ch & 0x7F);
+-                      *len = 0;
+-
+-                      while (cnt > 0) {
+-                              if (!asn1_octet_decode(ctx, &ch))
+-                                      return 0;
+-                              *len <<= 8;
+-                              *len |= ch;
+-                              cnt--;
+-                      }
+-              }
+-      }
+-
+-      /* don't trust len bigger than ctx buffer */
+-      if (*len > ctx->end - ctx->pointer)
+-              return 0;
+-
+-      return 1;
+-}
+-
+-unsigned char
+-asn1_header_decode(struct asn1_ctx *ctx,
+-                 unsigned char **eoc,
+-                 unsigned int *cls, unsigned int *con, unsigned int *tag)
+-{
+-      unsigned int def = 0;
+-      unsigned int len = 0;
+-
+-      if (!asn1_id_decode(ctx, cls, con, tag))
+-              return 0;
+-
+-      if (!asn1_length_decode(ctx, &def, &len))
+-              return 0;
+-
+-      /* primitive shall be definite, indefinite shall be constructed */
+-      if (*con == ASN1_PRI && !def)
+-              return 0;
+-
+-      if (def)
+-              *eoc = ctx->pointer + len;
+-      else
+-              *eoc = NULL;
+-      return 1;
+-}
+-
+-static unsigned char
+-asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc)
+-{
+-      unsigned char ch;
+-
+-      if (eoc == NULL) {
+-              if (!asn1_octet_decode(ctx, &ch))
+-                      return 0;
+-
+-              if (ch != 0x00) {
+-                      ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
+-                      return 0;
+-              }
+-
+-              if (!asn1_octet_decode(ctx, &ch))
+-                      return 0;
+-
+-              if (ch != 0x00) {
+-                      ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
+-                      return 0;
+-              }
+-              return 1;
+-      }
+-
+-      if (ctx->pointer != eoc) {
+-              ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
+-              return 0;
+-      }
+-      return 1;
+-}
+-
+-unsigned char
+-asn1_octets_decode(struct asn1_ctx *ctx,
+-                 unsigned char *eoc,
+-                 unsigned char **octets, unsigned int *len)
+-{
+-      unsigned char *ptr;
+-
+-      *len = 0;
+-
+-      *octets = malloc(eoc - ctx->pointer);
+-      if (*octets == NULL)
+-              return 0;
+-
+-      ptr = *octets;
+-      while (ctx->pointer < eoc) {
+-              if (!asn1_octet_decode(ctx, (unsigned char *) ptr++)) {
+-                      free(*octets);
+-                      *octets = NULL;
+-                      return 0;
+-              }
+-              (*len)++;
+-      }
+-      return 1;
+-}
+-
+-unsigned char asn1_read(struct asn1_ctx *ctx,
+-              unsigned char **buf, unsigned int len)
+-{
+-      *buf = NULL;
+-      if (ctx->end - ctx->pointer < len) {
+-              ctx->error = ASN1_ERR_DEC_EMPTY;
+-              return 0;
+-      }
+-
+-      *buf = malloc(len);
+-      if (!*buf)
+-              return 0;
+-      memcpy(*buf, ctx->pointer, len);
+-      ctx->pointer += len;
+-      return 1;
+-}
+-
+-static unsigned char
+-asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid)
+-{
+-      unsigned char ch;
+-
+-      *subid = 0;
+-
+-      do {
+-              if (!asn1_octet_decode(ctx, &ch))
+-                      return 0;
+-
+-              *subid <<= 7;
+-              *subid |= ch & 0x7F;
+-      } while ((ch & 0x80) == 0x80);
+-      return 1;
+-}
+-
+-int
+-asn1_oid_decode(struct asn1_ctx *ctx,
+-              unsigned char *eoc, unsigned long **oid, unsigned int *len)
+-{
+-      unsigned long subid;
+-      unsigned int size;
+-      unsigned long *optr;
+-
+-      size = eoc - ctx->pointer + 1;
+-
+-      /* first subid actually encodes first two subids */
+-      if (size < 2 || size > UINT_MAX/sizeof(unsigned long))
+-              return 0;
+-
+-      *oid = g_try_malloc0_n(size, sizeof(unsigned long));
+-      if (*oid == NULL)
+-              return 0;
+-
+-      optr = *oid;
+-
+-      if (!asn1_subid_decode(ctx, &subid)) {
+-              g_free(*oid);
+-              *oid = NULL;
+-              return 0;
+-      }
+-
+-      if (subid < 40) {
+-              optr[0] = 0;
+-              optr[1] = subid;
+-      } else if (subid < 80) {
+-              optr[0] = 1;
+-              optr[1] = subid - 40;
+-      } else {
+-              optr[0] = 2;
+-              optr[1] = subid - 80;
+-      }
+-
+-      *len = 2;
+-      optr += 2;
+-
+-      while (ctx->pointer < eoc) {
+-              if (++(*len) > size) {
+-                      ctx->error = ASN1_ERR_DEC_BADVALUE;
+-                      g_free(*oid);
+-                      *oid = NULL;
+-                      return 0;
+-              }
+-
+-              if (!asn1_subid_decode(ctx, optr++)) {
+-                      g_free(*oid);
+-                      *oid = NULL;
+-                      return 0;
+-              }
+-      }
+-      return 1;
+-}
+-
+-/* return the size of @depth-nested headers + payload */
+-int asn1_header_len(unsigned int payload_len, int depth)
+-{
+-      unsigned int len;
+-      int i;
+-
+-      len = payload_len;
+-      for (i = 0; i < depth; i++) {
+-              /* length */
+-              if (len >= (1 << 24))
+-                      len += 5;
+-              else if (len >= (1 << 16))
+-                      len += 4;
+-              else if (len >= (1 << 8))
+-                      len += 3;
+-              else if (len >= (1 << 7))
+-                      len += 2;
+-              else
+-                      len += 1;
+-              /* 1-byte header */
+-              len += 1;
+-      }
+-      return len;
+-}
+-
+-int asn1_oid_encode(const unsigned long *in_oid, int in_len,
+-                      unsigned char **out_oid, int *out_len)
+-{
+-      unsigned char *oid;
+-      unsigned long id;
+-      int i;
+-
+-      *out_oid = g_try_malloc0_n(in_len, 5);
+-      if (*out_oid == NULL)
+-              return -ENOMEM;
+-
+-      oid = *out_oid;
+-      *oid++ = (unsigned char)(40 * in_oid[0] + in_oid[1]);
+-      for (i = 2; i < in_len; i++) {
+-              id = in_oid[i];
+-              if (id >= (1 << 28))
+-                      *oid++ = (0x80 | ((id>>28) & 0x7F));
+-              if (id >= (1 << 21))
+-                      *oid++ = (0x80 | ((id>>21) & 0x7F));
+-              if (id >= (1 << 14))
+-                      *oid++ = (0x80 | ((id>>14) & 0x7F));
+-              if (id >= (1 << 7))
+-                      *oid++ = (0x80 | ((id>>7) & 0x7F));
+-              *oid++ = id & 0x7F;
+-      }
+-      *out_len = (int)(oid - *out_oid);
+-      return 0;
+-}
+-
+-/*
+- * @len is the sum of all sizes of header, length and payload.
+- * it will be decreased by the sum of sizes of header and length.
+- */
+-int asn1_header_encode(unsigned char **buf,
+-                      unsigned int cls, unsigned int con, unsigned int tag,
+-                      unsigned int *len)
+-{
+-      unsigned char *loc;
+-      unsigned int r_len;
+-
+-      /* at least, 1-byte header + 1-byte length is needed. */
+-      if (*len < 2)
+-              return -EINVAL;
+-
+-      loc = *buf;
+-      r_len = *len;
+-
+-      *loc++ = ((cls & 0x3) << 6) | ((con & 0x1) << 5) | (tag & 0x1F);
+-      r_len -= 1;
+-
+-      if (r_len - 1 < (1 << 7)) {
+-              r_len -= 1;
+-              *loc++ = (unsigned char)(r_len & 0x7F);
+-      } else if (r_len - 2 < (1 << 8)) {
+-              r_len -= 2;
+-              *loc++ = 0x81;
+-              *loc++ = (unsigned char)(r_len & 0xFF);
+-      } else if (r_len - 3 < (1 << 16)) {
+-              r_len -= 3;
+-              *loc++ = 0x82;
+-              *loc++ = (unsigned char)((r_len>>8) & 0xFF);
+-              *loc++ = (unsigned char)(r_len & 0xFF);
+-      } else if (r_len - 4 < (1 << 24)) {
+-              r_len -= 4;
+-              *loc++ = 0x83;
+-              *loc++ = (unsigned char)((r_len>>16) & 0xFF);
+-              *loc++ = (unsigned char)((r_len>>8) & 0xFF);
+-              *loc++ = (unsigned char)(r_len & 0xFF);
+-      } else {
+-              r_len -= 5;
+-              *loc++ = 0x84;
+-              *loc++ = (unsigned char)((r_len>>24) & 0xFF);
+-              *loc++ = (unsigned char)((r_len>>16) & 0xFF);
+-              *loc++ = (unsigned char)((r_len>>8) & 0xFF);
+-              *loc++ = (unsigned char)(r_len & 0xFF);
+-      }
+-
+-      *buf = loc;
+-      *len = r_len;
+-      return 0;
+-}
+--- a/lib/management/spnego_mech.h
++++ /dev/null
+@@ -1,48 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-or-later */
+-/*
+- *   Copyright (C) 2020 LG Electronics
+- *
+- *   linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#ifndef _SPNEGO_MECH_H_
+-#define _SPNEGO_MECH_H_
+-
+-enum {
+-      SPNEGO_MECH_MSKRB5      = 0,
+-      SPNEGO_MECH_KRB5,
+-      SPNEGO_MAX_MECHS,
+-};
+-
+-struct spnego_mech_ctx;
+-
+-typedef int (*spnego_encode_t)(char *in_blob, int in_len,
+-                              const unsigned long *oid, int oid_len,
+-                              char **out_blob, int *out_len);
+-
+-struct spnego_mech_operations {
+-      int (*setup)(struct spnego_mech_ctx *mech_ctx);
+-      void (*cleanup)(struct spnego_mech_ctx *mech_ctx);
+-      int (*handle_authen)(struct spnego_mech_ctx *mech_ctx,
+-                              char *in_blob, unsigned int in_len,
+-                              struct ksmbd_spnego_auth_out *auth_out,
+-                              spnego_encode_t encode);
+-};
+-
+-struct spnego_mech_ctx {
+-      const unsigned long     *oid;
+-      int                     oid_len;
+-      void                    *private;
+-      union {
+-              struct {
+-                      void *keytab_name;
+-                      void *service_name;
+-              } krb5;
+-      } params;
+-      struct spnego_mech_operations *ops;
+-};
+-
+-extern struct spnego_mech_operations spnego_krb5_operations;
+-extern struct spnego_mech_operations spnego_mskrb5_operations;
+-
+-#endif
+--- /dev/null
++++ b/tools/asn1.c
+@@ -0,0 +1,391 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in
++ * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
++ *
++ * Copyright (c) 2000 RP Internet (www.rpi.net.au).
++ */
++
++/*****************************************************************************
++ *
++ * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse)
++ *
++ *****************************************************************************/
++#include <stdlib.h>
++#include <limits.h>
++#include <string.h>
++#include <errno.h>
++#include <glib.h>
++
++#include "asn1.h"
++
++void
++asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len)
++{
++      ctx->begin = buf;
++      ctx->end = buf + len;
++      ctx->pointer = buf;
++      ctx->error = ASN1_ERR_NOERROR;
++}
++
++static unsigned char
++asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch)
++{
++      if (ctx->pointer >= ctx->end) {
++              ctx->error = ASN1_ERR_DEC_EMPTY;
++              return 0;
++      }
++      *ch = *(ctx->pointer)++;
++      return 1;
++}
++
++static unsigned char
++asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag)
++{
++      unsigned char ch;
++
++      *tag = 0;
++
++      do {
++              if (!asn1_octet_decode(ctx, &ch))
++                      return 0;
++              *tag <<= 7;
++              *tag |= ch & 0x7F;
++      } while ((ch & 0x80) == 0x80);
++      return 1;
++}
++
++static unsigned char
++asn1_id_decode(struct asn1_ctx *ctx,
++             unsigned int *cls, unsigned int *con, unsigned int *tag)
++{
++      unsigned char ch;
++
++      if (!asn1_octet_decode(ctx, &ch))
++              return 0;
++
++      *cls = (ch & 0xC0) >> 6;
++      *con = (ch & 0x20) >> 5;
++      *tag = (ch & 0x1F);
++
++      if (*tag == 0x1F) {
++              if (!asn1_tag_decode(ctx, tag))
++                      return 0;
++      }
++      return 1;
++}
++
++static unsigned char
++asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len)
++{
++      unsigned char ch, cnt;
++
++      if (!asn1_octet_decode(ctx, &ch))
++              return 0;
++
++      if (ch == 0x80)
++              *def = 0;
++      else {
++              *def = 1;
++
++              if (ch < 0x80)
++                      *len = ch;
++              else {
++                      cnt = (unsigned char) (ch & 0x7F);
++                      *len = 0;
++
++                      while (cnt > 0) {
++                              if (!asn1_octet_decode(ctx, &ch))
++                                      return 0;
++                              *len <<= 8;
++                              *len |= ch;
++                              cnt--;
++                      }
++              }
++      }
++
++      /* don't trust len bigger than ctx buffer */
++      if (*len > ctx->end - ctx->pointer)
++              return 0;
++
++      return 1;
++}
++
++unsigned char
++asn1_header_decode(struct asn1_ctx *ctx,
++                 unsigned char **eoc,
++                 unsigned int *cls, unsigned int *con, unsigned int *tag)
++{
++      unsigned int def = 0;
++      unsigned int len = 0;
++
++      if (!asn1_id_decode(ctx, cls, con, tag))
++              return 0;
++
++      if (!asn1_length_decode(ctx, &def, &len))
++              return 0;
++
++      /* primitive shall be definite, indefinite shall be constructed */
++      if (*con == ASN1_PRI && !def)
++              return 0;
++
++      if (def)
++              *eoc = ctx->pointer + len;
++      else
++              *eoc = NULL;
++      return 1;
++}
++
++static unsigned char
++asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc)
++{
++      unsigned char ch;
++
++      if (eoc == NULL) {
++              if (!asn1_octet_decode(ctx, &ch))
++                      return 0;
++
++              if (ch != 0x00) {
++                      ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
++                      return 0;
++              }
++
++              if (!asn1_octet_decode(ctx, &ch))
++                      return 0;
++
++              if (ch != 0x00) {
++                      ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
++                      return 0;
++              }
++              return 1;
++      }
++
++      if (ctx->pointer != eoc) {
++              ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
++              return 0;
++      }
++      return 1;
++}
++
++unsigned char
++asn1_octets_decode(struct asn1_ctx *ctx,
++                 unsigned char *eoc,
++                 unsigned char **octets, unsigned int *len)
++{
++      unsigned char *ptr;
++
++      *len = 0;
++
++      *octets = malloc(eoc - ctx->pointer);
++      if (*octets == NULL)
++              return 0;
++
++      ptr = *octets;
++      while (ctx->pointer < eoc) {
++              if (!asn1_octet_decode(ctx, (unsigned char *) ptr++)) {
++                      free(*octets);
++                      *octets = NULL;
++                      return 0;
++              }
++              (*len)++;
++      }
++      return 1;
++}
++
++unsigned char asn1_read(struct asn1_ctx *ctx,
++              unsigned char **buf, unsigned int len)
++{
++      *buf = NULL;
++      if (ctx->end - ctx->pointer < len) {
++              ctx->error = ASN1_ERR_DEC_EMPTY;
++              return 0;
++      }
++
++      *buf = malloc(len);
++      if (!*buf)
++              return 0;
++      memcpy(*buf, ctx->pointer, len);
++      ctx->pointer += len;
++      return 1;
++}
++
++static unsigned char
++asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid)
++{
++      unsigned char ch;
++
++      *subid = 0;
++
++      do {
++              if (!asn1_octet_decode(ctx, &ch))
++                      return 0;
++
++              *subid <<= 7;
++              *subid |= ch & 0x7F;
++      } while ((ch & 0x80) == 0x80);
++      return 1;
++}
++
++int
++asn1_oid_decode(struct asn1_ctx *ctx,
++              unsigned char *eoc, unsigned long **oid, unsigned int *len)
++{
++      unsigned long subid;
++      unsigned int size;
++      unsigned long *optr;
++
++      size = eoc - ctx->pointer + 1;
++
++      /* first subid actually encodes first two subids */
++      if (size < 2 || size > UINT_MAX/sizeof(unsigned long))
++              return 0;
++
++      *oid = g_try_malloc0_n(size, sizeof(unsigned long));
++      if (*oid == NULL)
++              return 0;
++
++      optr = *oid;
++
++      if (!asn1_subid_decode(ctx, &subid)) {
++              g_free(*oid);
++              *oid = NULL;
++              return 0;
++      }
++
++      if (subid < 40) {
++              optr[0] = 0;
++              optr[1] = subid;
++      } else if (subid < 80) {
++              optr[0] = 1;
++              optr[1] = subid - 40;
++      } else {
++              optr[0] = 2;
++              optr[1] = subid - 80;
++      }
++
++      *len = 2;
++      optr += 2;
++
++      while (ctx->pointer < eoc) {
++              if (++(*len) > size) {
++                      ctx->error = ASN1_ERR_DEC_BADVALUE;
++                      g_free(*oid);
++                      *oid = NULL;
++                      return 0;
++              }
++
++              if (!asn1_subid_decode(ctx, optr++)) {
++                      g_free(*oid);
++                      *oid = NULL;
++                      return 0;
++              }
++      }
++      return 1;
++}
++
++/* return the size of @depth-nested headers + payload */
++int asn1_header_len(unsigned int payload_len, int depth)
++{
++      unsigned int len;
++      int i;
++
++      len = payload_len;
++      for (i = 0; i < depth; i++) {
++              /* length */
++              if (len >= (1 << 24))
++                      len += 5;
++              else if (len >= (1 << 16))
++                      len += 4;
++              else if (len >= (1 << 8))
++                      len += 3;
++              else if (len >= (1 << 7))
++                      len += 2;
++              else
++                      len += 1;
++              /* 1-byte header */
++              len += 1;
++      }
++      return len;
++}
++
++int asn1_oid_encode(const unsigned long *in_oid, int in_len,
++                      unsigned char **out_oid, int *out_len)
++{
++      unsigned char *oid;
++      unsigned long id;
++      int i;
++
++      *out_oid = g_try_malloc0_n(in_len, 5);
++      if (*out_oid == NULL)
++              return -ENOMEM;
++
++      oid = *out_oid;
++      *oid++ = (unsigned char)(40 * in_oid[0] + in_oid[1]);
++      for (i = 2; i < in_len; i++) {
++              id = in_oid[i];
++              if (id >= (1 << 28))
++                      *oid++ = (0x80 | ((id>>28) & 0x7F));
++              if (id >= (1 << 21))
++                      *oid++ = (0x80 | ((id>>21) & 0x7F));
++              if (id >= (1 << 14))
++                      *oid++ = (0x80 | ((id>>14) & 0x7F));
++              if (id >= (1 << 7))
++                      *oid++ = (0x80 | ((id>>7) & 0x7F));
++              *oid++ = id & 0x7F;
++      }
++      *out_len = (int)(oid - *out_oid);
++      return 0;
++}
++
++/*
++ * @len is the sum of all sizes of header, length and payload.
++ * it will be decreased by the sum of sizes of header and length.
++ */
++int asn1_header_encode(unsigned char **buf,
++                      unsigned int cls, unsigned int con, unsigned int tag,
++                      unsigned int *len)
++{
++      unsigned char *loc;
++      unsigned int r_len;
++
++      /* at least, 1-byte header + 1-byte length is needed. */
++      if (*len < 2)
++              return -EINVAL;
++
++      loc = *buf;
++      r_len = *len;
++
++      *loc++ = ((cls & 0x3) << 6) | ((con & 0x1) << 5) | (tag & 0x1F);
++      r_len -= 1;
++
++      if (r_len - 1 < (1 << 7)) {
++              r_len -= 1;
++              *loc++ = (unsigned char)(r_len & 0x7F);
++      } else if (r_len - 2 < (1 << 8)) {
++              r_len -= 2;
++              *loc++ = 0x81;
++              *loc++ = (unsigned char)(r_len & 0xFF);
++      } else if (r_len - 3 < (1 << 16)) {
++              r_len -= 3;
++              *loc++ = 0x82;
++              *loc++ = (unsigned char)((r_len>>8) & 0xFF);
++              *loc++ = (unsigned char)(r_len & 0xFF);
++      } else if (r_len - 4 < (1 << 24)) {
++              r_len -= 4;
++              *loc++ = 0x83;
++              *loc++ = (unsigned char)((r_len>>16) & 0xFF);
++              *loc++ = (unsigned char)((r_len>>8) & 0xFF);
++              *loc++ = (unsigned char)(r_len & 0xFF);
++      } else {
++              r_len -= 5;
++              *loc++ = 0x84;
++              *loc++ = (unsigned char)((r_len>>24) & 0xFF);
++              *loc++ = (unsigned char)((r_len>>16) & 0xFF);
++              *loc++ = (unsigned char)((r_len>>8) & 0xFF);
++              *loc++ = (unsigned char)(r_len & 0xFF);
++      }
++
++      *buf = loc;
++      *len = r_len;
++      return 0;
++}
+--- /dev/null
++++ b/tools/management/spnego_mech.h
+@@ -0,0 +1,48 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ *   Copyright (C) 2020 LG Electronics
++ *
++ *   linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#ifndef _SPNEGO_MECH_H_
++#define _SPNEGO_MECH_H_
++
++enum {
++      SPNEGO_MECH_MSKRB5      = 0,
++      SPNEGO_MECH_KRB5,
++      SPNEGO_MAX_MECHS,
++};
++
++struct spnego_mech_ctx;
++
++typedef int (*spnego_encode_t)(char *in_blob, int in_len,
++                              const unsigned long *oid, int oid_len,
++                              char **out_blob, int *out_len);
++
++struct spnego_mech_operations {
++      int (*setup)(struct spnego_mech_ctx *mech_ctx);
++      void (*cleanup)(struct spnego_mech_ctx *mech_ctx);
++      int (*handle_authen)(struct spnego_mech_ctx *mech_ctx,
++                              char *in_blob, unsigned int in_len,
++                              struct ksmbd_spnego_auth_out *auth_out,
++                              spnego_encode_t encode);
++};
++
++struct spnego_mech_ctx {
++      const unsigned long     *oid;
++      int                     oid_len;
++      void                    *private;
++      union {
++              struct {
++                      void *keytab_name;
++                      void *service_name;
++              } krb5;
++      } params;
++      struct spnego_mech_operations *ops;
++};
++
++extern struct spnego_mech_operations spnego_krb5_operations;
++extern struct spnego_mech_operations spnego_mskrb5_operations;
++
++#endif
diff --git a/net/ksmbd-tools/patches/030-glib.patch b/net/ksmbd-tools/patches/030-glib.patch
new file mode 100644 (file)
index 0000000..1fb240a
--- /dev/null
@@ -0,0 +1,10 @@
+--- a/meson.build
++++ b/meson.build
+@@ -21,6 +21,7 @@ include_dirs = include_directories(
+ glib_dep = dependency(
+   'glib-2.0',
+   version: '>= 2.40',
++  static: true,
+ )
+ libnl_dep = dependency(
+   'libnl-genl-3.0',