]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/file_utils.c
file_utils: add open_at()
[mirror_lxc.git] / src / lxc / file_utils.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: LGPL-2.1+ */
37ef15bb 2
d38dd64a
CB
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE 1
5#endif
37ef15bb
CB
6#include <errno.h>
7#include <fcntl.h>
8#include <linux/magic.h>
e1e08f39 9#include <stdio.h>
37ef15bb 10#include <stdlib.h>
7c4d9466 11#include <sys/sendfile.h>
07d1f84a 12#include <sys/stat.h>
37ef15bb 13#include <sys/types.h>
07d1f84a 14#include <time.h>
37ef15bb 15
d38dd64a 16#include "config.h"
37ef15bb 17#include "file_utils.h"
37ef15bb 18#include "macro.h"
4aa90f60 19#include "memory_utils.h"
6400238d 20#include "string_utils.h"
8ea93a0f 21#include "syscall_wrappers.h"
4aa90f60 22#include "utils.h"
37ef15bb 23
c04a6d4e
CB
24int lxc_open_dirfd(const char *dir)
25{
1973b62a 26 return open(dir, O_DIRECTORY | O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
c04a6d4e
CB
27}
28
29int lxc_readat(int dirfd, const char *filename, void *buf, size_t count)
30{
f62cf1d4 31 __do_close int fd = -EBADF;
c04a6d4e
CB
32 ssize_t ret;
33
34 fd = openat(dirfd, filename, O_RDONLY | O_CLOEXEC);
35 if (fd < 0)
36 return -1;
37
38 ret = lxc_read_nointr(fd, buf, count);
39 if (ret < 0 || (size_t)ret != count)
40 return -1;
41
42 return 0;
43}
44
bad788b0
CB
45int lxc_writeat(int dirfd, const char *filename, const void *buf, size_t count)
46{
f62cf1d4 47 __do_close int fd = -EBADF;
bad788b0
CB
48 ssize_t ret;
49
ef6d231f
CB
50 fd = openat(dirfd, filename,
51 O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
bad788b0
CB
52 if (fd < 0)
53 return -1;
54
55 ret = lxc_write_nointr(fd, buf, count);
56 if (ret < 0 || (size_t)ret != count)
57 return -1;
58
59 return 0;
60}
61
c04a6d4e
CB
62int lxc_write_openat(const char *dir, const char *filename, const void *buf,
63 size_t count)
64{
f62cf1d4 65 __do_close int dirfd = -EBADF;
c04a6d4e 66
ef6d231f 67 dirfd = open(dir, O_DIRECTORY | O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
c04a6d4e
CB
68 if (dirfd < 0)
69 return -1;
70
71 return lxc_writeat(dirfd, filename, buf, count);
72}
73
37ef15bb
CB
74int lxc_write_to_file(const char *filename, const void *buf, size_t count,
75 bool add_newline, mode_t mode)
76{
f62cf1d4 77 __do_close int fd = -EBADF;
37ef15bb
CB
78 ssize_t ret;
79
80 fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
81 if (fd < 0)
82 return -1;
83
84 ret = lxc_write_nointr(fd, buf, count);
85 if (ret < 0)
1dc51604 86 return -1;
37ef15bb
CB
87
88 if ((size_t)ret != count)
1dc51604 89 return -1;
37ef15bb
CB
90
91 if (add_newline) {
92 ret = lxc_write_nointr(fd, "\n", 1);
93 if (ret != 1)
1dc51604 94 return -1;
37ef15bb
CB
95 }
96
37ef15bb 97 return 0;
37ef15bb
CB
98}
99
100int lxc_read_from_file(const char *filename, void *buf, size_t count)
101{
f62cf1d4 102 __do_close int fd = -EBADF;
37ef15bb
CB
103 ssize_t ret;
104
105 fd = open(filename, O_RDONLY | O_CLOEXEC);
106 if (fd < 0)
107 return -1;
108
109 if (!buf || !count) {
110 char buf2[100];
111 size_t count2 = 0;
112
113 while ((ret = lxc_read_nointr(fd, buf2, 100)) > 0)
114 count2 += ret;
115
116 if (ret >= 0)
117 ret = count2;
118 } else {
119 memset(buf, 0, count);
120 ret = lxc_read_nointr(fd, buf, count);
121 }
122
37ef15bb
CB
123 return ret;
124}
125
126ssize_t lxc_write_nointr(int fd, const void *buf, size_t count)
127{
128 ssize_t ret;
1dc51604
CB
129
130 do {
131 ret = write(fd, buf, count);
132 } while (ret < 0 && errno == EINTR);
37ef15bb
CB
133
134 return ret;
135}
136
2120b89b
CB
137ssize_t lxc_pwrite_nointr(int fd, const void *buf, size_t count, off_t offset)
138{
139 ssize_t ret;
140
141 do {
142 ret = pwrite(fd, buf, count, offset);
143 } while (ret < 0 && errno == EINTR);
144
145 return ret;
146}
147
28143f88
CB
148ssize_t lxc_send_nointr(int sockfd, void *buf, size_t len, int flags)
149{
150 ssize_t ret;
1dc51604
CB
151
152 do {
153 ret = send(sockfd, buf, len, flags);
154 } while (ret < 0 && errno == EINTR);
28143f88
CB
155
156 return ret;
157}
158
37ef15bb
CB
159ssize_t lxc_read_nointr(int fd, void *buf, size_t count)
160{
161 ssize_t ret;
1dc51604
CB
162
163 do {
164 ret = read(fd, buf, count);
165 } while (ret < 0 && errno == EINTR);
37ef15bb
CB
166
167 return ret;
168}
169
de69edd1
CB
170ssize_t lxc_recv_nointr(int sockfd, void *buf, size_t len, int flags)
171{
172 ssize_t ret;
1dc51604
CB
173
174 do {
175 ret = recv(sockfd, buf, len, flags);
176 } while (ret < 0 && errno == EINTR);
de69edd1
CB
177
178 return ret;
179}
180
99d03dec
WB
181ssize_t lxc_recvmsg_nointr_iov(int sockfd, struct iovec *iov, size_t iovlen,
182 int flags)
183{
184 ssize_t ret;
1dc51604
CB
185 struct msghdr msg = {
186 .msg_iov = iov,
187 .msg_iovlen = iovlen,
188 };
99d03dec 189
1dc51604
CB
190 do {
191 ret = recvmsg(sockfd, &msg, flags);
192 } while (ret < 0 && errno == EINTR);
99d03dec
WB
193
194 return ret;
195}
196
1dc51604
CB
197ssize_t lxc_read_nointr_expect(int fd, void *buf, size_t count,
198 const void *expected_buf)
37ef15bb
CB
199{
200 ssize_t ret;
201
202 ret = lxc_read_nointr(fd, buf, count);
6509154d 203 if (ret < 0)
37ef15bb
CB
204 return ret;
205
206 if ((size_t)ret != count)
207 return -1;
208
1dc51604
CB
209 if (expected_buf && memcmp(buf, expected_buf, count) != 0)
210 return ret_set_errno(-1, EINVAL);
37ef15bb 211
6509154d 212 return 0;
213}
214
1dc51604
CB
215ssize_t lxc_read_file_expect(const char *path, void *buf, size_t count,
216 const void *expected_buf)
6509154d 217{
f62cf1d4 218 __do_close int fd = -EBADF;
6509154d 219
220 fd = open(path, O_RDONLY | O_CLOEXEC);
221 if (fd < 0)
222 return -1;
223
224 return lxc_read_nointr_expect(fd, buf, count, expected_buf);
37ef15bb
CB
225}
226
227bool file_exists(const char *f)
228{
229 struct stat statbuf;
230
231 return stat(f, &statbuf) == 0;
232}
233
234int print_to_file(const char *file, const char *content)
235{
1dc51604 236 __do_fclose FILE *f = NULL;
37ef15bb
CB
237 int ret = 0;
238
4110345b 239 f = fopen(file, "we");
37ef15bb
CB
240 if (!f)
241 return -1;
242
243 if (fprintf(f, "%s", content) != strlen(content))
244 ret = -1;
245
37ef15bb
CB
246 return ret;
247}
248
249int is_dir(const char *path)
250{
37ef15bb 251 int ret;
1dc51604 252 struct stat statbuf;
37ef15bb
CB
253
254 ret = stat(path, &statbuf);
255 if (ret == 0 && S_ISDIR(statbuf.st_mode))
256 return 1;
257
258 return 0;
259}
260
261/*
262 * Return the number of lines in file @fn, or -1 on error
263 */
264int lxc_count_file_lines(const char *fn)
265{
1dc51604
CB
266 __do_free char *line = NULL;
267 __do_fclose FILE *f = NULL;
37ef15bb
CB
268 size_t sz = 0;
269 int n = 0;
270
271 f = fopen_cloexec(fn, "r");
272 if (!f)
273 return -1;
274
1dc51604 275 while (getline(&line, &sz, f) != -1)
37ef15bb 276 n++;
37ef15bb 277
37ef15bb
CB
278 return n;
279}
280
281int lxc_make_tmpfile(char *template, bool rm)
282{
f62cf1d4 283 __do_close int fd = -EBADF;
4aa90f60 284 int ret;
37ef15bb
CB
285 mode_t msk;
286
287 msk = umask(0022);
288 fd = mkstemp(template);
289 umask(msk);
290 if (fd < 0)
291 return -1;
292
4aa90f60
CB
293 if (lxc_set_cloexec(fd))
294 return -1;
295
37ef15bb 296 if (!rm)
240fecd0 297 return move_fd(fd);
37ef15bb
CB
298
299 ret = unlink(template);
4aa90f60 300 if (ret < 0)
37ef15bb 301 return -1;
37ef15bb 302
240fecd0 303 return move_fd(fd);
37ef15bb
CB
304}
305
37ef15bb
CB
306bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val)
307{
308 return (fs->f_type == (fs_type_magic)magic_val);
309}
310
311bool has_fs_type(const char *path, fs_type_magic magic_val)
312{
37ef15bb
CB
313 int ret;
314 struct statfs sb;
315
316 ret = statfs(path, &sb);
317 if (ret < 0)
318 return false;
319
320 return is_fs_type(&sb, magic_val);
321}
322
323bool fhas_fs_type(int fd, fs_type_magic magic_val)
324{
325 int ret;
326 struct statfs sb;
327
328 ret = fstatfs(fd, &sb);
329 if (ret < 0)
330 return false;
331
332 return is_fs_type(&sb, magic_val);
333}
334
335FILE *fopen_cloexec(const char *path, const char *mode)
336{
f62cf1d4 337 __do_close int fd = -EBADF;
1dc51604
CB
338 int open_mode = 0, step = 0;
339 FILE *f;
37ef15bb
CB
340
341 if (!strncmp(mode, "r+", 2)) {
342 open_mode = O_RDWR;
343 step = 2;
344 } else if (!strncmp(mode, "r", 1)) {
345 open_mode = O_RDONLY;
346 step = 1;
347 } else if (!strncmp(mode, "w+", 2)) {
348 open_mode = O_RDWR | O_TRUNC | O_CREAT;
349 step = 2;
350 } else if (!strncmp(mode, "w", 1)) {
351 open_mode = O_WRONLY | O_TRUNC | O_CREAT;
352 step = 1;
353 } else if (!strncmp(mode, "a+", 2)) {
354 open_mode = O_RDWR | O_CREAT | O_APPEND;
355 step = 2;
356 } else if (!strncmp(mode, "a", 1)) {
357 open_mode = O_WRONLY | O_CREAT | O_APPEND;
358 step = 1;
359 }
360 for (; mode[step]; step++)
361 if (mode[step] == 'x')
362 open_mode |= O_EXCL;
37ef15bb 363
1dc51604 364 fd = open(path, open_mode | O_CLOEXEC, 0660);
37ef15bb
CB
365 if (fd < 0)
366 return NULL;
367
1dc51604
CB
368 f = fdopen(fd, mode);
369 if (f)
370 move_fd(fd);
371 return f;
37ef15bb 372}
7c4d9466
CB
373
374ssize_t lxc_sendfile_nointr(int out_fd, int in_fd, off_t *offset, size_t count)
375{
376 ssize_t ret;
377
1dc51604
CB
378 do {
379 ret = sendfile(out_fd, in_fd, offset, count);
380 } while (ret < 0 && errno == EINTR);
7c4d9466
CB
381
382 return ret;
383}
6400238d 384
26dffd82 385ssize_t __fd_to_fd(int from, int to)
4aa90f60 386{
26dffd82
CB
387 ssize_t total_bytes = 0;
388
4aa90f60
CB
389 for (;;) {
390 uint8_t buf[PATH_MAX];
391 uint8_t *p = buf;
392 ssize_t bytes_to_write;
393 ssize_t bytes_read;
394
395 bytes_read = lxc_read_nointr(from, buf, sizeof buf);
396 if (bytes_read < 0)
397 return -1;
398 if (bytes_read == 0)
399 break;
400
401 bytes_to_write = (size_t)bytes_read;
7d84e2cd 402 total_bytes += bytes_read;
4aa90f60
CB
403 do {
404 ssize_t bytes_written;
405
406 bytes_written = lxc_write_nointr(to, p, bytes_to_write);
407 if (bytes_written < 0)
408 return -1;
409
410 bytes_to_write -= bytes_written;
411 p += bytes_written;
412 } while (bytes_to_write > 0);
413 }
414
26dffd82 415 return total_bytes;
4aa90f60 416}
4110345b 417
2120b89b 418int fd_to_buf(int fd, char **buf, size_t *length)
4110345b
CB
419{
420 __do_free char *copy = NULL;
421
422 if (!length)
2120b89b 423 return 0;
4110345b
CB
424
425 *length = 0;
426 for (;;) {
427 ssize_t bytes_read;
2120b89b 428 char chunk[4096];
4110345b
CB
429 char *old = copy;
430
2120b89b 431 bytes_read = lxc_read_nointr(fd, chunk, sizeof(chunk));
4110345b 432 if (bytes_read < 0)
2120b89b 433 return 0;
4110345b
CB
434
435 if (!bytes_read)
436 break;
437
438 copy = must_realloc(old, (*length + bytes_read) * sizeof(*old));
2120b89b 439 memcpy(copy + *length, chunk, bytes_read);
4110345b
CB
440 *length += bytes_read;
441 }
442
2120b89b
CB
443 *buf = move_ptr(copy);
444 return 0;
4110345b
CB
445}
446
447char *file_to_buf(const char *path, size_t *length)
448{
f62cf1d4 449 __do_close int fd = -EBADF;
2120b89b 450 char *buf = NULL;
4110345b
CB
451
452 if (!length)
453 return NULL;
454
455 fd = open(path, O_RDONLY | O_CLOEXEC);
456 if (fd < 0)
457 return NULL;
458
2120b89b
CB
459 if (fd_to_buf(fd, &buf, length) < 0)
460 return NULL;
461
462 return buf;
4110345b
CB
463}
464
465FILE *fopen_cached(const char *path, const char *mode, void **caller_freed_buffer)
466{
7fa90630 467#ifdef HAVE_FMEMOPEN
4110345b
CB
468 __do_free char *buf = NULL;
469 size_t len = 0;
470 FILE *f;
471
472 buf = file_to_buf(path, &len);
473 if (!buf)
474 return NULL;
475
476 f = fmemopen(buf, len, mode);
477 if (!f)
478 return NULL;
479 *caller_freed_buffer = move_ptr(buf);
480 return f;
7fa90630
CB
481#else
482 return fopen(path, mode);
483#endif
4110345b
CB
484}
485
486FILE *fdopen_cached(int fd, const char *mode, void **caller_freed_buffer)
487{
7fa90630
CB
488 FILE *f;
489#ifdef HAVE_FMEMOPEN
4110345b
CB
490 __do_free char *buf = NULL;
491 size_t len = 0;
4110345b 492
2120b89b 493 if (fd_to_buf(fd, &buf, &len) < 0)
4110345b
CB
494 return NULL;
495
496 f = fmemopen(buf, len, mode);
497 if (!f)
498 return NULL;
499
500 *caller_freed_buffer = move_ptr(buf);
7fa90630
CB
501
502#else
503
f62cf1d4 504 __do_close int dupfd = -EBADF;
7fa90630
CB
505
506 dupfd = dup(fd);
507 if (dupfd < 0)
508 return NULL;
509
510 f = fdopen(dupfd, "re");
511 if (!f)
512 return NULL;
513
514 /* Transfer ownership of fd. */
515 move_fd(dupfd);
516#endif
4110345b
CB
517 return f;
518}
70fd7fc9 519
a60d8c4e
CB
520int fd_cloexec(int fd, bool cloexec)
521{
522 int oflags, nflags;
523
524 oflags = fcntl(fd, F_GETFD, 0);
525 if (oflags < 0)
526 return -errno;
527
528 if (cloexec)
529 nflags = oflags | FD_CLOEXEC;
530 else
531 nflags = oflags & ~FD_CLOEXEC;
532
533 if (nflags == oflags)
534 return 0;
535
536 if (fcntl(fd, F_SETFD, nflags) < 0)
537 return -errno;
538
539 return 0;
540}
541
542static inline int dup_cloexec(int fd)
543{
544 __do_close int fd_dup = -EBADF;
545
546 fd_dup = dup(fd);
547 if (fd_dup < 0)
548 return -errno;
549
550 if (fd_cloexec(fd_dup, true))
551 return -errno;
552
553 return move_fd(fd_dup);
554}
555
556FILE *fdopenat(int dfd, const char *path, const char *mode)
557{
558 __do_close int fd = -EBADF;
559 __do_fclose FILE *f = NULL;
560
561 if (is_empty_string(path))
562 fd = dup_cloexec(dfd);
563 else
564 fd = openat(dfd, path, O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
565 if (fd < 0)
566 return NULL;
567
568 f = fdopen(fd, "re");
569 if (!f)
570 return NULL;
571
572 /* Transfer ownership of fd. */
573 move_fd(fd);
574
575 return move_ptr(f);
576}
577
70fd7fc9
CB
578int timens_offset_write(clockid_t clk_id, int64_t s_offset, int64_t ns_offset)
579{
580 __do_close int fd = -EBADF;
581 int ret;
582 ssize_t len;
583 char buf[INTTYPE_TO_STRLEN(int) +
584 STRLITERALLEN(" ") + INTTYPE_TO_STRLEN(int64_t) +
585 STRLITERALLEN(" ") + INTTYPE_TO_STRLEN(int64_t) + 1];
586
587 if (clk_id == CLOCK_MONOTONIC_COARSE || clk_id == CLOCK_MONOTONIC_RAW)
588 clk_id = CLOCK_MONOTONIC;
589
590 fd = open("/proc/self/timens_offsets", O_WRONLY | O_CLOEXEC);
591 if (fd < 0)
592 return -errno;
593
594 len = snprintf(buf, sizeof(buf), "%d %" PRId64 " %" PRId64, clk_id, s_offset, ns_offset);
595 if (len < 0 || len >= sizeof(buf))
596 return ret_errno(EFBIG);
597
598 ret = lxc_write_nointr(fd, buf, len);
599 if (ret < 0 || (size_t)ret != len)
600 return -EIO;
601
602 return 0;
603}
6f61472b
CB
604
605bool exists_dir_at(int dir_fd, const char *path)
606{
607 struct stat sb;
608 int ret;
609
610 ret = fstatat(dir_fd, path, &sb, 0);
611 if (ret < 0)
612 return false;
613
614 return S_ISDIR(sb.st_mode);
615}
953db219
CB
616
617bool exists_file_at(int dir_fd, const char *path)
618{
619 struct stat sb;
620
621 return fstatat(dir_fd, path, &sb, 0) == 0;
622}
8ea93a0f 623
7166ab75
CB
624int open_at(int dfd, const char *path, mode_t mode, unsigned int o_flags,
625 unsigned int resolve_flags)
8ea93a0f
CB
626{
627 __do_close int fd = -EBADF;
628 struct lxc_open_how how = {
7166ab75
CB
629 .flags = o_flags,
630 .mode = mode,
631 .resolve = resolve_flags,
8ea93a0f
CB
632 };
633
7166ab75 634 fd = openat2(dfd, path, &how, sizeof(how));
8ea93a0f
CB
635 if (fd >= 0)
636 return move_fd(fd);
637
638 if (errno != ENOSYS)
639 return -errno;
640
7166ab75 641 return openat(dfd, path, O_NOFOLLOW | o_flags);
8ea93a0f 642}
a60c98aa
CB
643
644int fd_make_nonblocking(int fd)
645{
646 int flags;
647
648 flags = fcntl(fd, F_GETFL);
649 if (flags < 0)
650 return -1;
651
652 flags &= ~O_NONBLOCK;
653 return fcntl(fd, F_SETFL, flags);
654}
d23cb29e
CB
655
656#define BATCH_SIZE 50
657static void batch_realloc(char **mem, size_t oldlen, size_t newlen)
658{
659 int newbatches = (newlen / BATCH_SIZE) + 1;
660 int oldbatches = (oldlen / BATCH_SIZE) + 1;
661
662 if (!*mem || newbatches > oldbatches)
663 *mem = must_realloc(*mem, newbatches * BATCH_SIZE);
664}
665
666static void append_line(char **dest, size_t oldlen, char *new, size_t newlen)
667{
668 size_t full = oldlen + newlen;
669
670 batch_realloc(dest, oldlen, full + 1);
671
672 memcpy(*dest + oldlen, new, newlen + 1);
673}
674
675/* Slurp in a whole file */
676char *read_file_at(int dfd, const char *fnam)
677{
678 __do_close int fd = -EBADF;
679 __do_free char *buf = NULL, *line = NULL;
680 __do_fclose FILE *f = NULL;
681 size_t len = 0, fulllen = 0;
682 int linelen;
683
684 fd = openat(dfd, fnam, O_NOCTTY | O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
685 if (fd < 0)
686 return NULL;
687
688 f = fdopen(fd, "re");
689 if (!f)
690 return NULL;
691 /* Transfer ownership to fdopen(). */
692 move_fd(fd);
693
694 while ((linelen = getline(&line, &len, f)) != -1) {
695 append_line(&buf, fulllen, line, linelen);
696 fulllen += linelen;
697 }
698
699 return move_ptr(buf);
700}