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