+ ret = 0;
+error:
+ return ret;
+}
+
+/*
+ * Create lock file to the given path and filename.
+ * Returns the associated file descriptor, -1 on error.
+ */
+LTTNG_HIDDEN
+int utils_create_lock_file(const char *filepath)
+{
+ int ret;
+ int fd;
+
+ assert(filepath);
+
+ fd = open(filepath, O_CREAT,
+ O_WRONLY | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ if (fd < 0) {
+ PERROR("open lock file %s", filepath);
+ ret = -1;
+ goto error;
+ }
+
+ /*
+ * Attempt to lock the file. If this fails, there is
+ * already a process using the same lock file running
+ * and we should exit.
+ */
+ ret = flock(fd, LOCK_EX | LOCK_NB);
+ if (ret) {
+ ERR("Could not get lock file %s, another instance is running.",
+ filepath);
+ if (close(fd)) {
+ PERROR("close lock file");
+ }
+ fd = ret;
+ goto error;
+ }
+
+error:
+ return fd;
+}
+
+/*
+ * Create directory using the given path and mode.
+ *
+ * On success, return 0 else a negative error code.
+ */
+LTTNG_HIDDEN
+int utils_mkdir(const char *path, mode_t mode, int uid, int gid)
+{
+ int ret;
+
+ if (uid < 0 || gid < 0) {
+ ret = mkdir(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(tmp, mode);
+ if (ret < 0) {
+ if (errno != EEXIST) {
+ PERROR("mkdir recursive");
+ ret = -errno;
+ goto error;
+ }
+ }
+ *p = '/';
+ }
+ }
+
+ ret = mkdir(tmp, mode);
+ if (ret < 0) {
+ if (errno != EEXIST) {
+ PERROR("mkdir recursive last element");
+ ret = -errno;
+ } else {
+ ret = 0;
+ }
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * Recursively create directory using the given path and mode, under the
+ * provided uid and gid.
+ *
+ * On success, return 0 else a negative error code.
+ */
+LTTNG_HIDDEN
+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);
+ }
+
+ return ret;
+}
+
+/*
+ * path is the output parameter. It needs to be PATH_MAX len.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int utils_stream_file_name(char *path,
+ const char *path_name, const char *file_name,
+ uint64_t size, uint64_t count,
+ const char *suffix)
+{
+ int ret;
+ char full_path[PATH_MAX];
+ char *path_name_suffix = NULL;
+ char *extra = NULL;
+
+ ret = snprintf(full_path, sizeof(full_path), "%s/%s",
+ path_name, file_name);
+ if (ret < 0) {
+ PERROR("snprintf create output file");
+ goto error;
+ }
+
+ /* Setup extra string if suffix or/and a count is needed. */
+ if (size > 0 && suffix) {
+ ret = asprintf(&extra, "_%" PRIu64 "%s", count, suffix);
+ } else if (size > 0) {
+ ret = asprintf(&extra, "_%" PRIu64, count);
+ } else if (suffix) {
+ ret = asprintf(&extra, "%s", suffix);
+ }
+ if (ret < 0) {
+ PERROR("Allocating extra string to name");
+ goto error;
+ }
+
+ /*
+ * If we split the trace in multiple files, we have to add the count at
+ * the end of the tracefile name.
+ */
+ if (extra) {
+ ret = asprintf(&path_name_suffix, "%s%s", full_path, extra);
+ if (ret < 0) {
+ PERROR("Allocating path name with extra string");
+ goto error_free_suffix;
+ }
+ strncpy(path, path_name_suffix, PATH_MAX - 1);
+ path[PATH_MAX - 1] = '\0';
+ } else {
+ strncpy(path, full_path, PATH_MAX - 1);
+ }
+ path[PATH_MAX - 1] = '\0';
+ ret = 0;
+
+ free(path_name_suffix);
+error_free_suffix:
+ free(extra);
+error:
+ return ret;
+}
+
+/*
+ * Create the stream file on disk.
+ *
+ * Return 0 on success or else a negative value.
+ */
+LTTNG_HIDDEN
+int utils_create_stream_file(const char *path_name, char *file_name, uint64_t size,
+ uint64_t count, int uid, int gid, char *suffix)
+{
+ int ret, flags, mode;
+ char path[PATH_MAX];
+
+ ret = utils_stream_file_name(path, path_name, file_name,
+ size, count, suffix);
+ if (ret < 0) {
+ goto error;
+ }
+
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+ /* Open with 660 mode */
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+ if (uid < 0 || gid < 0) {
+ ret = open(path, flags, mode);
+ } else {
+ ret = run_as_open(path, flags, mode, uid, gid);
+ }
+ if (ret < 0) {
+ PERROR("open stream path %s", path);
+ }
+error:
+ return ret;
+}
+
+/*
+ * Unlink the stream tracefile from disk.
+ *
+ * Return 0 on success or else a negative value.
+ */
+LTTNG_HIDDEN
+int utils_unlink_stream_file(const char *path_name, char *file_name, uint64_t size,
+ uint64_t count, int uid, int gid, char *suffix)
+{
+ int ret;
+ char path[PATH_MAX];
+
+ ret = utils_stream_file_name(path, path_name, file_name,
+ size, count, suffix);
+ if (ret < 0) {
+ goto error;
+ }
+ if (uid < 0 || gid < 0) {
+ ret = unlink(path);
+ } else {
+ ret = run_as_unlink(path, uid, gid);
+ if (ret < 0) {
+ errno = -ret;
+ ret = -1;
+ }
+ }
+ if (ret < 0) {
+ goto error;
+ }
+error:
+ DBG("utils_unlink_stream_file %s returns %d", path, ret);
+ return ret;
+}
+
+/*
+ * Change the output tracefile according to the given size and count The
+ * new_count pointer is set during this operation.
+ *
+ * From the consumer, the stream lock MUST be held before calling this function
+ * because we are modifying the stream status.
+ *
+ * Return 0 on success or else a negative value.
+ */
+LTTNG_HIDDEN
+int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size,
+ uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count,
+ int *stream_fd)
+{
+ int ret;
+
+ assert(new_count);
+ assert(stream_fd);
+
+ ret = close(out_fd);
+ if (ret < 0) {
+ PERROR("Closing tracefile");
+ goto error;
+ }
+
+ if (count > 0) {
+ /*
+ * In tracefile rotation, for the relay daemon we need
+ * to unlink the old file if present, because it may
+ * still be open in reading by the live thread, and we
+ * need to ensure that we do not overwrite the content
+ * between get_index and get_packet. Since we have no
+ * way to verify integrity of the data content compared
+ * to the associated index, we need to ensure the reader
+ * has exclusive access to the file content, and that
+ * the open of the data file is performed in get_index.
+ * Unlinking the old file rather than overwriting it
+ * achieves this.
+ */
+ *new_count = (*new_count + 1) % count;
+ ret = utils_unlink_stream_file(path_name, file_name,
+ size, *new_count, uid, gid, 0);
+ if (ret < 0 && errno != ENOENT) {
+ goto error;
+ }
+ } else {
+ (*new_count)++;
+ }
+
+ ret = utils_create_stream_file(path_name, file_name, size, *new_count,
+ uid, gid, 0);
+ if (ret < 0) {
+ goto error;
+ }
+ *stream_fd = ret;
+
+ /* Success. */
+ ret = 0;
+