diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-01-02 15:05:27 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-01-21 23:53:22 -0800 |
commit | 5a8ea165926cb0737ab03bc48c18dc5198ab5305 (patch) | |
tree | 962dc3357c57f019f85658f99e2e753e30201c27 /libgo/go/io | |
parent | 6ac6529e155c9baa0aaaed7aca06bd38ebda5b43 (diff) | |
download | gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.zip gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.tar.gz gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.tar.bz2 |
libgo: update to Go1.14beta1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/214297
Diffstat (limited to 'libgo/go/io')
-rw-r--r-- | libgo/go/io/io.go | 3 | ||||
-rw-r--r-- | libgo/go/io/io_test.go | 2 | ||||
-rw-r--r-- | libgo/go/io/ioutil/example_test.go | 23 | ||||
-rw-r--r-- | libgo/go/io/ioutil/ioutil.go | 9 | ||||
-rw-r--r-- | libgo/go/io/ioutil/ioutil_test.go | 34 | ||||
-rw-r--r-- | libgo/go/io/ioutil/tempfile.go | 32 | ||||
-rw-r--r-- | libgo/go/io/ioutil/tempfile_test.go | 45 | ||||
-rw-r--r-- | libgo/go/io/multi_test.go | 2 | ||||
-rw-r--r-- | libgo/go/io/pipe.go | 35 | ||||
-rw-r--r-- | libgo/go/io/pipe_test.go | 8 |
10 files changed, 150 insertions, 43 deletions
diff --git a/libgo/go/io/io.go b/libgo/go/io/io.go index 2010770..9cc3086 100644 --- a/libgo/go/io/io.go +++ b/libgo/go/io/io.go @@ -368,6 +368,9 @@ func Copy(dst Writer, src Reader) (written int64, err error) { // provided buffer (if one is required) rather than allocating a // temporary one. If buf is nil, one is allocated; otherwise if it has // zero length, CopyBuffer panics. +// +// If either src implements WriterTo or dst implements ReaderFrom, +// buf will not be used to perform the copy. func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { if buf != nil && len(buf) == 0 { panic("empty buffer in io.CopyBuffer") diff --git a/libgo/go/io/io_test.go b/libgo/go/io/io_test.go index 0e4ce61..ca90403 100644 --- a/libgo/go/io/io_test.go +++ b/libgo/go/io/io_test.go @@ -13,7 +13,7 @@ import ( "testing" ) -// An version of bytes.Buffer without ReadFrom and WriteTo +// A version of bytes.Buffer without ReadFrom and WriteTo type Buffer struct { bytes.Buffer ReaderFrom // conflicts with and hides bytes.Buffer's ReaderFrom. diff --git a/libgo/go/io/ioutil/example_test.go b/libgo/go/io/ioutil/example_test.go index a7d340b..bc2b6fb 100644 --- a/libgo/go/io/ioutil/example_test.go +++ b/libgo/go/io/ioutil/example_test.go @@ -53,6 +53,29 @@ func ExampleTempDir() { } } +func ExampleTempDir_suffix() { + parentDir := os.TempDir() + logsDir, err := ioutil.TempDir(parentDir, "*-logs") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(logsDir) // clean up + + // Logs can be cleaned out earlier if needed by searching + // for all directories whose suffix ends in *-logs. + globPattern := filepath.Join(parentDir, "*-logs") + matches, err := filepath.Glob(globPattern) + if err != nil { + log.Fatalf("Failed to match %q: %v", globPattern, err) + } + + for _, match := range matches { + if err := os.RemoveAll(match); err != nil { + log.Printf("Failed to remove %q: %v", match, err) + } + } +} + func ExampleTempFile() { content := []byte("temporary file's content") tmpfile, err := ioutil.TempFile("", "example") diff --git a/libgo/go/io/ioutil/ioutil.go b/libgo/go/io/ioutil/ioutil.go index 674b270..b1cb841 100644 --- a/libgo/go/io/ioutil/ioutil.go +++ b/libgo/go/io/ioutil/ioutil.go @@ -74,17 +74,14 @@ func ReadFile(filename string) ([]byte, error) { } // WriteFile writes data to a file named by filename. -// If the file does not exist, WriteFile creates it with permissions perm; -// otherwise WriteFile truncates it before writing. +// If the file does not exist, WriteFile creates it with permissions perm +// (before umask); otherwise WriteFile truncates it before writing. func WriteFile(filename string, data []byte, perm os.FileMode) error { f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) if err != nil { return err } - n, err := f.Write(data) - if err == nil && n < len(data) { - err = io.ErrShortWrite - } + _, err = f.Write(data) if err1 := f.Close(); err == nil { err = err1 } diff --git a/libgo/go/io/ioutil/ioutil_test.go b/libgo/go/io/ioutil/ioutil_test.go index 81fc602..1fc4c8b 100644 --- a/libgo/go/io/ioutil/ioutil_test.go +++ b/libgo/go/io/ioutil/ioutil_test.go @@ -5,7 +5,9 @@ package ioutil import ( + "bytes" "os" + "path/filepath" "testing" ) @@ -63,6 +65,38 @@ func TestWriteFile(t *testing.T) { os.Remove(filename) // ignore error } +func TestReadOnlyWriteFile(t *testing.T) { + if os.Getuid() == 0 { + t.Skipf("Root can write to read-only files anyway, so skip the read-only test.") + } + + // We don't want to use TempFile directly, since that opens a file for us as 0600. + tempDir, err := TempDir("", t.Name()) + if err != nil { + t.Fatalf("TempDir %s: %v", t.Name(), err) + } + defer os.RemoveAll(tempDir) + filename := filepath.Join(tempDir, "blurp.txt") + + shmorp := []byte("shmorp") + florp := []byte("florp") + err = WriteFile(filename, shmorp, 0444) + if err != nil { + t.Fatalf("WriteFile %s: %v", filename, err) + } + err = WriteFile(filename, florp, 0444) + if err == nil { + t.Fatalf("Expected an error when writing to read-only file %s", filename) + } + got, err := ReadFile(filename) + if err != nil { + t.Fatalf("ReadFile %s: %v", filename, err) + } + if !bytes.Equal(got, shmorp) { + t.Fatalf("want %s, got %s", shmorp, got) + } +} + func TestReadDir(t *testing.T) { dirname := "rumpelstilzchen" _, err := ReadDir(dirname) diff --git a/libgo/go/io/ioutil/tempfile.go b/libgo/go/io/ioutil/tempfile.go index ba8783b..3aa23c5 100644 --- a/libgo/go/io/ioutil/tempfile.go +++ b/libgo/go/io/ioutil/tempfile.go @@ -52,12 +52,7 @@ func TempFile(dir, pattern string) (f *os.File, err error) { dir = os.TempDir() } - var prefix, suffix string - if pos := strings.LastIndex(pattern, "*"); pos != -1 { - prefix, suffix = pattern[:pos], pattern[pos+1:] - } else { - prefix = pattern - } + prefix, suffix := prefixAndSuffix(pattern) nconflict := 0 for i := 0; i < 10000; i++ { @@ -76,21 +71,36 @@ func TempFile(dir, pattern string) (f *os.File, err error) { return } -// TempDir creates a new temporary directory in the directory dir -// with a name beginning with prefix and returns the path of the -// new directory. If dir is the empty string, TempDir uses the +// prefixAndSuffix splits pattern by the last wildcard "*", if applicable, +// returning prefix as the part before "*" and suffix as the part after "*". +func prefixAndSuffix(pattern string) (prefix, suffix string) { + if pos := strings.LastIndex(pattern, "*"); pos != -1 { + prefix, suffix = pattern[:pos], pattern[pos+1:] + } else { + prefix = pattern + } + return +} + +// TempDir creates a new temporary directory in the directory dir. +// The directory name is generated by taking pattern and applying a +// random string to the end. If pattern includes a "*", the random string +// replaces the last "*". TempDir returns the name of the new directory. +// If dir is the empty string, TempDir uses the // default directory for temporary files (see os.TempDir). // Multiple programs calling TempDir simultaneously // will not choose the same directory. It is the caller's responsibility // to remove the directory when no longer needed. -func TempDir(dir, prefix string) (name string, err error) { +func TempDir(dir, pattern string) (name string, err error) { if dir == "" { dir = os.TempDir() } + prefix, suffix := prefixAndSuffix(pattern) + nconflict := 0 for i := 0; i < 10000; i++ { - try := filepath.Join(dir, prefix+nextRandom()) + try := filepath.Join(dir, prefix+nextRandom()+suffix) err = os.Mkdir(try, 0700) if os.IsExist(err) { if nconflict++; nconflict > 10 { diff --git a/libgo/go/io/ioutil/tempfile_test.go b/libgo/go/io/ioutil/tempfile_test.go index 0758890..698ebab 100644 --- a/libgo/go/io/ioutil/tempfile_test.go +++ b/libgo/go/io/ioutil/tempfile_test.go @@ -54,18 +54,47 @@ func TestTempDir(t *testing.T) { t.Errorf("TempDir(`/_not_exists_`, `foo`) = %v, %v", name, err) } - dir := os.TempDir() - name, err = TempDir(dir, "ioutil_test") - if name == "" || err != nil { - t.Errorf("TempDir(dir, `ioutil_test`) = %v, %v", name, err) + tests := []struct { + pattern string + wantPrefix, wantSuffix string + }{ + {"ioutil_test", "ioutil_test", ""}, + {"ioutil_test*", "ioutil_test", ""}, + {"ioutil_test*xyz", "ioutil_test", "xyz"}, } - if name != "" { - os.Remove(name) - re := regexp.MustCompile("^" + regexp.QuoteMeta(filepath.Join(dir, "ioutil_test")) + "[0-9]+$") + + dir := os.TempDir() + + runTestTempDir := func(t *testing.T, pattern, wantRePat string) { + name, err := TempDir(dir, pattern) + if name == "" || err != nil { + t.Fatalf("TempDir(dir, `ioutil_test`) = %v, %v", name, err) + } + defer os.Remove(name) + + re := regexp.MustCompile(wantRePat) if !re.MatchString(name) { - t.Errorf("TempDir(`"+dir+"`, `ioutil_test`) created bad name %s", name) + t.Errorf("TempDir(%q, %q) created bad name\n\t%q\ndid not match pattern\n\t%q", dir, pattern, name, wantRePat) } } + + for _, tt := range tests { + t.Run(tt.pattern, func(t *testing.T) { + wantRePat := "^" + regexp.QuoteMeta(filepath.Join(dir, tt.wantPrefix)) + "[0-9]+" + regexp.QuoteMeta(tt.wantSuffix) + "$" + runTestTempDir(t, tt.pattern, wantRePat) + }) + } + + // Separately testing "*xyz" (which has no prefix). That is when constructing the + // pattern to assert on, as in the previous loop, using filepath.Join for an empty + // prefix filepath.Join(dir, ""), produces the pattern: + // ^<DIR>[0-9]+xyz$ + // yet we just want to match + // "^<DIR>/[0-9]+xyz" + t.Run("*xyz", func(t *testing.T) { + wantRePat := "^" + regexp.QuoteMeta(filepath.Join(dir)) + regexp.QuoteMeta(string(filepath.Separator)) + "[0-9]+xyz$" + runTestTempDir(t, "*xyz", wantRePat) + }) } // test that we return a nice error message if the dir argument to TempDir doesn't diff --git a/libgo/go/io/multi_test.go b/libgo/go/io/multi_test.go index 9aad954..bc6b0ab 100644 --- a/libgo/go/io/multi_test.go +++ b/libgo/go/io/multi_test.go @@ -287,7 +287,7 @@ func TestMultiReaderSingleByteWithEOF(t *testing.T) { } } -// Test that a reader returning (n, EOF) at the end of an MultiReader +// Test that a reader returning (n, EOF) at the end of a MultiReader // chain continues to return EOF on its final read, rather than // yielding a (0, EOF). func TestMultiReaderFinalEOF(t *testing.T) { diff --git a/libgo/go/io/pipe.go b/libgo/go/io/pipe.go index 4efaf2f..b5343bb 100644 --- a/libgo/go/io/pipe.go +++ b/libgo/go/io/pipe.go @@ -10,19 +10,26 @@ package io import ( "errors" "sync" - "sync/atomic" ) -// atomicError is a type-safe atomic value for errors. -// We use a struct{ error } to ensure consistent use of a concrete type. -type atomicError struct{ v atomic.Value } +// onceError is an object that will only store an error once. +type onceError struct { + sync.Mutex // guards following + err error +} -func (a *atomicError) Store(err error) { - a.v.Store(struct{ error }{err}) +func (a *onceError) Store(err error) { + a.Lock() + defer a.Unlock() + if a.err != nil { + return + } + a.err = err } -func (a *atomicError) Load() error { - err, _ := a.v.Load().(struct{ error }) - return err.error +func (a *onceError) Load() error { + a.Lock() + defer a.Unlock() + return a.err } // ErrClosedPipe is the error used for read or write operations on a closed pipe. @@ -36,8 +43,8 @@ type pipe struct { once sync.Once // Protects closing done done chan struct{} - rerr atomicError - werr atomicError + rerr onceError + werr onceError } func (p *pipe) Read(b []byte) (n int, err error) { @@ -135,6 +142,9 @@ func (r *PipeReader) Close() error { // CloseWithError closes the reader; subsequent writes // to the write half of the pipe will return the error err. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. func (r *PipeReader) CloseWithError(err error) error { return r.p.CloseRead(err) } @@ -163,7 +173,8 @@ func (w *PipeWriter) Close() error { // read half of the pipe will return no bytes and the error err, // or EOF if err is nil. // -// CloseWithError always returns nil. +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. func (w *PipeWriter) CloseWithError(err error) error { return w.p.CloseWrite(err) } diff --git a/libgo/go/io/pipe_test.go b/libgo/go/io/pipe_test.go index f18b1c4..8973360 100644 --- a/libgo/go/io/pipe_test.go +++ b/libgo/go/io/pipe_test.go @@ -326,8 +326,8 @@ func TestPipeCloseError(t *testing.T) { t.Errorf("Write error: got %T, want testError1", err) } r.CloseWithError(testError2{}) - if _, err := w.Write(nil); err != (testError2{}) { - t.Errorf("Write error: got %T, want testError2", err) + if _, err := w.Write(nil); err != (testError1{}) { + t.Errorf("Write error: got %T, want testError1", err) } r, w = Pipe() @@ -336,8 +336,8 @@ func TestPipeCloseError(t *testing.T) { t.Errorf("Read error: got %T, want testError1", err) } w.CloseWithError(testError2{}) - if _, err := r.Read(nil); err != (testError2{}) { - t.Errorf("Read error: got %T, want testError2", err) + if _, err := r.Read(nil); err != (testError1{}) { + t.Errorf("Read error: got %T, want testError1", err) } } |