1 package sequential
2
3 import (
4 "os"
5 "path/filepath"
6 "strconv"
7 "sync"
8 "syscall"
9 "time"
10 "unsafe"
11
12 "golang.org/x/sys/windows"
13 )
14
15
16
17
18
19
20 func Create(name string) (*os.File, error) {
21 return OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0)
22 }
23
24
25
26
27
28 func Open(name string) (*os.File, error) {
29 return OpenFile(name, os.O_RDONLY, 0)
30 }
31
32
33
34
35 func OpenFile(name string, flag int, _ os.FileMode) (*os.File, error) {
36 if name == "" {
37 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT}
38 }
39 r, err := openFileSequential(name, flag, 0)
40 if err == nil {
41 return r, nil
42 }
43 return nil, &os.PathError{Op: "open", Path: name, Err: err}
44 }
45
46 func openFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) {
47 r, e := openSequential(name, flag|windows.O_CLOEXEC, 0)
48 if e != nil {
49 return nil, e
50 }
51 return os.NewFile(uintptr(r), name), nil
52 }
53
54 func makeInheritSa() *windows.SecurityAttributes {
55 var sa windows.SecurityAttributes
56 sa.Length = uint32(unsafe.Sizeof(sa))
57 sa.InheritHandle = 1
58 return &sa
59 }
60
61 func openSequential(path string, mode int, _ uint32) (fd windows.Handle, err error) {
62 if len(path) == 0 {
63 return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND
64 }
65 pathp, err := windows.UTF16PtrFromString(path)
66 if err != nil {
67 return windows.InvalidHandle, err
68 }
69 var access uint32
70 switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) {
71 case windows.O_RDONLY:
72 access = windows.GENERIC_READ
73 case windows.O_WRONLY:
74 access = windows.GENERIC_WRITE
75 case windows.O_RDWR:
76 access = windows.GENERIC_READ | windows.GENERIC_WRITE
77 }
78 if mode&windows.O_CREAT != 0 {
79 access |= windows.GENERIC_WRITE
80 }
81 if mode&windows.O_APPEND != 0 {
82 access &^= windows.GENERIC_WRITE
83 access |= windows.FILE_APPEND_DATA
84 }
85 sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE)
86 var sa *windows.SecurityAttributes
87 if mode&windows.O_CLOEXEC == 0 {
88 sa = makeInheritSa()
89 }
90 var createmode uint32
91 switch {
92 case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL):
93 createmode = windows.CREATE_NEW
94 case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC):
95 createmode = windows.CREATE_ALWAYS
96 case mode&windows.O_CREAT == windows.O_CREAT:
97 createmode = windows.OPEN_ALWAYS
98 case mode&windows.O_TRUNC == windows.O_TRUNC:
99 createmode = windows.TRUNCATE_EXISTING
100 default:
101 createmode = windows.OPEN_EXISTING
102 }
103
104
105 const fileFlagSequentialScan = 0x08000000
106 h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0)
107 return h, e
108 }
109
110
111 var rand uint32
112 var randmu sync.Mutex
113
114 func reseed() uint32 {
115 return uint32(time.Now().UnixNano() + int64(os.Getpid()))
116 }
117
118 func nextSuffix() string {
119 randmu.Lock()
120 r := rand
121 if r == 0 {
122 r = reseed()
123 }
124 r = r*1664525 + 1013904223
125 rand = r
126 randmu.Unlock()
127 return strconv.Itoa(int(1e9 + r%1e9))[1:]
128 }
129
130
131
132
133
134
135
136
137
138
139
140
141 func CreateTemp(dir, prefix string) (f *os.File, err error) {
142 if dir == "" {
143 dir = os.TempDir()
144 }
145
146 nconflict := 0
147 for i := 0; i < 10000; i++ {
148 name := filepath.Join(dir, prefix+nextSuffix())
149 f, err = OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600)
150 if os.IsExist(err) {
151 if nconflict++; nconflict > 10 {
152 randmu.Lock()
153 rand = reseed()
154 randmu.Unlock()
155 }
156 continue
157 }
158 break
159 }
160 return
161 }
162
View as plain text