func (p *rwFolder) moveForConflict(name string) error { if p.maxConflicts == 0 { if err := osutil.Remove(name); err != nil && !os.IsNotExist(err) { return err } return nil } ext := filepath.Ext(name) withoutExt := name[:len(name)-len(ext)] newName := withoutExt + time.Now().Format(".sync-conflict-20060102-150405") + ext err := os.Rename(name, newName) if os.IsNotExist(err) { // We were supposed to move a file away but it does not exist. Either // the user has already moved it away, or the conflict was between a // remote modification and a local delete. In either way it does not // matter, go ahead as if the move succeeded. err = nil } if p.maxConflicts > -1 { matches, gerr := osutil.Glob(withoutExt + ".sync-conflict-????????-??????" + ext) if gerr == nil && len(matches) > p.maxConflicts { sort.Sort(sort.Reverse(sort.StringSlice(matches))) for _, match := range matches[p.maxConflicts:] { gerr = osutil.Remove(match) if gerr != nil { l.Debugln(p, "removing extra conflict", gerr) } } } else if gerr != nil { l.Debugln(p, "globbing for conflicts", gerr) } } return err }
func (t *Trashcan) cleanoutArchive() error { versionsDir := filepath.Join(t.folderPath, ".stversions") if _, err := osutil.Lstat(versionsDir); os.IsNotExist(err) { return nil } cutoff := time.Now().Add(time.Duration(-24*t.cleanoutDays) * time.Hour) currentDir := "" filesInDir := 0 walkFn := func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { // We have entered a new directory. Lets check if the previous // directory was empty and try to remove it. We ignore failure for // the time being. if currentDir != "" && filesInDir == 0 { osutil.Remove(currentDir) } currentDir = path filesInDir = 0 return nil } if info.ModTime().Before(cutoff) { // The file is too old; remove it. osutil.Remove(path) } else { // Keep this file, and remember it so we don't unnecessarily try // to remove this directory. filesInDir++ } return nil } if err := filepath.Walk(versionsDir, walkFn); err != nil { return err } // The last directory seen by the walkFn may not have been removed as it // should be. if currentDir != "" && filesInDir == 0 { osutil.Remove(currentDir) } return nil }
func (v Staggered) expire(versions []string) { l.Debugln("Versioner: Expiring versions", versions) for _, file := range v.toRemove(versions, time.Now()) { if fi, err := osutil.Lstat(file); err != nil { l.Warnln("versioner:", err) continue } else if fi.IsDir() { l.Infof("non-file %q is named like a file version", file) continue } if err := osutil.Remove(file); err != nil { l.Warnf("Versioner: can't remove %q: %v", file, err) } } }
func alterFiles(dir string) error { err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if os.IsNotExist(err) { // Something we deleted. Never mind. return nil } info, err = os.Stat(path) if err != nil { // Something we deleted while walking. Ignore. return nil } if strings.HasPrefix(filepath.Base(path), "test-") { return nil } switch filepath.Base(path) { case ".stfolder": return nil case ".stversions": return nil } // File structure is base/x/xy/xyz12345... // comps == 1: base (don't touch) // comps == 2: base/x (must be dir) // comps == 3: base/x/xy (must be dir) // comps > 3: base/x/xy/xyz12345... (can be dir or file) comps := len(strings.Split(path, string(os.PathSeparator))) r := rand.Intn(10) switch { case r == 0 && comps > 2: // Delete every tenth file or directory, except top levels return removeAll(path) case r == 1 && info.Mode().IsRegular(): if info.Mode()&0200 != 0200 { // Not owner writable. Fix. if err = os.Chmod(path, 0644); err != nil { return err } } // Overwrite a random kilobyte of every tenth file fd, err := os.OpenFile(path, os.O_RDWR, 0644) if err != nil { return err } if info.Size() > 1024 { _, err = fd.Seek(rand.Int63n(info.Size()), os.SEEK_SET) if err != nil { return err } } _, err = io.Copy(fd, io.LimitReader(cr.Reader, 1024)) if err != nil { return err } return fd.Close() // Change capitalization case r == 2 && comps > 3 && rand.Float64() < 0.2: if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { // Syncthing is currently broken for case-only renames on case- // insensitive platforms. // https://github.com/syncthing/syncthing/issues/1787 return nil } base := []rune(filepath.Base(path)) for i, r := range base { if rand.Float64() < 0.5 { base[i] = unicode.ToLower(r) } else { base[i] = unicode.ToUpper(r) } } newPath := filepath.Join(filepath.Dir(path), string(base)) if newPath != path { return osutil.TryRename(path, newPath) } // Switch between files and directories case r == 3 && comps > 3 && rand.Float64() < 0.2: if !info.Mode().IsRegular() { err = removeAll(path) if err != nil { return err } d1 := []byte("I used to be a dir: " + path) err := ioutil.WriteFile(path, d1, 0644) if err != nil { return err } } else { err := osutil.Remove(path) if err != nil { return err } err = os.MkdirAll(path, 0755) if err != nil { return err } generateFiles(path, 10, 20, "../LICENSE") } return err /* This fails. Bug? // Rename the file, while potentially moving it up in the directory hierarchy case r == 4 && comps > 2 && (info.Mode().IsRegular() || rand.Float64() < 0.2): rpath := filepath.Dir(path) if rand.Float64() < 0.2 { for move := rand.Intn(comps - 1); move > 0; move-- { rpath = filepath.Join(rpath, "..") } } return osutil.TryRename(path, filepath.Join(rpath, randomName())) */ } return nil }) if err != nil { return err } return generateFiles(dir, 25, 20, "../LICENSE") }