132ff3fecSArnaldo Carvalho de Melo // SPDX-License-Identifier: GPL-2.0
232ff3fecSArnaldo Carvalho de Melo #include "util/copyfile.h"
332ff3fecSArnaldo Carvalho de Melo #include "util/namespaces.h"
432ff3fecSArnaldo Carvalho de Melo #include <internal/lib.h>
532ff3fecSArnaldo Carvalho de Melo #include <sys/mman.h>
632ff3fecSArnaldo Carvalho de Melo #include <sys/stat.h>
732ff3fecSArnaldo Carvalho de Melo #include <errno.h>
832ff3fecSArnaldo Carvalho de Melo #include <fcntl.h>
932ff3fecSArnaldo Carvalho de Melo #include <stdio.h>
1032ff3fecSArnaldo Carvalho de Melo #include <stdlib.h>
1132ff3fecSArnaldo Carvalho de Melo #include <string.h>
1232ff3fecSArnaldo Carvalho de Melo #include <unistd.h>
1332ff3fecSArnaldo Carvalho de Melo
slow_copyfile(const char * from,const char * to,struct nsinfo * nsi)1432ff3fecSArnaldo Carvalho de Melo static int slow_copyfile(const char *from, const char *to, struct nsinfo *nsi)
1532ff3fecSArnaldo Carvalho de Melo {
1632ff3fecSArnaldo Carvalho de Melo int err = -1;
1732ff3fecSArnaldo Carvalho de Melo char *line = NULL;
1832ff3fecSArnaldo Carvalho de Melo size_t n;
1932ff3fecSArnaldo Carvalho de Melo FILE *from_fp, *to_fp;
2032ff3fecSArnaldo Carvalho de Melo struct nscookie nsc;
2132ff3fecSArnaldo Carvalho de Melo
2232ff3fecSArnaldo Carvalho de Melo nsinfo__mountns_enter(nsi, &nsc);
2332ff3fecSArnaldo Carvalho de Melo from_fp = fopen(from, "r");
2432ff3fecSArnaldo Carvalho de Melo nsinfo__mountns_exit(&nsc);
2532ff3fecSArnaldo Carvalho de Melo if (from_fp == NULL)
2632ff3fecSArnaldo Carvalho de Melo goto out;
2732ff3fecSArnaldo Carvalho de Melo
2832ff3fecSArnaldo Carvalho de Melo to_fp = fopen(to, "w");
2932ff3fecSArnaldo Carvalho de Melo if (to_fp == NULL)
3032ff3fecSArnaldo Carvalho de Melo goto out_fclose_from;
3132ff3fecSArnaldo Carvalho de Melo
3232ff3fecSArnaldo Carvalho de Melo while (getline(&line, &n, from_fp) > 0)
3332ff3fecSArnaldo Carvalho de Melo if (fputs(line, to_fp) == EOF)
3432ff3fecSArnaldo Carvalho de Melo goto out_fclose_to;
3532ff3fecSArnaldo Carvalho de Melo err = 0;
3632ff3fecSArnaldo Carvalho de Melo out_fclose_to:
3732ff3fecSArnaldo Carvalho de Melo fclose(to_fp);
3832ff3fecSArnaldo Carvalho de Melo free(line);
3932ff3fecSArnaldo Carvalho de Melo out_fclose_from:
4032ff3fecSArnaldo Carvalho de Melo fclose(from_fp);
4132ff3fecSArnaldo Carvalho de Melo out:
4232ff3fecSArnaldo Carvalho de Melo return err;
4332ff3fecSArnaldo Carvalho de Melo }
4432ff3fecSArnaldo Carvalho de Melo
copyfile_offset(int ifd,loff_t off_in,int ofd,loff_t off_out,u64 size)4532ff3fecSArnaldo Carvalho de Melo int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size)
4632ff3fecSArnaldo Carvalho de Melo {
4732ff3fecSArnaldo Carvalho de Melo void *ptr;
4832ff3fecSArnaldo Carvalho de Melo loff_t pgoff;
4932ff3fecSArnaldo Carvalho de Melo
5032ff3fecSArnaldo Carvalho de Melo pgoff = off_in & ~(page_size - 1);
5132ff3fecSArnaldo Carvalho de Melo off_in -= pgoff;
5232ff3fecSArnaldo Carvalho de Melo
5332ff3fecSArnaldo Carvalho de Melo ptr = mmap(NULL, off_in + size, PROT_READ, MAP_PRIVATE, ifd, pgoff);
5432ff3fecSArnaldo Carvalho de Melo if (ptr == MAP_FAILED)
5532ff3fecSArnaldo Carvalho de Melo return -1;
5632ff3fecSArnaldo Carvalho de Melo
5732ff3fecSArnaldo Carvalho de Melo while (size) {
5832ff3fecSArnaldo Carvalho de Melo ssize_t ret = pwrite(ofd, ptr + off_in, size, off_out);
5932ff3fecSArnaldo Carvalho de Melo if (ret < 0 && errno == EINTR)
6032ff3fecSArnaldo Carvalho de Melo continue;
6132ff3fecSArnaldo Carvalho de Melo if (ret <= 0)
6232ff3fecSArnaldo Carvalho de Melo break;
6332ff3fecSArnaldo Carvalho de Melo
6432ff3fecSArnaldo Carvalho de Melo size -= ret;
6532ff3fecSArnaldo Carvalho de Melo off_in += ret;
6632ff3fecSArnaldo Carvalho de Melo off_out += ret;
6732ff3fecSArnaldo Carvalho de Melo }
6832ff3fecSArnaldo Carvalho de Melo munmap(ptr, off_in + size);
6932ff3fecSArnaldo Carvalho de Melo
7032ff3fecSArnaldo Carvalho de Melo return size ? -1 : 0;
7132ff3fecSArnaldo Carvalho de Melo }
7232ff3fecSArnaldo Carvalho de Melo
copyfile_mode_ns(const char * from,const char * to,mode_t mode,struct nsinfo * nsi)7332ff3fecSArnaldo Carvalho de Melo static int copyfile_mode_ns(const char *from, const char *to, mode_t mode,
7432ff3fecSArnaldo Carvalho de Melo struct nsinfo *nsi)
7532ff3fecSArnaldo Carvalho de Melo {
7632ff3fecSArnaldo Carvalho de Melo int fromfd, tofd;
7732ff3fecSArnaldo Carvalho de Melo struct stat st;
7832ff3fecSArnaldo Carvalho de Melo int err;
7932ff3fecSArnaldo Carvalho de Melo char *tmp = NULL, *ptr = NULL;
8032ff3fecSArnaldo Carvalho de Melo struct nscookie nsc;
8132ff3fecSArnaldo Carvalho de Melo
8232ff3fecSArnaldo Carvalho de Melo nsinfo__mountns_enter(nsi, &nsc);
8332ff3fecSArnaldo Carvalho de Melo err = stat(from, &st);
8432ff3fecSArnaldo Carvalho de Melo nsinfo__mountns_exit(&nsc);
8532ff3fecSArnaldo Carvalho de Melo if (err)
8632ff3fecSArnaldo Carvalho de Melo goto out;
8732ff3fecSArnaldo Carvalho de Melo err = -1;
8832ff3fecSArnaldo Carvalho de Melo
8932ff3fecSArnaldo Carvalho de Melo /* extra 'x' at the end is to reserve space for '.' */
9032ff3fecSArnaldo Carvalho de Melo if (asprintf(&tmp, "%s.XXXXXXx", to) < 0) {
9132ff3fecSArnaldo Carvalho de Melo tmp = NULL;
9232ff3fecSArnaldo Carvalho de Melo goto out;
9332ff3fecSArnaldo Carvalho de Melo }
9432ff3fecSArnaldo Carvalho de Melo ptr = strrchr(tmp, '/');
9532ff3fecSArnaldo Carvalho de Melo if (!ptr)
9632ff3fecSArnaldo Carvalho de Melo goto out;
9732ff3fecSArnaldo Carvalho de Melo ptr = memmove(ptr + 1, ptr, strlen(ptr) - 1);
9832ff3fecSArnaldo Carvalho de Melo *ptr = '.';
9932ff3fecSArnaldo Carvalho de Melo
10032ff3fecSArnaldo Carvalho de Melo tofd = mkstemp(tmp);
10132ff3fecSArnaldo Carvalho de Melo if (tofd < 0)
10232ff3fecSArnaldo Carvalho de Melo goto out;
10332ff3fecSArnaldo Carvalho de Melo
10432ff3fecSArnaldo Carvalho de Melo if (st.st_size == 0) { /* /proc? do it slowly... */
10532ff3fecSArnaldo Carvalho de Melo err = slow_copyfile(from, tmp, nsi);
106*5a0baf51SAdrian Hunter if (!err && fchmod(tofd, mode))
107*5a0baf51SAdrian Hunter err = -1;
10832ff3fecSArnaldo Carvalho de Melo goto out_close_to;
10932ff3fecSArnaldo Carvalho de Melo }
11032ff3fecSArnaldo Carvalho de Melo
111*5a0baf51SAdrian Hunter if (fchmod(tofd, mode))
112*5a0baf51SAdrian Hunter goto out_close_to;
113*5a0baf51SAdrian Hunter
11432ff3fecSArnaldo Carvalho de Melo nsinfo__mountns_enter(nsi, &nsc);
11532ff3fecSArnaldo Carvalho de Melo fromfd = open(from, O_RDONLY);
11632ff3fecSArnaldo Carvalho de Melo nsinfo__mountns_exit(&nsc);
11732ff3fecSArnaldo Carvalho de Melo if (fromfd < 0)
11832ff3fecSArnaldo Carvalho de Melo goto out_close_to;
11932ff3fecSArnaldo Carvalho de Melo
12032ff3fecSArnaldo Carvalho de Melo err = copyfile_offset(fromfd, 0, tofd, 0, st.st_size);
12132ff3fecSArnaldo Carvalho de Melo
12232ff3fecSArnaldo Carvalho de Melo close(fromfd);
12332ff3fecSArnaldo Carvalho de Melo out_close_to:
12432ff3fecSArnaldo Carvalho de Melo close(tofd);
12532ff3fecSArnaldo Carvalho de Melo if (!err)
12632ff3fecSArnaldo Carvalho de Melo err = link(tmp, to);
12732ff3fecSArnaldo Carvalho de Melo unlink(tmp);
12832ff3fecSArnaldo Carvalho de Melo out:
12932ff3fecSArnaldo Carvalho de Melo free(tmp);
13032ff3fecSArnaldo Carvalho de Melo return err;
13132ff3fecSArnaldo Carvalho de Melo }
13232ff3fecSArnaldo Carvalho de Melo
copyfile_ns(const char * from,const char * to,struct nsinfo * nsi)13332ff3fecSArnaldo Carvalho de Melo int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi)
13432ff3fecSArnaldo Carvalho de Melo {
13532ff3fecSArnaldo Carvalho de Melo return copyfile_mode_ns(from, to, 0755, nsi);
13632ff3fecSArnaldo Carvalho de Melo }
13732ff3fecSArnaldo Carvalho de Melo
copyfile_mode(const char * from,const char * to,mode_t mode)13832ff3fecSArnaldo Carvalho de Melo int copyfile_mode(const char *from, const char *to, mode_t mode)
13932ff3fecSArnaldo Carvalho de Melo {
14032ff3fecSArnaldo Carvalho de Melo return copyfile_mode_ns(from, to, mode, NULL);
14132ff3fecSArnaldo Carvalho de Melo }
14232ff3fecSArnaldo Carvalho de Melo
copyfile(const char * from,const char * to)14332ff3fecSArnaldo Carvalho de Melo int copyfile(const char *from, const char *to)
14432ff3fecSArnaldo Carvalho de Melo {
14532ff3fecSArnaldo Carvalho de Melo return copyfile_mode(from, to, 0755);
14632ff3fecSArnaldo Carvalho de Melo }
147