copyfile.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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. static int slow_copyfile(const char *from, const char *to, struct nsinfo *nsi)
  14. {
  15. int err = -1;
  16. char *line = NULL;
  17. size_t n;
  18. FILE *from_fp, *to_fp;
  19. struct nscookie nsc;
  20. nsinfo__mountns_enter(nsi, &nsc);
  21. from_fp = fopen(from, "r");
  22. nsinfo__mountns_exit(&nsc);
  23. if (from_fp == NULL)
  24. goto out;
  25. to_fp = fopen(to, "w");
  26. if (to_fp == NULL)
  27. goto out_fclose_from;
  28. while (getline(&line, &n, from_fp) > 0)
  29. if (fputs(line, to_fp) == EOF)
  30. goto out_fclose_to;
  31. err = 0;
  32. out_fclose_to:
  33. fclose(to_fp);
  34. free(line);
  35. out_fclose_from:
  36. fclose(from_fp);
  37. out:
  38. return err;
  39. }
  40. int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size)
  41. {
  42. void *ptr;
  43. loff_t pgoff;
  44. pgoff = off_in & ~(page_size - 1);
  45. off_in -= pgoff;
  46. ptr = mmap(NULL, off_in + size, PROT_READ, MAP_PRIVATE, ifd, pgoff);
  47. if (ptr == MAP_FAILED)
  48. return -1;
  49. while (size) {
  50. ssize_t ret = pwrite(ofd, ptr + off_in, size, off_out);
  51. if (ret < 0 && errno == EINTR)
  52. continue;
  53. if (ret <= 0)
  54. break;
  55. size -= ret;
  56. off_in += ret;
  57. off_out += ret;
  58. }
  59. munmap(ptr, off_in + size);
  60. return size ? -1 : 0;
  61. }
  62. static int copyfile_mode_ns(const char *from, const char *to, mode_t mode,
  63. struct nsinfo *nsi)
  64. {
  65. int fromfd, tofd;
  66. struct stat st;
  67. int err;
  68. char *tmp = NULL, *ptr = NULL;
  69. struct nscookie nsc;
  70. nsinfo__mountns_enter(nsi, &nsc);
  71. err = stat(from, &st);
  72. nsinfo__mountns_exit(&nsc);
  73. if (err)
  74. goto out;
  75. err = -1;
  76. /* extra 'x' at the end is to reserve space for '.' */
  77. if (asprintf(&tmp, "%s.XXXXXXx", to) < 0) {
  78. tmp = NULL;
  79. goto out;
  80. }
  81. ptr = strrchr(tmp, '/');
  82. if (!ptr)
  83. goto out;
  84. ptr = memmove(ptr + 1, ptr, strlen(ptr) - 1);
  85. *ptr = '.';
  86. tofd = mkstemp(tmp);
  87. if (tofd < 0)
  88. goto out;
  89. if (st.st_size == 0) { /* /proc? do it slowly... */
  90. err = slow_copyfile(from, tmp, nsi);
  91. if (!err && fchmod(tofd, mode))
  92. err = -1;
  93. goto out_close_to;
  94. }
  95. if (fchmod(tofd, mode))
  96. goto out_close_to;
  97. nsinfo__mountns_enter(nsi, &nsc);
  98. fromfd = open(from, O_RDONLY);
  99. nsinfo__mountns_exit(&nsc);
  100. if (fromfd < 0)
  101. goto out_close_to;
  102. err = copyfile_offset(fromfd, 0, tofd, 0, st.st_size);
  103. close(fromfd);
  104. out_close_to:
  105. close(tofd);
  106. if (!err)
  107. err = link(tmp, to);
  108. unlink(tmp);
  109. out:
  110. free(tmp);
  111. return err;
  112. }
  113. int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi)
  114. {
  115. return copyfile_mode_ns(from, to, 0755, nsi);
  116. }
  117. int copyfile_mode(const char *from, const char *to, mode_t mode)
  118. {
  119. return copyfile_mode_ns(from, to, mode, NULL);
  120. }
  121. int copyfile(const char *from, const char *to)
  122. {
  123. return copyfile_mode(from, to, 0755);
  124. }