Skip to content

Commit c2a48be

Browse files
committed
add FILE_SHARE_DELETE flag when opening file
1 parent 22e5195 commit c2a48be

7 files changed

+151
-2
lines changed

tail.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func TailFile(filename string, config Config) (*Tail, error) {
104104

105105
if t.MustExist {
106106
var err error
107-
t.file, err = os.Open(t.Filename)
107+
t.file, err = OpenFile(t.Filename)
108108
if err != nil {
109109
return nil, err
110110
}
@@ -149,7 +149,7 @@ func (tail *Tail) reopen() error {
149149
}
150150
for {
151151
var err error
152-
tail.file, err = os.Open(tail.Filename)
152+
tail.file, err = OpenFile(tail.Filename)
153153
if err != nil {
154154
if os.IsNotExist(err) {
155155
tail.Logger.Printf("Waiting for %s to appear...", tail.Filename)

tail_linux.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// +build linux
2+
3+
package tail
4+
5+
import (
6+
"os"
7+
)
8+
9+
func OpenFile(name string) (file *os.File, err error) {
10+
return os.Open(name)
11+
}

tail_windows.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// +build windows
2+
3+
package tail
4+
5+
import (
6+
"github.com/ActiveState/tail/winfile"
7+
"os"
8+
)
9+
10+
func OpenFile(name string) (file *os.File, err error) {
11+
return winfile.OpenFile(name, os.O_RDONLY, 0)
12+
}

watch/polling.go

+7
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, origFi os.FileInfo) *Fi
5151
go func() {
5252
defer changes.Close()
5353

54+
var retry int = 0
55+
5456
prevSize := fw.Size
5557
for {
5658
select {
@@ -67,6 +69,11 @@ func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, origFi os.FileInfo) *Fi
6769
changes.NotifyDeleted()
6870
return
6971
}
72+
73+
if permissionErrorRetry(err, &retry) {
74+
continue
75+
}
76+
7077
// XXX: report this error back to the user
7178
util.Fatal("Failed to stat file %v: %v", fw.Filename, err)
7279
}

watch/polling_linux.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
2+
// +build linux
3+
4+
package watch
5+
6+
func permissionErrorRetry(err error, retry *int) bool {
7+
// No need for this on linux, don't retry
8+
return false
9+
}

watch/polling_windows.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// +build windows
2+
3+
package watch
4+
5+
import (
6+
"os"
7+
)
8+
9+
const permissionDeniedRetryCount int = 5
10+
11+
func permissionErrorRetry(err error, retry *int) bool {
12+
if os.IsPermission(err) && *retry < permissionDeniedRetryCount {
13+
// While pooling a file that does not exist yet, but will be created by another process we can get Permission Denied
14+
(*retry)++
15+
return true
16+
}
17+
return false
18+
}

winfile/winfile.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// +build windows
2+
3+
package winfile
4+
5+
import (
6+
"os"
7+
"syscall"
8+
"unsafe"
9+
)
10+
11+
// issue also described here
12+
//https://codereview.appspot.com/8203043/
13+
14+
// https://github.com/jnwhiteh/golang/blob/master/src/pkg/syscall/syscall_windows.go#L218
15+
func Open(path string, mode int, perm uint32) (fd syscall.Handle, err error) {
16+
if len(path) == 0 {
17+
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
18+
}
19+
pathp, err := syscall.UTF16PtrFromString(path)
20+
if err != nil {
21+
return syscall.InvalidHandle, err
22+
}
23+
var access uint32
24+
switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
25+
case syscall.O_RDONLY:
26+
access = syscall.GENERIC_READ
27+
case syscall.O_WRONLY:
28+
access = syscall.GENERIC_WRITE
29+
case syscall.O_RDWR:
30+
access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
31+
}
32+
if mode&syscall.O_CREAT != 0 {
33+
access |= syscall.GENERIC_WRITE
34+
}
35+
if mode&syscall.O_APPEND != 0 {
36+
access &^= syscall.GENERIC_WRITE
37+
access |= syscall.FILE_APPEND_DATA
38+
}
39+
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE)
40+
var sa *syscall.SecurityAttributes
41+
if mode&syscall.O_CLOEXEC == 0 {
42+
sa = makeInheritSa()
43+
}
44+
var createmode uint32
45+
switch {
46+
case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
47+
createmode = syscall.CREATE_NEW
48+
case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC):
49+
createmode = syscall.CREATE_ALWAYS
50+
case mode&syscall.O_CREAT == syscall.O_CREAT:
51+
createmode = syscall.OPEN_ALWAYS
52+
case mode&syscall.O_TRUNC == syscall.O_TRUNC:
53+
createmode = syscall.TRUNCATE_EXISTING
54+
default:
55+
createmode = syscall.OPEN_EXISTING
56+
}
57+
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, syscall.FILE_ATTRIBUTE_NORMAL, 0)
58+
return h, e
59+
}
60+
61+
// https://github.com/jnwhiteh/golang/blob/master/src/pkg/syscall/syscall_windows.go#L211
62+
func makeInheritSa() *syscall.SecurityAttributes {
63+
var sa syscall.SecurityAttributes
64+
sa.Length = uint32(unsafe.Sizeof(sa))
65+
sa.InheritHandle = 1
66+
return &sa
67+
}
68+
69+
// https://github.com/jnwhiteh/golang/blob/master/src/pkg/os/file_windows.go#L133
70+
func OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error) {
71+
r, e := Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
72+
if e != nil {
73+
return nil, e
74+
}
75+
return os.NewFile(uintptr(r), name), nil
76+
}
77+
78+
// https://github.com/jnwhiteh/golang/blob/master/src/pkg/os/file_posix.go#L61
79+
func syscallMode(i os.FileMode) (o uint32) {
80+
o |= uint32(i.Perm())
81+
if i&os.ModeSetuid != 0 {
82+
o |= syscall.S_ISUID
83+
}
84+
if i&os.ModeSetgid != 0 {
85+
o |= syscall.S_ISGID
86+
}
87+
if i&os.ModeSticky != 0 {
88+
o |= syscall.S_ISVTX
89+
}
90+
// No mapping for Go's ModeTemporary (plan9 only).
91+
return
92+
}

0 commit comments

Comments
 (0)