copyfd.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Utility routines.
  4. *
  5. * Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
  6. *
  7. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  8. */
  9. #include "libbb.h"
  10. /* Used by NOFORK applets (e.g. cat) - must not use xmalloc.
  11. * size < 0 means "ignore write errors", used by tar --to-command
  12. * size = 0 means "copy till EOF"
  13. */
  14. static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
  15. {
  16. int status = -1;
  17. off_t total = 0;
  18. bool continue_on_write_error = 0;
  19. #if CONFIG_FEATURE_COPYBUF_KB <= 4
  20. char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024];
  21. enum { buffer_size = sizeof(buffer) };
  22. #else
  23. char *buffer;
  24. int buffer_size;
  25. #endif
  26. if (size < 0) {
  27. size = -size;
  28. continue_on_write_error = 1;
  29. }
  30. #if CONFIG_FEATURE_COPYBUF_KB > 4
  31. if (size > 0 && size <= 4 * 1024)
  32. goto use_small_buf;
  33. /* We want page-aligned buffer, just in case kernel is clever
  34. * and can do page-aligned io more efficiently */
  35. buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024,
  36. PROT_READ | PROT_WRITE,
  37. MAP_PRIVATE | MAP_ANON,
  38. /* ignored: */ -1, 0);
  39. buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024;
  40. if (buffer == MAP_FAILED) {
  41. use_small_buf:
  42. buffer = alloca(4 * 1024);
  43. buffer_size = 4 * 1024;
  44. }
  45. #endif
  46. if (src_fd < 0)
  47. goto out;
  48. if (!size) {
  49. size = buffer_size;
  50. status = 1; /* copy until eof */
  51. }
  52. while (1) {
  53. ssize_t rd;
  54. rd = safe_read(src_fd, buffer, size > buffer_size ? buffer_size : size);
  55. if (!rd) { /* eof - all done */
  56. status = 0;
  57. break;
  58. }
  59. if (rd < 0) {
  60. bb_perror_msg(bb_msg_read_error);
  61. break;
  62. }
  63. /* dst_fd == -1 is a fake, else... */
  64. if (dst_fd >= 0) {
  65. ssize_t wr = full_write(dst_fd, buffer, rd);
  66. if (wr < rd) {
  67. if (!continue_on_write_error) {
  68. bb_perror_msg(bb_msg_write_error);
  69. break;
  70. }
  71. dst_fd = -1;
  72. }
  73. }
  74. total += rd;
  75. if (status < 0) { /* if we aren't copying till EOF... */
  76. size -= rd;
  77. if (!size) {
  78. /* 'size' bytes copied - all done */
  79. status = 0;
  80. break;
  81. }
  82. }
  83. }
  84. out:
  85. #if CONFIG_FEATURE_COPYBUF_KB > 4
  86. if (buffer_size != 4 * 1024)
  87. munmap(buffer, buffer_size);
  88. #endif
  89. return status ? -1 : total;
  90. }
  91. #if 0
  92. void FAST_FUNC complain_copyfd_and_die(off_t sz)
  93. {
  94. if (sz != -1)
  95. bb_error_msg_and_die("short read");
  96. /* if sz == -1, bb_copyfd_XX already complained */
  97. xfunc_die();
  98. }
  99. #endif
  100. off_t FAST_FUNC bb_copyfd_size(int fd1, int fd2, off_t size)
  101. {
  102. if (size) {
  103. return bb_full_fd_action(fd1, fd2, size);
  104. }
  105. return 0;
  106. }
  107. void FAST_FUNC bb_copyfd_exact_size(int fd1, int fd2, off_t size)
  108. {
  109. off_t sz = bb_copyfd_size(fd1, fd2, size);
  110. if (sz == (size >= 0 ? size : -size))
  111. return;
  112. if (sz != -1)
  113. bb_error_msg_and_die("short read");
  114. /* if sz == -1, bb_copyfd_XX already complained */
  115. xfunc_die();
  116. }
  117. off_t FAST_FUNC bb_copyfd_eof(int fd1, int fd2)
  118. {
  119. return bb_full_fd_action(fd1, fd2, 0);
  120. }