diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d97927b5..4cf1781c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,8 @@ option(LWS_WITH_FILE_OPS "Support file operations vfs" ON) option(LWS_WITH_DETAILED_LATENCY "Record detailed latency stats for each read and write" OFF) option(LWS_WITH_UDP "Platform supports UDP" ON) option(LWS_WITH_SPAWN "Spawn subprocesses with piped stdin/out/stderr" OFF) +option(LWS_WITH_FSMOUNT "Overlayfs and fallback mounting apis" OFF) + # # to use miniz, enable both LWS_WITH_ZLIB and LWS_WITH_MINIZ @@ -197,6 +199,7 @@ if (LWS_FOR_GITOHASHI) set(LWS_WITH_LWSAC 1) set(LWS_WITH_LEJP_CONF 1) set(LWS_WITH_SPAWN 1) + set(LWS_WITH_FSMOUNT 1) set(LWS_WITH_STRUCT_JSON 1) set(LWS_WITH_STRUCT_SQLITE3 1) endif() @@ -232,6 +235,7 @@ if(LWS_WITH_DISTRO_RECOMMENDED) set(LWS_WITH_STRUCT_JSON 1) set(LWS_WITH_STRUCT_SQLITE3 1) set(LWS_WITH_SPAWN 1) + set(LWS_WITH_FSMOUNT 1) endif() if (NOT LWS_WITH_NETWORK) @@ -561,7 +565,8 @@ set(LWS_LIBEVENT_INCLUDE_DIRS CACHE PATH "Path to the libevent include directory set(LWS_LIBEVENT_LIBRARIES CACHE PATH "Path to the libevent library") set(LWS_GLIB_INCLUDE_DIRS CACHE PATH "Path to the glib include directory") set(LWS_GLIB_LIBRARIES CACHE PATH "Path to the glib library") - +set(LWS_LIBMOUNT_INCLUDE_DIRS CACHE PATH "Path to the libmount include directory") +set(LWS_LIBMOUNT_LIBRARIES CACHE PATH "Path to the libmount library") if (NOT LWS_WITH_SSL) @@ -636,6 +641,23 @@ if (LWS_WITH_HTTP_STREAM_COMPRESSION) set(LWS_WITH_ZLIB 1) endif() +if (LWS_WITH_FSMOUNT AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + if ("${LWS_LIBMOUNT_LIBRARIES}" STREQUAL "" OR "${LWS_LIBMOUNT_INCLUDE_DIRS}" STREQUAL "") + + # libmount paths (this is only on Linux) + # + find_path( LIBMOUNT_INC_PATH NAMES "libmount/libmount.h") + find_library(LIBMOUNT_LIB_PATH NAMES "mount") + else() + set(LIBMOUNT_INC_PATH ${LWS_LIBMOUNT_INCLUDE_DIRS}) + set(LIBMOUNT_LIB_PATH ${LWS_LIBMOUNT_LIBRARIES}) + endif() + + if (NOT LIBMOUNT_INC_PATH OR NOT LIBMOUNT_LIB_PATH) + message(FATAL_ERROR " Unable to find libmount") + endif() +endif() + if (LWS_WITH_ZLIB AND NOT LWS_WITH_BUNDLED_ZLIB) if ("${LWS_ZLIB_LIBRARIES}" STREQUAL "" OR "${LWS_ZLIB_INCLUDE_DIRS}" STREQUAL "") else() @@ -1055,6 +1077,11 @@ if (LWS_WITH_SPAWN) list(APPEND SOURCES lib/misc/spawn.c) endif() +if (LWS_WITH_FSMOUNT AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + list(APPEND SOURCES lib/misc/fsmount.c) +endif() + + if (LWS_WITH_FILE_OPS) list(APPEND SOURCES lib/core/vfs.c) @@ -1961,6 +1988,13 @@ if (LWS_WITH_GLIB) list(APPEND LIB_LIST ${GLIB_LIBRARIES}) endif(LWS_WITH_GLIB) +if (LWS_WITH_FSMOUNT AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message("libmount include dir: ${LIBMOUNT_INC_PATH}") + message("libmount libraries: ${LIBMOUNT_LIB_PATH}") + + include_directories("${LIBMOUNT_INC_PATH}") + list(APPEND LIB_LIST ${LIBMOUNT_LIB_PATH}) +endif() if (LWS_WITH_SQLITE3) diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index 15f5a182d..8c67a5b98 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -111,6 +111,7 @@ #cmakedefine LWS_HAVE_EVBACKEND_LINUXAIO #cmakedefine LWS_WITH_EXTERNAL_POLL #cmakedefine LWS_WITH_FILE_OPS +#cmakedefine LWS_WITH_FSMOUNT #cmakedefine LWS_WITH_FTS #cmakedefine LWS_WITH_GENCRYPTO #cmakedefine LWS_WITH_GENERIC_SESSIONS diff --git a/include/libwebsockets/lws-misc.h b/include/libwebsockets/lws-misc.h index ee180f1c5..1d36923b0 100644 --- a/include/libwebsockets/lws-misc.h +++ b/include/libwebsockets/lws-misc.h @@ -834,3 +834,66 @@ lws_spawn_get_stdfd(struct lws *wsi); #endif +struct lws_fsmount { + const char *layers_path; /* where layers live */ + const char *overlay_path; /* where overlay instantiations live */ + + char mp[256]; /* mountpoint path */ + char ovname[64]; /* unique name for mount instance */ + char distro[64]; /* unique name for layer source */ + +#if defined(__linux__) + const char *layers[4]; /* distro layers, like "base", "env" */ +#endif +}; + +/** + * lws_fsmount_mount() - Mounts an overlayfs stack of layers + * + * \p fsm: struct lws_fsmount specifying the mount layout + * + * This api is able to assemble up to 4 layer directories on to a mountpoint + * using overlayfs mount (Linux only). + * + * Set fsm.layers_path to the base dir where the layers themselves live, the + * entries in fsm.layers[] specifies the relative path to the layer, comprising + * fsm.layers_path/fsm.distro/fsm.layers[], with [0] being the deepest, earliest + * layer and the rest being progressively on top of [0]; NULL indicates the + * layer is unused. + * + * fsm.overlay_path is the base path of the overlayfs instantiations... empty + * dirs must exist at + * + * fsm.overlay_path/overlays/fsm.ovname/work + * fsm.overlay_path/overlays/fsm.ovname/session + * + * Set fsm.mp to the path of an already-existing empty dir that will be the + * mountpoint, this can be whereever you like. + * + * Overlayfs merges the union of all the contributing layers at the mountpoint, + * the mount is writeable but the layer themselves are immutable, all additions + * and changes are stored in + * + * fsm.overlay_path/overlays/fsm.ovname/session + * + * Returns 0 if mounted OK, nonzero if errors. + * + * Retain fsm for use with unmounting. + */ +LWS_VISIBLE LWS_EXTERN int +lws_fsmount_mount(struct lws_fsmount *fsm); + +/** + * lws_fsmount_unmount() - Unmounts an overlayfs dir + * + * \p fsm: struct lws_fsmount specifying the mount layout + * + * Unmounts the mountpoint in fsm.mp. + * + * Delete fsm.overlay_path/overlays/fsm.ovname/session to permanently eradicate + * all changes from the time the mountpoint was in use. + * + * Returns 0 if unmounted OK. + */ +LWS_VISIBLE LWS_EXTERN int +lws_fsmount_unmount(struct lws_fsmount *fsm); diff --git a/lib/misc/fsmount.c b/lib/misc/fsmount.c new file mode 100644 index 000000000..9948d6cf9 --- /dev/null +++ b/lib/misc/fsmount.c @@ -0,0 +1,110 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2020 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicefsme, and/or + * sell copies of the Software, and to permit persofsm to whom the Software is + * furnished to do so, subject to the following conditiofsm: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portiofsm of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Mount and unmount overlayfs mountpoints (linux only) + */ + +#include "private-lib-core.h" +#include + +#include + +#include +#include + +#include +#include +#include +#include + +int +lws_fsmount_mount(struct lws_fsmount *fsm) +{ + struct libmnt_context *ctx; + char opts[512]; + int n, m; + + /* + * Piece together the options for the overlay mount... + */ + + n = lws_snprintf(opts, sizeof(opts), "lowerdir="); + for (m = LWS_ARRAY_SIZE(fsm->layers) - 1; m >= 0; m--) + if (fsm->layers[m]) { + if (n != 9) + opts[n++] = ':'; + + n += lws_snprintf(&opts[n], sizeof(opts) - n, + "%s/%s/%s", fsm->layers_path, + fsm->distro, fsm->layers[m]); + } + + n += lws_snprintf(&opts[n], sizeof(opts) - n, + ",upperdir=%s/overlays/%s/session", + fsm->overlay_path, fsm->ovname); + + n += lws_snprintf(&opts[n], sizeof(opts) - n, + ",workdir=%s/overlays/%s/work", + fsm->overlay_path, fsm->ovname); + + ctx = mnt_new_context(); + if (!ctx) + return 1; + + mnt_context_set_fstype(ctx, "overlay"); + mnt_context_set_options(ctx, opts); + mnt_context_set_mflags(ctx, MS_NOATIME /* |MS_NOEXEC */); + mnt_context_set_target(ctx, fsm->mp); + mnt_context_set_source(ctx, "none"); + + lwsl_notice("%s: mount opts %s\n", __func__, opts); + + m = mnt_context_mount(ctx); + lwsl_notice("%s: mountpoint %s: %d\n", __func__, fsm->mp, m); + + mnt_free_context(ctx); + + return m; +} + +int +lws_fsmount_unmount(struct lws_fsmount *fsm) +{ + struct libmnt_context *ctx; + int m; + + lwsl_notice("%s: %s\n", __func__, fsm->mp); + + ctx = mnt_new_context(); + if (!ctx) + return 1; + + mnt_context_set_target(ctx, fsm->mp); + + m = mnt_context_umount(ctx); + mnt_free_context(ctx); + + fsm->mp[0] = '\0'; + + return m; +}