github.com/pkg/sftp@v1.13.6/client_integration_test.go (about)

     1  package sftp
     2  
     3  // sftp integration tests
     4  // enable with -integration
     5  
     6  import (
     7  	"bytes"
     8  	"crypto/sha1"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"math/rand"
    14  	"net"
    15  	"os"
    16  	"os/exec"
    17  	"os/user"
    18  	"path"
    19  	"path/filepath"
    20  	"reflect"
    21  	"regexp"
    22  	"runtime"
    23  	"sort"
    24  	"strconv"
    25  	"sync"
    26  	"testing"
    27  	"testing/quick"
    28  	"time"
    29  
    30  	"github.com/kr/fs"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  const (
    36  	READONLY                = true
    37  	READWRITE               = false
    38  	NODELAY   time.Duration = 0
    39  
    40  	debuglevel = "ERROR" // set to "DEBUG" for debugging
    41  )
    42  
    43  type delayedWrite struct {
    44  	t time.Time
    45  	b []byte
    46  }
    47  
    48  // delayedWriter wraps a writer and artificially delays the write. This is
    49  // meant to mimic connections with various latencies. Error's returned from the
    50  // underlying writer will panic so this should only be used over reliable
    51  // connections.
    52  type delayedWriter struct {
    53  	closed chan struct{}
    54  
    55  	mu      sync.Mutex
    56  	ch      chan delayedWrite
    57  	closing chan struct{}
    58  }
    59  
    60  func newDelayedWriter(w io.WriteCloser, delay time.Duration) io.WriteCloser {
    61  	dw := &delayedWriter{
    62  		ch:      make(chan delayedWrite, 128),
    63  		closed:  make(chan struct{}),
    64  		closing: make(chan struct{}),
    65  	}
    66  
    67  	go func() {
    68  		defer close(dw.closed)
    69  		defer w.Close()
    70  
    71  		for writeMsg := range dw.ch {
    72  			time.Sleep(time.Until(writeMsg.t.Add(delay)))
    73  
    74  			n, err := w.Write(writeMsg.b)
    75  			if err != nil {
    76  				panic("write error")
    77  			}
    78  
    79  			if n < len(writeMsg.b) {
    80  				panic("showrt write")
    81  			}
    82  		}
    83  	}()
    84  
    85  	return dw
    86  }
    87  
    88  func (dw *delayedWriter) Write(b []byte) (int, error) {
    89  	dw.mu.Lock()
    90  	defer dw.mu.Unlock()
    91  
    92  	write := delayedWrite{
    93  		t: time.Now(),
    94  		b: append([]byte(nil), b...),
    95  	}
    96  
    97  	select {
    98  	case <-dw.closing:
    99  		return 0, errors.New("delayedWriter is closing")
   100  	case dw.ch <- write:
   101  	}
   102  
   103  	return len(b), nil
   104  }
   105  
   106  func (dw *delayedWriter) Close() error {
   107  	dw.mu.Lock()
   108  	defer dw.mu.Unlock()
   109  
   110  	select {
   111  	case <-dw.closing:
   112  	default:
   113  		close(dw.ch)
   114  		close(dw.closing)
   115  	}
   116  
   117  	<-dw.closed
   118  	return nil
   119  }
   120  
   121  // netPipe provides a pair of io.ReadWriteClosers connected to each other.
   122  // The functions is identical to os.Pipe with the exception that netPipe
   123  // provides the Read/Close guarantees that os.File derrived pipes do not.
   124  func netPipe(t testing.TB) (io.ReadWriteCloser, io.ReadWriteCloser) {
   125  	type result struct {
   126  		net.Conn
   127  		error
   128  	}
   129  
   130  	l, err := net.Listen("tcp", "127.0.0.1:0")
   131  	if err != nil {
   132  		t.Fatal(err)
   133  	}
   134  
   135  	closeListener := make(chan struct{}, 1)
   136  	closeListener <- struct{}{}
   137  
   138  	ch := make(chan result, 1)
   139  	go func() {
   140  		conn, err := l.Accept()
   141  		ch <- result{conn, err}
   142  
   143  		if _, ok := <-closeListener; ok {
   144  			err = l.Close()
   145  			if err != nil {
   146  				t.Error(err)
   147  			}
   148  			close(closeListener)
   149  		}
   150  	}()
   151  
   152  	c1, err := net.Dial("tcp", l.Addr().String())
   153  	if err != nil {
   154  		if _, ok := <-closeListener; ok {
   155  			l.Close()
   156  			close(closeListener)
   157  		}
   158  		t.Fatal(err)
   159  	}
   160  
   161  	r := <-ch
   162  	if r.error != nil {
   163  		t.Fatal(err)
   164  	}
   165  
   166  	return c1, r.Conn
   167  }
   168  
   169  func testClientGoSvr(t testing.TB, readonly bool, delay time.Duration, opts ...ClientOption) (*Client, *exec.Cmd) {
   170  	c1, c2 := netPipe(t)
   171  
   172  	options := []ServerOption{WithDebug(os.Stderr)}
   173  	if readonly {
   174  		options = append(options, ReadOnly())
   175  	}
   176  
   177  	server, err := NewServer(c1, options...)
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  	go server.Serve()
   182  
   183  	var wr io.WriteCloser = c2
   184  	if delay > NODELAY {
   185  		wr = newDelayedWriter(wr, delay)
   186  	}
   187  
   188  	client, err := NewClientPipe(c2, wr, opts...)
   189  	if err != nil {
   190  		t.Fatal(err)
   191  	}
   192  
   193  	// dummy command...
   194  	return client, exec.Command("true")
   195  }
   196  
   197  // testClient returns a *Client connected to a locally running sftp-server
   198  // the *exec.Cmd returned must be defer Wait'd.
   199  func testClient(t testing.TB, readonly bool, delay time.Duration, opts ...ClientOption) (*Client, *exec.Cmd) {
   200  	if !*testIntegration {
   201  		t.Skip("skipping integration test")
   202  	}
   203  
   204  	if *testServerImpl {
   205  		return testClientGoSvr(t, readonly, delay, opts...)
   206  	}
   207  
   208  	cmd := exec.Command(*testSftp, "-e", "-R", "-l", debuglevel) // log to stderr, read only
   209  	if !readonly {
   210  		cmd = exec.Command(*testSftp, "-e", "-l", debuglevel) // log to stderr
   211  	}
   212  
   213  	cmd.Stderr = os.Stdout
   214  
   215  	pw, err := cmd.StdinPipe()
   216  	if err != nil {
   217  		t.Fatal(err)
   218  	}
   219  
   220  	if delay > NODELAY {
   221  		pw = newDelayedWriter(pw, delay)
   222  	}
   223  
   224  	pr, err := cmd.StdoutPipe()
   225  	if err != nil {
   226  		t.Fatal(err)
   227  	}
   228  
   229  	if err := cmd.Start(); err != nil {
   230  		t.Skipf("could not start sftp-server process: %v", err)
   231  	}
   232  
   233  	sftp, err := NewClientPipe(pr, pw, opts...)
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	}
   237  
   238  	return sftp, cmd
   239  }
   240  
   241  func TestNewClient(t *testing.T) {
   242  	sftp, cmd := testClient(t, READONLY, NODELAY)
   243  	defer cmd.Wait()
   244  
   245  	if err := sftp.Close(); err != nil {
   246  		t.Fatal(err)
   247  	}
   248  }
   249  
   250  func TestClientLstat(t *testing.T) {
   251  	sftp, cmd := testClient(t, READONLY, NODELAY)
   252  	defer cmd.Wait()
   253  	defer sftp.Close()
   254  
   255  	f, err := ioutil.TempFile("", "sftptest-lstat")
   256  	if err != nil {
   257  		t.Fatal(err)
   258  	}
   259  	f.Close()
   260  	defer os.Remove(f.Name())
   261  
   262  	want, err := os.Lstat(f.Name())
   263  	if err != nil {
   264  		t.Fatal(err)
   265  	}
   266  
   267  	got, err := sftp.Lstat(f.Name())
   268  	if err != nil {
   269  		t.Fatal(err)
   270  	}
   271  
   272  	if !sameFile(want, got) {
   273  		t.Fatalf("Lstat(%q): want %#v, got %#v", f.Name(), want, got)
   274  	}
   275  }
   276  
   277  func TestClientLstatIsNotExist(t *testing.T) {
   278  	sftp, cmd := testClient(t, READONLY, NODELAY)
   279  	defer cmd.Wait()
   280  	defer sftp.Close()
   281  
   282  	f, err := ioutil.TempFile("", "sftptest-lstatisnotexist")
   283  	if err != nil {
   284  		t.Fatal(err)
   285  	}
   286  	f.Close()
   287  	os.Remove(f.Name())
   288  
   289  	if _, err := sftp.Lstat(f.Name()); !os.IsNotExist(err) {
   290  		t.Errorf("os.IsNotExist(%v) = false, want true", err)
   291  	}
   292  }
   293  
   294  func TestClientMkdir(t *testing.T) {
   295  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   296  	defer cmd.Wait()
   297  	defer sftp.Close()
   298  
   299  	dir, err := ioutil.TempDir("", "sftptest-mkdir")
   300  	if err != nil {
   301  		t.Fatal(err)
   302  	}
   303  	defer os.RemoveAll(dir)
   304  
   305  	sub := path.Join(dir, "mkdir1")
   306  	if err := sftp.Mkdir(sub); err != nil {
   307  		t.Fatal(err)
   308  	}
   309  	if _, err := os.Lstat(sub); err != nil {
   310  		t.Fatal(err)
   311  	}
   312  }
   313  func TestClientMkdirAll(t *testing.T) {
   314  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   315  	defer cmd.Wait()
   316  	defer sftp.Close()
   317  
   318  	dir, err := ioutil.TempDir("", "sftptest-mkdirall")
   319  	if err != nil {
   320  		t.Fatal(err)
   321  	}
   322  	defer os.RemoveAll(dir)
   323  
   324  	sub := path.Join(dir, "mkdir1", "mkdir2", "mkdir3")
   325  	if err := sftp.MkdirAll(sub); err != nil {
   326  		t.Fatal(err)
   327  	}
   328  	info, err := os.Lstat(sub)
   329  	if err != nil {
   330  		t.Fatal(err)
   331  	}
   332  	if !info.IsDir() {
   333  		t.Fatalf("Expected mkdirall to create dir at: %s", sub)
   334  	}
   335  }
   336  
   337  func TestClientOpen(t *testing.T) {
   338  	sftp, cmd := testClient(t, READONLY, NODELAY)
   339  	defer cmd.Wait()
   340  	defer sftp.Close()
   341  
   342  	f, err := ioutil.TempFile("", "sftptest-open")
   343  	if err != nil {
   344  		t.Fatal(err)
   345  	}
   346  	f.Close()
   347  	defer os.Remove(f.Name())
   348  
   349  	got, err := sftp.Open(f.Name())
   350  	if err != nil {
   351  		t.Fatal(err)
   352  	}
   353  	if err := got.Close(); err != nil {
   354  		t.Fatal(err)
   355  	}
   356  }
   357  
   358  func TestClientOpenIsNotExist(t *testing.T) {
   359  	sftp, cmd := testClient(t, READONLY, NODELAY)
   360  	defer cmd.Wait()
   361  	defer sftp.Close()
   362  
   363  	if _, err := sftp.Open("/doesnt/exist"); !os.IsNotExist(err) {
   364  		t.Errorf("os.IsNotExist(%v) = false, want true", err)
   365  	}
   366  }
   367  
   368  func TestClientStatIsNotExist(t *testing.T) {
   369  	sftp, cmd := testClient(t, READONLY, NODELAY)
   370  	defer cmd.Wait()
   371  	defer sftp.Close()
   372  
   373  	if _, err := sftp.Stat("/doesnt/exist"); !os.IsNotExist(err) {
   374  		t.Errorf("os.IsNotExist(%v) = false, want true", err)
   375  	}
   376  }
   377  
   378  const seekBytes = 128 * 1024
   379  
   380  type seek struct {
   381  	offset int64
   382  }
   383  
   384  func (s seek) Generate(r *rand.Rand, _ int) reflect.Value {
   385  	s.offset = int64(r.Int31n(seekBytes))
   386  	return reflect.ValueOf(s)
   387  }
   388  
   389  func (s seek) set(t *testing.T, r io.ReadSeeker) {
   390  	if _, err := r.Seek(s.offset, io.SeekStart); err != nil {
   391  		t.Fatalf("error while seeking with %+v: %v", s, err)
   392  	}
   393  }
   394  
   395  func (s seek) current(t *testing.T, r io.ReadSeeker) {
   396  	const mid = seekBytes / 2
   397  
   398  	skip := s.offset / 2
   399  	if s.offset > mid {
   400  		skip = -skip
   401  	}
   402  
   403  	if _, err := r.Seek(mid, io.SeekStart); err != nil {
   404  		t.Fatalf("error seeking to midpoint with %+v: %v", s, err)
   405  	}
   406  	if _, err := r.Seek(skip, io.SeekCurrent); err != nil {
   407  		t.Fatalf("error seeking from %d with %+v: %v", mid, s, err)
   408  	}
   409  }
   410  
   411  func (s seek) end(t *testing.T, r io.ReadSeeker) {
   412  	if _, err := r.Seek(-s.offset, io.SeekEnd); err != nil {
   413  		t.Fatalf("error seeking from end with %+v: %v", s, err)
   414  	}
   415  }
   416  
   417  func TestClientSeek(t *testing.T) {
   418  	sftp, cmd := testClient(t, READONLY, NODELAY)
   419  	defer cmd.Wait()
   420  	defer sftp.Close()
   421  
   422  	fOS, err := ioutil.TempFile("", "sftptest-seek")
   423  	if err != nil {
   424  		t.Fatal(err)
   425  	}
   426  	defer os.Remove(fOS.Name())
   427  	defer fOS.Close()
   428  
   429  	fSFTP, err := sftp.Open(fOS.Name())
   430  	if err != nil {
   431  		t.Fatal(err)
   432  	}
   433  	defer fSFTP.Close()
   434  
   435  	writeN(t, fOS, seekBytes)
   436  
   437  	if err := quick.CheckEqual(
   438  		func(s seek) (string, int64) { s.set(t, fOS); return readHash(t, fOS) },
   439  		func(s seek) (string, int64) { s.set(t, fSFTP); return readHash(t, fSFTP) },
   440  		nil,
   441  	); err != nil {
   442  		t.Errorf("Seek: expected equal absolute seeks: %v", err)
   443  	}
   444  
   445  	if err := quick.CheckEqual(
   446  		func(s seek) (string, int64) { s.current(t, fOS); return readHash(t, fOS) },
   447  		func(s seek) (string, int64) { s.current(t, fSFTP); return readHash(t, fSFTP) },
   448  		nil,
   449  	); err != nil {
   450  		t.Errorf("Seek: expected equal seeks from middle: %v", err)
   451  	}
   452  
   453  	if err := quick.CheckEqual(
   454  		func(s seek) (string, int64) { s.end(t, fOS); return readHash(t, fOS) },
   455  		func(s seek) (string, int64) { s.end(t, fSFTP); return readHash(t, fSFTP) },
   456  		nil,
   457  	); err != nil {
   458  		t.Errorf("Seek: expected equal seeks from end: %v", err)
   459  	}
   460  }
   461  
   462  func TestClientCreate(t *testing.T) {
   463  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   464  	defer cmd.Wait()
   465  	defer sftp.Close()
   466  
   467  	f, err := ioutil.TempFile("", "sftptest-create")
   468  	if err != nil {
   469  		t.Fatal(err)
   470  	}
   471  	defer f.Close()
   472  	defer os.Remove(f.Name())
   473  
   474  	f2, err := sftp.Create(f.Name())
   475  	if err != nil {
   476  		t.Fatal(err)
   477  	}
   478  	defer f2.Close()
   479  }
   480  
   481  func TestClientAppend(t *testing.T) {
   482  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   483  	defer cmd.Wait()
   484  	defer sftp.Close()
   485  
   486  	f, err := ioutil.TempFile("", "sftptest-append")
   487  	if err != nil {
   488  		t.Fatal(err)
   489  	}
   490  	defer f.Close()
   491  	defer os.Remove(f.Name())
   492  
   493  	f2, err := sftp.OpenFile(f.Name(), os.O_RDWR|os.O_APPEND)
   494  	if err != nil {
   495  		t.Fatal(err)
   496  	}
   497  	defer f2.Close()
   498  }
   499  
   500  func TestClientCreateFailed(t *testing.T) {
   501  	sftp, cmd := testClient(t, READONLY, NODELAY)
   502  	defer cmd.Wait()
   503  	defer sftp.Close()
   504  
   505  	f, err := ioutil.TempFile("", "sftptest-createfailed")
   506  	require.NoError(t, err)
   507  
   508  	defer f.Close()
   509  	defer os.Remove(f.Name())
   510  
   511  	f2, err := sftp.Create(f.Name())
   512  	require.True(t, os.IsPermission(err))
   513  	if err == nil {
   514  		f2.Close()
   515  	}
   516  }
   517  
   518  func TestClientFileName(t *testing.T) {
   519  	sftp, cmd := testClient(t, READONLY, NODELAY)
   520  	defer cmd.Wait()
   521  	defer sftp.Close()
   522  
   523  	f, err := ioutil.TempFile("", "sftptest-filename")
   524  	if err != nil {
   525  		t.Fatal(err)
   526  	}
   527  	defer os.Remove(f.Name())
   528  
   529  	f2, err := sftp.Open(f.Name())
   530  	if err != nil {
   531  		t.Fatal(err)
   532  	}
   533  	defer f2.Close()
   534  
   535  	if got, want := f2.Name(), f.Name(); got != want {
   536  		t.Fatalf("Name: got %q want %q", want, got)
   537  	}
   538  }
   539  
   540  func TestClientFileStat(t *testing.T) {
   541  	sftp, cmd := testClient(t, READONLY, NODELAY)
   542  	defer cmd.Wait()
   543  	defer sftp.Close()
   544  
   545  	f, err := ioutil.TempFile("", "sftptest-filestat")
   546  	if err != nil {
   547  		t.Fatal(err)
   548  	}
   549  	defer os.Remove(f.Name())
   550  
   551  	want, err := os.Lstat(f.Name())
   552  	if err != nil {
   553  		t.Fatal(err)
   554  	}
   555  
   556  	f2, err := sftp.Open(f.Name())
   557  	if err != nil {
   558  		t.Fatal(err)
   559  	}
   560  	defer f2.Close()
   561  
   562  	got, err := f2.Stat()
   563  	if err != nil {
   564  		t.Fatal(err)
   565  	}
   566  
   567  	if !sameFile(want, got) {
   568  		t.Fatalf("Lstat(%q): want %#v, got %#v", f.Name(), want, got)
   569  	}
   570  }
   571  
   572  func TestClientStatLink(t *testing.T) {
   573  	skipIfWindows(t) // Windows does not support links.
   574  
   575  	sftp, cmd := testClient(t, READONLY, NODELAY)
   576  	defer cmd.Wait()
   577  	defer sftp.Close()
   578  
   579  	f, err := ioutil.TempFile("", "sftptest-statlink")
   580  	if err != nil {
   581  		t.Fatal(err)
   582  	}
   583  	defer os.Remove(f.Name())
   584  
   585  	realName := f.Name()
   586  	linkName := f.Name() + ".softlink"
   587  
   588  	// create a symlink that points at sftptest
   589  	if err := os.Symlink(realName, linkName); err != nil {
   590  		t.Fatal(err)
   591  	}
   592  	defer os.Remove(linkName)
   593  
   594  	// compare Lstat of links
   595  	wantLstat, err := os.Lstat(linkName)
   596  	if err != nil {
   597  		t.Fatal(err)
   598  	}
   599  	wantStat, err := os.Stat(linkName)
   600  	if err != nil {
   601  		t.Fatal(err)
   602  	}
   603  
   604  	gotLstat, err := sftp.Lstat(linkName)
   605  	if err != nil {
   606  		t.Fatal(err)
   607  	}
   608  	gotStat, err := sftp.Stat(linkName)
   609  	if err != nil {
   610  		t.Fatal(err)
   611  	}
   612  
   613  	// check that stat is not lstat from os package
   614  	if sameFile(wantLstat, wantStat) {
   615  		t.Fatalf("Lstat / Stat(%q): both %#v %#v", f.Name(), wantLstat, wantStat)
   616  	}
   617  
   618  	// compare Lstat of links
   619  	if !sameFile(wantLstat, gotLstat) {
   620  		t.Fatalf("Lstat(%q): want %#v, got %#v", f.Name(), wantLstat, gotLstat)
   621  	}
   622  
   623  	// compare Stat of links
   624  	if !sameFile(wantStat, gotStat) {
   625  		t.Fatalf("Stat(%q): want %#v, got %#v", f.Name(), wantStat, gotStat)
   626  	}
   627  
   628  	// check that stat is not lstat
   629  	if sameFile(gotLstat, gotStat) {
   630  		t.Fatalf("Lstat / Stat(%q): both %#v %#v", f.Name(), gotLstat, gotStat)
   631  	}
   632  }
   633  
   634  func TestClientRemove(t *testing.T) {
   635  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   636  	defer cmd.Wait()
   637  	defer sftp.Close()
   638  
   639  	f, err := ioutil.TempFile("", "sftptest-remove")
   640  	if err != nil {
   641  		t.Fatal(err)
   642  	}
   643  	defer os.Remove(f.Name())
   644  	f.Close()
   645  
   646  	if err := sftp.Remove(f.Name()); err != nil {
   647  		t.Fatal(err)
   648  	}
   649  	if _, err := os.Lstat(f.Name()); !os.IsNotExist(err) {
   650  		t.Fatal(err)
   651  	}
   652  }
   653  
   654  func TestClientRemoveAll(t *testing.T) {
   655  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   656  	defer cmd.Wait()
   657  	defer sftp.Close()
   658  
   659  	// Create a temporary directory for testing
   660  	tempDir, err := ioutil.TempDir("", "sftptest-removeAll")
   661  	if err != nil {
   662  		t.Fatal(err)
   663  	}
   664  	defer os.RemoveAll(tempDir)
   665  
   666  	// Create a directory tree
   667  	dir1, err := ioutil.TempDir(tempDir, "foo")
   668  	if err != nil {
   669  		t.Fatal(err)
   670  	}
   671  	dir2, err := ioutil.TempDir(dir1, "bar")
   672  	if err != nil {
   673  		t.Fatal(err)
   674  	}
   675  
   676  	// Create some files within the directory tree
   677  	file1 := tempDir + "/file1.txt"
   678  	file2 := dir1 + "/file2.txt"
   679  	file3 := dir2 + "/file3.txt"
   680  	err = ioutil.WriteFile(file1, []byte("File 1"), 0644)
   681  	if err != nil {
   682  		t.Fatalf("Failed to create file: %v", err)
   683  	}
   684  	err = ioutil.WriteFile(file2, []byte("File 2"), 0644)
   685  	if err != nil {
   686  		t.Fatalf("Failed to create file: %v", err)
   687  	}
   688  	err = ioutil.WriteFile(file3, []byte("File 3"), 0644)
   689  	if err != nil {
   690  		t.Fatalf("Failed to create file: %v", err)
   691  	}
   692  
   693  	// Call the function to delete the files recursively
   694  	err = sftp.RemoveAll(tempDir)
   695  	if err != nil {
   696  		t.Fatalf("Failed to delete files recursively: %v", err)
   697  	}
   698  
   699  	// Check if the directories and files have been deleted
   700  	if _, err := os.Stat(dir1); !os.IsNotExist(err) {
   701  		t.Errorf("Directory %s still exists", dir1)
   702  	}
   703  	if _, err := os.Stat(dir2); !os.IsNotExist(err) {
   704  		t.Errorf("Directory %s still exists", dir2)
   705  	}
   706  	if _, err := os.Stat(file1); !os.IsNotExist(err) {
   707  		t.Errorf("File %s still exists", file1)
   708  	}
   709  	if _, err := os.Stat(file2); !os.IsNotExist(err) {
   710  		t.Errorf("File %s still exists", file2)
   711  	}
   712  	if _, err := os.Stat(file3); !os.IsNotExist(err) {
   713  		t.Errorf("File %s still exists", file3)
   714  	}
   715  }
   716  
   717  func TestClientRemoveDir(t *testing.T) {
   718  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   719  	defer cmd.Wait()
   720  	defer sftp.Close()
   721  
   722  	dir, err := ioutil.TempDir("", "sftptest-removedir")
   723  	if err != nil {
   724  		t.Fatal(err)
   725  	}
   726  	defer os.RemoveAll(dir)
   727  
   728  	if err := sftp.Remove(dir); err != nil {
   729  		t.Fatal(err)
   730  	}
   731  	if _, err := os.Lstat(dir); !os.IsNotExist(err) {
   732  		t.Fatal(err)
   733  	}
   734  }
   735  
   736  func TestClientRemoveFailed(t *testing.T) {
   737  	sftp, cmd := testClient(t, READONLY, NODELAY)
   738  	defer cmd.Wait()
   739  	defer sftp.Close()
   740  
   741  	f, err := ioutil.TempFile("", "sftptest-removefailed")
   742  	if err != nil {
   743  		t.Fatal(err)
   744  	}
   745  	defer os.Remove(f.Name())
   746  
   747  	if err := sftp.Remove(f.Name()); err == nil {
   748  		t.Fatalf("Remove(%v): want: permission denied, got %v", f.Name(), err)
   749  	}
   750  	if _, err := os.Lstat(f.Name()); err != nil {
   751  		t.Fatal(err)
   752  	}
   753  }
   754  
   755  func TestClientRename(t *testing.T) {
   756  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   757  	defer cmd.Wait()
   758  	defer sftp.Close()
   759  
   760  	dir, err := ioutil.TempDir("", "sftptest-rename")
   761  	require.NoError(t, err)
   762  	defer os.RemoveAll(dir)
   763  	f, err := os.Create(filepath.Join(dir, "old"))
   764  	require.NoError(t, err)
   765  	f.Close()
   766  
   767  	f2 := filepath.Join(dir, "new")
   768  	if err := sftp.Rename(f.Name(), f2); err != nil {
   769  		t.Fatal(err)
   770  	}
   771  	if _, err := os.Lstat(f.Name()); !os.IsNotExist(err) {
   772  		t.Fatal(err)
   773  	}
   774  	if _, err := os.Lstat(f2); err != nil {
   775  		t.Fatal(err)
   776  	}
   777  }
   778  
   779  func TestClientPosixRename(t *testing.T) {
   780  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   781  	defer cmd.Wait()
   782  	defer sftp.Close()
   783  
   784  	dir, err := ioutil.TempDir("", "sftptest-posixrename")
   785  	require.NoError(t, err)
   786  	defer os.RemoveAll(dir)
   787  	f, err := os.Create(filepath.Join(dir, "old"))
   788  	require.NoError(t, err)
   789  	f.Close()
   790  
   791  	f2 := filepath.Join(dir, "new")
   792  	if err := sftp.PosixRename(f.Name(), f2); err != nil {
   793  		t.Fatal(err)
   794  	}
   795  	if _, err := os.Lstat(f.Name()); !os.IsNotExist(err) {
   796  		t.Fatal(err)
   797  	}
   798  	if _, err := os.Lstat(f2); err != nil {
   799  		t.Fatal(err)
   800  	}
   801  }
   802  
   803  func TestClientGetwd(t *testing.T) {
   804  	sftp, cmd := testClient(t, READONLY, NODELAY)
   805  	defer cmd.Wait()
   806  	defer sftp.Close()
   807  
   808  	lwd, err := os.Getwd()
   809  	if err != nil {
   810  		t.Fatal(err)
   811  	}
   812  	rwd, err := sftp.Getwd()
   813  	if err != nil {
   814  		t.Fatal(err)
   815  	}
   816  	if !filepath.IsAbs(rwd) {
   817  		t.Fatalf("Getwd: wanted absolute path, got %q", rwd)
   818  	}
   819  	if filepath.ToSlash(lwd) != filepath.ToSlash(rwd) {
   820  		t.Fatalf("Getwd: want %q, got %q", lwd, rwd)
   821  	}
   822  }
   823  
   824  func TestClientReadLink(t *testing.T) {
   825  	if runtime.GOOS == "windows" && *testServerImpl {
   826  		// os.Symlink requires privilege escalation.
   827  		t.Skip()
   828  	}
   829  
   830  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   831  	defer cmd.Wait()
   832  	defer sftp.Close()
   833  
   834  	dir, err := ioutil.TempDir("", "sftptest-readlink")
   835  	require.NoError(t, err)
   836  	defer os.RemoveAll(dir)
   837  	f, err := os.Create(filepath.Join(dir, "file"))
   838  	require.NoError(t, err)
   839  	f.Close()
   840  
   841  	f2 := filepath.Join(dir, "symlink")
   842  	if err := os.Symlink(f.Name(), f2); err != nil {
   843  		t.Fatal(err)
   844  	}
   845  	if rl, err := sftp.ReadLink(f2); err != nil {
   846  		t.Fatal(err)
   847  	} else if rl != f.Name() {
   848  		t.Fatalf("unexpected link target: %v, not %v", rl, f.Name())
   849  	}
   850  }
   851  
   852  func TestClientLink(t *testing.T) {
   853  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   854  	defer cmd.Wait()
   855  	defer sftp.Close()
   856  
   857  	dir, err := ioutil.TempDir("", "sftptest-link")
   858  	require.NoError(t, err)
   859  	defer os.RemoveAll(dir)
   860  
   861  	f, err := os.Create(filepath.Join(dir, "file"))
   862  	require.NoError(t, err)
   863  	data := []byte("linktest")
   864  	_, err = f.Write(data)
   865  	f.Close()
   866  	if err != nil {
   867  		t.Fatal(err)
   868  	}
   869  
   870  	f2 := filepath.Join(dir, "link")
   871  	if err := sftp.Link(f.Name(), f2); err != nil {
   872  		t.Fatal(err)
   873  	}
   874  	if st2, err := sftp.Stat(f2); err != nil {
   875  		t.Fatal(err)
   876  	} else if int(st2.Size()) != len(data) {
   877  		t.Fatalf("unexpected link size: %v, not %v", st2.Size(), len(data))
   878  	}
   879  }
   880  
   881  func TestClientSymlink(t *testing.T) {
   882  	if runtime.GOOS == "windows" && *testServerImpl {
   883  		// os.Symlink requires privilege escalation.
   884  		t.Skip()
   885  	}
   886  
   887  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   888  	defer cmd.Wait()
   889  	defer sftp.Close()
   890  
   891  	dir, err := ioutil.TempDir("", "sftptest-symlink")
   892  	require.NoError(t, err)
   893  	defer os.RemoveAll(dir)
   894  	f, err := os.Create(filepath.Join(dir, "file"))
   895  	require.NoError(t, err)
   896  	f.Close()
   897  
   898  	f2 := filepath.Join(dir, "symlink")
   899  	if err := sftp.Symlink(f.Name(), f2); err != nil {
   900  		t.Fatal(err)
   901  	}
   902  	if rl, err := sftp.ReadLink(f2); err != nil {
   903  		t.Fatal(err)
   904  	} else if rl != f.Name() {
   905  		t.Fatalf("unexpected link target: %v, not %v", rl, f.Name())
   906  	}
   907  }
   908  
   909  func TestClientChmod(t *testing.T) {
   910  	skipIfWindows(t) // No UNIX permissions.
   911  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   912  	defer cmd.Wait()
   913  	defer sftp.Close()
   914  
   915  	f, err := ioutil.TempFile("", "sftptest-chmod")
   916  	if err != nil {
   917  		t.Fatal(err)
   918  	}
   919  	defer os.Remove(f.Name())
   920  	f.Close()
   921  
   922  	if err := sftp.Chmod(f.Name(), 0531); err != nil {
   923  		t.Fatal(err)
   924  	}
   925  	if stat, err := os.Stat(f.Name()); err != nil {
   926  		t.Fatal(err)
   927  	} else if stat.Mode()&os.ModePerm != 0531 {
   928  		t.Fatalf("invalid perm %o\n", stat.Mode())
   929  	}
   930  
   931  	sf, err := sftp.Open(f.Name())
   932  	require.NoError(t, err)
   933  	require.NoError(t, sf.Chmod(0500))
   934  	sf.Close()
   935  
   936  	stat, err := os.Stat(f.Name())
   937  	require.NoError(t, err)
   938  	require.EqualValues(t, 0500, stat.Mode())
   939  }
   940  
   941  func TestClientChmodReadonly(t *testing.T) {
   942  	skipIfWindows(t) // No UNIX permissions.
   943  	sftp, cmd := testClient(t, READONLY, NODELAY)
   944  	defer cmd.Wait()
   945  	defer sftp.Close()
   946  
   947  	f, err := ioutil.TempFile("", "sftptest-chmodreadonly")
   948  	if err != nil {
   949  		t.Fatal(err)
   950  	}
   951  	defer os.Remove(f.Name())
   952  	f.Close()
   953  
   954  	if err := sftp.Chmod(f.Name(), 0531); err == nil {
   955  		t.Fatal("expected error")
   956  	}
   957  }
   958  
   959  func TestClientSetuid(t *testing.T) {
   960  	skipIfWindows(t) // No UNIX permissions.
   961  	if *testServerImpl {
   962  		t.Skipf("skipping with -testserver")
   963  	}
   964  
   965  	sftp, cmd := testClient(t, READWRITE, NODELAY)
   966  	defer cmd.Wait()
   967  	defer sftp.Close()
   968  
   969  	f, err := ioutil.TempFile("", "sftptest-setuid")
   970  	if err != nil {
   971  		t.Fatal(err)
   972  	}
   973  	defer os.Remove(f.Name())
   974  	f.Close()
   975  
   976  	const allPerm = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky |
   977  		s_ISUID | s_ISGID | s_ISVTX
   978  
   979  	for _, c := range []struct {
   980  		goPerm    os.FileMode
   981  		posixPerm uint32
   982  	}{
   983  		{os.ModeSetuid, s_ISUID},
   984  		{os.ModeSetgid, s_ISGID},
   985  		{os.ModeSticky, s_ISVTX},
   986  		{os.ModeSetuid | os.ModeSticky, s_ISUID | s_ISVTX},
   987  	} {
   988  		goPerm := 0700 | c.goPerm
   989  		posixPerm := 0700 | c.posixPerm
   990  
   991  		err = sftp.Chmod(f.Name(), goPerm)
   992  		require.NoError(t, err)
   993  
   994  		info, err := sftp.Stat(f.Name())
   995  		require.NoError(t, err)
   996  		require.Equal(t, goPerm, info.Mode()&allPerm)
   997  
   998  		err = sftp.Chmod(f.Name(), 0700) // Reset funny bits.
   999  		require.NoError(t, err)
  1000  
  1001  		// For historical reasons, we also support literal POSIX mode bits in
  1002  		// Chmod. Stat should still translate these to Go os.FileMode bits.
  1003  		err = sftp.Chmod(f.Name(), os.FileMode(posixPerm))
  1004  		require.NoError(t, err)
  1005  
  1006  		info, err = sftp.Stat(f.Name())
  1007  		require.NoError(t, err)
  1008  		require.Equal(t, goPerm, info.Mode()&allPerm)
  1009  	}
  1010  }
  1011  
  1012  func TestClientChown(t *testing.T) {
  1013  	skipIfWindows(t) // No UNIX permissions.
  1014  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  1015  	defer cmd.Wait()
  1016  	defer sftp.Close()
  1017  
  1018  	usr, err := user.Current()
  1019  	if err != nil {
  1020  		t.Fatal(err)
  1021  	}
  1022  	if usr.Uid != "0" {
  1023  		t.Log("must be root to run chown tests")
  1024  		t.Skip()
  1025  	}
  1026  
  1027  	chownto, err := user.Lookup("daemon") // seems common-ish...
  1028  	if err != nil {
  1029  		t.Fatal(err)
  1030  	}
  1031  	toUID, err := strconv.Atoi(chownto.Uid)
  1032  	if err != nil {
  1033  		t.Fatal(err)
  1034  	}
  1035  	toGID, err := strconv.Atoi(chownto.Gid)
  1036  	if err != nil {
  1037  		t.Fatal(err)
  1038  	}
  1039  
  1040  	f, err := ioutil.TempFile("", "sftptest-chown")
  1041  	if err != nil {
  1042  		t.Fatal(err)
  1043  	}
  1044  	defer os.Remove(f.Name())
  1045  	f.Close()
  1046  
  1047  	before, err := exec.Command("ls", "-nl", f.Name()).Output()
  1048  	if err != nil {
  1049  		t.Fatal(err)
  1050  	}
  1051  	if err := sftp.Chown(f.Name(), toUID, toGID); err != nil {
  1052  		t.Fatal(err)
  1053  	}
  1054  	after, err := exec.Command("ls", "-nl", f.Name()).Output()
  1055  	if err != nil {
  1056  		t.Fatal(err)
  1057  	}
  1058  
  1059  	spaceRegex := regexp.MustCompile(`\s+`)
  1060  
  1061  	beforeWords := spaceRegex.Split(string(before), -1)
  1062  	if beforeWords[2] != "0" {
  1063  		t.Fatalf("bad previous user? should be root")
  1064  	}
  1065  	afterWords := spaceRegex.Split(string(after), -1)
  1066  	if afterWords[2] != chownto.Uid || afterWords[3] != chownto.Gid {
  1067  		t.Fatalf("bad chown: %#v", afterWords)
  1068  	}
  1069  	t.Logf("before: %v", string(before))
  1070  	t.Logf(" after: %v", string(after))
  1071  }
  1072  
  1073  func TestClientChownReadonly(t *testing.T) {
  1074  	skipIfWindows(t) // No UNIX permissions.
  1075  	sftp, cmd := testClient(t, READONLY, NODELAY)
  1076  	defer cmd.Wait()
  1077  	defer sftp.Close()
  1078  
  1079  	usr, err := user.Current()
  1080  	if err != nil {
  1081  		t.Fatal(err)
  1082  	}
  1083  	if usr.Uid != "0" {
  1084  		t.Log("must be root to run chown tests")
  1085  		t.Skip()
  1086  	}
  1087  
  1088  	chownto, err := user.Lookup("daemon") // seems common-ish...
  1089  	if err != nil {
  1090  		t.Fatal(err)
  1091  	}
  1092  	toUID, err := strconv.Atoi(chownto.Uid)
  1093  	if err != nil {
  1094  		t.Fatal(err)
  1095  	}
  1096  	toGID, err := strconv.Atoi(chownto.Gid)
  1097  	if err != nil {
  1098  		t.Fatal(err)
  1099  	}
  1100  
  1101  	f, err := ioutil.TempFile("", "sftptest-chownreadonly")
  1102  	if err != nil {
  1103  		t.Fatal(err)
  1104  	}
  1105  	defer os.Remove(f.Name())
  1106  	f.Close()
  1107  
  1108  	if err := sftp.Chown(f.Name(), toUID, toGID); err == nil {
  1109  		t.Fatal("expected error")
  1110  	}
  1111  }
  1112  
  1113  func TestClientChtimes(t *testing.T) {
  1114  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  1115  	defer cmd.Wait()
  1116  	defer sftp.Close()
  1117  
  1118  	f, err := ioutil.TempFile("", "sftptest-chtimes")
  1119  	if err != nil {
  1120  		t.Fatal(err)
  1121  	}
  1122  	defer os.Remove(f.Name())
  1123  	f.Close()
  1124  
  1125  	atime := time.Date(2013, 2, 23, 13, 24, 35, 0, time.UTC)
  1126  	mtime := time.Date(1985, 6, 12, 6, 6, 6, 0, time.UTC)
  1127  	if err := sftp.Chtimes(f.Name(), atime, mtime); err != nil {
  1128  		t.Fatal(err)
  1129  	}
  1130  	if stat, err := os.Stat(f.Name()); err != nil {
  1131  		t.Fatal(err)
  1132  	} else if stat.ModTime().Sub(mtime) != 0 {
  1133  		t.Fatalf("incorrect mtime: %v vs %v", stat.ModTime(), mtime)
  1134  	}
  1135  }
  1136  
  1137  func TestClientChtimesReadonly(t *testing.T) {
  1138  	sftp, cmd := testClient(t, READONLY, NODELAY)
  1139  	defer cmd.Wait()
  1140  	defer sftp.Close()
  1141  
  1142  	f, err := ioutil.TempFile("", "sftptest-chtimesreadonly")
  1143  	if err != nil {
  1144  		t.Fatal(err)
  1145  	}
  1146  	defer os.Remove(f.Name())
  1147  	f.Close()
  1148  
  1149  	atime := time.Date(2013, 2, 23, 13, 24, 35, 0, time.UTC)
  1150  	mtime := time.Date(1985, 6, 12, 6, 6, 6, 0, time.UTC)
  1151  	if err := sftp.Chtimes(f.Name(), atime, mtime); err == nil {
  1152  		t.Fatal("expected error")
  1153  	}
  1154  }
  1155  
  1156  func TestClientTruncate(t *testing.T) {
  1157  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  1158  	defer cmd.Wait()
  1159  	defer sftp.Close()
  1160  
  1161  	f, err := ioutil.TempFile("", "sftptest-truncate")
  1162  	if err != nil {
  1163  		t.Fatal(err)
  1164  	}
  1165  	defer os.Remove(f.Name())
  1166  	fname := f.Name()
  1167  
  1168  	if n, err := f.Write([]byte("hello world")); n != 11 || err != nil {
  1169  		t.Fatal(err)
  1170  	}
  1171  	f.Close()
  1172  
  1173  	if err := sftp.Truncate(fname, 5); err != nil {
  1174  		t.Fatal(err)
  1175  	}
  1176  	if stat, err := os.Stat(fname); err != nil {
  1177  		t.Fatal(err)
  1178  	} else if stat.Size() != 5 {
  1179  		t.Fatalf("unexpected size: %d", stat.Size())
  1180  	}
  1181  }
  1182  
  1183  func TestClientTruncateReadonly(t *testing.T) {
  1184  	sftp, cmd := testClient(t, READONLY, NODELAY)
  1185  	defer cmd.Wait()
  1186  	defer sftp.Close()
  1187  
  1188  	f, err := ioutil.TempFile("", "sftptest-truncreadonly")
  1189  	if err != nil {
  1190  		t.Fatal(err)
  1191  	}
  1192  	defer os.Remove(f.Name())
  1193  	fname := f.Name()
  1194  
  1195  	if n, err := f.Write([]byte("hello world")); n != 11 || err != nil {
  1196  		t.Fatal(err)
  1197  	}
  1198  	f.Close()
  1199  
  1200  	if err := sftp.Truncate(fname, 5); err == nil {
  1201  		t.Fatal("expected error")
  1202  	}
  1203  	if stat, err := os.Stat(fname); err != nil {
  1204  		t.Fatal(err)
  1205  	} else if stat.Size() != 11 {
  1206  		t.Fatalf("unexpected size: %d", stat.Size())
  1207  	}
  1208  }
  1209  
  1210  func sameFile(want, got os.FileInfo) bool {
  1211  	_, wantName := filepath.Split(want.Name())
  1212  	_, gotName := filepath.Split(got.Name())
  1213  	return wantName == gotName &&
  1214  		want.Size() == got.Size()
  1215  }
  1216  
  1217  func TestClientReadSimple(t *testing.T) {
  1218  	sftp, cmd := testClient(t, READONLY, NODELAY)
  1219  	defer cmd.Wait()
  1220  	defer sftp.Close()
  1221  
  1222  	d, err := ioutil.TempDir("", "sftptest-readsimple")
  1223  	if err != nil {
  1224  		t.Fatal(err)
  1225  	}
  1226  	defer os.RemoveAll(d)
  1227  
  1228  	f, err := ioutil.TempFile(d, "read-test")
  1229  	if err != nil {
  1230  		t.Fatal(err)
  1231  	}
  1232  	fname := f.Name()
  1233  	f.Write([]byte("hello"))
  1234  	f.Close()
  1235  
  1236  	f2, err := sftp.Open(fname)
  1237  	if err != nil {
  1238  		t.Fatal(err)
  1239  	}
  1240  	defer f2.Close()
  1241  	stuff := make([]byte, 32)
  1242  	n, err := f2.Read(stuff)
  1243  	if err != nil && err != io.EOF {
  1244  		t.Fatalf("err: %v", err)
  1245  	}
  1246  	if n != 5 {
  1247  		t.Fatalf("n should be 5, is %v", n)
  1248  	}
  1249  	if string(stuff[0:5]) != "hello" {
  1250  		t.Fatalf("invalid contents")
  1251  	}
  1252  }
  1253  
  1254  func TestClientReadSequential(t *testing.T) {
  1255  	sftp, cmd := testClient(t, READONLY, NODELAY)
  1256  	defer cmd.Wait()
  1257  	defer sftp.Close()
  1258  
  1259  	sftp.disableConcurrentReads = true
  1260  	d, err := ioutil.TempDir("", "sftptest-readsequential")
  1261  	require.NoError(t, err)
  1262  
  1263  	defer os.RemoveAll(d)
  1264  
  1265  	f, err := ioutil.TempFile(d, "read-sequential-test")
  1266  	require.NoError(t, err)
  1267  	fname := f.Name()
  1268  	content := []byte("hello world")
  1269  	f.Write(content)
  1270  	f.Close()
  1271  
  1272  	for _, maxPktSize := range []int{1, 2, 3, 4} {
  1273  		sftp.maxPacket = maxPktSize
  1274  
  1275  		sftpFile, err := sftp.Open(fname)
  1276  		require.NoError(t, err)
  1277  
  1278  		stuff := make([]byte, 32)
  1279  		n, err := sftpFile.Read(stuff)
  1280  		require.ErrorIs(t, err, io.EOF)
  1281  		require.Equal(t, len(content), n)
  1282  		require.Equal(t, content, stuff[0:len(content)])
  1283  
  1284  		err = sftpFile.Close()
  1285  		require.NoError(t, err)
  1286  
  1287  		sftpFile, err = sftp.Open(fname)
  1288  		require.NoError(t, err)
  1289  
  1290  		stuff = make([]byte, 5)
  1291  		n, err = sftpFile.Read(stuff)
  1292  		require.NoError(t, err)
  1293  		require.Equal(t, len(stuff), n)
  1294  		require.Equal(t, content[:len(stuff)], stuff)
  1295  
  1296  		err = sftpFile.Close()
  1297  		require.NoError(t, err)
  1298  
  1299  		// now read from a offset
  1300  		off := int64(3)
  1301  		sftpFile, err = sftp.Open(fname)
  1302  		require.NoError(t, err)
  1303  
  1304  		stuff = make([]byte, 5)
  1305  		n, err = sftpFile.ReadAt(stuff, off)
  1306  		require.NoError(t, err)
  1307  		require.Equal(t, len(stuff), n)
  1308  		require.Equal(t, content[off:off+int64(len(stuff))], stuff)
  1309  
  1310  		err = sftpFile.Close()
  1311  		require.NoError(t, err)
  1312  	}
  1313  }
  1314  
  1315  // this writer requires maxPacket = 3 and always returns an error for the second write call
  1316  type lastChunkErrSequentialWriter struct {
  1317  	counter int
  1318  }
  1319  
  1320  func (w *lastChunkErrSequentialWriter) Write(b []byte) (int, error) {
  1321  	w.counter++
  1322  	if w.counter == 1 {
  1323  		if len(b) != 3 {
  1324  			return 0, errors.New("this writer requires maxPacket = 3, please set MaxPacketChecked(3)")
  1325  		}
  1326  		return len(b), nil
  1327  	}
  1328  	return 1, errors.New("this writer fails after the first write")
  1329  }
  1330  
  1331  func TestClientWriteSequentialWriterErr(t *testing.T) {
  1332  	client, cmd := testClient(t, READONLY, NODELAY, MaxPacketChecked(3))
  1333  	defer cmd.Wait()
  1334  	defer client.Close()
  1335  
  1336  	d, err := ioutil.TempDir("", "sftptest-writesequential-writeerr")
  1337  	require.NoError(t, err)
  1338  
  1339  	defer os.RemoveAll(d)
  1340  
  1341  	f, err := ioutil.TempFile(d, "write-sequential-writeerr-test")
  1342  	require.NoError(t, err)
  1343  	fname := f.Name()
  1344  	_, err = f.Write([]byte("12345"))
  1345  	require.NoError(t, err)
  1346  	require.NoError(t, f.Close())
  1347  
  1348  	sftpFile, err := client.Open(fname)
  1349  	require.NoError(t, err)
  1350  	defer sftpFile.Close()
  1351  
  1352  	w := &lastChunkErrSequentialWriter{}
  1353  	written, err := sftpFile.writeToSequential(w)
  1354  	assert.Error(t, err)
  1355  	expected := int64(4)
  1356  	if written != expected {
  1357  		t.Errorf("sftpFile.Write() = %d, but expected %d", written, expected)
  1358  	}
  1359  	assert.Equal(t, 2, w.counter)
  1360  }
  1361  
  1362  func TestClientReadDir(t *testing.T) {
  1363  	sftp1, cmd1 := testClient(t, READONLY, NODELAY)
  1364  	sftp2, cmd2 := testClientGoSvr(t, READONLY, NODELAY)
  1365  	defer cmd1.Wait()
  1366  	defer cmd2.Wait()
  1367  	defer sftp1.Close()
  1368  	defer sftp2.Close()
  1369  
  1370  	dir := os.TempDir()
  1371  
  1372  	d, err := os.Open(dir)
  1373  	if err != nil {
  1374  		t.Fatal(err)
  1375  	}
  1376  	defer d.Close()
  1377  	osfiles, err := d.Readdir(4096)
  1378  	if err != nil {
  1379  		t.Fatal(err)
  1380  	}
  1381  
  1382  	sftp1Files, err := sftp1.ReadDir(dir)
  1383  	if err != nil {
  1384  		t.Fatal(err)
  1385  	}
  1386  	sftp2Files, err := sftp2.ReadDir(dir)
  1387  	if err != nil {
  1388  		t.Fatal(err)
  1389  	}
  1390  
  1391  	osFilesByName := map[string]os.FileInfo{}
  1392  	for _, f := range osfiles {
  1393  		osFilesByName[f.Name()] = f
  1394  	}
  1395  	sftp1FilesByName := map[string]os.FileInfo{}
  1396  	for _, f := range sftp1Files {
  1397  		sftp1FilesByName[f.Name()] = f
  1398  	}
  1399  	sftp2FilesByName := map[string]os.FileInfo{}
  1400  	for _, f := range sftp2Files {
  1401  		sftp2FilesByName[f.Name()] = f
  1402  	}
  1403  
  1404  	if len(osFilesByName) != len(sftp1FilesByName) || len(sftp1FilesByName) != len(sftp2FilesByName) {
  1405  		t.Fatalf("os gives %v, sftp1 gives %v, sftp2 gives %v", len(osFilesByName), len(sftp1FilesByName), len(sftp2FilesByName))
  1406  	}
  1407  
  1408  	for name, osF := range osFilesByName {
  1409  		sftp1F, ok := sftp1FilesByName[name]
  1410  		if !ok {
  1411  			t.Fatalf("%v present in os but not sftp1", name)
  1412  		}
  1413  		sftp2F, ok := sftp2FilesByName[name]
  1414  		if !ok {
  1415  			t.Fatalf("%v present in os but not sftp2", name)
  1416  		}
  1417  
  1418  		//t.Logf("%v: %v %v %v", name, osF, sftp1F, sftp2F)
  1419  		if osF.Size() != sftp1F.Size() || sftp1F.Size() != sftp2F.Size() {
  1420  			t.Fatalf("size %v %v %v", osF.Size(), sftp1F.Size(), sftp2F.Size())
  1421  		}
  1422  		if osF.IsDir() != sftp1F.IsDir() || sftp1F.IsDir() != sftp2F.IsDir() {
  1423  			t.Fatalf("isdir %v %v %v", osF.IsDir(), sftp1F.IsDir(), sftp2F.IsDir())
  1424  		}
  1425  		if osF.ModTime().Sub(sftp1F.ModTime()) > time.Second || sftp1F.ModTime() != sftp2F.ModTime() {
  1426  			t.Fatalf("modtime %v %v %v", osF.ModTime(), sftp1F.ModTime(), sftp2F.ModTime())
  1427  		}
  1428  		if osF.Mode() != sftp1F.Mode() || sftp1F.Mode() != sftp2F.Mode() {
  1429  			t.Fatalf("mode %x %x %x", osF.Mode(), sftp1F.Mode(), sftp2F.Mode())
  1430  		}
  1431  	}
  1432  }
  1433  
  1434  var clientReadTests = []struct {
  1435  	n int64
  1436  }{
  1437  	{0},
  1438  	{1},
  1439  	{1000},
  1440  	{1024},
  1441  	{1025},
  1442  	{2048},
  1443  	{4096},
  1444  	{1 << 12},
  1445  	{1 << 13},
  1446  	{1 << 14},
  1447  	{1 << 15},
  1448  	{1 << 16},
  1449  	{1 << 17},
  1450  	{1 << 18},
  1451  	{1 << 19},
  1452  	{1 << 20},
  1453  }
  1454  
  1455  func TestClientRead(t *testing.T) {
  1456  	sftp, cmd := testClient(t, READONLY, NODELAY)
  1457  	defer cmd.Wait()
  1458  	defer sftp.Close()
  1459  
  1460  	d, err := ioutil.TempDir("", "sftptest-read")
  1461  	if err != nil {
  1462  		t.Fatal(err)
  1463  	}
  1464  	defer os.RemoveAll(d)
  1465  
  1466  	for _, disableConcurrentReads := range []bool{true, false} {
  1467  		for _, tt := range clientReadTests {
  1468  			f, err := ioutil.TempFile(d, "read-test")
  1469  			if err != nil {
  1470  				t.Fatal(err)
  1471  			}
  1472  			defer f.Close()
  1473  			hash := writeN(t, f, tt.n)
  1474  			sftp.disableConcurrentReads = disableConcurrentReads
  1475  			f2, err := sftp.Open(f.Name())
  1476  			if err != nil {
  1477  				t.Fatal(err)
  1478  			}
  1479  			defer f2.Close()
  1480  			hash2, n := readHash(t, f2)
  1481  			if hash != hash2 || tt.n != n {
  1482  				t.Errorf("Read: hash: want: %q, got %q, read: want: %v, got %v", hash, hash2, tt.n, n)
  1483  			}
  1484  		}
  1485  	}
  1486  }
  1487  
  1488  // readHash reads r until EOF returning the number of bytes read
  1489  // and the hash of the contents.
  1490  func readHash(t *testing.T, r io.Reader) (string, int64) {
  1491  	h := sha1.New()
  1492  	read, err := io.Copy(h, r)
  1493  	if err != nil {
  1494  		t.Fatal(err)
  1495  	}
  1496  	return string(h.Sum(nil)), read
  1497  }
  1498  
  1499  // writeN writes n bytes of random data to w and returns the
  1500  // hash of that data.
  1501  func writeN(t *testing.T, w io.Writer, n int64) string {
  1502  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
  1503  
  1504  	h := sha1.New()
  1505  
  1506  	mw := io.MultiWriter(w, h)
  1507  
  1508  	written, err := io.CopyN(mw, r, n)
  1509  	if err != nil {
  1510  		t.Fatal(err)
  1511  	}
  1512  	if written != n {
  1513  		t.Fatalf("CopyN(%v): wrote: %v", n, written)
  1514  	}
  1515  	return string(h.Sum(nil))
  1516  }
  1517  
  1518  var clientWriteTests = []struct {
  1519  	n     int
  1520  	total int64 // cumulative file size
  1521  }{
  1522  	{0, 0},
  1523  	{1, 1},
  1524  	{0, 1},
  1525  	{999, 1000},
  1526  	{24, 1024},
  1527  	{1023, 2047},
  1528  	{2048, 4095},
  1529  	{1 << 12, 8191},
  1530  	{1 << 13, 16383},
  1531  	{1 << 14, 32767},
  1532  	{1 << 15, 65535},
  1533  	{1 << 16, 131071},
  1534  	{1 << 17, 262143},
  1535  	{1 << 18, 524287},
  1536  	{1 << 19, 1048575},
  1537  	{1 << 20, 2097151},
  1538  	{1 << 21, 4194303},
  1539  }
  1540  
  1541  func TestClientWrite(t *testing.T) {
  1542  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  1543  	defer cmd.Wait()
  1544  	defer sftp.Close()
  1545  
  1546  	d, err := ioutil.TempDir("", "sftptest-write")
  1547  	if err != nil {
  1548  		t.Fatal(err)
  1549  	}
  1550  	defer os.RemoveAll(d)
  1551  
  1552  	f := path.Join(d, "writeTest")
  1553  	w, err := sftp.Create(f)
  1554  	if err != nil {
  1555  		t.Fatal(err)
  1556  	}
  1557  	defer w.Close()
  1558  
  1559  	for _, tt := range clientWriteTests {
  1560  		got, err := w.Write(make([]byte, tt.n))
  1561  		if err != nil {
  1562  			t.Fatal(err)
  1563  		}
  1564  		if got != tt.n {
  1565  			t.Errorf("Write(%v): wrote: want: %v, got %v", tt.n, tt.n, got)
  1566  		}
  1567  		fi, err := os.Stat(f)
  1568  		if err != nil {
  1569  			t.Fatal(err)
  1570  		}
  1571  		if total := fi.Size(); total != tt.total {
  1572  			t.Errorf("Write(%v): size: want: %v, got %v", tt.n, tt.total, total)
  1573  		}
  1574  	}
  1575  }
  1576  
  1577  // ReadFrom is basically Write with io.Reader as the arg
  1578  func TestClientReadFrom(t *testing.T) {
  1579  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  1580  	defer cmd.Wait()
  1581  	defer sftp.Close()
  1582  
  1583  	d, err := ioutil.TempDir("", "sftptest-readfrom")
  1584  	if err != nil {
  1585  		t.Fatal(err)
  1586  	}
  1587  	defer os.RemoveAll(d)
  1588  
  1589  	f := path.Join(d, "writeTest")
  1590  	w, err := sftp.Create(f)
  1591  	if err != nil {
  1592  		t.Fatal(err)
  1593  	}
  1594  	defer w.Close()
  1595  
  1596  	for _, tt := range clientWriteTests {
  1597  		got, err := w.ReadFrom(bytes.NewReader(make([]byte, tt.n)))
  1598  		if err != nil {
  1599  			t.Fatal(err)
  1600  		}
  1601  		if got != int64(tt.n) {
  1602  			t.Errorf("Write(%v): wrote: want: %v, got %v", tt.n, tt.n, got)
  1603  		}
  1604  		fi, err := os.Stat(f)
  1605  		if err != nil {
  1606  			t.Fatal(err)
  1607  		}
  1608  		if total := fi.Size(); total != tt.total {
  1609  			t.Errorf("Write(%v): size: want: %v, got %v", tt.n, tt.total, total)
  1610  		}
  1611  	}
  1612  }
  1613  
  1614  // A sizedReader is a Reader with a completely arbitrary Size.
  1615  type sizedReader struct {
  1616  	io.Reader
  1617  	size int
  1618  }
  1619  
  1620  func (r *sizedReader) Size() int { return r.size }
  1621  
  1622  // Test File.ReadFrom's handling of a Reader's Size:
  1623  // it should be used as a heuristic for determining concurrency only.
  1624  func TestClientReadFromSizeMismatch(t *testing.T) {
  1625  	const (
  1626  		packetSize = 1024
  1627  		filesize   = 4 * packetSize
  1628  	)
  1629  
  1630  	sftp, cmd := testClient(t, READWRITE, NODELAY, MaxPacketChecked(packetSize), UseConcurrentWrites(true))
  1631  	defer cmd.Wait()
  1632  	defer sftp.Close()
  1633  
  1634  	d, err := ioutil.TempDir("", "sftptest-readfrom-size-mismatch")
  1635  	if err != nil {
  1636  		t.Fatal("cannot create temp dir:", err)
  1637  	}
  1638  	defer os.RemoveAll(d)
  1639  
  1640  	buf := make([]byte, filesize)
  1641  
  1642  	for i, reportedSize := range []int{
  1643  		-1, filesize - 100, filesize, filesize + 100,
  1644  	} {
  1645  		t.Run(fmt.Sprint(i), func(t *testing.T) {
  1646  			r := &sizedReader{Reader: bytes.NewReader(buf), size: reportedSize}
  1647  
  1648  			f := path.Join(d, fmt.Sprint(i))
  1649  			w, err := sftp.Create(f)
  1650  			if err != nil {
  1651  				t.Fatal("unexpected error:", err)
  1652  			}
  1653  			defer w.Close()
  1654  
  1655  			n, err := w.ReadFrom(r)
  1656  			assert.EqualValues(t, filesize, n)
  1657  
  1658  			fi, err := os.Stat(f)
  1659  			if err != nil {
  1660  				t.Fatal("unexpected error:", err)
  1661  			}
  1662  			assert.EqualValues(t, filesize, fi.Size())
  1663  		})
  1664  	}
  1665  }
  1666  
  1667  // Issue #145 in github
  1668  // Deadlock in ReadFrom when network drops after 1 good packet.
  1669  // Deadlock would occur anytime desiredInFlight-inFlight==2 and 2 errors
  1670  // occurred in a row. The channel to report the errors only had a buffer
  1671  // of 1 and 2 would be sent.
  1672  var errFakeNet = errors.New("Fake network issue")
  1673  
  1674  func TestClientReadFromDeadlock(t *testing.T) {
  1675  	for i := 0; i < 5; i++ {
  1676  		clientWriteDeadlock(t, i, func(f *File) {
  1677  			b := make([]byte, 32768*4)
  1678  			content := bytes.NewReader(b)
  1679  			_, err := f.ReadFrom(content)
  1680  			if !errors.Is(err, errFakeNet) {
  1681  				t.Fatal("Didn't receive correct error:", err)
  1682  			}
  1683  		})
  1684  	}
  1685  }
  1686  
  1687  // Write has exact same problem
  1688  func TestClientWriteDeadlock(t *testing.T) {
  1689  	for i := 0; i < 5; i++ {
  1690  		clientWriteDeadlock(t, i, func(f *File) {
  1691  			b := make([]byte, 32768*4)
  1692  
  1693  			_, err := f.Write(b)
  1694  			if !errors.Is(err, errFakeNet) {
  1695  				t.Fatal("Didn't receive correct error:", err)
  1696  			}
  1697  		})
  1698  	}
  1699  }
  1700  
  1701  type timeBombWriter struct {
  1702  	count int
  1703  	w     io.WriteCloser
  1704  }
  1705  
  1706  func (w *timeBombWriter) Write(b []byte) (int, error) {
  1707  	if w.count < 1 {
  1708  		return 0, errFakeNet
  1709  	}
  1710  
  1711  	w.count--
  1712  	return w.w.Write(b)
  1713  }
  1714  
  1715  func (w *timeBombWriter) Close() error {
  1716  	return w.w.Close()
  1717  }
  1718  
  1719  // shared body for both previous tests
  1720  func clientWriteDeadlock(t *testing.T, N int, badfunc func(*File)) {
  1721  	if !*testServerImpl {
  1722  		t.Skipf("skipping without -testserver")
  1723  	}
  1724  
  1725  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  1726  	defer cmd.Wait()
  1727  	defer sftp.Close()
  1728  
  1729  	d, err := ioutil.TempDir("", "sftptest-writedeadlock")
  1730  	if err != nil {
  1731  		t.Fatal(err)
  1732  	}
  1733  	defer os.RemoveAll(d)
  1734  
  1735  	f := path.Join(d, "writeTest")
  1736  	w, err := sftp.Create(f)
  1737  	if err != nil {
  1738  		t.Fatal(err)
  1739  	}
  1740  	defer w.Close()
  1741  
  1742  	// Override the clienConn Writer with a failing version
  1743  	// Replicates network error/drop part way through (after N good writes)
  1744  	wrap := sftp.clientConn.conn.WriteCloser
  1745  	sftp.clientConn.conn.WriteCloser = &timeBombWriter{
  1746  		count: N,
  1747  		w:     wrap,
  1748  	}
  1749  
  1750  	// this locked (before the fix)
  1751  	badfunc(w)
  1752  }
  1753  
  1754  // Read/WriteTo has this issue as well
  1755  func TestClientReadDeadlock(t *testing.T) {
  1756  	for i := 0; i < 3; i++ {
  1757  		clientReadDeadlock(t, i, func(f *File) {
  1758  			b := make([]byte, 32768*4)
  1759  
  1760  			_, err := f.Read(b)
  1761  			if !errors.Is(err, errFakeNet) {
  1762  				t.Fatal("Didn't receive correct error:", err)
  1763  			}
  1764  		})
  1765  	}
  1766  }
  1767  
  1768  func TestClientWriteToDeadlock(t *testing.T) {
  1769  	for i := 0; i < 3; i++ {
  1770  		clientReadDeadlock(t, i, func(f *File) {
  1771  			b := make([]byte, 32768*4)
  1772  
  1773  			buf := bytes.NewBuffer(b)
  1774  
  1775  			_, err := f.WriteTo(buf)
  1776  			if !errors.Is(err, errFakeNet) {
  1777  				t.Fatal("Didn't receive correct error:", err)
  1778  			}
  1779  		})
  1780  	}
  1781  }
  1782  
  1783  func clientReadDeadlock(t *testing.T, N int, badfunc func(*File)) {
  1784  	if !*testServerImpl {
  1785  		t.Skipf("skipping without -testserver")
  1786  	}
  1787  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  1788  	defer cmd.Wait()
  1789  	defer sftp.Close()
  1790  
  1791  	d, err := ioutil.TempDir("", "sftptest-readdeadlock")
  1792  	if err != nil {
  1793  		t.Fatal(err)
  1794  	}
  1795  	defer os.RemoveAll(d)
  1796  
  1797  	f := path.Join(d, "writeTest")
  1798  
  1799  	w, err := sftp.Create(f)
  1800  	if err != nil {
  1801  		t.Fatal(err)
  1802  	}
  1803  	defer w.Close()
  1804  
  1805  	// write the data for the read tests
  1806  	b := make([]byte, 32768*4)
  1807  	w.Write(b)
  1808  
  1809  	// open new copy of file for read tests
  1810  	r, err := sftp.Open(f)
  1811  	if err != nil {
  1812  		t.Fatal(err)
  1813  	}
  1814  	defer r.Close()
  1815  
  1816  	// Override the clienConn Writer with a failing version
  1817  	// Replicates network error/drop part way through (after N good writes)
  1818  	wrap := sftp.clientConn.conn.WriteCloser
  1819  	sftp.clientConn.conn.WriteCloser = &timeBombWriter{
  1820  		count: N,
  1821  		w:     wrap,
  1822  	}
  1823  
  1824  	// this locked (before the fix)
  1825  	badfunc(r)
  1826  }
  1827  
  1828  func TestClientSyncGo(t *testing.T) {
  1829  	if !*testServerImpl {
  1830  		t.Skipf("skipping without -testserver")
  1831  	}
  1832  	err := testClientSync(t)
  1833  
  1834  	// Since Server does not support the fsync extension, we can only
  1835  	// check that we get the right error.
  1836  	require.Error(t, err)
  1837  
  1838  	switch err := err.(type) {
  1839  	case *StatusError:
  1840  		assert.Equal(t, ErrSSHFxOpUnsupported, err.FxCode())
  1841  	default:
  1842  		t.Error(err)
  1843  	}
  1844  }
  1845  
  1846  func TestClientSyncSFTP(t *testing.T) {
  1847  	if *testServerImpl {
  1848  		t.Skipf("skipping with -testserver")
  1849  	}
  1850  	err := testClientSync(t)
  1851  	assert.NoError(t, err)
  1852  }
  1853  
  1854  func testClientSync(t *testing.T) error {
  1855  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  1856  	defer cmd.Wait()
  1857  	defer sftp.Close()
  1858  
  1859  	d, err := ioutil.TempDir("", "sftptest.sync")
  1860  	require.NoError(t, err)
  1861  	defer os.RemoveAll(d)
  1862  
  1863  	f := path.Join(d, "syncTest")
  1864  	w, err := sftp.Create(f)
  1865  	require.NoError(t, err)
  1866  	defer w.Close()
  1867  
  1868  	return w.Sync()
  1869  }
  1870  
  1871  // taken from github.com/kr/fs/walk_test.go
  1872  
  1873  type Node struct {
  1874  	name    string
  1875  	entries []*Node // nil if the entry is a file
  1876  	mark    int
  1877  }
  1878  
  1879  var tree = &Node{
  1880  	"testdata",
  1881  	[]*Node{
  1882  		{"a", nil, 0},
  1883  		{"b", []*Node{}, 0},
  1884  		{"c", nil, 0},
  1885  		{
  1886  			"d",
  1887  			[]*Node{
  1888  				{"x", nil, 0},
  1889  				{"y", []*Node{}, 0},
  1890  				{
  1891  					"z",
  1892  					[]*Node{
  1893  						{"u", nil, 0},
  1894  						{"v", nil, 0},
  1895  					},
  1896  					0,
  1897  				},
  1898  			},
  1899  			0,
  1900  		},
  1901  	},
  1902  	0,
  1903  }
  1904  
  1905  func walkTree(n *Node, path string, f func(path string, n *Node)) {
  1906  	f(path, n)
  1907  	for _, e := range n.entries {
  1908  		walkTree(e, filepath.Join(path, e.name), f)
  1909  	}
  1910  }
  1911  
  1912  func makeTree(t *testing.T) {
  1913  	walkTree(tree, tree.name, func(path string, n *Node) {
  1914  		if n.entries == nil {
  1915  			fd, err := os.Create(path)
  1916  			if err != nil {
  1917  				t.Errorf("makeTree: %v", err)
  1918  				return
  1919  			}
  1920  			fd.Close()
  1921  		} else {
  1922  			os.Mkdir(path, 0770)
  1923  		}
  1924  	})
  1925  }
  1926  
  1927  func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) }
  1928  
  1929  func checkMarks(t *testing.T, report bool) {
  1930  	walkTree(tree, tree.name, func(path string, n *Node) {
  1931  		if n.mark != 1 && report {
  1932  			t.Errorf("node %s mark = %d; expected 1", path, n.mark)
  1933  		}
  1934  		n.mark = 0
  1935  	})
  1936  }
  1937  
  1938  // Assumes that each node name is unique. Good enough for a test.
  1939  // If clear is true, any incoming error is cleared before return. The errors
  1940  // are always accumulated, though.
  1941  func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error {
  1942  	if err != nil {
  1943  		*errors = append(*errors, err)
  1944  		if clear {
  1945  			return nil
  1946  		}
  1947  		return err
  1948  	}
  1949  	name := info.Name()
  1950  	walkTree(tree, tree.name, func(path string, n *Node) {
  1951  		if n.name == name {
  1952  			n.mark++
  1953  		}
  1954  	})
  1955  	return nil
  1956  }
  1957  
  1958  func TestClientWalk(t *testing.T) {
  1959  	sftp, cmd := testClient(t, READONLY, NODELAY)
  1960  	defer cmd.Wait()
  1961  	defer sftp.Close()
  1962  
  1963  	makeTree(t)
  1964  	errors := make([]error, 0, 10)
  1965  	clear := true
  1966  	markFn := func(walker *fs.Walker) error {
  1967  		for walker.Step() {
  1968  			err := mark(walker.Path(), walker.Stat(), walker.Err(), &errors, clear)
  1969  			if err != nil {
  1970  				return err
  1971  			}
  1972  		}
  1973  		return nil
  1974  	}
  1975  	// Expect no errors.
  1976  	err := markFn(sftp.Walk(tree.name))
  1977  	if err != nil {
  1978  		t.Fatalf("no error expected, found: %s", err)
  1979  	}
  1980  	if len(errors) != 0 {
  1981  		t.Fatalf("unexpected errors: %s", errors)
  1982  	}
  1983  	checkMarks(t, true)
  1984  	errors = errors[0:0]
  1985  
  1986  	// Test permission errors.  Only possible if we're not root
  1987  	// and only on some file systems (AFS, FAT).  To avoid errors during
  1988  	// all.bash on those file systems, skip during go test -short.
  1989  	if os.Getuid() > 0 && !testing.Short() {
  1990  		// introduce 2 errors: chmod top-level directories to 0
  1991  		os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0)
  1992  		os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0)
  1993  
  1994  		// 3) capture errors, expect two.
  1995  		// mark respective subtrees manually
  1996  		markTree(tree.entries[1])
  1997  		markTree(tree.entries[3])
  1998  		// correct double-marking of directory itself
  1999  		tree.entries[1].mark--
  2000  		tree.entries[3].mark--
  2001  		err := markFn(sftp.Walk(tree.name))
  2002  		if err != nil {
  2003  			t.Fatalf("expected no error return from Walk, got %s", err)
  2004  		}
  2005  		if len(errors) != 2 {
  2006  			t.Errorf("expected 2 errors, got %d: %s", len(errors), errors)
  2007  		}
  2008  		// the inaccessible subtrees were marked manually
  2009  		checkMarks(t, true)
  2010  		errors = errors[0:0]
  2011  
  2012  		// 4) capture errors, stop after first error.
  2013  		// mark respective subtrees manually
  2014  		markTree(tree.entries[1])
  2015  		markTree(tree.entries[3])
  2016  		// correct double-marking of directory itself
  2017  		tree.entries[1].mark--
  2018  		tree.entries[3].mark--
  2019  		clear = false // error will stop processing
  2020  		err = markFn(sftp.Walk(tree.name))
  2021  		if err == nil {
  2022  			t.Fatalf("expected error return from Walk")
  2023  		}
  2024  		if len(errors) != 1 {
  2025  			t.Errorf("expected 1 error, got %d: %s", len(errors), errors)
  2026  		}
  2027  		// the inaccessible subtrees were marked manually
  2028  		checkMarks(t, false)
  2029  		errors = errors[0:0]
  2030  
  2031  		// restore permissions
  2032  		os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770)
  2033  		os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770)
  2034  	}
  2035  
  2036  	// cleanup
  2037  	if err := os.RemoveAll(tree.name); err != nil {
  2038  		t.Errorf("removeTree: %v", err)
  2039  	}
  2040  }
  2041  
  2042  type MatchTest struct {
  2043  	pattern, s string
  2044  	match      bool
  2045  	err        error
  2046  }
  2047  
  2048  var matchTests = []MatchTest{
  2049  	{"abc", "abc", true, nil},
  2050  	{"*", "abc", true, nil},
  2051  	{"*c", "abc", true, nil},
  2052  	{"a*", "a", true, nil},
  2053  	{"a*", "abc", true, nil},
  2054  	{"a*", "ab/c", false, nil},
  2055  	{"a*/b", "abc/b", true, nil},
  2056  	{"a*/b", "a/c/b", false, nil},
  2057  	{"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil},
  2058  	{"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil},
  2059  	{"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil},
  2060  	{"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil},
  2061  	{"a*b?c*x", "abxbbxdbxebxczzx", true, nil},
  2062  	{"a*b?c*x", "abxbbxdbxebxczzy", false, nil},
  2063  	{"ab[c]", "abc", true, nil},
  2064  	{"ab[b-d]", "abc", true, nil},
  2065  	{"ab[e-g]", "abc", false, nil},
  2066  	{"ab[^c]", "abc", false, nil},
  2067  	{"ab[^b-d]", "abc", false, nil},
  2068  	{"ab[^e-g]", "abc", true, nil},
  2069  	{"a\\*b", "a*b", true, nil},
  2070  	{"a\\*b", "ab", false, nil},
  2071  	{"a?b", "a☺b", true, nil},
  2072  	{"a[^a]b", "a☺b", true, nil},
  2073  	{"a???b", "a☺b", false, nil},
  2074  	{"a[^a][^a][^a]b", "a☺b", false, nil},
  2075  	{"[a-ζ]*", "α", true, nil},
  2076  	{"*[a-ζ]", "A", false, nil},
  2077  	{"a?b", "a/b", false, nil},
  2078  	{"a*b", "a/b", false, nil},
  2079  	{"[\\]a]", "]", true, nil},
  2080  	{"[\\-]", "-", true, nil},
  2081  	{"[x\\-]", "x", true, nil},
  2082  	{"[x\\-]", "-", true, nil},
  2083  	{"[x\\-]", "z", false, nil},
  2084  	{"[\\-x]", "x", true, nil},
  2085  	{"[\\-x]", "-", true, nil},
  2086  	{"[\\-x]", "a", false, nil},
  2087  	{"[]a]", "]", false, ErrBadPattern},
  2088  	{"[-]", "-", false, ErrBadPattern},
  2089  	{"[x-]", "x", false, ErrBadPattern},
  2090  	{"[x-]", "-", false, ErrBadPattern},
  2091  	{"[x-]", "z", false, ErrBadPattern},
  2092  	{"[-x]", "x", false, ErrBadPattern},
  2093  	{"[-x]", "-", false, ErrBadPattern},
  2094  	{"[-x]", "a", false, ErrBadPattern},
  2095  	{"\\", "a", false, ErrBadPattern},
  2096  	{"[a-b-c]", "a", false, ErrBadPattern},
  2097  	{"[", "a", false, ErrBadPattern},
  2098  	{"[^", "a", false, ErrBadPattern},
  2099  	{"[^bc", "a", false, ErrBadPattern},
  2100  	{"a[", "ab", false, ErrBadPattern},
  2101  	{"*x", "xxx", true, nil},
  2102  
  2103  	// The following test behaves differently on Go 1.15.3 and Go tip as
  2104  	// https://github.com/golang/go/commit/b5ddc42b465dd5b9532ee336d98343d81a6d35b2
  2105  	// (pre-Go 1.16). TODO: reevaluate when Go 1.16 is released.
  2106  	//{"a[", "a", false, nil},
  2107  }
  2108  
  2109  func errp(e error) string {
  2110  	if e == nil {
  2111  		return "<nil>"
  2112  	}
  2113  	return e.Error()
  2114  }
  2115  
  2116  // contains returns true if vector contains the string s.
  2117  func contains(vector []string, s string) bool {
  2118  	for _, elem := range vector {
  2119  		if elem == s {
  2120  			return true
  2121  		}
  2122  	}
  2123  	return false
  2124  }
  2125  
  2126  var globTests = []struct {
  2127  	pattern, result string
  2128  }{
  2129  	{"match.go", "match.go"},
  2130  	{"mat?h.go", "match.go"},
  2131  	{"ma*ch.go", "match.go"},
  2132  	{`\m\a\t\c\h\.\g\o`, "match.go"},
  2133  	{"../*/match.go", "../sftp/match.go"},
  2134  }
  2135  
  2136  type globTest struct {
  2137  	pattern string
  2138  	matches []string
  2139  }
  2140  
  2141  func (test *globTest) buildWant(root string) []string {
  2142  	var want []string
  2143  	for _, m := range test.matches {
  2144  		want = append(want, root+filepath.FromSlash(m))
  2145  	}
  2146  	sort.Strings(want)
  2147  	return want
  2148  }
  2149  
  2150  func TestMatch(t *testing.T) {
  2151  	for _, tt := range matchTests {
  2152  		pattern := tt.pattern
  2153  		s := tt.s
  2154  		ok, err := Match(pattern, s)
  2155  		if ok != tt.match || err != tt.err {
  2156  			t.Errorf("Match(%#q, %#q) = %v, %q want %v, %q", pattern, s, ok, errp(err), tt.match, errp(tt.err))
  2157  		}
  2158  	}
  2159  }
  2160  
  2161  func TestGlob(t *testing.T) {
  2162  	sftp, cmd := testClient(t, READONLY, NODELAY)
  2163  	defer cmd.Wait()
  2164  	defer sftp.Close()
  2165  
  2166  	for _, tt := range globTests {
  2167  		pattern := tt.pattern
  2168  		result := tt.result
  2169  		matches, err := sftp.Glob(pattern)
  2170  		if err != nil {
  2171  			t.Errorf("Glob error for %q: %s", pattern, err)
  2172  			continue
  2173  		}
  2174  		if !contains(matches, result) {
  2175  			t.Errorf("Glob(%#q) = %#v want %v", pattern, matches, result)
  2176  		}
  2177  	}
  2178  	for _, pattern := range []string{"no_match", "../*/no_match"} {
  2179  		matches, err := sftp.Glob(pattern)
  2180  		if err != nil {
  2181  			t.Errorf("Glob error for %q: %s", pattern, err)
  2182  			continue
  2183  		}
  2184  		if len(matches) != 0 {
  2185  			t.Errorf("Glob(%#q) = %#v want []", pattern, matches)
  2186  		}
  2187  	}
  2188  }
  2189  
  2190  func TestGlobError(t *testing.T) {
  2191  	sftp, cmd := testClient(t, READONLY, NODELAY)
  2192  	defer cmd.Wait()
  2193  	defer sftp.Close()
  2194  
  2195  	_, err := sftp.Glob("[7]")
  2196  	if err != nil {
  2197  		t.Error("expected error for bad pattern; got none")
  2198  	}
  2199  }
  2200  
  2201  func TestGlobUNC(t *testing.T) {
  2202  	sftp, cmd := testClient(t, READONLY, NODELAY)
  2203  	defer cmd.Wait()
  2204  	defer sftp.Close()
  2205  	// Just make sure this runs without crashing for now.
  2206  	// See issue 15879.
  2207  	sftp.Glob(`\\?\C:\*`)
  2208  }
  2209  
  2210  // sftp/issue/42, abrupt server hangup would result in client hangs.
  2211  func TestServerRoughDisconnect(t *testing.T) {
  2212  	skipIfWindows(t)
  2213  	if *testServerImpl {
  2214  		t.Skipf("skipping with -testserver")
  2215  	}
  2216  	sftp, cmd := testClient(t, READONLY, NODELAY)
  2217  	defer cmd.Wait()
  2218  	defer sftp.Close()
  2219  
  2220  	f, err := sftp.Open("/dev/zero")
  2221  	if err != nil {
  2222  		t.Fatal(err)
  2223  	}
  2224  	defer f.Close()
  2225  	go func() {
  2226  		time.Sleep(100 * time.Millisecond)
  2227  		cmd.Process.Kill()
  2228  	}()
  2229  
  2230  	_, err = io.Copy(ioutil.Discard, f)
  2231  	assert.Error(t, err)
  2232  }
  2233  
  2234  // sftp/issue/181, abrupt server hangup would result in client hangs.
  2235  // due to broadcastErr filling up the request channel
  2236  // this reproduces it about 50% of the time
  2237  func TestServerRoughDisconnect2(t *testing.T) {
  2238  	skipIfWindows(t)
  2239  	if *testServerImpl {
  2240  		t.Skipf("skipping with -testserver")
  2241  	}
  2242  	sftp, cmd := testClient(t, READONLY, NODELAY)
  2243  	defer cmd.Wait()
  2244  	defer sftp.Close()
  2245  
  2246  	f, err := sftp.Open("/dev/zero")
  2247  	if err != nil {
  2248  		t.Fatal(err)
  2249  	}
  2250  	defer f.Close()
  2251  	b := make([]byte, 32768*100)
  2252  	go func() {
  2253  		time.Sleep(1 * time.Millisecond)
  2254  		cmd.Process.Kill()
  2255  	}()
  2256  	for {
  2257  		_, err = f.Read(b)
  2258  		if err != nil {
  2259  			break
  2260  		}
  2261  	}
  2262  }
  2263  
  2264  // sftp/issue/234 - abrupt shutdown during ReadFrom hangs client
  2265  func TestServerRoughDisconnect3(t *testing.T) {
  2266  	skipIfWindows(t)
  2267  	if *testServerImpl {
  2268  		t.Skipf("skipping with -testserver")
  2269  	}
  2270  
  2271  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  2272  	defer cmd.Wait()
  2273  	defer sftp.Close()
  2274  
  2275  	dest, err := sftp.OpenFile("/dev/null", os.O_RDWR)
  2276  	if err != nil {
  2277  		t.Fatal(err)
  2278  	}
  2279  	defer dest.Close()
  2280  
  2281  	src, err := os.Open("/dev/zero")
  2282  	if err != nil {
  2283  		t.Fatal(err)
  2284  	}
  2285  	defer src.Close()
  2286  
  2287  	go func() {
  2288  		time.Sleep(10 * time.Millisecond)
  2289  		cmd.Process.Kill()
  2290  	}()
  2291  
  2292  	_, err = io.Copy(dest, src)
  2293  	assert.Error(t, err)
  2294  }
  2295  
  2296  // sftp/issue/234 - also affected Write
  2297  func TestServerRoughDisconnect4(t *testing.T) {
  2298  	skipIfWindows(t)
  2299  	if *testServerImpl {
  2300  		t.Skipf("skipping with -testserver")
  2301  	}
  2302  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  2303  	defer cmd.Wait()
  2304  	defer sftp.Close()
  2305  
  2306  	dest, err := sftp.OpenFile("/dev/null", os.O_RDWR)
  2307  	if err != nil {
  2308  		t.Fatal(err)
  2309  	}
  2310  	defer dest.Close()
  2311  
  2312  	src, err := os.Open("/dev/zero")
  2313  	if err != nil {
  2314  		t.Fatal(err)
  2315  	}
  2316  	defer src.Close()
  2317  
  2318  	go func() {
  2319  		time.Sleep(10 * time.Millisecond)
  2320  		cmd.Process.Kill()
  2321  	}()
  2322  
  2323  	b := make([]byte, 32768*200)
  2324  	src.Read(b)
  2325  	for {
  2326  		_, err = dest.Write(b)
  2327  		if err != nil {
  2328  			assert.NotEqual(t, io.EOF, err)
  2329  			break
  2330  		}
  2331  	}
  2332  
  2333  	_, err = io.Copy(dest, src)
  2334  	assert.Error(t, err)
  2335  }
  2336  
  2337  // sftp/issue/390 - server disconnect should not cause io.EOF or
  2338  // io.ErrUnexpectedEOF in sftp.File.Read, because those confuse io.ReadFull.
  2339  func TestServerRoughDisconnectEOF(t *testing.T) {
  2340  	skipIfWindows(t)
  2341  	if *testServerImpl {
  2342  		t.Skipf("skipping with -testserver")
  2343  	}
  2344  	sftp, cmd := testClient(t, READONLY, NODELAY)
  2345  	defer cmd.Wait()
  2346  	defer sftp.Close()
  2347  
  2348  	f, err := sftp.Open("/dev/null")
  2349  	if err != nil {
  2350  		t.Fatal(err)
  2351  	}
  2352  	defer f.Close()
  2353  	go func() {
  2354  		time.Sleep(100 * time.Millisecond)
  2355  		cmd.Process.Kill()
  2356  	}()
  2357  
  2358  	_, err = io.ReadFull(f, make([]byte, 10))
  2359  	assert.Error(t, err)
  2360  	assert.NotEqual(t, io.ErrUnexpectedEOF, err)
  2361  }
  2362  
  2363  // sftp/issue/26 writing to a read only file caused client to loop.
  2364  func TestClientWriteToROFile(t *testing.T) {
  2365  	skipIfWindows(t)
  2366  
  2367  	sftp, cmd := testClient(t, READWRITE, NODELAY)
  2368  	defer cmd.Wait()
  2369  
  2370  	defer func() {
  2371  		err := sftp.Close()
  2372  		assert.NoError(t, err)
  2373  	}()
  2374  
  2375  	// TODO (puellanivis): /dev/zero is not actually a read-only file.
  2376  	// So, this test works purely by accident.
  2377  	f, err := sftp.Open("/dev/zero")
  2378  	if err != nil {
  2379  		t.Fatal(err)
  2380  	}
  2381  	defer f.Close()
  2382  
  2383  	_, err = f.Write([]byte("hello"))
  2384  	if err == nil {
  2385  		t.Fatal("expected error, got", err)
  2386  	}
  2387  }
  2388  
  2389  func benchmarkRead(b *testing.B, bufsize int, delay time.Duration) {
  2390  	skipIfWindows(b)
  2391  	size := 10*1024*1024 + 123 // ~10MiB
  2392  
  2393  	// open sftp client
  2394  	sftp, cmd := testClient(b, READONLY, delay)
  2395  	defer cmd.Wait()
  2396  	defer sftp.Close()
  2397  
  2398  	buf := make([]byte, bufsize)
  2399  
  2400  	b.ResetTimer()
  2401  	b.SetBytes(int64(size))
  2402  
  2403  	for i := 0; i < b.N; i++ {
  2404  		offset := 0
  2405  
  2406  		f2, err := sftp.Open("/dev/zero")
  2407  		if err != nil {
  2408  			b.Fatal(err)
  2409  		}
  2410  
  2411  		for offset < size {
  2412  			n, err := io.ReadFull(f2, buf)
  2413  			offset += n
  2414  			if err == io.ErrUnexpectedEOF && offset != size {
  2415  				b.Fatalf("read too few bytes! want: %d, got: %d", size, n)
  2416  			}
  2417  
  2418  			if err != nil {
  2419  				b.Fatal(err)
  2420  			}
  2421  
  2422  			offset += n
  2423  		}
  2424  
  2425  		f2.Close()
  2426  	}
  2427  }
  2428  
  2429  func BenchmarkRead1k(b *testing.B) {
  2430  	benchmarkRead(b, 1*1024, NODELAY)
  2431  }
  2432  
  2433  func BenchmarkRead16k(b *testing.B) {
  2434  	benchmarkRead(b, 16*1024, NODELAY)
  2435  }
  2436  
  2437  func BenchmarkRead32k(b *testing.B) {
  2438  	benchmarkRead(b, 32*1024, NODELAY)
  2439  }
  2440  
  2441  func BenchmarkRead128k(b *testing.B) {
  2442  	benchmarkRead(b, 128*1024, NODELAY)
  2443  }
  2444  
  2445  func BenchmarkRead512k(b *testing.B) {
  2446  	benchmarkRead(b, 512*1024, NODELAY)
  2447  }
  2448  
  2449  func BenchmarkRead1MiB(b *testing.B) {
  2450  	benchmarkRead(b, 1024*1024, NODELAY)
  2451  }
  2452  
  2453  func BenchmarkRead4MiB(b *testing.B) {
  2454  	benchmarkRead(b, 4*1024*1024, NODELAY)
  2455  }
  2456  
  2457  func BenchmarkRead4MiBDelay10Msec(b *testing.B) {
  2458  	benchmarkRead(b, 4*1024*1024, 10*time.Millisecond)
  2459  }
  2460  
  2461  func BenchmarkRead4MiBDelay50Msec(b *testing.B) {
  2462  	benchmarkRead(b, 4*1024*1024, 50*time.Millisecond)
  2463  }
  2464  
  2465  func BenchmarkRead4MiBDelay150Msec(b *testing.B) {
  2466  	benchmarkRead(b, 4*1024*1024, 150*time.Millisecond)
  2467  }
  2468  
  2469  func benchmarkWrite(b *testing.B, bufsize int, delay time.Duration) {
  2470  	size := 10*1024*1024 + 123 // ~10MiB
  2471  
  2472  	// open sftp client
  2473  	sftp, cmd := testClient(b, false, delay)
  2474  	defer cmd.Wait()
  2475  	defer sftp.Close()
  2476  
  2477  	data := make([]byte, size)
  2478  
  2479  	b.ResetTimer()
  2480  	b.SetBytes(int64(size))
  2481  
  2482  	for i := 0; i < b.N; i++ {
  2483  		offset := 0
  2484  
  2485  		f, err := ioutil.TempFile("", "sftptest-benchwrite")
  2486  		if err != nil {
  2487  			b.Fatal(err)
  2488  		}
  2489  		defer os.Remove(f.Name()) // actually queue up a series of removes for these files
  2490  
  2491  		f2, err := sftp.Create(f.Name())
  2492  		if err != nil {
  2493  			b.Fatal(err)
  2494  		}
  2495  
  2496  		for offset < size {
  2497  			buf := data[offset:]
  2498  			if len(buf) > bufsize {
  2499  				buf = buf[:bufsize]
  2500  			}
  2501  
  2502  			n, err := f2.Write(buf)
  2503  			if err != nil {
  2504  				b.Fatal(err)
  2505  			}
  2506  
  2507  			if offset+n < size && n != bufsize {
  2508  				b.Fatalf("wrote too few bytes! want: %d, got: %d", size, n)
  2509  			}
  2510  
  2511  			offset += n
  2512  		}
  2513  
  2514  		f2.Close()
  2515  
  2516  		fi, err := os.Stat(f.Name())
  2517  		if err != nil {
  2518  			b.Fatal(err)
  2519  		}
  2520  
  2521  		if fi.Size() != int64(size) {
  2522  			b.Fatalf("wrong file size: want %d, got %d", size, fi.Size())
  2523  		}
  2524  
  2525  		os.Remove(f.Name())
  2526  	}
  2527  }
  2528  
  2529  func BenchmarkWrite1k(b *testing.B) {
  2530  	benchmarkWrite(b, 1*1024, NODELAY)
  2531  }
  2532  
  2533  func BenchmarkWrite16k(b *testing.B) {
  2534  	benchmarkWrite(b, 16*1024, NODELAY)
  2535  }
  2536  
  2537  func BenchmarkWrite32k(b *testing.B) {
  2538  	benchmarkWrite(b, 32*1024, NODELAY)
  2539  }
  2540  
  2541  func BenchmarkWrite128k(b *testing.B) {
  2542  	benchmarkWrite(b, 128*1024, NODELAY)
  2543  }
  2544  
  2545  func BenchmarkWrite512k(b *testing.B) {
  2546  	benchmarkWrite(b, 512*1024, NODELAY)
  2547  }
  2548  
  2549  func BenchmarkWrite1MiB(b *testing.B) {
  2550  	benchmarkWrite(b, 1024*1024, NODELAY)
  2551  }
  2552  
  2553  func BenchmarkWrite4MiB(b *testing.B) {
  2554  	benchmarkWrite(b, 4*1024*1024, NODELAY)
  2555  }
  2556  
  2557  func BenchmarkWrite4MiBDelay10Msec(b *testing.B) {
  2558  	benchmarkWrite(b, 4*1024*1024, 10*time.Millisecond)
  2559  }
  2560  
  2561  func BenchmarkWrite4MiBDelay50Msec(b *testing.B) {
  2562  	benchmarkWrite(b, 4*1024*1024, 50*time.Millisecond)
  2563  }
  2564  
  2565  func BenchmarkWrite4MiBDelay150Msec(b *testing.B) {
  2566  	benchmarkWrite(b, 4*1024*1024, 150*time.Millisecond)
  2567  }
  2568  
  2569  func benchmarkReadFrom(b *testing.B, bufsize int, delay time.Duration) {
  2570  	size := 10*1024*1024 + 123 // ~10MiB
  2571  
  2572  	// open sftp client
  2573  	sftp, cmd := testClient(b, false, delay)
  2574  	defer cmd.Wait()
  2575  	defer sftp.Close()
  2576  
  2577  	data := make([]byte, size)
  2578  
  2579  	b.ResetTimer()
  2580  	b.SetBytes(int64(size))
  2581  
  2582  	for i := 0; i < b.N; i++ {
  2583  		f, err := ioutil.TempFile("", "sftptest-benchreadfrom")
  2584  		if err != nil {
  2585  			b.Fatal(err)
  2586  		}
  2587  		defer os.Remove(f.Name())
  2588  
  2589  		f2, err := sftp.Create(f.Name())
  2590  		if err != nil {
  2591  			b.Fatal(err)
  2592  		}
  2593  		defer f2.Close()
  2594  
  2595  		f2.ReadFrom(bytes.NewReader(data))
  2596  		f2.Close()
  2597  
  2598  		fi, err := os.Stat(f.Name())
  2599  		if err != nil {
  2600  			b.Fatal(err)
  2601  		}
  2602  
  2603  		if fi.Size() != int64(size) {
  2604  			b.Fatalf("wrong file size: want %d, got %d", size, fi.Size())
  2605  		}
  2606  
  2607  		os.Remove(f.Name())
  2608  	}
  2609  }
  2610  
  2611  func BenchmarkReadFrom1k(b *testing.B) {
  2612  	benchmarkReadFrom(b, 1*1024, NODELAY)
  2613  }
  2614  
  2615  func BenchmarkReadFrom16k(b *testing.B) {
  2616  	benchmarkReadFrom(b, 16*1024, NODELAY)
  2617  }
  2618  
  2619  func BenchmarkReadFrom32k(b *testing.B) {
  2620  	benchmarkReadFrom(b, 32*1024, NODELAY)
  2621  }
  2622  
  2623  func BenchmarkReadFrom128k(b *testing.B) {
  2624  	benchmarkReadFrom(b, 128*1024, NODELAY)
  2625  }
  2626  
  2627  func BenchmarkReadFrom512k(b *testing.B) {
  2628  	benchmarkReadFrom(b, 512*1024, NODELAY)
  2629  }
  2630  
  2631  func BenchmarkReadFrom1MiB(b *testing.B) {
  2632  	benchmarkReadFrom(b, 1024*1024, NODELAY)
  2633  }
  2634  
  2635  func BenchmarkReadFrom4MiB(b *testing.B) {
  2636  	benchmarkReadFrom(b, 4*1024*1024, NODELAY)
  2637  }
  2638  
  2639  func BenchmarkReadFrom4MiBDelay10Msec(b *testing.B) {
  2640  	benchmarkReadFrom(b, 4*1024*1024, 10*time.Millisecond)
  2641  }
  2642  
  2643  func BenchmarkReadFrom4MiBDelay50Msec(b *testing.B) {
  2644  	benchmarkReadFrom(b, 4*1024*1024, 50*time.Millisecond)
  2645  }
  2646  
  2647  func BenchmarkReadFrom4MiBDelay150Msec(b *testing.B) {
  2648  	benchmarkReadFrom(b, 4*1024*1024, 150*time.Millisecond)
  2649  }
  2650  
  2651  func benchmarkWriteTo(b *testing.B, bufsize int, delay time.Duration) {
  2652  	size := 10*1024*1024 + 123 // ~10MiB
  2653  
  2654  	// open sftp client
  2655  	sftp, cmd := testClient(b, false, delay)
  2656  	defer cmd.Wait()
  2657  	defer sftp.Close()
  2658  
  2659  	f, err := ioutil.TempFile("", "sftptest-benchwriteto")
  2660  	if err != nil {
  2661  		b.Fatal(err)
  2662  	}
  2663  	defer os.Remove(f.Name())
  2664  
  2665  	data := make([]byte, size)
  2666  
  2667  	f.Write(data)
  2668  	f.Close()
  2669  
  2670  	buf := bytes.NewBuffer(make([]byte, 0, size))
  2671  
  2672  	b.ResetTimer()
  2673  	b.SetBytes(int64(size))
  2674  
  2675  	for i := 0; i < b.N; i++ {
  2676  		buf.Reset()
  2677  
  2678  		f2, err := sftp.Open(f.Name())
  2679  		if err != nil {
  2680  			b.Fatal(err)
  2681  		}
  2682  
  2683  		f2.WriteTo(buf)
  2684  		f2.Close()
  2685  
  2686  		if buf.Len() != size {
  2687  			b.Fatalf("wrote buffer size: want %d, got %d", size, buf.Len())
  2688  		}
  2689  	}
  2690  }
  2691  
  2692  func BenchmarkWriteTo1k(b *testing.B) {
  2693  	benchmarkWriteTo(b, 1*1024, NODELAY)
  2694  }
  2695  
  2696  func BenchmarkWriteTo16k(b *testing.B) {
  2697  	benchmarkWriteTo(b, 16*1024, NODELAY)
  2698  }
  2699  
  2700  func BenchmarkWriteTo32k(b *testing.B) {
  2701  	benchmarkWriteTo(b, 32*1024, NODELAY)
  2702  }
  2703  
  2704  func BenchmarkWriteTo128k(b *testing.B) {
  2705  	benchmarkWriteTo(b, 128*1024, NODELAY)
  2706  }
  2707  
  2708  func BenchmarkWriteTo512k(b *testing.B) {
  2709  	benchmarkWriteTo(b, 512*1024, NODELAY)
  2710  }
  2711  
  2712  func BenchmarkWriteTo1MiB(b *testing.B) {
  2713  	benchmarkWriteTo(b, 1024*1024, NODELAY)
  2714  }
  2715  
  2716  func BenchmarkWriteTo4MiB(b *testing.B) {
  2717  	benchmarkWriteTo(b, 4*1024*1024, NODELAY)
  2718  }
  2719  
  2720  func BenchmarkWriteTo4MiBDelay10Msec(b *testing.B) {
  2721  	benchmarkWriteTo(b, 4*1024*1024, 10*time.Millisecond)
  2722  }
  2723  
  2724  func BenchmarkWriteTo4MiBDelay50Msec(b *testing.B) {
  2725  	benchmarkWriteTo(b, 4*1024*1024, 50*time.Millisecond)
  2726  }
  2727  
  2728  func BenchmarkWriteTo4MiBDelay150Msec(b *testing.B) {
  2729  	benchmarkWriteTo(b, 4*1024*1024, 150*time.Millisecond)
  2730  }
  2731  
  2732  func benchmarkCopyDown(b *testing.B, fileSize int64, delay time.Duration) {
  2733  	skipIfWindows(b)
  2734  	// Create a temp file and fill it with zero's.
  2735  	src, err := ioutil.TempFile("", "sftptest-benchcopydown")
  2736  	if err != nil {
  2737  		b.Fatal(err)
  2738  	}
  2739  	defer src.Close()
  2740  	srcFilename := src.Name()
  2741  	defer os.Remove(srcFilename)
  2742  	zero, err := os.Open("/dev/zero")
  2743  	if err != nil {
  2744  		b.Fatal(err)
  2745  	}
  2746  	n, err := io.Copy(src, io.LimitReader(zero, fileSize))
  2747  	if err != nil {
  2748  		b.Fatal(err)
  2749  	}
  2750  	if n < fileSize {
  2751  		b.Fatal("short copy")
  2752  	}
  2753  	zero.Close()
  2754  	src.Close()
  2755  
  2756  	sftp, cmd := testClient(b, READONLY, delay)
  2757  	defer cmd.Wait()
  2758  	defer sftp.Close()
  2759  	b.ResetTimer()
  2760  	b.SetBytes(fileSize)
  2761  
  2762  	for i := 0; i < b.N; i++ {
  2763  		dst, err := ioutil.TempFile("", "sftptest-benchcopydown")
  2764  		if err != nil {
  2765  			b.Fatal(err)
  2766  		}
  2767  		defer os.Remove(dst.Name())
  2768  
  2769  		src, err := sftp.Open(srcFilename)
  2770  		if err != nil {
  2771  			b.Fatal(err)
  2772  		}
  2773  		defer src.Close()
  2774  		n, err := io.Copy(dst, src)
  2775  		if err != nil {
  2776  			b.Fatalf("copy error: %v", err)
  2777  		}
  2778  		if n < fileSize {
  2779  			b.Fatal("unable to copy all bytes")
  2780  		}
  2781  		dst.Close()
  2782  		fi, err := os.Stat(dst.Name())
  2783  		if err != nil {
  2784  			b.Fatal(err)
  2785  		}
  2786  
  2787  		if fi.Size() != fileSize {
  2788  			b.Fatalf("wrong file size: want %d, got %d", fileSize, fi.Size())
  2789  		}
  2790  		os.Remove(dst.Name())
  2791  	}
  2792  }
  2793  
  2794  func BenchmarkCopyDown10MiBDelay10Msec(b *testing.B) {
  2795  	benchmarkCopyDown(b, 10*1024*1024, 10*time.Millisecond)
  2796  }
  2797  
  2798  func BenchmarkCopyDown10MiBDelay50Msec(b *testing.B) {
  2799  	benchmarkCopyDown(b, 10*1024*1024, 50*time.Millisecond)
  2800  }
  2801  
  2802  func BenchmarkCopyDown10MiBDelay150Msec(b *testing.B) {
  2803  	benchmarkCopyDown(b, 10*1024*1024, 150*time.Millisecond)
  2804  }
  2805  
  2806  func benchmarkCopyUp(b *testing.B, fileSize int64, delay time.Duration) {
  2807  	skipIfWindows(b)
  2808  	// Create a temp file and fill it with zero's.
  2809  	src, err := ioutil.TempFile("", "sftptest-benchcopyup")
  2810  	if err != nil {
  2811  		b.Fatal(err)
  2812  	}
  2813  	defer src.Close()
  2814  	srcFilename := src.Name()
  2815  	defer os.Remove(srcFilename)
  2816  	zero, err := os.Open("/dev/zero")
  2817  	if err != nil {
  2818  		b.Fatal(err)
  2819  	}
  2820  	n, err := io.Copy(src, io.LimitReader(zero, fileSize))
  2821  	if err != nil {
  2822  		b.Fatal(err)
  2823  	}
  2824  	if n < fileSize {
  2825  		b.Fatal("short copy")
  2826  	}
  2827  	zero.Close()
  2828  	src.Close()
  2829  
  2830  	sftp, cmd := testClient(b, false, delay)
  2831  	defer cmd.Wait()
  2832  	defer sftp.Close()
  2833  
  2834  	b.ResetTimer()
  2835  	b.SetBytes(fileSize)
  2836  
  2837  	for i := 0; i < b.N; i++ {
  2838  		tmp, err := ioutil.TempFile("", "sftptest-benchcopyup")
  2839  		if err != nil {
  2840  			b.Fatal(err)
  2841  		}
  2842  		tmp.Close()
  2843  		defer os.Remove(tmp.Name())
  2844  
  2845  		dst, err := sftp.Create(tmp.Name())
  2846  		if err != nil {
  2847  			b.Fatal(err)
  2848  		}
  2849  		defer dst.Close()
  2850  		src, err := os.Open(srcFilename)
  2851  		if err != nil {
  2852  			b.Fatal(err)
  2853  		}
  2854  		defer src.Close()
  2855  		n, err := io.Copy(dst, src)
  2856  		if err != nil {
  2857  			b.Fatalf("copy error: %v", err)
  2858  		}
  2859  		if n < fileSize {
  2860  			b.Fatal("unable to copy all bytes")
  2861  		}
  2862  
  2863  		fi, err := os.Stat(tmp.Name())
  2864  		if err != nil {
  2865  			b.Fatal(err)
  2866  		}
  2867  
  2868  		if fi.Size() != fileSize {
  2869  			b.Fatalf("wrong file size: want %d, got %d", fileSize, fi.Size())
  2870  		}
  2871  		os.Remove(tmp.Name())
  2872  	}
  2873  }
  2874  
  2875  func BenchmarkCopyUp10MiBDelay10Msec(b *testing.B) {
  2876  	benchmarkCopyUp(b, 10*1024*1024, 10*time.Millisecond)
  2877  }
  2878  
  2879  func BenchmarkCopyUp10MiBDelay50Msec(b *testing.B) {
  2880  	benchmarkCopyUp(b, 10*1024*1024, 50*time.Millisecond)
  2881  }
  2882  
  2883  func BenchmarkCopyUp10MiBDelay150Msec(b *testing.B) {
  2884  	benchmarkCopyUp(b, 10*1024*1024, 150*time.Millisecond)
  2885  }