@@ -91,39 +91,58 @@ func (sc *sourceCoordinator) getSourceGatewayFor(ctx context.Context, id Project
91
91
}
92
92
sc .srcmut .RUnlock ()
93
93
94
+ // Without a direct match, we must fold the input name to a generally
95
+ // stable, caseless variant and primarily work from that. This ensures that
96
+ // on case-insensitive filesystems, we do not end up with multiple
97
+ // sourceGateways for paths that vary only by case. We perform folding
98
+ // unconditionally, independent of whether the underlying fs is
99
+ // case-sensitive, in order to ensure uniform behavior.
100
+ //
101
+ // This has significant implications. It is effectively deciding that the
102
+ // ProjectRoot portion of import paths are case-insensitive, which is by no
103
+ // means an invariant maintained by all hosting systems. If this presents a
104
+ // problem in practice, then we can explore expanding the deduction system
105
+ // to include case-sensitivity-for-roots metadata and treat it on a
106
+ // host-by-host basis. Such cases would still be rejected by the Go
107
+ // toolchain's compiler, though, and case-sensitivity in root names is
108
+ // likely to be at least frowned on if not disallowed by most hosting
109
+ // systems. So we follow this path, which is both a vastly simpler solution
110
+ // and one that seems quite likely to work in practice.
111
+ foldedNormalName := toFold (normalizedName )
112
+
94
113
// No gateway exists for this path yet; set up a proto, being careful to fold
95
- // together simultaneous attempts on the same path.
114
+ // together simultaneous attempts on the same case-folded path.
96
115
sc .psrcmut .Lock ()
97
- if chans , has := sc .protoSrcs [normalizedName ]; has {
116
+ if chans , has := sc .protoSrcs [foldedNormalName ]; has {
98
117
// Another goroutine is already working on this normalizedName. Fold
99
118
// in with that work by attaching our return channels to the list.
100
119
rc := srcReturnChans {
101
120
ret : make (chan * sourceGateway , 1 ),
102
121
err : make (chan error , 1 ),
103
122
}
104
- sc .protoSrcs [normalizedName ] = append (chans , rc )
123
+ sc .protoSrcs [foldedNormalName ] = append (chans , rc )
105
124
sc .psrcmut .Unlock ()
106
125
return rc .awaitReturn ()
107
126
}
108
127
109
- sc .protoSrcs [normalizedName ] = []srcReturnChans {}
128
+ sc .protoSrcs [foldedNormalName ] = []srcReturnChans {}
110
129
sc .psrcmut .Unlock ()
111
130
112
131
doReturn := func (sg * sourceGateway , err error ) {
113
132
sc .psrcmut .Lock ()
114
133
if sg != nil {
115
- for _ , rc := range sc .protoSrcs [normalizedName ] {
134
+ for _ , rc := range sc .protoSrcs [foldedNormalName ] {
116
135
rc .ret <- sg
117
136
}
118
137
} else if err != nil {
119
- for _ , rc := range sc .protoSrcs [normalizedName ] {
138
+ for _ , rc := range sc .protoSrcs [foldedNormalName ] {
120
139
rc .err <- err
121
140
}
122
141
} else {
123
142
panic ("sg and err both nil" )
124
143
}
125
144
126
- delete (sc .protoSrcs , normalizedName )
145
+ delete (sc .protoSrcs , foldedNormalName )
127
146
sc .psrcmut .Unlock ()
128
147
}
129
148
@@ -142,7 +161,7 @@ func (sc *sourceCoordinator) getSourceGatewayFor(ctx context.Context, id Project
142
161
// and bailing out if we find an entry.
143
162
var srcGate * sourceGateway
144
163
sc .srcmut .RLock ()
145
- if url , has := sc .nameToURL [normalizedName ]; has {
164
+ if url , has := sc .nameToURL [foldedNormalName ]; has {
146
165
if srcGate , has := sc .srcs [url ]; has {
147
166
sc .srcmut .RUnlock ()
148
167
doReturn (srcGate , nil )
@@ -155,10 +174,10 @@ func (sc *sourceCoordinator) getSourceGatewayFor(ctx context.Context, id Project
155
174
srcGate = newSourceGateway (pd .mb , sc .supervisor , sc .cachedir )
156
175
157
176
// The normalized name is usually different from the source URL- e.g.
158
- // github.com/golang/dep/internal/ gps vs. https://github.com/golang/dep/internal /gps. But it's
159
- // possible to arrive here with a full URL as the normalized name - and
160
- // both paths *must* lead to the same sourceGateway instance in order to
161
- // ensure disk access is correctly managed.
177
+ // github.com/sdboyer/ gps vs. https://github.com/sdboyer /gps. But it's
178
+ // possible to arrive here with a full URL as the normalized name - and both
179
+ // paths *must* lead to the same sourceGateway instance in order to ensure
180
+ // disk access is correctly managed.
162
181
//
163
182
// Therefore, we now must query the sourceGateway to get the actual
164
183
// sourceURL it's operating on, and ensure it's *also* registered at
@@ -176,6 +195,11 @@ func (sc *sourceCoordinator) getSourceGatewayFor(ctx context.Context, id Project
176
195
defer sc .srcmut .Unlock ()
177
196
// Record the name -> URL mapping, even if it's a self-mapping.
178
197
sc .nameToURL [normalizedName ] = url
198
+ // Make sure we have both the folded and unfolded names recorded in the map,
199
+ // should they differ.
200
+ if normalizedName != foldedNormalName {
201
+ sc .nameToURL [foldedNormalName ] = url
202
+ }
179
203
180
204
if sa , has := sc .srcs [url ]; has {
181
205
// URL already had an entry in the main map; use that as the result.
0 commit comments