]>
git.proxmox.com Git - mirror_lxcfs.git/blob - src/utils.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
10 #ifndef FUSE_USE_VERSION
11 #define FUSE_USE_VERSION 30
14 #ifndef FUSE_USE_VERSION
15 #define FUSE_USE_VERSION 26
19 #define _FILE_OFFSET_BITS 64
33 #include <sys/epoll.h>
35 #include <sys/types.h>
41 #include "memory_utils.h"
45 * append the given formatted string to *src.
46 * src: a pointer to a char* in which to append the formatted string.
47 * sz: the number of characters printed so far, minus trailing \0.
48 * asz: the allocated size so far
49 * format: string format. See printf for details.
50 * ...: varargs. See printf for details.
53 * append the given formatted string to *src.
54 * src: a pointer to a char* in which to append the formatted string.
55 * sz: the number of characters printed so far, minus trailing \0.
56 * asz: the allocated size so far
57 * format: string format. See printf for details.
58 * ...: varargs. See printf for details.
60 char *must_strcat(char **src
, size_t *sz
, size_t *asz
, const char *format
, ...)
62 char tmp
[BUF_RESERVE_SIZE
];
66 va_start (args
, format
);
67 tmplen
= vsnprintf(tmp
, BUF_RESERVE_SIZE
, format
, args
);
70 if (!*src
|| tmplen
+ *sz
+ 1 >= *asz
) {
73 str
= realloc(*src
, *asz
+ BUF_RESERVE_SIZE
);
76 *asz
+= BUF_RESERVE_SIZE
;
78 memcpy((*src
) +*sz
, tmp
, tmplen
+1); /* include the \0 */
85 * in_same_namespace - Check whether two processes are in the same namespace.
86 * @pid1 - PID of the first process.
87 * @pid2 - PID of the second process.
88 * @ns - Name of the namespace to check. Must correspond to one of the names
89 * for the namespaces as shown in /proc/<pid/ns/
91 * If the two processes are not in the same namespace returns an fd to the
92 * namespace of the second process identified by @pid2. If the two processes are
93 * in the same namespace returns -EINVAL, -1 if an error occurred.
95 static int in_same_namespace(pid_t pid1
, pid_t pid2
, const char *ns
)
97 __do_close
int ns_fd1
= -1, ns_fd2
= -1;
99 struct stat ns_st1
, ns_st2
;
101 ns_fd1
= preserve_ns(pid1
, ns
);
103 /* The kernel does not support this namespace. This is not an
112 ns_fd2
= preserve_ns(pid2
, ns
);
116 ret
= fstat(ns_fd1
, &ns_st1
);
120 ret
= fstat(ns_fd2
, &ns_st2
);
124 /* processes are in the same namespace */
125 if ((ns_st1
.st_dev
== ns_st2
.st_dev
) && (ns_st1
.st_ino
== ns_st2
.st_ino
))
128 /* processes are in different namespaces */
129 return move_fd(ns_fd2
);
132 bool is_shared_pidns(pid_t pid
)
134 __do_close
int fd
= -EBADF
;
139 fd
= in_same_namespace(pid
, getpid(), "pid");
146 int preserve_ns(const int pid
, const char *ns
)
149 /* 5 /proc + 21 /int_as_str + 3 /ns + 20 /NS_NAME + 1 \0 */
150 #define __NS_PATH_LEN 50
151 char path
[__NS_PATH_LEN
];
153 /* This way we can use this function to also check whether namespaces
154 * are supported by the kernel by passing in the NULL or the empty
157 ret
= snprintf(path
, __NS_PATH_LEN
, "/proc/%d/ns%s%s", pid
,
158 !ns
|| strcmp(ns
, "") == 0 ? "" : "/",
159 !ns
|| strcmp(ns
, "") == 0 ? "" : ns
);
160 if (ret
< 0 || (size_t)ret
>= __NS_PATH_LEN
) {
165 return open(path
, O_RDONLY
| O_CLOEXEC
);
168 void do_release_file_info(struct fuse_file_info
*fi
)
172 f
= INTTYPE_TO_PTR(fi
->fh
);
178 free_disarm(f
->controller
);
179 free_disarm(f
->cgroup
);
180 free_disarm(f
->file
);
185 #define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
187 bool wait_for_sock(int sock
, int timeout
)
189 __do_close
int epfd
= -EBADF
;
190 struct epoll_event ev
;
191 int ret
, now
, starttime
, deltatime
;
193 if ((starttime
= time(NULL
)) < 0)
196 epfd
= epoll_create(1);
198 return log_error(false, "%m - Failed to create epoll socket");
200 ev
.events
= POLLIN_SET
;
202 if (epoll_ctl(epfd
, EPOLL_CTL_ADD
, sock
, &ev
) < 0)
203 return log_error(false, "Failed adding socket to epoll: %m");
206 if ((now
= time(NULL
)) < 0)
209 deltatime
= (starttime
+ timeout
) - now
;
213 ret
= epoll_wait(epfd
, &ev
, 1, 1000*deltatime
+ 1);
214 if (ret
< 0 && errno
== EINTR
)
223 bool recv_creds(int sock
, struct ucred
*cred
, char *v
)
225 struct msghdr msg
= {};
227 struct cmsghdr
*cmsg
;
229 char cmsgbuf
[CMSG_SPACE(sizeof(*cred
))] = {};
235 msg
.msg_control
= cmsgbuf
;
236 msg
.msg_controllen
= sizeof(cmsgbuf
);
239 iov
.iov_len
= sizeof(buf
);
245 ret
= setsockopt(sock
, SOL_SOCKET
, SO_PASSCRED
, &optval
, sizeof(optval
));
247 return log_error(false, "Failed to set passcred: %s\n", strerror(errno
));
249 ret
= write_nointr(sock
, &buf
, sizeof(buf
));
250 if (ret
!= sizeof(buf
))
251 return log_error(false, "Failed to start write on scm fd: %s\n", strerror(errno
));
253 if (!wait_for_sock(sock
, 2))
254 return log_error(false, "Timed out waiting for scm_cred: %s\n", strerror(errno
));
256 ret
= recvmsg(sock
, &msg
, MSG_DONTWAIT
);
258 return log_error(false, "Failed to receive scm_cred: %s\n", strerror(errno
));
260 cmsg
= CMSG_FIRSTHDR(&msg
);
262 if (cmsg
&& cmsg
->cmsg_len
== CMSG_LEN(sizeof(*cred
)) &&
263 cmsg
->cmsg_level
== SOL_SOCKET
&&
264 cmsg
->cmsg_type
== SCM_CREDENTIALS
) {
265 memcpy(cred
, CMSG_DATA(cmsg
), sizeof(*cred
));
272 static int msgrecv(int sockfd
, void *buf
, size_t len
)
274 if (!wait_for_sock(sockfd
, 2))
277 return recv(sockfd
, buf
, len
, MSG_DONTWAIT
);
280 int send_creds(int sock
, struct ucred
*cred
, char v
, bool pingfirst
)
282 struct msghdr msg
= { 0 };
284 struct cmsghdr
*cmsg
;
285 char cmsgbuf
[CMSG_SPACE(sizeof(*cred
))];
289 if (pingfirst
&& msgrecv(sock
, buf
, 1) != 1)
290 return log_error(SEND_CREDS_FAIL
, "%s - Failed getting reply from server over socketpair: %d",
291 strerror(errno
), SEND_CREDS_FAIL
);
293 msg
.msg_control
= cmsgbuf
;
294 msg
.msg_controllen
= sizeof(cmsgbuf
);
296 cmsg
= CMSG_FIRSTHDR(&msg
);
297 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct ucred
));
298 cmsg
->cmsg_level
= SOL_SOCKET
;
299 cmsg
->cmsg_type
= SCM_CREDENTIALS
;
300 memcpy(CMSG_DATA(cmsg
), cred
, sizeof(*cred
));
307 iov
.iov_len
= sizeof(buf
);
311 if (sendmsg(sock
, &msg
, 0) < 0) {
313 return log_error(SEND_CREDS_NOTSK
, "%s - Failed at sendmsg: %d", strerror(errno
), SEND_CREDS_NOTSK
);
315 return log_error(SEND_CREDS_FAIL
, "%s - Failed at sendmsg: %d", strerror(errno
), SEND_CREDS_FAIL
);
318 return SEND_CREDS_OK
;
321 int read_file_fuse(const char *path
, char *buf
, size_t size
, struct file_info
*d
)
323 __do_free
char *line
= NULL
;
324 __do_fclose
FILE *f
= NULL
;
325 size_t linelen
= 0, total_len
= 0;
326 char *cache
= d
->buf
;
327 size_t cache_size
= d
->buflen
;
329 f
= fopen(path
, "re");
333 while (getline(&line
, &linelen
, f
) != -1) {
334 ssize_t l
= snprintf(cache
, cache_size
, "%s", line
);
336 return log_error(0, "Failed to write cache");
338 return log_error(0, "Write to cache was truncated");
346 if (total_len
> size
)
349 /* read from off 0 */
350 memcpy(buf
, d
->buf
, total_len
);
352 if (d
->size
> total_len
)
353 d
->cached
= d
->size
- total_len
;
358 int read_file_fuse_with_offset(const char *path
, char *buf
, size_t size
,
359 off_t offset
, struct file_info
*d
)
362 ssize_t total_len
= 0;
363 char *cache
= d
->buf
;
366 if (offset
> d
->size
)
372 left
= d
->size
- offset
;
373 total_len
= left
> size
? size
: left
;
374 memcpy(buf
, cache
+ offset
, total_len
);
379 return read_file_fuse(path
, buf
, size
, d
);
382 #define INITSCOPE "/init.scope"
383 void prune_init_slice(char *cg
)
386 size_t cg_len
= strlen(cg
), initscope_len
= strlen(INITSCOPE
);
388 if (cg_len
< initscope_len
)
391 point
= cg
+ cg_len
- initscope_len
;
392 if (strcmp(point
, INITSCOPE
) == 0) {
400 int wait_for_pid(pid_t pid
)
408 ret
= waitpid(pid
, &status
, 0);
416 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0)
421 static ssize_t
read_nointr(int fd
, void *buf
, size_t count
)
425 ret
= read(fd
, buf
, count
);
426 if (ret
< 0 && errno
== EINTR
)
432 static void *must_realloc(void *orig
, size_t sz
)
437 ret
= realloc(orig
, sz
);
443 static char *fd_to_buf(int fd
, size_t *length
)
445 __do_free
char *copy
= NULL
;
456 bytes_read
= read_nointr(fd
, buf
, sizeof(buf
));
463 copy
= must_realloc(old
, (*length
+ bytes_read
) * sizeof(*old
));
464 memcpy(copy
+ *length
, buf
, bytes_read
);
465 *length
+= bytes_read
;
468 return move_ptr(copy
);
471 static char *file_to_buf(const char *path
, size_t *length
)
473 __do_close
int fd
= -EBADF
;
478 fd
= open(path
, O_RDONLY
| O_CLOEXEC
);
482 return fd_to_buf(fd
, length
);
485 FILE *fopen_cached(const char *path
, const char *mode
, void **caller_freed_buffer
)
487 __do_free
char *buf
= NULL
;
491 buf
= file_to_buf(path
, &len
);
495 f
= fmemopen(buf
, len
, mode
);
498 *caller_freed_buffer
= move_ptr(buf
);
502 FILE *fdopen_cached(int fd
, const char *mode
, void **caller_freed_buffer
)
504 __do_free
char *buf
= NULL
;
508 buf
= fd_to_buf(fd
, &len
);
512 f
= fmemopen(buf
, len
, mode
);
516 *caller_freed_buffer
= move_ptr(buf
);
520 ssize_t
write_nointr(int fd
, const void *buf
, size_t count
)
525 ret
= write(fd
, buf
, count
);
526 } while (ret
< 0 && errno
== EINTR
);
531 int safe_uint64(const char *numstr
, uint64_t *converted
, int base
)
536 while (isspace(*numstr
))
543 u
= strtoull(numstr
, &err
, base
);
544 if (errno
== ERANGE
&& u
== UINT64_MAX
)
547 if (err
== numstr
|| *err
!= '\0')
554 static int char_left_gc(const char *buffer
, size_t len
)
558 for (i
= 0; i
< len
; i
++) {
559 if (buffer
[i
] == ' ' ||
569 static int char_right_gc(const char *buffer
, size_t len
)
573 for (i
= len
- 1; i
>= 0; i
--) {
574 if (buffer
[i
] == ' ' ||
586 char *trim_whitespace_in_place(char *buffer
)
588 buffer
+= char_left_gc(buffer
, strlen(buffer
));
589 buffer
[char_right_gc(buffer
, strlen(buffer
))] = '\0';