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 (fchmod(tofd, mode)) 105 goto out_close_to; 106 107 if (st.st_size == 0) { /* /proc? do it slowly... */ 108 err = slow_copyfile(from, tmp, nsi); 109 goto out_close_to; 110 } 111 112 nsinfo__mountns_enter(nsi, &nsc); 113 fromfd = open(from, O_RDONLY); 114 nsinfo__mountns_exit(&nsc); 115 if (fromfd < 0) 116 goto out_close_to; 117 118 err = copyfile_offset(fromfd, 0, tofd, 0, st.st_size); 119 120 close(fromfd); 121 out_close_to: 122 close(tofd); 123 if (!err) 124 err = link(tmp, to); 125 unlink(tmp); 126 out: 127 free(tmp); 128 return err; 129 } 130 131 int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi) 132 { 133 return copyfile_mode_ns(from, to, 0755, nsi); 134 } 135 136 int copyfile_mode(const char *from, const char *to, mode_t mode) 137 { 138 return copyfile_mode_ns(from, to, mode, NULL); 139 } 140 141 int copyfile(const char *from, const char *to) 142 { 143 return copyfile_mode(from, to, 0755); 144 } 145