kdbus: switch over to generic memfd implementation (ABI+API break)

This commit is contained in:
Daniel Mack
2014-08-17 21:47:00 +02:00
parent a6a4f52889
commit a6082d778e
9 changed files with 73 additions and 333 deletions
-4
View File
@@ -2571,7 +2571,6 @@ tests += \
test-bus-kernel \
test-bus-kernel-bloom \
test-bus-kernel-benchmark \
test-bus-memfd \
test-bus-zero-copy \
test-bus-introspect \
test-bus-objects \
@@ -2725,9 +2724,6 @@ test_bus_kernel_benchmark_LDADD = \
libsystemd-internal.la \
libsystemd-shared.la
test_bus_memfd_SOURCES = \
src/libsystemd/sd-bus/test-bus-memfd.c
test_bus_memfd_LDADD = \
libsystemd-internal.la \
libsystemd-shared.la
-40
View File
@@ -362,46 +362,6 @@ ioctl()s are added for a single match strings.
MEMFDS
The "memfd" concept is used for zero-copy data transfers (see
above). memfds are file descriptors to memory chunks of arbitrary
sizes. If you have a memfd you can mmap() it to get access to the data
it contains or write to it. They are comparable to file descriptors to
unlinked files on a tmpfs, or to anonymous memory that one may refer
to with an fd. They have one particular property: they can be
"sealed". A memfd that is "sealed" is protected from alteration. Only
memfds that are currently not mapped and to which a single fd refers
may be sealed (they may also be unsealed in that case).
The concept of "sealing" makes memfds useful for using them as
transport for kdbus messages: only when the receiver knows that the
message it has received cannot change while looking at, it can safely
parse it without having to copy it to a safe memory area. memfds can also
be reused in multiple messages. A sender may send the same memfd to
multiple peers, and since it is sealed, it can be sure that the receiver
will not be able to modify it. "Sealing" hence provides both sides of
a transaction with the guarantee that the data stays constant and is
reusable.
memfds are a generic concept that can be used outside of the immediate
kdbus usecase. You can send them across AF_UNIX sockets too, sealed or
unsealed. In kdbus themselves, they can be used to send zero-copy
payloads, but may also be sent as normal fds.
memfds are allocated with the KDBUS_CMD_MEMFD_NEW ioctl. After allocation,
simply memory map them and write to them. To set their size, use
KDBUS_CMD_MEMFD_SIZE_SET. Note that memfds will be increased in size
automatically if you touch previously unallocated pages. However, the
size will only be increased in multiples of the page size in that
case. Thus, in almost all cases, an explicit KDBUS_CMD_MEMFD_SIZE_SET
is necessary, since it allows setting memfd sizes in finer
granularity. To seal a memfd use the KDBUS_CMD_MEMFD_SEAL_SET ioctl
call. It will only succeed if the caller has the only fd reference to
the memfd open, and if the memfd is currently unmapped.
If memfds are shared, keep in mind that the file pointer used by
write/read/seek is shared too, only pread/pwrite are safe to use
in that case.
memfds may be sent across kdbus via KDBUS_ITEM_PAYLOAD_MEMFD items
attached to messages. If this is done, the data included in the memfd
is considered part of the payload stream of a message, and are treated
+3 -18
View File
@@ -1111,9 +1111,6 @@ int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *al
if (bus->n_memfd_cache <= 0) {
_cleanup_free_ char *g = NULL;
struct kdbus_cmd_memfd_make *cmd;
struct kdbus_item *item;
size_t l, sz;
int r;
assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
@@ -1124,26 +1121,14 @@ int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *al
if (!g)
return -ENOMEM;
l = strlen(g);
sz = ALIGN8(offsetof(struct kdbus_cmd_memfd_make, items)) +
ALIGN8(offsetof(struct kdbus_item, str)) +
l + 1;
cmd = alloca0(sz);
cmd->size = sz;
item = cmd->items;
item->size = ALIGN8(offsetof(struct kdbus_item, str)) + l + 1;
item->type = KDBUS_ITEM_MEMFD_NAME;
memcpy(item->str, g, l + 1);
r = ioctl(bus->input_fd, KDBUS_CMD_MEMFD_NEW, cmd);
r = memfd_create(g, 0, MFD_ALLOW_SEALING);
if (r < 0)
return -errno;
*address = NULL;
*mapped = 0;
*allocated = 0;
return cmd->fd;
return r;
}
c = &bus->memfd_cache[--bus->n_memfd_cache];
@@ -1195,7 +1180,7 @@ void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, si
/* If overly long, let's return a bit to the OS */
if (mapped > max_mapped) {
assert_se(ioctl(fd, KDBUS_CMD_MEMFD_SIZE_SET, &max_mapped) >= 0);
assert_se(ftruncate(fd, max_mapped) >= 0);
assert_se(munmap((uint8_t*) address + max_mapped, PAGE_ALIGN(mapped - max_mapped)) >= 0);
c->mapped = c->allocated = max_mapped;
} else {
+5 -5
View File
@@ -1076,7 +1076,7 @@ static int part_make_space(
uint64_t new_allocated;
new_allocated = PAGE_ALIGN(sz > 0 ? 2 * sz : 1);
r = ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &new_allocated);
r = ftruncate(part->memfd, new_allocated);
if (r < 0) {
m->poisoned = true;
return -errno;
@@ -2546,7 +2546,7 @@ _public_ int sd_bus_message_append_array_memfd(sd_bus_message *m,
if (m->poisoned)
return -ESTALE;
r = sd_memfd_set_sealed(memfd, true);
r = sd_memfd_set_sealed(memfd);
if (r < 0)
return r;
@@ -2606,7 +2606,7 @@ _public_ int sd_bus_message_append_string_memfd(sd_bus_message *m, sd_memfd *mem
assert_return(!m->sealed, -EPERM);
assert_return(!m->poisoned, -ESTALE);
r = sd_memfd_set_sealed(memfd, true);
r = sd_memfd_set_sealed(memfd);
if (r < 0)
return r;
@@ -2799,11 +2799,11 @@ int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) {
/* Then, sync up real memfd size */
sz = part->size;
if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &sz) < 0)
if (ftruncate(part->memfd, sz) < 0)
return -errno;
/* Finally, try to seal */
if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) >= 0)
if (fcntl(part->memfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) >= 0)
part->sealed = true;
}
}
-51
View File
@@ -733,24 +733,6 @@ struct kdbus_cmd_match {
struct kdbus_item items[0];
} __attribute__((aligned(8)));
/**
* struct kdbus_cmd_memfd_make - create a kdbus memfd
* @size: The total size of the struct
* @file_size: The initial file size
* @fd: The returned file descriptor number
* @__pad: Padding to ensure proper alignement
* @items: A list of items for additional information
*
* This structure is used with the KDBUS_CMD_MEMFD_NEW ioctl.
*/
struct kdbus_cmd_memfd_make {
__u64 size;
__u64 file_size;
int fd;
__u32 __pad;
struct kdbus_item items[0];
} __attribute__((aligned(8)));
/**
* enum kdbus_ioctl_type - Ioctl API
* @KDBUS_CMD_BUS_MAKE: After opening the "control" device node, this
@@ -801,32 +783,6 @@ struct kdbus_cmd_memfd_make {
* @KDBUS_CMD_MATCH_ADD: Install a match which broadcast messages should
* be delivered to the connection.
* @KDBUS_CMD_MATCH_REMOVE: Remove a current match for broadcast messages.
* @KDBUS_CMD_MEMFD_NEW: Return a new file descriptor which provides an
* anonymous shared memory file and which can be
* used to pass around larger chunks of data.
* Kdbus memfd files can be sealed, which allows
* the receiver to trust the data it has received.
* Kdbus memfd files expose only very limited
* operations, they can be mmap()ed, seek()ed,
* (p)read(v)() and (p)write(v)(); most other
* common file operations are not implemented.
* Special caution needs to be taken with
* read(v)()/write(v)() on a shared file; the
* underlying file position is always shared
* between all users of the file and race against
* each other, pread(v)()/pwrite(v)() avoid these
* issues.
* @KDBUS_CMD_MEMFD_SIZE_GET: Return the size of the underlying file, which
* changes with write().
* @KDBUS_CMD_MEMFD_SIZE_SET: Truncate the underlying file to the specified
* size.
* @KDBUS_CMD_MEMFD_SEAL_GET: Return the state of the file sealing.
* @KDBUS_CMD_MEMFD_SEAL_SET: Seal or break a seal of the file. Only files
* which are not shared with other processes and
* which are currently not mapped can be sealed.
* The current process needs to be the one and
* single owner of the file, the sealing cannot
* be changed as long as the file is shared.
*/
enum kdbus_ioctl_type {
KDBUS_CMD_BUS_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x00,
@@ -866,13 +822,6 @@ enum kdbus_ioctl_type {
struct kdbus_cmd_match),
KDBUS_CMD_MATCH_REMOVE = _IOW(KDBUS_IOCTL_MAGIC, 0x81,
struct kdbus_cmd_match),
KDBUS_CMD_MEMFD_NEW = _IOWR(KDBUS_IOCTL_MAGIC, 0xc0,
struct kdbus_cmd_memfd_make),
KDBUS_CMD_MEMFD_SIZE_GET = _IOR(KDBUS_IOCTL_MAGIC, 0xc1, __u64 *),
KDBUS_CMD_MEMFD_SIZE_SET = _IOW(KDBUS_IOCTL_MAGIC, 0xc2, __u64 *),
KDBUS_CMD_MEMFD_SEAL_GET = _IOR(KDBUS_IOCTL_MAGIC, 0xc3, int *),
KDBUS_CMD_MEMFD_SEAL_SET = _IO(KDBUS_IOCTL_MAGIC, 0xc4),
};
/*
+20 -34
View File
@@ -26,8 +26,8 @@
#include <sys/prctl.h>
#include "util.h"
#include "kdbus.h"
#include "bus-label.h"
#include "missing.h"
#include "sd-memfd.h"
#include "sd-bus.h"
@@ -39,11 +39,8 @@ struct sd_memfd {
_public_ int sd_memfd_new(sd_memfd **m, const char *name) {
struct kdbus_cmd_memfd_make *cmd;
struct kdbus_item *item;
_cleanup_close_ int kdbus = -1;
_cleanup_free_ char *g = NULL;
size_t sz, l;
sd_memfd *n;
assert_return(m, -EINVAL);
@@ -89,42 +86,28 @@ _public_ int sd_memfd_new(sd_memfd **m, const char *name) {
}
}
l = strlen(name);
sz = ALIGN8(offsetof(struct kdbus_cmd_memfd_make, items)) +
ALIGN8(offsetof(struct kdbus_item, str)) +
l + 1;
cmd = alloca0(sz);
cmd->size = sz;
item = cmd->items;
item->size = ALIGN8(offsetof(struct kdbus_item, str)) + l + 1;
item->type = KDBUS_ITEM_MEMFD_NAME;
memcpy(item->str, name, l + 1);
if (ioctl(kdbus, KDBUS_CMD_MEMFD_NEW, cmd) < 0)
return -errno;
n = new0(struct sd_memfd, 1);
if (!n) {
safe_close(cmd->fd);
if (!n)
return -ENOMEM;
n->fd = memfd_create(name, 0, MFD_ALLOW_SEALING);
if (n->fd < 0) {
free(n);
return -errno;
}
n->fd = cmd->fd;
*m = n;
return 0;
}
_public_ int sd_memfd_new_from_fd(sd_memfd **m, int fd) {
sd_memfd *n;
uint64_t sz;
assert_return(m, -EINVAL);
assert_return(fd >= 0, -EINVAL);
/* Check if this is a valid memfd */
if (ioctl(fd, KDBUS_CMD_MEMFD_SIZE_GET, &sz) < 0)
/* Check if this is a sealable fd */
if (fcntl(fd, F_GET_SEALS) < 0)
return -ENOTTY;
n = new0(struct sd_memfd, 1);
@@ -193,7 +176,7 @@ _public_ int sd_memfd_map(sd_memfd *m, uint64_t offset, size_t size, void **p) {
if (sealed < 0)
return sealed;
q = mmap(NULL, size, sealed ? PROT_READ : PROT_READ|PROT_WRITE, MAP_SHARED, m->fd, offset);
q = mmap(NULL, size, sealed ? PROT_READ : PROT_READ|PROT_WRITE, MAP_PRIVATE, m->fd, offset);
if (q == MAP_FAILED)
return -errno;
@@ -201,12 +184,12 @@ _public_ int sd_memfd_map(sd_memfd *m, uint64_t offset, size_t size, void **p) {
return 0;
}
_public_ int sd_memfd_set_sealed(sd_memfd *m, int b) {
_public_ int sd_memfd_set_sealed(sd_memfd *m) {
int r;
assert_return(m, -EINVAL);
r = ioctl(m->fd, KDBUS_CMD_MEMFD_SEAL_SET, b);
r = fcntl(m->fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
if (r < 0)
return -errno;
@@ -214,27 +197,30 @@ _public_ int sd_memfd_set_sealed(sd_memfd *m, int b) {
}
_public_ int sd_memfd_get_sealed(sd_memfd *m) {
int r, b;
int r;
assert_return(m, -EINVAL);
r = ioctl(m->fd, KDBUS_CMD_MEMFD_SEAL_GET, &b);
r = fcntl(m->fd, F_GET_SEALS);
if (r < 0)
return -errno;
return !!b;
return (r & (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)) ==
(F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
}
_public_ int sd_memfd_get_size(sd_memfd *m, uint64_t *sz) {
int r;
struct stat stat;
assert_return(m, -EINVAL);
assert_return(sz, -EINVAL);
r = ioctl(m->fd, KDBUS_CMD_MEMFD_SIZE_GET, sz);
r = fstat(m->fd, &stat);
if (r < 0)
return -errno;
*sz = stat.st_size;
return r;
}
@@ -243,7 +229,7 @@ _public_ int sd_memfd_set_size(sd_memfd *m, uint64_t sz) {
assert_return(m, -EINVAL);
r = ioctl(m->fd, KDBUS_CMD_MEMFD_SIZE_SET, &sz);
r = ftruncate(m->fd, sz);
if (r < 0)
return -errno;
-180
View File
@@ -1,180 +0,0 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/mman.h>
#include <sys/uio.h>
#include "log.h"
#include "macro.h"
#include "util.h"
#include "sd-memfd.h"
int main(int argc, char *argv[]) {
sd_memfd *m;
char *s, *name;
uint64_t sz;
int r, fd;
FILE *f = NULL;
char buf[3] = {};
struct iovec iov[3] = {};
char bufv[3][3] = {};
log_set_max_level(LOG_DEBUG);
r = sd_memfd_new(&m, NULL);
if (r == -ENOENT)
return EXIT_TEST_SKIP;
assert_se(r >= 0);
assert_se(sd_memfd_get_name(m, &name) >= 0);
log_info("name: %s", name);
free(name);
r = sd_memfd_map(m, 0, 12, (void**) &s);
assert_se(r >= 0);
strcpy(s, "----- world");
r = sd_memfd_set_sealed(m, 1);
assert_se(r == -ETXTBSY);
assert_se(write(sd_memfd_get_fd(m), "he", 2) == 2);
assert_se(write(sd_memfd_get_fd(m), "XXX", 3) == 3);
assert_se(streq(s, "heXXX world"));
/* fix "hello" */
assert_se(lseek(sd_memfd_get_fd(m), 2, SEEK_SET) == 2);
assert_se(write(sd_memfd_get_fd(m), "ll", 2) == 2);
assert_se(sd_memfd_get_file(m, &f) >= 0);
fputc('o', f);
fflush(f);
/* check content */
assert_se(streq(s, "hello world"));
assert_se(munmap(s, 12) == 0);
r = sd_memfd_get_sealed(m);
assert_se(r == 0);
r = sd_memfd_get_size(m, &sz);
assert_se(r >= 0);
assert_se(sz = page_size());
/* truncate it */
r = sd_memfd_set_size(m, 6);
assert_se(r >= 0);
/* get back new value */
r = sd_memfd_get_size(m, &sz);
assert_se(r >= 0);
assert_se(sz == 6);
r = sd_memfd_set_sealed(m, 1);
assert_se(r >= 0);
r = sd_memfd_get_sealed(m);
assert_se(r == 1);
fd = sd_memfd_dup_fd(m);
assert_se(fd >= 0);
sd_memfd_free(m);
/* new sd_memfd, same underlying memfd */
r = sd_memfd_new_from_fd(&m, fd);
assert_se(r >= 0);
/* we did truncate it to 6 */
r = sd_memfd_get_size(m, &sz);
assert_se(r >= 0 && sz == 6);
/* map it, check content */
r = sd_memfd_map(m, 0, 12, (void **)&s);
assert_se(r >= 0);
/* we only see the truncated size */
assert_se(streq(s, "hello "));
/* it was already sealed */
r = sd_memfd_set_sealed(m, 1);
assert_se(r == -EALREADY);
/* we cannot break the seal, it is mapped */
r = sd_memfd_set_sealed(m, 0);
assert_se(r == -ETXTBSY);
/* unmap it; become the single owner */
assert_se(munmap(s, 12) == 0);
/* now we can do flip the sealing */
r = sd_memfd_set_sealed(m, 0);
assert_se(r == 0);
r = sd_memfd_get_sealed(m);
assert_se(r == 0);
r = sd_memfd_set_sealed(m, 1);
assert_se(r == 0);
r = sd_memfd_get_sealed(m);
assert_se(r == 1);
r = sd_memfd_set_sealed(m, 0);
assert_se(r == 0);
r = sd_memfd_get_sealed(m);
assert_se(r == 0);
/* seek at 2, read() 2 bytes */
assert_se(lseek(fd, 2, SEEK_SET) == 2);
assert_se(read(fd, buf, 2) == 2);
/* check content */
assert_se(memcmp(buf, "ll", 2) == 0);
/* writev it out*/
iov[0].iov_base = (char *)"ABC";
iov[0].iov_len = 3;
iov[1].iov_base = (char *)"DEF";
iov[1].iov_len = 3;
iov[2].iov_base = (char *)"GHI";
iov[2].iov_len = 3;
assert_se(pwritev(fd, iov, 3, 0) == 9);
/* readv it back */
iov[0].iov_base = bufv[0];
iov[0].iov_len = 3;
iov[1].iov_base = bufv[1];
iov[1].iov_len = 3;
iov[2].iov_base = bufv[2];
iov[2].iov_len = 3;
assert_se(preadv(fd, iov, 3, 0) == 9);
/* check content */
assert_se(memcmp(bufv[0], "ABC", 3) == 0);
assert_se(memcmp(bufv[1], "DEF", 3) == 0);
assert_se(memcmp(bufv[2], "GHI", 3) == 0);
sd_memfd_free(m);
return 0;
}
+44
View File
@@ -64,6 +64,34 @@
#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
#endif
#ifndef F_ADD_SEALS
#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
#endif
#ifndef F_GET_SEALS
#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
#endif
#ifndef F_SEAL_SEAL
#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
#endif
#ifndef F_SEAL_SHRINK
#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
#endif
#ifndef F_SEAL_GROW
#define F_SEAL_GROW 0x0004 /* prevent file from growing */
#endif
#ifndef F_SEAL_WRITE
#define F_SEAL_WRITE 0x0008 /* prevent writes */
#endif
#ifndef MFD_ALLOW_SEALING
#define MFD_ALLOW_SEALING 0x0002ULL
#endif
#ifndef IP_FREEBIND
#define IP_FREEBIND 15
#endif
@@ -109,6 +137,13 @@ static inline int pivot_root(const char *new_root, const char *put_old) {
# ifndef __NR_fanotify_mark
# define __NR_fanotify_mark 301
# endif
# ifndef __NR_memfd_create
# define __NR_memfd_create 319
# endif
#elif defined __arm__
# ifndef __NR_memfd_create
# define __NR_memfd_create 385
# endif
#elif defined _MIPS_SIM
# if _MIPS_SIM == _MIPS_SIM_ABI32
# ifndef __NR_fanotify_init
@@ -139,6 +174,9 @@ static inline int pivot_root(const char *new_root, const char *put_old) {
# ifndef __NR_fanotify_mark
# define __NR_fanotify_mark 339
# endif
# ifndef __NR_memfd_create
# define __NR_memfd_create 356
# endif
#endif
#ifndef HAVE_FANOTIFY_INIT
@@ -166,6 +204,12 @@ static inline int fanotify_mark(int fanotify_fd, unsigned int flags, uint64_t ma
}
#endif
#ifndef HAVE_MEMFD_CREATE
static inline int memfd_create(const char *name, uint64_t size, uint64_t flags) {
return syscall(__NR_memfd_create, name, size, flags);
}
#endif
#ifndef BTRFS_IOCTL_MAGIC
#define BTRFS_IOCTL_MAGIC 0x94
#endif
+1 -1
View File
@@ -44,7 +44,7 @@ int sd_memfd_get_file(sd_memfd *m, FILE **f);
int sd_memfd_map(sd_memfd *m, uint64_t offset, size_t size, void **p);
int sd_memfd_set_sealed(sd_memfd *m, int b);
int sd_memfd_set_sealed(sd_memfd *m);
int sd_memfd_get_sealed(sd_memfd *m);
int sd_memfd_get_size(sd_memfd *m, uint64_t *sz);