1 Star 0 Fork 1

mysnapcore / mysnapd

forked from tupelo-shen / mysnapd 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
synctree.go 5.96 KB
一键复制 编辑 原始数据 按行查看 历史
tupelo-shen 提交于 2022-11-07 16:32 . fix: osutil 4 commit
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2019 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package osutil
import (
"fmt"
"os"
"path/filepath"
"sort"
"syscall"
)
func appendWithPrefix(paths []string, prefix string, filenames []string) []string {
for _, filename := range filenames {
paths = append(paths, filepath.Join(prefix, filename))
}
return paths
}
func removeEmptyDirs(baseDir, relPath string) error {
for relPath != "." {
if err := os.Remove(filepath.Join(baseDir, relPath)); err != nil {
// If the directory doesn't exist, then stop.
if os.IsNotExist(err) {
return nil
}
// If the directory is not empty, then stop.
if pathErr, ok := err.(*os.PathError); ok && pathErr.Err == syscall.ENOTEMPTY {
return nil
}
return err
}
relPath = filepath.Dir(relPath)
}
return nil
}
func matchAnyComponent(globs []string, path string) (ok bool, index int) {
for path != "." {
component := filepath.Base(path)
if ok, index, _ = matchAny(globs, component); ok {
return ok, index
}
path = filepath.Dir(path)
}
return false, 0
}
// EnsureTreeState ensures that a directory tree content matches expectations.
//
// EnsureTreeState walks subdirectories of the base directory, and
// uses EnsureDirStateGlobs to synchronise content with the
// corresponding entry in the content map. Any non-existent
// subdirectories in the content map will be created.
//
// After synchronising all subdirectories, any subdirectories where
// files were removed that are now empty will itself be removed, plus
// its parent directories up to but not including the base directory.
//
// While there is a quick check to prevent creation of directories
// that match the file glob pattern, it is the caller's responsibility
// to not create directories that may match globs passed to other
// invocations.
//
// For example, if the glob "snap.$SNAP_NAME.*" is used then the
// caller should avoid trying to populate any directories matching
// "snap.*".
//
// If an error occurs, all matching files are removed from the tree.
//
// A list of changed and removed files is returned, as relative paths
// to the base directory.
func EnsureTreeState(baseDir string, globs []string, content map[string]map[string]FileState) (changed, removed []string, err error) {
// Validity check globs before doing anything
if _, index, err := matchAny(globs, "foo"); err != nil {
return nil, nil, fmt.Errorf("internal error: EnsureTreeState got invalid pattern %q: %s", globs[index], err)
}
// Validity check directory paths and file names in content dict
for relPath, dirContent := range content {
if filepath.IsAbs(relPath) {
return nil, nil, fmt.Errorf("internal error: EnsureTreeState got absolute directory %q", relPath)
}
if ok, index := matchAnyComponent(globs, relPath); ok {
return nil, nil, fmt.Errorf("internal error: EnsureTreeState got path %q that matches glob pattern %q", relPath, globs[index])
}
for baseName := range dirContent {
if filepath.Base(baseName) != baseName {
return nil, nil, fmt.Errorf("internal error: EnsureTreeState got filename %q in %q, which has a path component", baseName, relPath)
}
if ok, _, _ := matchAny(globs, baseName); !ok {
return nil, nil, fmt.Errorf("internal error: EnsureTreeState got filename %q in %q, which doesn't match any glob patterns %q", baseName, relPath, globs)
}
}
}
// Find all existing subdirectories under the base dir. Don't
// perform any modifications here because, as it may confuse
// Walk().
subdirs := make(map[string]bool)
err = filepath.Walk(baseDir, func(path string, fileInfo os.FileInfo, err error) error {
if err != nil {
return err
}
if !fileInfo.IsDir() {
return nil
}
relPath, err := filepath.Rel(baseDir, path)
if err != nil {
return err
}
subdirs[relPath] = true
return nil
})
if err != nil {
return nil, nil, err
}
// Ensure we process directories listed in content
for relPath := range content {
subdirs[relPath] = true
}
maybeEmpty := []string{}
var firstErr error
for relPath := range subdirs {
dirContent := content[relPath]
path := filepath.Join(baseDir, relPath)
if err := os.MkdirAll(path, 0755); err != nil {
firstErr = err
break
}
dirChanged, dirRemoved, err := EnsureDirStateGlobs(path, globs, dirContent)
changed = appendWithPrefix(changed, relPath, dirChanged)
removed = appendWithPrefix(removed, relPath, dirRemoved)
if err != nil {
firstErr = err
break
}
if len(removed) != 0 {
maybeEmpty = append(maybeEmpty, relPath)
}
}
// As with EnsureDirState, if an error occurred we want to
// delete all matching files under the whole baseDir
// hierarchy. This also means emptying subdirectories that
// were successfully synchronised.
if firstErr != nil {
// changed paths will be deleted by this next step
changed = nil
for relPath := range subdirs {
path := filepath.Join(baseDir, relPath)
if !IsDirectory(path) {
continue
}
_, dirRemoved, _ := EnsureDirStateGlobs(path, globs, nil)
removed = appendWithPrefix(removed, relPath, dirRemoved)
if len(removed) != 0 {
maybeEmpty = append(maybeEmpty, relPath)
}
}
}
sort.Strings(changed)
sort.Strings(removed)
// For directories where files were removed, attempt to remove
// empty directories.
for _, relPath := range maybeEmpty {
if err := removeEmptyDirs(baseDir, relPath); err != nil {
if firstErr != nil {
firstErr = err
}
}
}
return changed, removed, firstErr
}
Go
1
https://gitee.com/mysnapcore/mysnapd.git
git@gitee.com:mysnapcore/mysnapd.git
mysnapcore
mysnapd
mysnapd
v0.1.0

搜索帮助