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