]> git.proxmox.com Git - mirror_lxcfs.git/blobdiff - src/utils.c
utils: add get_task_personality helper
[mirror_lxcfs.git] / src / utils.c
index ccbb50bf7794ee4d8dee126161d53c1bbdbea657..ab665f74f6971ed2c03ab18be9995e2f565dffb7 100644 (file)
@@ -1,22 +1,13 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#ifndef FUSE_USE_VERSION
-#define FUSE_USE_VERSION 26
-#endif
-
-#define _FILE_OFFSET_BITS 64
+#include "config.h"
 
+#include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <fuse.h>
 #include <inttypes.h>
 #include <sched.h>
 #include <stdarg.h>
-#include <stdio.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include "utils.h"
+
 #include "bindings.h"
-#include "config.h"
 #include "macro.h"
 #include "memory_utils.h"
-#include "utils.h"
 
-/*
- * append the given formatted string to *src.
- * src: a pointer to a char* in which to append the formatted string.
- * sz: the number of characters printed so far, minus trailing \0.
- * asz: the allocated size so far
- * format: string format. See printf for details.
- * ...: varargs. See printf for details.
- */
 /*
  * append the given formatted string to *src.
  * src: a pointer to a char* in which to append the formatted string.
@@ -87,7 +70,7 @@ char *must_strcat(char **src, size_t *sz, size_t *asz, const char *format, ...)
  */
 static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
 {
-       __do_close_prot_errno int ns_fd1 = -1, ns_fd2 = -1;
+       __do_close int ns_fd1 = -1, ns_fd2 = -1;
        int ret = -1;
        struct stat ns_st1, ns_st2;
 
@@ -124,10 +107,13 @@ static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
 
 bool is_shared_pidns(pid_t pid)
 {
+       __do_close int fd = -EBADF;
+
        if (pid != 1)
                return false;
 
-       if (in_same_namespace(pid, getpid(), "pid") == -EINVAL)
+       fd = in_same_namespace(pid, getpid(), "pid");
+       if (fd == EINVAL)
                return true;
 
        return false;
@@ -176,7 +162,7 @@ void do_release_file_info(struct fuse_file_info *fi)
 
 bool wait_for_sock(int sock, int timeout)
 {
-       __do_close_prot_errno int epfd = -EBADF;
+       __do_close int epfd = -EBADF;
        struct epoll_event ev;
        int ret, now, starttime, deltatime;
 
@@ -185,7 +171,7 @@ bool wait_for_sock(int sock, int timeout)
 
        epfd = epoll_create(1);
        if (epfd < 0)
-               return log_error(false, "%s\n", "Failed to create epoll socket: %m");
+               return log_error(false, "%m - Failed to create epoll socket");
 
        ev.events = POLLIN_SET;
        ev.data.fd = sock;
@@ -212,37 +198,34 @@ again:
 
 bool recv_creds(int sock, struct ucred *cred, char *v)
 {
-       struct msghdr msg = { 0 };
+       struct msghdr msg = {};
        struct iovec iov;
        struct cmsghdr *cmsg;
-       char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
-       char buf[1];
-       int ret;
+       ssize_t ret;
+       char cmsgbuf[CMSG_SPACE(sizeof(*cred))] = {};
+       char buf = '1';
        int optval = 1;
 
-       *v = '1';
-
-       cred->pid = -1;
-       cred->uid = -1;
-       cred->gid = -1;
-
-       if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1)
-               return log_error(false, "Failed to set passcred: %s\n", strerror(errno));
-
-       buf[0] = '1';
-       if (write(sock, buf, 1) != 1)
-               return log_error(false, "Failed to start write on scm fd: %s\n", strerror(errno));
-
        msg.msg_name = NULL;
        msg.msg_namelen = 0;
        msg.msg_control = cmsgbuf;
        msg.msg_controllen = sizeof(cmsgbuf);
 
-       iov.iov_base = buf;
+       iov.iov_base = &buf;
        iov.iov_len = sizeof(buf);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
 
+       *v = buf;
+
+       ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval));
+       if (ret < 0)
+               return log_error(false, "Failed to set passcred: %s\n", strerror(errno));
+
+       ret = write_nointr(sock, &buf, sizeof(buf));
+       if (ret != sizeof(buf))
+               return log_error(false, "Failed to start write on scm fd: %s\n", strerror(errno));
+
        if (!wait_for_sock(sock, 2))
                return log_error(false, "Timed out waiting for scm_cred: %s\n", strerror(errno));
 
@@ -252,12 +235,12 @@ bool recv_creds(int sock, struct ucred *cred, char *v)
 
        cmsg = CMSG_FIRSTHDR(&msg);
 
-       if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) &&
-                       cmsg->cmsg_level == SOL_SOCKET &&
-                       cmsg->cmsg_type == SCM_CREDENTIALS) {
+       if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(*cred)) &&
+           cmsg->cmsg_level == SOL_SOCKET &&
+           cmsg->cmsg_type == SCM_CREDENTIALS) {
                memcpy(cred, CMSG_DATA(cmsg), sizeof(*cred));
        }
-       *v = buf[0];
+       *v = buf;
 
        return true;
 }
@@ -324,10 +307,12 @@ int read_file_fuse(const char *path, char *buf, size_t size, struct file_info *d
                return 0;
 
        while (getline(&line, &linelen, f) != -1) {
-               ssize_t l = snprintf(cache, cache_size, "%s", line);
+               ssize_t l;
+
+               l = snprintf(cache, cache_size, "%s", line);
                if (l < 0)
                        return log_error(0, "Failed to write cache");
-               if (l >= cache_size)
+               if ((size_t)l >= cache_size)
                        return log_error(0, "Write to cache was truncated");
 
                cache += l;
@@ -342,11 +327,36 @@ int read_file_fuse(const char *path, char *buf, size_t size, struct file_info *d
        /* read from off 0 */
        memcpy(buf, d->buf, total_len);
 
-       if (d->size > total_len)
+       if (d->size > (int)total_len)
                d->cached = d->size - total_len;
+
        return total_len;
 }
 
+int read_file_fuse_with_offset(const char *path, char *buf, size_t size,
+                              off_t offset, struct file_info *d)
+{
+       if (offset) {
+               ssize_t total_len = 0;
+               char *cache = d->buf;
+               size_t left;
+
+               if (offset > d->size)
+                       return -EINVAL;
+
+               if (!d->cached)
+                       return 0;
+
+               left = d->size - offset;
+               total_len = left > size ? size : left;
+               memcpy(buf, cache + offset, total_len);
+
+               return total_len;
+       }
+
+       return read_file_fuse(path, buf, size, d);
+}
+
 #define INITSCOPE "/init.scope"
 void prune_init_slice(char *cg)
 {
@@ -438,7 +448,7 @@ static char *fd_to_buf(int fd, size_t *length)
 
 static char *file_to_buf(const char *path, size_t *length)
 {
-       __do_close_prot_errno int fd = -EBADF;
+       __do_close int fd = -EBADF;
 
        if (!length)
                return NULL;
@@ -484,3 +494,200 @@ FILE *fdopen_cached(int fd, const char *mode, void **caller_freed_buffer)
        *caller_freed_buffer = move_ptr(buf);
        return f;
 }
+
+ssize_t write_nointr(int fd, const void *buf, size_t count)
+{
+       ssize_t ret;
+
+       do {
+               ret = write(fd, buf, count);
+       } while (ret < 0 && errno == EINTR);
+
+       return ret;
+}
+
+int 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 == UINT64_MAX)
+               return -ERANGE;
+
+       if (err == numstr || *err != '\0')
+               return -EINVAL;
+
+       *converted = u;
+       return 0;
+}
+
+int safe_uint32(const char *numstr, uint32_t *converted, int base)
+{
+       char *err = NULL;
+       unsigned long uli;
+
+       while (isspace(*numstr))
+               numstr++;
+
+       if (*numstr == '-')
+               return -EINVAL;
+
+       errno = 0;
+       uli = strtoul(numstr, &err, base);
+       if (errno == ERANGE && uli == UINT32_MAX)
+               return -ERANGE;
+
+       if (err == numstr || *err != '\0')
+               return -EINVAL;
+
+       *converted = (uint32_t)uli;
+       return 0;
+}
+
+static int 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;
+}
+
+static int 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 *trim_whitespace_in_place(char *buffer)
+{
+       buffer += char_left_gc(buffer, strlen(buffer));
+       buffer[char_right_gc(buffer, strlen(buffer))] = '\0';
+       return buffer;
+}
+
+#define BATCH_SIZE 50
+static int batch_realloc(char **mem, size_t oldlen, size_t newlen)
+{
+       int newbatches = (newlen / BATCH_SIZE) + 1;
+       int oldbatches = (oldlen / BATCH_SIZE) + 1;
+
+       if (!*mem || newbatches > oldbatches) {
+               char *tmp;
+
+               tmp = realloc(*mem, newbatches * BATCH_SIZE);
+               if (!tmp)
+                       return -ENOMEM;
+               *mem = tmp;
+       }
+
+       return 0;
+}
+
+static int append_line(char **dest, size_t oldlen, char *new, size_t newlen)
+{
+       int ret;
+       size_t full = oldlen + newlen;
+
+       ret = batch_realloc(dest, oldlen, full + 1);
+       if (ret)
+               return ret;
+
+       memcpy(*dest + oldlen, new, newlen + 1);
+       return 0;
+}
+
+/* Slurp in a whole file */
+char *read_file_at(int dfd, const char *fnam, unsigned int o_flags)
+{
+       __do_close int fd = -EBADF;
+       __do_free char *buf = NULL, *line = NULL;
+       __do_fclose FILE *f = NULL;
+       size_t len = 0, fulllen = 0;
+       int linelen;
+
+       fd = openat(dfd, fnam, o_flags, 0);
+       if (fd < 0)
+               return NULL;
+
+       f = fdopen(fd, "re");
+       if (!f)
+               return NULL;
+       /* Transfer ownership to fdopen(). */
+       move_fd(fd);
+
+       while ((linelen = getline(&line, &len, f)) != -1) {
+               if (append_line(&buf, fulllen, line, linelen))
+                       return NULL;
+               fulllen += linelen;
+       }
+
+       return move_ptr(buf);
+}
+
+DIR *opendir_flags(const char *path, int flags)
+{
+       __do_close int dfd = -EBADF;
+       DIR *dirp;
+
+       dfd = open(path, O_DIRECTORY | flags);
+       if (dfd < 0)
+               return NULL;
+
+       dirp = fdopendir(dfd);
+       if (dirp)
+               move_fd(dfd); /* Transfer ownership to fdopendir(). */
+
+       return dirp;
+}
+
+int get_task_personality(pid_t pid, __u32 *personality)
+{
+       __do_close int fd = -EBADF;
+       int ret = -1;
+       char path[STRLITERALLEN("/proc//personality") + INTTYPE_TO_STRLEN(pid_t) + 1];
+       /* seq_printf(m, "%08x\n", task->personality); */
+       char buf[8 + 1];
+
+       ret = strnprintf(path, sizeof(path), "/proc/%d/personality", pid);
+       if (ret < 0)
+               return -1;
+
+       fd = open(path, O_RDONLY | O_CLOEXEC);
+       if (fd < 0)
+               return -1;
+
+       ret = read_nointr(fd, buf, sizeof(buf) - 1);
+       if (ret >= 0) {
+               buf[ret] = '\0';
+               if (safe_uint32(buf, personality, 16) < 0)
+                       return log_error(-1, "Failed to convert personality %s", buf);
+       }
+
+       return ret;
+}