Merge branch 'for-5.5' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[linux-2.6-microblaze.git] / tools / perf / util / copyfile.c
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, 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 }