147 lines · c
1// SPDX-License-Identifier: GPL-2.02#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}147