2016-05-13 06:48:53 +03:00
// Copyright 2015 The etcd Authors
2015-01-25 06:19:16 +03:00
//
// 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.
2014-10-28 01:46:59 +03:00
package fileutil
import (
2016-06-23 00:58:53 +03:00
"fmt"
2017-03-21 01:01:01 +03:00
"io"
2014-10-28 01:46:59 +03:00
"io/ioutil"
"os"
2017-03-16 05:31:10 +03:00
"path/filepath"
2020-08-24 03:20:16 +03:00
"go.uber.org/zap"
2014-10-28 01:46:59 +03:00
)
const (
2016-06-11 01:18:29 +03:00
// PrivateFileMode grants owner to read/write a file.
PrivateFileMode = 0600
2014-10-28 01:46:59 +03:00
)
// 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 {
2017-03-16 05:31:10 +03:00
f := filepath . Join ( dir , ".touch" )
2016-06-11 01:18:29 +03:00
if err := ioutil . WriteFile ( f , [ ] byte ( "" ) , PrivateFileMode ) ; err != nil {
2014-10-28 01:46:59 +03:00
return err
}
return os . Remove ( f )
}
2014-11-29 08:32:28 +03:00
2016-02-01 08:42:39 +03:00
// TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory
2015-12-29 21:40:58 +03:00
// does not exists. TouchDirAll also ensures the given directory is writable.
func TouchDirAll ( dir string ) error {
2020-04-22 05:58:48 +03: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-08-24 03:20:16 +03:00
lg , _ := zap . NewProduction ( )
if lg == nil {
lg = zap . NewExample ( )
}
lg . Warn ( "check file permission" , zap . Error ( err ) )
2020-04-22 05:58:48 +03: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
}
2015-12-29 21:40:58 +03:00
}
2020-04-22 05:58:48 +03:00
2015-12-29 21:40:58 +03:00
return IsDirWriteable ( dir )
}
2016-06-23 00:58:53 +03:00
// 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
}
2018-04-09 00:24:59 +03:00
// Exist returns true if a file or directory exists.
2015-09-15 01:44:21 +03:00
func Exist ( name string ) bool {
_ , err := os . Stat ( name )
return err == nil
}
2016-08-25 23:15:13 +03:00
2020-04-05 00:41:19 +03:00
// 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
}
2016-08-25 23:15:13 +03:00
// 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
2017-03-21 01:01:01 +03:00
off , err := f . Seek ( 0 , io . SeekCurrent )
2016-08-25 23:15:13 +03:00
if err != nil {
return err
}
2017-03-21 01:01:01 +03:00
lenf , lerr := f . Seek ( 0 , io . SeekEnd )
2016-08-25 23:15:13 +03:00
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
}
2017-03-21 01:01:01 +03:00
_ , err = f . Seek ( off , io . SeekStart )
2016-08-25 23:15:13 +03:00
return err
}
2020-04-22 05:58:48 +03: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-08-24 03:20:16 +03: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-04-22 05:58:48 +03:00
return err
}
return nil
}