-
Notifications
You must be signed in to change notification settings - Fork 234
/
Copy pathwriteThoughFile_linux.go
200 lines (164 loc) · 6.87 KB
/
writeThoughFile_linux.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Copyright © 2017 Microsoft <wastore@microsoft.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package common
import (
"encoding/binary"
"fmt"
"os"
"syscall"
"time"
"github.com/pkg/xattr"
"golang.org/x/sys/unix"
)
// Extended Attribute (xattr) keys for fetching various information from Linux cifs client.
const (
CIFS_XATTR_CREATETIME = "user.cifs.creationtime" // File creation time.
CIFS_XATTR_ATTRIB = "user.cifs.dosattrib" // FileAttributes.
CIFS_XATTR_CIFS_ACL = "system.cifs_acl" // DACL only.
CIFS_XATTR_CIFS_NTSD = "system.cifs_ntsd" // Owner, Group, DACL.
CIFS_XATTR_CIFS_NTSD_FULL = "system.cifs_ntsd_full" // Owner, Group, DACL, SACL.
)
// 100-nanosecond intervals from Windows Epoch (January 1, 1601) to Unix Epoch (January 1, 1970).
const (
TICKS_FROM_WINDOWS_EPOCH_TO_UNIX_EPOCH = 116444736000000000
)
// windows.Filetime.
type Filetime struct {
LowDateTime uint32
HighDateTime uint32
}
// windows.ByHandleFileInformation
type ByHandleFileInformation struct {
FileAttributes uint32
CreationTime Filetime
LastAccessTime Filetime
LastWriteTime Filetime
VolumeSerialNumber uint32
FileSizeHigh uint32
FileSizeLow uint32
NumberOfLinks uint32
FileIndexHigh uint32
FileIndexLow uint32
}
// Nanoseconds converts Filetime (as ticks since Windows Epoch) to nanoseconds since Unix Epoch (January 1, 1970).
func (ft *Filetime) Nanoseconds() int64 {
// 100-nanosecond intervals (ticks) since Windows Epoch (January 1, 1601).
nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime)
// 100-nanosecond intervals since Unix Epoch (January 1, 1970).
nsec -= TICKS_FROM_WINDOWS_EPOCH_TO_UNIX_EPOCH
// nanoseconds since Unix Epoch.
return nsec * 100
}
// Convert nanoseconds since Unix Epoch (January 1, 1970) to Filetime since Windows Epoch (January 1, 1601).
func NsecToFiletime(nsec int64) Filetime {
// 100-nanosecond intervals since Unix Epoch (January 1, 1970).
nsec /= 100
// 100-nanosecond intervals since Windows Epoch (January 1, 1601).
nsec += TICKS_FROM_WINDOWS_EPOCH_TO_UNIX_EPOCH
return Filetime{LowDateTime: uint32(nsec & 0xFFFFFFFF), HighDateTime: uint32(nsec >> 32)}
}
// WindowsTicksToUnixNano converts ticks (100-ns intervals) since Windows Epoch to nanoseconds since Unix Epoch.
func WindowsTicksToUnixNano(ticks int64) int64 {
// 100-nanosecond intervals since Unix Epoch (January 1, 1970).
ticks -= TICKS_FROM_WINDOWS_EPOCH_TO_UNIX_EPOCH
// nanoseconds since Unix Epoch (January 1, 1970).
return ticks * 100
}
// UnixNanoToWindowsTicks converts nanoseconds since Unix Epoch to ticks since Windows Epoch.
func UnixNanoToWindowsTicks(nsec int64) int64 {
// 100-nanosecond intervals since Unix Epoch (January 1, 1970).
nsec /= 100
// 100-nanosecond intervals since Windows Epoch (January 1, 1601).
nsec += TICKS_FROM_WINDOWS_EPOCH_TO_UNIX_EPOCH
return nsec
}
// StatxTimestampToFiletime converts the unix StatxTimestamp (sec, nsec) to the Windows' Filetime.
// Note that StatxTimestamp is from Unix Epoch while Filetime holds time from Windows Epoch.
func StatxTimestampToFiletime(ts unix.StatxTimestamp) Filetime {
return NsecToFiletime(ts.Sec*int64(time.Second) + int64(ts.Nsec))
}
func GetFileInformation(path string) (ByHandleFileInformation, error) {
var stx unix.Statx_t
// We want all attributes including Btime (aka creation time).
// For consistency with Windows implementation we pass flags==0 which causes it to follow symlinks.
err := unix.Statx(unix.AT_FDCWD, path, 0 /* flags */, unix.STATX_ALL, &stx)
if err == unix.ENOSYS || err == unix.EPERM {
panic(fmt.Errorf("statx syscall is not available: %v", err))
} else if err != nil {
return ByHandleFileInformation{}, fmt.Errorf("statx(%s) failed: %v", path, err)
}
// For getting FileAttributes we need to query the CIFS_XATTR_ATTRIB extended attribute.
// Note: This doesn't necessarily cause a new QUERY_PATH_INFO call to the SMB server, instead
// the value cached in the inode (likely as a result of the above Statx call) will be
// returned.
xattrbuf, err := xattr.Get(path, CIFS_XATTR_ATTRIB)
if err != nil {
return ByHandleFileInformation{},
fmt.Errorf("xattr.Get(%s, %s) failed: %v", path, CIFS_XATTR_ATTRIB, err)
}
var info ByHandleFileInformation
info.FileAttributes = binary.LittleEndian.Uint32(xattrbuf)
info.CreationTime = StatxTimestampToFiletime(stx.Btime)
info.LastAccessTime = StatxTimestampToFiletime(stx.Atime)
info.LastWriteTime = StatxTimestampToFiletime(stx.Mtime)
// TODO: Do we need this?
info.VolumeSerialNumber = 0
info.FileSizeHigh = uint32(stx.Size >> 32)
info.FileSizeLow = uint32(stx.Size & 0xFFFFFFFF)
info.NumberOfLinks = stx.Nlink
info.FileIndexHigh = uint32(stx.Ino >> 32)
info.FileIndexLow = uint32(stx.Ino & 0xFFFFFFFF)
return info, nil
}
func CreateFileOfSizeWithWriteThroughOption(destinationPath string, fileSize int64, writeThrough bool, t FolderCreationTracker, forceIfReadOnly bool) (*os.File, error) {
// forceIfReadOnly is not used on this OS
err := CreateParentDirectoryIfNotExist(destinationPath, t)
if err != nil {
return nil, err
}
flags := os.O_RDWR | os.O_CREATE | os.O_TRUNC
if writeThrough {
// TODO: conduct further testing of this code path, on Linux
flags = flags | os.O_SYNC // technically, O_DSYNC may be very slightly faster, but its not exposed in the os package
}
f, err := os.OpenFile(destinationPath, flags, DEFAULT_FILE_PERM)
if err != nil {
return nil, err
}
if fileSize == 0 {
return f, err
}
err = syscall.Fallocate(int(f.Fd()), 0, 0, fileSize)
if err != nil {
// To solve the case that Fallocate cannot work well with cifs/smb3.
if err == syscall.ENOTSUP {
if truncateError := f.Truncate(fileSize); truncateError != nil {
return nil, truncateError
}
} else {
return nil, err
}
}
return f, nil
}
func SetBackupMode(enable bool, fromTo FromTo) error {
// n/a on this platform
return nil
}