xref: /linux/tools/perf/util/copyfile.c (revision c95baf12f5077419db01313ab61c2aac007d40cd)
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