diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 75efa0a572..32bb8d5e05 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -11,6 +11,7 @@ noinst_HEADERS = attach.h \ confile_utils.h \ criu.h \ error.h \ + file_utils.h \ initutils.h \ list.h \ log.h \ @@ -32,6 +33,7 @@ noinst_HEADERS = attach.h \ storage/storage.h \ storage/storage_utils.h \ storage/zfs.h \ + string_utils.h \ terminal.h \ ../tests/lxctest.h \ tools/arguments.h \ @@ -93,6 +95,7 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \ error.c error.h \ execute.c \ freezer.c \ + file_utils.c file_utils.h \ initutils.c initutils.h \ list.h \ log.c log.h \ @@ -122,6 +125,7 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \ storage/storage.c storage/storage.h \ storage/storage_utils.c storage/storage_utils.h \ storage/zfs.c storage/zfs.h \ + string_utils.c string_utils.h \ sync.c sync.h \ terminal.c \ utils.c utils.h \ @@ -319,7 +323,8 @@ endif if ENABLE_COMMANDS # Binaries shipping with liblxc -init_lxc_SOURCES = cmd/lxc_init.c +init_lxc_SOURCES = cmd/lxc_init.c \ + string_utils.c string_utils.h lxc_monitord_SOURCES = cmd/lxc_monitord.c lxc_user_nic_SOURCES = cmd/lxc_user_nic.c \ log.c log.h \ @@ -332,6 +337,8 @@ lxc_usernsexec_SOURCES = cmd/lxc_usernsexec.c \ log.c log.h \ macro.h \ namespace.c namespace.h \ + file_utils.c file_utils.h \ + string_utils.c string_utils.h \ utils.c utils.h endif @@ -350,10 +357,11 @@ init_lxc_static_SOURCES = cmd/lxc_init.c \ caps.c caps.h \ error.c error.h \ initutils.c initutils.h \ + file_utils.c file_utils.h \ log.c log.h \ macro.h \ namespace.c namespace.h \ - parse.c parse.h + string_utils.c string_utils.h if !HAVE_GETLINE if HAVE_FGETLN @@ -378,12 +386,27 @@ endif if ENABLE_PAM if HAVE_PAM pam_LTLIBRARIES = pam_cgfs.la + pam_cgfs_la_SOURCES = pam/pam_cgfs.c \ - pam/utils.c pam/utils.h -pam_cgfs_la_CFLAGS = $(AM_CFLAGS) + macro.h \ + file_utils.c file_utils.h \ + string_utils.c string_utils.h + +if !HAVE_STRLCAT +pam_cgfs_la_SOURCES += ../include/strlcat.c ../include/strlcat.h +endif + +if !HAVE_STRLCPY +pam_cgfs_la_SOURCES += ../include/strlcpy.c ../include/strlcpy.h +endif + +pam_cgfs_la_CFLAGS = $(AM_CFLAGS) \ + -DNO_LOG + pam_cgfs_la_LIBADD = $(AM_LIBS) \ $(PAM_LIBS) \ -L$(top_srcdir) + pam_cgfs_la_LDFLAGS = $(AM_LDFLAGS) \ -avoid-version \ -module \ diff --git a/src/lxc/cmd/lxc_init.c b/src/lxc/cmd/lxc_init.c index 7595989a70..362f5b4959 100644 --- a/src/lxc/cmd/lxc_init.c +++ b/src/lxc/cmd/lxc_init.c @@ -44,6 +44,7 @@ #include "log.h" #include "namespace.h" #include "parse.h" +#include "string_utils.h" /* option keys for long only options */ #define OPT_USAGE 0x1000 diff --git a/src/lxc/file_utils.c b/src/lxc/file_utils.c new file mode 100644 index 0000000000..89d90c269c --- /dev/null +++ b/src/lxc/file_utils.c @@ -0,0 +1,306 @@ +/* liblxcapi + * + * Copyright © 2018 Christian Brauner . + * Copyright © 2018 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. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "file_utils.h" +#include "log.h" +#include "macro.h" +#include "string.h" + +#ifndef NO_LOG +lxc_log_define(file_utils, lxc); +#endif + +int lxc_write_to_file(const char *filename, const void *buf, size_t count, + bool add_newline, mode_t mode) +{ + int fd, saved_errno; + ssize_t ret; + + fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); + if (fd < 0) + return -1; + + ret = lxc_write_nointr(fd, buf, count); + if (ret < 0) + goto out_error; + + if ((size_t)ret != count) + goto out_error; + + if (add_newline) { + ret = lxc_write_nointr(fd, "\n", 1); + if (ret != 1) + goto out_error; + } + + close(fd); + return 0; + +out_error: + saved_errno = errno; + close(fd); + errno = saved_errno; + return -1; +} + +int lxc_read_from_file(const char *filename, void *buf, size_t count) +{ + int fd = -1, saved_errno; + ssize_t ret; + + fd = open(filename, O_RDONLY | O_CLOEXEC); + if (fd < 0) + return -1; + + if (!buf || !count) { + char buf2[100]; + size_t count2 = 0; + + while ((ret = lxc_read_nointr(fd, buf2, 100)) > 0) + count2 += ret; + + if (ret >= 0) + ret = count2; + } else { + memset(buf, 0, count); + ret = lxc_read_nointr(fd, buf, count); + } + + saved_errno = errno; + close(fd); + errno = saved_errno; + return ret; +} + +ssize_t lxc_write_nointr(int fd, const void *buf, size_t count) +{ + ssize_t ret; +again: + ret = write(fd, buf, count); + if (ret < 0 && errno == EINTR) + goto again; + + return ret; +} + +ssize_t lxc_read_nointr(int fd, void *buf, size_t count) +{ + ssize_t ret; +again: + ret = read(fd, buf, count); + if (ret < 0 && errno == EINTR) + goto again; + + return ret; +} + +ssize_t lxc_read_nointr_expect(int fd, void *buf, size_t count, const void *expected_buf) +{ + ssize_t ret; + + ret = lxc_read_nointr(fd, buf, count); + if (ret <= 0) + return ret; + + if ((size_t)ret != count) + return -1; + + if (expected_buf && memcmp(buf, expected_buf, count) != 0) { + errno = EINVAL; + return -1; + } + + return ret; +} + +bool file_exists(const char *f) +{ + struct stat statbuf; + + return stat(f, &statbuf) == 0; +} + +int print_to_file(const char *file, const char *content) +{ + FILE *f; + int ret = 0; + + f = fopen(file, "w"); + if (!f) + return -1; + + if (fprintf(f, "%s", content) != strlen(content)) + ret = -1; + + fclose(f); + return ret; +} + +int is_dir(const char *path) +{ + struct stat statbuf; + int ret; + + ret = stat(path, &statbuf); + if (ret == 0 && S_ISDIR(statbuf.st_mode)) + return 1; + + return 0; +} + +/* + * Return the number of lines in file @fn, or -1 on error + */ +int lxc_count_file_lines(const char *fn) +{ + FILE *f; + char *line = NULL; + size_t sz = 0; + int n = 0; + + f = fopen_cloexec(fn, "r"); + if (!f) + return -1; + + while (getline(&line, &sz, f) != -1) { + n++; + } + + free(line); + fclose(f); + return n; +} + +int lxc_make_tmpfile(char *template, bool rm) +{ + int fd, ret; + mode_t msk; + + msk = umask(0022); + fd = mkstemp(template); + umask(msk); + if (fd < 0) + return -1; + + if (!rm) + return fd; + + ret = unlink(template); + if (ret < 0) { + close(fd); + return -1; + } + + return fd; +} + +/* In overlayfs, st_dev is unreliable. So on overlayfs we don't do the + * lxc_rmdir_onedev() + */ +static bool is_native_overlayfs(const char *path) +{ + if (has_fs_type(path, OVERLAY_SUPER_MAGIC) || + has_fs_type(path, OVERLAYFS_SUPER_MAGIC)) + return true; + + return false; +} + +bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val) +{ + return (fs->f_type == (fs_type_magic)magic_val); +} + +bool has_fs_type(const char *path, fs_type_magic magic_val) +{ + bool has_type; + int ret; + struct statfs sb; + + ret = statfs(path, &sb); + if (ret < 0) + return false; + + return is_fs_type(&sb, magic_val); +} + +bool fhas_fs_type(int fd, fs_type_magic magic_val) +{ + int ret; + struct statfs sb; + + ret = fstatfs(fd, &sb); + if (ret < 0) + return false; + + return is_fs_type(&sb, magic_val); +} + +FILE *fopen_cloexec(const char *path, const char *mode) +{ + int open_mode = 0; + int step = 0; + int fd; + int saved_errno = 0; + FILE *ret; + + if (!strncmp(mode, "r+", 2)) { + open_mode = O_RDWR; + step = 2; + } else if (!strncmp(mode, "r", 1)) { + open_mode = O_RDONLY; + step = 1; + } else if (!strncmp(mode, "w+", 2)) { + open_mode = O_RDWR | O_TRUNC | O_CREAT; + step = 2; + } else if (!strncmp(mode, "w", 1)) { + open_mode = O_WRONLY | O_TRUNC | O_CREAT; + step = 1; + } else if (!strncmp(mode, "a+", 2)) { + open_mode = O_RDWR | O_CREAT | O_APPEND; + step = 2; + } else if (!strncmp(mode, "a", 1)) { + open_mode = O_WRONLY | O_CREAT | O_APPEND; + step = 1; + } + for (; mode[step]; step++) + if (mode[step] == 'x') + open_mode |= O_EXCL; + open_mode |= O_CLOEXEC; + + fd = open(path, open_mode, 0666); + if (fd < 0) + return NULL; + + ret = fdopen(fd, mode); + saved_errno = errno; + if (!ret) + close(fd); + errno = saved_errno; + return ret; +} diff --git a/src/lxc/file_utils.h b/src/lxc/file_utils.h new file mode 100644 index 0000000000..9467f53ac1 --- /dev/null +++ b/src/lxc/file_utils.h @@ -0,0 +1,56 @@ +/* liblxcapi + * + * Copyright © 2018 Christian Brauner . + * Copyright © 2018 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_FILE_UTILS_H +#define __LXC_FILE_UTILS_H + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +/* read and write whole files */ +extern int lxc_write_to_file(const char *filename, const void *buf, + size_t count, bool add_newline, mode_t mode); +extern int lxc_read_from_file(const char *filename, void *buf, size_t count); + +/* send and receive buffers completely */ +extern ssize_t lxc_write_nointr(int fd, const void *buf, size_t count); +extern ssize_t lxc_read_nointr(int fd, void *buf, size_t count); +extern ssize_t lxc_read_nointr_expect(int fd, void *buf, size_t count, + const void *expected_buf); +extern bool file_exists(const char *f); +extern int print_to_file(const char *file, const char *content); +extern int is_dir(const char *path); +extern int lxc_count_file_lines(const char *fn); +extern int lxc_make_tmpfile(char *template, bool rm); + +/* __typeof__ should be safe to use with all compilers. */ +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 fhas_fs_type(int fd, fs_type_magic magic_val); +extern bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val); +extern FILE *fopen_cloexec(const char *path, const char *mode); + +#endif /* __LXC_FILE_UTILS_H */ diff --git a/src/lxc/freezer.c b/src/lxc/freezer.c index 2103b50b91..a6b9da0613 100644 --- a/src/lxc/freezer.c +++ b/src/lxc/freezer.c @@ -37,8 +37,8 @@ #include "log.h" #include "lxc.h" #include "monitor.h" -#include "parse.h" #include "state.h" +#include "string_utils.h" lxc_log_define(freezer, lxc); diff --git a/src/lxc/initutils.c b/src/lxc/initutils.c index cadd82757a..9e5397aa69 100644 --- a/src/lxc/initutils.c +++ b/src/lxc/initutils.c @@ -24,6 +24,7 @@ #define _GNU_SOURCE #include +#include "file_utils.h" #include "initutils.h" #include "log.h" #include "macro.h" @@ -223,50 +224,6 @@ extern void remove_trailing_slashes(char *p) p[l] = '\0'; } -FILE *fopen_cloexec(const char *path, const char *mode) -{ - int open_mode = 0; - int step = 0; - int fd; - int saved_errno = 0; - FILE *ret; - - if (!strncmp(mode, "r+", 2)) { - open_mode = O_RDWR; - step = 2; - } else if (!strncmp(mode, "r", 1)) { - open_mode = O_RDONLY; - step = 1; - } else if (!strncmp(mode, "w+", 2)) { - open_mode = O_RDWR | O_TRUNC | O_CREAT; - step = 2; - } else if (!strncmp(mode, "w", 1)) { - open_mode = O_WRONLY | O_TRUNC | O_CREAT; - step = 1; - } else if (!strncmp(mode, "a+", 2)) { - open_mode = O_RDWR | O_CREAT | O_APPEND; - step = 2; - } else if (!strncmp(mode, "a", 1)) { - open_mode = O_WRONLY | O_CREAT | O_APPEND; - step = 1; - } - for (; mode[step]; step++) - if (mode[step] == 'x') - open_mode |= O_EXCL; - open_mode |= O_CLOEXEC; - - fd = open(path, open_mode, 0666); - if (fd < 0) - return NULL; - - ret = fdopen(fd, mode); - saved_errno = errno; - if (!ret) - close(fd); - errno = saved_errno; - return ret; -} - /* * Sets the process title to the specified title. Note that this may fail if * the kernel doesn't support PR_SET_MM_MAP (kernels <3.18). diff --git a/src/lxc/initutils.h b/src/lxc/initutils.h index b815cd1943..902663f3da 100644 --- a/src/lxc/initutils.h +++ b/src/lxc/initutils.h @@ -72,7 +72,6 @@ extern const char *lxc_global_config_value(const char *option_name); /* open a file with O_CLOEXEC */ extern void remove_trailing_slashes(char *p); -extern FILE *fopen_cloexec(const char *path, const char *mode); extern int setproctitle(char *title); #endif /* __LXC_INITUTILS_H */ diff --git a/src/lxc/macro.h b/src/lxc/macro.h index 6113adc2e7..ee94d5f93a 100644 --- a/src/lxc/macro.h +++ b/src/lxc/macro.h @@ -79,6 +79,15 @@ #define NSFS_MAGIC 0x6e736673 #endif +/* We have two different magic values for overlayfs, yay. */ +#ifndef OVERLAYFS_SUPER_MAGIC +#define OVERLAYFS_SUPER_MAGIC 0x794c764f +#endif + +#ifndef OVERLAY_SUPER_MAGIC +#define OVERLAY_SUPER_MAGIC 0x794c7630 +#endif + /* Useful macros */ /* Maximum number for 64 bit integer is a string with 21 digits: 2^64 - 1 = 21 */ #define LXC_NUMSTRLEN64 21 @@ -222,4 +231,13 @@ extern int __build_bug_on_failed; #define MS_SLAVE (1 << 19) #endif +/* open */ +#ifndef O_PATH +#define O_PATH 010000000 +#endif + +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 00400000 +#endif + #endif /* __LXC_MACRO_H */ diff --git a/src/lxc/pam/pam_cgfs.c b/src/lxc/pam/pam_cgfs.c index 2975b64567..9a8e877606 100644 --- a/src/lxc/pam/pam_cgfs.c +++ b/src/lxc/pam/pam_cgfs.c @@ -57,6 +57,7 @@ #include #include +#include "macro.h" #include "utils.h" #ifndef HAVE_STRLCPY diff --git a/src/lxc/pam/utils.c b/src/lxc/pam/utils.c deleted file mode 100644 index 5da870c58a..0000000000 --- a/src/lxc/pam/utils.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library 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. - * - * This library 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 this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "utils.h" - -#ifndef HAVE_STRLCAT -#include "include/strlcat.h" -#endif - -bool file_exists(const char *f) -{ - struct stat statbuf; - - return stat(f, &statbuf) == 0; -} - -void *must_realloc(void *orig, size_t sz) -{ - void *ret; - - do { - ret = realloc(orig, sz); - } while (!ret); - - return ret; -} - -char *must_copy_string(const char *entry) -{ - char *ret; - - if (!entry) - return NULL; - do { - ret = strdup(entry); - } while (!ret); - - return ret; -} - -char *must_make_path(const char *first, ...) -{ - va_list args; - char *cur, *dest; - size_t full_len = strlen(first); - size_t buf_len; - - dest = must_copy_string(first); - - va_start(args, first); - while ((cur = va_arg(args, char *)) != NULL) { - full_len += strlen(cur); - if (cur[0] != '/') - full_len++; - - buf_len = full_len + 1; - dest = must_realloc(dest, buf_len); - - if (cur[0] != '/') - (void)strlcat(dest, "/", buf_len); - (void)strlcat(dest, cur, buf_len); - } - va_end(args); - - return dest; -} - -bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val) -{ - return (fs->f_type == (fs_type_magic)magic_val); -} - -bool has_fs_type(const char *path, fs_type_magic magic_val) -{ - bool has_type; - int ret; - struct statfs sb; - - ret = statfs(path, &sb); - if (ret < 0) - return false; - - has_type = is_fs_type(&sb, magic_val); - - return has_type; -} diff --git a/src/lxc/pam/utils.h b/src/lxc/pam/utils.h deleted file mode 100644 index f0a01e6932..0000000000 --- a/src/lxc/pam/utils.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * This library 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. - * - * This library 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 this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __PAM_UTILS_H -#define __PAM_UTILS_H - -#include - -#ifndef CGROUP_SUPER_MAGIC -#define CGROUP_SUPER_MAGIC 0x27e0eb -#endif - -#ifndef CGROUP2_SUPER_MAGIC -#define CGROUP2_SUPER_MAGIC 0x63677270 -#endif - -typedef __typeof__(((struct statfs *)NULL)->f_type) fs_type_magic; -extern bool file_exists(const char *f); -extern void *must_realloc(void *orig, size_t sz); -extern char *must_copy_string(const char *entry); -__attribute__((sentinel)) extern char *must_make_path(const char *first, ...); -extern bool has_fs_type(const char *path, fs_type_magic magic_val); - -#endif /* __PAM_UTILS_H */ diff --git a/src/lxc/parse.c b/src/lxc/parse.c index cd31103775..d1d7cf8e15 100644 --- a/src/lxc/parse.c +++ b/src/lxc/parse.c @@ -139,55 +139,3 @@ int lxc_file_for_each_line(const char *file, lxc_file_cb callback, void *data) fclose(f); return err; } - -int lxc_char_left_gc(const char *buffer, size_t len) -{ - size_t i; - - for (i = 0; i < len; i++) { - if (buffer[i] == ' ' || - buffer[i] == '\t') - continue; - - return i; - } - - return 0; -} - -int lxc_char_right_gc(const char *buffer, size_t len) -{ - int i; - - for (i = len - 1; i >= 0; i--) { - if (buffer[i] == ' ' || - buffer[i] == '\t' || - buffer[i] == '\n' || - buffer[i] == '\0') - continue; - - return i + 1; - } - - return 0; -} - -char *lxc_trim_whitespace_in_place(char *buffer) -{ - buffer += lxc_char_left_gc(buffer, strlen(buffer)); - buffer[lxc_char_right_gc(buffer, strlen(buffer))] = '\0'; - return buffer; -} - -int lxc_is_line_empty(const char *line) -{ - int i; - size_t len = strlen(line); - - for (i = 0; i < len; i++) - if (line[i] != ' ' && line[i] != '\t' && - line[i] != '\n' && line[i] != '\r' && - line[i] != '\f' && line[i] != '\0') - return 0; - return 1; -} diff --git a/src/lxc/parse.h b/src/lxc/parse.h index 9809e9cec0..9134727409 100644 --- a/src/lxc/parse.h +++ b/src/lxc/parse.h @@ -37,14 +37,6 @@ extern int lxc_file_for_each_line(const char *file, lxc_file_cb callback, extern int lxc_file_for_each_line_mmap(const char *file, lxc_file_cb callback, void *data); -extern int lxc_char_left_gc(const char *buffer, size_t len); - -extern int lxc_char_right_gc(const char *buffer, size_t len); - -extern char *lxc_trim_whitespace_in_place(char *buffer); - -extern int lxc_is_line_empty(const char *line); - /* mmap() wrapper. lxc_strmmap() will take care to \0-terminate files so that * normal string-handling functions can be used on the buffer. */ extern void *lxc_strmmap(void *addr, size_t length, int prot, int flags, int fd, diff --git a/src/lxc/string_utils.c b/src/lxc/string_utils.c new file mode 100644 index 0000000000..e5e818373b --- /dev/null +++ b/src/lxc/string_utils.c @@ -0,0 +1,983 @@ +/* liblxcapi + * + * Copyright © 2018 Christian Brauner . + * Copyright © 2018 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. + */ + +#include "config.h" + +#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "lxclock.h" +#include "namespace.h" +#include "parse.h" +#include "string_utils.h" +#include "utils.h" + +#ifndef HAVE_STRLCPY +#include "include/strlcpy.h" +#endif + +#ifndef HAVE_STRLCAT +#include "include/strlcat.h" +#endif + +#ifndef NO_LOG +lxc_log_define(string_utils, lxc); +#endif + +char **lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup) +{ + va_list ap2; + size_t count = 1 + skip; + char **result; + + /* first determine size of argument list, we don't want to reallocate + * constantly... + */ + va_copy(ap2, ap); + while (1) { + char *arg = va_arg(ap2, char *); + if (!arg) + break; + count++; + } + va_end(ap2); + + result = calloc(count, sizeof(char *)); + if (!result) + return NULL; + + count = skip; + while (1) { + char *arg = va_arg(ap, char *); + if (!arg) + break; + arg = do_strdup ? strdup(arg) : arg; + if (!arg) + goto oom; + result[count++] = arg; + } + + /* calloc has already set last element to NULL*/ + return result; + +oom: + free(result); + return NULL; +} + +const char **lxc_va_arg_list_to_argv_const(va_list ap, size_t skip) +{ + return (const char **)lxc_va_arg_list_to_argv(ap, skip, 0); +} + +char *lxc_string_replace(const char *needle, const char *replacement, + const char *haystack) +{ + ssize_t len = -1, saved_len = -1; + char *result = NULL; + size_t replacement_len = strlen(replacement); + size_t needle_len = strlen(needle); + + /* should be executed exactly twice */ + while (len == -1 || result == NULL) { + char *p; + char *last_p; + ssize_t part_len; + + if (len != -1) { + result = calloc(1, len + 1); + if (!result) + return NULL; + + saved_len = len; + } + + len = 0; + + for (last_p = (char *)haystack, p = strstr(last_p, needle); p; + last_p = p, p = strstr(last_p, needle)) { + part_len = (ssize_t)(p - last_p); + if (result && part_len > 0) + memcpy(&result[len], last_p, part_len); + + len += part_len; + + if (result && replacement_len > 0) + memcpy(&result[len], replacement, + replacement_len); + + len += replacement_len; + p += needle_len; + } + + part_len = strlen(last_p); + if (result && part_len > 0) + memcpy(&result[len], last_p, part_len); + + len += part_len; + } + + /* make sure we did the same thing twice, + * once for calculating length, the other + * time for copying data */ + if (saved_len != len) { + free(result); + return NULL; + } + + /* make sure we didn't overwrite any buffer, + * due to calloc the string should be 0-terminated */ + if (result[len] != '\0') { + free(result); + return NULL; + } + + return result; +} + +bool lxc_string_in_array(const char *needle, const char **haystack) +{ + for (; haystack && *haystack; haystack++) + if (!strcmp(needle, *haystack)) + return true; + + return false; +} + +char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix) +{ + char *result; + char **p; + size_t sep_len = strlen(sep); + size_t result_len = use_as_prefix * sep_len; + size_t buf_len; + + /* calculate new string length */ + for (p = (char **)parts; *p; p++) + result_len += (p > (char **)parts) * sep_len + strlen(*p); + + buf_len = result_len + 1; + result = calloc(buf_len, 1); + if (!result) + return NULL; + + if (use_as_prefix) + (void)strlcpy(result, sep, buf_len); + + for (p = (char **)parts; *p; p++) { + if (p > (char **)parts) + (void)strlcat(result, sep, buf_len); + + (void)strlcat(result, *p, buf_len); + } + + return result; +} + +char **lxc_normalize_path(const char *path) +{ + char **components; + char **p; + size_t components_len = 0; + size_t pos = 0; + + components = lxc_string_split(path, '/'); + if (!components) + return NULL; + + for (p = components; *p; p++) + components_len++; + + /* resolve '.' and '..' */ + for (pos = 0; pos < components_len;) { + if (!strcmp(components[pos], ".") || + (!strcmp(components[pos], "..") && pos == 0)) { + /* eat this element */ + free(components[pos]); + memmove(&components[pos], &components[pos + 1], + sizeof(char *) * (components_len - pos)); + components_len--; + } else if (!strcmp(components[pos], "..")) { + /* eat this and the previous element */ + free(components[pos - 1]); + free(components[pos]); + memmove(&components[pos - 1], &components[pos + 1], + sizeof(char *) * (components_len - pos)); + components_len -= 2; + pos--; + } else { + pos++; + } + } + + return components; +} + +char *lxc_deslashify(const char *path) +{ + char *dup, *p; + char **parts = NULL; + size_t n, len; + + dup = strdup(path); + if (!dup) + return NULL; + + parts = lxc_normalize_path(dup); + if (!parts) { + free(dup); + return NULL; + } + + /* We'll end up here if path == "///" or path == "". */ + if (!*parts) { + len = strlen(dup); + if (!len) { + lxc_free_array((void **)parts, free); + return dup; + } + + n = strcspn(dup, "/"); + if (n == len) { + free(dup); + lxc_free_array((void **)parts, free); + + p = strdup("/"); + if (!p) + return NULL; + + return p; + } + } + + p = lxc_string_join("/", (const char **)parts, *dup == '/'); + free(dup); + lxc_free_array((void **)parts, free); + return p; +} + +char *lxc_append_paths(const char *first, const char *second) +{ + int ret; + size_t len; + char *result = NULL; + const char *pattern = "%s%s"; + + len = strlen(first) + strlen(second) + 1; + if (second[0] != '/') { + len += 1; + pattern = "%s/%s"; + } + + result = calloc(1, len); + if (!result) + return NULL; + + ret = snprintf(result, len, pattern, first, second); + if (ret < 0 || (size_t)ret >= len) { + free(result); + return NULL; + } + + return result; +} + +bool lxc_string_in_list(const char *needle, const char *haystack, char _sep) +{ + char *token, *str; + char sep[2] = { _sep, '\0' }; + size_t len; + + if (!haystack || !needle) + return 0; + + len = strlen(haystack); + str = alloca(len + 1); + (void)strlcpy(str, haystack, len + 1); + + lxc_iterate_parts(token, str, sep) + if (strcmp(needle, token) == 0) + return 1; + + return 0; +} + +char **lxc_string_split(const char *string, char _sep) +{ + char *token, *str; + char sep[2] = {_sep, '\0'}; + char **tmp = NULL, **result = NULL; + size_t result_capacity = 0; + size_t result_count = 0; + int r, saved_errno; + size_t len; + + if (!string) + return calloc(1, sizeof(char *)); + + len = strlen(string); + str = alloca(len + 1); + (void)strlcpy(str, string, len + 1); + + lxc_iterate_parts(token, str, sep) { + r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16); + if (r < 0) + goto error_out; + + result[result_count] = strdup(token); + if (!result[result_count]) + goto error_out; + + result_count++; + } + + /* if we allocated too much, reduce it */ + tmp = realloc(result, (result_count + 1) * sizeof(char *)); + if (!tmp) + goto error_out; + + result = tmp; + + /* Make sure we don't return uninitialized memory. */ + if (result_count == 0) + *result = NULL; + + return result; + +error_out: + saved_errno = errno; + lxc_free_array((void **)result, free); + errno = saved_errno; + return NULL; +} + +static bool complete_word(char ***result, char *start, char *end, size_t *cap, + size_t *cnt) +{ + int r; + + r = lxc_grow_array((void ***)result, cap, 2 + *cnt, 16); + if (r < 0) + return false; + + (*result)[*cnt] = strndup(start, end - start); + if (!(*result)[*cnt]) + return false; + + (*cnt)++; + + return true; +} + +/* + * Given a a string 'one two "three four"', split into three words, + * one, two, and "three four" + */ +char **lxc_string_split_quoted(char *string) +{ + char *nextword = string, *p, state; + char **result = NULL; + size_t result_capacity = 0; + size_t result_count = 0; + + if (!string || !*string) + return calloc(1, sizeof(char *)); + + // TODO I'm *not* handling escaped quote + state = ' '; + for (p = string; *p; p++) { + switch(state) { + case ' ': + if (isspace(*p)) + continue; + else if (*p == '"' || *p == '\'') { + nextword = p; + state = *p; + continue; + } + nextword = p; + state = 'a'; + continue; + case 'a': + if (isspace(*p)) { + complete_word(&result, nextword, p, &result_capacity, &result_count); + state = ' '; + continue; + } + continue; + case '"': + case '\'': + if (*p == state) { + complete_word(&result, nextword+1, p, &result_capacity, &result_count); + state = ' '; + continue; + } + continue; + } + } + + if (state == 'a') + complete_word(&result, nextword, p, &result_capacity, &result_count); + + return realloc(result, (result_count + 1) * sizeof(char *)); +} + +char **lxc_string_split_and_trim(const char *string, char _sep) +{ + char *token, *str; + char sep[2] = { _sep, '\0' }; + char **result = NULL; + size_t result_capacity = 0; + size_t result_count = 0; + int r, saved_errno; + size_t i = 0; + size_t len; + + if (!string) + return calloc(1, sizeof(char *)); + + len = strlen(string); + str = alloca(len + 1); + (void)strlcpy(str, string, len + 1); + + lxc_iterate_parts(token, str, sep) { + while (token[0] == ' ' || token[0] == '\t') + token++; + + i = strlen(token); + while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) { + token[i - 1] = '\0'; + i--; + } + + r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16); + if (r < 0) + goto error_out; + + result[result_count] = strdup(token); + if (!result[result_count]) + goto error_out; + + result_count++; + } + + /* if we allocated too much, reduce it */ + return realloc(result, (result_count + 1) * sizeof(char *)); + +error_out: + saved_errno = errno; + lxc_free_array((void **)result, free); + errno = saved_errno; + return NULL; +} + +void lxc_free_array(void **array, lxc_free_fn element_free_fn) +{ + void **p; + + for (p = array; p && *p; p++) + element_free_fn(*p); + + free((void*)array); +} + +int lxc_grow_array(void ***array, size_t *capacity, size_t new_size, size_t capacity_increment) +{ + size_t new_capacity; + void **new_array; + + /* first time around, catch some trivial mistakes of the user + * only initializing one of these */ + if (!*array || !*capacity) { + *array = NULL; + *capacity = 0; + } + + new_capacity = *capacity; + while (new_size + 1 > new_capacity) + new_capacity += capacity_increment; + + if (new_capacity != *capacity) { + /* we have to reallocate */ + new_array = realloc(*array, new_capacity * sizeof(void *)); + if (!new_array) + return -1; + + memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *)); + *array = new_array; + *capacity = new_capacity; + } + + /* array has sufficient elements */ + return 0; +} + +size_t lxc_array_len(void **array) +{ + void **p; + size_t result = 0; + + for (p = array; p && *p; p++) + result++; + + return result; +} + +void **lxc_append_null_to_array(void **array, size_t count) +{ + void **temp; + + /* Append NULL to the array */ + if (count) { + temp = realloc(array, (count + 1) * sizeof(*array)); + if (!temp) { + size_t i; + for (i = 0; i < count; i++) + free(array[i]); + free(array); + return NULL; + } + + array = temp; + array[count] = NULL; + } + + return array; +} + +static int lxc_append_null_to_list(void ***list) +{ + int newentry = 0; + void **tmp; + + if (*list) + for (; (*list)[newentry]; newentry++) { + ; + } + + tmp = realloc(*list, (newentry + 2) * sizeof(void **)); + if (!tmp) + return -1; + + *list = tmp; + (*list)[newentry + 1] = NULL; + + return newentry; +} + +int lxc_append_string(char ***list, char *entry) +{ + char *copy; + int newentry; + + newentry = lxc_append_null_to_list((void ***)list); + if (newentry < 0) + return -1; + + copy = strdup(entry); + if (!copy) + return -1; + + (*list)[newentry] = copy; + + return 0; +} + +int lxc_safe_uint(const char *numstr, unsigned int *converted) +{ + char *err = NULL; + unsigned long int uli; + + while (isspace(*numstr)) + numstr++; + + if (*numstr == '-') + return -EINVAL; + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno == ERANGE && uli == ULONG_MAX) + return -ERANGE; + + if (err == numstr || *err != '\0') + return -EINVAL; + + if (uli > UINT_MAX) + return -ERANGE; + + *converted = (unsigned int)uli; + return 0; +} + +int lxc_safe_ulong(const char *numstr, unsigned long *converted) +{ + char *err = NULL; + unsigned long int uli; + + while (isspace(*numstr)) + numstr++; + + if (*numstr == '-') + return -EINVAL; + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno == ERANGE && uli == ULONG_MAX) + return -ERANGE; + + if (err == numstr || *err != '\0') + return -EINVAL; + + *converted = uli; + return 0; +} + +int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base) +{ + char *err = NULL; + uint64_t u; + + while (isspace(*numstr)) + numstr++; + + if (*numstr == '-') + return -EINVAL; + + errno = 0; + u = strtoull(numstr, &err, base); + if (errno == ERANGE && u == ULLONG_MAX) + return -ERANGE; + + if (err == numstr || *err != '\0') + return -EINVAL; + + *converted = u; + return 0; +} + +int lxc_safe_int(const char *numstr, int *converted) +{ + char *err = NULL; + signed long int sli; + + errno = 0; + sli = strtol(numstr, &err, 0); + if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) + return -ERANGE; + + if (errno != 0 && sli == 0) + return -EINVAL; + + if (err == numstr || *err != '\0') + return -EINVAL; + + if (sli > INT_MAX || sli < INT_MIN) + return -ERANGE; + + *converted = (int)sli; + return 0; +} + +int lxc_safe_long(const char *numstr, long int *converted) +{ + char *err = NULL; + signed long int sli; + + errno = 0; + sli = strtol(numstr, &err, 0); + if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) + return -ERANGE; + + if (errno != 0 && sli == 0) + return -EINVAL; + + if (err == numstr || *err != '\0') + return -EINVAL; + + *converted = sli; + 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; +} + +char *must_concat(const char *first, ...) +{ + va_list args; + char *cur, *dest; + size_t cur_len, it_len; + + dest = must_copy_string(first); + cur_len = it_len = strlen(first); + + va_start(args, first); + while ((cur = va_arg(args, char *)) != NULL) { + it_len = strlen(cur); + + dest = must_realloc(dest, cur_len + it_len + 1); + + (void)memcpy(dest + cur_len, cur, it_len); + cur_len += it_len; + } + va_end(args); + + dest[cur_len] = 0; + return dest; +} + +char *must_make_path(const char *first, ...) +{ + va_list args; + char *cur, *dest; + size_t full_len = strlen(first); + size_t buf_len; + + dest = must_copy_string(first); + + va_start(args, first); + while ((cur = va_arg(args, char *)) != NULL) { + full_len += strlen(cur); + if (cur[0] != '/') + full_len++; + + buf_len = full_len + 1; + dest = must_realloc(dest, buf_len); + + if (cur[0] != '/') + (void)strlcat(dest, "/", buf_len); + (void)strlcat(dest, cur, buf_len); + } + va_end(args); + + return dest; +} + +char *must_append_path(char *first, ...) +{ + char *cur; + size_t full_len; + va_list args; + char *dest = first; + size_t buf_len; + + full_len = strlen(first); + va_start(args, first); + while ((cur = va_arg(args, char *)) != NULL) { + full_len += strlen(cur); + if (cur[0] != '/') + full_len++; + + buf_len = full_len + 1; + dest = must_realloc(dest, buf_len); + + if (cur[0] != '/') + (void)strlcat(dest, "/", buf_len); + (void)strlcat(dest, cur, buf_len); + } + va_end(args); + + return dest; +} + +char *must_copy_string(const char *entry) +{ + char *ret; + + if (!entry) + return NULL; + + do { + ret = strdup(entry); + } while (!ret); + + return ret; +} + +void *must_realloc(void *orig, size_t sz) +{ + void *ret; + + do { + ret = realloc(orig, sz); + } while (!ret); + + return ret; +} + +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] = {0}; + + if (!s || !strcmp(s, "")) + return -EINVAL; + + end = stpncpy(dup, s, sizeof(dup) - 1); + if (*end != '\0') + return -EINVAL; + + if (isdigit(*(end - 1))) + suffix_len = 0; + else if (isalpha(*(end - 1))) + suffix_len = 1; + else + return -EINVAL; + + if (suffix_len > 0 && (end - 2) == dup && !isdigit(*(end - 2))) + return -EINVAL; + + if (suffix_len > 0 && isalpha(*(end - 2))) + suffix_len++; + + 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 (strcasecmp(suffix, "KB") == 0) + mltpl = 1024; + else if (strcasecmp(suffix, "MB") == 0) + mltpl = 1024 * 1024; + else if (strcasecmp(suffix, "GB") == 0) + mltpl = 1024 * 1024 * 1024; + else + return -EINVAL; + + overflow = conv * mltpl; + if (conv != 0 && (overflow / conv) != mltpl) + return -ERANGE; + + *converted = overflow; + return 0; +} + +void remove_trailing_newlines(char *l) +{ + char *p = l; + + while (*p) + p++; + + while (--p >= l && *p == '\n') + *p = '\0'; +} + +int lxc_char_left_gc(const char *buffer, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (buffer[i] == ' ' || + buffer[i] == '\t') + continue; + + return i; + } + + return 0; +} + +int lxc_char_right_gc(const char *buffer, size_t len) +{ + int i; + + for (i = len - 1; i >= 0; i--) { + if (buffer[i] == ' ' || + buffer[i] == '\t' || + buffer[i] == '\n' || + buffer[i] == '\0') + continue; + + return i + 1; + } + + return 0; +} + +char *lxc_trim_whitespace_in_place(char *buffer) +{ + buffer += lxc_char_left_gc(buffer, strlen(buffer)); + buffer[lxc_char_right_gc(buffer, strlen(buffer))] = '\0'; + return buffer; +} + +int lxc_is_line_empty(const char *line) +{ + int i; + size_t len = strlen(line); + + for (i = 0; i < len; i++) + if (line[i] != ' ' && line[i] != '\t' && + line[i] != '\n' && line[i] != '\r' && + line[i] != '\f' && line[i] != '\0') + return 0; + return 1; +} diff --git a/src/lxc/string_utils.h b/src/lxc/string_utils.h new file mode 100644 index 0000000000..c1155f64a0 --- /dev/null +++ b/src/lxc/string_utils.h @@ -0,0 +1,131 @@ +/* liblxcapi + * + * Copyright © 2018 Christian Brauner . + * Copyright © 2018 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_STRING_UTILS_H +#define __LXC_STRING_UTILS_H + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LINUX_MEMFD_H +#include +#endif + +#include "initutils.h" +#include "macro.h" + +/* convert variadic argument lists to arrays (for execl type argument lists) */ +extern char **lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup); +extern const char **lxc_va_arg_list_to_argv_const(va_list ap, size_t skip); + +/* + * Some simple string functions; if they return pointers, they are allocated + * buffers. + */ +extern char *lxc_string_replace(const char *needle, const char *replacement, + const char *haystack); +extern bool lxc_string_in_array(const char *needle, const char **haystack); +extern char *lxc_string_join(const char *sep, const char **parts, + bool use_as_prefix); +/* + * Normalize and split path: Leading and trailing / are removed, multiple + * / are compactified, .. and . are resolved (.. on the top level is considered + * identical to .). + * Examples: + * / -> { NULL } + * foo/../bar -> { bar, NULL } + * ../../ -> { NULL } + * ./bar/baz/.. -> { bar, NULL } + * foo//bar -> { foo, bar, NULL } + */ +extern char **lxc_normalize_path(const char *path); + +/* remove multiple slashes from the path, e.g. ///foo//bar -> /foo/bar */ +extern char *lxc_deslashify(const char *path); +extern char *lxc_append_paths(const char *first, const char *second); + +/* + * Note: the following two functions use strtok(), so they will never + * consider an empty element, even if two delimiters are next to + * each other. + */ +extern bool lxc_string_in_list(const char *needle, const char *haystack, + char sep); +extern char **lxc_string_split(const char *string, char sep); +extern char **lxc_string_split_and_trim(const char *string, char sep); +extern char **lxc_string_split_quoted(char *string); + +/* Append string to NULL-terminated string array. */ +extern int lxc_append_string(char ***list, char *entry); + +/* Some simple array manipulation utilities */ +typedef void (*lxc_free_fn)(void *); +typedef void *(*lxc_dup_fn)(void *); +extern int lxc_grow_array(void ***array, size_t *capacity, size_t new_size, + size_t capacity_increment); +extern void lxc_free_array(void **array, lxc_free_fn element_free_fn); +extern size_t lxc_array_len(void **array); + +extern void **lxc_append_null_to_array(void **array, size_t count); +extern void remove_trailing_newlines(char *l); + +/* Helper functions to parse numbers. */ +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); +extern int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base); +/* Handles B, kb, MB, GB. Detects overflows and reports -ERANGE. */ +extern int parse_byte_size_string(const char *s, int64_t *converted); + +/* + * Concatenate all passed-in strings into one path. Do not fail. If any piece + * is not prefixed with '/', add a '/'. + */ +__attribute__((sentinel)) extern char *must_concat(const char *first, ...); +__attribute__((sentinel)) extern char *must_make_path(const char *first, ...); +__attribute__((sentinel)) extern char *must_append_path(char *first, ...); + +/* Return copy of string @entry. Do not fail. */ +extern char *must_copy_string(const char *entry); + +/* Re-alllocate a pointer, do not fail */ +extern void *must_realloc(void *orig, size_t sz); + +extern int lxc_char_left_gc(const char *buffer, size_t len); + +extern int lxc_char_right_gc(const char *buffer, size_t len); + +extern char *lxc_trim_whitespace_in_place(char *buffer); + +extern int lxc_is_line_empty(const char *line); + +#endif /* __LXC_STRING_UTILS_H */ diff --git a/src/lxc/utils.c b/src/lxc/utils.c index 22c72272fb..58dd223a97 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -166,15 +166,6 @@ static int _recursive_rmdir(const char *dirname, dev_t pdev, return failed ? -1 : 0; } -/* We have two different magic values for overlayfs, yay. */ -#ifndef OVERLAYFS_SUPER_MAGIC -#define OVERLAYFS_SUPER_MAGIC 0x794c764f -#endif - -#ifndef OVERLAY_SUPER_MAGIC -#define OVERLAY_SUPER_MAGIC 0x794c7630 -#endif - /* In overlayfs, st_dev is unreliable. So on overlayfs we don't do the * lxc_rmdir_onedev() */ @@ -327,47 +318,6 @@ int lxc_wait_for_pid_status(pid_t pid) return status; } -ssize_t lxc_write_nointr(int fd, const void *buf, size_t count) -{ - ssize_t ret; -again: - ret = write(fd, buf, count); - if (ret < 0 && errno == EINTR) - goto again; - - return ret; -} - -ssize_t lxc_read_nointr(int fd, void *buf, size_t count) -{ - ssize_t ret; -again: - ret = read(fd, buf, count); - if (ret < 0 && errno == EINTR) - goto again; - - return ret; -} - -ssize_t lxc_read_nointr_expect(int fd, void *buf, size_t count, const void *expected_buf) -{ - ssize_t ret; - - ret = lxc_read_nointr(fd, buf, count); - if (ret <= 0) - return ret; - - if ((size_t)ret != count) - return -1; - - if (expected_buf && memcmp(buf, expected_buf, count) != 0) { - errno = EINVAL; - return -1; - } - - return ret; -} - #if HAVE_LIBGNUTLS #include #include @@ -438,52 +388,6 @@ int sha1sum_file(char *fnam, unsigned char *digest) } #endif -char** lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup) -{ - va_list ap2; - size_t count = 1 + skip; - char **result; - - /* first determine size of argument list, we don't want to reallocate - * constantly... - */ - va_copy(ap2, ap); - while (1) { - char* arg = va_arg(ap2, char*); - if (!arg) - break; - count++; - } - va_end(ap2); - - result = calloc(count, sizeof(char*)); - if (!result) - return NULL; - - count = skip; - while (1) { - char* arg = va_arg(ap, char*); - if (!arg) - break; - arg = do_strdup ? strdup(arg) : arg; - if (!arg) - goto oom; - result[count++] = arg; - } - - /* calloc has already set last element to NULL*/ - return result; - -oom: - free(result); - return NULL; -} - -const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip) -{ - return (const char**)lxc_va_arg_list_to_argv(ap, skip, 0); -} - struct lxc_popen_FILE *lxc_popen(const char *command) { int ret; @@ -598,539 +502,6 @@ int lxc_pclose(struct lxc_popen_FILE *fp) return wstatus; } -char *lxc_string_replace(const char *needle, const char *replacement, const char *haystack) -{ - ssize_t len = -1, saved_len = -1; - char *result = NULL; - size_t replacement_len = strlen(replacement); - size_t needle_len = strlen(needle); - - /* should be executed exactly twice */ - while (len == -1 || result == NULL) { - char *p; - char *last_p; - ssize_t part_len; - - if (len != -1) { - result = calloc(1, len + 1); - if (!result) - return NULL; - - saved_len = len; - } - - len = 0; - - for (last_p = (char *)haystack, p = strstr(last_p, needle); p; last_p = p, p = strstr(last_p, needle)) { - part_len = (ssize_t)(p - last_p); - if (result && part_len > 0) - memcpy(&result[len], last_p, part_len); - - len += part_len; - - if (result && replacement_len > 0) - memcpy(&result[len], replacement, replacement_len); - - len += replacement_len; - p += needle_len; - } - - part_len = strlen(last_p); - if (result && part_len > 0) - memcpy(&result[len], last_p, part_len); - - len += part_len; - } - - /* make sure we did the same thing twice, - * once for calculating length, the other - * time for copying data */ - if (saved_len != len) { - free(result); - return NULL; - } - - /* make sure we didn't overwrite any buffer, - * due to calloc the string should be 0-terminated */ - if (result[len] != '\0') { - free(result); - return NULL; - } - - return result; -} - -bool lxc_string_in_array(const char *needle, const char **haystack) -{ - for (; haystack && *haystack; haystack++) - if (!strcmp(needle, *haystack)) - return true; - - return false; -} - -char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix) -{ - char *result; - char **p; - size_t sep_len = strlen(sep); - size_t result_len = use_as_prefix * sep_len; - size_t buf_len; - - /* calculate new string length */ - for (p = (char **)parts; *p; p++) - result_len += (p > (char **)parts) * sep_len + strlen(*p); - - buf_len = result_len + 1; - result = calloc(buf_len, 1); - if (!result) - return NULL; - - if (use_as_prefix) - (void)strlcpy(result, sep, buf_len); - - for (p = (char **)parts; *p; p++) { - if (p > (char **)parts) - (void)strlcat(result, sep, buf_len); - - (void)strlcat(result, *p, buf_len); - } - - return result; -} - -char **lxc_normalize_path(const char *path) -{ - char **components; - char **p; - size_t components_len = 0; - size_t pos = 0; - - components = lxc_string_split(path, '/'); - if (!components) - return NULL; - - for (p = components; *p; p++) - components_len++; - - /* resolve '.' and '..' */ - for (pos = 0; pos < components_len; ) { - if (!strcmp(components[pos], ".") || (!strcmp(components[pos], "..") && pos == 0)) { - /* eat this element */ - free(components[pos]); - memmove(&components[pos], &components[pos+1], sizeof(char *) * (components_len - pos)); - components_len--; - } else if (!strcmp(components[pos], "..")) { - /* eat this and the previous element */ - free(components[pos - 1]); - free(components[pos]); - memmove(&components[pos-1], &components[pos+1], sizeof(char *) * (components_len - pos)); - components_len -= 2; - pos--; - } else { - pos++; - } - } - - return components; -} - -char *lxc_deslashify(const char *path) -{ - char *dup, *p; - char **parts = NULL; - size_t n, len; - - dup = strdup(path); - if (!dup) - return NULL; - - parts = lxc_normalize_path(dup); - if (!parts) { - free(dup); - return NULL; - } - - /* We'll end up here if path == "///" or path == "". */ - if (!*parts) { - len = strlen(dup); - if (!len) { - lxc_free_array((void **)parts, free); - return dup; - } - - n = strcspn(dup, "/"); - if (n == len) { - free(dup); - lxc_free_array((void **)parts, free); - - p = strdup("/"); - if (!p) - return NULL; - - return p; - } - } - - p = lxc_string_join("/", (const char **)parts, *dup == '/'); - free(dup); - lxc_free_array((void **)parts, free); - return p; -} - -char *lxc_append_paths(const char *first, const char *second) -{ - int ret; - size_t len; - char *result = NULL; - const char *pattern = "%s%s"; - - len = strlen(first) + strlen(second) + 1; - if (second[0] != '/') { - len += 1; - pattern = "%s/%s"; - } - - result = calloc(1, len); - if (!result) - return NULL; - - ret = snprintf(result, len, pattern, first, second); - if (ret < 0 || (size_t)ret >= len) { - free(result); - return NULL; - } - - return result; -} - -bool lxc_string_in_list(const char *needle, const char *haystack, char _sep) -{ - char *token, *str; - char sep[2] = { _sep, '\0' }; - size_t len; - - if (!haystack || !needle) - return 0; - - len = strlen(haystack); - str = alloca(len + 1); - (void)strlcpy(str, haystack, len + 1); - - lxc_iterate_parts(token, str, sep) - if (strcmp(needle, token) == 0) - return 1; - - return 0; -} - -char **lxc_string_split(const char *string, char _sep) -{ - char *token, *str; - char sep[2] = {_sep, '\0'}; - char **tmp = NULL, **result = NULL; - size_t result_capacity = 0; - size_t result_count = 0; - int r, saved_errno; - size_t len; - - if (!string) - return calloc(1, sizeof(char *)); - - len = strlen(string); - str = alloca(len + 1); - (void)strlcpy(str, string, len + 1); - - lxc_iterate_parts(token, str, sep) { - r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16); - if (r < 0) - goto error_out; - - result[result_count] = strdup(token); - if (!result[result_count]) - goto error_out; - - result_count++; - } - - /* if we allocated too much, reduce it */ - tmp = realloc(result, (result_count + 1) * sizeof(char *)); - if (!tmp) - goto error_out; - - result = tmp; - - /* Make sure we don't return uninitialized memory. */ - if (result_count == 0) - *result = NULL; - - return result; - -error_out: - saved_errno = errno; - lxc_free_array((void **)result, free); - errno = saved_errno; - return NULL; -} - -static bool complete_word(char ***result, char *start, char *end, size_t *cap, size_t *cnt) -{ - int r; - - r = lxc_grow_array((void ***)result, cap, 2 + *cnt, 16); - if (r < 0) - return false; - - (*result)[*cnt] = strndup(start, end - start); - if (!(*result)[*cnt]) - return false; - - (*cnt)++; - - return true; -} - -/* - * Given a a string 'one two "three four"', split into three words, - * one, two, and "three four" - */ -char **lxc_string_split_quoted(char *string) -{ - char *nextword = string, *p, state; - char **result = NULL; - size_t result_capacity = 0; - size_t result_count = 0; - - if (!string || !*string) - return calloc(1, sizeof(char *)); - - // TODO I'm *not* handling escaped quote - state = ' '; - for (p = string; *p; p++) { - switch(state) { - case ' ': - if (isspace(*p)) - continue; - else if (*p == '"' || *p == '\'') { - nextword = p; - state = *p; - continue; - } - nextword = p; - state = 'a'; - continue; - case 'a': - if (isspace(*p)) { - complete_word(&result, nextword, p, &result_capacity, &result_count); - state = ' '; - continue; - } - continue; - case '"': - case '\'': - if (*p == state) { - complete_word(&result, nextword+1, p, &result_capacity, &result_count); - state = ' '; - continue; - } - continue; - } - } - - if (state == 'a') - complete_word(&result, nextword, p, &result_capacity, &result_count); - - return realloc(result, (result_count + 1) * sizeof(char *)); -} - -char **lxc_string_split_and_trim(const char *string, char _sep) -{ - char *token, *str; - char sep[2] = { _sep, '\0' }; - char **result = NULL; - size_t result_capacity = 0; - size_t result_count = 0; - int r, saved_errno; - size_t i = 0; - size_t len; - - if (!string) - return calloc(1, sizeof(char *)); - - len = strlen(string); - str = alloca(len + 1); - (void)strlcpy(str, string, len + 1); - - lxc_iterate_parts(token, str, sep) { - while (token[0] == ' ' || token[0] == '\t') - token++; - - i = strlen(token); - while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) { - token[i - 1] = '\0'; - i--; - } - - r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16); - if (r < 0) - goto error_out; - - result[result_count] = strdup(token); - if (!result[result_count]) - goto error_out; - - result_count++; - } - - /* if we allocated too much, reduce it */ - return realloc(result, (result_count + 1) * sizeof(char *)); - -error_out: - saved_errno = errno; - lxc_free_array((void **)result, free); - errno = saved_errno; - return NULL; -} - -void lxc_free_array(void **array, lxc_free_fn element_free_fn) -{ - void **p; - - for (p = array; p && *p; p++) - element_free_fn(*p); - - free((void*)array); -} - -int lxc_grow_array(void ***array, size_t *capacity, size_t new_size, size_t capacity_increment) -{ - size_t new_capacity; - void **new_array; - - /* first time around, catch some trivial mistakes of the user - * only initializing one of these */ - if (!*array || !*capacity) { - *array = NULL; - *capacity = 0; - } - - new_capacity = *capacity; - while (new_size + 1 > new_capacity) - new_capacity += capacity_increment; - - if (new_capacity != *capacity) { - /* we have to reallocate */ - new_array = realloc(*array, new_capacity * sizeof(void *)); - if (!new_array) - return -1; - - memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *)); - *array = new_array; - *capacity = new_capacity; - } - - /* array has sufficient elements */ - return 0; -} - -size_t lxc_array_len(void **array) -{ - void **p; - size_t result = 0; - - for (p = array; p && *p; p++) - result++; - - return result; -} - -int lxc_write_to_file(const char *filename, const void *buf, size_t count, - bool add_newline, mode_t mode) -{ - int fd, saved_errno; - ssize_t ret; - - fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); - if (fd < 0) - return -1; - - ret = lxc_write_nointr(fd, buf, count); - if (ret < 0) - goto out_error; - - if ((size_t)ret != count) - goto out_error; - - if (add_newline) { - ret = lxc_write_nointr(fd, "\n", 1); - if (ret != 1) - goto out_error; - } - - close(fd); - return 0; - -out_error: - saved_errno = errno; - close(fd); - errno = saved_errno; - return -1; -} - -int lxc_read_from_file(const char *filename, void *buf, size_t count) -{ - int fd = -1, saved_errno; - ssize_t ret; - - fd = open(filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) - return -1; - - if (!buf || !count) { - char buf2[100]; - size_t count2 = 0; - - while ((ret = lxc_read_nointr(fd, buf2, 100)) > 0) - count2 += ret; - - if (ret >= 0) - ret = count2; - } else { - memset(buf, 0, count); - ret = lxc_read_nointr(fd, buf, count); - } - - if (ret < 0) - SYSERROR("Read %s", filename); - - saved_errno = errno; - close(fd); - errno = saved_errno; - return ret; -} - -void **lxc_append_null_to_array(void **array, size_t count) -{ - void **temp; - - /* Append NULL to the array */ - if (count) { - temp = realloc(array, (count + 1) * sizeof(*array)); - if (!temp) { - size_t i; - for (i = 0; i < count; i++) - free(array[i]); - free(array); - return NULL; - } - - array = temp; - array[count] = NULL; - } - - return array; -} - int randseed(bool srand_it) { /* @@ -1268,7 +639,8 @@ int detect_shared_rootfs(void) return 0; } -bool switch_to_ns(pid_t pid, const char *ns) { +bool switch_to_ns(pid_t pid, const char *ns) +{ int fd, ret; char nspath[MAXPATHLEN]; @@ -1341,7 +713,8 @@ bool detect_ramfs_rootfs(void) return false; } -char *on_path(const char *cmd, const char *rootfs) { +char *on_path(const char *cmd, const char *rootfs) +{ char *entry = NULL, *path = NULL; char cmdpath[MAXPATHLEN]; int ret; @@ -1354,9 +727,10 @@ char *on_path(const char *cmd, const char *rootfs) { if (!path) return NULL; - lxc_iterate_parts(entry, path, ":") { + lxc_iterate_parts (entry, path, ":") { if (rootfs) - ret = snprintf(cmdpath, MAXPATHLEN, "%s/%s/%s", rootfs, entry, cmd); + ret = snprintf(cmdpath, MAXPATHLEN, "%s/%s/%s", rootfs, + entry, cmd); else ret = snprintf(cmdpath, MAXPATHLEN, "%s/%s", entry, cmd); if (ret < 0 || ret >= MAXPATHLEN) @@ -1372,13 +746,6 @@ char *on_path(const char *cmd, const char *rootfs) { return NULL; } -bool file_exists(const char *f) -{ - struct stat statbuf; - - return stat(f, &statbuf) == 0; -} - bool cgns_supported(void) { return file_exists("/proc/self/ns/cgroup"); @@ -1480,34 +847,6 @@ char *choose_init(const char *rootfs) return NULL; } -int print_to_file(const char *file, const char *content) -{ - FILE *f; - int ret = 0; - - f = fopen(file, "w"); - if (!f) - return -1; - - if (fprintf(f, "%s", content) != strlen(content)) - ret = -1; - - fclose(f); - return ret; -} - -int is_dir(const char *path) -{ - struct stat statbuf; - int ret; - - ret = stat(path, &statbuf); - if (ret == 0 && S_ISDIR(statbuf.st_mode)) - return 1; - - return 0; -} - /* * Given the '-t' template option to lxc-create, figure out what to * do. If the template is a full executable path, use that. If it @@ -1919,29 +1258,6 @@ int null_stdfds(void) return ret; } -/* - * Return the number of lines in file @fn, or -1 on error - */ -int lxc_count_file_lines(const char *fn) -{ - FILE *f; - char *line = NULL; - size_t sz = 0; - int n = 0; - - f = fopen_cloexec(fn, "r"); - if (!f) - return -1; - - while (getline(&line, &sz, f) != -1) { - n++; - } - - free(line); - fclose(f); - return n; -} - /* Check whether a signal is blocked by a process. */ /* /proc/pid-to-str/status\0 = (5 + 21 + 7 + 1) */ #define __PROC_STATUS_LEN (6 + (LXC_NUMSTRLEN64) + 7 + 1) @@ -1986,44 +1302,6 @@ bool task_blocks_signal(pid_t pid, int signal) return bret; } -static int lxc_append_null_to_list(void ***list) -{ - int newentry = 0; - void **tmp; - - if (*list) - for (; (*list)[newentry]; newentry++) { - ; - } - - tmp = realloc(*list, (newentry + 2) * sizeof(void **)); - if (!tmp) - return -1; - - *list = tmp; - (*list)[newentry + 1] = NULL; - - return newentry; -} - -int lxc_append_string(char ***list, char *entry) -{ - char *copy; - int newentry; - - newentry = lxc_append_null_to_list((void ***)list); - if (newentry < 0) - return -1; - - copy = strdup(entry); - if (!copy) - return -1; - - (*list)[newentry] = copy; - - return 0; -} - int lxc_preserve_ns(const int pid, const char *ns) { int ret; @@ -2046,141 +1324,6 @@ int lxc_preserve_ns(const int pid, const char *ns) return open(path, O_RDONLY | O_CLOEXEC); } -int lxc_safe_uint(const char *numstr, unsigned int *converted) -{ - char *err = NULL; - unsigned long int uli; - - while (isspace(*numstr)) - numstr++; - - if (*numstr == '-') - return -EINVAL; - - errno = 0; - uli = strtoul(numstr, &err, 0); - if (errno == ERANGE && uli == ULONG_MAX) - return -ERANGE; - - if (err == numstr || *err != '\0') - return -EINVAL; - - if (uli > UINT_MAX) - return -ERANGE; - - *converted = (unsigned int)uli; - return 0; -} - -int lxc_safe_ulong(const char *numstr, unsigned long *converted) -{ - char *err = NULL; - unsigned long int uli; - - while (isspace(*numstr)) - numstr++; - - if (*numstr == '-') - return -EINVAL; - - errno = 0; - uli = strtoul(numstr, &err, 0); - if (errno == ERANGE && uli == ULONG_MAX) - return -ERANGE; - - if (err == numstr || *err != '\0') - return -EINVAL; - - *converted = uli; - return 0; -} - -int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base) -{ - char *err = NULL; - uint64_t u; - - while (isspace(*numstr)) - numstr++; - - if (*numstr == '-') - return -EINVAL; - - errno = 0; - u = strtoull(numstr, &err, base); - if (errno == ERANGE && u == ULLONG_MAX) - return -ERANGE; - - if (err == numstr || *err != '\0') - return -EINVAL; - - *converted = u; - return 0; -} - -int lxc_safe_int(const char *numstr, int *converted) -{ - char *err = NULL; - signed long int sli; - - errno = 0; - sli = strtol(numstr, &err, 0); - if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) - return -ERANGE; - - if (errno != 0 && sli == 0) - return -EINVAL; - - if (err == numstr || *err != '\0') - return -EINVAL; - - if (sli > INT_MAX || sli < INT_MIN) - return -ERANGE; - - *converted = (int)sli; - return 0; -} - -int lxc_safe_long(const char *numstr, long int *converted) -{ - char *err = NULL; - signed long int sli; - - errno = 0; - sli = strtol(numstr, &err, 0); - if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) - return -ERANGE; - - if (errno != 0 && sli == 0) - return -EINVAL; - - if (err == numstr || *err != '\0') - return -EINVAL; - - *converted = sli; - 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) { @@ -2426,143 +1569,6 @@ int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args) return fret; } -char *must_concat(const char *first, ...) -{ - va_list args; - char *cur, *dest; - size_t cur_len, it_len; - - dest = must_copy_string(first); - cur_len = it_len = strlen(first); - - va_start(args, first); - while ((cur = va_arg(args, char *)) != NULL) { - it_len = strlen(cur); - - dest = must_realloc(dest, cur_len + it_len + 1); - - (void)memcpy(dest + cur_len, cur, it_len); - cur_len += it_len; - } - va_end(args); - - dest[cur_len] = 0; - return dest; -} - -char *must_make_path(const char *first, ...) -{ - va_list args; - char *cur, *dest; - size_t full_len = strlen(first); - size_t buf_len; - - dest = must_copy_string(first); - - va_start(args, first); - while ((cur = va_arg(args, char *)) != NULL) { - full_len += strlen(cur); - if (cur[0] != '/') - full_len++; - - buf_len = full_len + 1; - dest = must_realloc(dest, buf_len); - - if (cur[0] != '/') - (void)strlcat(dest, "/", buf_len); - (void)strlcat(dest, cur, buf_len); - } - va_end(args); - - return dest; -} - -char *must_append_path(char *first, ...) -{ - char *cur; - size_t full_len; - va_list args; - char *dest = first; - size_t buf_len; - - full_len = strlen(first); - va_start(args, first); - while ((cur = va_arg(args, char *)) != NULL) { - full_len += strlen(cur); - if (cur[0] != '/') - full_len++; - - buf_len = full_len + 1; - dest = must_realloc(dest, buf_len); - - if (cur[0] != '/') - (void)strlcat(dest, "/", buf_len); - (void)strlcat(dest, cur, buf_len); - } - va_end(args); - - return dest; -} - -char *must_copy_string(const char *entry) -{ - char *ret; - - if (!entry) - return NULL; - - do { - ret = strdup(entry); - } while (!ret); - - return ret; -} - -void *must_realloc(void *orig, size_t sz) -{ - void *ret; - - do { - ret = realloc(orig, sz); - } while (!ret); - - return ret; -} - -bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val) -{ - return (fs->f_type == (fs_type_magic)magic_val); -} - -bool has_fs_type(const char *path, fs_type_magic magic_val) -{ - bool has_type; - int ret; - struct statfs sb; - - ret = statfs(path, &sb); - if (ret < 0) - return false; - - has_type = is_fs_type(&sb, magic_val); - if (!has_type && magic_val == RAMFS_MAGIC) - WARN("When the ramfs it a tmpfs statfs() might report tmpfs"); - - return has_type; -} - -bool fhas_fs_type(int fd, fs_type_magic magic_val) -{ - int ret; - struct statfs sb; - - ret = fstatfs(fd, &sb); - if (ret < 0) - return false; - - return is_fs_type(&sb, magic_val); -} - bool lxc_nic_exists(char *nic) { #define __LXC_SYS_CLASS_NET_LEN 15 + IFNAMSIZ + 1 @@ -2584,91 +1590,6 @@ bool lxc_nic_exists(char *nic) return true; } -int lxc_make_tmpfile(char *template, bool rm) -{ - int fd, ret; - mode_t msk; - - msk = umask(0022); - fd = mkstemp(template); - umask(msk); - if (fd < 0) - return -1; - - if (!rm) - return fd; - - ret = unlink(template); - if (ret < 0) { - close(fd); - return -1; - } - - return fd; -} - -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] = {0}; - - if (!s || !strcmp(s, "")) - return -EINVAL; - - end = stpncpy(dup, s, sizeof(dup) - 1); - if (*end != '\0') - return -EINVAL; - - if (isdigit(*(end - 1))) - suffix_len = 0; - else if (isalpha(*(end - 1))) - suffix_len = 1; - else - return -EINVAL; - - if (suffix_len > 0 && (end - 2) == dup && !isdigit(*(end - 2))) - return -EINVAL; - - if (suffix_len > 0 && isalpha(*(end - 2))) - suffix_len++; - - 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 (strcasecmp(suffix, "KB") == 0) - mltpl = 1024; - else if (strcasecmp(suffix, "MB") == 0) - mltpl = 1024 * 1024; - else if (strcasecmp(suffix, "GB") == 0) - 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 @@ -2714,17 +1635,6 @@ int lxc_set_death_signal(int signal) return 0; } -void remove_trailing_newlines(char *l) -{ - char *p = l; - - while (*p) - p++; - - while (--p >= l && *p == '\n') - *p = '\0'; -} - int fd_cloexec(int fd, bool cloexec) { int oflags, nflags; diff --git a/src/lxc/utils.h b/src/lxc/utils.h index 920f82faf8..f2d802991f 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -44,8 +43,10 @@ #include #endif +#include "file_utils.h" #include "initutils.h" #include "macro.h" +#include "string_utils.h" /* returns 1 on success, 0 if there were any failures */ extern int lxc_rmdir_onedev(const char *path, const char *exclude); @@ -281,70 +282,11 @@ extern int lxc_pclose(struct lxc_popen_FILE *fp); extern int wait_for_pid(pid_t pid); extern int lxc_wait_for_pid_status(pid_t pid); -/* send and receive buffers completely */ -extern ssize_t lxc_write_nointr(int fd, const void *buf, size_t count); -extern ssize_t lxc_read_nointr(int fd, void *buf, size_t count); -extern ssize_t lxc_read_nointr_expect(int fd, void *buf, size_t count, - const void *expected_buf); #if HAVE_LIBGNUTLS #define SHA_DIGEST_LENGTH 20 extern int sha1sum_file(char *fnam, unsigned char *md_value); #endif -/* read and write whole files */ -extern int lxc_write_to_file(const char *filename, const void *buf, - size_t count, bool add_newline, mode_t mode); -extern int lxc_read_from_file(const char *filename, void *buf, size_t count); - -/* convert variadic argument lists to arrays (for execl type argument lists) */ -extern char** lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup); -extern const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip); - -/* Some simple string functions; if they return pointers, they are allocated - * buffers. - */ -extern char *lxc_string_replace(const char *needle, const char *replacement, - const char *haystack); -extern bool lxc_string_in_array(const char *needle, const char **haystack); -extern char *lxc_string_join(const char *sep, const char **parts, - bool use_as_prefix); -/* Normalize and split path: Leading and trailing / are removed, multiple - * / are compactified, .. and . are resolved (.. on the top level is considered - * identical to .). - * Examples: - * / -> { NULL } - * foo/../bar -> { bar, NULL } - * ../../ -> { NULL } - * ./bar/baz/.. -> { bar, NULL } - * foo//bar -> { foo, bar, NULL } - */ -extern char **lxc_normalize_path(const char *path); -/* remove multiple slashes from the path, e.g. ///foo//bar -> /foo/bar */ -extern char *lxc_deslashify(const char *path); -extern char *lxc_append_paths(const char *first, const char *second); -/* Note: the following two functions use strtok(), so they will never - * consider an empty element, even if two delimiters are next to - * each other. - */ -extern bool lxc_string_in_list(const char *needle, const char *haystack, - char sep); -extern char **lxc_string_split(const char *string, char sep); -extern char **lxc_string_split_and_trim(const char *string, char sep); -extern char **lxc_string_split_quoted(char *string); -/* Append string to NULL-terminated string array. */ -extern int lxc_append_string(char ***list, char *entry); - -/* some simple array manipulation utilities */ -typedef void (*lxc_free_fn)(void *); -typedef void *(*lxc_dup_fn)(void *); -extern int lxc_grow_array(void ***array, size_t *capacity, size_t new_size, - size_t capacity_increment); -extern void lxc_free_array(void **array, lxc_free_fn element_free_fn); -extern size_t lxc_array_len(void **array); - -extern void **lxc_append_null_to_array(void **array, size_t count); -extern void remove_trailing_newlines(char *l); - /* initialize rand with urandom */ extern int randseed(bool); @@ -396,12 +338,9 @@ extern bool is_shared_mountpoint(const char *path); extern int detect_shared_rootfs(void); extern bool detect_ramfs_rootfs(void); extern char *on_path(const char *cmd, const char *rootfs); -extern bool file_exists(const char *f); extern bool cgns_supported(void); extern char *choose_init(const char *rootfs); -extern int print_to_file(const char *file, const char *content); extern bool switch_to_ns(pid_t pid, const char *ns); -extern int is_dir(const char *path); extern char *get_template_path(const char *t); extern int safe_mount(const char *src, const char *dest, const char *fstype, unsigned long flags, const void *data, @@ -410,22 +349,11 @@ extern int lxc_mount_proc_if_needed(const char *rootfs); extern int open_devnull(void); extern int set_stdfds(int fd); extern int null_stdfds(void); -extern int lxc_count_file_lines(const char *fn); extern int lxc_preserve_ns(const int pid, const char *ns); /* Check whether a signal is blocked by a process. */ extern bool task_blocks_signal(pid_t pid, int signal); -/* Helper functions to parse numbers. */ -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); -extern int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base); -/* 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); extern int lxc_setgroups(int size, gid_t list[]); @@ -468,13 +396,7 @@ extern char *must_copy_string(const char *entry); /* Re-alllocate a pointer, do not fail */ extern void *must_realloc(void *orig, size_t sz); -/* __typeof__ should be safe to use with all compilers. */ -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 fhas_fs_type(int fd, 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); static inline uint64_t lxc_getpagesize(void) {