// 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 implements utility functions related to files and paths.
package fileutil import ( ) const ( // PrivateFileMode grants owner to read/write a file. PrivateFileMode = 0600 ) var ( plog = capnslog.NewPackageLogger("github.com/coreos/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 ( string) error { := filepath.Join(, ".touch") if := ioutil.WriteFile(, []byte(""), PrivateFileMode); != nil { return } return os.Remove() } // ReadDir returns the filenames in the given directory in sorted order. func ( string) ([]string, error) { , := os.Open() if != nil { return nil, } defer .Close() , := .Readdirnames(-1) if != nil { return nil, } sort.Strings() return , nil } // 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 ( 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() { := CheckDirPermission(, PrivateDirMode) if != nil { plog.Warningf("check file permission: %v", ) } } else { := os.MkdirAll(, PrivateDirMode) if != nil { // if mkdirAll("a/text") and "text" is not // a directory, this will return syscall.ENOTDIR return } } return IsDirWriteable() } // CreateDirAll is similar to TouchDirAll but returns error // if the deepest directory was not empty. func ( string) error { := TouchDirAll() if == nil { var []string , = ReadDir() if != nil { return } if len() != 0 { = fmt.Errorf("expected %q to be empty, got %q", , ) } } return } func ( string) bool { , := os.Stat() return == nil } // ZeroToEnd zeros a file starting from SEEK_CUR to its SEEK_END. May temporarily // shorten the length of the file. func ( *os.File) error { // TODO: support FALLOC_FL_ZERO_RANGE , := .Seek(0, io.SeekCurrent) if != nil { return } , := .Seek(0, io.SeekEnd) if != nil { return } if = .Truncate(); != nil { return } // make sure blocks remain allocated if = Preallocate(, , true); != nil { return } _, = .Seek(, io.SeekStart) return } // CheckDirPermission checks permission on an existing dir. // Returns error if dir is empty or exist with a different permission than specified. func ( string, os.FileMode) error { if !Exist() { return fmt.Errorf("directory %q empty, cannot check permission.", ) } //check the existing permission on the directory , := os.Stat() if != nil { return } := .Mode().Perm() if != { = fmt.Errorf("directory %q exist, but the permission is %q. The recommended permission is %q to prevent possible unprivileged access to the data.", , .Mode(), os.FileMode(PrivateDirMode)) return } return nil }