github.com/opencontainers/umoci@v0.4.8-0.20240508124516-656e4836fb0d/pkg/system/copy.go (about)

     1  /*
     2   * umoci: Umoci Modifies Open Containers' Images
     3   * Copyright (C) 2016-2022 SUSE LLC
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *    http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package system
    19  
    20  import (
    21  	"errors"
    22  	"io"
    23  
    24  	"golang.org/x/sys/unix"
    25  )
    26  
    27  // Copy has identical semantics to io.Copy except it will automatically resume
    28  // the copy after it receives an EINTR error.
    29  func Copy(dst io.Writer, src io.Reader) (int64, error) {
    30  	// Make a buffer so io.Copy doesn't make one for each iteration.
    31  	var buf []byte
    32  	size := 32 * 1024
    33  	if lr, ok := src.(*io.LimitedReader); ok && lr.N < int64(size) {
    34  		if lr.N < 1 {
    35  			size = 1
    36  		} else {
    37  			size = int(lr.N)
    38  		}
    39  	}
    40  	buf = make([]byte, size)
    41  
    42  	var written int64
    43  	for {
    44  		n, err := io.CopyBuffer(dst, src, buf)
    45  		written += n // n is always non-negative
    46  		if errors.Is(err, unix.EINTR) {
    47  			continue
    48  		}
    49  		return written, err
    50  	}
    51  }
    52  
    53  // CopyN has identical semantics to io.CopyN except it will automatically
    54  // resume the copy after it receives an EINTR error.
    55  func CopyN(dst io.Writer, src io.Reader, n int64) (written int64, err error) {
    56  	// This is based on the stdlib io.CopyN implementation.
    57  	written, err = Copy(dst, io.LimitReader(src, n))
    58  	if written == n {
    59  		err = nil // somewhat confusing io.CopyN semantics
    60  	}
    61  	if written < n && err == nil {
    62  		err = io.EOF // if the source ends prematurely, io.EOF
    63  	}
    64  	return
    65  }