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 | |
104 | if (st.st_size == 0) { /* /proc? do it slowly... */ |
105 | err = slow_copyfile(from, to: 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 | } |
147 | |