Commit | Line | Data |
---|---|---|
32ff3fec ACM |
1 | // SPDX-License-Identifier: GPL-2.0 |
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 | /* extra 'x' at the end is to reserve space for '.' */ | |
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 | ||
32ff3fec ACM |
104 | if (st.st_size == 0) { /* /proc? do it slowly... */ |
105 | err = slow_copyfile(from, tmp, nsi); | |
5a0baf51 AH |
106 | if (!err && fchmod(tofd, mode)) |
107 | err = -1; | |
32ff3fec ACM |
108 | goto out_close_to; |
109 | } | |
110 | ||
5a0baf51 AH |
111 | if (fchmod(tofd, mode)) |
112 | goto out_close_to; | |
113 | ||
32ff3fec ACM |
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 | } |