blob: fce5126c6956de4a3139b6d9c5729c76ec58282b [file] [log] [blame]
sslobodrd046be82019-01-16 10:02:22 -05001// Copyright 2015 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package fileutil implements utility functions related to files and paths.
16package fileutil
17
18import (
19 "fmt"
20 "io"
21 "io/ioutil"
22 "os"
23 "path/filepath"
24 "sort"
25
26 "github.com/coreos/pkg/capnslog"
27)
28
29const (
30 // PrivateFileMode grants owner to read/write a file.
31 PrivateFileMode = 0600
32 // PrivateDirMode grants owner to make/remove files inside the directory.
33 PrivateDirMode = 0700
34)
35
36var (
37 plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "pkg/fileutil")
38)
39
40// IsDirWriteable checks if dir is writable by writing and removing a file
41// to dir. It returns nil if dir is writable.
42func IsDirWriteable(dir string) error {
43 f := filepath.Join(dir, ".touch")
44 if err := ioutil.WriteFile(f, []byte(""), PrivateFileMode); err != nil {
45 return err
46 }
47 return os.Remove(f)
48}
49
50// ReadDir returns the filenames in the given directory in sorted order.
51func ReadDir(dirpath string) ([]string, error) {
52 dir, err := os.Open(dirpath)
53 if err != nil {
54 return nil, err
55 }
56 defer dir.Close()
57 names, err := dir.Readdirnames(-1)
58 if err != nil {
59 return nil, err
60 }
61 sort.Strings(names)
62 return names, nil
63}
64
65// TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory
66// does not exists. TouchDirAll also ensures the given directory is writable.
67func TouchDirAll(dir string) error {
68 // If path is already a directory, MkdirAll does nothing
69 // and returns nil.
70 err := os.MkdirAll(dir, PrivateDirMode)
71 if err != nil {
72 // if mkdirAll("a/text") and "text" is not
73 // a directory, this will return syscall.ENOTDIR
74 return err
75 }
76 return IsDirWriteable(dir)
77}
78
79// CreateDirAll is similar to TouchDirAll but returns error
80// if the deepest directory was not empty.
81func CreateDirAll(dir string) error {
82 err := TouchDirAll(dir)
83 if err == nil {
84 var ns []string
85 ns, err = ReadDir(dir)
86 if err != nil {
87 return err
88 }
89 if len(ns) != 0 {
90 err = fmt.Errorf("expected %q to be empty, got %q", dir, ns)
91 }
92 }
93 return err
94}
95
96func Exist(name string) bool {
97 _, err := os.Stat(name)
98 return err == nil
99}
100
101// ZeroToEnd zeros a file starting from SEEK_CUR to its SEEK_END. May temporarily
102// shorten the length of the file.
103func ZeroToEnd(f *os.File) error {
104 // TODO: support FALLOC_FL_ZERO_RANGE
105 off, err := f.Seek(0, io.SeekCurrent)
106 if err != nil {
107 return err
108 }
109 lenf, lerr := f.Seek(0, io.SeekEnd)
110 if lerr != nil {
111 return lerr
112 }
113 if err = f.Truncate(off); err != nil {
114 return err
115 }
116 // make sure blocks remain allocated
117 if err = Preallocate(f, lenf, true); err != nil {
118 return err
119 }
120 _, err = f.Seek(off, io.SeekStart)
121 return err
122}