)
AM_CONDITIONAL([COMPAT_EPOLL], [ test "$enable_epoll" = "yes" ])
+AS_IF([test "x$ac_cv_func_dirfd" = "xyes"],
+ [AX_CONFIG_FEATURE_ENABLE(dirfd)],
+ [AX_CONFIG_FEATURE_DISABLE(dirfd)]
+)
+AX_CONFIG_FEATURE(
+ [dirfd], [Use directory file descriptors],
+ [COMPAT_DIRFD], [This platform supports directory file descriptors.],
+ [enable_dirfd="yes"], [enable_dirfd="no"]
+)
+AM_CONDITIONAL([COMPAT_DIRFD], [ test "$enable_dirfd" = "yes" ])
+
AM_CONDITIONAL([TEST_JAVA_JUL_AGENT], [test "x$test_java_agent_jul" = "xyes"])
AM_CONDITIONAL([TEST_JAVA_LOG4J_AGENT], [test "x$test_java_agent_log4j" = "xyes"])
location.c \
waiter.h waiter.c \
userspace-probe.c event.c time.c \
- session-descriptor.c
+ session-descriptor.c credentials.h
if HAVE_ELF_H
libcommon_la_SOURCES += lttng-elf.h lttng-elf.c
libcommon_la_LIBADD = \
$(top_builddir)/src/common/config/libconfig.la \
+ $(top_builddir)/src/common/compat/libcompat.la \
$(UUID_LIBS)
if BUILD_LIB_COMPAT
libcompat_la_SOURCES = poll.h fcntl.h endian.h mman.h dirent.h \
socket.h compat-fcntl.c uuid.h uuid.c tid.h \
getenv.h string.h prctl.h paths.h netdb.h $(COMPAT) \
- time.h
+ time.h directory-handle.h directory-handle.c
--- /dev/null
+/*
+ * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * 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 <common/compat/directory-handle.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/runas.h>
+#include <common/credentials.h>
+#include <lttng/constant.h>
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+static
+int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
+ const char *path, struct stat *st);
+static
+int lttng_directory_handle_mkdir(
+ const struct lttng_directory_handle *handle,
+ const char *path, mode_t mode);
+static
+int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
+ mode_t mode, uid_t uid, gid_t gid);
+static
+int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
+ const char *path, mode_t mode, uid_t uid, gid_t gid);
+
+#ifdef COMPAT_DIRFD
+
+LTTNG_HIDDEN
+int lttng_directory_handle_init(struct lttng_directory_handle *handle,
+ const char *path)
+{
+ int ret;
+
+ if (!path) {
+ handle->dirfd = AT_FDCWD;
+ ret = 0;
+ goto end;
+ }
+ ret = open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (ret == -1) {
+ PERROR("Failed to initialize directory handle to \"%s\"", path);
+ goto end;
+ }
+ handle->dirfd = ret;
+ ret = 0;
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_directory_handle_init_from_dirfd(
+ struct lttng_directory_handle *handle, int dirfd)
+{
+ handle->dirfd = dirfd;
+ return 0;
+}
+
+LTTNG_HIDDEN
+void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
+{
+ int ret;
+
+ if (handle->dirfd == AT_FDCWD) {
+ return;
+ }
+ ret = close(handle->dirfd);
+ if (ret == -1) {
+ PERROR("Failed to close directory file descriptor of directory handle");
+ }
+}
+
+static
+int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
+ const char *path, struct stat *st)
+{
+ return fstatat(handle->dirfd, path, st, 0);
+}
+
+static
+int lttng_directory_handle_mkdir(
+ const struct lttng_directory_handle *handle,
+ const char *path, mode_t mode)
+{
+ return mkdirat(handle->dirfd, path, mode);
+}
+
+static
+int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
+ mode_t mode, uid_t uid, gid_t gid)
+{
+ return run_as_mkdirat(handle->dirfd, path, mode, uid, gid);
+}
+
+static
+int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
+ const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+ return run_as_mkdirat_recursive(handle->dirfd, path, mode, uid, gid);
+}
+
+#else /* COMPAT_DIRFD */
+
+LTTNG_HIDDEN
+int lttng_directory_handle_init(struct lttng_directory_handle *handle,
+ const char *path)
+{
+ int ret;
+ size_t cwd_len, path_len, handle_path_len;
+ char cwd_buf[LTTNG_PATH_MAX];
+ const char *cwd;
+ bool add_slash = false;
+ struct stat stat_buf;
+
+ cwd = getcwd(cwd_buf, sizeof(cwd_buf));
+ if (!cwd) {
+ PERROR("Failed to initialize directory handle, can't get current working directory");
+ ret = -1;
+ goto end;
+ }
+ cwd_len = strlen(cwd);
+ if (cwd_len == 0) {
+ ERR("Failed to initialize directory handle to \"%s\": getcwd() returned an empty string",
+ path);
+ ret = -1;
+ goto end;
+ }
+ if (cwd[cwd_len - 1] != '/') {
+ add_slash = true;
+ }
+
+ if (path) {
+ path_len = strlen(path);
+ if (path_len == 0) {
+ ERR("Failed to initialize directory handle: provided path is an empty string");
+ ret = -1;
+ goto end;
+ }
+
+ /*
+ * Ensure that 'path' is a directory. There is a race
+ * (TOCTOU) since the directory could be removed/replaced/renamed,
+ * but this is inevitable on platforms that don't provide dirfd support.
+ */
+ ret = stat(path, &stat_buf);
+ if (ret == -1) {
+ PERROR("Failed to initialize directory handle to \"%s\", stat() failed",
+ path);
+ goto end;
+ }
+ if (!S_ISDIR(stat_buf.st_mode)) {
+ ERR("Failed to initialize directory handle to \"%s\": not a directory",
+ path);
+ ret = -1;
+ goto end;
+ }
+ if (*path == '/') {
+ handle->base_path = strdup(path);
+ if (!handle->base_path) {
+ ret = -1;
+ }
+ /* Not an error. */
+ goto end;
+ }
+ } else {
+ path = "";
+ path_len = 0;
+ add_slash = false;
+ }
+
+ handle_path_len = cwd_len + path_len + !!add_slash + 2;
+ if (handle_path_len >= LTTNG_PATH_MAX) {
+ ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
+ handle_path_len, LTTNG_PATH_MAX);
+ ret = -1;
+ goto end;
+ }
+ handle->base_path = zmalloc(handle_path_len);
+ if (!handle->base_path) {
+ PERROR("Failed to initialize directory handle");
+ ret = -1;
+ goto end;
+ }
+
+ ret = sprintf(handle->base_path, "%s%s%s/", cwd,
+ add_slash ? "/" : "", path);
+ if (ret == -1 || ret >= handle_path_len) {
+ ERR("Failed to initialize directory handle: path formatting failed");
+ ret = -1;
+ goto end;
+ }
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_directory_handle_init_from_dirfd(
+ struct lttng_directory_handle *handle, int dirfd)
+{
+ assert(dirfd == AT_FDCWD);
+ return lttng_directory_handle_init(handle, NULL);
+}
+
+LTTNG_HIDDEN
+void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
+{
+ free(handle->base_path);
+}
+
+static
+int get_full_path(const struct lttng_directory_handle *handle,
+ const char *subdirectory, char *fullpath, size_t size)
+{
+ int ret;
+
+ /*
+ * Don't include the base path if subdirectory is absolute.
+ * This is the same behaviour than mkdirat.
+ */
+ ret = snprintf(fullpath, size, "%s%s",
+ *subdirectory != '/' ? handle->base_path : "",
+ subdirectory);
+ if (ret == -1 || ret >= size) {
+ ERR("Failed to format subdirectory from directory handle");
+ ret = -1;
+ }
+ ret = 0;
+ return ret;
+}
+
+static
+int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
+ const char *subdirectory, struct stat *st)
+{
+ int ret;
+ char fullpath[LTTNG_PATH_MAX];
+
+ ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
+ if (ret) {
+ errno = ENOMEM;
+ goto end;
+ }
+
+ ret = stat(fullpath, st);
+end:
+ return ret;
+}
+
+static
+int lttng_directory_handle_mkdir(const struct lttng_directory_handle *handle,
+ const char *subdirectory, mode_t mode)
+{
+ int ret;
+ char fullpath[LTTNG_PATH_MAX];
+
+ ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
+ if (ret) {
+ errno = ENOMEM;
+ goto end;
+ }
+
+ ret = mkdir(fullpath, mode);
+end:
+ return ret;
+}
+
+static
+int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
+ mode_t mode, uid_t uid, gid_t gid)
+{
+ int ret;
+ char fullpath[LTTNG_PATH_MAX];
+
+ ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
+ if (ret) {
+ errno = ENOMEM;
+ goto end;
+ }
+
+ ret = run_as_mkdir(fullpath, mode, uid, gid);
+end:
+ return ret;
+}
+
+static
+int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
+ const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+ int ret;
+ char fullpath[LTTNG_PATH_MAX];
+
+ ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
+ if (ret) {
+ errno = ENOMEM;
+ goto end;
+ }
+
+ ret = run_as_mkdir_recursive(fullpath, mode, uid, gid);
+end:
+ return ret;
+}
+
+#endif /* COMPAT_DIRFD */
+
+/*
+ * On some filesystems (e.g. nfs), mkdir will validate access rights before
+ * checking for the existence of the path element. This means that on a setup
+ * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
+ * recursively creating a path of the form "/home/my_user/trace/" will fail with
+ * EACCES on mkdir("/home", ...).
+ *
+ * Checking the path for existence allows us to work around this behaviour.
+ */
+static
+int create_directory_check_exists(const struct lttng_directory_handle *handle,
+ const char *path, mode_t mode)
+{
+ int ret = 0;
+ struct stat st;
+
+ ret = lttng_directory_handle_stat(handle, path, &st);
+ if (ret == 0) {
+ if (S_ISDIR(st.st_mode)) {
+ /* Directory exists, skip. */
+ goto end;
+ } else {
+ /* Exists, but is not a directory. */
+ errno = ENOTDIR;
+ ret = -1;
+ goto end;
+ }
+ }
+
+ /*
+ * Let mkdir handle other errors as the caller expects mkdir
+ * semantics.
+ */
+ ret = lttng_directory_handle_mkdir(handle, path, mode);
+end:
+ return ret;
+}
+
+/* Common implementation. */
+static
+int create_directory_recursive(const struct lttng_directory_handle *handle,
+ const char *path, mode_t mode)
+{
+ char *p, tmp[LTTNG_PATH_MAX];
+ size_t len;
+ int ret;
+
+ assert(path);
+
+ ret = lttng_strncpy(tmp, path, sizeof(tmp));
+ if (ret) {
+ ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
+ strlen(path) + 1, sizeof(tmp));
+ goto error;
+ }
+
+ len = strlen(path);
+ if (tmp[len - 1] == '/') {
+ tmp[len - 1] = 0;
+ }
+
+ for (p = tmp + 1; *p; p++) {
+ if (*p == '/') {
+ *p = 0;
+ if (tmp[strlen(tmp) - 1] == '.' &&
+ tmp[strlen(tmp) - 2] == '.' &&
+ tmp[strlen(tmp) - 3] == '/') {
+ ERR("Using '/../' is not permitted in the trace path (%s)",
+ tmp);
+ ret = -1;
+ goto error;
+ }
+ ret = create_directory_check_exists(handle, tmp, mode);
+ if (ret < 0) {
+ if (errno != EACCES) {
+ PERROR("Failed to create directory \"%s\"",
+ path);
+ ret = -errno;
+ goto error;
+ }
+ }
+ *p = '/';
+ }
+ }
+
+ ret = create_directory_check_exists(handle, tmp, mode);
+ if (ret < 0) {
+ PERROR("mkdirat recursive last element");
+ ret = -errno;
+ }
+error:
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_directory_handle_create_subdirectory_as_user(
+ const struct lttng_directory_handle *handle,
+ const char *subdirectory,
+ mode_t mode, struct lttng_credentials *creds)
+{
+ int ret;
+
+ if (!creds) {
+ /* Run as current user. */
+ ret = create_directory_check_exists(handle,
+ subdirectory, mode);
+ } else {
+ ret = _run_as_mkdir(handle, subdirectory,
+ mode, creds->uid, creds->gid);
+ }
+
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_directory_handle_create_subdirectory_recursive_as_user(
+ const struct lttng_directory_handle *handle,
+ const char *subdirectory_path,
+ mode_t mode, struct lttng_credentials *creds)
+{
+ int ret;
+
+ if (!creds) {
+ /* Run as current user. */
+ ret = create_directory_recursive(handle,
+ subdirectory_path, mode);
+ } else {
+ ret = _run_as_mkdir_recursive(handle, subdirectory_path,
+ mode, creds->uid, creds->gid);
+ }
+
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_directory_handle_create_subdirectory(
+ const struct lttng_directory_handle *handle,
+ const char *subdirectory,
+ mode_t mode)
+{
+ return lttng_directory_handle_create_subdirectory_as_user(
+ handle, subdirectory, mode, NULL);
+}
+
+LTTNG_HIDDEN
+int lttng_directory_handle_create_subdirectory_recursive(
+ const struct lttng_directory_handle *handle,
+ const char *subdirectory_path,
+ mode_t mode)
+{
+ return lttng_directory_handle_create_subdirectory_recursive_as_user(
+ handle, subdirectory_path, mode, NULL);
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * 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 _COMPAT_DIRECTORY_HANDLE_H
+#define _COMPAT_DIRECTORY_HANDLE_H
+
+#include <common/macros.h>
+#include <common/credentials.h>
+
+/*
+ * Some platforms, such as Solaris 10, do not support directory file descriptors
+ * and their associated functions (*at(...)), which are defined in POSIX.2008.
+ *
+ * This wrapper provides a handle that is either a copy of a directory's path
+ * or a directory file descriptors, depending on the platform's capabilities.
+ */
+#ifdef COMPAT_DIRFD
+struct lttng_directory_handle {
+ int dirfd;
+};
+#else
+struct lttng_directory_handle {
+ char *base_path;
+};
+#endif
+
+/*
+ * Initialize a directory handle to the provided path. Passing a NULL path
+ * returns a handle to the current working directory. The working directory
+ * is not sampled; it will be accessed at the time of use of the functions
+ * of this API.
+ *
+ * An initialized directory handle must be finalized using
+ * lttng_directory_handle_fini().
+ */
+LTTNG_HIDDEN
+int lttng_directory_handle_init(struct lttng_directory_handle *handle,
+ const char *path);
+
+LTTNG_HIDDEN
+int lttng_directory_handle_init_from_dirfd(
+ struct lttng_directory_handle *handle, int dirfd);
+
+/*
+ * Release the resources of a directory handle.
+ */
+LTTNG_HIDDEN
+void lttng_directory_handle_fini(struct lttng_directory_handle *handle);
+
+/*
+ * Create a subdirectory relative to a directory handle.
+ */
+LTTNG_HIDDEN
+int lttng_directory_handle_create_subdirectory(
+ const struct lttng_directory_handle *handle,
+ const char *subdirectory,
+ mode_t mode);
+
+/*
+ * Create a subdirectory relative to a directory handle
+ * as a given user.
+ */
+LTTNG_HIDDEN
+int lttng_directory_handle_create_subdirectory_as_user(
+ const struct lttng_directory_handle *handle,
+ const char *subdirectory,
+ mode_t mode, struct lttng_credentials *creds);
+
+/*
+ * Recursively create a directory relative to a directory handle.
+ */
+LTTNG_HIDDEN
+int lttng_directory_handle_create_subdirectory_recursive(
+ const struct lttng_directory_handle *handle,
+ const char *subdirectory_path,
+ mode_t mode);
+
+/*
+ * Recursively create a directory relative to a directory handle
+ * as a given user.
+ */
+LTTNG_HIDDEN
+int lttng_directory_handle_create_subdirectory_recursive_as_user(
+ const struct lttng_directory_handle *handle,
+ const char *subdirectory_path,
+ mode_t mode, struct lttng_credentials *creds);
+
+#endif /* _COMPAT_PATH_HANDLE_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * 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 LTTNG_CREDENTIALS_H
+#define LTTNG_CREDENTIALS_H
+
+#include <sys/types.h>
+
+struct lttng_credentials {
+ uid_t uid;
+ gid_t gid;
+};
+
+#endif /* LTTNG_CREDENTIALS_H */
struct run_as_ret;
typedef int (*run_as_fct)(struct run_as_data *data, struct run_as_ret *ret_value);
-struct run_as_mkdir_data {
+struct run_as_mkdirat_data {
char path[PATH_MAX];
mode_t mode;
};
char provider_name[LTTNG_SYMBOL_NAME_LEN];
};
-struct run_as_mkdir_ret {
+struct run_as_mkdirat_ret {
int ret;
};
enum run_as_cmd {
RUN_AS_MKDIR,
+ RUN_AS_MKDIRAT,
+ RUN_AS_MKDIR_RECURSIVE,
+ RUN_AS_MKDIRAT_RECURSIVE,
RUN_AS_OPEN,
RUN_AS_UNLINK,
RUN_AS_RMDIR_RECURSIVE,
- RUN_AS_MKDIR_RECURSIVE,
RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET,
RUN_AS_EXTRACT_SDT_PROBE_OFFSETS,
};
enum run_as_cmd cmd;
int fd;
union {
- struct run_as_mkdir_data mkdir;
+ struct run_as_mkdirat_data mkdirat;
struct run_as_open_data open;
struct run_as_unlink_data unlink;
struct run_as_rmdir_recursive_data rmdir_recursive;
struct run_as_ret {
int fd;
union {
- struct run_as_mkdir_ret mkdir;
+ struct run_as_mkdirat_ret mkdirat;
struct run_as_open_ret open;
struct run_as_unlink_ret unlink;
struct run_as_rmdir_recursive_ret rmdir_recursive;
}
#endif
-LTTNG_HIDDEN
-int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
-
/*
* Create recursively directory using the FULL path.
*/
static
-int _mkdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
+int _mkdirat_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
{
const char *path;
mode_t mode;
+ struct lttng_directory_handle handle;
- path = data->u.mkdir.path;
- mode = data->u.mkdir.mode;
+ path = data->u.mkdirat.path;
+ mode = data->u.mkdirat.mode;
+ (void) lttng_directory_handle_init_from_dirfd(&handle, data->fd);
/* Safe to call as we have transitioned to the requested uid/gid. */
- ret_value->u.mkdir.ret = _utils_mkdir_recursive_unsafe(path, mode);
+ ret_value->u.mkdirat.ret =
+ lttng_directory_handle_create_subdirectory_recursive(
+ &handle, path, mode);
ret_value->_errno = errno;
- ret_value->_error = (ret_value->u.mkdir.ret) ? true : false;
- return ret_value->u.mkdir.ret;
+ ret_value->_error = (ret_value->u.mkdirat.ret) ? true : false;
+ lttng_directory_handle_fini(&handle);
+ return ret_value->u.mkdirat.ret;
}
static
-int _mkdir(struct run_as_data *data, struct run_as_ret *ret_value)
+int _mkdirat(struct run_as_data *data, struct run_as_ret *ret_value)
{
- ret_value->u.mkdir.ret = mkdir(data->u.mkdir.path, data->u.mkdir.mode);
+ const char *path;
+ mode_t mode;
+ struct lttng_directory_handle handle;
+
+ path = data->u.mkdirat.path;
+ mode = data->u.mkdirat.mode;
+
+ (void) lttng_directory_handle_init_from_dirfd(&handle, data->fd);
+ /* Safe to call as we have transitioned to the requested uid/gid. */
+ ret_value->u.mkdirat.ret =
+ lttng_directory_handle_create_subdirectory(
+ &handle, path, mode);
ret_value->_errno = errno;
- ret_value->_error = (ret_value->u.mkdir.ret) ? true : false;
- return ret_value->u.mkdir.ret;
+ ret_value->_error = (ret_value->u.mkdirat.ret) ? true : false;
+ lttng_directory_handle_fini(&handle);
+ return ret_value->u.mkdirat.ret;
}
static
{
switch (cmd) {
case RUN_AS_MKDIR:
- return _mkdir;
+ case RUN_AS_MKDIRAT:
+ return _mkdirat;
+ case RUN_AS_MKDIR_RECURSIVE:
+ case RUN_AS_MKDIRAT_RECURSIVE:
+ return _mkdirat_recursive;
case RUN_AS_OPEN:
return _open;
case RUN_AS_UNLINK:
return _unlink;
case RUN_AS_RMDIR_RECURSIVE:
return _rmdir_recursive;
- case RUN_AS_MKDIR_RECURSIVE:
- return _mkdir_recursive;
case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
return _extract_elf_symbol_offset;
case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
switch (cmd) {
case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
+ case RUN_AS_MKDIRAT:
+ case RUN_AS_MKDIRAT_RECURSIVE:
break;
default:
return 0;
switch (cmd) {
case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
+ case RUN_AS_MKDIRAT:
+ case RUN_AS_MKDIRAT_RECURSIVE:
break;
+ case RUN_AS_MKDIR:
+ case RUN_AS_MKDIR_RECURSIVE:
+ *fd = AT_FDCWD;
+ /* fall-through */
default:
return 0;
}
switch (cmd) {
case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
+ case RUN_AS_MKDIRAT:
+ case RUN_AS_MKDIRAT_RECURSIVE:
break;
default:
return 0;
LTTNG_HIDDEN
int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
{
+ return run_as_mkdirat_recursive(AT_FDCWD, path, mode, uid, gid);
+}
+
+LTTNG_HIDDEN
+int run_as_mkdirat_recursive(int dirfd, const char *path, mode_t mode,
+ uid_t uid, gid_t gid)
+{
+ int ret;
struct run_as_data data;
- struct run_as_ret ret;
+ struct run_as_ret run_as_ret;
memset(&data, 0, sizeof(data));
- memset(&ret, 0, sizeof(ret));
- DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
+ memset(&run_as_ret, 0, sizeof(run_as_ret));
+ DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d",
+ dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
path, (int) mode, (int) uid, (int) gid);
- strncpy(data.u.mkdir.path, path, PATH_MAX - 1);
- data.u.mkdir.path[PATH_MAX - 1] = '\0';
- data.u.mkdir.mode = mode;
-
- run_as(RUN_AS_MKDIR_RECURSIVE, &data, &ret, uid, gid);
- errno = ret._errno;
- return ret.u.mkdir.ret;
+ ret = lttng_strncpy(data.u.mkdirat.path, path,
+ sizeof(data.u.mkdirat.path));
+ if (ret) {
+ ERR("Failed to copy path argument of mkdirat recursive command");
+ goto error;
+ }
+ data.u.mkdirat.path[PATH_MAX - 1] = '\0';
+ data.u.mkdirat.mode = mode;
+ data.fd = dirfd;
+ run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR_RECURSIVE : RUN_AS_MKDIRAT_RECURSIVE,
+ &data, &run_as_ret, uid, gid);
+ errno = run_as_ret._errno;
+ ret = run_as_ret.u.mkdirat.ret;
+error:
+ return ret;
}
LTTNG_HIDDEN
int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
{
+ return run_as_mkdirat(AT_FDCWD, path, mode, uid, gid);
+}
+
+LTTNG_HIDDEN
+int run_as_mkdirat(int dirfd, const char *path, mode_t mode,
+ uid_t uid, gid_t gid)
+{
+ int ret;
struct run_as_data data;
- struct run_as_ret ret;
+ struct run_as_ret run_as_ret;
memset(&data, 0, sizeof(data));
- memset(&ret, 0, sizeof(ret));
+ memset(&run_as_ret, 0, sizeof(run_as_ret));
- DBG3("mkdir() %s with mode %d for uid %d and gid %d",
+ DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d",
+ dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
path, (int) mode, (int) uid, (int) gid);
- strncpy(data.u.mkdir.path, path, PATH_MAX - 1);
- data.u.mkdir.path[PATH_MAX - 1] = '\0';
- data.u.mkdir.mode = mode;
- run_as(RUN_AS_MKDIR, &data, &ret, uid, gid);
- errno = ret._errno;
- return ret.u.mkdir.ret;
+ ret = lttng_strncpy(data.u.mkdirat.path, path,
+ sizeof(data.u.mkdirat.path));
+ if (ret) {
+ ERR("Failed to copy path argument of mkdirat command");
+ goto error;
+ }
+ data.u.mkdirat.path[PATH_MAX - 1] = '\0';
+ data.u.mkdirat.mode = mode;
+ data.fd = dirfd;
+ run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR : RUN_AS_MKDIRAT,
+ &data, &run_as_ret, uid, gid);
+ errno = run_as_ret._errno;
+ ret = run_as_ret._errno;
+error:
+ return ret;
}
LTTNG_HIDDEN
LTTNG_HIDDEN
int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid);
LTTNG_HIDDEN
+int run_as_mkdirat_recursive(int dirfd, const char *path, mode_t mode,
+ uid_t uid, gid_t gid);
+LTTNG_HIDDEN
int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid);
LTTNG_HIDDEN
+int run_as_mkdirat(int dirfd, const char *path, mode_t mode,
+ uid_t uid, gid_t gid);
+LTTNG_HIDDEN
int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid);
LTTNG_HIDDEN
int run_as_unlink(const char *path, uid_t uid, gid_t gid);
#include <common/compat/getenv.h>
#include <common/compat/string.h>
#include <common/compat/dirent.h>
+#include <common/compat/directory-handle.h>
#include <lttng/constant.h>
#include "utils.h"
return fd;
}
-/*
- * On some filesystems (e.g. nfs), mkdir will validate access rights before
- * checking for the existence of the path element. This means that on a setup
- * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
- * recursively creating a path of the form "/home/my_user/trace/" will fail with
- * EACCES on mkdir("/home", ...).
- *
- * Performing a stat(...) on the path to check for existence allows us to
- * work around this behaviour.
- */
-static
-int mkdir_check_exists(const char *path, mode_t mode)
-{
- int ret = 0;
- struct stat st;
-
- ret = stat(path, &st);
- if (ret == 0) {
- if (S_ISDIR(st.st_mode)) {
- /* Directory exists, skip. */
- goto end;
- } else {
- /* Exists, but is not a directory. */
- errno = ENOTDIR;
- ret = -1;
- goto end;
- }
- }
-
- /*
- * Let mkdir handle other errors as the caller expects mkdir
- * semantics.
- */
- ret = mkdir(path, mode);
-end:
- return ret;
-}
-
/*
* Create directory using the given path and mode.
*
int utils_mkdir(const char *path, mode_t mode, int uid, int gid)
{
int ret;
-
- if (uid < 0 || gid < 0) {
- ret = mkdir_check_exists(path, mode);
- } else {
- ret = run_as_mkdir(path, mode, uid, gid);
- }
- if (ret < 0) {
- if (errno != EEXIST) {
- PERROR("mkdir %s, uid %d, gid %d", path ? path : "NULL",
- uid, gid);
- } else {
- ret = 0;
- }
- }
-
- return ret;
-}
-
-/*
- * Internal version of mkdir_recursive. Runs as the current user.
- * Don't call directly; use utils_mkdir_recursive().
- *
- * This function is ominously marked as "unsafe" since it should only
- * be called by a caller that has transitioned to the uid and gid under which
- * the directory creation should occur.
- */
-LTTNG_HIDDEN
-int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode)
-{
- char *p, tmp[PATH_MAX];
- size_t len;
- int ret;
-
- assert(path);
-
- ret = snprintf(tmp, sizeof(tmp), "%s", path);
- if (ret < 0) {
- PERROR("snprintf mkdir");
- goto error;
- }
-
- len = ret;
- if (tmp[len - 1] == '/') {
- tmp[len - 1] = 0;
- }
-
- for (p = tmp + 1; *p; p++) {
- if (*p == '/') {
- *p = 0;
- if (tmp[strlen(tmp) - 1] == '.' &&
- tmp[strlen(tmp) - 2] == '.' &&
- tmp[strlen(tmp) - 3] == '/') {
- ERR("Using '/../' is not permitted in the trace path (%s)",
- tmp);
- ret = -1;
- goto error;
- }
- ret = mkdir_check_exists(tmp, mode);
- if (ret < 0) {
- if (errno != EACCES) {
- PERROR("mkdir recursive");
- ret = -errno;
- goto error;
- }
- }
- *p = '/';
- }
- }
-
- ret = mkdir_check_exists(tmp, mode);
- if (ret < 0) {
- PERROR("mkdir recursive last element");
- ret = -errno;
- }
-
-error:
+ struct lttng_directory_handle handle;
+ struct lttng_credentials creds = {
+ .uid = (uid_t) uid,
+ .gid = (gid_t) gid,
+ };
+
+ (void) lttng_directory_handle_init(&handle, NULL);
+ ret = lttng_directory_handle_create_subdirectory_as_user(
+ &handle, path, mode,
+ (uid >= 0 || gid >= 0) ? &creds : NULL);
+ lttng_directory_handle_fini(&handle);
return ret;
}
int utils_mkdir_recursive(const char *path, mode_t mode, int uid, int gid)
{
int ret;
-
- if (uid < 0 || gid < 0) {
- /* Run as current user. */
- ret = _utils_mkdir_recursive_unsafe(path, mode);
- } else {
- ret = run_as_mkdir_recursive(path, mode, uid, gid);
- }
- if (ret < 0) {
- PERROR("mkdir %s, uid %d, gid %d", path ? path : "NULL",
- uid, gid);
- }
-
+ struct lttng_directory_handle handle;
+ struct lttng_credentials creds = {
+ .uid = (uid_t) uid,
+ .gid = (gid_t) gid,
+ };
+
+ (void) lttng_directory_handle_init(&handle, NULL);
+ ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
+ &handle, path, mode,
+ (uid >= 0 || gid >= 0) ? &creds : NULL);
+ lttng_directory_handle_fini(&handle);
return ret;
}
#include <stdint.h>
#include <getopt.h>
+#include <common/compat/directory-handle.h>
+
#define KIBI_LOG2 10
#define MEBI_LOG2 20
#define GIBI_LOG2 30