| 1 | |
| 2 | #include "util/copyfile.h"
|
| 3 | #include "util/namespaces.h"
|
| 4 | #include <internal/lib.h>
|
| 5 | #include <sys/mman.h>
|
| 6 | #include <sys/stat.h>
|
| 7 | #include <errno.h>
|
| 8 | #include <fcntl.h>
|
| 9 | #include <stdio.h>
|
| 10 | #include <stdlib.h>
|
| 11 | #include <string.h>
|
| 12 | #include <unistd.h>
|
| 13 |
|
| 14 | static int slow_copyfile(const char *from, const char *to, struct nsinfo *nsi)
|
| 15 | {
|
| 16 | int err = -1;
|
| 17 | char *line = NULL;
|
| 18 | size_t n;
|
| 19 | FILE *from_fp, *to_fp;
|
| 20 | struct nscookie nsc;
|
| 21 |
|
| 22 | nsinfo__mountns_enter(nsi, &nsc);
|
| 23 | from_fp = fopen(from, "r");
|
| 24 | nsinfo__mountns_exit(&nsc);
|
| 25 | if (from_fp == NULL)
|
| 26 | goto out;
|
| 27 |
|
| 28 | to_fp = fopen(to, "w");
|
| 29 | if (to_fp == NULL)
|
| 30 | goto out_fclose_from;
|
| 31 |
|
| 32 | while (getline(&line, &n, from_fp) > 0)
|
| 33 | if (fputs(line, to_fp) == EOF)
|
| 34 | goto out_fclose_to;
|
| 35 | err = 0;
|
| 36 | out_fclose_to:
|
| 37 | fclose(to_fp);
|
| 38 | free(line);
|
| 39 | out_fclose_from:
|
| 40 | fclose(from_fp);
|
| 41 | out:
|
| 42 | return err;
|
| 43 | }
|
| 44 |
|
| 45 | int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size)
|
| 46 | {
|
| 47 | void *ptr;
|
| 48 | loff_t pgoff;
|
| 49 |
|
| 50 | pgoff = off_in & ~(page_size - 1);
|
| 51 | off_in -= pgoff;
|
| 52 |
|
| 53 | ptr = mmap(NULL, off_in + size, PROT_READ, MAP_PRIVATE, ifd, pgoff);
|
| 54 | if (ptr == MAP_FAILED)
|
| 55 | return -1;
|
| 56 |
|
| 57 | while (size) {
|
| 58 | ssize_t ret = pwrite(ofd, ptr + off_in, size, off_out);
|
| 59 | if (ret < 0 && errno == EINTR)
|
| 60 | continue;
|
| 61 | if (ret <= 0)
|
| 62 | break;
|
| 63 |
|
| 64 | size -= ret;
|
| 65 | off_in += ret;
|
| 66 | off_out += ret;
|
| 67 | }
|
| 68 | munmap(ptr, off_in + size);
|
| 69 |
|
| 70 | return size ? -1 : 0;
|
| 71 | }
|
| 72 |
|
| 73 | static int copyfile_mode_ns(const char *from, const char *to, mode_t mode,
|
| 74 | struct nsinfo *nsi)
|
| 75 | {
|
| 76 | int fromfd, tofd;
|
| 77 | struct stat st;
|
| 78 | int err;
|
| 79 | char *tmp = NULL, *ptr = NULL;
|
| 80 | struct nscookie nsc;
|
| 81 |
|
| 82 | nsinfo__mountns_enter(nsi, &nsc);
|
| 83 | err = stat(from, &st);
|
| 84 | nsinfo__mountns_exit(&nsc);
|
| 85 | if (err)
|
| 86 | goto out;
|
| 87 | err = -1;
|
| 88 |
|
| 89 |
|
| 90 | if (asprintf(&tmp, "%s.XXXXXXx", to) < 0) {
|
| 91 | tmp = NULL;
|
| 92 | goto out;
|
| 93 | }
|
| 94 | ptr = strrchr(tmp, '/');
|
| 95 | if (!ptr)
|
| 96 | goto out;
|
| 97 | ptr = memmove(ptr + 1, ptr, strlen(ptr) - 1);
|
| 98 | *ptr = '.';
|
| 99 |
|
| 100 | tofd = mkstemp(tmp);
|
| 101 | if (tofd < 0)
|
| 102 | goto out;
|
| 103 |
|
| 104 | if (st.st_size == 0) {
|
| 105 | err = slow_copyfile(from, tmp, nsi);
|
| 106 | if (!err && fchmod(tofd, mode))
|
| 107 | err = -1;
|
| 108 | goto out_close_to;
|
| 109 | }
|
| 110 |
|
| 111 | if (fchmod(tofd, mode))
|
| 112 | goto out_close_to;
|
| 113 |
|
| 114 | nsinfo__mountns_enter(nsi, &nsc);
|
| 115 | fromfd = open(from, O_RDONLY);
|
| 116 | nsinfo__mountns_exit(&nsc);
|
| 117 | if (fromfd < 0)
|
| 118 | goto out_close_to;
|
| 119 |
|
| 120 | err = copyfile_offset(fromfd, 0, tofd, 0, st.st_size);
|
| 121 |
|
| 122 | close(fromfd);
|
| 123 | out_close_to:
|
| 124 | close(tofd);
|
| 125 | if (!err)
|
| 126 | err = link(tmp, to);
|
| 127 | unlink(tmp);
|
| 128 | out:
|
| 129 | free(tmp);
|
| 130 | return err;
|
| 131 | }
|
| 132 |
|
| 133 | int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi)
|
| 134 | {
|
| 135 | return copyfile_mode_ns(from, to, 0755, nsi);
|
| 136 | }
|
| 137 |
|
| 138 | int copyfile_mode(const char *from, const char *to, mode_t mode)
|
| 139 | {
|
| 140 | return copyfile_mode_ns(from, to, mode, NULL);
|
| 141 | }
|
| 142 |
|
| 143 | int copyfile(const char *from, const char *to)
|
| 144 | {
|
| 145 | return copyfile_mode(from, to, 0755);
|
| 146 | }
|