This also fixes a very unlikely race condition which could delete a
temporary file from a concurrent nsscash run if it chooses the same
temporary file name after os.Rename() was called but before our deferred
os.Remove() remove it. This race would cause the second nsscash run to
fail but not cause any corruption of persistent files.
== REQUIREMENTS
- Go, for `nsscash`
== REQUIREMENTS
- Go, for `nsscash`
- * github.com/pkg/errors
* github.com/BurntSushi/toml
* github.com/BurntSushi/toml
+ * github.com/google/renameio
+ * github.com/pkg/errors
- C compiler, for `libnss_cash.so.2`
- HTTP(S) server to provide the passwd/group/etc. files
- C compiler, for `libnss_cash.so.2`
- HTTP(S) server to provide the passwd/group/etc. files
+ "github.com/google/renameio"
"github.com/pkg/errors"
)
"github.com/pkg/errors"
)
return fmt.Errorf("refusing to write empty file")
}
return fmt.Errorf("refusing to write empty file")
}
- // Write the file in an atomic fashion by creating a temporary file
- // and renaming it over the target file
-
- dir := filepath.Dir(file.Path)
- name := filepath.Base(file.Path)
-
- f, err := ioutil.TempFile(dir, "tmp-"+name+"-")
+ f, err := renameio.TempFile(filepath.Dir(file.Path), file.Path)
if err != nil {
return err
}
if err != nil {
return err
}
- defer os.Remove(f.Name())
- defer f.Close()
// Apply permissions/user/group from the target file but remove the
// write permissions to discourage manual modifications, use Stat
// Apply permissions/user/group from the target file but remove the
// write permissions to discourage manual modifications, use Stat
if err != nil {
return err
}
if err != nil {
return err
}
- err = f.Sync()
- if err != nil {
- return err
- }
- return os.Rename(f.Name(), file.Path)
+ return f.CloseAtomicallyReplace()
require (
github.com/BurntSushi/toml v0.3.1
require (
github.com/BurntSushi/toml v0.3.1
+ github.com/google/renameio v0.1.1-0.20200217212219-353f81969824
github.com/pkg/errors v0.8.1
)
github.com/pkg/errors v0.8.1
)
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/google/renameio v0.1.1-0.20200217212219-353f81969824 h1:9q700G0beHecUuiZOuKgNqNsGQixTeDLnzVZ5nsW3lc=
+github.com/google/renameio v0.1.1-0.20200217212219-353f81969824/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
"log"
"os"
"path/filepath"
"log"
"os"
"path/filepath"
+
+ "github.com/google/renameio"
return fmt.Errorf("unsupported file type %v", t)
}
return fmt.Errorf("unsupported file type %v", t)
}
- dstDir := filepath.Dir(dstPath)
- dstName := filepath.Base(dstPath)
-
// We must create the file first or deployFile() will abort; this is
// ugly because deployFile() already performs an atomic replacement
// but the simplest solution with the least duplicate code
// We must create the file first or deployFile() will abort; this is
// ugly because deployFile() already performs an atomic replacement
// but the simplest solution with the least duplicate code
- f, err := ioutil.TempFile(dstDir, "tmp-"+dstName+"-")
+ f, err := renameio.TempFile(filepath.Dir(dstPath), dstPath)
if err != nil {
return err
}
if err != nil {
return err
}
- tmpPath := f.Name()
- defer os.Remove(tmpPath)
- f.Close()
err = deployFile(&File{
Type: t,
Url: srcPath,
err = deployFile(&File{
Type: t,
Url: srcPath,
body: x.Bytes(),
})
if err != nil {
return err
}
body: x.Bytes(),
})
if err != nil {
return err
}
- return os.Rename(tmpPath, dstPath)
+ return f.CloseAtomicallyReplace()
"os"
"path/filepath"
"time"
"os"
"path/filepath"
"time"
+
+ "github.com/google/renameio"
- // Write the file in an atomic fashion by creating a temporary file
- // and renaming it over the target file
-
- dir := filepath.Dir(path)
- name := filepath.Base(path)
-
- f, err := ioutil.TempFile(dir, "tmp-"+name+"-")
+ f, err := renameio.TempFile(filepath.Dir(path), path)
if err != nil {
return err
}
if err != nil {
return err
}
- defer os.Remove(f.Name())
- defer f.Close()
_, err = f.Write(x)
if err != nil {
return err
}
_, err = f.Write(x)
if err != nil {
return err
}
- err = f.Sync()
- if err != nil {
- return err
- }
- return os.Rename(f.Name(), path)
+ return f.CloseAtomicallyReplace()