Merge pull request #1864 from brauner/2017-10-18/ringbuffer

ringbuffer: implement efficient and performant ringbuffer
This commit is contained in:
Serge Hallyn 2017-10-21 13:03:46 -04:00 committed by GitHub
commit f3d91bf09a
14 changed files with 713 additions and 75 deletions

View File

@ -116,6 +116,7 @@ liblxc_la_SOURCES = \
log.c log.h \
attach.c attach.h \
criu.c criu.h \
ringbuf.c ringbuf.h \
\
network.c network.h \
nl.c nl.h \

View File

@ -68,10 +68,6 @@
#include <../include/openpty.h>
#endif
#ifdef HAVE_LINUX_MEMFD_H
#include <linux/memfd.h>
#endif
#include "af_unix.h"
#include "caps.h" /* for lxc_caps_last_cap() */
#include "cgroup.h"
@ -84,6 +80,7 @@
#include "namespace.h"
#include "network.h"
#include "parse.h"
#include "ringbuf.h"
#include "storage.h"
#include "storage/aufs.h"
#include "storage/overlay.h"
@ -181,59 +178,6 @@ static int sethostname(const char * name, size_t len)
#define MS_LAZYTIME (1<<25)
#endif
/* memfd_create() */
#ifndef MFD_CLOEXEC
#define MFD_CLOEXEC 0x0001U
#endif
#ifndef MFD_ALLOW_SEALING
#define MFD_ALLOW_SEALING 0x0002U
#endif
#ifndef HAVE_MEMFD_CREATE
static int memfd_create(const char *name, unsigned int flags) {
#ifndef __NR_memfd_create
#if defined __i386__
#define __NR_memfd_create 356
#elif defined __x86_64__
#define __NR_memfd_create 319
#elif defined __arm__
#define __NR_memfd_create 385
#elif defined __aarch64__
#define __NR_memfd_create 279
#elif defined __s390__
#define __NR_memfd_create 350
#elif defined __powerpc__
#define __NR_memfd_create 360
#elif defined __sparc__
#define __NR_memfd_create 348
#elif defined __blackfin__
#define __NR_memfd_create 390
#elif defined __ia64__
#define __NR_memfd_create 1340
#elif defined _MIPS_SIM
#if _MIPS_SIM == _MIPS_SIM_ABI32
#define __NR_memfd_create 4354
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32
#define __NR_memfd_create 6318
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64
#define __NR_memfd_create 5314
#endif
#endif
#endif
#ifdef __NR_memfd_create
return syscall(__NR_memfd_create, name, flags);
#else
errno = ENOSYS;
return -1;
#endif
}
#else
extern int memfd_create(const char *name, unsigned int flags);
#endif
char *lxchook_names[NUM_LXC_HOOKS] = {"pre-start", "pre-mount", "mount",
"autodev", "start", "stop",
"post-stop", "clone", "destroy",
@ -2485,6 +2429,7 @@ struct lxc_conf *lxc_conf_init(void)
new->autodev = 1;
new->console.log_path = NULL;
new->console.log_fd = -1;
new->console.log_size = 0;
new->console.path = NULL;
new->console.peer = -1;
new->console.peerpty.busy = -1;
@ -2493,6 +2438,7 @@ struct lxc_conf *lxc_conf_init(void)
new->console.master = -1;
new->console.slave = -1;
new->console.name[0] = '\0';
memset(&new->console.ringbuf, 0, sizeof(struct lxc_ringbuf));
new->maincmd_fd = -1;
new->nbd_idx = -1;
new->rootfs.mount = strdup(default_rootfs_mount);
@ -3136,7 +3082,65 @@ static bool verify_start_hooks(struct lxc_conf *conf)
return true;
}
int lxc_setup(struct lxc_handler *handler)
/**
* Note that this function needs to run before the mainloop starts. Since we
* register a handler for the console's masterfd when we create the mainloop
* the console handler needs to see an allocated ringbuffer.
*/
static int lxc_setup_console_ringbuf(struct lxc_console *console)
{
int ret;
struct lxc_ringbuf *buf = &console->ringbuf;
uint64_t size = console->log_size;
/* no ringbuffer previously allocated and no ringbuffer requested */
if (!buf->addr && size <= 0)
return 0;
/* ringbuffer allocated but no new ringbuffer requested */
if (buf->addr && size <= 0) {
lxc_ringbuf_release(buf);
buf->addr = NULL;
buf->r_off = 0;
buf->w_off = 0;
buf->size = 0;
TRACE("Deallocated console ringbuffer");
return 0;
}
if (size <= 0)
return 0;
/* check wether the requested size for the ringbuffer has changed */
if (buf->addr && buf->size != size) {
TRACE("Console ringbuffer size changed from %" PRIu64
" to %" PRIu64 " bytes. Deallocating console ringbuffer",
buf->size, size);
lxc_ringbuf_release(buf);
}
ret = lxc_ringbuf_create(buf, size);
if (ret < 0) {
ERROR("Failed to setup %" PRIu64 " byte console ringbuffer", size);
return -1;
}
TRACE("Allocated %" PRIu64 " byte console ringbuffer", size);
return 0;
}
int lxc_setup_parent(struct lxc_handler *handler)
{
int ret;
ret = lxc_setup_console_ringbuf(&handler->conf->console);
if (ret < 0)
return -1;
return 0;
}
int lxc_setup_child(struct lxc_handler *handler)
{
int ret;
const char *name = handler->name;
@ -3516,6 +3520,8 @@ void lxc_conf_free(struct lxc_conf *conf)
current_config = NULL;
free(conf->console.log_path);
free(conf->console.path);
if (conf->console.log_size > 0 && conf->console.ringbuf.addr)
lxc_ringbuf_release(&conf->console.ringbuf);
free(conf->rootfs.mount);
free(conf->rootfs.bdev_type);
free(conf->rootfs.options);

View File

@ -36,6 +36,7 @@
#include <stdbool.h>
#include "list.h"
#include "ringbuf.h"
#include "start.h" /* for lxc_handler */
#if HAVE_SCMP_FILTER_CTX
@ -152,6 +153,8 @@ struct lxc_console {
char name[MAXPATHLEN];
struct termios *tios;
struct lxc_tty_state *tty_state;
uint64_t log_size;
struct lxc_ringbuf ringbuf;
};
/*
@ -376,7 +379,8 @@ extern int lxc_delete_autodev(struct lxc_handler *handler);
extern void lxc_clear_includes(struct lxc_conf *conf);
extern int do_rootfs_setup(struct lxc_conf *conf, const char *name,
const char *lxcpath);
extern int lxc_setup(struct lxc_handler *handler);
extern int lxc_setup_child(struct lxc_handler *handler);
extern int lxc_setup_parent(struct lxc_handler *handler);
extern int setup_resource_limits(struct lxc_list *limits, pid_t pid);
extern int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype);
extern int mapped_hostid(unsigned id, struct lxc_conf *conf,

View File

@ -83,6 +83,7 @@ lxc_config_define(cap_keep);
lxc_config_define(cgroup_controller);
lxc_config_define(cgroup_dir);
lxc_config_define(console_logfile);
lxc_config_define(console_logsize);
lxc_config_define(console_path);
lxc_config_define(environment);
lxc_config_define(ephemeral);
@ -148,6 +149,7 @@ static struct lxc_config_t config[] = {
{ "lxc.cgroup.dir", false, set_config_cgroup_dir, get_config_cgroup_dir, clr_config_cgroup_dir, },
{ "lxc.cgroup", false, set_config_cgroup_controller, get_config_cgroup_controller, clr_config_cgroup_controller, },
{ "lxc.console.logfile", false, set_config_console_logfile, get_config_console_logfile, clr_config_console_logfile, },
{ "lxc.console.logsize", false, set_config_console_logsize, get_config_console_logsize, clr_config_console_logsize, },
{ "lxc.console.path", false, set_config_console_path, get_config_console_path, clr_config_console_path, },
{ "lxc.environment", false, set_config_environment, get_config_environment, clr_config_environment, },
{ "lxc.ephemeral", false, set_config_ephemeral, get_config_ephemeral, clr_config_ephemeral, },
@ -1790,6 +1792,53 @@ static int set_config_console_logfile(const char *key, const char *value,
return set_config_path_item(&lxc_conf->console.log_path, value);
}
static int set_config_console_logsize(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
int ret;
int64_t size;
uint64_t logsize, pgsz;
if (lxc_config_value_empty(value)) {
lxc_conf->console.log_size = 0;
return 0;
}
/* If the user specified "auto" the default log size is 2^17 = 128 Kib */
if (!strcmp(value, "auto")) {
lxc_conf->console.log_size = 1 << 17;
return 0;
}
ret = parse_byte_size_string(value, &size);
if (ret < 0)
return -1;
if (size < 0)
return -EINVAL;
/* must be at least a page size */
pgsz = lxc_getpagesize();
if ((uint64_t)size < pgsz) {
NOTICE("Requested ringbuffer size for the console is %" PRId64
" but must be at least %" PRId64
" bytes. Setting ringbuffer size to %" PRId64 " bytes",
size, pgsz, pgsz);
size = pgsz;
}
logsize = lxc_find_next_power2((uint64_t)size);
if (logsize == 0)
return -EINVAL;
if (logsize != size)
NOTICE("Passed size was not a power of 2. Rounding log size to "
"next power of two: %" PRIu64 " bytes", logsize);
lxc_conf->console.log_size = logsize;
return 0;
}
int append_unexp_config_line(const char *line, struct lxc_conf *conf)
{
size_t len = conf->unexpanded_len, linelen = strlen(line);
@ -3023,6 +3072,12 @@ static int get_config_console_logfile(const char *key, char *retv, int inlen,
return lxc_get_conf_str(retv, inlen, c->console.log_path);
}
static int get_config_console_logsize(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
return lxc_get_conf_uint64(c, retv, inlen, c->autodev);
}
static int get_config_seccomp_profile(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
@ -3405,6 +3460,13 @@ static inline int clr_config_console_logfile(const char *key,
return 0;
}
static inline int clr_config_console_logsize(const char *key,
struct lxc_conf *c, void *data)
{
c->console.log_size = 0;
return 0;
}
static inline int clr_config_seccomp_profile(const char *key,
struct lxc_conf *c, void *data)
{

View File

@ -672,6 +672,16 @@ int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v)
return snprintf(retv, inlen, "%d", v);
}
int lxc_get_conf_uint64(struct lxc_conf *c, char *retv, int inlen, uint64_t v)
{
if (!retv)
inlen = 0;
else
memset(retv, 0, inlen);
return snprintf(retv, inlen, "%"PRIu64, v);
}
bool parse_limit_value(const char **value, rlim_t *res)
{
char *endptr = NULL;

View File

@ -84,5 +84,7 @@ extern void update_hwaddr(const char *line);
extern bool new_hwaddr(char *hwaddr);
extern int lxc_get_conf_str(char *retv, int inlen, const char *value);
extern int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v);
extern int lxc_get_conf_uint64(struct lxc_conf *c, char *retv, int inlen, uint64_t v);
extern bool parse_limit_value(const char **value, rlim_t *res);
#endif /* __LXC_CONFILE_UTILS_H */

View File

@ -51,6 +51,8 @@
#include <../include/openpty.h>
#endif
#define LXC_CONSOLE_BUFFER_SIZE 1024
lxc_log_define(console, lxc);
static struct lxc_list lxc_ttys;
@ -167,12 +169,12 @@ static int lxc_console_cb_con(int fd, uint32_t events, void *data,
struct lxc_epoll_descr *descr)
{
struct lxc_console *console = (struct lxc_console *)data;
char buf[1024];
int r, w;
char buf[LXC_CONSOLE_BUFFER_SIZE];
int r, w, w_log, w_rbuf;
w = r = lxc_read_nointr(fd, buf, sizeof(buf));
if (r <= 0) {
INFO("console client on fd %d has exited", fd);
INFO("Console client on fd %d has exited", fd);
lxc_mainloop_del_handler(descr, fd);
if (fd == console->peer) {
if (console->tty_state) {
@ -190,16 +192,30 @@ static int lxc_console_cb_con(int fd, uint32_t events, void *data,
if (fd == console->peer)
w = lxc_write_nointr(console->master, buf, r);
w_rbuf = w_log = 0;
if (fd == console->master) {
if (console->log_fd >= 0)
w = lxc_write_nointr(console->log_fd, buf, r);
/* write to peer first */
if (console->peer >= 0)
w = lxc_write_nointr(console->peer, buf, r);
/* write to console ringbuffer */
if (console->log_size > 0)
w_rbuf = lxc_ringbuf_write(&console->ringbuf, buf, r);
/* write to console log */
if (console->log_fd >= 0)
w_log = lxc_write_nointr(console->log_fd, buf, r);
}
if (w != r)
WARN("console short write r:%d w:%d", r, w);
WARN("Console short write r:%d != w:%d", r, w);
if (w_rbuf < 0)
TRACE("%s - Failed to write %d bytes to console ringbuffer",
strerror(-w_rbuf), r);
if (w_log < 0)
TRACE("Failed to write %d bytes to console log", r);
return 0;
}
@ -632,7 +648,7 @@ int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata,
struct lxc_epoll_descr *descr)
{
struct lxc_tty_state *ts = cbdata;
char buf[1024];
char buf[LXC_CONSOLE_BUFFER_SIZE];
int r, w;
if (fd != ts->masterfd)

View File

@ -21,17 +21,18 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <unistd.h>
#include <alloca.h>
#include <errno.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "namespace.h"
#include "log.h"
#include "namespace.h"
#include "utils.h"
lxc_log_define(lxc_namespace, lxc);
@ -53,7 +54,7 @@ pid_t lxc_clone(int (*fn)(void *), void *arg, int flags)
.arg = arg,
};
size_t stack_size = sysconf(_SC_PAGESIZE);
size_t stack_size = lxc_getpagesize();
void *stack = alloca(stack_size);
pid_t ret;

145
src/lxc/ringbuf.c Normal file
View File

@ -0,0 +1,145 @@
/* liblxcapi
*
* Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
* Copyright © 2017 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
#define __STDC_FORMAT_MACROS
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include "ringbuf.h"
#include "utils.h"
int lxc_ringbuf_create(struct lxc_ringbuf *buf, size_t size)
{
char *tmp;
int ret;
int memfd = -1;
buf->size = size;
buf->r_off = 0;
buf->w_off = 0;
/* verify that we are at least given the multiple of a page size */
if (buf->size % lxc_getpagesize())
return -EINVAL;
buf->addr = mmap(NULL, buf->size * 2, PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (buf->addr == MAP_FAILED)
return -EINVAL;
memfd = memfd_create(".lxc_ringbuf", MFD_CLOEXEC);
if (memfd < 0) {
if (errno != ENOSYS)
goto on_error;
memfd = lxc_make_tmpfile((char *){P_tmpdir"/.lxc_ringbuf_XXXXXX"}, true);
}
if (memfd < 0)
goto on_error;
ret = ftruncate(memfd, buf->size);
if (ret < 0)
goto on_error;
tmp = mmap(buf->addr, buf->size, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_SHARED, memfd, 0);
if (tmp == MAP_FAILED || tmp != buf->addr)
goto on_error;
tmp = mmap(buf->addr + buf->size, buf->size, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_SHARED, memfd, 0);
if (tmp == MAP_FAILED || tmp != (buf->addr + buf->size))
goto on_error;
close(memfd);
return 0;
on_error:
lxc_ringbuf_release(buf);
if (memfd >= 0)
close(memfd);
return -1;
}
void lxc_ringbuf_move_read_addr(struct lxc_ringbuf *buf, size_t len)
{
buf->r_off += len;
if (buf->r_off < buf->size)
return;
/* wrap around */
buf->r_off -= buf->size;
buf->w_off -= buf->size;
}
/**
* lxc_ringbuf_write - write a message to the ringbuffer
* - The size of the message should never be greater than the size of the whole
* ringbuffer.
* - The write method will always succeed i.e. it will always advance the r_off
* if it detects that there's not enough space available to write the
* message.
*/
int lxc_ringbuf_write(struct lxc_ringbuf *buf, const char *msg, size_t len)
{
char *w_addr;
uint64_t free;
/* sanity check: a write should never exceed the ringbuffer's total size */
if (len > buf->size)
return -EFBIG;
free = lxc_ringbuf_free(buf);
/* not enough space left so advance read address */
if (len > free)
lxc_ringbuf_move_read_addr(buf, len);
w_addr = lxc_ringbuf_get_write_addr(buf);
memcpy(w_addr, msg, len);
lxc_ringbuf_move_write_addr(buf, len);
return 0;
}
int lxc_ringbuf_read(struct lxc_ringbuf *buf, char *out, size_t *len)
{
uint64_t used;
/* there's nothing to read */
if (buf->r_off == buf->w_off)
return -ENODATA;
/* read maximum amount available */
used = lxc_ringbuf_used(buf);
if (used < *len)
*len = used;
/* copy data to reader but don't advance addr */
memcpy(out, lxc_ringbuf_get_read_addr(buf), *len);
out[*len - 1] = '\0';
return 0;
}

90
src/lxc/ringbuf.h Normal file
View File

@ -0,0 +1,90 @@
/* liblxcapi
*
* Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
* Copyright © 2017 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __LXC_RINGBUF_H
#define __LXC_RINGBUF_H
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/mman.h>
/**
* lxc_ringbuf - Implements a simple and efficient memory mapped ringbuffer.
* - The "addr" field of struct lxc_ringbuf is considered immutable. Instead the
* read and write offsets r_off and w_off are used to calculate the current
* read and write addresses. There should never be a need to use any of those
* fields directly. Instead use the appropriate helpers below.
* - Callers are expected to synchronize read and write accesses to the
* ringbuffer.
*/
struct lxc_ringbuf {
char *addr; /* start address of the ringbuffer */
uint64_t size; /* total size of the ringbuffer in bytes */
uint64_t r_off; /* read offset */
uint64_t w_off; /* write offset */
};
/**
* lxc_ringbuf_create - Initialize a new ringbuffer.
*
* @param[in] size Size of the new ringbuffer as a power of 2.
*/
extern int lxc_ringbuf_create(struct lxc_ringbuf *buf, size_t size);
extern void lxc_ringbuf_move_read_addr(struct lxc_ringbuf *buf, size_t len);
extern int lxc_ringbuf_write(struct lxc_ringbuf *buf, const char *msg, size_t len);
extern int lxc_ringbuf_read(struct lxc_ringbuf *buf, char *out, size_t *len);
static inline void lxc_ringbuf_release(struct lxc_ringbuf *buf)
{
munmap(buf->addr, buf->size * 2);
}
static inline void lxc_ringbuf_clear(struct lxc_ringbuf *buf)
{
buf->r_off = 0;
buf->w_off = 0;
}
static inline uint64_t lxc_ringbuf_used(struct lxc_ringbuf *buf)
{
return buf->w_off - buf->r_off;
}
static inline uint64_t lxc_ringbuf_free(struct lxc_ringbuf *buf)
{
return buf->size - lxc_ringbuf_used(buf);
}
static inline char *lxc_ringbuf_get_read_addr(struct lxc_ringbuf *buf)
{
return buf->addr + buf->r_off;
}
static inline char *lxc_ringbuf_get_write_addr(struct lxc_ringbuf *buf)
{
return buf->addr + buf->w_off;
}
static inline void lxc_ringbuf_move_write_addr(struct lxc_ringbuf *buf, size_t len)
{
buf->w_off += len;
}
#endif /* __LXC_RINGBUF_H */

View File

@ -937,7 +937,7 @@ static int do_start(void *data)
}
/* Setup the container, ip, names, utsname, ... */
ret = lxc_setup(handler);
ret = lxc_setup_child(handler);
close(handler->data_sock[0]);
close(handler->data_sock[1]);
if (ret < 0) {
@ -1254,6 +1254,11 @@ static int lxc_spawn(struct lxc_handler *handler)
*/
flags &= ~CLONE_NEWNET;
}
ret = lxc_setup_parent(handler);
if (ret < 0)
goto out_delete_net;
handler->pid = lxc_clone(do_start, handler, flags);
if (handler->pid < 0) {
SYSERROR("Failed to clone a new set of namespaces.");

View File

@ -47,6 +47,7 @@
#include "log.h"
#include "lxclock.h"
#include "namespace.h"
#include "parse.h"
#include "utils.h"
#ifndef O_PATH
@ -2003,6 +2004,26 @@ int lxc_safe_long(const char *numstr, long int *converted)
return 0;
}
int lxc_safe_long_long(const char *numstr, long long int *converted)
{
char *err = NULL;
signed long long int sli;
errno = 0;
sli = strtoll(numstr, &err, 0);
if (errno == ERANGE && (sli == LLONG_MAX || sli == LLONG_MIN))
return -ERANGE;
if (errno != 0 && sli == 0)
return -EINVAL;
if (err == numstr || *err != '\0')
return -EINVAL;
*converted = sli;
return 0;
}
int lxc_switch_uid_gid(uid_t uid, gid_t gid)
{
if (setgid(gid) < 0) {
@ -2339,3 +2360,118 @@ bool lxc_nic_exists(char *nic)
return true;
}
int lxc_make_tmpfile(char *template, bool rm)
{
int fd, ret;
fd = mkstemp(template);
if (fd < 0)
return -1;
if (!rm)
return fd;
ret = unlink(template);
if (ret < 0) {
close(fd);
return -1;
}
return fd;
}
uint64_t lxc_getpagesize(void)
{
int64_t pgsz;
pgsz = sysconf(_SC_PAGESIZE);
if (pgsz <= 0)
pgsz = 1 << 12;
return pgsz;
}
int parse_byte_size_string(const char *s, int64_t *converted)
{
int ret, suffix_len;
long long int conv;
int64_t mltpl, overflow;
char *end;
char dup[LXC_NUMSTRLEN64 + 2];
char suffix[3];
if (!s || !strcmp(s, ""))
return -EINVAL;
end = stpncpy(dup, s, sizeof(dup));
if (*end != '\0')
return -EINVAL;
if (isdigit(*(end - 1)))
suffix_len = 0;
else if (isalpha(*(end - 1)))
suffix_len = 1;
else
return -EINVAL;
if ((end - 2) == dup && !isdigit(*(end - 2)))
return -EINVAL;
if (isalpha(*(end - 2))) {
if (suffix_len == 1)
suffix_len++;
else
return -EINVAL;
}
if (suffix_len > 0) {
memcpy(suffix, end - suffix_len, suffix_len);
*(suffix + suffix_len) = '\0';
*(end - suffix_len) = '\0';
}
dup[lxc_char_right_gc(dup, strlen(dup))] = '\0';
ret = lxc_safe_long_long(dup, &conv);
if (ret < 0)
return -ret;
if (suffix_len != 2) {
*converted = conv;
return 0;
}
if (!strcmp(suffix, "kB"))
mltpl = 1024;
else if (!strcmp(suffix, "MB"))
mltpl = 1024 * 1024;
else if (!strcmp(suffix, "GB"))
mltpl = 1024 * 1024 * 1024;
else
return -EINVAL;
overflow = conv * mltpl;
if (conv != 0 && (overflow / conv) != mltpl)
return -ERANGE;
*converted = overflow;
return 0;
}
uint64_t lxc_find_next_power2(uint64_t n)
{
/* 0 is not valid input. We return 0 to the caller since 0 is not a
* valid power of two.
*/
if (n == 0)
return 0;
if (!(n & (n - 1)))
return n;
while (n & (n - 1))
n = n & (n - 1);
n = n << 1;
return n;
}

View File

@ -39,6 +39,10 @@
#include <sys/types.h>
#include <sys/vfs.h>
#ifdef HAVE_LINUX_MEMFD_H
#include <linux/memfd.h>
#endif
#include "initutils.h"
/* Define __S_ISTYPE if missing from the C library. */
@ -184,6 +188,64 @@ static inline int signalfd(int fd, const sigset_t *mask, int flags)
#define LOOP_CTL_GET_FREE 0x4C82
#endif
/* memfd_create() */
#ifndef MFD_CLOEXEC
#define MFD_CLOEXEC 0x0001U
#endif
#ifndef MFD_ALLOW_SEALING
#define MFD_ALLOW_SEALING 0x0002U
#endif
#ifndef HAVE_MEMFD_CREATE
static inline int memfd_create(const char *name, unsigned int flags) {
#ifndef __NR_memfd_create
#if defined __i386__
#define __NR_memfd_create 356
#elif defined __x86_64__
#define __NR_memfd_create 319
#elif defined __arm__
#define __NR_memfd_create 385
#elif defined __aarch64__
#define __NR_memfd_create 279
#elif defined __s390__
#define __NR_memfd_create 350
#elif defined __powerpc__
#define __NR_memfd_create 360
#elif defined __sparc__
#define __NR_memfd_create 348
#elif defined __blackfin__
#define __NR_memfd_create 390
#elif defined __ia64__
#define __NR_memfd_create 1340
#elif defined _MIPS_SIM
#if _MIPS_SIM == _MIPS_SIM_ABI32
#define __NR_memfd_create 4354
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32
#define __NR_memfd_create 6318
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64
#define __NR_memfd_create 5314
#endif
#endif
#endif
#ifdef __NR_memfd_create
return syscall(__NR_memfd_create, name, flags);
#else
errno = ENOSYS;
return -1;
#endif
}
#else
extern int memfd_create(const char *name, unsigned int flags);
#endif
static inline int lxc_set_cloexec(int fd)
{
return fcntl(fd, F_SETFD, FD_CLOEXEC);
}
/* Struct to carry child pid from lxc_popen() to lxc_pclose().
* Not an opaque struct to allow direct access to the underlying FILE *
* (i.e., struct lxc_popen_FILE *file; fgets(buf, sizeof(buf), file->f))
@ -357,7 +419,10 @@ extern bool task_blocking_signal(pid_t pid, int signal);
extern int lxc_safe_uint(const char *numstr, unsigned int *converted);
extern int lxc_safe_int(const char *numstr, int *converted);
extern int lxc_safe_long(const char *numstr, long int *converted);
extern int lxc_safe_long_long(const char *numstr, long long int *converted);
extern int lxc_safe_ulong(const char *numstr, unsigned long *converted);
/* Handles B, kb, MB, GB. Detects overflows and reports -ERANGE. */
extern int parse_byte_size_string(const char *s, int64_t *converted);
/* Switch to a new uid and gid. */
extern int lxc_switch_uid_gid(uid_t uid, gid_t gid);
@ -404,5 +469,17 @@ typedef __typeof__(((struct statfs *)NULL)->f_type) fs_type_magic;
extern bool has_fs_type(const char *path, fs_type_magic magic_val);
extern bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val);
extern bool lxc_nic_exists(char *nic);
extern int lxc_make_tmpfile(char *template, bool rm);
extern uint64_t lxc_getpagesize(void);
/* If n is not a power of 2 this function will return the next power of 2
* greater than that number. Note that this function always returns the *next*
* power of 2 *greater* that number not the *nearest*. For example, passing 1025
* as argument this function will return 2048 although the closest power of 2
* would be 1024.
* If the caller passes in 0 they will receive 0 in return since this is invalid
* input and 0 is not a power of 2.
*/
extern uint64_t lxc_find_next_power2(uint64_t n);
#endif /* __LXC_UTILS_H */

View File

@ -380,6 +380,88 @@ void test_lxc_string_in_array(void)
lxc_test_assert_abort(lxc_string_in_array("XYZ", (const char *[]){"BERTA", "ARQWE(9", "C8Zhkd", "7U", "XYZ", "UOIZ9", "=)()", NULL}));
}
void test_parse_byte_size_string(void)
{
int ret;
int64_t n;
ret = parse_byte_size_string("0", &n);
if (ret < 0)
exit(EXIT_FAILURE);
if (n != 0)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1", &n);
if (ret < 0)
exit(EXIT_FAILURE);
if (n != 1)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1 ", &n);
if (ret == 0)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1B", &n);
if (ret < 0)
exit(EXIT_FAILURE);
if (n != 1)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1kB", &n);
if (ret < 0)
exit(EXIT_FAILURE);
if (n != 1024)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1MB", &n);
if (ret < 0)
exit(EXIT_FAILURE);
if (n != 1048576)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1GB", &n);
if (ret < 0)
exit(EXIT_FAILURE);
if (n != 1073741824)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1TB", &n);
if (ret == 0)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1 B", &n);
if (ret < 0)
exit(EXIT_FAILURE);
if (n != 1)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1 kB", &n);
if (ret < 0)
exit(EXIT_FAILURE);
if (n != 1024)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1 MB", &n);
if (ret < 0)
exit(EXIT_FAILURE);
if (n != 1048576)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1 GB", &n);
if (ret < 0)
exit(EXIT_FAILURE);
if (n != 1073741824)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("1 TB", &n);
if (ret == 0)
exit(EXIT_FAILURE);
ret = parse_byte_size_string("asdf", &n);
if (ret == 0)
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
test_lxc_string_replace();
@ -389,6 +471,7 @@ int main(int argc, char *argv[])
test_lxc_safe_uint();
test_lxc_safe_int();
test_lxc_safe_long();
test_parse_byte_size_string();
exit(EXIT_SUCCESS);
}