@@ -4,7 +4,9 @@ package vfs
4
4
5
5
import (
6
6
"context"
7
+ "errors"
7
8
"io"
9
+ "io/fs"
8
10
"os"
9
11
"sync"
10
12
@@ -71,23 +73,16 @@ func (s *vfsShm) shmOpen() _ErrorCode {
71
73
return _OK
72
74
}
73
75
74
- // Always open file read-write, as it will be shared.
75
- f , err := os .OpenFile (s .path ,
76
- os .O_RDWR | os .O_CREATE | _O_NOFOLLOW , 0666 )
77
- if err != nil {
78
- return _CANTOPEN
79
- }
80
- // Closes file if it's not nil.
81
- defer func () { f .Close () }()
76
+ vfsShmListMtx .Lock ()
77
+ defer vfsShmListMtx .Unlock ()
82
78
83
- fi , err := f .Stat ()
84
- if err != nil {
79
+ // Stat file without opening it.
80
+ // Closing it would release all POSIX locks on it.
81
+ fi , err := os .Stat (s .path )
82
+ if err != nil && ! errors .Is (err , fs .ErrNotExist ) {
85
83
return _IOERR_FSTAT
86
84
}
87
85
88
- vfsShmListMtx .Lock ()
89
- defer vfsShmListMtx .Unlock ()
90
-
91
86
// Find a shared file, increase the reference count.
92
87
for _ , g := range vfsShmList {
93
88
if g != nil && os .SameFile (fi , g .info ) {
@@ -97,13 +92,35 @@ func (s *vfsShm) shmOpen() _ErrorCode {
97
92
}
98
93
}
99
94
100
- // Lock and truncate the file.
101
- // The lock is only released by closing the file.
102
- if rc := osLock (f , unix .LOCK_EX | unix .LOCK_NB , _IOERR_LOCK ); rc != _OK {
95
+ // Always open file read-write, as it will be shared.
96
+ f , err := os .OpenFile (s .path ,
97
+ os .O_RDWR | os .O_CREATE | _O_NOFOLLOW , 0666 )
98
+ if err != nil {
99
+ return _CANTOPEN
100
+ }
101
+ // Closes file if it's not nil.
102
+ defer func () { f .Close () }()
103
+
104
+ // Dead man's switch.
105
+ if lock , rc := osTestLock (f , _SHM_DMS , 1 ); rc != _OK {
106
+ return _IOERR_LOCK
107
+ } else if lock == unix .F_WRLCK {
108
+ return _BUSY
109
+ } else if lock == unix .F_UNLCK {
110
+ if rc := osWriteLock (f , _SHM_DMS , 1 ); rc != _OK {
111
+ return rc
112
+ }
113
+ if err := f .Truncate (0 ); err != nil {
114
+ return _IOERR_SHMOPEN
115
+ }
116
+ }
117
+ if rc := osReadLock (f , _SHM_DMS , 1 ); rc != _OK {
103
118
return rc
104
119
}
105
- if err := f .Truncate (0 ); err != nil {
106
- return _IOERR_SHMOPEN
120
+
121
+ fi , err = f .Stat ()
122
+ if err != nil {
123
+ return _IOERR_FSTAT
107
124
}
108
125
109
126
// Add the new shared file.
@@ -157,7 +174,30 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
157
174
func (s * vfsShm ) shmLock (offset , n int32 , flags _ShmFlag ) _ErrorCode {
158
175
s .Lock ()
159
176
defer s .Unlock ()
160
- return s .shmMemLock (offset , n , flags )
177
+
178
+ // Check if we could obtain/release the lock locally.
179
+ rc := s .shmMemLock (offset , n , flags )
180
+ if rc != _OK {
181
+ return rc
182
+ }
183
+
184
+ // Obtain/release the appropriate file lock.
185
+ switch {
186
+ case flags & _SHM_UNLOCK != 0 :
187
+ return osUnlock (s .File , _SHM_BASE + int64 (offset ), int64 (n ))
188
+ case flags & _SHM_SHARED != 0 :
189
+ rc = osReadLock (s .File , _SHM_BASE + int64 (offset ), int64 (n ))
190
+ case flags & _SHM_EXCLUSIVE != 0 :
191
+ rc = osWriteLock (s .File , _SHM_BASE + int64 (offset ), int64 (n ))
192
+ default :
193
+ panic (util .AssertErr ())
194
+ }
195
+
196
+ // Release the local lock.
197
+ if rc != _OK {
198
+ s .shmMemLock (offset , n , flags ^ (_SHM_UNLOCK | _SHM_LOCK ))
199
+ }
200
+ return rc
161
201
}
162
202
163
203
func (s * vfsShm ) shmUnmap (delete bool ) {
0 commit comments