mirror of https://github.com/k3s-io/k3s
173 lines
4.6 KiB
Go
173 lines
4.6 KiB
Go
// Copyright 2015 The etcd Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package fileutil
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
const (
|
|
// PrivateFileMode grants owner to read/write a file.
|
|
PrivateFileMode = 0600
|
|
)
|
|
|
|
// IsDirWriteable checks if dir is writable by writing and removing a file
|
|
// to dir. It returns nil if dir is writable.
|
|
func IsDirWriteable(dir string) error {
|
|
f, err := filepath.Abs(filepath.Join(dir, ".touch"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := ioutil.WriteFile(f, []byte(""), PrivateFileMode); err != nil {
|
|
return err
|
|
}
|
|
return os.Remove(f)
|
|
}
|
|
|
|
// TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory
|
|
// does not exists. TouchDirAll also ensures the given directory is writable.
|
|
func TouchDirAll(dir string) error {
|
|
// If path is already a directory, MkdirAll does nothing and returns nil, so,
|
|
// first check if dir exist with an expected permission mode.
|
|
if Exist(dir) {
|
|
err := CheckDirPermission(dir, PrivateDirMode)
|
|
if err != nil {
|
|
lg, _ := zap.NewProduction()
|
|
if lg == nil {
|
|
lg = zap.NewExample()
|
|
}
|
|
lg.Warn("check file permission", zap.Error(err))
|
|
}
|
|
} else {
|
|
err := os.MkdirAll(dir, PrivateDirMode)
|
|
if err != nil {
|
|
// if mkdirAll("a/text") and "text" is not
|
|
// a directory, this will return syscall.ENOTDIR
|
|
return err
|
|
}
|
|
}
|
|
|
|
return IsDirWriteable(dir)
|
|
}
|
|
|
|
// CreateDirAll is similar to TouchDirAll but returns error
|
|
// if the deepest directory was not empty.
|
|
func CreateDirAll(dir string) error {
|
|
err := TouchDirAll(dir)
|
|
if err == nil {
|
|
var ns []string
|
|
ns, err = ReadDir(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(ns) != 0 {
|
|
err = fmt.Errorf("expected %q to be empty, got %q", dir, ns)
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Exist returns true if a file or directory exists.
|
|
func Exist(name string) bool {
|
|
_, err := os.Stat(name)
|
|
return err == nil
|
|
}
|
|
|
|
// DirEmpty returns true if a directory empty and can access.
|
|
func DirEmpty(name string) bool {
|
|
ns, err := ReadDir(name)
|
|
return len(ns) == 0 && err == nil
|
|
}
|
|
|
|
// ZeroToEnd zeros a file starting from SEEK_CUR to its SEEK_END. May temporarily
|
|
// shorten the length of the file.
|
|
func ZeroToEnd(f *os.File) error {
|
|
// TODO: support FALLOC_FL_ZERO_RANGE
|
|
off, err := f.Seek(0, io.SeekCurrent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lenf, lerr := f.Seek(0, io.SeekEnd)
|
|
if lerr != nil {
|
|
return lerr
|
|
}
|
|
if err = f.Truncate(off); err != nil {
|
|
return err
|
|
}
|
|
// make sure blocks remain allocated
|
|
if err = Preallocate(f, lenf, true); err != nil {
|
|
return err
|
|
}
|
|
_, err = f.Seek(off, io.SeekStart)
|
|
return err
|
|
}
|
|
|
|
// CheckDirPermission checks permission on an existing dir.
|
|
// Returns error if dir is empty or exist with a different permission than specified.
|
|
func CheckDirPermission(dir string, perm os.FileMode) error {
|
|
if !Exist(dir) {
|
|
return fmt.Errorf("directory %q empty, cannot check permission", dir)
|
|
}
|
|
//check the existing permission on the directory
|
|
dirInfo, err := os.Stat(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dirMode := dirInfo.Mode().Perm()
|
|
if dirMode != perm {
|
|
err = fmt.Errorf("directory %q exist, but the permission is %q. The recommended permission is %q to prevent possible unprivileged access to the data", dir, dirInfo.Mode(), os.FileMode(PrivateDirMode))
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RemoveMatchFile deletes file if matchFunc is true on an existing dir
|
|
// Returns error if the dir does not exist or remove file fail
|
|
func RemoveMatchFile(lg *zap.Logger, dir string, matchFunc func(fileName string) bool) error {
|
|
if lg == nil {
|
|
lg = zap.NewNop()
|
|
}
|
|
if !Exist(dir) {
|
|
return fmt.Errorf("directory %s does not exist", dir)
|
|
}
|
|
fileNames, err := ReadDir(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var removeFailedFiles []string
|
|
for _, fileName := range fileNames {
|
|
if matchFunc(fileName) {
|
|
file := filepath.Join(dir, fileName)
|
|
if err = os.Remove(file); err != nil {
|
|
removeFailedFiles = append(removeFailedFiles, fileName)
|
|
lg.Error("remove file failed",
|
|
zap.String("file", file),
|
|
zap.Error(err))
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
if len(removeFailedFiles) != 0 {
|
|
return fmt.Errorf("remove file(s) %v error", removeFailedFiles)
|
|
}
|
|
return nil
|
|
}
|