Merge PR #58376 into main

* refs/pull/58376/head:
	Temporarily change the libcephfs dependencies
	proxy: Add the design document
	proxy: Add the proxy to the deb builds
	proxy: Add the proxy to the rpm builds
	Initial version of the libcephfs proxy

Reviewed-by: Sachin Prabhu <sp@spui.uk>
Reviewed-by: Venky Shankar <vshankar@redhat.com>
This commit is contained in:
Venky Shankar 2024-12-13 13:24:05 +05:30
commit 955bf28fe4
26 changed files with 6243 additions and 0 deletions

View File

@ -188,3 +188,4 @@ robbat2 Robin H. Johnson <robbat2@orbis-terrarum.net>
leonid-s-usov Leonid Usov <leonid.usov@ibm.com>
ffilz Frank S. Filz <ffilzlnx@mindspring.com>
Jayaprakash-ibm Jaya Prakash Madaka <jayaprakash@ibm.com>
spuiuk Sachin Prabhu <sp@spui.uk>

View File

@ -1186,18 +1186,41 @@ Obsoletes: libcephfs1 < %{_epoch_prefix}%{version}-%{release}
Obsoletes: ceph-libs < %{_epoch_prefix}%{version}-%{release}
Obsoletes: ceph-libcephfs < %{_epoch_prefix}%{version}-%{release}
%endif
Recommends: libcephfs-proxy2 = %{_epoch_prefix}%{version}-%{release}
Requires: libcephfs-daemon
%description -n libcephfs2
Ceph is a distributed network file system designed to provide excellent
performance, reliability, and scalability. This is a shared library
allowing applications to access a Ceph distributed file system via a
POSIX-like interface.
%package -n libcephfs-proxy2
Summary: Proxy for libcephfs
%if 0%{?suse_version}
Group: System/Libraries
%endif
Recommends: libcephfs-daemon = %{_epoch_prefix}%{version}-%{release}
%description -n libcephfs-proxy2
This package contains the libcephfs_proxy.so library that allow applications
to share cephfs mounts to reduce resource consumption.
%package -n libcephfs-daemon
Summary: Deamon for the libcephfs proxy
%if 0%{?suse_version}
Group: System/Filesystems
%endif
Requires: libcephfs2 = %{_epoch_prefix}%{version}-%{release}
%description -n libcephfs-daemon
This package contains the libcephfsd daemon that allows applications to share
cephfs mounts to reduce resource consumption.
%package -n libcephfs-devel
Summary: Ceph distributed file system headers
%if 0%{?suse_version}
Group: Development/Libraries/C and C++
%endif
Requires: libcephfs2 = %{_epoch_prefix}%{version}-%{release}
Requires: libcephfs-proxy2 = %{_epoch_prefix}%{version}-%{release}
Requires: librados-devel = %{_epoch_prefix}%{version}-%{release}
Obsoletes: ceph-devel < %{_epoch_prefix}%{version}-%{release}
Provides: libcephfs2-devel = %{_epoch_prefix}%{version}-%{release}
@ -2524,6 +2547,16 @@ fi
%postun -n libcephfs2 -p /sbin/ldconfig
%files -n libcephfs-proxy2
%{_libdir}/libcephfs_proxy.so.*
%post -n libcephfs-proxy2 -p /sbin/ldconfig
%postun -n libcephfs-proxy2 -p /sbin/ldconfig
%files -n libcephfs-daemon
%{_sbindir}/libcephfsd
%files -n libcephfs-devel
%dir %{_includedir}/cephfs
%{_includedir}/cephfs/libcephfs.h
@ -2532,6 +2565,7 @@ fi
%dir %{_includedir}/cephfs/metrics
%{_includedir}/cephfs/metrics/Types.h
%{_libdir}/libcephfs.so
%{_libdir}/libcephfs_proxy.so
%files -n python%{python3_pkgversion}-cephfs
%{python3_sitearch}/cephfs.cpython*.so

2
debian/.gitignore vendored
View File

@ -38,4 +38,6 @@
/python-cephfs
/libcephfs-java
/libcephfs-jni
/libcephfs-proxy0-dbg
/libcephfs-proxy0
/tmp

52
debian/control vendored
View File

@ -891,6 +891,7 @@ Conflicts: libceph,
Replaces: libceph,
libceph1,
libcephfs,
Recommends: libcephfs-proxy2 (= ${binary:Version})
Architecture: linux-any
Section: libs
Depends: ${misc:Depends},
@ -919,10 +920,61 @@ Description: debugging symbols for libcephfs2
.
This package contains debugging symbols for libcephfs2.
Package: libcephfs-proxy2
Architecture: linux-any
Section: libs
Depends: ${misc:Depends},
${shlibs:Depends},
Recommends: libcephfs-daemon (= ${binary:Version})
Description: Libcephfs proxy library
Ceph is a massively scalable, open-source, distributed
storage system that runs on commodity hardware and delivers object,
block and file system storage. This allows applications to share
libcephfs' CephFS mounts to reduce resource consumption.
Package: libcephfs-proxy2-dbg
Architecture: linux-any
Section: debug
Priority: extra
Depends: libcephfs-proxy2 (= ${binary:Version}),
${misc:Depends},
Description: debugging symbols for libcephfs-proxy2
Ceph is a massively scalable, open-source, distributed
storage system that runs on commodity hardware and delivers object,
block and file system storage. This allows applications to share
libcephfs' CephFS mounts to reduce resource consumption.
.
This package contains debugging symbols for libcephfs-proxy2.
Package: libcephfs-daemon
Architecture: linux-any
Depends: libcephfs2 (= ${binary:Version}),
${misc:Depends},
Description: Libcephfs proxy daemon
Ceph is a massively scalable, open-source, distributed
storage system that runs on commodity hardware and delivers object,
block and file system storage. This allows applications to share
libcephfs' CephFS mounts to reduce resource consumption.
Package: libcephfs-daemon-dbg
Architecture: linux-any
Section: debug
Priority: extra
Depends: libcephfs-daemon (= ${binary:Version}),
${misc:Depends},
Description: debugging symbols for libcephfs-daemon
Ceph is a massively scalable, open-source, distributed
storage system that runs on commodity hardware and delivers object,
block and file system storage. This allows applications to share
libcephfs' CephFS mounts to reduce resource consumption.
.
This package contains debugging symbols for libcephfs-proxy2.
Package: libcephfs-dev
Architecture: linux-any
Section: libdevel
Depends: libcephfs2 (= ${binary:Version}),
libcephfs-proxy2 (= ${binary:Version}),
${misc:Depends},
Conflicts: libceph-dev,
libceph1-dev,

1
debian/libcephfs-daemon.install vendored Normal file
View File

@ -0,0 +1 @@
usr/sbin/libcephfsd

View File

@ -3,3 +3,4 @@ usr/include/cephfs/libcephfs.h
usr/include/cephfs/types.h
usr/include/cephfs/metrics/Types.h
usr/lib/libcephfs.so
usr/lib/libcephfs_proxy.so

1
debian/libcephfs-proxy2.install vendored Normal file
View File

@ -0,0 +1 @@
usr/lib/libcephfs_proxy.so.*

2
debian/rules vendored
View File

@ -121,6 +121,8 @@ override_dh_strip:
dh_strip -plibradosstriper1 --dbg-package=libradosstriper1-dbg
dh_strip -plibrbd1 --dbg-package=librbd1-dbg
dh_strip -plibcephfs2 --dbg-package=libcephfs2-dbg
dh_strip -plibcephfs-proxy2 --dbg-package=libcephfs-proxy2-dbg
dh_strip -plibcephfs-daemon --dbg-package=libcephfs-daemon-dbg
dh_strip -plibrgw2 --dbg-package=librgw2-dbg
dh_strip -pradosgw --dbg-package=radosgw-dbg
dh_strip -pceph-test --dbg-package=ceph-test-dbg

289
doc/dev/libcephfs_proxy.rst Normal file
View File

@ -0,0 +1,289 @@
Design of the libcephfs proxy
=============================
Description of the problem
--------------------------
When an application connects to a Ceph volume through the *libcephfs.so*
library, a cache is created locally inside the process. The *libcephfs.so*
implementation already deals with memory usage of the cache and adjusts it so
that it doesn't consume all the available memory. However, if multiple
processes connect to CephFS through different instances of the library, each
one of them will keep a private cache. In this case memory management is not
effective because, even configuring memory limits, the number of libcephfs
instances that can be created is unbounded and they can't work in a coordinated
way to correctly control ressource usage. Due to this, it's relatively easy to
consume all memory when all processes are using data cache intensively. This
causes the OOM killer to terminate those processes.
Proposed solution
-----------------
High level approach
^^^^^^^^^^^^^^^^^^^
The main idea is to create a *libcephfs_proxy.so* library that will provide the
same API as the original *libcephfs.so*, but won't cache any data. This library
can be used by any application currently using *libcephfs.so* in a transparent
way (i.e. no code modification required) just by linking against
*libcephfs_proxy.so* instead of *libcephfs.so*, or even using *LD_PRELOAD*.
A new *libcephfsd* daemon will also be created. This daemon will link against
the real *libcephfs.so* library and will listen for incoming connections on a
UNIX socket.
When an application starts and initiates CephFS requests through the
*libcephfs_proxy.so* library, it will connect to the *libcephfsd* daemon
through the UNIX socket and it will forward all CephFS requests to it. The
daemon will use the real *libcephfs.so* to execute those requests and the
answers will be returned back to the application, potentially caching data in
the *libcephfsd* process itself. All this will happen transparently, without
any knowledge from the application.
The daemon will share low level *libcephfs.so* mounts between different
applications to avoid creating an instance for each application, which would
have the same effect on memory as linking each application directly to the
*libcephfs.so* library. This will be done only if the configuration defined by
the applications is identical. Otherwise new independent instances will still
be created.
Some *libcephfs.so* functions will need to be implemented in an special way
inside the *libcephfsd* daemon to hide the differences caused by sharing the
same mount instance with more than one client (for example chdir/getcwd cannot
rely directly on the ``ceph_chdir()``/``ceph_getcwd()`` of *libcephfs.so*).
Initially, only the subset of the low-level interface functions of
*libcephfs.so* that are used by the Samba's VFS CephFS module will be provided.
Design of common components
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Network protocol
""""""""""""""""
Since the connection through the UNIX socket is to another process that runs on
the same machine and the data we need to pass is quite simple, we'll avoid all
the overhead of generic XDR encoding/decoding and RPC transmissions by using a
very simple serialization implemented in the code itself. For the future we may
consider using cap'n proto (https://capnproto.org), which claims to have zero
overhead for encoding and decoding, and would provide an easy way to support
backward compatibility if the network protocol needs to be modified in the
future.
Design of the *libcephfs_proxy.so* library
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This library will basically connect to the UNIX socket where the *libcephfsd*
daemon is listening, wait for requests coming from the application, serialize
all function arguments and send them to the daemon. Once the daemon responds it
will deserialize the answer and return the result to the application.
Local caching
"""""""""""""
While the main purpose of this library is to avoid independent caches on each
process, some preliminary testing has shown a big performance drop for
workloads based on metadata operations and/or small files when all requests go
through the proxy daemon. To minimize this, metadata caching should be
implemented. Metadata cache is much smaller than data cache and will provide a
good trade-off between memory usage and performance.
To implement caching in a safe way, it's required to correctly invalidate data
before it becomes stale. Currently libcephfs.so provides invalidation
notifications that can be used to implement this, but its semantics are not
fully understood yet, so the cache in the libcephfs_proxy.so library will be
designed and implemented in a future version.
Design of the *libcephfsd* daemon
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The daemon will be a regular process that will centralize libcephfs requests
coming from other processes on the same machine.
Process maintenance
"""""""""""""""""""
Since the process will work as a standalone daemon, a simple systemd unit file
will be provided to manage it as a regular system service. Most probably this
will be integrated inside cephadm in the future.
In case the *libcephfsd* daemon crashes, we'll rely on systemd to restart it.
Special functions
^^^^^^^^^^^^^^^^^
Some functions will need to be handled in a special way inside the *libcephfsd*
daemon to provide correct functionality since forwarding them directly to
*libcephfs.so* could return incorrect results due to the sharing of low-level
mounts.
**Sharing of underlying struct ceph_mount_info**
The main purpose of the proxy is to avoid creating a new mount for each process
when they are accessing the same data. To be able to provide this we need to
"virtualize" the mount points and let the application believe it's using its
own mount when, in fact, it could be using a shared mount.
The daemon will track the Ceph account used to connect to the volume, the
configuration file and any specific configuration changes done before mounting
the volume. Only if all settings are exactly the same as another already
mounted instance, then the mount will be shared. The daemon won't understand
CephFS settings nor any potential dependencies between settings. For this
reason, a very strict comparison will be done: the configuration file needs to
be identical and any other changes made afterwards need to be set to the exact
same value and in the same order so that two configurations can be considered
identical.
The check to determine whether two configurations are identical or not will be
done just before mounting the volume (i.e. ``ceph_mount()``). This means that
during the configuration phase, we may have many simultaneous mounts allocated
but not yet mounted. However only one of them will become a real mount. The
others will remain unmounted and will be eventually destroyed once users
unmount and release them.
The following functions will be affected:
* **ceph_create**
This one will allocate a new ceph_mount_info structure, and the provided id
will be recorded for future comparison of potentially matching mounts.
* **ceph_release**
This one will release an unmounted ceph_mount_info structure. Unmounted
structures won't be shared with anyone else.
* **ceph_conf_read_file**
This one will read the configuration file, compute a checksum and make a
copy. The copy will make sure that there are no changes in the configuration
file since the time the checksum was computed, and the checksum will be
recorded for future comparison of potentially matching mounts.
* **ceph_conf_get**
This one will obtain the requested setting, recording it for future
comparison of potentially matching mounts.
Even though this may seem unnecessary, since the daemon is considering the
configuration as a black box, it could be possible to have some dynamic
setting that could return different values depending on external factors, so
the daemon also requires that any requested setting returns the same value to
consider two configurations identical.
* **ceph_conf_set**
This one will record the modified value for future comparison of potentially
matching mounts.
In normal circumstances, some settings may be set even after having mounted
the volume. The proxy won't allow that to avoid potential interferences with
other clients sharing the same mount.
* **ceph_init**
This one will be a no-op. Calling this function triggers the allocation of
several resources and starts some threads. This is just a waste of resources
if this *ceph_mount_info* structure is not finally mounted because it matches
with an already existing mount.
Only if at the time of mount (i.e. ``ceph_mount()``) there's no match with
already existing mounts, then the mount will be initialized and mounted at
the same time.
* **ceph_select_filesystem**
This one will record the selected file system for future comparison of
potentially matching mounts.
* **ceph_mount**
This one will try to find an active mount that matches with all the
configurations defined for this *ceph_mount_info* structure. If none is
found, it will be mounted. Otherwise, the already existing mount will be
shared with this client.
The unmounted *ceph_mount_info* structures will be kept around associated
with the mounted one.
All "real" mounts will be made against the absolute root of the volume
(i.e. "/") to make sure they can be shared with other clients later,
regardless of whether they use the same mount point or not. This means that
just after mounting, the daemon will need to resolve and store the root inode
of the "virtual" mount point.
The CWD (Current Working Directory) will also be initialized to the same
inode.
* **ceph_unmount**
This one will detach the client from the mounted *ceph_mount_info* structure
and reattach it to one of the associated unmounted structures. If this was
the last user of the mount, it's finally unmounted instead.
After calling this function, the client continues using a private
*ceph_mount_info* structure that is used exclusively by itself, so other
configuration changes and operations can be done safely.
**Confine accesses to the intended mount point**
Since the effective mount point may not match the real mount point, some
functions could be able to return inodes outside of the effective mount point
if not handled with care. To avoid it and provide the result that the user
application expects, we will need to simulate some of them inside the
*libcephfsd* daemon.
There are three special cases to consider:
1. Handling of paths starting with "/"
2. Handling of paths containing ".." (i.e. parent directory)
3. Handling of paths containing symbolic links
When these special paths are found, they need to be handled in a special way to
make sure that the returned inodes are what the client expects.
The following functions will be affected:
* **ceph_ll_lookup**
Lookup accepts ".." as the name to resolve. If the parent directory is the
root of the "virtual" mount point (which may not be the same as the real
mount point), we'll need to return the inode corresponding to the "virtual"
mount point stored at the time of mount, instead of the real parent.
* **ceph_ll_lookup_root**
This one needs to return the root inode stored at the time of mount.
* **ceph_ll_walk**
This one will be completely reimplemented inside the daemon to be able to
correctly parse each path component and symbolic link, and handle "/" and
".." in the correct way.
* **ceph_chdir**
This one will resolve the passed path and store it along the corresponding
inode inside the current "virtual" mount. The real ``ceph_chdir()`` won't be
called.
* **ceph_getcwd**
This one will just return the path stored in the "virtual" mount from
previous ``ceph_chdir()`` calls.
**Handle AT_FDCWD**
Any function that receives a file descriptor could also receive the special
*AT_FDCWD* value. These functions need to check for that value and use the
"virtual" CWD instead.
Testing
-------
The proxy should be transparent to any application already using
*libcephfs.so*. This also applies to testing scripts and applications. So any
existing test against the regular *libcephfs.so* library can also be used to
test the proxy.

View File

@ -857,6 +857,7 @@ if(WITH_LIBCEPHFS)
if(LINUX)
add_subdirectory(mount)
endif()
add_subdirectory(libcephfs_proxy)
endif(WITH_LIBCEPHFS)
if(WITH_LIBCEPHSQLITE)

View File

@ -0,0 +1,18 @@
set(proxy_common_srcs proxy_link.c proxy_log.c)
set(libcephfsd_srcs libcephfsd.c proxy_manager.c proxy_mount.c proxy_helpers.c ${proxy_common_srcs})
set(libcephfs_proxy_srcs libcephfs_proxy.c ${proxy_common_srcs})
add_executable(libcephfsd ${libcephfsd_srcs})
add_library(cephfs_proxy ${CEPH_SHARED} ${libcephfs_proxy_srcs})
target_link_libraries(libcephfsd cephfs ${CRYPTO_LIBS})
if(ENABLE_SHARED)
set_target_properties(cephfs_proxy PROPERTIES
OUTPUT_NAME cephfs_proxy
VERSION 2.0.0
SOVERSION 2)
endif(ENABLE_SHARED)
install(TARGETS libcephfsd DESTINATION ${CMAKE_INSTALL_SBINDIR})
install(TARGETS cephfs_proxy DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@ -0,0 +1,869 @@
#include <stdlib.h>
#include "include/cephfs/libcephfs.h"
#include "proxy_log.h"
#include "proxy_helpers.h"
#include "proxy_requests.h"
/* We override the definition of the ceph_mount_info structure to contain
* internal proxy information. This is already a black box for libcephfs users,
* so this won't be noticed. */
struct ceph_mount_info {
proxy_link_t link;
uint64_t cmount;
};
/* The global_cmount is used to stablish an initial connection to serve requests
* not related to a real cmount, like ceph_version or ceph_userperm_new. */
static struct ceph_mount_info global_cmount = { PROXY_LINK_DISCONNECTED, 0 };
static bool client_stop(proxy_link_t *link)
{
return false;
}
static int32_t proxy_connect(proxy_link_t *link)
{
CEPH_REQ(hello, req, 0, ans, 0);
char *path, *env;
int32_t sd, err;
path = PROXY_SOCKET;
env = getenv(PROXY_SOCKET_ENV);
if (env != NULL) {
path = env;
}
sd = proxy_link_client(link, path, client_stop);
if (sd < 0) {
return sd;
}
req.id = LIBCEPHFS_LIB_CLIENT;
err = proxy_link_send(sd, req_iov, 1);
if (err < 0) {
goto failed;
}
err = proxy_link_recv(sd, ans_iov, 1);
if (err < 0) {
goto failed;
}
proxy_log(LOG_INFO, 0, "Connected to libcephfsd version %d.%d",
ans.major, ans.minor);
if ((ans.major != LIBCEPHFSD_MAJOR) ||
(ans.minor != LIBCEPHFSD_MINOR)) {
err = proxy_log(LOG_ERR, ENOTSUP, "Version not supported");
goto failed;
}
return sd;
failed:
proxy_link_close(link);
return err;
}
static void proxy_disconnect(proxy_link_t *link)
{
proxy_link_close(link);
}
static int32_t proxy_global_connect(void)
{
int32_t err;
err = 0;
if (!proxy_link_is_connected(&global_cmount.link)) {
err = proxy_connect(&global_cmount.link);
}
return err;
}
static int32_t proxy_check(struct ceph_mount_info *cmount, int32_t err,
int32_t result)
{
if (err < 0) {
proxy_disconnect(&cmount->link);
proxy_log(LOG_ERR, err, "Disconnected from libcephfsd");
return err;
}
return result;
}
/* Macros to simplify communication with the server. */
#define CEPH_RUN(_cmount, _op, _req, _ans) \
({ \
int32_t __err = \
CEPH_CALL((_cmount)->link.sd, _op, _req, _ans); \
__err = proxy_check(_cmount, __err, (_ans).header.result); \
__err; \
})
#define CEPH_PROCESS(_cmount, _op, _req, _ans) \
({ \
int32_t __err = -ENOTCONN; \
if (proxy_link_is_connected(&(_cmount)->link)) { \
(_req).cmount = (_cmount)->cmount; \
__err = CEPH_RUN(_cmount, _op, _req, _ans); \
} \
__err; \
})
__public int ceph_chdir(struct ceph_mount_info *cmount, const char *path)
{
CEPH_REQ(ceph_chdir, req, 1, ans, 0);
CEPH_STR_ADD(req, path, path);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_CHDIR, req, ans);
}
__public int ceph_conf_get(struct ceph_mount_info *cmount, const char *option,
char *buf, size_t len)
{
CEPH_REQ(ceph_conf_get, req, 1, ans, 1);
req.size = len;
CEPH_STR_ADD(req, option, option);
CEPH_BUFF_ADD(ans, buf, len);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_CONF_GET, req, ans);
}
__public int ceph_conf_read_file(struct ceph_mount_info *cmount,
const char *path_list)
{
CEPH_REQ(ceph_conf_read_file, req, 1, ans, 0);
CEPH_STR_ADD(req, path, path_list);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_CONF_READ_FILE, req, ans);
}
__public int ceph_conf_set(struct ceph_mount_info *cmount, const char *option,
const char *value)
{
CEPH_REQ(ceph_conf_set, req, 2, ans, 0);
CEPH_STR_ADD(req, option, option);
CEPH_STR_ADD(req, value, value);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_CONF_SET, req, ans);
}
__public int ceph_create(struct ceph_mount_info **cmount, const char *const id)
{
CEPH_REQ(ceph_create, req, 1, ans, 0);
struct ceph_mount_info *ceph_mount;
int32_t sd, err;
ceph_mount = proxy_malloc(sizeof(struct ceph_mount_info));
if (ceph_mount == NULL) {
return -ENOMEM;
}
err = proxy_connect(&ceph_mount->link);
if (err < 0) {
goto failed;
}
sd = err;
CEPH_STR_ADD(req, id, id);
err = CEPH_CALL(sd, LIBCEPHFSD_OP_CREATE, req, ans);
if ((err < 0) || ((err = ans.header.result) < 0)) {
goto failed_link;
}
ceph_mount->cmount = ans.cmount;
*cmount = ceph_mount;
return 0;
failed_link:
proxy_disconnect(&ceph_mount->link);
failed:
proxy_free(ceph_mount);
return err;
}
__public const char *ceph_getcwd(struct ceph_mount_info *cmount)
{
static char cwd[PATH_MAX];
int32_t err;
CEPH_REQ(ceph_getcwd, req, 0, ans, 1);
CEPH_BUFF_ADD(ans, cwd, sizeof(cwd));
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_GETCWD, req, ans);
if (err >= 0) {
return cwd;
}
errno = -err;
return NULL;
}
__public int ceph_init(struct ceph_mount_info *cmount)
{
CEPH_REQ(ceph_init, req, 0, ans, 0);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_INIT, req, ans);
}
__public int ceph_ll_close(struct ceph_mount_info *cmount,
struct Fh *filehandle)
{
CEPH_REQ(ceph_ll_close, req, 0, ans, 0);
req.fh = ptr_value(filehandle);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_CLOSE, req, ans);
}
__public int ceph_ll_create(struct ceph_mount_info *cmount, Inode *parent,
const char *name, mode_t mode, int oflags,
Inode **outp, Fh **fhp, struct ceph_statx *stx,
unsigned want, unsigned lflags,
const UserPerm *perms)
{
CEPH_REQ(ceph_ll_create, req, 1, ans, 1);
int32_t err;
req.userperm = ptr_value(perms);
req.parent = ptr_value(parent);
req.mode = mode;
req.oflags = oflags;
req.want = want;
req.flags = lflags;
CEPH_STR_ADD(req, name, name);
CEPH_BUFF_ADD(ans, stx, sizeof(*stx));
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_CREATE, req, ans);
if (err >= 0) {
*outp = value_ptr(ans.inode);
*fhp = value_ptr(ans.fh);
}
return err;
}
__public int ceph_ll_fallocate(struct ceph_mount_info *cmount, struct Fh *fh,
int mode, int64_t offset, int64_t length)
{
CEPH_REQ(ceph_ll_fallocate, req, 0, ans, 0);
req.fh = ptr_value(fh);
req.mode = mode;
req.offset = offset;
req.length = length;
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_FALLOCATE, req, ans);
}
__public int ceph_ll_fsync(struct ceph_mount_info *cmount, struct Fh *fh,
int syncdataonly)
{
CEPH_REQ(ceph_ll_fsync, req, 0, ans, 0);
req.fh = ptr_value(fh);
req.dataonly = syncdataonly;
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_FSYNC, req, ans);
}
__public int ceph_ll_getattr(struct ceph_mount_info *cmount, struct Inode *in,
struct ceph_statx *stx, unsigned int want,
unsigned int flags, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_getattr, req, 0, ans, 1);
req.userperm = ptr_value(perms);
req.inode = ptr_value(in);
req.want = want;
req.flags = flags;
CEPH_BUFF_ADD(ans, stx, sizeof(*stx));
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_GETATTR, req, ans);
}
__public int ceph_ll_getxattr(struct ceph_mount_info *cmount, struct Inode *in,
const char *name, void *value, size_t size,
const UserPerm *perms)
{
CEPH_REQ(ceph_ll_getxattr, req, 1, ans, 1);
req.userperm = ptr_value(perms);
req.inode = ptr_value(in);
req.size = size;
CEPH_STR_ADD(req, name, name);
CEPH_BUFF_ADD(ans, value, size);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_GETXATTR, req, ans);
}
__public int ceph_ll_link(struct ceph_mount_info *cmount, struct Inode *in,
struct Inode *newparent, const char *name,
const UserPerm *perms)
{
CEPH_REQ(ceph_ll_link, req, 1, ans, 0);
req.userperm = ptr_value(perms);
req.inode = ptr_value(in);
req.parent = ptr_value(newparent);
CEPH_STR_ADD(req, name, name);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_LINK, req, ans);
}
__public int ceph_ll_listxattr(struct ceph_mount_info *cmount, struct Inode *in,
char *list, size_t buf_size, size_t *list_size,
const UserPerm *perms)
{
CEPH_REQ(ceph_ll_listxattr, req, 0, ans, 1);
int32_t err;
req.userperm = ptr_value(perms);
req.inode = ptr_value(in);
req.size = buf_size;
CEPH_BUFF_ADD(ans, list, buf_size);
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_LISTXATTR, req, ans);
if (err >= 0) {
*list_size = ans.size;
}
return err;
}
__public int ceph_ll_lookup(struct ceph_mount_info *cmount, Inode *parent,
const char *name, Inode **out,
struct ceph_statx *stx, unsigned want,
unsigned flags, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_lookup, req, 1, ans, 1);
int32_t err;
req.userperm = ptr_value(perms);
req.parent = ptr_value(parent);
req.want = want;
req.flags = flags;
CEPH_STR_ADD(req, name, name);
CEPH_BUFF_ADD(ans, stx, sizeof(*stx));
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_LOOKUP, req, ans);
if (err >= 0) {
*out = value_ptr(ans.inode);
}
return err;
}
__public int ceph_ll_lookup_inode(struct ceph_mount_info *cmount,
struct inodeno_t ino, Inode **inode)
{
CEPH_REQ(ceph_ll_lookup_inode, req, 0, ans, 0);
int32_t err;
req.ino = ino;
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_LOOKUP_INODE, req, ans);
if (err >= 0) {
*inode = value_ptr(ans.inode);
}
return err;
}
__public int ceph_ll_lookup_root(struct ceph_mount_info *cmount, Inode **parent)
{
CEPH_REQ(ceph_ll_lookup_root, req, 0, ans, 0);
int32_t err;
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_LOOKUP_ROOT, req, ans);
if (err >= 0) {
*parent = value_ptr(ans.inode);
}
return err;
}
__public off_t ceph_ll_lseek(struct ceph_mount_info *cmount,
struct Fh *filehandle, off_t offset, int whence)
{
CEPH_REQ(ceph_ll_lseek, req, 0, ans, 0);
int32_t err;
req.fh = ptr_value(filehandle);
req.offset = offset;
req.whence = whence;
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_LSEEK, req, ans);
if (err >= 0) {
return ans.offset;
}
return err;
}
__public int ceph_ll_mkdir(struct ceph_mount_info *cmount, Inode *parent,
const char *name, mode_t mode, Inode **out,
struct ceph_statx *stx, unsigned want,
unsigned flags, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_mkdir, req, 1, ans, 1);
int32_t err;
req.userperm = ptr_value(perms);
req.parent = ptr_value(parent);
req.mode = mode;
req.want = want;
req.flags = flags;
CEPH_STR_ADD(req, name, name);
CEPH_BUFF_ADD(ans, stx, sizeof(*stx));
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_MKDIR, req, ans);
if (err >= 0) {
*out = value_ptr(ans.inode);
}
return err;
}
__public int ceph_ll_mknod(struct ceph_mount_info *cmount, Inode *parent,
const char *name, mode_t mode, dev_t rdev,
Inode **out, struct ceph_statx *stx, unsigned want,
unsigned flags, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_mknod, req, 1, ans, 1);
int32_t err;
req.userperm = ptr_value(perms);
req.parent = ptr_value(parent);
req.mode = mode;
req.rdev = rdev;
req.want = want;
req.flags = flags;
CEPH_STR_ADD(req, name, name);
CEPH_BUFF_ADD(ans, stx, sizeof(*stx));
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_MKNOD, req, ans);
if (err >= 0) {
*out = value_ptr(ans.inode);
}
return err;
}
__public int ceph_ll_open(struct ceph_mount_info *cmount, struct Inode *in,
int flags, struct Fh **fh, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_open, req, 0, ans, 0);
int32_t err;
req.userperm = ptr_value(perms);
req.inode = ptr_value(in);
req.flags = flags;
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_OPEN, req, ans);
if (err >= 0) {
*fh = value_ptr(ans.fh);
}
return err;
}
__public int ceph_ll_opendir(struct ceph_mount_info *cmount, struct Inode *in,
struct ceph_dir_result **dirpp,
const UserPerm *perms)
{
CEPH_REQ(ceph_ll_opendir, req, 0, ans, 0);
int32_t err;
req.userperm = ptr_value(perms);
req.inode = ptr_value(in);
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_OPENDIR, req, ans);
if (err >= 0) {
*dirpp = value_ptr(ans.dir);
}
return err;
}
__public int ceph_ll_put(struct ceph_mount_info *cmount, struct Inode *in)
{
CEPH_REQ(ceph_ll_put, req, 0, ans, 0);
req.inode = ptr_value(in);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_PUT, req, ans);
}
__public int ceph_ll_read(struct ceph_mount_info *cmount, struct Fh *filehandle,
int64_t off, uint64_t len, char *buf)
{
CEPH_REQ(ceph_ll_read, req, 0, ans, 1);
req.fh = ptr_value(filehandle);
req.offset = off;
req.len = len;
CEPH_BUFF_ADD(ans, buf, len);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_READ, req, ans);
}
__public int ceph_ll_readlink(struct ceph_mount_info *cmount, struct Inode *in,
char *buf, size_t bufsize, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_readlink, req, 0, ans, 1);
req.userperm = ptr_value(perms);
req.inode = ptr_value(in);
req.size = bufsize;
CEPH_BUFF_ADD(ans, buf, bufsize);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_READLINK, req, ans);
}
__public int ceph_ll_releasedir(struct ceph_mount_info *cmount,
struct ceph_dir_result *dir)
{
CEPH_REQ(ceph_ll_releasedir, req, 0, ans, 0);
req.dir = ptr_value(dir);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_RELEASEDIR, req, ans);
}
__public int ceph_ll_removexattr(struct ceph_mount_info *cmount,
struct Inode *in, const char *name,
const UserPerm *perms)
{
CEPH_REQ(ceph_ll_removexattr, req, 1, ans, 0);
req.userperm = ptr_value(perms);
req.inode = ptr_value(in);
CEPH_STR_ADD(req, name, name);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_REMOVEXATTR, req, ans);
}
__public int ceph_ll_rename(struct ceph_mount_info *cmount,
struct Inode *parent, const char *name,
struct Inode *newparent, const char *newname,
const UserPerm *perms)
{
CEPH_REQ(ceph_ll_rename, req, 2, ans, 0);
req.userperm = ptr_value(perms);
req.old_parent = ptr_value(parent);
req.new_parent = ptr_value(newparent);
CEPH_STR_ADD(req, old_name, name);
CEPH_STR_ADD(req, new_name, newname);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_RENAME, req, ans);
}
__public void ceph_rewinddir(struct ceph_mount_info *cmount,
struct ceph_dir_result *dirp)
{
CEPH_REQ(ceph_rewinddir, req, 0, ans, 0);
req.dir = ptr_value(dirp);
CEPH_PROCESS(cmount, LIBCEPHFSD_OP_REWINDDIR, req, ans);
}
__public int ceph_ll_rmdir(struct ceph_mount_info *cmount, struct Inode *in,
const char *name, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_rmdir, req, 1, ans, 0);
req.userperm = ptr_value(perms);
req.parent = ptr_value(in);
CEPH_STR_ADD(req, name, name);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_RMDIR, req, ans);
}
__public int ceph_ll_setattr(struct ceph_mount_info *cmount, struct Inode *in,
struct ceph_statx *stx, int mask,
const UserPerm *perms)
{
CEPH_REQ(ceph_ll_setattr, req, 1, ans, 0);
req.userperm = ptr_value(perms);
req.inode = ptr_value(in);
req.mask = mask;
CEPH_BUFF_ADD(req, stx, sizeof(*stx));
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_SETATTR, req, ans);
}
__public int ceph_ll_setxattr(struct ceph_mount_info *cmount, struct Inode *in,
const char *name, const void *value, size_t size,
int flags, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_setxattr, req, 2, ans, 0);
req.userperm = ptr_value(perms);
req.inode = ptr_value(in);
req.size = size;
req.flags = flags;
CEPH_STR_ADD(req, name, name);
CEPH_BUFF_ADD(req, value, size);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_SETXATTR, req, ans);
}
__public int ceph_ll_statfs(struct ceph_mount_info *cmount, struct Inode *in,
struct statvfs *stbuf)
{
CEPH_REQ(ceph_ll_statfs, req, 0, ans, 1);
req.inode = ptr_value(in);
CEPH_BUFF_ADD(ans, stbuf, sizeof(*stbuf));
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_STATFS, req, ans);
}
__public int ceph_ll_symlink(struct ceph_mount_info *cmount, Inode *in,
const char *name, const char *value, Inode **out,
struct ceph_statx *stx, unsigned want,
unsigned flags, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_symlink, req, 2, ans, 1);
int32_t err;
req.userperm = ptr_value(perms);
req.parent = ptr_value(in);
req.want = want;
req.flags = flags;
CEPH_STR_ADD(req, name, name);
CEPH_STR_ADD(req, target, value);
CEPH_BUFF_ADD(req, stx, sizeof(*stx));
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_SYMLINK, req, ans);
if (err >= 0) {
*out = value_ptr(ans.inode);
}
return err;
}
__public int ceph_ll_unlink(struct ceph_mount_info *cmount, struct Inode *in,
const char *name, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_unlink, req, 1, ans, 0);
req.userperm = ptr_value(perms);
req.parent = ptr_value(in);
CEPH_STR_ADD(req, name, name);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_UNLINK, req, ans);
}
__public int ceph_ll_walk(struct ceph_mount_info *cmount, const char *name,
Inode **i, struct ceph_statx *stx, unsigned int want,
unsigned int flags, const UserPerm *perms)
{
CEPH_REQ(ceph_ll_walk, req, 1, ans, 1);
int32_t err;
req.userperm = ptr_value(perms);
req.want = want;
req.flags = flags;
CEPH_STR_ADD(req, path, name);
CEPH_BUFF_ADD(ans, stx, sizeof(*stx));
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_WALK, req, ans);
if (err >= 0) {
*i = value_ptr(ans.inode);
}
return err;
}
__public int ceph_ll_write(struct ceph_mount_info *cmount,
struct Fh *filehandle, int64_t off, uint64_t len,
const char *data)
{
CEPH_REQ(ceph_ll_write, req, 1, ans, 0);
req.fh = ptr_value(filehandle);
req.offset = off;
req.len = len;
CEPH_BUFF_ADD(req, data, len);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_LL_WRITE, req, ans);
}
__public int ceph_mount(struct ceph_mount_info *cmount, const char *root)
{
CEPH_REQ(ceph_mount, req, 1, ans, 0);
CEPH_STR_ADD(req, root, root);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_MOUNT, req, ans);
}
__public struct dirent *ceph_readdir(struct ceph_mount_info *cmount,
struct ceph_dir_result *dirp)
{
static struct dirent de;
int32_t err;
CEPH_REQ(ceph_readdir, req, 0, ans, 1);
req.dir = ptr_value(dirp);
CEPH_BUFF_ADD(ans, &de, sizeof(de));
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_READDIR, req, ans);
if (err < 0) {
errno = -err;
return NULL;
}
if (ans.eod) {
return NULL;
}
return &de;
}
__public int ceph_release(struct ceph_mount_info *cmount)
{
CEPH_REQ(ceph_release, req, 0, ans, 0);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_RELEASE, req, ans);
}
__public int ceph_select_filesystem(struct ceph_mount_info *cmount,
const char *fs_name)
{
CEPH_REQ(ceph_select_filesystem, req, 1, ans, 0);
CEPH_STR_ADD(req, fs, fs_name);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_SELECT_FILESYSTEM, req, ans);
}
__public int ceph_unmount(struct ceph_mount_info *cmount)
{
CEPH_REQ(ceph_unmount, req, 0, ans, 0);
return CEPH_PROCESS(cmount, LIBCEPHFSD_OP_UNMOUNT, req, ans);
}
__public void ceph_userperm_destroy(UserPerm *perms)
{
CEPH_REQ(ceph_userperm_destroy, req, 0, ans, 0);
req.userperm = ptr_value(perms);
CEPH_RUN(&global_cmount, LIBCEPHFSD_OP_USERPERM_DESTROY, req, ans);
}
__public UserPerm *ceph_userperm_new(uid_t uid, gid_t gid, int ngids,
gid_t *gidlist)
{
CEPH_REQ(ceph_userperm_new, req, 1, ans, 0);
int32_t err;
req.uid = uid;
req.gid = gid;
req.groups = ngids;
CEPH_BUFF_ADD(req, gidlist, sizeof(gid_t) * ngids);
err = proxy_global_connect();
if (err >= 0) {
err = CEPH_RUN(&global_cmount, LIBCEPHFSD_OP_USERPERM_NEW, req,
ans);
}
if (err >= 0) {
return value_ptr(ans.userperm);
}
errno = -err;
return NULL;
}
__public const char *ceph_version(int *major, int *minor, int *patch)
{
static char cached_version[128];
static int32_t cached_major = -1, cached_minor, cached_patch;
if (cached_major < 0) {
CEPH_REQ(ceph_version, req, 0, ans, 1);
int32_t err;
CEPH_BUFF_ADD(ans, cached_version, sizeof(cached_version));
err = proxy_global_connect();
if (err >= 0) {
err = CEPH_RUN(&global_cmount, LIBCEPHFSD_OP_VERSION,
req, ans);
}
if (err < 0) {
*major = 0;
*minor = 0;
*patch = 0;
return "Unknown";
}
cached_major = ans.major;
cached_minor = ans.minor;
cached_patch = ans.patch;
}
*major = cached_major;
*minor = cached_minor;
*patch = cached_patch;
return cached_version;
}
__public UserPerm *ceph_mount_perms(struct ceph_mount_info *cmount)
{
CEPH_REQ(ceph_mount_perms, req, 0, ans, 0);
int32_t err;
err = CEPH_PROCESS(cmount, LIBCEPHFSD_OP_MOUNT_PERMS, req, ans);
if (err < 0) {
errno = -err;
return NULL;
}
return value_ptr(ans.userperm);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
#ifndef __LIBCEPHFSD_PROXY_H__
#define __LIBCEPHFSD_PROXY_H__
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#define LIBCEPHFSD_MAJOR 0
#define LIBCEPHFSD_MINOR 2
#define LIBCEPHFS_LIB_CLIENT 0xe3e5f0e8 // 'ceph' xor 0x80808080
#define PROXY_SOCKET "/run/libcephfsd.sock"
#define PROXY_SOCKET_ENV "LIBCEPHFSD_SOCKET"
#define offset_of(_type, _field) ((uintptr_t) & ((_type *)0)->_field)
#define container_of(_ptr, _type, _field) \
((_type *)((uintptr_t)(_ptr) - offset_of(_type, _field)))
struct _list;
typedef struct _list list_t;
struct _proxy_buffer_ops;
typedef struct _proxy_buffer_ops proxy_buffer_ops_t;
struct _proxy_buffer;
typedef struct _proxy_buffer proxy_buffer_t;
struct _proxy_output;
typedef struct _proxy_output proxy_output_t;
struct _proxy_log_handler;
typedef struct _proxy_log_handler proxy_log_handler_t;
struct _proxy_worker;
typedef struct _proxy_worker proxy_worker_t;
struct _proxy_manager;
typedef struct _proxy_manager proxy_manager_t;
struct _proxy_link;
typedef struct _proxy_link proxy_link_t;
typedef int32_t (*proxy_output_write_t)(proxy_output_t *);
typedef int32_t (*proxy_output_full_t)(proxy_output_t *);
typedef void (*proxy_log_callback_t)(proxy_log_handler_t *, int32_t, int32_t,
const char *);
typedef void (*proxy_worker_start_t)(proxy_worker_t *);
typedef void (*proxy_worker_destroy_t)(proxy_worker_t *);
typedef int32_t (*proxy_manager_start_t)(proxy_manager_t *);
typedef int32_t (*proxy_link_start_t)(proxy_link_t *, int32_t);
typedef bool (*proxy_link_stop_t)(proxy_link_t *);
struct _list {
list_t *next;
list_t *prev;
};
#endif

View File

@ -0,0 +1,81 @@
#include "proxy_helpers.h"
#include <openssl/evp.h>
static const char hex_digits[] = "0123456789abcdef";
int32_t proxy_hash(uint8_t *hash, size_t size,
int32_t (*feed)(void **, void *, int32_t), void *data)
{
EVP_MD_CTX *ctx;
void *ptr;
uint32_t bytes;
int32_t i, err, len;
if (size < 32) {
return proxy_log(LOG_ERR, ENOBUFS,
"Digest buffer is too small");
}
ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
return proxy_log(LOG_ERR, ENOMEM, "EVP_MD_CTX_new() failed");
}
if (!EVP_DigestInit_ex2(ctx, EVP_sha256(), NULL)) {
err = proxy_log(LOG_ERR, ENOMEM, "EVP_DigestInit_ex2() failed");
goto done;
}
i = 0;
while ((len = feed(&ptr, data, i)) > 0) {
if (!EVP_DigestUpdate(ctx, ptr, len)) {
err = proxy_log(LOG_ERR, ENOMEM,
"EVP_DigestUpdate() failed");
goto done;
}
i++;
}
if (len < 0) {
err = len;
goto done;
}
if (!EVP_DigestFinal_ex(ctx, hash, &bytes)) {
err = proxy_log(LOG_ERR, ENOMEM, "EVP_DigestFinal_ex() failed");
goto done;
}
err = 0;
done:
EVP_MD_CTX_free(ctx);
return err;
}
int32_t proxy_hash_hex(char *digest, size_t size,
int32_t (*feed)(void **, void *, int32_t), void *data)
{
uint8_t hash[32];
int32_t i, err;
if (size < 65) {
return proxy_log(LOG_ERR, ENOBUFS,
"Digest buffer is too small");
}
err = proxy_hash(hash, sizeof(hash), feed, data);
if (err < 0) {
return err;
}
for (i = 0; i < 32; i++) {
*digest++ = hex_digits[hash[i] >> 4];
*digest++ = hex_digits[hash[i] & 15];
}
*digest = 0;
return 0;
}

View File

@ -0,0 +1,311 @@
#ifndef __LIBCEPHFS_PROXY_HELPERS_H__
#define __LIBCEPHFS_PROXY_HELPERS_H__
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <stdint.h>
#include <stdbool.h>
#include "proxy_log.h"
#define __public __attribute__((__visibility__("default")))
#define ptr_value(_ptr) ((uint64_t)(uintptr_t)(_ptr))
#define value_ptr(_val) ((void *)(uintptr_t)(_val))
typedef struct _proxy_random {
uint64_t mask;
uint64_t factor;
uint64_t factor_inv;
uint64_t shift;
} proxy_random_t;
/* Generate a 64-bits random number different than 0. */
static inline uint64_t random_u64(void)
{
uint64_t value;
int32_t i;
do {
value = 0;
for (i = 0; i < 4; i++) {
value <<= 16;
value ^= (random() >> 8) & 0xffff;
}
} while (value == 0);
return value;
}
/* Randomly initialize the data used to scramble pointers. */
static inline void random_init(proxy_random_t *rnd)
{
uint64_t inv;
rnd->mask = random_u64();
/* Generate an odd multiplicative factor different than 1. */
do {
rnd->factor = random_u64() | 1;
} while (rnd->factor == 1);
/* Compute the inverse of 'factor' modulo 2^64. */
inv = rnd->factor & 0x3;
inv *= 0x000000012 - rnd->factor * inv;
inv *= 0x000000102 - rnd->factor * inv;
inv *= 0x000010002 - rnd->factor * inv;
inv *= 0x100000002 - rnd->factor * inv;
rnd->factor_inv = inv * (2 - rnd->factor * inv);
rnd->shift = random_u64();
}
/* Obfuscate a pointer. */
static inline uint64_t random_scramble(proxy_random_t *rnd, uint64_t value)
{
uint32_t bits;
bits = __builtin_popcountll(value);
/* rnd->shift is rotated by the amount of bits set to 1 in the original
* value, and the lowest 6 bits are extracted. This generates a
* pseudo-random number that depends on the number of bits of the
* value. */
bits = ((rnd->shift >> bits) | (rnd->shift << (64 - bits))) & 0x3f;
/* The value is rotated by the amount just computed. */
value = (value << bits) | (value >> (64 - bits));
/* The final result is masked with a random number. */
value ^= rnd->mask;
/* And multiplied by a random factor modulo 2^64. */
return value * rnd->factor;
}
/* Recover a pointer. */
static inline uint64_t random_unscramble(proxy_random_t *rnd, uint64_t value)
{
uint32_t bits;
/* Divide by the random factor (i.e. multiply by the inverse of the
* factor). */
value *= rnd->factor_inv;
/* Remove the mask. */
value ^= rnd->mask;
/* Get the number of bits the pointer was rotated. */
bits = __builtin_popcountll(value);
bits = ((rnd->shift >> bits) | (rnd->shift << (64 - bits))) & 0x3f;
/* Undo the rotation to recover the original value. */
return (value >> bits) | (value << (64 - bits));
}
static inline void *proxy_malloc(size_t size)
{
void *ptr;
ptr = malloc(size);
if (ptr == NULL) {
proxy_log(LOG_ERR, errno, "Failed to allocate memory");
}
return ptr;
}
static inline int32_t proxy_realloc(void **pptr, size_t size)
{
void *ptr;
ptr = realloc(*pptr, size);
if (ptr == NULL) {
return proxy_log(LOG_ERR, errno, "Failed to reallocate memory");
}
*pptr = ptr;
return 0;
}
static inline void proxy_free(void *ptr)
{
free(ptr);
}
static inline char *proxy_strdup(const char *str)
{
char *ptr;
ptr = strdup(str);
if (ptr == NULL) {
proxy_log(LOG_ERR, errno, "Failed to copy a string");
return NULL;
}
return ptr;
}
static inline int32_t proxy_mutex_init(pthread_mutex_t *mutex)
{
int32_t err;
err = pthread_mutex_init(mutex, NULL);
if (err != 0) {
return proxy_log(LOG_ERR, err, "Failed to initialize a mutex");
}
return 0;
}
static inline void proxy_mutex_lock(pthread_mutex_t *mutex)
{
int32_t err;
err = pthread_mutex_lock(mutex);
if (err != 0) {
proxy_abort(err, "Mutex cannot be acquired");
}
}
static inline void proxy_mutex_unlock(pthread_mutex_t *mutex)
{
int32_t err;
err = pthread_mutex_unlock(mutex);
if (err != 0) {
proxy_abort(err, "Mutex cannot be released");
}
}
static inline int32_t proxy_rwmutex_init(pthread_rwlock_t *mutex)
{
int32_t err;
err = pthread_rwlock_init(mutex, NULL);
if (err != 0) {
return proxy_log(LOG_ERR, err,
"Failed to initialize a rwmutex");
}
return 0;
}
static inline void proxy_rwmutex_rdlock(pthread_rwlock_t *mutex)
{
int32_t err;
err = pthread_rwlock_rdlock(mutex);
if (err != 0) {
proxy_abort(err, "RWMutex cannot be acquired for read");
}
}
static inline void proxy_rwmutex_wrlock(pthread_rwlock_t *mutex)
{
int32_t err;
err = pthread_rwlock_wrlock(mutex);
if (err != 0) {
proxy_abort(err, "RWMutex cannot be acquired for write");
}
}
static inline void proxy_rwmutex_unlock(pthread_rwlock_t *mutex)
{
int32_t err;
err = pthread_rwlock_unlock(mutex);
if (err != 0) {
proxy_abort(err, "RWMutex cannot be released");
}
}
static inline int32_t proxy_condition_init(pthread_cond_t *condition)
{
int32_t err;
err = pthread_cond_init(condition, NULL);
if (err != 0) {
return proxy_log(LOG_ERR, err,
"Failed to initialize a condition variable");
}
return 0;
}
static inline void proxy_condition_signal(pthread_cond_t *condition)
{
int32_t err;
err = pthread_cond_signal(condition);
if (err != 0) {
proxy_abort(err, "Condition variable cannot be signaled");
}
}
static inline void proxy_condition_wait(pthread_cond_t *condition,
pthread_mutex_t *mutex)
{
int32_t err;
err = pthread_cond_wait(condition, mutex);
if (err != 0) {
proxy_abort(err, "Condition variable cannot be waited");
}
}
static inline int32_t proxy_thread_create(pthread_t *tid,
void *(*start)(void *), void *arg)
{
int32_t err;
err = pthread_create(tid, NULL, start, arg);
if (err != 0) {
proxy_log(LOG_ERR, err, "Failed to create a thread");
}
return err;
}
static inline void proxy_thread_kill(pthread_t tid, int32_t signum)
{
int32_t err;
err = pthread_kill(tid, signum);
if (err != 0) {
proxy_abort(err, "Failed to send a signal to a thread");
}
}
static inline void proxy_thread_join(pthread_t tid)
{
int32_t err;
err = pthread_join(tid, NULL);
if (err != 0) {
proxy_log(LOG_ERR, err, "Unable to join a thread");
}
}
static inline int32_t proxy_signal_set(int32_t signum, struct sigaction *action,
struct sigaction *old)
{
if (sigaction(signum, action, old) < 0) {
return proxy_log(LOG_ERR, errno,
"Failed to configure a signal");
}
return 0;
}
int32_t proxy_hash(uint8_t *hash, size_t size,
int32_t (*feed)(void **, void *, int32_t), void *data);
int32_t proxy_hash_hex(char *digest, size_t size,
int32_t (*feed)(void **, void *, int32_t), void *data);
#endif

View File

@ -0,0 +1,421 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/uio.h>
#include "proxy_link.h"
#include "proxy_manager.h"
#include "proxy_helpers.h"
#include "proxy_log.h"
static int32_t iov_length(struct iovec *iov, int32_t count)
{
int32_t len;
len = 0;
while (count > 0) {
len += iov->iov_len;
iov++;
count--;
}
return len;
}
static int32_t proxy_link_prepare(struct sockaddr_un *addr, const char *path)
{
struct sigaction action;
int32_t sd, len, err;
memset(&action, 0, sizeof(action));
action.sa_handler = SIG_IGN;
err = proxy_signal_set(SIGPIPE, &action, NULL);
if (err < 0) {
return err;
}
memset(addr, 0, sizeof(*addr));
addr->sun_family = AF_UNIX;
len = snprintf(addr->sun_path, sizeof(addr->sun_path), "%s", path);
if (len < 0) {
return proxy_log(LOG_ERR, EINVAL,
"Failed to copy Unix socket path");
}
if (len >= sizeof(addr->sun_path)) {
return proxy_log(LOG_ERR, ENAMETOOLONG,
"Unix socket path too long");
}
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd < 0) {
return proxy_log(LOG_ERR, errno,
"Failed to create a Unix socket");
}
return sd;
}
int32_t proxy_link_client(proxy_link_t *link, const char *path,
proxy_link_stop_t stop)
{
struct sockaddr_un addr;
int32_t sd, err;
link->stop = stop;
link->sd = -1;
sd = proxy_link_prepare(&addr, path);
if (sd < 0) {
return sd;
}
err = 0;
while (err >= 0) {
if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if (errno == EINTR) {
continue;
}
err = proxy_log(LOG_ERR, errno,
"Failed to connect to libcephfsd");
} else {
link->sd = sd;
return sd;
}
}
close(sd);
return err;
}
void proxy_link_close(proxy_link_t *link)
{
close(link->sd);
link->sd = -1;
}
int32_t proxy_link_server(proxy_link_t *link, const char *path,
proxy_link_start_t start, proxy_link_stop_t stop)
{
struct sockaddr_un addr;
socklen_t len;
int32_t cd, err;
link->stop = stop;
link->sd = -1;
err = proxy_link_prepare(&addr, path);
if (err < 0) {
return err;
}
link->sd = err;
if ((unlink(path) < 0) && (errno != ENOENT) && (errno != ENOTDIR)) {
err = proxy_log(LOG_ERR, errno,
"Failed to remove existing socket");
goto done;
}
if (bind(link->sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
err = proxy_log(LOG_ERR, errno, "Failed to bind Unix socket");
goto done;
}
if (listen(link->sd, SOMAXCONN) < 0) {
err = proxy_log(LOG_ERR, errno,
"Failed to listen from Unix socket");
goto done;
}
while (!stop(link)) {
len = sizeof(addr);
cd = accept(link->sd, (struct sockaddr *)&addr, &len);
if (cd < 0) {
if (errno != EINTR) {
proxy_log(LOG_ERR, errno,
"Failed to accept a connection");
}
} else {
start(link, cd);
}
}
err = 0;
done:
close(link->sd);
return err;
}
int32_t proxy_link_read(proxy_link_t *link, int32_t sd, void *buffer,
int32_t size)
{
ssize_t len;
do {
len = read(sd, buffer, size);
if (len < 0) {
if (errno == EINTR) {
if (link->stop(link)) {
return -EINTR;
}
continue;
}
return proxy_log(LOG_ERR, errno,
"Failed to read from socket");
}
} while (len < 0);
return len;
}
int32_t proxy_link_write(proxy_link_t *link, int32_t sd, void *buffer,
int32_t size)
{
ssize_t len;
int32_t total;
total = size;
while (total > 0) {
len = write(sd, buffer, total);
if (len < 0) {
if (errno == EINTR) {
if (link->stop(link)) {
return -EINTR;
}
continue;
}
return proxy_log(LOG_ERR, errno,
"Failed to write to socket");
}
if (len == 0) {
return proxy_log(LOG_ERR, ENOBUFS,
"No data written to socket");
}
buffer += len;
total -= len;
}
return size;
}
int32_t proxy_link_send(int32_t sd, struct iovec *iov, int32_t count)
{
struct iovec iov_copy[count];
ssize_t len;
int32_t total;
memcpy(iov_copy, iov, sizeof(struct iovec) * count);
iov = iov_copy;
total = 0;
while (count > 0) {
len = writev(sd, iov, count);
if (len < 0) {
return proxy_log(LOG_ERR, errno, "Failed to send data");
}
if (len == 0) {
return proxy_log(LOG_ERR, ENOBUFS, "Partial write");
}
total += len;
while ((count > 0) && (iov->iov_len <= len)) {
len -= iov->iov_len;
iov++;
count--;
}
if (count > 0) {
iov->iov_base += len;
iov->iov_len -= len;
}
}
return total;
}
int32_t proxy_link_recv(int32_t sd, struct iovec *iov, int32_t count)
{
struct iovec iov_copy[count];
ssize_t len;
int32_t total;
memcpy(iov_copy, iov, sizeof(struct iovec) * count);
iov = iov_copy;
total = 0;
while (count > 0) {
len = readv(sd, iov, count);
if (len < 0) {
return proxy_log(LOG_ERR, errno,
"Failed to receive data");
}
if (len == 0) {
return proxy_log(LOG_ERR, ENODATA, "Partial read");
}
total += len;
while ((count > 0) && (iov->iov_len <= len)) {
len -= iov->iov_len;
iov++;
count--;
}
if (count > 0) {
iov->iov_base += len;
iov->iov_len -= len;
}
}
return total;
}
int32_t proxy_link_req_send(int32_t sd, int32_t op, struct iovec *iov,
int32_t count)
{
proxy_link_req_t *req;
req = iov[0].iov_base;
req->header_len = iov[0].iov_len;
req->op = op;
req->data_len = iov_length(iov + 1, count - 1);
return proxy_link_send(sd, iov, count);
}
int32_t proxy_link_req_recv(int32_t sd, struct iovec *iov, int32_t count)
{
proxy_link_req_t *req;
void *buffer;
int32_t err, len, total;
len = iov->iov_len;
iov->iov_len = sizeof(proxy_link_req_t);
err = proxy_link_recv(sd, iov, 1);
if (err < 0) {
return err;
}
total = err;
req = iov->iov_base;
if (req->data_len > 0) {
if (count == 1) {
return proxy_log(LOG_ERR, ENOBUFS,
"Request data is too long");
}
if (iov[1].iov_len < req->data_len) {
buffer = proxy_malloc(req->data_len);
if (buffer == NULL) {
return -ENOMEM;
}
iov[1].iov_base = buffer;
}
iov[1].iov_len = req->data_len;
} else {
count = 1;
}
if (req->header_len > sizeof(proxy_link_req_t)) {
if (len < req->header_len) {
return proxy_log(LOG_ERR, ENOBUFS,
"Request is too long");
}
iov->iov_base += sizeof(proxy_link_req_t);
iov->iov_len = req->header_len - sizeof(proxy_link_req_t);
} else {
iov++;
count--;
if (count == 0) {
return total;
}
}
err = proxy_link_recv(sd, iov, count);
if (err < 0) {
return err;
}
return total + err;
}
int32_t proxy_link_ans_send(int32_t sd, int32_t result, struct iovec *iov,
int32_t count)
{
proxy_link_ans_t *ans;
ans = iov->iov_base;
ans->header_len = iov->iov_len;
ans->flags = 0;
ans->result = result;
ans->data_len = iov_length(iov + 1, count - 1);
return proxy_link_send(sd, iov, count);
}
int32_t proxy_link_ans_recv(int32_t sd, struct iovec *iov, int32_t count)
{
proxy_link_ans_t *ans;
int32_t err, len, total;
len = iov->iov_len;
iov->iov_len = sizeof(proxy_link_ans_t);
err = proxy_link_recv(sd, iov, 1);
if (err < 0) {
return err;
}
total = err;
ans = iov->iov_base;
if (ans->data_len > 0) {
if ((count == 1) || (iov[1].iov_len < ans->data_len)) {
return proxy_log(LOG_ERR, ENOBUFS,
"Answer data is too long");
}
iov[1].iov_len = ans->data_len;
} else {
count = 1;
}
if (ans->header_len > sizeof(proxy_link_ans_t)) {
if (len < ans->header_len) {
return proxy_log(LOG_ERR, ENOBUFS,
"Answer is too long");
}
iov->iov_base += sizeof(proxy_link_ans_t);
iov->iov_len = ans->header_len - sizeof(proxy_link_ans_t);
} else {
iov++;
count--;
if (count == 0) {
return total;
}
}
err = proxy_link_recv(sd, iov, count);
if (err < 0) {
return err;
}
return total + err;
}
int32_t proxy_link_request(int32_t sd, int32_t op, struct iovec *req_iov,
int32_t req_count, struct iovec *ans_iov,
int32_t ans_count)
{
int32_t err;
err = proxy_link_req_send(sd, op, req_iov, req_count);
if (err < 0) {
return err;
}
return proxy_link_ans_recv(sd, ans_iov, ans_count);
}

View File

@ -0,0 +1,67 @@
#ifndef __LIBCEPHFS_PROXY_LINK_H__
#define __LIBCEPHFS_PROXY_LINK_H__
#include <sys/socket.h>
#include <sys/un.h>
#include "proxy.h"
#define PROXY_LINK_DISCONNECTED { NULL, -1 }
struct _proxy_link {
proxy_link_stop_t stop;
int32_t sd;
};
typedef struct _proxy_link_req {
uint16_t header_len;
uint16_t op;
uint32_t data_len;
} proxy_link_req_t;
typedef struct _proxy_link_ans {
uint16_t header_len;
uint16_t flags;
int32_t result;
uint32_t data_len;
} proxy_link_ans_t;
static inline bool proxy_link_is_connected(proxy_link_t *link)
{
return link->sd >= 0;
}
int32_t proxy_link_client(proxy_link_t *link, const char *path,
proxy_link_stop_t stop);
void proxy_link_close(proxy_link_t *link);
int32_t proxy_link_server(proxy_link_t *link, const char *path,
proxy_link_start_t start, proxy_link_stop_t stop);
int32_t proxy_link_read(proxy_link_t *link, int32_t sd, void *buffer,
int32_t size);
int32_t proxy_link_write(proxy_link_t *link, int32_t sd, void *buffer,
int32_t size);
int32_t proxy_link_send(int32_t sd, struct iovec *iov, int32_t count);
int32_t proxy_link_recv(int32_t sd, struct iovec *iov, int32_t count);
int32_t proxy_link_req_send(int32_t sd, int32_t op, struct iovec *iov,
int32_t count);
int32_t proxy_link_req_recv(int32_t sd, struct iovec *iov, int32_t count);
int32_t proxy_link_ans_send(int32_t sd, int32_t result, struct iovec *iov,
int32_t count);
int32_t proxy_link_ans_recv(int32_t sd, struct iovec *iov, int32_t count);
int32_t proxy_link_request(int32_t sd, int32_t op, struct iovec *req_iov,
int32_t req_count, struct iovec *ans_iov,
int32_t ans_count);
#endif

View File

@ -0,0 +1,121 @@
#ifndef __LIBCEPHFS_PROXY_LIST_H__
#define __LIBCEPHFS_PROXY_LIST_H__
#include "proxy.h"
#define LIST_INIT(_list) { _list, _list }
#define list_entry(_ptr, _type, _field) container_of(_ptr, _type, _field)
#define list_first_entry(_list, _type, _field) \
list_entry((_list)->next, _type, _field)
#define list_last_entry(_list, _type, _field) \
list_entry((_list)->prev, _type, _field)
#define list_next_entry(_ptr, _field) \
list_first_entry(&_ptr->_field, __typeof(*_ptr), _field)
#define list_for_each_entry(_ptr, _list, _field) \
for (_ptr = list_first_entry(_list, __typeof(*_ptr), _field); \
&_ptr->_field != _list; _ptr = list_next_entry(_ptr, _field))
static inline void list_init(list_t *list)
{
list->next = list;
list->prev = list;
}
static inline bool list_empty(list_t *list)
{
return list->next == list;
}
static inline void list_add_between(list_t *item, list_t *prev, list_t *next)
{
item->next = next;
item->prev = prev;
prev->next = item;
next->prev = item;
}
static inline void list_add(list_t *item, list_t *list)
{
list_add_between(item, list, list->next);
}
static inline void list_add_tail(list_t *item, list_t *list)
{
list_add_between(item, list->prev, list);
}
static inline void list_del(list_t *list)
{
list->next->prev = list->prev;
list->prev->next = list->next;
}
static inline void list_del_init(list_t *list)
{
list_del(list);
list_init(list);
}
static inline void list_move(list_t *item, list_t *list)
{
list_del(item);
list_add(item, list);
}
static inline void list_move_tail(list_t *item, list_t *list)
{
list_del(item);
list_add_tail(item, list);
}
static inline void list_splice_between(list_t *src, list_t *prev, list_t *next)
{
list_t *first, *last;
first = src->next;
last = src->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
static inline void list_splice(list_t *src, list_t *dst)
{
if (!list_empty(src)) {
list_splice_between(src, dst, dst->next);
}
}
static inline void list_splice_tail(list_t *src, list_t *dst)
{
if (!list_empty(src)) {
list_splice_between(src, dst->prev, dst);
}
}
static inline void list_splice_init(list_t *src, list_t *dst)
{
if (!list_empty(src)) {
list_splice_between(src, dst, dst->next);
list_init(src);
}
}
static inline void list_splice_tail_init(list_t *src, list_t *dst)
{
if (!list_empty(src)) {
list_splice_between(src, dst->prev, dst);
list_init(src);
}
}
#endif

View File

@ -0,0 +1,110 @@
#include <stdio.h>
#include <stdarg.h>
#include "proxy_log.h"
#include "proxy_helpers.h"
#include "proxy_list.h"
#define PROXY_LOG_BUFFER_SIZE 4096
static __thread char proxy_log_buffer[PROXY_LOG_BUFFER_SIZE];
static pthread_rwlock_t proxy_log_mutex = PTHREAD_RWLOCK_INITIALIZER;
static list_t proxy_log_handlers = LIST_INIT(&proxy_log_handlers);
static void proxy_log_write(int32_t level, int32_t err, const char *msg)
{
proxy_log_handler_t *handler;
proxy_rwmutex_rdlock(&proxy_log_mutex);
list_for_each_entry(handler, &proxy_log_handlers, list) {
handler->callback(handler, level, err, msg);
}
proxy_rwmutex_unlock(&proxy_log_mutex);
}
__public void proxy_log_register(proxy_log_handler_t *handler,
proxy_log_callback_t callback)
{
handler->callback = callback;
proxy_rwmutex_wrlock(&proxy_log_mutex);
list_add_tail(&handler->list, &proxy_log_handlers);
proxy_rwmutex_unlock(&proxy_log_mutex);
}
__public void proxy_log_deregister(proxy_log_handler_t *handler)
{
proxy_rwmutex_wrlock(&proxy_log_mutex);
list_del_init(&handler->list);
proxy_rwmutex_unlock(&proxy_log_mutex);
}
static void proxy_log_msg(char *buffer, const char *text)
{
int32_t len;
len = strlen(text) + 1;
memcpy(buffer, text, len);
}
int32_t proxy_log_args(int32_t level, int32_t err, const char *fmt,
va_list args)
{
static __thread bool busy = false;
int32_t len;
if (busy) {
return -err;
}
busy = true;
len = vsnprintf(proxy_log_buffer, sizeof(proxy_log_buffer), fmt, args);
if (len < 0) {
proxy_log_msg(proxy_log_buffer,
"<log message formatting failed>");
} else if (len >= sizeof(proxy_log_buffer)) {
proxy_log_msg(proxy_log_buffer + sizeof(proxy_log_buffer) - 6,
"[...]");
}
proxy_log_write(level, err, proxy_log_buffer);
busy = false;
return -err;
}
int32_t proxy_log(int32_t level, int32_t err, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
err = proxy_log_args(level, err, fmt, args);
va_end(args);
return err;
}
void proxy_abort_args(int32_t err, const char *fmt, va_list args)
{
proxy_log_args(LOG_CRIT, err, fmt, args);
abort();
}
void proxy_abort(int32_t err, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
proxy_abort_args(err, fmt, args);
va_end(args);
}

View File

@ -0,0 +1,28 @@
#ifndef __LIBCEPHFSD_PROXY_LOG_H__
#define __LIBCEPHFSD_PROXY_LOG_H__
#include "proxy.h"
enum { LOG_CRIT, LOG_ERR, LOG_WARN, LOG_INFO, LOG_DBG };
struct _proxy_log_handler {
list_t list;
proxy_log_callback_t callback;
};
int32_t proxy_log_args(int32_t level, int32_t err, const char *fmt,
va_list args);
int32_t proxy_log(int32_t level, int32_t err, const char *fmt, ...);
void proxy_abort_args(int32_t err, const char *fmt, va_list args);
void proxy_abort(int32_t err, const char *fmt, ...);
void proxy_log_register(proxy_log_handler_t *handler,
proxy_log_callback_t callback);
void proxy_log_deregister(proxy_log_handler_t *handler);
#endif

View File

@ -0,0 +1,247 @@
#include <signal.h>
#include "proxy_manager.h"
#include "proxy_helpers.h"
#include "proxy_list.h"
#include "proxy_log.h"
static void proxy_manager_signal_handler(int32_t signum, siginfo_t *info,
void *ctx)
{
}
static void proxy_worker_register(proxy_worker_t *worker)
{
proxy_manager_t *manager;
manager = worker->manager;
proxy_mutex_lock(&manager->mutex);
list_add_tail(&worker->list, &manager->workers);
proxy_mutex_unlock(&manager->mutex);
}
static void proxy_worker_deregister(proxy_worker_t *worker)
{
proxy_manager_t *manager;
manager = worker->manager;
proxy_mutex_lock(&manager->mutex);
list_del_init(&worker->list);
if (list_empty(&manager->workers)) {
proxy_condition_signal(&manager->condition);
}
proxy_mutex_unlock(&manager->mutex);
}
static void proxy_worker_finished(proxy_worker_t *worker)
{
proxy_manager_t *manager;
manager = worker->manager;
proxy_mutex_lock(&manager->mutex);
if (list_empty(&manager->finished)) {
proxy_condition_signal(&manager->condition);
}
list_move_tail(&worker->list, &manager->finished);
proxy_mutex_unlock(&manager->mutex);
}
static void *proxy_worker_start(void *arg)
{
proxy_worker_t *worker;
worker = arg;
worker->start(worker);
proxy_worker_finished(worker);
return NULL;
}
static void *proxy_manager_start(void *arg)
{
proxy_manager_t *manager;
proxy_worker_t *worker;
manager = arg;
proxy_mutex_lock(&manager->mutex);
while (true) {
while (!list_empty(&manager->finished)) {
worker = list_first_entry(&manager->finished,
proxy_worker_t, list);
list_del_init(&worker->list);
proxy_mutex_unlock(&manager->mutex);
proxy_thread_join(worker->tid);
if (worker->destroy != NULL) {
worker->destroy(worker);
}
proxy_mutex_lock(&manager->mutex);
}
if (manager->stop && list_empty(&manager->workers)) {
break;
}
proxy_condition_wait(&manager->condition, &manager->mutex);
}
manager->done = true;
proxy_condition_signal(&manager->condition);
proxy_mutex_unlock(&manager->mutex);
return NULL;
}
static int32_t proxy_manager_init(proxy_manager_t *manager)
{
int32_t err;
list_init(&manager->workers);
list_init(&manager->finished);
manager->stop = false;
manager->done = false;
manager->main_tid = pthread_self();
err = proxy_mutex_init(&manager->mutex);
if (err < 0) {
return err;
}
err = proxy_condition_init(&manager->condition);
if (err < 0) {
pthread_mutex_destroy(&manager->mutex);
}
return err;
}
static void proxy_manager_destroy(proxy_manager_t *manager)
{
pthread_cond_destroy(&manager->condition);
pthread_mutex_destroy(&manager->mutex);
}
static int32_t proxy_manager_setup_signals(struct sigaction *old)
{
struct sigaction action;
/* The CONT signal will be used to wake threads blocked in I/O. */
memset(&action, 0, sizeof(action));
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = proxy_manager_signal_handler;
return proxy_signal_set(SIGCONT, &action, old);
}
static void proxy_manager_restore_signals(struct sigaction *action)
{
proxy_signal_set(SIGCONT, action, NULL);
}
static void proxy_manager_terminate(proxy_manager_t *manager)
{
proxy_worker_t *worker;
proxy_mutex_lock(&manager->mutex);
list_for_each_entry(worker, &manager->workers, list) {
worker->stop = true;
proxy_thread_kill(worker->tid, SIGCONT);
}
while (!manager->done) {
proxy_condition_wait(&manager->condition, &manager->mutex);
}
proxy_mutex_unlock(&manager->mutex);
proxy_thread_join(manager->tid);
}
int32_t proxy_manager_run(proxy_manager_t *manager, proxy_manager_start_t start)
{
struct sigaction old_action;
int32_t err;
err = proxy_manager_init(manager);
if (err < 0) {
return err;
}
err = proxy_manager_setup_signals(&old_action);
if (err < 0) {
goto done_destroy;
}
err = proxy_thread_create(&manager->tid, proxy_manager_start, manager);
if (err < 0) {
goto done_signal;
}
err = start(manager);
proxy_manager_terminate(manager);
done_signal:
proxy_manager_restore_signals(&old_action);
done_destroy:
proxy_manager_destroy(manager);
return err;
}
void proxy_manager_shutdown(proxy_manager_t *manager)
{
proxy_mutex_lock(&manager->mutex);
manager->stop = true;
proxy_condition_signal(&manager->condition);
proxy_mutex_unlock(&manager->mutex);
/* Wake the thread if it was blocked in an I/O operation. */
proxy_thread_kill(manager->main_tid, SIGCONT);
}
int32_t proxy_manager_launch(proxy_manager_t *manager, proxy_worker_t *worker,
proxy_worker_start_t start,
proxy_worker_destroy_t destroy)
{
int32_t err;
worker->manager = manager;
worker->start = start;
worker->destroy = destroy;
worker->stop = false;
proxy_worker_register(worker);
err = proxy_thread_create(&worker->tid, proxy_worker_start, worker);
if (err < 0) {
proxy_worker_deregister(worker);
}
return err;
}

View File

@ -0,0 +1,43 @@
#ifndef __LIBCEPHFSD_PROXY_MANAGER_H__
#define __LIBCEPHFSD_PROXY_MANAGER_H__
#include <pthread.h>
#include "proxy.h"
struct _proxy_worker {
list_t list;
pthread_t tid;
proxy_manager_t *manager;
proxy_worker_start_t start;
proxy_worker_destroy_t destroy;
bool stop;
};
struct _proxy_manager {
list_t workers;
list_t finished;
pthread_t main_tid;
pthread_t tid;
pthread_mutex_t mutex;
pthread_cond_t condition;
bool stop;
bool done;
};
int32_t proxy_manager_run(proxy_manager_t *manager,
proxy_manager_start_t start);
void proxy_manager_shutdown(proxy_manager_t *manager);
int32_t proxy_manager_launch(proxy_manager_t *manager, proxy_worker_t *worker,
proxy_worker_start_t start,
proxy_worker_destroy_t destroy);
static inline bool proxy_manager_stop(proxy_manager_t *manager)
{
return manager->stop;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
#ifndef __LIBCEPHFSD_PROXY_MOUNT_H__
#define __LIBCEPHFSD_PROXY_MOUNT_H__
#include "proxy.h"
#include "proxy_list.h"
#include "include/cephfs/libcephfs.h"
typedef struct _proxy_instance {
uint8_t hash[32];
list_t list;
list_t siblings;
list_t changes;
struct ceph_mount_info *cmount;
struct Inode *root;
bool inited;
bool mounted;
} proxy_instance_t;
typedef struct _proxy_mount {
proxy_instance_t *instance;
UserPerm *perms;
struct Inode *root;
struct Inode *cwd;
char *cwd_path;
uint64_t root_ino;
uint64_t cwd_ino;
uint32_t cwd_path_len;
} proxy_mount_t;
static inline struct ceph_mount_info *proxy_cmount(proxy_mount_t *mount)
{
return mount->instance->cmount;
}
int32_t proxy_inode_ref(proxy_mount_t *mount, uint64_t inode);
int32_t proxy_mount_create(proxy_mount_t **pmount, const char *id);
int32_t proxy_mount_config(proxy_mount_t *mount, const char *config);
int32_t proxy_mount_set(proxy_mount_t *mount, const char *name,
const char *value);
int32_t proxy_mount_get(proxy_mount_t *mount, const char *name, char *value,
size_t size);
int32_t proxy_mount_select(proxy_mount_t *mount, const char *fs);
int32_t proxy_mount_init(proxy_mount_t *mount);
int32_t proxy_mount_mount(proxy_mount_t *mount, const char *root);
int32_t proxy_mount_unmount(proxy_mount_t *mount);
int32_t proxy_mount_release(proxy_mount_t *mount);
int32_t proxy_path_resolve(proxy_mount_t *mount, const char *path,
struct Inode **inode, struct ceph_statx *stx,
uint32_t want, uint32_t flags, UserPerm *perms,
char **realpath);
#endif

View File

@ -0,0 +1,343 @@
#ifndef __LIBCEPHFSD_PROXY_REQUESTS_H__
#define __LIBCEPHFSD_PROXY_REQUESTS_H__
#include "proxy.h"
#include "proxy_link.h"
/* Macros to add and get data from communication buffers. */
#define CEPH_BUFF_ADD(_data, _ptr, _size) \
do { \
_data##_iov[_data##_count].iov_base = (void *)(_ptr); \
_data##_iov[_data##_count].iov_len = (_size); \
_data##_count++; \
} while (0)
#define CEPH_DATA_ADD(_data, _field, _ptr, _size) \
do { \
(_data)._field = (_size); \
CEPH_BUFF_ADD(_data, _ptr, (_data)._field); \
} while (0)
#define CEPH_STR_ADD(_data, _field, _str) \
do { \
if ((_str) != NULL) { \
CEPH_DATA_ADD(_data, _field, _str, strlen(_str) + 1); \
} else { \
(_data)._field = 0; \
} \
} while (0)
#define CEPH_STR_GET(_data, _field, _ptr) \
({ \
const void *__ptr = (_ptr); \
if ((_data)._field == 0) { \
__ptr = NULL; \
} \
__ptr; \
})
#define CEPH_DATA(_name, _data, _data_count) \
proxy_##_name##_##_data##_t _data; \
struct iovec _data##_iov[_data_count + 1]; \
int32_t _data##_count = 0; \
CEPH_BUFF_ADD(_data, &_data, sizeof(_data))
#define CEPH_REQ(_name, _req, _req_count, _ans, _ans_count) \
CEPH_DATA(_name, _req, _req_count); \
CEPH_DATA(_name, _ans, _ans_count)
#define CEPH_CALL(_sd, _op, _req, _ans) \
proxy_link_request((_sd), _op, _req##_iov, _req##_count, _ans##_iov, \
_ans##_count)
#define CEPH_RET(_sd, _res, _ans) \
proxy_link_ans_send((_sd), (_res), _ans##_iov, _ans##_count)
enum {
LIBCEPHFSD_OP_NULL = 0,
LIBCEPHFSD_OP_VERSION,
LIBCEPHFSD_OP_USERPERM_NEW,
LIBCEPHFSD_OP_USERPERM_DESTROY,
LIBCEPHFSD_OP_CREATE,
LIBCEPHFSD_OP_RELEASE,
LIBCEPHFSD_OP_CONF_READ_FILE,
LIBCEPHFSD_OP_CONF_GET,
LIBCEPHFSD_OP_CONF_SET,
LIBCEPHFSD_OP_INIT,
LIBCEPHFSD_OP_SELECT_FILESYSTEM,
LIBCEPHFSD_OP_MOUNT,
LIBCEPHFSD_OP_UNMOUNT,
LIBCEPHFSD_OP_LL_STATFS,
LIBCEPHFSD_OP_LL_LOOKUP,
LIBCEPHFSD_OP_LL_LOOKUP_INODE,
LIBCEPHFSD_OP_LL_LOOKUP_ROOT,
LIBCEPHFSD_OP_LL_PUT,
LIBCEPHFSD_OP_LL_WALK,
LIBCEPHFSD_OP_CHDIR,
LIBCEPHFSD_OP_GETCWD,
LIBCEPHFSD_OP_READDIR,
LIBCEPHFSD_OP_REWINDDIR,
LIBCEPHFSD_OP_LL_OPEN,
LIBCEPHFSD_OP_LL_CREATE,
LIBCEPHFSD_OP_LL_MKNOD,
LIBCEPHFSD_OP_LL_CLOSE,
LIBCEPHFSD_OP_LL_RENAME,
LIBCEPHFSD_OP_LL_LSEEK,
LIBCEPHFSD_OP_LL_READ,
LIBCEPHFSD_OP_LL_WRITE,
LIBCEPHFSD_OP_LL_LINK,
LIBCEPHFSD_OP_LL_UNLINK,
LIBCEPHFSD_OP_LL_GETATTR,
LIBCEPHFSD_OP_LL_SETATTR,
LIBCEPHFSD_OP_LL_FALLOCATE,
LIBCEPHFSD_OP_LL_FSYNC,
LIBCEPHFSD_OP_LL_LISTXATTR,
LIBCEPHFSD_OP_LL_GETXATTR,
LIBCEPHFSD_OP_LL_SETXATTR,
LIBCEPHFSD_OP_LL_REMOVEXATTR,
LIBCEPHFSD_OP_LL_READLINK,
LIBCEPHFSD_OP_LL_SYMLINK,
LIBCEPHFSD_OP_LL_OPENDIR,
LIBCEPHFSD_OP_LL_MKDIR,
LIBCEPHFSD_OP_LL_RMDIR,
LIBCEPHFSD_OP_LL_RELEASEDIR,
LIBCEPHFSD_OP_MOUNT_PERMS,
LIBCEPHFSD_OP_TOTAL_OPS
};
#define CEPH_TYPE_REQ(_name, _fields...) \
struct _proxy_##_name##_req; \
typedef struct _proxy_##_name##_req proxy_##_name##_req_t; \
struct _proxy_##_name##_req { \
_fields \
}
#define CEPH_TYPE_ANS(_name, _fields...) \
struct _proxy_##_name##_ans; \
typedef struct _proxy_##_name##_ans proxy_##_name##_ans_t; \
struct _proxy_##_name##_ans { \
_fields \
}
#define FIELDS(_fields...) _fields
#define REQ(_fields...) FIELDS(proxy_link_req_t header; _fields)
#define REQ_CMOUNT(_fields...) REQ(uint64_t cmount; _fields)
#define ANS(_fields...) FIELDS(proxy_link_ans_t header; _fields)
#define ANS_CMOUNT(_fields...) ANS(uint64_t cmount; _fields)
#define CEPH_TYPE(_name, _req, _ans) \
CEPH_TYPE_REQ(_name, _req); \
CEPH_TYPE_ANS(_name, _ans)
/* Declaration of types used to transder requests and answers. */
CEPH_TYPE(hello, FIELDS(uint32_t id;), FIELDS(int16_t major; int16_t minor;));
CEPH_TYPE(ceph_version, REQ(),
ANS(int32_t major; int32_t minor; int32_t patch; int16_t text;));
CEPH_TYPE(ceph_userperm_new, REQ(uint32_t uid; uint32_t gid; uint32_t groups;),
ANS(uint64_t userperm;));
CEPH_TYPE(ceph_userperm_destroy, REQ(uint64_t userperm;), ANS());
CEPH_TYPE(ceph_create, REQ(int16_t id;), ANS_CMOUNT());
CEPH_TYPE(ceph_release, REQ_CMOUNT(), ANS());
CEPH_TYPE(ceph_conf_read_file, REQ_CMOUNT(uint16_t path;), ANS());
CEPH_TYPE(ceph_conf_get, REQ_CMOUNT(uint32_t size; uint16_t option;),
ANS(uint16_t value;));
CEPH_TYPE(ceph_conf_set, REQ_CMOUNT(uint16_t option; uint16_t value;), ANS());
CEPH_TYPE(ceph_init, REQ_CMOUNT(), ANS());
CEPH_TYPE(ceph_select_filesystem, REQ_CMOUNT(uint16_t fs;), ANS());
CEPH_TYPE(ceph_mount, REQ_CMOUNT(uint16_t root;), ANS());
CEPH_TYPE(ceph_unmount, REQ_CMOUNT(), ANS());
CEPH_TYPE(ceph_ll_statfs, REQ_CMOUNT(uint64_t inode;), ANS());
CEPH_TYPE(ceph_ll_lookup,
REQ_CMOUNT(uint64_t userperm; uint64_t parent; uint32_t want;
uint32_t flags; uint16_t name;),
ANS(uint64_t inode;));
CEPH_TYPE(ceph_ll_lookup_inode, REQ_CMOUNT(struct inodeno_t ino;),
ANS(uint64_t inode;));
CEPH_TYPE(ceph_ll_lookup_root, REQ_CMOUNT(), ANS(uint64_t inode;));
CEPH_TYPE(ceph_ll_put, REQ_CMOUNT(uint64_t inode;), ANS());
CEPH_TYPE(ceph_ll_walk,
REQ_CMOUNT(uint64_t userperm; uint32_t want; uint32_t flags;
uint16_t path;),
ANS(uint64_t inode;));
CEPH_TYPE(ceph_chdir, REQ_CMOUNT(uint16_t path;), ANS());
CEPH_TYPE(ceph_getcwd, REQ_CMOUNT(), ANS(uint16_t path;));
CEPH_TYPE(ceph_readdir, REQ_CMOUNT(uint64_t dir;), ANS(bool eod;));
CEPH_TYPE(ceph_rewinddir, REQ_CMOUNT(uint64_t dir;), ANS());
CEPH_TYPE(ceph_ll_open,
REQ_CMOUNT(uint64_t userperm; uint64_t inode; int32_t flags;),
ANS(uint64_t fh;));
CEPH_TYPE(ceph_ll_create,
REQ_CMOUNT(uint64_t userperm; uint64_t parent; mode_t mode;
int32_t oflags; uint32_t want; uint32_t flags;
uint16_t name;),
ANS(uint64_t inode; uint64_t fh;));
CEPH_TYPE(ceph_ll_mknod,
REQ_CMOUNT(uint64_t userperm; uint64_t parent; mode_t mode;
dev_t rdev; uint32_t want; uint32_t flags; uint16_t name;),
ANS(uint64_t inode;));
CEPH_TYPE(ceph_ll_close, REQ_CMOUNT(uint64_t fh;), ANS());
CEPH_TYPE(ceph_ll_rename,
REQ_CMOUNT(uint64_t userperm; uint64_t old_parent;
uint64_t new_parent; uint16_t old_name;
uint16_t new_name;),
ANS());
CEPH_TYPE(ceph_ll_lseek, REQ_CMOUNT(uint64_t fh; off_t offset; int32_t whence;),
ANS(off_t offset;));
CEPH_TYPE(ceph_ll_read, REQ_CMOUNT(uint64_t fh; int64_t offset; uint64_t len;),
ANS());
CEPH_TYPE(ceph_ll_write, REQ_CMOUNT(uint64_t fh; int64_t offset; uint64_t len;),
ANS());
CEPH_TYPE(ceph_ll_link,
REQ_CMOUNT(uint64_t userperm; uint64_t inode; uint64_t parent;
uint16_t name;),
ANS());
CEPH_TYPE(ceph_ll_unlink,
REQ_CMOUNT(uint64_t userperm; uint64_t parent; uint16_t name;),
ANS());
CEPH_TYPE(ceph_ll_getattr,
REQ_CMOUNT(uint64_t userperm; uint64_t inode; uint32_t want;
uint32_t flags;),
ANS());
CEPH_TYPE(ceph_ll_setattr,
REQ_CMOUNT(uint64_t userperm; uint64_t inode; int32_t mask;), ANS());
CEPH_TYPE(ceph_ll_fallocate,
REQ_CMOUNT(uint64_t fh; int64_t offset; int64_t length;
int32_t mode;),
ANS());
CEPH_TYPE(ceph_ll_fsync, REQ_CMOUNT(uint64_t fh; int32_t dataonly;), ANS());
CEPH_TYPE(ceph_ll_listxattr,
REQ_CMOUNT(uint64_t userperm; uint64_t inode; size_t size;),
ANS(size_t size;));
CEPH_TYPE(ceph_ll_getxattr,
REQ_CMOUNT(uint64_t userperm; uint64_t inode; size_t size;
uint16_t name;),
ANS());
CEPH_TYPE(ceph_ll_setxattr,
REQ_CMOUNT(uint64_t userperm; uint64_t inode; size_t size;
int32_t flags; uint16_t name;),
ANS());
CEPH_TYPE(ceph_ll_removexattr,
REQ_CMOUNT(uint64_t userperm; uint64_t inode; uint16_t name;), ANS());
CEPH_TYPE(ceph_ll_readlink,
REQ_CMOUNT(uint64_t userperm; uint64_t inode; size_t size;), ANS());
CEPH_TYPE(ceph_ll_symlink,
REQ_CMOUNT(uint64_t userperm; uint64_t parent; uint32_t want;
uint32_t flags; uint16_t name; uint16_t target;),
ANS(uint64_t inode;));
CEPH_TYPE(ceph_ll_opendir, REQ_CMOUNT(uint64_t userperm; uint64_t inode;),
ANS(uint64_t dir;));
CEPH_TYPE(ceph_ll_mkdir,
REQ_CMOUNT(uint64_t userperm; uint64_t parent; mode_t mode;
uint32_t want; uint32_t flags; uint16_t name;),
ANS(uint64_t inode;));
CEPH_TYPE(ceph_ll_rmdir,
REQ_CMOUNT(uint64_t userperm; uint64_t parent; uint16_t name;),
ANS());
CEPH_TYPE(ceph_ll_releasedir, REQ_CMOUNT(uint64_t dir;), ANS());
CEPH_TYPE(ceph_mount_perms, REQ_CMOUNT(), ANS(uint64_t userperm;));
typedef union _proxy_req {
proxy_link_req_t header;
proxy_ceph_version_req_t version;
proxy_ceph_userperm_new_req_t userperm_new;
proxy_ceph_userperm_destroy_req_t userperm_destroy;
proxy_ceph_create_req_t create;
proxy_ceph_release_req_t release;
proxy_ceph_conf_read_file_req_t conf_read_file;
proxy_ceph_conf_get_req_t conf_get;
proxy_ceph_conf_set_req_t conf_set;
proxy_ceph_init_req_t init;
proxy_ceph_select_filesystem_req_t select_filesystem;
proxy_ceph_mount_req_t mount;
proxy_ceph_unmount_req_t unmount;
proxy_ceph_ll_statfs_req_t ll_statfs;
proxy_ceph_ll_lookup_req_t ll_lookup;
proxy_ceph_ll_lookup_inode_req_t ll_lookup_inode;
proxy_ceph_ll_lookup_root_req_t ll_lookup_root;
proxy_ceph_ll_put_req_t ll_put;
proxy_ceph_ll_walk_req_t ll_walk;
proxy_ceph_chdir_req_t chdir;
proxy_ceph_getcwd_req_t getcwd;
proxy_ceph_readdir_req_t readdir;
proxy_ceph_rewinddir_req_t rewinddir;
proxy_ceph_ll_open_req_t ll_open;
proxy_ceph_ll_create_req_t ll_create;
proxy_ceph_ll_mknod_req_t ll_mknod;
proxy_ceph_ll_close_req_t ll_close;
proxy_ceph_ll_rename_req_t ll_rename;
proxy_ceph_ll_lseek_req_t ll_lseek;
proxy_ceph_ll_read_req_t ll_read;
proxy_ceph_ll_write_req_t ll_write;
proxy_ceph_ll_link_req_t ll_link;
proxy_ceph_ll_unlink_req_t ll_unlink;
proxy_ceph_ll_getattr_req_t ll_getattr;
proxy_ceph_ll_setattr_req_t ll_setattr;
proxy_ceph_ll_fallocate_req_t ll_fallocate;
proxy_ceph_ll_fsync_req_t ll_fsync;
proxy_ceph_ll_listxattr_req_t ll_listxattr;
proxy_ceph_ll_getxattr_req_t ll_getxattr;
proxy_ceph_ll_setxattr_req_t ll_setxattr;
proxy_ceph_ll_removexattr_req_t ll_removexattr;
proxy_ceph_ll_readlink_req_t ll_readlink;
proxy_ceph_ll_symlink_req_t ll_symlink;
proxy_ceph_ll_opendir_req_t ll_opendir;
proxy_ceph_ll_mkdir_req_t ll_mkdir;
proxy_ceph_ll_rmdir_req_t ll_rmdir;
proxy_ceph_ll_releasedir_req_t ll_releasedir;
proxy_ceph_mount_perms_req_t mount_perms;
} proxy_req_t;
#endif