This fixes various test issues with Go 1.20. - Most tests rewritten to use fakefs where possible - Some tests that were already skipped, or dubious (invasive, unmaintainable, unclear what they even tested) have been removed - Some actual code rewritten to better support testing in fakefs Co-authored-by: Eric P <eric@kastelo.net>
This commit is contained in:
+6
-4
@@ -329,10 +329,12 @@ func (f *folder) getHealthErrorWithoutIgnores() error {
|
||||
return err
|
||||
}
|
||||
|
||||
dbPath := locations.Get(locations.Database)
|
||||
if usage, err := fs.NewFilesystem(fs.FilesystemTypeBasic, dbPath).Usage("."); err == nil {
|
||||
if err = config.CheckFreeSpace(f.model.cfg.Options().MinHomeDiskFree, usage); err != nil {
|
||||
return fmt.Errorf("insufficient space on disk for database (%v): %w", dbPath, err)
|
||||
if minFree := f.model.cfg.Options().MinHomeDiskFree; minFree.Value > 0 {
|
||||
dbPath := locations.Get(locations.Database)
|
||||
if usage, err := fs.NewFilesystem(fs.FilesystemTypeBasic, dbPath).Usage("."); err == nil {
|
||||
if err = config.CheckFreeSpace(minFree, usage); err != nil {
|
||||
return fmt.Errorf("insufficient space on disk for database (%v): %w", dbPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,11 +35,11 @@ func TestRecvOnlyRevertDeletes(t *testing.T) {
|
||||
// Create some test data
|
||||
|
||||
for _, dir := range []string{".stfolder", "ignDir", "unknownDir"} {
|
||||
must(t, ffs.MkdirAll(dir, 0755))
|
||||
must(t, ffs.MkdirAll(dir, 0o755))
|
||||
}
|
||||
writeFilePerm(t, ffs, "ignDir/ignFile", []byte("hello\n"), 0644)
|
||||
writeFilePerm(t, ffs, "unknownDir/unknownFile", []byte("hello\n"), 0644)
|
||||
writeFilePerm(t, ffs, ".stignore", []byte("ignDir\n"), 0644)
|
||||
writeFilePerm(t, ffs, "ignDir/ignFile", []byte("hello\n"), 0o644)
|
||||
writeFilePerm(t, ffs, "unknownDir/unknownFile", []byte("hello\n"), 0o644)
|
||||
writeFilePerm(t, ffs, ".stignore", []byte("ignDir\n"), 0o644)
|
||||
|
||||
knownFiles := setupKnownFiles(t, ffs, []byte("hello\n"))
|
||||
|
||||
@@ -116,7 +116,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
|
||||
|
||||
// Create some test data
|
||||
|
||||
must(t, ffs.MkdirAll(".stfolder", 0755))
|
||||
must(t, ffs.MkdirAll(".stfolder", 0o755))
|
||||
oldData := []byte("hello\n")
|
||||
knownFiles := setupKnownFiles(t, ffs, oldData)
|
||||
|
||||
@@ -151,7 +151,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
|
||||
// Update the file.
|
||||
|
||||
newData := []byte("totally different data\n")
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", newData, 0644)
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", newData, 0o644)
|
||||
|
||||
// Rescan.
|
||||
|
||||
@@ -206,7 +206,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
|
||||
|
||||
// Create some test data
|
||||
|
||||
must(t, ffs.MkdirAll(".stfolder", 0755))
|
||||
must(t, ffs.MkdirAll(".stfolder", 0o755))
|
||||
oldData := []byte("hello\n")
|
||||
knownFiles := setupKnownFiles(t, ffs, oldData)
|
||||
|
||||
@@ -241,8 +241,8 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
|
||||
// Create a file and modify another
|
||||
|
||||
const file = "foo"
|
||||
writeFilePerm(t, ffs, file, []byte("hello\n"), 0644)
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", []byte("bye\n"), 0644)
|
||||
writeFilePerm(t, ffs, file, []byte("hello\n"), 0o644)
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", []byte("bye\n"), 0o644)
|
||||
|
||||
must(t, m.ScanFolder("ro"))
|
||||
|
||||
@@ -254,7 +254,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
|
||||
// Remove the file again and undo the modification
|
||||
|
||||
must(t, ffs.Remove(file))
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", oldData, 0644)
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", oldData, 0o644)
|
||||
must(t, ffs.Chtimes("knownDir/knownFile", knownFiles[1].ModTime(), knownFiles[1].ModTime()))
|
||||
|
||||
must(t, m.ScanFolder("ro"))
|
||||
@@ -276,7 +276,7 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) {
|
||||
|
||||
// Create some test data
|
||||
|
||||
must(t, ffs.MkdirAll(".stfolder", 0755))
|
||||
must(t, ffs.MkdirAll(".stfolder", 0o755))
|
||||
oldData := []byte("hello\n")
|
||||
knownFiles := setupKnownFiles(t, ffs, oldData)
|
||||
|
||||
@@ -341,7 +341,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
|
||||
|
||||
// Create some test data
|
||||
|
||||
must(t, ffs.MkdirAll(".stfolder", 0755))
|
||||
must(t, ffs.MkdirAll(".stfolder", 0o755))
|
||||
oldData := []byte("hello\n")
|
||||
knownFiles := setupKnownFiles(t, ffs, oldData)
|
||||
|
||||
@@ -377,8 +377,8 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
|
||||
|
||||
const file = "foo"
|
||||
knownFile := filepath.Join("knownDir", "knownFile")
|
||||
writeFilePerm(t, ffs, file, []byte("hello\n"), 0644)
|
||||
writeFilePerm(t, ffs, knownFile, []byte("bye\n"), 0644)
|
||||
writeFilePerm(t, ffs, file, []byte("hello\n"), 0o644)
|
||||
writeFilePerm(t, ffs, knownFile, []byte("bye\n"), 0o644)
|
||||
|
||||
must(t, m.ScanFolder("ro"))
|
||||
|
||||
@@ -431,10 +431,10 @@ func TestRecvOnlyRevertOwnID(t *testing.T) {
|
||||
|
||||
// Create some test data
|
||||
|
||||
must(t, ffs.MkdirAll(".stfolder", 0755))
|
||||
must(t, ffs.MkdirAll(".stfolder", 0o755))
|
||||
data := []byte("hello\n")
|
||||
name := "foo"
|
||||
writeFilePerm(t, ffs, name, data, 0644)
|
||||
writeFilePerm(t, ffs, name, data, 0o644)
|
||||
|
||||
// Make sure the file is scanned and locally changed
|
||||
must(t, m.ScanFolder("ro"))
|
||||
@@ -483,8 +483,8 @@ func TestRecvOnlyRevertOwnID(t *testing.T) {
|
||||
func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.FileInfo {
|
||||
t.Helper()
|
||||
|
||||
must(t, ffs.MkdirAll("knownDir", 0755))
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", data, 0644)
|
||||
must(t, ffs.MkdirAll("knownDir", 0o755))
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", data, 0o644)
|
||||
|
||||
t0 := time.Now().Add(-1 * time.Minute)
|
||||
must(t, ffs.Chtimes("knownDir/knownFile", t0, t0))
|
||||
@@ -498,14 +498,14 @@ func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.Fi
|
||||
{
|
||||
Name: "knownDir",
|
||||
Type: protocol.FileInfoTypeDirectory,
|
||||
Permissions: 0755,
|
||||
Permissions: 0o755,
|
||||
Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 42}}},
|
||||
Sequence: 42,
|
||||
},
|
||||
{
|
||||
Name: "knownDir/knownFile",
|
||||
Type: protocol.FileInfoTypeFile,
|
||||
Permissions: 0644,
|
||||
Permissions: 0o644,
|
||||
Size: fi.Size(),
|
||||
ModifiedS: fi.ModTime().Unix(),
|
||||
ModifiedNs: int(fi.ModTime().UnixNano() % 1e9),
|
||||
@@ -521,9 +521,9 @@ func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.Fi
|
||||
func setupROFolder(t *testing.T) (*testModel, *receiveOnlyFolder, context.CancelFunc) {
|
||||
t.Helper()
|
||||
|
||||
w, cancel := createTmpWrapper(defaultCfg)
|
||||
w, cancel := newConfigWrapper(defaultCfg)
|
||||
cfg := w.RawCopy()
|
||||
fcfg := testFolderConfigFake()
|
||||
fcfg := newFolderConfig()
|
||||
fcfg.ID = "ro"
|
||||
fcfg.Label = "ro"
|
||||
fcfg.Type = config.FolderTypeReceiveOnly
|
||||
|
||||
+155
-162
@@ -9,7 +9,6 @@ package model
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -25,8 +24,8 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/ignore"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/scanner"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
@@ -43,6 +42,28 @@ var blocks = []protocol.BlockInfo{
|
||||
{Offset: 917504, Size: 0x20000, Hash: []uint8{0x96, 0x6b, 0x15, 0x6b, 0xc4, 0xf, 0x19, 0x18, 0xca, 0xbb, 0x5f, 0xd6, 0xbb, 0xa2, 0xc6, 0x2a, 0xac, 0xbb, 0x8a, 0xb9, 0xce, 0xec, 0x4c, 0xdb, 0x78, 0xec, 0x57, 0x5d, 0x33, 0xf9, 0x8e, 0xaf}},
|
||||
}
|
||||
|
||||
func prepareTmpFile(to fs.Filesystem) (string, error) {
|
||||
tmpName := fs.TempName("file")
|
||||
in, err := os.Open("testdata/tmpfile")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := to.Create(tmpName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer out.Close()
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
return "", err
|
||||
}
|
||||
future := time.Now().Add(time.Hour)
|
||||
if err := to.Chtimes(tmpName, future, future); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tmpName, nil
|
||||
}
|
||||
|
||||
var folders = []string{"default"}
|
||||
|
||||
var diffTestData = []struct {
|
||||
@@ -91,7 +112,7 @@ func createEmptyFileInfo(t *testing.T, name string, fs fs.Filesystem) protocol.F
|
||||
|
||||
// Sets up a folder and model, but makes sure the services aren't actually running.
|
||||
func setupSendReceiveFolder(t testing.TB, files ...protocol.FileInfo) (*testModel, *sendReceiveFolder, context.CancelFunc) {
|
||||
w, fcfg, wCancel := tmpDefaultWrapper(t)
|
||||
w, fcfg, wCancel := newDefaultCfgWrapper()
|
||||
// Initialise model and stop immediately.
|
||||
model := setupModel(t, w)
|
||||
model.cancel()
|
||||
@@ -108,12 +129,6 @@ func setupSendReceiveFolder(t testing.TB, files ...protocol.FileInfo) (*testMode
|
||||
return model, f, wCancel
|
||||
}
|
||||
|
||||
func cleanupSRFolder(f *sendReceiveFolder, m *testModel, wrapperCancel context.CancelFunc) {
|
||||
wrapperCancel()
|
||||
os.Remove(m.cfg.ConfigPath())
|
||||
os.RemoveAll(f.Filesystem(nil).URI())
|
||||
}
|
||||
|
||||
// Layout of the files: (indexes from the above array)
|
||||
// 12345678 - Required file
|
||||
// 02005008 - Existing file (currently in the index)
|
||||
@@ -129,8 +144,8 @@ func TestHandleFile(t *testing.T) {
|
||||
requiredFile := existingFile
|
||||
requiredFile.Blocks = blocks[1:]
|
||||
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
|
||||
defer wcfgCancel()
|
||||
|
||||
copyChan := make(chan copyBlocksState, 1)
|
||||
|
||||
@@ -171,8 +186,8 @@ func TestHandleFileWithTemp(t *testing.T) {
|
||||
requiredFile := existingFile
|
||||
requiredFile.Blocks = blocks[1:]
|
||||
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
|
||||
defer wcfgCancel()
|
||||
|
||||
if _, err := prepareTmpFile(f.Filesystem(nil)); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -205,111 +220,101 @@ func TestHandleFileWithTemp(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCopierFinder(t *testing.T) {
|
||||
methods := []fs.CopyRangeMethod{fs.CopyRangeMethodStandard, fs.CopyRangeMethodAllWithFallback}
|
||||
if build.IsLinux {
|
||||
methods = append(methods, fs.CopyRangeMethodSendFile)
|
||||
// After diff between required and existing we should:
|
||||
// Copy: 1, 2, 3, 4, 6, 7, 8
|
||||
// Since there is no existing file, nor a temp file
|
||||
|
||||
// After dropping out blocks found locally:
|
||||
// Pull: 1, 5, 6, 8
|
||||
|
||||
tempFile := fs.TempName("file2")
|
||||
|
||||
existingBlocks := []int{0, 2, 3, 4, 0, 0, 7, 0}
|
||||
existingFile := setupFile(fs.TempName("file"), existingBlocks)
|
||||
existingFile.Size = 1
|
||||
requiredFile := existingFile
|
||||
requiredFile.Blocks = blocks[1:]
|
||||
requiredFile.Name = "file2"
|
||||
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
|
||||
defer wcfgCancel()
|
||||
|
||||
if _, err := prepareTmpFile(f.Filesystem(nil)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, method := range methods {
|
||||
t.Run(method.String(), func(t *testing.T) {
|
||||
// After diff between required and existing we should:
|
||||
// Copy: 1, 2, 3, 4, 6, 7, 8
|
||||
// Since there is no existing file, nor a temp file
|
||||
|
||||
// After dropping out blocks found locally:
|
||||
// Pull: 1, 5, 6, 8
|
||||
copyChan := make(chan copyBlocksState)
|
||||
pullChan := make(chan pullBlockState, 4)
|
||||
finisherChan := make(chan *sharedPullerState, 1)
|
||||
|
||||
tempFile := fs.TempName("file2")
|
||||
// Run a single fetcher routine
|
||||
go f.copierRoutine(copyChan, pullChan, finisherChan)
|
||||
defer close(copyChan)
|
||||
|
||||
existingBlocks := []int{0, 2, 3, 4, 0, 0, 7, 0}
|
||||
existingFile := setupFile(fs.TempName("file"), existingBlocks)
|
||||
existingFile.Size = 1
|
||||
requiredFile := existingFile
|
||||
requiredFile.Blocks = blocks[1:]
|
||||
requiredFile.Name = "file2"
|
||||
f.handleFile(requiredFile, fsetSnapshot(t, f.fset), copyChan)
|
||||
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t, existingFile)
|
||||
f.CopyRangeMethod = method
|
||||
timeout := time.After(10 * time.Second)
|
||||
pulls := make([]pullBlockState, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
select {
|
||||
case pulls[i] = <-pullChan:
|
||||
case <-timeout:
|
||||
t.Fatalf("Timed out before receiving all 4 states on pullChan (already got %v)", i)
|
||||
}
|
||||
}
|
||||
var finish *sharedPullerState
|
||||
select {
|
||||
case finish = <-finisherChan:
|
||||
case <-timeout:
|
||||
t.Fatal("Timed out before receiving 4 states on pullChan")
|
||||
}
|
||||
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
defer cleanupSharedPullerState(finish)
|
||||
|
||||
if _, err := prepareTmpFile(f.Filesystem(nil)); err != nil {
|
||||
t.Fatal(err)
|
||||
select {
|
||||
case <-pullChan:
|
||||
t.Fatal("Pull channel has data to be read")
|
||||
case <-finisherChan:
|
||||
t.Fatal("Finisher channel has data to be read")
|
||||
default:
|
||||
}
|
||||
|
||||
// Verify that the right blocks went into the pull list.
|
||||
// They are pulled in random order.
|
||||
for _, idx := range []int{1, 5, 6, 8} {
|
||||
found := false
|
||||
block := blocks[idx]
|
||||
for _, pulledBlock := range pulls {
|
||||
if bytes.Equal(pulledBlock.block.Hash, block.Hash) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Did not find block %s", block.String())
|
||||
}
|
||||
if !bytes.Equal(finish.file.Blocks[idx-1].Hash, blocks[idx].Hash) {
|
||||
t.Errorf("Block %d mismatch: %s != %s", idx, finish.file.Blocks[idx-1].String(), blocks[idx].String())
|
||||
}
|
||||
}
|
||||
|
||||
copyChan := make(chan copyBlocksState)
|
||||
pullChan := make(chan pullBlockState, 4)
|
||||
finisherChan := make(chan *sharedPullerState, 1)
|
||||
// Verify that the fetched blocks have actually been written to the temp file
|
||||
blks, err := scanner.HashFile(context.TODO(), f.Filesystem(nil), tempFile, protocol.MinBlockSize, nil, false)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
// Run a single fetcher routine
|
||||
go f.copierRoutine(copyChan, pullChan, finisherChan)
|
||||
defer close(copyChan)
|
||||
|
||||
f.handleFile(requiredFile, fsetSnapshot(t, f.fset), copyChan)
|
||||
|
||||
timeout := time.After(10 * time.Second)
|
||||
pulls := make([]pullBlockState, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
select {
|
||||
case pulls[i] = <-pullChan:
|
||||
case <-timeout:
|
||||
t.Fatalf("Timed out before receiving all 4 states on pullChan (already got %v)", i)
|
||||
}
|
||||
}
|
||||
var finish *sharedPullerState
|
||||
select {
|
||||
case finish = <-finisherChan:
|
||||
case <-timeout:
|
||||
t.Fatal("Timed out before receiving 4 states on pullChan")
|
||||
}
|
||||
|
||||
defer cleanupSharedPullerState(finish)
|
||||
|
||||
select {
|
||||
case <-pullChan:
|
||||
t.Fatal("Pull channel has data to be read")
|
||||
case <-finisherChan:
|
||||
t.Fatal("Finisher channel has data to be read")
|
||||
default:
|
||||
}
|
||||
|
||||
// Verify that the right blocks went into the pull list.
|
||||
// They are pulled in random order.
|
||||
for _, idx := range []int{1, 5, 6, 8} {
|
||||
found := false
|
||||
block := blocks[idx]
|
||||
for _, pulledBlock := range pulls {
|
||||
if bytes.Equal(pulledBlock.block.Hash, block.Hash) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Did not find block %s", block.String())
|
||||
}
|
||||
if !bytes.Equal(finish.file.Blocks[idx-1].Hash, blocks[idx].Hash) {
|
||||
t.Errorf("Block %d mismatch: %s != %s", idx, finish.file.Blocks[idx-1].String(), blocks[idx].String())
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the fetched blocks have actually been written to the temp file
|
||||
blks, err := scanner.HashFile(context.TODO(), f.Filesystem(nil), tempFile, protocol.MinBlockSize, nil, false)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
for _, eq := range []int{2, 3, 4, 7} {
|
||||
if !bytes.Equal(blks[eq-1].Hash, blocks[eq].Hash) {
|
||||
t.Errorf("Block %d mismatch: %s != %s", eq, blks[eq-1].String(), blocks[eq].String())
|
||||
}
|
||||
}
|
||||
})
|
||||
for _, eq := range []int{2, 3, 4, 7} {
|
||||
if !bytes.Equal(blks[eq-1].Hash, blocks[eq].Hash) {
|
||||
t.Errorf("Block %d mismatch: %s != %s", eq, blks[eq-1].String(), blocks[eq].String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWeakHash(t *testing.T) {
|
||||
// Setup the model/pull environment
|
||||
model, fo, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(fo, model, wcfgCancel)
|
||||
_, fo, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
ffs := fo.Filesystem(nil)
|
||||
|
||||
tempFile := fs.TempName("weakhash")
|
||||
@@ -438,7 +443,7 @@ func TestCopierCleanup(t *testing.T) {
|
||||
file := setupFile("test", []int{0})
|
||||
file.Size = 1
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t, file)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
defer wcfgCancel()
|
||||
|
||||
file.Blocks = []protocol.BlockInfo{blocks[1]}
|
||||
file.Version = file.Version.Update(myID.Short())
|
||||
@@ -471,7 +476,7 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
|
||||
file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8})
|
||||
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
defer wcfgCancel()
|
||||
|
||||
// Set up our evet subscription early
|
||||
s := m.evLogger.Subscribe(events.ItemFinished)
|
||||
@@ -571,7 +576,7 @@ func TestDeregisterOnFailInPull(t *testing.T) {
|
||||
file := setupFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8})
|
||||
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
defer wcfgCancel()
|
||||
|
||||
// Set up our evet subscription early
|
||||
s := m.evLogger.Subscribe(events.ItemFinished)
|
||||
@@ -673,16 +678,15 @@ func TestDeregisterOnFailInPull(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue3164(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
ffs := f.Filesystem(nil)
|
||||
tmpDir := ffs.URI()
|
||||
|
||||
ignDir := filepath.Join("issue3164", "oktodelete")
|
||||
subDir := filepath.Join(ignDir, "foobar")
|
||||
must(t, ffs.MkdirAll(subDir, 0777))
|
||||
must(t, os.WriteFile(filepath.Join(tmpDir, subDir, "file"), []byte("Hello"), 0644))
|
||||
must(t, os.WriteFile(filepath.Join(tmpDir, ignDir, "file"), []byte("Hello"), 0644))
|
||||
must(t, ffs.MkdirAll(subDir, 0o777))
|
||||
must(t, fs.WriteFile(ffs, filepath.Join(subDir, "file"), []byte("Hello"), 0o644))
|
||||
must(t, fs.WriteFile(ffs, filepath.Join(ignDir, "file"), []byte("Hello"), 0o644))
|
||||
file := protocol.FileInfo{
|
||||
Name: "issue3164",
|
||||
}
|
||||
@@ -764,8 +768,8 @@ func TestDiffEmpty(t *testing.T) {
|
||||
// option is true and the permissions do not match between the file on disk and
|
||||
// in the db.
|
||||
func TestDeleteIgnorePerms(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
ffs := f.Filesystem(nil)
|
||||
f.IgnorePerms = true
|
||||
|
||||
@@ -780,7 +784,7 @@ func TestDeleteIgnorePerms(t *testing.T) {
|
||||
must(t, err)
|
||||
fi, err := scanner.CreateFileInfo(stat, name, ffs, false, false, config.XattrFilter{})
|
||||
must(t, err)
|
||||
ffs.Chmod(name, 0600)
|
||||
ffs.Chmod(name, 0o600)
|
||||
if info, err := ffs.Stat(name); err == nil {
|
||||
fi.InodeChangeNs = info.InodeChangeTime().UnixNano()
|
||||
}
|
||||
@@ -806,7 +810,7 @@ func TestCopyOwner(t *testing.T) {
|
||||
// filesystem.
|
||||
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
defer wcfgCancel()
|
||||
f.folder.FolderConfiguration = newFolderConfiguration(m.cfg, f.ID, f.Label, fs.FilesystemTypeFake, "/TestCopyOwner")
|
||||
f.folder.FolderConfiguration.CopyOwnershipFromParent = true
|
||||
|
||||
@@ -815,13 +819,13 @@ func TestCopyOwner(t *testing.T) {
|
||||
|
||||
// Create a parent dir with a certain owner/group.
|
||||
|
||||
f.mtimefs.Mkdir("foo", 0755)
|
||||
f.mtimefs.Mkdir("foo", 0o755)
|
||||
f.mtimefs.Lchown("foo", strconv.Itoa(expOwner), strconv.Itoa(expGroup))
|
||||
|
||||
dir := protocol.FileInfo{
|
||||
Name: "foo/bar",
|
||||
Type: protocol.FileInfoTypeDirectory,
|
||||
Permissions: 0755,
|
||||
Permissions: 0o755,
|
||||
}
|
||||
|
||||
// Have the folder create a subdirectory, verify that it's the correct
|
||||
@@ -851,7 +855,7 @@ func TestCopyOwner(t *testing.T) {
|
||||
file := protocol.FileInfo{
|
||||
Name: "foo/bar/baz",
|
||||
Type: protocol.FileInfoTypeFile,
|
||||
Permissions: 0644,
|
||||
Permissions: 0o644,
|
||||
}
|
||||
|
||||
// Wire some stuff. The flow here is handleFile() -[copierChan]->
|
||||
@@ -885,7 +889,7 @@ func TestCopyOwner(t *testing.T) {
|
||||
symlink := protocol.FileInfo{
|
||||
Name: "foo/bar/sym",
|
||||
Type: protocol.FileInfoTypeSymlink,
|
||||
Permissions: 0644,
|
||||
Permissions: 0o644,
|
||||
SymlinkTarget: "over the rainbow",
|
||||
}
|
||||
|
||||
@@ -908,8 +912,8 @@ func TestCopyOwner(t *testing.T) {
|
||||
// TestSRConflictReplaceFileByDir checks that a conflict is created when an existing file
|
||||
// is replaced with a directory and versions are conflicting
|
||||
func TestSRConflictReplaceFileByDir(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
ffs := f.Filesystem(nil)
|
||||
|
||||
name := "foo"
|
||||
@@ -940,8 +944,8 @@ func TestSRConflictReplaceFileByDir(t *testing.T) {
|
||||
// TestSRConflictReplaceFileByLink checks that a conflict is created when an existing file
|
||||
// is replaced with a link and versions are conflicting
|
||||
func TestSRConflictReplaceFileByLink(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
ffs := f.Filesystem(nil)
|
||||
|
||||
name := "foo"
|
||||
@@ -973,30 +977,19 @@ func TestSRConflictReplaceFileByLink(t *testing.T) {
|
||||
// TestDeleteBehindSymlink checks that we don't delete or schedule a scan
|
||||
// when trying to delete a file behind a symlink.
|
||||
func TestDeleteBehindSymlink(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
ffs := f.Filesystem(nil)
|
||||
|
||||
destDir := t.TempDir()
|
||||
destFs := fs.NewFilesystem(fs.FilesystemTypeBasic, destDir)
|
||||
|
||||
link := "link"
|
||||
file := filepath.Join(link, "file")
|
||||
linkFile := filepath.Join(link, "file")
|
||||
|
||||
must(t, ffs.MkdirAll(link, 0755))
|
||||
fi := createEmptyFileInfo(t, file, ffs)
|
||||
must(t, ffs.MkdirAll(link, 0o755))
|
||||
fi := createEmptyFileInfo(t, linkFile, ffs)
|
||||
f.updateLocalsFromScanning([]protocol.FileInfo{fi})
|
||||
must(t, osutil.RenameOrCopy(fs.CopyRangeMethodStandard, ffs, destFs, file, "file"))
|
||||
must(t, ffs.Rename(linkFile, "file"))
|
||||
must(t, ffs.RemoveAll(link))
|
||||
|
||||
if err := fs.DebugSymlinkForTestsOnly(destFs, ffs, "", link); err != nil {
|
||||
if build.IsWindows {
|
||||
// Probably we require permissions we don't have.
|
||||
t.Skip("Need admin permissions or developer mode to run symlink test on Windows: " + err.Error())
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
must(t, ffs.CreateSymlink("/", link))
|
||||
|
||||
fi.Deleted = true
|
||||
fi.Version = fi.Version.Update(device1.Short())
|
||||
@@ -1016,15 +1009,15 @@ func TestDeleteBehindSymlink(t *testing.T) {
|
||||
default:
|
||||
t.Fatalf("No db update received")
|
||||
}
|
||||
if _, err := destFs.Stat("file"); err != nil {
|
||||
if _, err := ffs.Stat("file"); err != nil {
|
||||
t.Errorf("Expected no error when stating file behind symlink, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Reproduces https://github.com/syncthing/syncthing/issues/6559
|
||||
func TestPullCtxCancel(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
|
||||
pullChan := make(chan pullBlockState)
|
||||
finisherChan := make(chan *sharedPullerState)
|
||||
@@ -1065,12 +1058,12 @@ func TestPullCtxCancel(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPullDeleteUnscannedDir(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
ffs := f.Filesystem(nil)
|
||||
|
||||
dir := "foobar"
|
||||
must(t, ffs.MkdirAll(dir, 0777))
|
||||
must(t, ffs.MkdirAll(dir, 0o777))
|
||||
fi := protocol.FileInfo{
|
||||
Name: dir,
|
||||
}
|
||||
@@ -1095,7 +1088,7 @@ func TestPullDeleteUnscannedDir(t *testing.T) {
|
||||
|
||||
func TestPullCaseOnlyPerformFinish(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
defer wcfgCancel()
|
||||
ffs := f.Filesystem(nil)
|
||||
|
||||
name := "foo"
|
||||
@@ -1157,12 +1150,12 @@ func TestPullCaseOnlySymlink(t *testing.T) {
|
||||
|
||||
func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
defer wcfgCancel()
|
||||
ffs := f.Filesystem(nil)
|
||||
|
||||
name := "foo"
|
||||
if dir {
|
||||
must(t, ffs.Mkdir(name, 0777))
|
||||
must(t, ffs.Mkdir(name, 0o777))
|
||||
} else {
|
||||
must(t, ffs.CreateSymlink("target", name))
|
||||
}
|
||||
@@ -1212,8 +1205,8 @@ func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) {
|
||||
}
|
||||
|
||||
func TestPullTempFileCaseConflict(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
|
||||
copyChan := make(chan copyBlocksState, 1)
|
||||
|
||||
@@ -1241,7 +1234,7 @@ func TestPullTempFileCaseConflict(t *testing.T) {
|
||||
|
||||
func TestPullCaseOnlyRename(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
defer wcfgCancel()
|
||||
|
||||
// tempNameConfl := fs.TempName(confl)
|
||||
|
||||
@@ -1284,7 +1277,7 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) {
|
||||
}
|
||||
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
defer wcfgCancel()
|
||||
addFakeConn(m, device1, f.ID)
|
||||
|
||||
name := "foo"
|
||||
@@ -1325,8 +1318,8 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPullDeleteCaseConflict(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
|
||||
name := "foo"
|
||||
fi := protocol.FileInfo{Name: "Foo"}
|
||||
@@ -1359,8 +1352,8 @@ func TestPullDeleteCaseConflict(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPullDeleteIgnoreChildDir(t *testing.T) {
|
||||
m, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer cleanupSRFolder(f, m, wcfgCancel)
|
||||
_, f, wcfgCancel := setupSendReceiveFolder(t)
|
||||
defer wcfgCancel()
|
||||
|
||||
parent := "parent"
|
||||
del := "ignored"
|
||||
@@ -1372,9 +1365,9 @@ func TestPullDeleteIgnoreChildDir(t *testing.T) {
|
||||
`, child, del)), ""))
|
||||
f.ignores = matcher
|
||||
|
||||
must(t, f.mtimefs.Mkdir(parent, 0777))
|
||||
must(t, f.mtimefs.Mkdir(filepath.Join(parent, del), 0777))
|
||||
must(t, f.mtimefs.Mkdir(filepath.Join(parent, del, child), 0777))
|
||||
must(t, f.mtimefs.Mkdir(parent, 0o777))
|
||||
must(t, f.mtimefs.Mkdir(filepath.Join(parent, del), 0o777))
|
||||
must(t, f.mtimefs.Mkdir(filepath.Join(parent, del, child), 0o777))
|
||||
|
||||
scanChan := make(chan string, 2)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
)
|
||||
|
||||
type unifySubsCase struct {
|
||||
@@ -156,8 +157,7 @@ func TestSetPlatformData(t *testing.T) {
|
||||
// Checks that setPlatformData runs without error when applied to a temp
|
||||
// file, named differently than the given FileInfo.
|
||||
|
||||
dir := t.TempDir()
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32))
|
||||
if fd, err := fs.Create("file.tmp"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
@@ -167,7 +167,7 @@ func TestSetPlatformData(t *testing.T) {
|
||||
xattr := []protocol.Xattr{{Name: "user.foo", Value: []byte("bar")}}
|
||||
fi := &protocol.FileInfo{
|
||||
Name: "should be ignored",
|
||||
Permissions: 0400,
|
||||
Permissions: 0o400,
|
||||
ModifiedS: 1234567890,
|
||||
Platform: protocol.PlatformData{
|
||||
Linux: &protocol.XattrData{Xattrs: xattr},
|
||||
|
||||
+5
-5
@@ -1220,7 +1220,7 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
|
||||
haveFcfg := cfg.FolderMap()
|
||||
for _, folder := range cm.Folders {
|
||||
from, ok := haveFcfg[folder.ID]
|
||||
if to, changed := m.handleAutoAccepts(deviceID, folder, ccDeviceInfos[folder.ID], from, ok, cfg.Defaults.Folder.Path); changed {
|
||||
if to, changed := m.handleAutoAccepts(deviceID, folder, ccDeviceInfos[folder.ID], from, ok, cfg.Defaults.Folder); changed {
|
||||
changedFcfg[folder.ID] = to
|
||||
}
|
||||
}
|
||||
@@ -1664,9 +1664,9 @@ func (*model) handleDeintroductions(introducerCfg config.DeviceConfiguration, fo
|
||||
|
||||
// handleAutoAccepts handles adding and sharing folders for devices that have
|
||||
// AutoAcceptFolders set to true.
|
||||
func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *clusterConfigDeviceInfo, cfg config.FolderConfiguration, haveCfg bool, defaultPath string) (config.FolderConfiguration, bool) {
|
||||
func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *clusterConfigDeviceInfo, cfg config.FolderConfiguration, haveCfg bool, defaultFolderCfg config.FolderConfiguration) (config.FolderConfiguration, bool) {
|
||||
if !haveCfg {
|
||||
defaultPathFs := fs.NewFilesystem(fs.FilesystemTypeBasic, defaultPath)
|
||||
defaultPathFs := fs.NewFilesystem(defaultFolderCfg.FilesystemType, defaultFolderCfg.Path)
|
||||
var pathAlternatives []string
|
||||
if alt := fs.SanitizePath(folder.Label); alt != "" {
|
||||
pathAlternatives = append(pathAlternatives, alt)
|
||||
@@ -1685,13 +1685,13 @@ func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Fo
|
||||
}
|
||||
|
||||
// Attempt to create it to make sure it does, now.
|
||||
fullPath := filepath.Join(defaultPath, path)
|
||||
fullPath := filepath.Join(defaultFolderCfg.Path, path)
|
||||
if err := defaultPathFs.MkdirAll(path, 0o700); err != nil {
|
||||
l.Warnf("Failed to create path for auto-accepted folder %s at path %s: %v", folder.Description(), fullPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fcfg := newFolderConfiguration(m.cfg, folder.ID, folder.Label, fs.FilesystemTypeBasic, fullPath)
|
||||
fcfg := newFolderConfiguration(m.cfg, folder.ID, folder.Label, defaultFolderCfg.FilesystemType, fullPath)
|
||||
fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{
|
||||
DeviceID: deviceID,
|
||||
})
|
||||
|
||||
+322
-559
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,7 @@ func TestProgressEmitter(t *testing.T) {
|
||||
|
||||
w := evLogger.Subscribe(events.DownloadProgress)
|
||||
|
||||
c, cfgCancel := createTmpWrapper(config.Configuration{})
|
||||
c, cfgCancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
|
||||
defer os.Remove(c.ConfigPath())
|
||||
defer cfgCancel()
|
||||
waiter, err := c.Modify(func(cfg *config.Configuration) {
|
||||
@@ -110,11 +110,10 @@ func TestProgressEmitter(t *testing.T) {
|
||||
|
||||
expectEvent(w, t, 0)
|
||||
expectTimeout(w, t)
|
||||
|
||||
}
|
||||
|
||||
func TestSendDownloadProgressMessages(t *testing.T) {
|
||||
c, cfgCancel := createTmpWrapper(config.Configuration{})
|
||||
c, cfgCancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
|
||||
defer os.Remove(c.ConfigPath())
|
||||
defer cfgCancel()
|
||||
waiter, err := c.Modify(func(cfg *config.Configuration) {
|
||||
@@ -455,8 +454,8 @@ func TestSendDownloadProgressMessages(t *testing.T) {
|
||||
|
||||
// See progressemitter.go for explanation why this is commented out.
|
||||
// Search for state.cleanup
|
||||
//expect(-1, state2, protocol.FileDownloadProgressUpdateTypeForget, v1, nil, false)
|
||||
//expect(-1, state4, protocol.FileDownloadProgressUpdateTypeForget, v1, nil, true)
|
||||
// expect(-1, state2, protocol.FileDownloadProgressUpdateTypeForget, v1, nil, false)
|
||||
// expect(-1, state4, protocol.FileDownloadProgressUpdateTypeForget, v1, nil, true)
|
||||
|
||||
expectEmpty()
|
||||
|
||||
|
||||
+27
-96
@@ -10,7 +10,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -56,6 +56,7 @@ func TestRequestSimple(t *testing.T) {
|
||||
// Send an update for the test file, wait for it to sync and be reported back.
|
||||
contents := []byte("test file contents\n")
|
||||
fc.addFile("testfile", 0o644, protocol.FileInfoTypeFile, contents)
|
||||
fc.addFile("testfile", 0o644, protocol.FileInfoTypeFile, contents)
|
||||
fc.sendIndexUpdate()
|
||||
select {
|
||||
case <-done:
|
||||
@@ -64,7 +65,7 @@ func TestRequestSimple(t *testing.T) {
|
||||
}
|
||||
|
||||
// Verify the contents
|
||||
if err := equalContents(filepath.Join(tfs.URI(), "testfile"), contents); err != nil {
|
||||
if err := equalContents(tfs, "testfile", contents); err != nil {
|
||||
t.Error("File did not sync correctly:", err)
|
||||
}
|
||||
}
|
||||
@@ -213,76 +214,6 @@ func TestRequestCreateTmpSymlink(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestVersioningSymlinkAttack(t *testing.T) {
|
||||
if build.IsWindows {
|
||||
t.Skip("no symlink support on Windows")
|
||||
}
|
||||
|
||||
// Sets up a folder with trashcan versioning and tries to use a
|
||||
// deleted symlink to escape
|
||||
|
||||
w, fcfg, wCancel := tmpDefaultWrapper(t)
|
||||
defer wCancel()
|
||||
defer func() {
|
||||
os.RemoveAll(fcfg.Filesystem(nil).URI())
|
||||
os.Remove(w.ConfigPath())
|
||||
}()
|
||||
|
||||
fcfg.Versioning = config.VersioningConfiguration{Type: "trashcan"}
|
||||
setFolder(t, w, fcfg)
|
||||
m, fc := setupModelWithConnectionFromWrapper(t, w)
|
||||
defer cleanupModel(m)
|
||||
|
||||
// Create a temporary directory that we will use as target to see if
|
||||
// we can escape to it
|
||||
tmpdir := t.TempDir()
|
||||
|
||||
// We listen for incoming index updates and trigger when we see one for
|
||||
// the expected test file.
|
||||
idx := make(chan int)
|
||||
fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
|
||||
idx <- len(fs)
|
||||
return nil
|
||||
})
|
||||
|
||||
waitForIdx := func() {
|
||||
select {
|
||||
case c := <-idx:
|
||||
if c == 0 {
|
||||
t.Fatal("Got empty index update")
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("timed out before receiving index update")
|
||||
}
|
||||
}
|
||||
|
||||
// Send an update for the test file, wait for it to sync and be reported back.
|
||||
fc.addFile("foo", 0o644, protocol.FileInfoTypeSymlink, []byte(tmpdir))
|
||||
fc.sendIndexUpdate()
|
||||
waitForIdx()
|
||||
|
||||
// Delete the symlink, hoping for it to get versioned
|
||||
fc.deleteFile("foo")
|
||||
fc.sendIndexUpdate()
|
||||
waitForIdx()
|
||||
|
||||
// Recreate foo and a file in it with some data
|
||||
fc.updateFile("foo", 0o755, protocol.FileInfoTypeDirectory, nil)
|
||||
fc.addFile("foo/test", 0o644, protocol.FileInfoTypeFile, []byte("testtesttest"))
|
||||
fc.sendIndexUpdate()
|
||||
waitForIdx()
|
||||
|
||||
// Remove the test file and see if it escaped
|
||||
fc.deleteFile("foo/test")
|
||||
fc.sendIndexUpdate()
|
||||
waitForIdx()
|
||||
|
||||
path := filepath.Join(tmpdir, "test")
|
||||
if _, err := os.Lstat(path); !os.IsNotExist(err) {
|
||||
t.Fatal("File escaped to", path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPullInvalidIgnoredSO(t *testing.T) {
|
||||
t.Skip("flaky")
|
||||
pullInvalidIgnored(t, config.FolderTypeSendOnly)
|
||||
@@ -295,9 +226,9 @@ func TestPullInvalidIgnoredSR(t *testing.T) {
|
||||
|
||||
// This test checks that (un-)ignored/invalid/deleted files are treated as expected.
|
||||
func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
|
||||
w, wCancel := createTmpWrapper(defaultCfgWrapper.RawCopy())
|
||||
w, wCancel := newConfigWrapper(defaultCfgWrapper.RawCopy())
|
||||
defer wCancel()
|
||||
fcfg := testFolderConfig(t.TempDir())
|
||||
fcfg := w.FolderList()[0]
|
||||
fss := fcfg.Filesystem(nil)
|
||||
fcfg.Type = ft
|
||||
setFolder(t, w, fcfg)
|
||||
@@ -676,10 +607,17 @@ func TestRequestSymlinkWindows(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func equalContents(path string, contents []byte) error {
|
||||
if bs, err := os.ReadFile(path); err != nil {
|
||||
func equalContents(fs fs.Filesystem, path string, contents []byte) error {
|
||||
fd, err := fs.Open(path)
|
||||
defer fd.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !bytes.Equal(bs, contents) {
|
||||
}
|
||||
bs, err := io.ReadAll(fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(bs, contents) {
|
||||
return errors.New("incorrect data")
|
||||
}
|
||||
return nil
|
||||
@@ -691,8 +629,7 @@ func TestRequestRemoteRenameChanged(t *testing.T) {
|
||||
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
|
||||
defer wcfgCancel()
|
||||
tfs := fcfg.Filesystem(nil)
|
||||
tmpDir := tfs.URI()
|
||||
defer cleanupModelAndRemoveDir(m, tfs.URI())
|
||||
defer cleanupModel(m)
|
||||
|
||||
received := make(chan []protocol.FileInfo)
|
||||
fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
|
||||
@@ -727,7 +664,7 @@ func TestRequestRemoteRenameChanged(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, n := range [2]string{a, b} {
|
||||
must(t, equalContents(filepath.Join(tmpDir, n), data[n]))
|
||||
must(t, equalContents(tfs, n, data[n]))
|
||||
}
|
||||
|
||||
var gotA, gotB, gotConfl bool
|
||||
@@ -806,11 +743,11 @@ func TestRequestRemoteRenameChanged(t *testing.T) {
|
||||
case path == a:
|
||||
t.Errorf(`File "a" was not removed`)
|
||||
case path == b:
|
||||
if err := equalContents(filepath.Join(tmpDir, b), data[a]); err != nil {
|
||||
if err := equalContents(tfs, b, data[a]); err != nil {
|
||||
t.Error(`File "b" has unexpected content (renamed from a on remote)`)
|
||||
}
|
||||
case strings.HasPrefix(path, b+".sync-conflict-"):
|
||||
if err := equalContents(filepath.Join(tmpDir, path), otherData); err != nil {
|
||||
if err := equalContents(tfs, path, otherData); err != nil {
|
||||
t.Error(`Sync conflict of "b" has unexptected content`)
|
||||
}
|
||||
case path == "." || strings.HasPrefix(path, ".stfolder"):
|
||||
@@ -825,8 +762,7 @@ func TestRequestRemoteRenameConflict(t *testing.T) {
|
||||
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
|
||||
defer wcfgCancel()
|
||||
tfs := fcfg.Filesystem(nil)
|
||||
tmpDir := tfs.URI()
|
||||
defer cleanupModelAndRemoveDir(m, tmpDir)
|
||||
defer cleanupModel(m)
|
||||
|
||||
recv := make(chan int)
|
||||
fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
|
||||
@@ -855,7 +791,7 @@ func TestRequestRemoteRenameConflict(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, n := range [2]string{a, b} {
|
||||
must(t, equalContents(filepath.Join(tmpDir, n), data[n]))
|
||||
must(t, equalContents(tfs, n, data[n]))
|
||||
}
|
||||
|
||||
fd, err := tfs.OpenFile(b, fs.OptReadWrite, 0o644)
|
||||
@@ -983,9 +919,7 @@ func TestRequestDeleteChanged(t *testing.T) {
|
||||
func TestNeedFolderFiles(t *testing.T) {
|
||||
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
|
||||
defer wcfgCancel()
|
||||
tfs := fcfg.Filesystem(nil)
|
||||
tmpDir := tfs.URI()
|
||||
defer cleanupModelAndRemoveDir(m, tmpDir)
|
||||
defer cleanupModel(m)
|
||||
|
||||
sub := m.evLogger.Subscribe(events.RemoteIndexUpdated)
|
||||
defer sub.Unsubscribe()
|
||||
@@ -1028,12 +962,11 @@ func TestNeedFolderFiles(t *testing.T) {
|
||||
// propagated upon un-ignoring.
|
||||
// https://github.com/syncthing/syncthing/issues/6038
|
||||
func TestIgnoreDeleteUnignore(t *testing.T) {
|
||||
w, fcfg, wCancel := tmpDefaultWrapper(t)
|
||||
w, fcfg, wCancel := newDefaultCfgWrapper()
|
||||
defer wCancel()
|
||||
m := setupModel(t, w)
|
||||
fss := fcfg.Filesystem(nil)
|
||||
tmpDir := fss.URI()
|
||||
defer cleanupModelAndRemoveDir(m, tmpDir)
|
||||
defer cleanupModel(m)
|
||||
|
||||
folderIgnoresAlwaysReload(t, m, fcfg)
|
||||
m.ScanFolders()
|
||||
@@ -1272,7 +1205,7 @@ func TestRequestIndexSenderPause(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
|
||||
w, fcfg, wCancel := tmpDefaultWrapper(t)
|
||||
w, fcfg, wCancel := newDefaultCfgWrapper()
|
||||
defer wCancel()
|
||||
tfs := fcfg.Filesystem(nil)
|
||||
dir1 := "foo"
|
||||
@@ -1339,15 +1272,13 @@ func TestRequestReceiveEncrypted(t *testing.T) {
|
||||
t.Skip("skipping on short testing - scrypt is too slow")
|
||||
}
|
||||
|
||||
w, fcfg, wCancel := tmpDefaultWrapper(t)
|
||||
w, fcfg, wCancel := newDefaultCfgWrapper()
|
||||
defer wCancel()
|
||||
tfs := fcfg.Filesystem(nil)
|
||||
fcfg.Type = config.FolderTypeReceiveEncrypted
|
||||
setFolder(t, w, fcfg)
|
||||
|
||||
keyGen := protocol.NewKeyGenerator()
|
||||
encToken := protocol.PasswordToken(keyGen, fcfg.ID, "pw")
|
||||
must(t, tfs.Mkdir(config.DefaultMarkerName, 0o777))
|
||||
encToken := protocol.PasswordToken(protocol.NewKeyGenerator(), fcfg.ID, "pw")
|
||||
must(t, writeEncryptionToken(encToken, fcfg))
|
||||
|
||||
m := setupModel(t, w)
|
||||
|
||||
@@ -7,25 +7,21 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
|
||||
// Test creating temporary file inside read-only directory
|
||||
func TestReadOnlyDir(t *testing.T) {
|
||||
// Create a read only directory, clean it up afterwards.
|
||||
tmpDir := t.TempDir()
|
||||
if err := os.Chmod(tmpDir, 0555); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Chmod(tmpDir, 0755)
|
||||
ffs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32))
|
||||
ffs.Mkdir("testdir", 0o555)
|
||||
|
||||
s := sharedPullerState{
|
||||
fs: fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir),
|
||||
tempName: ".temp_name",
|
||||
fs: ffs,
|
||||
tempName: "testdir/.temp_name",
|
||||
mut: sync.NewRWMutex(),
|
||||
}
|
||||
|
||||
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
foobarbaz
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
baazquux
|
||||
Vendored
Vendored
-1
@@ -1 +0,0 @@
|
||||
foobar
|
||||
@@ -7,9 +7,6 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
)
|
||||
|
||||
@@ -19,10 +16,6 @@ type fatal interface {
|
||||
Helper()
|
||||
}
|
||||
|
||||
type fatalOs struct {
|
||||
fatal
|
||||
}
|
||||
|
||||
func must(f fatal, err error) {
|
||||
f.Helper()
|
||||
if err != nil {
|
||||
@@ -36,56 +29,3 @@ func mustRemove(f fatal, err error) {
|
||||
f.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fatalOs) Chmod(name string, mode os.FileMode) {
|
||||
f.Helper()
|
||||
must(f, os.Chmod(name, mode))
|
||||
}
|
||||
|
||||
func (f *fatalOs) Chtimes(name string, atime time.Time, mtime time.Time) {
|
||||
f.Helper()
|
||||
must(f, os.Chtimes(name, atime, mtime))
|
||||
}
|
||||
|
||||
func (f *fatalOs) Create(name string) *os.File {
|
||||
f.Helper()
|
||||
file, err := os.Create(name)
|
||||
must(f, err)
|
||||
return file
|
||||
}
|
||||
|
||||
func (f *fatalOs) Mkdir(name string, perm os.FileMode) {
|
||||
f.Helper()
|
||||
must(f, os.Mkdir(name, perm))
|
||||
}
|
||||
|
||||
func (f *fatalOs) MkdirAll(name string, perm os.FileMode) {
|
||||
f.Helper()
|
||||
must(f, os.MkdirAll(name, perm))
|
||||
}
|
||||
|
||||
func (f *fatalOs) Remove(name string) {
|
||||
f.Helper()
|
||||
if err := os.Remove(name); err != nil && !os.IsNotExist(err) {
|
||||
f.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fatalOs) RemoveAll(name string) {
|
||||
f.Helper()
|
||||
if err := os.RemoveAll(name); err != nil && !os.IsNotExist(err) {
|
||||
f.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fatalOs) Rename(oldname, newname string) {
|
||||
f.Helper()
|
||||
must(f, os.Rename(oldname, newname))
|
||||
}
|
||||
|
||||
func (f *fatalOs) Stat(name string) os.FileInfo {
|
||||
f.Helper()
|
||||
info, err := os.Stat(name)
|
||||
must(f, err)
|
||||
return info
|
||||
}
|
||||
|
||||
+16
-24
@@ -27,7 +27,6 @@ var (
|
||||
defaultCfgWrapper config.Wrapper
|
||||
defaultCfgWrapperCancel context.CancelFunc
|
||||
defaultFolderConfig config.FolderConfiguration
|
||||
defaultFs fs.Filesystem
|
||||
defaultCfg config.Configuration
|
||||
defaultAutoAcceptCfg config.Configuration
|
||||
)
|
||||
@@ -37,10 +36,11 @@ func init() {
|
||||
device1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
|
||||
device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
|
||||
|
||||
defaultCfgWrapper, defaultCfgWrapperCancel = createTmpWrapper(config.New(myID))
|
||||
cfg := config.New(myID)
|
||||
cfg.Options.MinHomeDiskFree.Value = 0 // avoids unnecessary free space checks
|
||||
defaultCfgWrapper, defaultCfgWrapperCancel = newConfigWrapper(cfg)
|
||||
|
||||
defaultFolderConfig = testFolderConfig("testdata")
|
||||
defaultFs = defaultFolderConfig.Filesystem(nil)
|
||||
defaultFolderConfig = newFolderConfig()
|
||||
|
||||
waiter, _ := defaultCfgWrapper.Modify(func(cfg *config.Configuration) {
|
||||
cfg.SetDevice(newDeviceConfiguration(cfg.Defaults.Device, device1, "device1"))
|
||||
@@ -68,41 +68,33 @@ func init() {
|
||||
},
|
||||
Defaults: config.Defaults{
|
||||
Folder: config.FolderConfiguration{
|
||||
Path: ".",
|
||||
FilesystemType: fs.FilesystemTypeFake,
|
||||
Path: rand.String(32),
|
||||
},
|
||||
},
|
||||
Options: config.OptionsConfiguration{
|
||||
MinHomeDiskFree: config.Size{}, // avoids unnecessary free space checks
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createTmpWrapper(cfg config.Configuration) (config.Wrapper, context.CancelFunc) {
|
||||
tmpFile, err := os.CreateTemp("", "syncthing-testConfig-")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
wrapper := config.Wrap(tmpFile.Name(), cfg, myID, events.NoopLogger)
|
||||
tmpFile.Close()
|
||||
func newConfigWrapper(cfg config.Configuration) (config.Wrapper, context.CancelFunc) {
|
||||
wrapper := config.Wrap("", cfg, myID, events.NoopLogger)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go wrapper.Serve(ctx)
|
||||
return wrapper, cancel
|
||||
}
|
||||
|
||||
func tmpDefaultWrapper(t testing.TB) (config.Wrapper, config.FolderConfiguration, context.CancelFunc) {
|
||||
w, cancel := createTmpWrapper(defaultCfgWrapper.RawCopy())
|
||||
fcfg := testFolderConfig(t.TempDir())
|
||||
func newDefaultCfgWrapper() (config.Wrapper, config.FolderConfiguration, context.CancelFunc) {
|
||||
w, cancel := newConfigWrapper(defaultCfgWrapper.RawCopy())
|
||||
fcfg := newFolderConfig()
|
||||
_, _ = w.Modify(func(cfg *config.Configuration) {
|
||||
cfg.SetFolder(fcfg)
|
||||
})
|
||||
return w, fcfg, cancel
|
||||
}
|
||||
|
||||
func testFolderConfig(path string) config.FolderConfiguration {
|
||||
cfg := newFolderConfiguration(defaultCfgWrapper, "default", "default", fs.FilesystemTypeBasic, path)
|
||||
cfg.FSWatcherEnabled = false
|
||||
cfg.Devices = append(cfg.Devices, config.FolderDeviceConfiguration{DeviceID: device1})
|
||||
return cfg
|
||||
}
|
||||
|
||||
func testFolderConfigFake() config.FolderConfiguration {
|
||||
func newFolderConfig() config.FolderConfiguration {
|
||||
cfg := newFolderConfiguration(defaultCfgWrapper, "default", "default", fs.FilesystemTypeFake, rand.String(32)+"?content=true")
|
||||
cfg.FSWatcherEnabled = false
|
||||
cfg.Devices = append(cfg.Devices, config.FolderDeviceConfiguration{DeviceID: device1})
|
||||
@@ -111,7 +103,7 @@ func testFolderConfigFake() config.FolderConfiguration {
|
||||
|
||||
func setupModelWithConnection(t testing.TB) (*testModel, *fakeConnection, config.FolderConfiguration, context.CancelFunc) {
|
||||
t.Helper()
|
||||
w, fcfg, cancel := tmpDefaultWrapper(t)
|
||||
w, fcfg, cancel := newDefaultCfgWrapper()
|
||||
m, fc := setupModelWithConnectionFromWrapper(t, w)
|
||||
return m, fc, fcfg, cancel
|
||||
}
|
||||
|
||||
+20
-34
@@ -11,16 +11,15 @@ import (
|
||||
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
)
|
||||
|
||||
func TestInWriteableDir(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32))
|
||||
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
|
||||
|
||||
fs.Mkdir("testdata", 0700)
|
||||
fs.Mkdir("testdata/rw", 0700)
|
||||
fs.Mkdir("testdata/ro", 0500)
|
||||
fs.Mkdir("testdata", 0o700)
|
||||
fs.Mkdir("testdata/rw", 0o700)
|
||||
fs.Mkdir("testdata/ro", 0o500)
|
||||
|
||||
create := func(name string) error {
|
||||
fd, err := fs.Create(name)
|
||||
@@ -68,17 +67,12 @@ func TestInWriteableDir(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOSWindowsRemove(t *testing.T) {
|
||||
// os.Remove should remove read only things on windows
|
||||
|
||||
if !build.IsWindows {
|
||||
t.Skipf("Tests not required")
|
||||
return
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
|
||||
defer fs.Chmod("testdata/windows/ro/readonlynew", 0700)
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32))
|
||||
|
||||
create := func(name string) error {
|
||||
fd, err := fs.Create(name)
|
||||
@@ -89,12 +83,12 @@ func TestOSWindowsRemove(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
fs.Mkdir("testdata", 0700)
|
||||
fs.Mkdir("testdata", 0o700)
|
||||
|
||||
fs.Mkdir("testdata/windows", 0500)
|
||||
fs.Mkdir("testdata/windows/ro", 0500)
|
||||
fs.Mkdir("testdata/windows", 0o500)
|
||||
fs.Mkdir("testdata/windows/ro", 0o500)
|
||||
create("testdata/windows/ro/readonly")
|
||||
fs.Chmod("testdata/windows/ro/readonly", 0500)
|
||||
fs.Chmod("testdata/windows/ro/readonly", 0o500)
|
||||
|
||||
for _, path := range []string{"testdata/windows/ro/readonly", "testdata/windows/ro", "testdata/windows"} {
|
||||
err := inWritableDir(fs.Remove, fs, path, false)
|
||||
@@ -105,17 +99,12 @@ func TestOSWindowsRemove(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOSWindowsRemoveAll(t *testing.T) {
|
||||
// os.RemoveAll should remove read only things on windows
|
||||
|
||||
if !build.IsWindows {
|
||||
t.Skipf("Tests not required")
|
||||
return
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
|
||||
defer fs.Chmod("testdata/windows/ro/readonlynew", 0700)
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32))
|
||||
|
||||
create := func(name string) error {
|
||||
fd, err := fs.Create(name)
|
||||
@@ -126,12 +115,12 @@ func TestOSWindowsRemoveAll(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
fs.Mkdir("testdata", 0700)
|
||||
fs.Mkdir("testdata", 0o700)
|
||||
|
||||
fs.Mkdir("testdata/windows", 0500)
|
||||
fs.Mkdir("testdata/windows/ro", 0500)
|
||||
fs.Mkdir("testdata/windows", 0o500)
|
||||
fs.Mkdir("testdata/windows/ro", 0o500)
|
||||
create("testdata/windows/ro/readonly")
|
||||
fs.Chmod("testdata/windows/ro/readonly", 0500)
|
||||
fs.Chmod("testdata/windows/ro/readonly", 0o500)
|
||||
|
||||
if err := fs.RemoveAll("testdata/windows"); err != nil {
|
||||
t.Errorf("Unexpected error: %s", err)
|
||||
@@ -144,10 +133,7 @@ func TestInWritableDirWindowsRename(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
|
||||
defer fs.Chmod("testdata/windows/ro/readonlynew", 0700)
|
||||
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32))
|
||||
|
||||
create := func(name string) error {
|
||||
fd, err := fs.Create(name)
|
||||
@@ -158,12 +144,12 @@ func TestInWritableDirWindowsRename(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
fs.Mkdir("testdata", 0700)
|
||||
fs.Mkdir("testdata", 0o700)
|
||||
|
||||
fs.Mkdir("testdata/windows", 0500)
|
||||
fs.Mkdir("testdata/windows/ro", 0500)
|
||||
fs.Mkdir("testdata/windows", 0o500)
|
||||
fs.Mkdir("testdata/windows/ro", 0o500)
|
||||
create("testdata/windows/ro/readonly")
|
||||
fs.Chmod("testdata/windows/ro/readonly", 0500)
|
||||
fs.Chmod("testdata/windows/ro/readonly", 0o500)
|
||||
|
||||
for _, path := range []string{"testdata/windows/ro/readonly", "testdata/windows/ro", "testdata/windows"} {
|
||||
err := fs.Rename(path, path+"new")
|
||||
|
||||
Reference in New Issue
Block a user