6
6
#include " nix/util/finally.hh"
7
7
#include " nix/util/callback.hh"
8
8
#include " nix/util/signals.hh"
9
+ #include " nix/util/sync.hh"
9
10
#include " nix/store/store-reference.hh"
10
11
11
12
#include " store-config-private.hh"
@@ -48,6 +49,61 @@ struct curlFileTransfer : public FileTransfer
48
49
std::random_device rd;
49
50
std::mt19937 mt19937;
50
51
52
+ #if NIX_WITH_S3_SUPPORT
53
+ public:
54
+ // Public method to clear credential provider cache (called from AWS CRT shutdown)
55
+ void clearCredentialCache ();
56
+
57
+ private:
58
+ struct CredentialProviderCache
59
+ {
60
+ // Key: profile name (empty string for default profile)
61
+ std::map<std::string, std::shared_ptr<AwsCredentialProvider>> providers;
62
+ };
63
+
64
+ Sync<CredentialProviderCache> credentialProviderCache;
65
+
66
+ /* *
67
+ * Get or create a credential provider for the given profile.
68
+ * Thread-safe: holds lock for entire duration to prevent duplicate creation.
69
+ */
70
+ std::shared_ptr<AwsCredentialProvider> getOrCreateCredentialProvider (const std::string & profile)
71
+ {
72
+ auto cache (credentialProviderCache.lock ());
73
+
74
+ // Check if provider exists
75
+ auto it = cache->providers .find (profile);
76
+ if (it != cache->providers .end ()) {
77
+ return it->second ;
78
+ }
79
+
80
+ try {
81
+ debug (
82
+ " creating new AWS credential provider for profile '%s'" ,
83
+ profile.empty () ? " (default)" : profile.c_str ());
84
+ auto provider = profile.empty () ? AwsCredentialProvider::createDefault ()
85
+ : AwsCredentialProvider::createProfile (profile);
86
+
87
+ auto sharedProvider = std::shared_ptr<AwsCredentialProvider>(std::move (provider));
88
+ cache->providers [profile] = sharedProvider;
89
+ return sharedProvider;
90
+
91
+ } catch (const AwsAuthError & e) {
92
+ // Don't cache failed providers, just propagate the error
93
+ throw ;
94
+ }
95
+ }
96
+
97
+ /* *
98
+ * Invalidate a cached credential provider (e.g., on auth failure)
99
+ */
100
+ void invalidateCredentialProvider (const std::string & profile)
101
+ {
102
+ auto cache (credentialProviderCache.lock ());
103
+ cache->providers .erase (profile);
104
+ }
105
+ #endif
106
+
51
107
struct TransferItem : public std ::enable_shared_from_this<TransferItem>
52
108
{
53
109
curlFileTransfer & fileTransfer;
@@ -157,9 +213,9 @@ struct curlFileTransfer : public FileTransfer
157
213
// Get credentials
158
214
try {
159
215
std::string profile = parsed.profile .value_or (" " );
160
- auto credProvider = profile.empty () ? AwsCredentialProvider::createDefault ()
161
- : AwsCredentialProvider::createProfile (profile);
162
216
217
+ // Use cached credential provider
218
+ auto credProvider = fileTransfer.getOrCreateCredentialProvider (profile);
163
219
auto creds = credProvider->getCredentials ();
164
220
awsCredentials = creds.accessKeyId + " :" + creds.secretAccessKey ;
165
221
@@ -174,6 +230,11 @@ struct curlFileTransfer : public FileTransfer
174
230
}
175
231
} catch (const AwsAuthError & e) {
176
232
warn (" AWS authentication failed for S3 request %s: %s" , request.uri , e.what ());
233
+
234
+ // Invalidate the cached provider so next request will retry
235
+ std::string profile = parsed.profile .value_or (" " );
236
+ fileTransfer.invalidateCredentialProvider (profile);
237
+
177
238
// Continue without authentication - might be a public bucket
178
239
}
179
240
} catch (std::exception & e) {
@@ -677,6 +738,12 @@ struct curlFileTransfer : public FileTransfer
677
738
678
739
std::thread workerThread;
679
740
741
+ public:
742
+ bool hasQuit ()
743
+ {
744
+ return state_.lock ()->quit ;
745
+ }
746
+
680
747
curlFileTransfer ()
681
748
: mt19937(rd())
682
749
{
@@ -703,9 +770,18 @@ struct curlFileTransfer : public FileTransfer
703
770
~curlFileTransfer ()
704
771
{
705
772
stopWorkerThread ();
706
-
707
773
workerThread.join ();
708
774
775
+ #if NIX_WITH_S3_SUPPORT
776
+ {
777
+ auto cache (credentialProviderCache.lock ());
778
+ // IMPORTANT: We must clear the providers here to ensure they are destroyed
779
+ // BEFORE the AWS CRT ApiHandle is destroyed during static destruction.
780
+ // This prevents a deadlock where AWS CRT waits for resources we're holding.
781
+ cache->providers .clear ();
782
+ }
783
+ #endif
784
+
709
785
if (curlm)
710
786
curl_multi_cleanup (curlm);
711
787
}
@@ -930,12 +1006,25 @@ ref<curlFileTransfer> makeCurlFileTransfer()
930
1006
931
1007
ref<FileTransfer> getFileTransfer ()
932
1008
{
933
- static ref<curlFileTransfer> fileTransfer = makeCurlFileTransfer ();
1009
+ struct StaticFileTransfer
1010
+ {
1011
+ ref<curlFileTransfer> fileTransfer;
1012
+
1013
+ StaticFileTransfer ()
1014
+ : fileTransfer(makeCurlFileTransfer())
1015
+ {
1016
+ }
934
1017
935
- if (fileTransfer->state_ .lock ()->quit )
936
- fileTransfer = makeCurlFileTransfer ();
1018
+ ~StaticFileTransfer () {}
1019
+ };
1020
+
1021
+ static StaticFileTransfer staticFT;
1022
+
1023
+ if (staticFT.fileTransfer ->hasQuit ()) {
1024
+ staticFT.fileTransfer = makeCurlFileTransfer ();
1025
+ }
937
1026
938
- return fileTransfer;
1027
+ return staticFT. fileTransfer ;
939
1028
}
940
1029
941
1030
ref<FileTransfer> makeFileTransfer ()
@@ -1091,4 +1180,28 @@ FileTransferError::FileTransferError(
1091
1180
err.msg = hf;
1092
1181
}
1093
1182
1183
+ #if NIX_WITH_S3_SUPPORT
1184
+ void curlFileTransfer::clearCredentialCache ()
1185
+ {
1186
+ auto cache (credentialProviderCache.lock ());
1187
+ cache->providers .clear ();
1188
+ }
1189
+
1190
+ // Function called by AWS CRT shutdown to clear credential provider cache
1191
+ void cleanupCredentialProviderCache ()
1192
+ {
1193
+ // Access the curlFileTransfer singleton and clear its cache
1194
+ try {
1195
+ auto ft = getFileTransfer ();
1196
+ if (auto curlFt = ft.dynamic_pointer_cast <curlFileTransfer>()) {
1197
+ curlFt->clearCredentialCache ();
1198
+ }
1199
+ } catch (const std::exception & e) {
1200
+ warn (" Error clearing credential cache during AWS CRT shutdown: %s" , e.what ());
1201
+ } catch (...) {
1202
+ warn (" Unknown error clearing credential cache during AWS CRT shutdown" );
1203
+ }
1204
+ }
1205
+ #endif
1206
+
1094
1207
} // namespace nix
0 commit comments