This repository has been archived by the owner on Mar 24, 2020. It is now read-only.
/
file.go
124 lines (108 loc) · 2.66 KB
/
file.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package validator
import (
"errors"
"fmt"
"io"
"os"
"strings"
)
var (
errFilePathEmpty = errors.New("filename is missing or empty")
errFileUnwritable = errors.New("unable to write file")
)
// File check input to fulfil specified param
func File(v interface{}, param string) (err error) {
var checksMapping = checkMap{
"omitempty": checkDefinition{
checkFct: checkFileOmitEmpty,
omitError: true,
},
"readable": checkDefinition{
checkFct: checkFileReadable,
omitError: false,
},
"writable": checkDefinition{
checkFct: checkFileWritable,
omitError: false,
},
}
return validate(v, param, checksMapping)
}
func checkFileOmitEmpty(str string, param string) (err error) {
if str == "" {
return errFilePathEmpty
}
return nil
}
func checkFileReadable(str string, param string) (err error) {
flag := os.O_RDONLY
if param != "" {
options := strings.Split(param, "|")
for _, option := range options {
switch option {
case "createifmissing":
flag |= os.O_CREATE
}
}
}
file, err := os.OpenFile(str, flag, 0600)
if err != nil {
return fmt.Errorf("unable to open file %s: %v", str, err)
}
defer func() {
if err = file.Close(); err != nil {
panic(err)
}
}()
return nil
}
func checkFileWritable(str string, param string) (err error) {
file, err := os.OpenFile(str, os.O_RDWR, 0600)
if err != nil {
return fmt.Errorf("unable to open file %s: %s", str, err)
}
defer func() {
if err = file.Close(); err != nil {
panic(err)
}
}()
// save file state before doing something
offset, readed, fileIsEmpty, err := saveFileStateBeforeWritting(file)
if err != nil {
return err
}
// try to write something
if w, errW := file.Write([]byte("\x00")); w != 1 || errW != nil {
return errFileUnwritable
}
// if file was empty, truncate(0) to avoid file length changement
if fileIsEmpty {
if err = os.Truncate(str, 0); err != nil {
return fmt.Errorf("unable to truncate file %s: %v", str, err)
}
} else {
if w, errW := file.WriteAt(readed, offset); w != 1 || errW != nil {
return errFileUnwritable
}
}
return nil
}
func saveFileStateBeforeWritting(file *os.File) (offset int64, readed []byte, fileIsEmpty bool, err error) {
// make sure we know where to write to remove what we write later
bof, err := file.Seek(0, 0)
if err != nil {
return 0, nil, false, fmt.Errorf("unable to seek: %v", err)
}
// we want to leave file as we found it
fileIsEmpty = false
readed = make([]byte, 1)
if _, err = file.ReadAt(readed, 0); err != nil {
if err == io.EOF {
fileIsEmpty = true
err = nil
} else {
return 0, nil, false, fmt.Errorf("unable to read: %v", err)
}
}
return bof, readed, fileIsEmpty, err
}