VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/http.cpp@ 56290

Last change on this file since 56290 was 56290, checked in by vboxsync, 10 years ago

IPRT: Updated (C) year.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.1 KB
Line 
1/* $Id: http.cpp 56290 2015-06-09 14:01:31Z vboxsync $ */
2/** @file
3 * IPRT - HTTP communication API.
4 */
5
6/*
7 * Copyright (C) 2012-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <iprt/http.h>
32#include "internal/iprt.h"
33
34#include <iprt/assert.h>
35#include <iprt/env.h>
36#include <iprt/err.h>
37#include <iprt/mem.h>
38#include <iprt/string.h>
39#include <iprt/file.h>
40#include <iprt/stream.h>
41
42#include <curl/curl.h>
43#include <openssl/ssl.h>
44#include "internal/magics.h"
45
46
47/*******************************************************************************
48* Structures and Typedefs *
49*******************************************************************************/
50typedef struct RTHTTPINTERNAL
51{
52 /** Magic value. */
53 uint32_t u32Magic;
54 /** cURL handle. */
55 CURL *pCurl;
56 /** The last response code. */
57 long lLastResp;
58 /** custom headers */
59 struct curl_slist *pHeaders;
60 /** CA certificate for HTTPS authentication check. */
61 char *pcszCAFile;
62 /** Abort the current HTTP request if true. */
63 bool fAbort;
64 /** The location field for 301 responses. */
65 char *pszRedirLocation;
66} RTHTTPINTERNAL;
67typedef RTHTTPINTERNAL *PRTHTTPINTERNAL;
68
69typedef struct RTHTTPMEMCHUNK
70{
71 uint8_t *pu8Mem;
72 size_t cb;
73} RTHTTPMEMCHUNK;
74typedef RTHTTPMEMCHUNK *PRTHTTPMEMCHUNK;
75
76/*******************************************************************************
77* Defined Constants And Macros *
78*******************************************************************************/
79#define CURL_FAILED(rcCurl) (RT_UNLIKELY(rcCurl != CURLE_OK))
80
81/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
82#define RTHTTP_VALID_RETURN_RC(hHttp, rcCurl) \
83 do { \
84 AssertPtrReturn((hHttp), (rcCurl)); \
85 AssertReturn((hHttp)->u32Magic == RTHTTP_MAGIC, (rcCurl)); \
86 } while (0)
87
88/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
89#define RTHTTP_VALID_RETURN(hHTTP) RTHTTP_VALID_RETURN_RC((hHttp), VERR_INVALID_HANDLE)
90
91/** Validates a handle and returns (void) if not valid. */
92#define RTHTTP_VALID_RETURN_VOID(hHttp) \
93 do { \
94 AssertPtrReturnVoid(hHttp); \
95 AssertReturnVoid((hHttp)->u32Magic == RTHTTP_MAGIC); \
96 } while (0)
97
98
99RTR3DECL(int) RTHttpCreate(PRTHTTP phHttp)
100{
101 AssertPtrReturn(phHttp, VERR_INVALID_PARAMETER);
102
103 CURLcode rcCurl = curl_global_init(CURL_GLOBAL_ALL);
104 if (CURL_FAILED(rcCurl))
105 return VERR_HTTP_INIT_FAILED;
106
107 CURL *pCurl = curl_easy_init();
108 if (!pCurl)
109 return VERR_HTTP_INIT_FAILED;
110
111 PRTHTTPINTERNAL pHttpInt = (PRTHTTPINTERNAL)RTMemAllocZ(sizeof(RTHTTPINTERNAL));
112 if (!pHttpInt)
113 return VERR_NO_MEMORY;
114
115 pHttpInt->u32Magic = RTHTTP_MAGIC;
116 pHttpInt->pCurl = pCurl;
117
118 *phHttp = (RTHTTP)pHttpInt;
119
120 return VINF_SUCCESS;
121}
122
123RTR3DECL(void) RTHttpDestroy(RTHTTP hHttp)
124{
125 if (!hHttp)
126 return;
127
128 PRTHTTPINTERNAL pHttpInt = hHttp;
129 RTHTTP_VALID_RETURN_VOID(pHttpInt);
130
131 pHttpInt->u32Magic = RTHTTP_MAGIC_DEAD;
132
133 curl_easy_cleanup(pHttpInt->pCurl);
134
135 if (pHttpInt->pHeaders)
136 curl_slist_free_all(pHttpInt->pHeaders);
137
138 if (pHttpInt->pcszCAFile)
139 RTStrFree(pHttpInt->pcszCAFile);
140
141 if (pHttpInt->pszRedirLocation)
142 RTStrFree(pHttpInt->pszRedirLocation);
143
144 RTMemFree(pHttpInt);
145
146 curl_global_cleanup();
147}
148
149static DECLCALLBACK(size_t) rtHttpWriteData(void *pvBuf, size_t cb, size_t n, void *pvUser)
150{
151 PRTHTTPMEMCHUNK pMem = (PRTHTTPMEMCHUNK)pvUser;
152 size_t cbAll = cb * n;
153
154 pMem->pu8Mem = (uint8_t*)RTMemRealloc(pMem->pu8Mem, pMem->cb + cbAll + 1);
155 if (pMem->pu8Mem)
156 {
157 memcpy(&pMem->pu8Mem[pMem->cb], pvBuf, cbAll);
158 pMem->cb += cbAll;
159 pMem->pu8Mem[pMem->cb] = '\0';
160 }
161 return cbAll;
162}
163
164static DECLCALLBACK(int) rtHttpProgress(void *pData, double DlTotal, double DlNow,
165 double UlTotal, double UlNow)
166{
167 PRTHTTPINTERNAL pHttpInt = (PRTHTTPINTERNAL)pData;
168 AssertReturn(pHttpInt->u32Magic == RTHTTP_MAGIC, 1);
169
170 return pHttpInt->fAbort ? 1 : 0;
171}
172
173RTR3DECL(int) RTHttpAbort(RTHTTP hHttp)
174{
175 PRTHTTPINTERNAL pHttpInt = hHttp;
176 RTHTTP_VALID_RETURN(pHttpInt);
177
178 pHttpInt->fAbort = true;
179
180 return VINF_SUCCESS;
181}
182
183RTR3DECL(int) RTHttpGetRedirLocation(RTHTTP hHttp, char **ppszRedirLocation)
184{
185 PRTHTTPINTERNAL pHttpInt = hHttp;
186 RTHTTP_VALID_RETURN(pHttpInt);
187
188 if (!pHttpInt->pszRedirLocation)
189 return VERR_HTTP_NOT_FOUND;
190
191 *ppszRedirLocation = RTStrDup(pHttpInt->pszRedirLocation);
192 return VINF_SUCCESS;
193}
194
195RTR3DECL(int) RTHttpUseSystemProxySettings(RTHTTP hHttp)
196{
197 PRTHTTPINTERNAL pHttpInt = hHttp;
198 RTHTTP_VALID_RETURN(pHttpInt);
199
200 /*
201 * Very limited right now, just enought to make it work for ourselves.
202 */
203 char szProxy[_1K];
204 int rc = RTEnvGetEx(RTENV_DEFAULT, "http_proxy", szProxy, sizeof(szProxy), NULL);
205 if (RT_SUCCESS(rc))
206 {
207 int rcCurl;
208 if (!strncmp(szProxy, RT_STR_TUPLE("http://")))
209 {
210 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);
211 if (CURL_FAILED(rcCurl))
212 return VERR_INVALID_PARAMETER;
213 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXYPORT, 80);
214 if (CURL_FAILED(rcCurl))
215 return VERR_INVALID_PARAMETER;
216 }
217 else
218 {
219 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);
220 if (CURL_FAILED(rcCurl))
221 return VERR_INVALID_PARAMETER;
222 }
223 }
224 else if (rc == VERR_ENV_VAR_NOT_FOUND)
225 rc = VINF_SUCCESS;
226
227 return rc;
228}
229
230RTR3DECL(int) RTHttpSetProxy(RTHTTP hHttp, const char *pcszProxy, uint32_t uPort,
231 const char *pcszProxyUser, const char *pcszProxyPwd)
232{
233 PRTHTTPINTERNAL pHttpInt = hHttp;
234 RTHTTP_VALID_RETURN(pHttpInt);
235 AssertPtrReturn(pcszProxy, VERR_INVALID_PARAMETER);
236
237 int rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXY, pcszProxy);
238 if (CURL_FAILED(rcCurl))
239 return VERR_INVALID_PARAMETER;
240
241 if (uPort != 0)
242 {
243 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXYPORT, (long)uPort);
244 if (CURL_FAILED(rcCurl))
245 return VERR_INVALID_PARAMETER;
246 }
247
248 if (pcszProxyUser && pcszProxyPwd)
249 {
250 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXYUSERNAME, pcszProxyUser);
251 if (CURL_FAILED(rcCurl))
252 return VERR_INVALID_PARAMETER;
253
254 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXYPASSWORD, pcszProxyPwd);
255 if (CURL_FAILED(rcCurl))
256 return VERR_INVALID_PARAMETER;
257 }
258
259 return VINF_SUCCESS;
260}
261
262RTR3DECL(int) RTHttpSetHeaders(RTHTTP hHttp, size_t cHeaders, const char * const *papszHeaders)
263{
264 PRTHTTPINTERNAL pHttpInt = hHttp;
265 RTHTTP_VALID_RETURN(pHttpInt);
266
267 if (!cHeaders)
268 {
269 if (pHttpInt->pHeaders)
270 curl_slist_free_all(pHttpInt->pHeaders);
271 pHttpInt->pHeaders = 0;
272 return VINF_SUCCESS;
273 }
274
275 struct curl_slist *pHeaders = NULL;
276 for (size_t i = 0; i < cHeaders; i++)
277 pHeaders = curl_slist_append(pHeaders, papszHeaders[i]);
278
279 pHttpInt->pHeaders = pHeaders;
280 int rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_HTTPHEADER, pHeaders);
281 if (CURL_FAILED(rcCurl))
282 return VERR_INVALID_PARAMETER;
283
284 return VINF_SUCCESS;
285}
286
287RTR3DECL(int) RTHttpCertDigest(RTHTTP hHttp, char *pcszCert, size_t cbCert,
288 uint8_t **pabSha1, size_t *pcbSha1,
289 uint8_t **pabSha512, size_t *pcbSha512)
290{
291 int rc = VINF_SUCCESS;
292
293 BIO *cert = BIO_new_mem_buf(pcszCert, (int)cbCert);
294 if (cert)
295 {
296 X509 *crt = NULL;
297 if (PEM_read_bio_X509(cert, &crt, NULL, NULL))
298 {
299 unsigned cb;
300 unsigned char md[EVP_MAX_MD_SIZE];
301
302 int rc1 = X509_digest(crt, EVP_sha1(), md, &cb);
303 if (rc1 > 0)
304 {
305 *pabSha1 = (uint8_t*)RTMemAlloc(cb);
306 if (*pabSha1)
307 {
308 memcpy(*pabSha1, md, cb);
309 *pcbSha1 = cb;
310
311 rc1 = X509_digest(crt, EVP_sha512(), md, &cb);
312 if (rc1 > 0)
313 {
314 *pabSha512 = (uint8_t*)RTMemAlloc(cb);
315 if (*pabSha512)
316 {
317 memcpy(*pabSha512, md, cb);
318 *pcbSha512 = cb;
319 }
320 else
321 rc = VERR_NO_MEMORY;
322 }
323 else
324 rc = VERR_HTTP_CACERT_WRONG_FORMAT;
325
326 if (RT_FAILURE(rc))
327 RTMemFree(*pabSha1);
328 }
329 else
330 rc = VERR_NO_MEMORY;
331 }
332 else
333 rc = VERR_HTTP_CACERT_WRONG_FORMAT;
334 X509_free(crt);
335 }
336 else
337 rc = VERR_HTTP_CACERT_WRONG_FORMAT;
338 BIO_free(cert);
339 }
340 else
341 rc = VERR_INTERNAL_ERROR;
342
343 return rc;
344}
345
346RTR3DECL(int) RTHttpSetCAFile(RTHTTP hHttp, const char *pcszCAFile)
347{
348 PRTHTTPINTERNAL pHttpInt = hHttp;
349 RTHTTP_VALID_RETURN(pHttpInt);
350
351 if (pHttpInt->pcszCAFile)
352 RTStrFree(pHttpInt->pcszCAFile);
353 pHttpInt->pcszCAFile = RTStrDup(pcszCAFile);
354 if (!pHttpInt->pcszCAFile)
355 return VERR_NO_MEMORY;
356
357 return VINF_SUCCESS;
358}
359
360
361/**
362 * Figures out the IPRT status code for a GET.
363 *
364 * @returns IPRT status code.
365 * @param pHttpInt HTTP instance.
366 * @param rcCurl What curl returned.
367 */
368static int rtHttpGetCalcStatus(PRTHTTPINTERNAL pHttpInt, int rcCurl)
369{
370 int rc = VERR_INTERNAL_ERROR;
371
372 if (pHttpInt->pszRedirLocation)
373 {
374 RTStrFree(pHttpInt->pszRedirLocation);
375 pHttpInt->pszRedirLocation = NULL;
376 }
377 if (rcCurl == CURLE_OK)
378 {
379 curl_easy_getinfo(pHttpInt->pCurl, CURLINFO_RESPONSE_CODE, &pHttpInt->lLastResp);
380 switch (pHttpInt->lLastResp)
381 {
382 case 200:
383 /* OK, request was fulfilled */
384 case 204:
385 /* empty response */
386 rc = VINF_SUCCESS;
387 break;
388 case 301:
389 {
390 const char *pszRedirect;
391 curl_easy_getinfo(pHttpInt->pCurl, CURLINFO_REDIRECT_URL, &pszRedirect);
392 size_t cb = strlen(pszRedirect);
393 if (cb > 0 && cb < 2048)
394 pHttpInt->pszRedirLocation = RTStrDup(pszRedirect);
395 rc = VERR_HTTP_REDIRECTED;
396 break;
397 }
398 case 400:
399 /* bad request */
400 rc = VERR_HTTP_BAD_REQUEST;
401 break;
402 case 403:
403 /* forbidden, authorization will not help */
404 rc = VERR_HTTP_ACCESS_DENIED;
405 break;
406 case 404:
407 /* URL not found */
408 rc = VERR_HTTP_NOT_FOUND;
409 break;
410 }
411 }
412 else
413 {
414 switch (rcCurl)
415 {
416 case CURLE_URL_MALFORMAT:
417 case CURLE_COULDNT_RESOLVE_HOST:
418 rc = VERR_HTTP_NOT_FOUND;
419 break;
420 case CURLE_COULDNT_CONNECT:
421 rc = VERR_HTTP_COULDNT_CONNECT;
422 break;
423 case CURLE_SSL_CONNECT_ERROR:
424 rc = VERR_HTTP_SSL_CONNECT_ERROR;
425 break;
426 case CURLE_SSL_CACERT:
427 /* The peer certificate cannot be authenticated with the CA certificates
428 * set by RTHttpSetCAFile(). We need other or additional CA certificates. */
429 rc = VERR_HTTP_CACERT_CANNOT_AUTHENTICATE;
430 break;
431 case CURLE_SSL_CACERT_BADFILE:
432 /* CAcert file (see RTHttpSetCAFile()) has wrong format */
433 rc = VERR_HTTP_CACERT_WRONG_FORMAT;
434 break;
435 case CURLE_ABORTED_BY_CALLBACK:
436 /* forcefully aborted */
437 rc = VERR_HTTP_ABORTED;
438 break;
439 default:
440 break;
441 }
442 }
443
444 return rc;
445}
446
447RTR3DECL(int) rtHttpGet(RTHTTP hHttp, const char *pcszUrl, uint8_t **ppvResponse, size_t *pcb)
448{
449 PRTHTTPINTERNAL pHttpInt = hHttp;
450 RTHTTP_VALID_RETURN(pHttpInt);
451
452 pHttpInt->fAbort = false;
453
454 int rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_URL, pcszUrl);
455 if (CURL_FAILED(rcCurl))
456 return VERR_INVALID_PARAMETER;
457
458#if 0
459 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_VERBOSE, 1);
460 if (CURL_FAILED(rcCurl))
461 return VERR_INVALID_PARAMETER;
462#endif
463
464 const char *pcszCAFile = "/etc/ssl/certs/ca-certificates.crt";
465 if (pHttpInt->pcszCAFile)
466 pcszCAFile = pHttpInt->pcszCAFile;
467 if (RTFileExists(pcszCAFile))
468 {
469 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_CAINFO, pcszCAFile);
470 if (CURL_FAILED(rcCurl))
471 return VERR_INTERNAL_ERROR;
472 }
473
474 RTHTTPMEMCHUNK chunk = { NULL, 0 };
475 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_WRITEFUNCTION, &rtHttpWriteData);
476 if (CURL_FAILED(rcCurl))
477 return VERR_INTERNAL_ERROR;
478 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_WRITEDATA, (void*)&chunk);
479 if (CURL_FAILED(rcCurl))
480 return VERR_INTERNAL_ERROR;
481 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROGRESSFUNCTION, &rtHttpProgress);
482 if (CURL_FAILED(rcCurl))
483 return VERR_INTERNAL_ERROR;
484 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROGRESSDATA, (void*)pHttpInt);
485 if (CURL_FAILED(rcCurl))
486 return VERR_INTERNAL_ERROR;
487 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_NOPROGRESS, (long)0);
488 if (CURL_FAILED(rcCurl))
489 return VERR_INTERNAL_ERROR;
490 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_SSLVERSION, (long)CURL_SSLVERSION_TLSv1);
491 if (CURL_FAILED(rcCurl))
492 return VERR_INVALID_PARAMETER;
493
494 rcCurl = curl_easy_perform(pHttpInt->pCurl);
495 int rc = rtHttpGetCalcStatus(pHttpInt, rcCurl);
496 *ppvResponse = chunk.pu8Mem;
497 *pcb = chunk.cb;
498
499 return rc;
500}
501
502
503RTR3DECL(int) RTHttpGetText(RTHTTP hHttp, const char *pcszUrl, char **ppszResponse)
504{
505 uint8_t *pv;
506 size_t cb;
507 int rc = rtHttpGet(hHttp, pcszUrl, &pv, &cb);
508 *ppszResponse = (char*)pv;
509 return rc;
510}
511
512
513RTR3DECL(int) RTHttpGetBinary(RTHTTP hHttp, const char *pcszUrl, void **ppvResponse, size_t *pcb)
514{
515 return rtHttpGet(hHttp, pcszUrl, (uint8_t**)ppvResponse, pcb);
516}
517
518
519static size_t rtHttpWriteDataToFile(void *pvBuf, size_t cb, size_t n, void *pvUser)
520{
521 size_t cbAll = cb * n;
522 RTFILE hFile = (RTFILE)(intptr_t)pvUser;
523
524 size_t cbWritten = 0;
525 int rc = RTFileWrite(hFile, pvBuf, cbAll, &cbWritten);
526 if (RT_SUCCESS(rc))
527 return cbWritten;
528 return 0;
529}
530
531
532RTR3DECL(int) RTHttpGetFile(RTHTTP hHttp, const char *pszUrl, const char *pszDstFile)
533{
534 PRTHTTPINTERNAL pHttpInt = hHttp;
535 RTHTTP_VALID_RETURN(pHttpInt);
536
537 /*
538 * Set up the request.
539 */
540 pHttpInt->fAbort = false;
541
542 int rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_URL, pszUrl);
543 if (CURL_FAILED(rcCurl))
544 return VERR_INVALID_PARAMETER;
545
546#if 0
547 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_VERBOSE, 1);
548 if (CURL_FAILED(rcCurl))
549 return VERR_INVALID_PARAMETER;
550#endif
551
552 const char *pcszCAFile = "/etc/ssl/certs/ca-certificates.crt";
553 if (pHttpInt->pcszCAFile)
554 pcszCAFile = pHttpInt->pcszCAFile;
555 if (RTFileExists(pcszCAFile))
556 {
557 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_CAINFO, pcszCAFile);
558 if (CURL_FAILED(rcCurl))
559 return VERR_INTERNAL_ERROR;
560 }
561
562 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_WRITEFUNCTION, &rtHttpWriteDataToFile);
563 if (CURL_FAILED(rcCurl))
564 return VERR_INTERNAL_ERROR;
565 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROGRESSFUNCTION, &rtHttpProgress);
566 if (CURL_FAILED(rcCurl))
567 return VERR_INTERNAL_ERROR;
568 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROGRESSDATA, (void*)pHttpInt);
569 if (CURL_FAILED(rcCurl))
570 return VERR_INTERNAL_ERROR;
571 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_NOPROGRESS, (long)0);
572 if (CURL_FAILED(rcCurl))
573 return VERR_INTERNAL_ERROR;
574
575 /*
576 * Open the output file.
577 */
578 RTFILE hFile;
579 int rc = RTFileOpen(&hFile, pszDstFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_READWRITE);
580 if (RT_SUCCESS(rc))
581 {
582 rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_WRITEDATA, (void *)(uintptr_t)hFile);
583 if (!CURL_FAILED(rcCurl))
584 {
585 /*
586 * Perform the request.
587 */
588 rcCurl = curl_easy_perform(pHttpInt->pCurl);
589 rc = rtHttpGetCalcStatus(pHttpInt, rcCurl);
590 }
591 else
592 rc = VERR_INTERNAL_ERROR;
593
594 int rc2 = RTFileClose(hFile);
595 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
596 rc = rc2;
597 }
598
599 return rc;
600}
601
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette