[lxc-devel] [lxd/master] rsync: handle eagain in netcat

brauner on Github lxc-bot at linuxcontainers.org
Wed Apr 19 21:16:32 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170419/ae705b52/attachment.bin>
-------------- next part --------------
From 8dd03bb4edceb03cb8951d2525099d988b3aac80 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 19 Apr 2017 22:55:07 +0200
Subject: [PATCH 1/2] rsync: make our netcat handle EAGAIN

In particular, some filesystems give this, and golang's io.Copy doesn't
understand it and calls it an error. So let's instead just mask it and have
io.Copy try again.

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 lxd/main_netcat.go | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/lxd/main_netcat.go b/lxd/main_netcat.go
index f5be1b3..cc1bc83 100644
--- a/lxd/main_netcat.go
+++ b/lxd/main_netcat.go
@@ -6,6 +6,7 @@ import (
 	"net"
 	"os"
 	"sync"
+	"syscall"
 )
 
 // Netcat is called with:
@@ -34,7 +35,7 @@ func cmdNetcat(args []string) error {
 	wg.Add(1)
 
 	go func() {
-		io.Copy(os.Stdout, conn)
+		io.Copy(os.Stdout, hideAgainReader{conn})
 		conn.Close()
 		wg.Done()
 	}()
@@ -47,3 +48,19 @@ func cmdNetcat(args []string) error {
 
 	return nil
 }
+
+type retryOnEagainReader struct {
+	r io.Reader
+}
+
+func (hr retryOnEagainReader) Read(p []byte) (int, error) {
+	n, err := hr.r.Read(p)
+	if err != nil {
+		// golang's io.Copy doesn't understand EAGAIN, so let's mask it
+		if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN {
+			return n, nil
+		}
+	}
+
+	return n, err
+}

From bfe10958850f6799da2919406c45c80a4e4f58b6 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 19 Apr 2017 23:06:39 +0200
Subject: [PATCH 2/2] rsync: handle EAGAIN properly

Go's io.Copy() is not able to handle EAGAIN but Read() and Write() are. We need
to keep retrying on EAGAIN.

Closes #3168.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/main_netcat.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 55 insertions(+), 8 deletions(-)

diff --git a/lxd/main_netcat.go b/lxd/main_netcat.go
index cc1bc83..3591e4e 100644
--- a/lxd/main_netcat.go
+++ b/lxd/main_netcat.go
@@ -35,7 +35,7 @@ func cmdNetcat(args []string) error {
 	wg.Add(1)
 
 	go func() {
-		io.Copy(os.Stdout, hideAgainReader{conn})
+		io.Copy(eagainWriter{os.Stdout}, eagainReader{conn})
 		conn.Close()
 		wg.Done()
 	}()
@@ -49,18 +49,65 @@ func cmdNetcat(args []string) error {
 	return nil
 }
 
-type retryOnEagainReader struct {
+type eagainReader struct {
 	r io.Reader
 }
 
-func (hr retryOnEagainReader) Read(p []byte) (int, error) {
-	n, err := hr.r.Read(p)
-	if err != nil {
-		// golang's io.Copy doesn't understand EAGAIN, so let's mask it
-		if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN {
-			return n, nil
+func (er eagainReader) Read(p []byte) (int, error) {
+	// keep retrying on EAGAIN
+	again:
+	n, err := er.r.Read(p)
+	if err == nil {
+		return n, nil
+	}
+
+	var errno error
+	// EAGAIN errors can hide in os.PathError. My best explanation for this
+	// weirdness is that os.Stdout refers to /dev/stdout which is a path.
+	sysErr, ok := err.(*os.PathError)
+	if ok {
+		errno = sysErr.Err
+	} else {
+		tmpErrno, ok := err.(syscall.Errno)
+		if ok {
+			errno = tmpErrno
+		}
+	}
+
+	if errno == syscall.EAGAIN {
+		goto again
+	}
+
+	return n, err
+}
+
+type eagainWriter struct {
+	w io.Writer
+}
+
+func (ew eagainWriter) Write(p []byte) (int, error) {
+	// keep retrying on EAGAIN
+	again:
+	n, err := ew.w.Write(p)
+	if err == nil {
+		return n, nil
+	}
+
+	var errno error
+	// EAGAIN errors can hide in os.PathError. My best explanation for this
+	// weirdness is that os.Stdout refers to /dev/stdout which is a path.
+	sysErr, ok := err.(*os.PathError)
+	if ok {
+		errno = sysErr.Err
+	} else {
+		tmpErrno, ok := err.(syscall.Errno)
+		if ok {
+			errno = tmpErrno
 		}
 	}
+	if errno == syscall.EAGAIN {
+		goto again
+	}
 
 	return n, err
 }


More information about the lxc-devel mailing list