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