2020-05-07 05:09:19 +00:00
// 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"
"github.com/coreos/pkg/capnslog"
)
const (
// PrivateFileMode grants owner to read/write a file.
PrivateFileMode = 0600
)
var plog = capnslog . NewPackageLogger ( "go.etcd.io/etcd" , "pkg/fileutil" )
// 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 := filepath . Join ( dir , ".touch" )
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 {
2020-08-10 17:43:49 +00:00
// 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 {
2020-09-09 22:08:21 +00:00
plog . Warningf ( "check file permission: %v" , err )
2020-08-10 17:43:49 +00:00
}
} 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
}
2020-05-07 05:09:19 +00:00
}
2020-08-10 17:43:49 +00:00
2020-05-07 05:09:19 +00:00
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
}
// 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
}
2020-08-10 17:43:49 +00:00
// 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 {
2020-09-09 22:08:21 +00:00
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 ) )
2020-08-10 17:43:49 +00:00
return err
}
return nil
}