blob: 3c73916a1e1c6a3f71a4c3f9da386651a7e1b57a [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// 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)
33
34var (
35 plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "pkg/fileutil")
36)
37
38// IsDirWriteable checks if dir is writable by writing and removing a file
39// to dir. It returns nil if dir is writable.
40func IsDirWriteable(dir string) error {
41 f := filepath.Join(dir, ".touch")
42 if err := ioutil.WriteFile(f, []byte(""), PrivateFileMode); err != nil {
43 return err
44 }
45 return os.Remove(f)
46}
47
48// ReadDir returns the filenames in the given directory in sorted order.
49func ReadDir(dirpath string) ([]string, error) {
50 dir, err := os.Open(dirpath)
51 if err != nil {
52 return nil, err
53 }
54 defer dir.Close()
55 names, err := dir.Readdirnames(-1)
56 if err != nil {
57 return nil, err
58 }
59 sort.Strings(names)
60 return names, nil
61}
62
63// TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory
64// does not exists. TouchDirAll also ensures the given directory is writable.
65func TouchDirAll(dir string) error {
66 // If path is already a directory, MkdirAll does nothing and returns nil, so,
67 // first check if dir exist with an expected permission mode.
68 if Exist(dir) {
69 err := CheckDirPermission(dir, PrivateDirMode)
70 if err != nil {
71 plog.Warningf("check file permission: %v", err)
72 }
73 } else {
74 err := os.MkdirAll(dir, PrivateDirMode)
75 if err != nil {
76 // if mkdirAll("a/text") and "text" is not
77 // a directory, this will return syscall.ENOTDIR
78 return err
79 }
80 }
81
82 return IsDirWriteable(dir)
83}
84
85// CreateDirAll is similar to TouchDirAll but returns error
86// if the deepest directory was not empty.
87func CreateDirAll(dir string) error {
88 err := TouchDirAll(dir)
89 if err == nil {
90 var ns []string
91 ns, err = ReadDir(dir)
92 if err != nil {
93 return err
94 }
95 if len(ns) != 0 {
96 err = fmt.Errorf("expected %q to be empty, got %q", dir, ns)
97 }
98 }
99 return err
100}
101
102func Exist(name string) bool {
103 _, err := os.Stat(name)
104 return err == nil
105}
106
107// ZeroToEnd zeros a file starting from SEEK_CUR to its SEEK_END. May temporarily
108// shorten the length of the file.
109func ZeroToEnd(f *os.File) error {
110 // TODO: support FALLOC_FL_ZERO_RANGE
111 off, err := f.Seek(0, io.SeekCurrent)
112 if err != nil {
113 return err
114 }
115 lenf, lerr := f.Seek(0, io.SeekEnd)
116 if lerr != nil {
117 return lerr
118 }
119 if err = f.Truncate(off); err != nil {
120 return err
121 }
122 // make sure blocks remain allocated
123 if err = Preallocate(f, lenf, true); err != nil {
124 return err
125 }
126 _, err = f.Seek(off, io.SeekStart)
127 return err
128}
129
130// CheckDirPermission checks permission on an existing dir.
131// Returns error if dir is empty or exist with a different permission than specified.
132func CheckDirPermission(dir string, perm os.FileMode) error {
133 if !Exist(dir) {
134 return fmt.Errorf("directory %q empty, cannot check permission.", dir)
135 }
136 //check the existing permission on the directory
137 dirInfo, err := os.Stat(dir)
138 if err != nil {
139 return err
140 }
141 dirMode := dirInfo.Mode().Perm()
142 if dirMode != perm {
143 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))
144 return err
145 }
146 return nil
147}