// Archive moves the named file away to a version archive. If this function // returns nil, the named file does not exist any more (has been archived). func (t *Trashcan) Archive(filePath string) error { _, err := osutil.Lstat(filePath) if os.IsNotExist(err) { if debug { l.Debugln("not archiving nonexistent file", filePath) } return nil } else if err != nil { return err } versionsDir := filepath.Join(t.folderPath, ".stversions") if _, err := os.Stat(versionsDir); err != nil { if !os.IsNotExist(err) { return err } if debug { l.Debugln("creating versions dir", versionsDir) } if err := osutil.MkdirAll(versionsDir, 0777); err != nil { return err } osutil.HideFile(versionsDir) } if debug { l.Debugln("archiving", filePath) } relativePath, err := filepath.Rel(t.folderPath, filePath) if err != nil { return err } archivedPath := filepath.Join(versionsDir, relativePath) if err := osutil.MkdirAll(filepath.Dir(archivedPath), 0777); err != nil && !os.IsExist(err) { return err } if debug { l.Debugln("moving to", archivedPath) } if err := osutil.Rename(filePath, archivedPath); err != nil { return err } // Set the mtime to the time the file was deleted. This is used by the // cleanout routine. If this fails things won't work optimally but there's // not much we can do about it so we ignore the error. os.Chtimes(archivedPath, time.Now(), time.Now()) return nil }
func (f *FolderConfiguration) CreateMarker() error { if !f.HasMarker() { marker := filepath.Join(f.Path(), ".stfolder") fd, err := os.Create(marker) if err != nil { return err } fd.Close() osutil.HideFile(marker) } return nil }
// Move away the named file to a version archive. If this function returns // nil, the named file does not exist any more (has been archived). func (v Staggered) Archive(filePath string) error { if debug { l.Debugln("Waiting for lock on ", v.versionsPath) } v.mutex.Lock() defer v.mutex.Unlock() fileInfo, err := os.Stat(filePath) if err != nil { if os.IsNotExist(err) { if debug { l.Debugln("not archiving nonexistent file", filePath) } return nil } else { return err } } _, err = os.Stat(v.versionsPath) if err != nil { if os.IsNotExist(err) { if debug { l.Debugln("creating versions dir", v.versionsPath) } os.MkdirAll(v.versionsPath, 0755) osutil.HideFile(v.versionsPath) } else { return err } } if debug { l.Debugln("archiving", filePath) } file := filepath.Base(filePath) inFolderPath, err := filepath.Rel(v.folderPath, filepath.Dir(filePath)) if err != nil { return err } dir := filepath.Join(v.versionsPath, inFolderPath) err = os.MkdirAll(dir, 0755) if err != nil && !os.IsExist(err) { return err } ver := file + "~" + fileInfo.ModTime().Format(TimeLayout) dst := filepath.Join(dir, ver) if debug { l.Debugln("moving to", dst) } err = osutil.Rename(filePath, dst) if err != nil { return err } versions, err := filepath.Glob(filepath.Join(dir, file+"~[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]")) if err != nil { l.Warnln("Versioner: error finding versions for", file, err) return nil } sort.Strings(versions) v.expire(versions) return nil }
func (v Staggered) clean() { if debug { l.Debugln("Versioner clean: Waiting for lock on", v.versionsPath) } v.mutex.Lock() defer v.mutex.Unlock() if debug { l.Debugln("Versioner clean: Cleaning", v.versionsPath) } _, err := os.Stat(v.versionsPath) if err != nil { if os.IsNotExist(err) { if debug { l.Debugln("creating versions dir", v.versionsPath) } os.MkdirAll(v.versionsPath, 0755) osutil.HideFile(v.versionsPath) } else { l.Warnln("Versioner: can't create versions dir", err) } } versionsPerFile := make(map[string][]string) filesPerDir := make(map[string]int) err = filepath.Walk(v.versionsPath, func(path string, f os.FileInfo, err error) error { if err != nil { return err } switch mode := f.Mode(); { case mode.IsDir(): filesPerDir[path] = 0 if path != v.versionsPath { dir := filepath.Dir(path) filesPerDir[dir]++ } case mode.IsRegular(): extension := versionExt(path) dir := filepath.Dir(path) name := path[:len(path)-len(extension)-1] filesPerDir[dir]++ versionsPerFile[name] = append(versionsPerFile[name], path) } return nil }) if err != nil { l.Warnln("Versioner: error scanning versions dir", err) return } for _, versionList := range versionsPerFile { // List from filepath.Walk is sorted v.expire(versionList) } for path, numFiles := range filesPerDir { if numFiles > 0 { continue } if path == v.versionsPath { if debug { l.Debugln("Cleaner: versions dir is empty, don't delete", path) } continue } if debug { l.Debugln("Cleaner: deleting empty directory", path) } err = os.Remove(path) if err != nil { l.Warnln("Versioner: can't remove directory", path, err) } } if debug { l.Debugln("Cleaner: Finished cleaning", v.versionsPath) } }
// Archive moves the named file away to a version archive. If this function // returns nil, the named file does not exist any more (has been archived). func (v Simple) Archive(filePath string) error { fileInfo, err := osutil.Lstat(filePath) if os.IsNotExist(err) { if debug { l.Debugln("not archiving nonexistent file", filePath) } return nil } else if err != nil { return err } versionsDir := filepath.Join(v.folderPath, ".stversions") _, err = os.Stat(versionsDir) if err != nil { if os.IsNotExist(err) { if debug { l.Debugln("creating versions dir", versionsDir) } osutil.MkdirAll(versionsDir, 0755) osutil.HideFile(versionsDir) } else { return err } } if debug { l.Debugln("archiving", filePath) } file := filepath.Base(filePath) inFolderPath, err := filepath.Rel(v.folderPath, filepath.Dir(filePath)) if err != nil { return err } dir := filepath.Join(versionsDir, inFolderPath) err = osutil.MkdirAll(dir, 0755) if err != nil && !os.IsExist(err) { return err } ver := taggedFilename(file, fileInfo.ModTime().Format(TimeFormat)) dst := filepath.Join(dir, ver) if debug { l.Debugln("moving to", dst) } err = osutil.Rename(filePath, dst) if err != nil { return err } // Glob according to the new file~timestamp.ext pattern. newVersions, err := osutil.Glob(filepath.Join(dir, taggedFilename(file, TimeGlob))) if err != nil { l.Warnln("globbing:", err) return nil } // Also according to the old file.ext~timestamp pattern. oldVersions, err := osutil.Glob(filepath.Join(dir, file+"~"+TimeGlob)) if err != nil { l.Warnln("globbing:", err) return nil } // Use all the found filenames. "~" sorts after "." so all old pattern // files will be deleted before any new, which is as it should be. versions := uniqueSortedStrings(append(oldVersions, newVersions...)) if len(versions) > v.keep { for _, toRemove := range versions[:len(versions)-v.keep] { if debug { l.Debugln("cleaning out", toRemove) } err = os.Remove(toRemove) if err != nil { l.Warnln("removing old version:", err) } } } return nil }
// Move away the named file to a version archive. If this function returns // nil, the named file does not exist any more (has been archived). func (v Simple) Archive(filePath string) error { fileInfo, err := os.Stat(filePath) if err != nil { if os.IsNotExist(err) { if debug { l.Debugln("not archiving nonexistent file", filePath) } return nil } else { return err } } versionsDir := filepath.Join(v.folderPath, ".stversions") _, err = os.Stat(versionsDir) if err != nil { if os.IsNotExist(err) { if debug { l.Debugln("creating versions dir", versionsDir) } os.MkdirAll(versionsDir, 0755) osutil.HideFile(versionsDir) } else { return err } } if debug { l.Debugln("archiving", filePath) } file := filepath.Base(filePath) inFolderPath, err := filepath.Rel(v.folderPath, filepath.Dir(filePath)) if err != nil { return err } dir := filepath.Join(versionsDir, inFolderPath) err = os.MkdirAll(dir, 0755) if err != nil && !os.IsExist(err) { return err } ver := file + "~" + fileInfo.ModTime().Format("20060102-150405") dst := filepath.Join(dir, ver) if debug { l.Debugln("moving to", dst) } err = osutil.Rename(filePath, dst) if err != nil { return err } versions, err := filepath.Glob(filepath.Join(dir, file+"~[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]")) if err != nil { l.Warnln("globbing:", err) return nil } if len(versions) > v.keep { sort.Strings(versions) for _, toRemove := range versions[:len(versions)-v.keep] { if debug { l.Debugln("cleaning out", toRemove) } err = os.Remove(toRemove) if err != nil { l.Warnln("removing old version:", err) } } } return nil }
// Archive moves the named file away to a version archive. If this function // returns nil, the named file does not exist any more (has been archived). func (v Staggered) Archive(filePath string) error { if debug { l.Debugln("Waiting for lock on ", v.versionsPath) } v.mutex.Lock() defer v.mutex.Unlock() _, err := osutil.Lstat(filePath) if os.IsNotExist(err) { if debug { l.Debugln("not archiving nonexistent file", filePath) } return nil } else if err != nil { return err } if _, err := os.Stat(v.versionsPath); err != nil { if os.IsNotExist(err) { if debug { l.Debugln("creating versions dir", v.versionsPath) } osutil.MkdirAll(v.versionsPath, 0755) osutil.HideFile(v.versionsPath) } else { return err } } if debug { l.Debugln("archiving", filePath) } file := filepath.Base(filePath) inFolderPath, err := filepath.Rel(v.folderPath, filepath.Dir(filePath)) if err != nil { return err } dir := filepath.Join(v.versionsPath, inFolderPath) err = osutil.MkdirAll(dir, 0755) if err != nil && !os.IsExist(err) { return err } ver := taggedFilename(file, time.Now().Format(TimeFormat)) dst := filepath.Join(dir, ver) if debug { l.Debugln("moving to", dst) } err = osutil.Rename(filePath, dst) if err != nil { return err } // Glob according to the new file~timestamp.ext pattern. newVersions, err := osutil.Glob(filepath.Join(dir, taggedFilename(file, TimeGlob))) if err != nil { l.Warnln("globbing:", err) return nil } // Also according to the old file.ext~timestamp pattern. oldVersions, err := osutil.Glob(filepath.Join(dir, file+"~"+TimeGlob)) if err != nil { l.Warnln("globbing:", err) return nil } // Use all the found filenames. versions := append(oldVersions, newVersions...) v.expire(uniqueSortedStrings(versions)) return nil }