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