• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #3837

03 Apr 2025 01:31PM UTC coverage: 61.991% (+26.1%) from 35.847%
#3837

push

travis-ci

web-flow
Merge pull request #30644 from taosdata/merge/mainto3.0

merge: from main to 3.0 branch

152850 of 315065 branches covered (48.51%)

Branch coverage included in aggregate %.

402 of 459 new or added lines in 2 files covered. (87.58%)

284 existing lines in 50 files now uncovered.

237727 of 314991 relevant lines covered (75.47%)

5799871.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.86
/source/os/src/osFile.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15
#define ALLOW_FORBID_FUNC
16
#include "os.h"
17
#include "osSemaphore.h"
18
#include "tdef.h"
19
#include "zlib.h"
20

21
#ifdef WINDOWS
22
#include <WinBase.h>
23
#include <io.h>
24
#include <ktmw32.h>
25
#include <windows.h>
26
#define F_OK 0
27
#define X_OK 1
28
#define W_OK 2
29
#define R_OK 4
30

31
#define _SEND_FILE_STEP_ 1024
32

33
#else
34
#include <fcntl.h>
35
#ifndef TD_ASTRA
36
#include <sys/file.h>
37

38
#if !defined(_TD_DARWIN_64)
39
#include <sys/sendfile.h>
40
#endif
41
#endif
42
#include <sys/stat.h>
43
#include <unistd.h>
44
#define LINUX_FILE_NO_TEXT_OPTION 0
45
#define O_TEXT                    LINUX_FILE_NO_TEXT_OPTION
46

47
#define _SEND_FILE_STEP_ 1000
48
#endif
49

50
typedef int32_t FileFd;
51

52
#ifdef WINDOWS
53
typedef struct TdFile {
54
  TdThreadRwlock rwlock;
55
  HANDLE         hFile;
56
  FILE          *fp;
57
  int32_t        tdFileOptions;
58
} TdFile;
59
#else
60
typedef struct TdFile {
61
  TdThreadRwlock rwlock;
62
  FileFd         fd;
63
  FILE          *fp;
64
} TdFile;
65
#endif  // WINDOWS
66

67
#define FILE_WITH_LOCK 1
68

69
#ifdef BUILD_WITH_RAND_ERR
70
#define STUB_RAND_IO_ERR(ret)                                \
71
  if (tsEnableRandErr && (tsRandErrScope & RAND_ERR_FILE)) { \
72
    uint32_t r = taosRand() % tsRandErrDivisor;              \
73
    if ((r + 1) <= tsRandErrChance) {                        \
74
      errno = EIO;                                           \
75
      terrno = TAOS_SYSTEM_ERROR(errno);                     \
76
      return (ret);                                          \
77
    }                                                        \
78
  }
79
#else
80
#define STUB_RAND_IO_ERR(ret)
81
#endif
82

83
void taosGetTmpfilePath(const char *inputTmpDir, const char *fileNamePrefix, char *dstPath) {
12,944✔
84
  if (inputTmpDir == NULL || fileNamePrefix == NULL) return;
12,944!
85
#ifdef WINDOWS
86

87
  char tmpPath[PATH_MAX];
88

89
  int32_t len = (int32_t)strlen(inputTmpDir);
90
  memcpy(tmpPath, inputTmpDir, len);
91

92
  if (tmpPath[len - 1] != '/' && tmpPath[len - 1] != '\\') {
93
    tmpPath[len++] = '\\';
94
  }
95

96
  snprintf(tmpPath + len, sizeof(tmpPath) - len, "%s%s%s", TD_TMP_FILE_PREFIX, fileNamePrefix, "-%d-%s");
97

98
  char rand[8] = {0};
99
  taosRandStr(rand, tListLen(rand) - 1);
100
  snprintf(dstPath, PATH_MAX, tmpPath, taosGetPId(), rand);
101

102
#else
103

104
  char    tmpPath[PATH_MAX];
105
  int32_t len = strlen(inputTmpDir);
12,944✔
106
  (void)memcpy(tmpPath, inputTmpDir, len);
12,944✔
107
  static uint64_t seqId = 0;
108

109
  if (tmpPath[len - 1] != '/') {
12,944!
110
    tmpPath[len++] = '/';
×
111
  }
112

113
  snprintf(tmpPath + len, sizeof(tmpPath) - len, "%s%s%s", TD_TMP_FILE_PREFIX, fileNamePrefix, "-%d-%s");
12,944✔
114

115
  char rand[32] = {0};
12,944✔
116

117
  (void)snprintf(rand, sizeof(rand), "%" PRIu64, atomic_add_fetch_64(&seqId, 1));
12,944✔
118

119
  (void)snprintf(dstPath, PATH_MAX, tmpPath, taosGetPId(), rand);
12,954✔
120

121
#endif
122
}
123

124
int64_t taosCopyFile(const char *from, const char *to) {
67✔
125
  if (from == NULL || to == NULL) {
67!
126
    terrno = TSDB_CODE_INVALID_PARA;
×
127
    return -1;
×
128
  }
129
#ifdef WINDOWS
130
  if (CopyFile(from, to, 0)) {
131
    return 1;
132
  } else {
133
    terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
134
    return -1;
135
  }
136
#else
137
  char    buffer[4096];
138
  int64_t size = 0;
67✔
139
  int64_t bytes;
140
  int32_t code = TSDB_CODE_SUCCESS;
67✔
141

142
  TdFilePtr pFileFrom = NULL;
67✔
143
  TdFilePtr pFileTo = NULL;
67✔
144
  pFileFrom = taosOpenFile(from, TD_FILE_READ);
67✔
145
  if (pFileFrom == NULL) {
67!
146
    code = terrno;
×
147
    goto _err;
×
148
  }
149

150
  pFileTo = taosOpenFile(to, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_EXCL);
67✔
151
  if (pFileTo == NULL) {
67!
152
    code = terrno;
×
153
    goto _err;
×
154
  }
155

156
  while (true) {
157
    bytes = taosReadFile(pFileFrom, buffer, sizeof(buffer));
77✔
158
    if (bytes < 0) {
77!
159
      code = terrno;
×
160
      goto _err;
×
161
    }
162

163
    if (bytes == 0) break;
77!
164

165
    size += bytes;
77✔
166

167
    if (taosWriteFile(pFileTo, (void *)buffer, bytes) < bytes) {
77!
168
      code = terrno;
×
169
      goto _err;
×
170
    }
171
    if (bytes < sizeof(buffer)) break;
77✔
172
  }
173

174
  code = taosFsyncFile(pFileTo);
67✔
175
  if (code != 0) {
67!
176
    goto _err;
×
177
  }
178

179
  TAOS_UNUSED(taosCloseFile(&pFileFrom));
67✔
180
  TAOS_UNUSED(taosCloseFile(&pFileTo));
67✔
181

182
  if (code != 0) {
67!
183
    terrno = code;
×
184
    return -1;
×
185
  }
186

187
  return size;
67✔
188

189
_err:
×
190

191
  if (pFileFrom != NULL) TAOS_SKIP_ERROR(taosCloseFile(&pFileFrom));
×
192
  if (pFileTo != NULL) TAOS_SKIP_ERROR(taosCloseFile(&pFileTo));
×
193
  /* coverity[+retval] */
194
  TAOS_SKIP_ERROR(taosRemoveFile(to));
×
195

196
  terrno = code;
×
197
  return -1;
×
198
#endif
199
}
200

201
TdFilePtr taosCreateFile(const char *path, int32_t tdFileOptions) {
×
202
  if (path == NULL) {
×
203
    terrno = TSDB_CODE_INVALID_PARA;
×
204
    return NULL;
×
205
  }
206
  TdFilePtr fp = taosOpenFile(path, tdFileOptions);
×
207
  if (!fp) {
×
208
    if (terrno == TAOS_SYSTEM_ERROR(ENOENT)) {
×
209
      // Try to create directory recursively
210
      char s[PATH_MAX];
211
      tstrncpy(s, path, sizeof(s));
×
212
      if (taosMulMkDir(taosDirName(s)) != 0) {
×
213
        return NULL;
×
214
      }
215
      fp = taosOpenFile(path, tdFileOptions);
×
216
      if (!fp) {
×
217
        return NULL;
×
218
      }
219
    }
220
  }
221

222
  return fp;
×
223
}
224

225
int32_t taosRemoveFile(const char *path) {
246,972✔
226
  OS_PARAM_CHECK(path);
246,972!
227
  int32_t code = remove(path);
246,972✔
228
  if (-1 == code) {
246,988✔
229
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
12,940✔
230
    return terrno;
12,944✔
231
  }
232
  return code;
234,048✔
233
}
234

235
int32_t taosRenameFile(const char *oldName, const char *newName) {
216,814✔
236
  OS_PARAM_CHECK(oldName);
216,814!
237
  OS_PARAM_CHECK(newName);
216,814!
238
#ifdef WINDOWS
239
  bool finished = false;
240

241
  HANDLE transactionHandle = CreateTransaction(NULL, NULL, 0, 0, 0, INFINITE, NULL);
242
  if (transactionHandle == INVALID_HANDLE_VALUE) {
243
    DWORD error = GetLastError();
244
    terrno = TAOS_SYSTEM_WINAPI_ERROR(error);
245
    return terrno;
246
  }
247

248
  BOOL result = MoveFileTransacted(oldName, newName, NULL, NULL, MOVEFILE_REPLACE_EXISTING, transactionHandle);
249

250
  if (result) {
251
    finished = CommitTransaction(transactionHandle);
252
    if (!finished) {
253
      DWORD error = GetLastError();
254
      terrno = TAOS_SYSTEM_WINAPI_ERROR(error);
255
    }
256
  } else {
257
    RollbackTransaction(transactionHandle);
258
    DWORD error = GetLastError();
259
    terrno = TAOS_SYSTEM_WINAPI_ERROR(error);
260
    finished = false;
261
  }
262

263
  CloseHandle(transactionHandle);
264

265
  return finished ? 0 : terrno;
266
#else
267
#ifdef TD_ASTRA // TD_ASTRA_TODO
268
  if (taosCheckExistFile(newName)) taosRemoveFile(newName);
269
#endif
270
  int32_t code = rename(oldName, newName);
216,814✔
271
  if (-1 == code) {
216,811✔
272
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
280✔
273
    return terrno;
280✔
274
  }
275

276
  return TSDB_CODE_SUCCESS;
216,531✔
277
#endif
278
}
279

280
int32_t taosStatFile(const char *path, int64_t *size, int64_t *mtime, int64_t *atime) {
463,395✔
281
  OS_PARAM_CHECK(path);
463,395!
282
#ifdef WINDOWS
283
  struct _stati64 fileStat;
284
  int32_t         code = _stati64(path, &fileStat);
285
#else
286
  struct stat fileStat;
287
  int32_t     code = stat(path, &fileStat);
463,395✔
288
#endif
289
  if (-1 == code) {
463,402✔
290
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
63,594✔
291
    return terrno;
63,572✔
292
  }
293

294
  if (size != NULL) {
399,808✔
295
    *size = fileStat.st_size;
372,301✔
296
  }
297

298
  if (mtime != NULL) {
399,808✔
299
    *mtime = fileStat.st_mtime;
15,370✔
300
  }
301

302
  if (atime != NULL) {
399,808!
303
    *atime = fileStat.st_atime;
×
304
  }
305

306
  return 0;
399,808✔
307
}
308

309
int32_t taosGetFileDiskID(const char *path, int64_t *diskid) {
2,044✔
310
  OS_PARAM_CHECK(path);
2,044!
311
#ifdef WINDOWS
312
  struct _stati64 fileStat;
313
  int32_t         code = _stati64(path, &fileStat);
314
#else
315
  struct stat fileStat;
316
  int32_t     code = stat(path, &fileStat);
2,044✔
317
#endif
318
  if (-1 == code) {
2,044!
319
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
320
    return terrno;
×
321
  }
322

323
  if (diskid != NULL) {
2,044!
324
    *diskid = fileStat.st_dev;
2,044✔
325
  }
326

327
  return 0;
2,044✔
328
}
329

330
int32_t taosDevInoFile(TdFilePtr pFile, int64_t *stDev, int64_t *stIno) {
33,798✔
331
#ifdef WINDOWS
332
  if (pFile == NULL || pFile->hFile == NULL) {
333
    terrno = TSDB_CODE_INVALID_PARA;
334
    return terrno;
335
  }
336
  BY_HANDLE_FILE_INFORMATION bhfi;
337
  if (GetFileInformationByHandle(pFile->hFile, &bhfi) == FALSE) {
338
    DWORD error = GetLastError();
339
    terrno = TAOS_SYSTEM_WINAPI_ERROR(error);
340
    return terrno;
341
  }
342

343
  if (stDev != NULL) {
344
    *stDev = (int64_t)(bhfi.dwVolumeSerialNumber);
345
  }
346

347
  if (stIno != NULL) {
348
    *stIno = (int64_t)((((uint64_t)bhfi.nFileIndexHigh) << 32) + bhfi.nFileIndexLow);
349
  }
350

351
#else
352
  if (pFile == NULL || pFile->fd < 0) {
33,798!
353
    terrno = TSDB_CODE_INVALID_PARA;
×
354
    return terrno;
×
355
  }
356

357
  struct stat fileStat;
358
  int32_t     code = fstat(pFile->fd, &fileStat);
33,820✔
359
  if (-1 == code) {
33,826!
360
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
361
    return terrno;
×
362
  }
363

364
  if (stDev != NULL) {
33,837!
365
    *stDev = fileStat.st_dev;
33,837✔
366
  }
367

368
  if (stIno != NULL) {
33,837✔
369
    *stIno = fileStat.st_ino;
33,836✔
370
  }
371
#endif
372

373
  return 0;
33,837✔
374
}
375

376
FILE *taosOpenFileForStream(const char *path, int32_t tdFileOptions) {
9,446,881✔
377
  if (path == NULL) {
9,446,881!
378
    terrno = TSDB_CODE_INVALID_PARA;
×
379
    return NULL;
×
380
  }
381
  char *mode = NULL;
9,446,881✔
382
  if (tdFileOptions & TD_FILE_APPEND) {
9,446,881✔
383
    mode = (tdFileOptions & TD_FILE_TEXT) ? "at+" : "ab+";
13,573!
384
  } else if (tdFileOptions & TD_FILE_TRUNC) {
9,433,308✔
385
    mode = (tdFileOptions & TD_FILE_TEXT) ? "wt+" : "wb+";
12,492✔
386
  } else if ((tdFileOptions & TD_FILE_READ) && !(tdFileOptions & TD_FILE_WRITE)) {
9,420,816!
387
    mode = (tdFileOptions & TD_FILE_TEXT) ? "rt" : "rb";
9,420,816!
388
  } else {
389
    mode = (tdFileOptions & TD_FILE_TEXT) ? "rt+" : "rb+";
×
390
  }
391
  if (tdFileOptions & TD_FILE_EXCL) {
9,446,881!
392
    terrno = TSDB_CODE_INVALID_PARA;
×
393
    return NULL;
×
394
  }
395
  FILE *f = fopen(path, mode);
9,446,881✔
396
  if (NULL == f) {
9,446,881✔
397
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
27,579✔
398
  }
399
  return f;
9,446,881✔
400
}
401

402
#ifdef WINDOWS
403
HANDLE taosOpenFileNotStream(const char *path, int32_t tdFileOptions) {
404
  if (path == NULL) {
405
    terrno = TSDB_CODE_INVALID_PARA;
406
    return INVALID_HANDLE_VALUE;
407
  }
408
  DWORD openMode = 0;
409
  DWORD access = 0;
410
  DWORD fileFlag = FILE_ATTRIBUTE_NORMAL;
411
  DWORD shareMode = FILE_SHARE_READ;
412

413
  openMode = OPEN_EXISTING;
414
  if (tdFileOptions & TD_FILE_CREATE) {
415
    openMode = OPEN_ALWAYS;
416
  } else if (tdFileOptions & TD_FILE_EXCL) {
417
    openMode = CREATE_NEW;
418
  } else if ((tdFileOptions & TD_FILE_TRUNC)) {
419
    openMode = TRUNCATE_EXISTING;
420
    access |= GENERIC_WRITE;
421
  }
422
  if (tdFileOptions & TD_FILE_APPEND) {
423
    access |= FILE_APPEND_DATA;
424
  }
425
  if (tdFileOptions & TD_FILE_WRITE) {
426
    access |= GENERIC_WRITE;
427
  }
428

429
  shareMode |= FILE_SHARE_WRITE;
430

431
  access |= GENERIC_READ;
432

433
  if (tdFileOptions & TD_FILE_AUTO_DEL) {
434
    fileFlag |= FILE_ATTRIBUTE_TEMPORARY;
435
  }
436
  if (tdFileOptions & TD_FILE_WRITE_THROUGH) {
437
    fileFlag |= FILE_FLAG_WRITE_THROUGH;
438
  }
439

440
  HANDLE h = CreateFile(path, access, shareMode, NULL, openMode, fileFlag, NULL);
441
  if (h != INVALID_HANDLE_VALUE && (tdFileOptions & TD_FILE_APPEND) && (tdFileOptions & TD_FILE_WRITE)) {
442
    SetFilePointer(h, 0, NULL, FILE_END);
443
  }
444
  if (h == INVALID_HANDLE_VALUE) {
445
    DWORD dwError = GetLastError();
446
    terrno = TAOS_SYSTEM_WINAPI_ERROR(dwError);
447
    // LPVOID lpMsgBuf;
448
    // FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, (LPTSTR)&lpMsgBuf,
449
    // 0,
450
    //               NULL);
451
    // printf("CreateFile failed with error %d: %s", dwError, (char *)lpMsgBuf);
452
    // LocalFree(lpMsgBuf);
453
  }
454
  return h;
455
}
456

457
int64_t taosReadFile(TdFilePtr pFile, void *buf, int64_t count) {
458
  if (pFile == NULL || buf == NULL) {
459
    terrno = TSDB_CODE_INVALID_PARA;
460
    return terrno;
461
  }
462
#if FILE_WITH_LOCK
463
  (void)taosThreadRwlockRdlock(&(pFile->rwlock));
464
#endif
465
  if (pFile->hFile == NULL) {
466
#if FILE_WITH_LOCK
467
    (void)taosThreadRwlockUnlock(&(pFile->rwlock));
468
#endif
469
    terrno = TSDB_CODE_INVALID_PARA;
470
    return terrno;
471
  }
472

473
  int64_t res = 0;
474
  DWORD   bytesRead;
475
  if (!ReadFile(pFile->hFile, buf, count, &bytesRead, NULL)) {
476
    DWORD errCode = GetLastError();
477
    terrno = TAOS_SYSTEM_WINAPI_ERROR(errCode);
478
    res = -1;
479
  } else {
480
    res = bytesRead;
481
  }
482
#if FILE_WITH_LOCK
483
  (void)taosThreadRwlockUnlock(&(pFile->rwlock));
484
#endif
485
  return res;
486
}
487

488
int64_t taosWriteFile(TdFilePtr pFile, const void *buf, int64_t count) {
489
  if (pFile == NULL || pFile->hFile == NULL || buf == NULL) {
490
    terrno = TSDB_CODE_INVALID_PARA;
491
    return 0;
492
  }
493
#if FILE_WITH_LOCK
494
  (void)taosThreadRwlockWrlock(&(pFile->rwlock));
495
#endif
496

497
  DWORD bytesWritten;
498
  if (!WriteFile(pFile->hFile, buf, count, &bytesWritten, NULL)) {
499
    SET_ERRNO(GetLastError());
500
    terrno = TAOS_SYSTEM_WINAPI_ERROR(ERRNO);
501
    bytesWritten = -1;
502
  }
503

504
#if FILE_WITH_LOCK
505
  (void)taosThreadRwlockUnlock(&(pFile->rwlock));
506
#endif
507
  return bytesWritten;
508
}
509

510
int64_t taosPWriteFile(TdFilePtr pFile, const void *buf, int64_t count, int64_t offset) {
511
  if (pFile == NULL || buf == NULL) {
512
    terrno = TSDB_CODE_INVALID_PARA;
513
    return 0;
514
  }
515
#if FILE_WITH_LOCK
516
  (void)taosThreadRwlockWrlock(&(pFile->rwlock));
517
#endif
518
  if (pFile->hFile == NULL) {
519
#if FILE_WITH_LOCK
520
    (void)taosThreadRwlockUnlock(&(pFile->rwlock));
521
#endif
522
    return 0;
523
  }
524

525
  DWORD      ret = 0;
526
  OVERLAPPED ol = {0};
527
  ol.OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 0x20);
528
  ol.Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
529

530
  SetLastError(0);
531
  BOOL result = WriteFile(pFile->hFile, buf, count, &ret, &ol);
532
  if (!result) {
533
    SET_ERRNO(GetLastError());
534
    terrno = TAOS_SYSTEM_WINAPI_ERROR(ERRNO);
535
    ret = -1;
536
  }
537

538
#if FILE_WITH_LOCK
539
  (void)taosThreadRwlockUnlock(&(pFile->rwlock));
540
#endif
541
  return ret;
542
}
543

544
int64_t taosLSeekFile(TdFilePtr pFile, int64_t offset, int32_t whence) {
545
  if (pFile == NULL || pFile->hFile == NULL) {
546
    terrno = TSDB_CODE_INVALID_PARA;
547
    return -1;
548
  }
549
#if FILE_WITH_LOCK
550
  (void)taosThreadRwlockRdlock(&(pFile->rwlock));
551
#endif
552

553
  LARGE_INTEGER liOffset;
554
  liOffset.QuadPart = offset;
555
  if (!SetFilePointerEx(pFile->hFile, liOffset, NULL, whence)) {
556
    SET_ERRNO(GetLastError());
557
    terrno = TAOS_SYSTEM_WINAPI_ERROR(ERRNO);
558
    return -1;
559
  }
560

561
  liOffset.QuadPart = 0;
562
  if (!SetFilePointerEx(pFile->hFile, liOffset, &liOffset, FILE_CURRENT)) {
563
    SET_ERRNO(GetLastError());
564
    terrno = TAOS_SYSTEM_WINAPI_ERROR(ERRNO);
565
    return -1;
566
  }
567
#if FILE_WITH_LOCK
568
  (void)taosThreadRwlockUnlock(&(pFile->rwlock));
569
#endif
570
  return liOffset.QuadPart;
571
}
572

573
int32_t taosFStatFile(TdFilePtr pFile, int64_t *size, int64_t *mtime) {
574
  if (pFile == NULL || pFile->hFile == NULL) {
575
    terrno = TSDB_CODE_INVALID_PARA;
576
    return terrno;
577
  }
578

579
  if (size != NULL) {
580
    LARGE_INTEGER fileSize;
581
    if (!GetFileSizeEx(pFile->hFile, &fileSize)) {
582
      SET_ERRNO(GetLastError());
583
      terrno = TAOS_SYSTEM_WINAPI_ERROR(ERRNO);
584
      return terrno;  // Error getting file size
585
    }
586
    *size = fileSize.QuadPart;
587
  }
588

589
  if (mtime != NULL) {
590
    FILETIME creationTime, lastAccessTime, lastWriteTime;
591
    if (!GetFileTime(pFile->hFile, &creationTime, &lastAccessTime, &lastWriteTime)) {
592
      SET_ERRNO(GetLastError());
593
      terrno = TAOS_SYSTEM_WINAPI_ERROR(ERRNO);
594
      return terrno;  // Error getting file time
595
    }
596
    // Convert the FILETIME structure to a time_t value
597
    ULARGE_INTEGER ull;
598
    ull.LowPart = lastWriteTime.dwLowDateTime;
599
    ull.HighPart = lastWriteTime.dwHighDateTime;
600
    *mtime = (int64_t)((ull.QuadPart - 116444736000000000ULL) / 10000000ULL);
601
  }
602
  return 0;
603
}
604

605
int32_t taosLockFile(TdFilePtr pFile) {
606
  if (pFile == NULL || pFile->hFile == NULL) {
607
    terrno = TSDB_CODE_INVALID_PARA;
608
    return terrno;
609
  }
610

611
  BOOL          fSuccess = FALSE;
612
  LARGE_INTEGER fileSize;
613
  OVERLAPPED    overlapped = {0};
614

615
  fSuccess = LockFileEx(pFile->hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
616
                        0,           // reserved
617
                        ~0,          // number of bytes to lock low
618
                        ~0,          // number of bytes to lock high
619
                        &overlapped  // overlapped structure
620
  );
621
  if (!fSuccess) {
622
    return TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
623
  }
624
  return 0;
625
}
626

627
int32_t taosUnLockFile(TdFilePtr pFile) {
628
  if (pFile == NULL || pFile->hFile == NULL) {
629
    return 0;
630
  }
631
  BOOL       fSuccess = FALSE;
632
  OVERLAPPED overlapped = {0};
633

634
  fSuccess = UnlockFileEx(pFile->hFile, 0, ~0, ~0, &overlapped);
635
  if (!fSuccess) {
636
    return TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
637
  }
638
  return 0;
639
}
640

641
int32_t taosFtruncateFile(TdFilePtr pFile, int64_t l_size) {
642
  if (pFile == NULL) {
643
    terrno = TSDB_CODE_INVALID_PARA;
644
    return terrno;
645
  }
646
  if (pFile->hFile == NULL) {
647
    printf("Ftruncate file error, hFile was null\n");
648
    terrno = TSDB_CODE_INVALID_PARA;
649
    return terrno;
650
  }
651

652
  LARGE_INTEGER li_0;
653
  li_0.QuadPart = (int64_t)0;
654
  BOOL cur = SetFilePointerEx(pFile->hFile, li_0, NULL, FILE_CURRENT);
655
  if (!cur) {
656
    printf("SetFilePointerEx Error getting current position in file.\n");
657
    terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
658
    return terrno;
659
  }
660

661
  LARGE_INTEGER li_size;
662
  li_size.QuadPart = l_size;
663
  BOOL cur2 = SetFilePointerEx(pFile->hFile, li_size, NULL, FILE_BEGIN);
664
  if (cur2 == 0) {
665
    int error = GetLastError();
666
    switch (error) {
667
      case ERROR_INVALID_HANDLE:
668
        SET_ERRNO(EBADF);
669
        break;
670
      default:
671
        SET_ERRNO(EIO);
672
        break;
673
    }
674
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
675
    return terrno;
676
  }
677

678
  if (!SetEndOfFile(pFile->hFile)) {
679
    int error = GetLastError();
680
    printf("SetEndOfFile GetLastError is:%d", error);
681
    switch (error) {
682
      case ERROR_INVALID_HANDLE:
683
        SET_ERRNO(EBADF);
684
        break;
685
      default:
686
        SET_ERRNO(EIO);
687
        break;
688
    }
689
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
690
    return terrno;
691
  }
692
  return 0;
693
}
694

695
int64_t taosFSendFile(TdFilePtr pFileOut, TdFilePtr pFileIn, int64_t *offset, int64_t size) {
696
  if (pFileOut == NULL || pFileIn == NULL) {
697
    terrno = TSDB_CODE_INVALID_PARA;
698
    return -1;
699
  }
700
  if (pFileIn->hFile == NULL || pFileOut->hFile == NULL) {
701
    terrno = TSDB_CODE_INVALID_PARA;
702
    return -1;
703
  }
704

705
  LARGE_INTEGER fileOffset;
706
  fileOffset.QuadPart = *offset;
707

708
  if (!SetFilePointerEx(pFileIn->hFile, fileOffset, &fileOffset, FILE_BEGIN)) {
709
    terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
710
    return -1;
711
  }
712

713
  int64_t writeLen = 0;
714
  uint8_t buffer[_SEND_FILE_STEP_] = {0};
715

716
  DWORD bytesRead;
717
  DWORD bytesWritten;
718
  for (int64_t len = 0; len < (size - _SEND_FILE_STEP_); len += _SEND_FILE_STEP_) {
719
    if (!ReadFile(pFileIn->hFile, buffer, _SEND_FILE_STEP_, &bytesRead, NULL)) {
720
      terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
721
      return writeLen;
722
    }
723

724
    if (bytesRead <= 0) {
725
      return writeLen;
726
    } else if (bytesRead < _SEND_FILE_STEP_) {
727
      if (!WriteFile(pFileOut->hFile, buffer, bytesRead, &bytesWritten, NULL)) {
728
        terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
729
        return -1;
730
      } else {
731
        return (int64_t)(writeLen + bytesRead);
732
      }
733
    } else {
734
      if (!WriteFile(pFileOut->hFile, buffer, _SEND_FILE_STEP_, &bytesWritten, NULL)) {
735
        terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
736
        return -1;
737
      } else {
738
        writeLen += _SEND_FILE_STEP_;
739
      }
740
    }
741
  }
742

743
  int64_t remain = size - writeLen;
744
  if (remain > 0) {
745
    DWORD bytesRead;
746
    if (!ReadFile(pFileIn->hFile, buffer, (DWORD)remain, &bytesRead, NULL)) {
747
      terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
748
      return -1;
749
    }
750

751
    if (bytesRead <= 0) {
752
      return writeLen;
753
    } else {
754
      DWORD bytesWritten;
755
      if (!WriteFile(pFileOut->hFile, buffer, bytesRead, &bytesWritten, NULL)) {
756
        terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
757
        return -1;
758
      } else {
759
        writeLen += bytesWritten;
760
      }
761
    }
762
  }
763
  return writeLen;
764
}
765

766
bool lastErrorIsFileNotExist() {
767
  DWORD dwError = GetLastError();
768
  return dwError == ERROR_FILE_NOT_FOUND;
769
}
770

771
#else
772
int taosOpenFileNotStream(const char *path, int32_t tdFileOptions) {
4,926,115✔
773
  if (path == NULL) {
4,926,115!
774
    terrno = TSDB_CODE_INVALID_PARA;
×
775
    return -1;
×
776
  }
777
  int access = O_BINARY;
4,926,115✔
778
  access |= (tdFileOptions & TD_FILE_CREATE) ? O_CREAT : 0;
4,926,115✔
779
  if ((tdFileOptions & TD_FILE_WRITE) && (tdFileOptions & TD_FILE_READ)) {
4,926,115✔
780
    access |= O_RDWR;
795,609✔
781
  } else if (tdFileOptions & TD_FILE_WRITE) {
4,130,506✔
782
    access |= O_WRONLY;
284,628✔
783
  } else if (tdFileOptions & TD_FILE_READ) {
3,845,878!
784
    access |= O_RDONLY;
3,846,266✔
785
  }
786
  access |= (tdFileOptions & TD_FILE_TRUNC) ? O_TRUNC : 0;
4,926,115✔
787
  access |= (tdFileOptions & TD_FILE_APPEND) ? O_APPEND : 0;
4,926,115✔
788
  access |= (tdFileOptions & TD_FILE_TEXT) ? O_TEXT : 0;
4,926,115✔
789
  access |= (tdFileOptions & TD_FILE_EXCL) ? O_EXCL : 0;
4,926,115✔
790
  access |= (tdFileOptions & TD_FILE_CLOEXEC) ? O_CLOEXEC : 0;
4,926,115✔
791

792
  int fd = open(path, access, S_IRWXU | S_IRWXG | S_IRWXO);
4,926,115✔
793
  if (-1 == fd) {
4,928,121✔
794
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
41,282✔
795
  }
796
  return fd;
4,929,090✔
797
}
798

799
int64_t taosReadFile(TdFilePtr pFile, void *buf, int64_t count) {
8,909,546✔
800
  if (pFile == NULL || buf == NULL) {
8,909,546!
801
    terrno = TSDB_CODE_INVALID_PARA;
×
802
    return -1;
×
803
  }
804
  STUB_RAND_IO_ERR(terrno)
805
#if FILE_WITH_LOCK
806
  (void)taosThreadRwlockRdlock(&(pFile->rwlock));
8,911,275✔
807
#endif
808

809
  if (pFile->fd < 0) {
8,914,743!
810
#if FILE_WITH_LOCK
UNCOV
811
    (void)taosThreadRwlockUnlock(&(pFile->rwlock));
×
812
#endif
813
    terrno = TSDB_CODE_INVALID_PARA;
×
814
    return -1;
×
815
  }
816

817
  int64_t leftbytes = count;
8,914,891✔
818
  int64_t readbytes;
819
  char   *tbuf = (char *)buf;
8,914,891✔
820
  int32_t code = 0;
8,914,891✔
821

822
  while (leftbytes > 0) {
17,825,225✔
823
#ifdef WINDOWS
824
    readbytes = _read(pFile->fd, (void *)tbuf, (uint32_t)leftbytes);
825
#else
826
    readbytes = read(pFile->fd, (void *)tbuf, (uint32_t)leftbytes);
8,914,981✔
827
#endif
828
    if (readbytes < 0) {
8,914,536✔
829
      if (ERRNO == EINTR) {
106!
830
        continue;
×
831
      } else {
832
        code = TAOS_SYSTEM_ERROR(ERRNO);
106✔
833
#if FILE_WITH_LOCK
834
        (void)taosThreadRwlockUnlock(&(pFile->rwlock));
106✔
835
#endif
836
        terrno = code;
106✔
837
        return -1;
106✔
838
      }
839
    } else if (readbytes == 0) {
8,914,430✔
840
#if FILE_WITH_LOCK
841
      (void)taosThreadRwlockUnlock(&(pFile->rwlock));
4,096✔
842
#endif
843
      return (int64_t)(count - leftbytes);
4,096✔
844
    }
845

846
    leftbytes -= readbytes;
8,910,334✔
847
    tbuf += readbytes;
8,910,334✔
848
  }
849

850
#if FILE_WITH_LOCK
851
  (void)taosThreadRwlockUnlock(&(pFile->rwlock));
8,910,244✔
852
#endif
853

854
  return count;
8,911,266✔
855
}
856

857
int64_t taosWriteFile(TdFilePtr pFile, const void *buf, int64_t count) {
722,277,997✔
858
  STUB_RAND_IO_ERR(terrno)
859
  if (pFile == NULL || buf == NULL) {
722,277,997!
860
    terrno = TSDB_CODE_INVALID_PARA;
×
861
    return 0;
×
862
  }
863
#if FILE_WITH_LOCK
864
  (void)taosThreadRwlockWrlock(&(pFile->rwlock));
722,336,821✔
865
#endif
866
  if (pFile->fd < 0) {
722,623,995✔
867
#if FILE_WITH_LOCK
868
    (void)taosThreadRwlockUnlock(&(pFile->rwlock));
9✔
869
#endif
870
    terrno = TSDB_CODE_INVALID_PARA;
×
871
    return 0;
×
872
  }
873

874
  int64_t nleft = count;
722,623,986✔
875
  int64_t nwritten = 0;
722,623,986✔
876
  char   *tbuf = (char *)buf;
722,623,986✔
877
  int32_t code = 0;
722,623,986✔
878

879
  while (nleft > 0) {
1,445,245,745✔
880
    nwritten = write(pFile->fd, (void *)tbuf, (uint32_t)nleft);
722,623,278✔
881
    if (nwritten < 0) {
722,621,759!
882
      if (ERRNO == EINTR) {
×
883
        continue;
×
884
      }
885
      code = TAOS_SYSTEM_ERROR(ERRNO);
×
886
#if FILE_WITH_LOCK
887
      (void)taosThreadRwlockUnlock(&(pFile->rwlock));
×
888
#endif
889
      terrno = code;
×
890
      return -1;
×
891
    }
892
    nleft -= nwritten;
722,621,759✔
893
    tbuf += nwritten;
722,621,759✔
894
  }
895

896
#if FILE_WITH_LOCK
897
  (void)taosThreadRwlockUnlock(&(pFile->rwlock));
722,622,467✔
898
#endif
899

900
  return count;
722,587,162✔
901
}
902

903
int64_t taosPWriteFile(TdFilePtr pFile, const void *buf, int64_t count, int64_t offset) {
1,033,808✔
904
  STUB_RAND_IO_ERR(terrno)
905
  if (pFile == NULL || buf == NULL) {
1,033,808!
906
    terrno = TSDB_CODE_INVALID_PARA;
×
907
    return 0;
×
908
  }
909

910
  int32_t code = 0;
1,033,861✔
911
#if FILE_WITH_LOCK
912
  (void)taosThreadRwlockWrlock(&(pFile->rwlock));
1,033,861✔
913
#endif
914

915
#if FILE_WITH_LOCK
916
  if (pFile->fd < 0) {
1,034,193!
917
    (void)taosThreadRwlockUnlock(&(pFile->rwlock));
×
918
    return 0;
×
919
  }
920
#endif
921
#ifndef TD_ASTRA
922
  int64_t ret = pwrite(pFile->fd, buf, count, offset);
1,034,193✔
923
  if (-1 == ret) {
1,033,745!
924
    code = TAOS_SYSTEM_ERROR(ERRNO);
×
925
  }
926
#else  // TD_ASTRA_TODO
927
  int64_t ret = -1;
928
  int64_t cur = lseek(pFile->fd, 0, SEEK_CUR);
929
  if (cur < 0) {
930
    code = TAOS_SYSTEM_ERROR(ERRNO);
931
    goto _exit;
932
  }
933
  if (lseek(pFile->fd, offset, SEEK_SET) < 0) {
934
    code = TAOS_SYSTEM_ERROR(ERRNO);
935
    goto _exit;
936
  }
937
  if ((ret = write(pFile->fd, buf, count)) < 0) {
938
    code = TAOS_SYSTEM_ERROR(ERRNO);
939
    goto _exit;
940
  }
941
_exit:
942
  if (cur >= 0 && lseek(pFile->fd, cur, SEEK_SET) < 0) {
943
    code = TAOS_SYSTEM_ERROR(ERRNO);
944
  }
945
#endif
946
#if FILE_WITH_LOCK
947
  (void)taosThreadRwlockUnlock(&(pFile->rwlock));
1,033,745✔
948
#endif
949

950
  if (code) {
1,034,158✔
951
    terrno = code;
8✔
952
  }
953

954
  return ret;
1,034,150✔
955
}
956

957
int64_t taosLSeekFile(TdFilePtr pFile, int64_t offset, int32_t whence) {
10,521,046✔
958
  if (pFile == NULL || pFile->fd < 0) {
10,521,046!
959
    terrno = TSDB_CODE_INVALID_PARA;
×
960
    return -1;
×
961
  }
962
#if FILE_WITH_LOCK
963
  (void)taosThreadRwlockRdlock(&(pFile->rwlock));
10,522,789✔
964
#endif
965

966
  int32_t code = 0;
10,526,197✔
967

968
  int64_t ret = lseek(pFile->fd, offset, whence);
10,526,197✔
969
  if (-1 == ret) {
10,525,759!
970
    code = TAOS_SYSTEM_ERROR(ERRNO);
×
971
  }
972

973
#if FILE_WITH_LOCK
974
  (void)taosThreadRwlockUnlock(&(pFile->rwlock));
10,525,759✔
975
#endif
976

977
  if (code) {
10,526,365✔
978
    terrno = code;
69✔
979
    return -1;
×
980
  }
981

982
  return ret;
10,526,296✔
983
}
984

985
int32_t taosFStatFile(TdFilePtr pFile, int64_t *size, int64_t *mtime) {
98,776✔
986
  if (pFile == NULL) {
98,776!
987
    terrno = TSDB_CODE_INVALID_PARA;
×
988
    return terrno;
×
989
  }
990

991
  if (pFile->fd < 0) {
98,776!
992
    terrno = TSDB_CODE_INVALID_PARA;
×
993
    return terrno;
×
994
  }
995

996
  struct stat fileStat;
997
  int32_t     code = fstat(pFile->fd, &fileStat);
98,776✔
998
  if (-1 == code) {
98,793✔
999
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
4✔
1000
    return terrno;
×
1001
  }
1002

1003
  if (size != NULL) {
98,789✔
1004
    *size = fileStat.st_size;
98,777✔
1005
  }
1006

1007
  if (mtime != NULL) {
98,789!
1008
    *mtime = fileStat.st_mtime;
×
1009
  }
1010

1011
  return 0;
98,789✔
1012
}
1013

1014
int32_t taosLockFile(TdFilePtr pFile) {
42,111✔
1015
  if (NULL == pFile || pFile->fd < 0) {
42,111!
1016
    terrno = TSDB_CODE_INVALID_PARA;
×
1017
    return terrno;
×
1018
  }
1019
#ifndef TD_ASTRA
1020
  int32_t code = (int32_t)flock(pFile->fd, LOCK_EX | LOCK_NB);
42,111✔
1021
  if (-1 == code) {
42,111✔
1022
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
9,614✔
1023
    return terrno;
9,614✔
1024
  }
1025
#else // TD_ASTRA_TODO
1026
  struct flock lock;
1027
  lock.l_type = F_WRLCK;
1028
  lock.l_whence = SEEK_SET;
1029
  lock.l_start = 0;
1030
  lock.l_len = 0;
1031
  int32_t code = fcntl(pFile->fd, F_SETLK, &lock);
1032
  if (-1 == code) {
1033
    //    terrno = TAOS_SYSTEM_ERROR(ERRNO); // TD_ASTRA_TODO
1034
    //    return terrno;                     // TD_ASTRA_TODO
1035
  }
1036
#endif
1037
  return 0;
32,497✔
1038
}
1039

1040
int32_t taosUnLockFile(TdFilePtr pFile) {
16,777✔
1041
  if (NULL == pFile || pFile->fd < 0) {
16,777!
1042
    terrno = TSDB_CODE_INVALID_PARA;
×
1043
    return terrno;
×
1044
  }
1045
#ifndef TD_ASTRA
1046
  int32_t code = (int32_t)flock(pFile->fd, LOCK_UN | LOCK_NB);
16,777✔
1047
  if (-1 == code) {
16,777!
1048
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1049
    return terrno;
×
1050
  }
1051
#else // TD_ASTRA_TODO
1052
  struct flock lock;
1053
  lock.l_type = F_UNLCK;
1054
  lock.l_whence = SEEK_SET;
1055
  lock.l_start = 0;
1056
  lock.l_len = 0;
1057
  int32_t code = fcntl(pFile->fd, F_SETLK, &lock);
1058
  if (-1 == code) {
1059
    //    terrno = TAOS_SYSTEM_ERROR(ERRNO);// TD_ASTRA_TODO
1060
    //    return terrno;// TD_ASTRA_TODO
1061
  }
1062
#endif
1063
  return 0;
16,777✔
1064
}
1065

1066
int32_t taosFtruncateFile(TdFilePtr pFile, int64_t l_size) {
225✔
1067
  if (NULL == pFile || pFile->fd < 0) {
225!
1068
    terrno = TSDB_CODE_INVALID_PARA;
×
1069
    return terrno;
×
1070
  }
1071

1072
  int32_t code = ftruncate(pFile->fd, l_size);
225✔
1073
  if (-1 == code) {
225!
1074
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1075
    return terrno;
×
1076
  }
1077
  return 0;
225✔
1078
}
1079

1080
int64_t taosFSendFile(TdFilePtr pFileOut, TdFilePtr pFileIn, int64_t *offset, int64_t size) {
22✔
1081
  if (pFileOut == NULL || pFileIn == NULL) {
22!
1082
    terrno = TSDB_CODE_INVALID_PARA;
×
1083
    return -1;
×
1084
  }
1085
  if (pFileIn->fd < 0 || pFileOut->fd < 0) {
22!
1086
    terrno = TSDB_CODE_INVALID_PARA;
×
1087
    return -1;
×
1088
  }
1089

1090
#if defined(_TD_DARWIN_64) || defined(TD_ASTRA)  // TD_ASTRA_TODO
1091
  if (lseek(pFileIn->fd, (int32_t)(*offset), 0) < 0) {
1092
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
1093
    return -1;
1094
  }
1095
  int64_t writeLen = 0;
1096
  uint8_t buffer[_SEND_FILE_STEP_] = {0};
1097

1098
  for (int64_t len = 0; len < (size - _SEND_FILE_STEP_); len += _SEND_FILE_STEP_) {
1099
    size_t rlen = read(pFileIn->fd, (void *)buffer, _SEND_FILE_STEP_);
1100
    if (rlen <= 0) {
1101
      return writeLen;
1102
    } else if (rlen < _SEND_FILE_STEP_) {
1103
      write(pFileOut->fd, (void *)buffer, (uint32_t)rlen);
1104
      return (int64_t)(writeLen + rlen);
1105
    } else {
1106
      write(pFileOut->fd, (void *)buffer, _SEND_FILE_STEP_);
1107
      writeLen += _SEND_FILE_STEP_;
1108
    }
1109
  }
1110

1111
  int64_t remain = size - writeLen;
1112
  if (remain > 0) {
1113
    size_t rlen = read(pFileIn->fd, (void *)buffer, (size_t)remain);
1114
    if (rlen <= 0) {
1115
      return writeLen;
1116
    } else {
1117
      write(pFileOut->fd, (void *)buffer, (uint32_t)remain);
1118
      writeLen += remain;
1119
    }
1120
  }
1121
  return writeLen;
1122

1123
#else  // for linux
1124

1125
  int64_t leftbytes = size;
22✔
1126
  int64_t sentbytes;
1127

1128
  while (leftbytes > 0) {
44✔
1129
#ifdef _TD_ARM_32
1130
    sentbytes = sendfile(pFileOut->fd, pFileIn->fd, (long int *)offset, leftbytes);
1131
#else
1132
    sentbytes = sendfile(pFileOut->fd, pFileIn->fd, offset, leftbytes);
22✔
1133
#endif
1134
    if (sentbytes == -1) {
22!
1135
      if (ERRNO == EINTR || ERRNO == EAGAIN || ERRNO == EWOULDBLOCK) {
×
1136
        continue;
×
1137
      } else {
1138
        terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1139
        return -1;
×
1140
      }
1141
    } else if (sentbytes == 0) {
22!
1142
      return (int64_t)(size - leftbytes);
×
1143
    }
1144

1145
    leftbytes -= sentbytes;
22✔
1146
  }
1147

1148
  return size;
22✔
1149
#endif
1150
}
1151

1152
bool lastErrorIsFileNotExist() { return terrno == TAOS_SYSTEM_ERROR(ENOENT); }
26,418✔
1153

1154
#endif  // WINDOWS
1155

1156
TdFilePtr taosOpenFile(const char *path, int32_t tdFileOptions) {
14,373,526✔
1157
  if (path == NULL) {
14,373,526!
1158
    terrno = TSDB_CODE_INVALID_PARA;
×
1159
    return NULL;
×
1160
  }
1161
  STUB_RAND_IO_ERR(NULL)
1162
  FILE *fp = NULL;
14,373,526✔
1163
#ifdef WINDOWS
1164
  HANDLE hFile = NULL;
1165
#else
1166
  int fd = -1;
14,373,526✔
1167
#endif
1168
  if (tdFileOptions & TD_FILE_STREAM) {
14,373,526✔
1169
    fp = taosOpenFileForStream(path, tdFileOptions);
9,446,881✔
1170
    if (fp == NULL) return NULL;
9,446,881✔
1171
  } else {
1172
#ifdef WINDOWS
1173
    hFile = taosOpenFileNotStream(path, tdFileOptions);
1174
    if (hFile == INVALID_HANDLE_VALUE) return NULL;
1175
#else
1176
    fd = taosOpenFileNotStream(path, tdFileOptions);
4,926,645✔
1177
    if (fd == -1) return NULL;
4,927,436✔
1178
#endif
1179
  }
1180

1181
  TdFilePtr pFile = (TdFilePtr)taosMemoryMalloc(sizeof(TdFile));
14,304,487!
1182
  if (pFile == NULL) {
14,306,451!
1183
#ifdef WINDOWS
1184
    if (hFile != NULL) CloseHandle(hFile);
1185
#else
1186
    if (fd >= 0) (void)close(fd);
×
1187
#endif
1188
    if (fp != NULL) (void)fclose(fp);
×
1189
    terrno = TSDB_CODE_OUT_OF_MEMORY;
×
1190
    return NULL;
×
1191
  }
1192

1193
#if FILE_WITH_LOCK
1194
  (void)taosThreadRwlockInit(&(pFile->rwlock), NULL);
14,306,451✔
1195
#endif
1196
  pFile->fp = fp;
14,304,853✔
1197

1198
#ifdef WINDOWS
1199
  pFile->hFile = hFile;
1200
  pFile->tdFileOptions = tdFileOptions;
1201
  // do nothing, since the property of pmode is set with _O_TEMPORARY; the OS will recycle
1202
  // the file handle, as well as the space on disk.
1203
#else
1204
  pFile->fd = fd;
14,304,853✔
1205
  // Remove it instantly, so when the program exits normally/abnormally, the file
1206
  // will be automatically remove by OS.
1207
  if (tdFileOptions & TD_FILE_AUTO_DEL) {
14,304,853✔
1208
    if (-1 == unlink(path)) {
833!
1209
      terrno = TAOS_SYSTEM_ERROR(ERRNO);
986✔
1210
      (void)close(fd);
×
1211
      taosMemoryFree(pFile);
×
1212
      return NULL;
×
1213
    }
1214
  }
1215
#endif
1216

1217
  return pFile;
14,303,867✔
1218
}
1219

1220
int32_t taosCloseFile(TdFilePtr *ppFile) {
17,319,524✔
1221
  int32_t code = 0;
17,319,524✔
1222
  if (ppFile == NULL || *ppFile == NULL) {
17,319,524!
1223
    return 0;
3,013,619✔
1224
  }
1225
#if FILE_WITH_LOCK
1226
  (void)taosThreadRwlockWrlock(&((*ppFile)->rwlock));
14,305,905✔
1227
#endif
1228
  if ((*ppFile)->fp != NULL) {
14,306,703✔
1229
    TAOS_UNUSED(fflush((*ppFile)->fp));
9,418,555✔
1230
    TAOS_UNUSED(fclose((*ppFile)->fp));
9,418,555✔
1231
    (*ppFile)->fp = NULL;
9,418,555✔
1232
  }
1233
#ifdef WINDOWS
1234
  if ((*ppFile)->hFile != NULL) {
1235
    // FlushFileBuffers((*ppFile)->hFile);
1236
    if (!CloseHandle((*ppFile)->hFile)) {
1237
      terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
1238
      code = -1;
1239
    }
1240
    (*ppFile)->hFile = NULL;
1241
#else
1242
  if ((*ppFile)->fd >= 0) {
14,306,703✔
1243
    // warning: never fsync silently in base lib
1244
    /*fsync((*ppFile)->fd);*/
1245
    code = close((*ppFile)->fd);
4,888,239✔
1246
    if (-1 == code) {
4,888,536!
1247
      terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1248
    }
1249
    (*ppFile)->fd = -1;
4,888,705✔
1250
#endif
1251
  }
1252
#if FILE_WITH_LOCK
1253
  (void)taosThreadRwlockUnlock(&((*ppFile)->rwlock));
14,307,169✔
1254
  (void)taosThreadRwlockDestroy(&((*ppFile)->rwlock));
14,307,305✔
1255
#endif
1256
  taosMemoryFree(*ppFile);
14,305,469!
1257
  *ppFile = NULL;
14,306,694✔
1258
  return code;
14,306,694✔
1259
}
1260

1261
int64_t taosPReadFile(TdFilePtr pFile, void *buf, int64_t count, int64_t offset) {
904,355✔
1262
  STUB_RAND_IO_ERR(terrno)
1263
  if (pFile == NULL) {
904,355!
1264
    terrno = TSDB_CODE_INVALID_PARA;
×
1265
    return -1;
×
1266
  }
1267

1268
  int32_t code = 0;
904,355✔
1269

1270
#ifdef WINDOWS
1271
#if FILE_WITH_LOCK
1272
  (void)taosThreadRwlockRdlock(&(pFile->rwlock));
1273
#endif
1274

1275
  if (pFile->hFile == NULL) {
1276
#if FILE_WITH_LOCK
1277
    (void)taosThreadRwlockUnlock(&(pFile->rwlock));
1278
#endif
1279

1280
    terrno = TSDB_CODE_INVALID_PARA;
1281
    return -1;
1282
  }
1283

1284
  DWORD      ret = 0;
1285
  OVERLAPPED ol = {0};
1286
  ol.OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 0x20);
1287
  ol.Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
1288

1289
  SetLastError(0);
1290
  BOOL result = ReadFile(pFile->hFile, buf, count, &ret, &ol);
1291
  if (!result && GetLastError() != ERROR_HANDLE_EOF) {
1292
    code = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
1293
    ret = -1;
1294
  }
1295
#else
1296
#if FILE_WITH_LOCK
1297
  (void)taosThreadRwlockRdlock(&(pFile->rwlock));
904,355✔
1298
#endif
1299

1300
  if (pFile->fd < 0) {
904,441!
1301
#if FILE_WITH_LOCK
1302
    (void)taosThreadRwlockUnlock(&(pFile->rwlock));
×
1303
#endif
1304
    terrno = TSDB_CODE_INVALID_PARA;
×
1305
    return -1;
×
1306
  }
1307
#ifndef TD_ASTRA
1308
  int64_t ret = pread(pFile->fd, buf, count, offset);
904,441✔
1309
  if (-1 == ret) {
904,330!
1310
    code = TAOS_SYSTEM_ERROR(ERRNO);
×
1311
  }
1312
#else  // TD_ASTRA_TODO
1313
  int64_t ret = -1;
1314
  int64_t cur = lseek(pFile->fd, 0, SEEK_CUR);
1315
  if (cur < 0) {
1316
    code = TAOS_SYSTEM_ERROR(ERRNO);
1317
    goto _exit;
1318
  }
1319
  if (lseek(pFile->fd, offset, SEEK_SET) < 0) {
1320
    code = TAOS_SYSTEM_ERROR(ERRNO);
1321
    goto _exit;
1322
  }
1323
  if ((ret = read(pFile->fd, buf, count)) < 0) {
1324
    code = TAOS_SYSTEM_ERROR(ERRNO);
1325
    goto _exit;
1326
  }
1327
_exit:
1328
  if (cur >= 0 && lseek(pFile->fd, cur, SEEK_SET) < 0) {
1329
    code = TAOS_SYSTEM_ERROR(ERRNO);
1330
  }
1331
#endif
1332
#endif
1333
#if FILE_WITH_LOCK
1334
  (void)taosThreadRwlockUnlock(&(pFile->rwlock));
904,330✔
1335
#endif
1336

1337
  if (code) {
904,460!
1338
    terrno = code;
×
1339
    return -1;
×
1340
  }
1341

1342
  return ret;
904,472✔
1343
}
1344

1345
int32_t taosFsyncFile(TdFilePtr pFile) {
2,059,625✔
1346
  if (pFile == NULL) {
2,059,625✔
1347
    return 0;
2,396✔
1348
  }
1349

1350
  int32_t code = 0;
2,057,229✔
1351
  // this implementation is WRONG
1352
  // fflush is not a replacement of fsync
1353
  if (pFile->fp != NULL) {
2,057,229✔
1354
    code = fflush(pFile->fp);
471,968✔
1355
    if (0 != code) {
471,968!
1356
      terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1357
      return terrno;
×
1358
    }
1359

1360
    return 0;
471,968✔
1361
  }
1362

1363
#ifdef WINDOWS
1364
  if (pFile->hFile != NULL) {
1365
    if (pFile->tdFileOptions & TD_FILE_WRITE_THROUGH) {
1366
      return 0;
1367
    }
1368
    bool ret = FlushFileBuffers(pFile->hFile);
1369
    if (!ret) {
1370
      terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
1371
      return terrno;
1372
    }
1373
    return 0;
1374
  }
1375
#else
1376
  if (pFile->fd >= 0) {
1,585,261!
1377
    code = fsync(pFile->fd);
1,585,509✔
1378
    if (-1 == code) {
1,585,733!
1379
#ifndef TD_ASTRA
1380
      terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1381
#else
1382
      terrno = 0;  // TD_ASTRA_TODO
1383
#endif
1384
      return terrno;
×
1385
    }
1386
  }
1387
#endif
1388

1389
  return 0;
1,585,767✔
1390
}
1391

1392
void taosFprintfFile(TdFilePtr pFile, const char *format, ...) {
198,478,944✔
1393
  if (pFile == NULL || pFile->fp == NULL) {
198,478,944!
1394
    return;
62,185,251✔
1395
  }
1396
  va_list ap;
1397
  va_start(ap, format);
136,293,693✔
1398
  (void)vfprintf(pFile->fp, format, ap);
136,293,693✔
1399
  va_end(ap);
136,351,095✔
1400
}
1401

1402
bool taosValidFile(TdFilePtr pFile) {
×
1403
#ifdef WINDOWS
1404
  return pFile != NULL && pFile->hFile != NULL;
1405
#else
1406
  return pFile != NULL && pFile->fd > 0;
×
1407
#endif
1408
}
1409

1410
int32_t taosUmaskFile(int32_t maskVal) {
40,099✔
1411
#ifdef WINDOWS
1412
  return 0;
1413
#else
1414
  return umask(maskVal);
40,099✔
1415
#endif
1416
}
1417

1418
int64_t taosGetLineFile(TdFilePtr pFile, char **__restrict ptrBuf) {
769,685✔
1419
  int64_t ret = -1;
769,685✔
1420
  int32_t code = 0;
769,685✔
1421

1422
  if (pFile == NULL || ptrBuf == NULL) {
769,685!
1423
    terrno = TSDB_CODE_INVALID_PARA;
×
1424
    goto END;
×
1425
  }
1426
  if (*ptrBuf != NULL) {
769,685✔
1427
    taosMemoryFreeClear(*ptrBuf);
769,580!
1428
  }
1429

1430
  if (pFile->fp == NULL) {
769,685!
1431
    terrno = TSDB_CODE_INVALID_PARA;
×
1432
    goto END;
×
1433
  }
1434

1435
#if FILE_WITH_LOCK
1436
  (void)taosThreadRwlockRdlock(&(pFile->rwlock));
769,685✔
1437
#endif
1438

1439
#ifdef WINDOWS
1440
  size_t bufferSize = 512;
1441
  *ptrBuf = taosMemoryMalloc(bufferSize);
1442
  if (*ptrBuf == NULL) {
1443
    goto END;
1444
  }
1445

1446
  size_t bytesRead = 0;
1447
  size_t totalBytesRead = 0;
1448

1449
  while (1) {
1450
    char *result = fgets(*ptrBuf + totalBytesRead, bufferSize - totalBytesRead, pFile->fp);
1451
    if (result == NULL) {
1452
      if (feof(pFile->fp)) {
1453
        break;
1454
      } else {
1455
        ret = -1;
1456
        terrno = TAOS_SYSTEM_ERROR(ferror(pFile->fp));
1457
        taosMemoryFreeClear(*ptrBuf);
1458
        goto END;
1459
      }
1460
    }
1461
    bytesRead = strlen(*ptrBuf + totalBytesRead);
1462
    totalBytesRead += bytesRead;
1463

1464
    if (totalBytesRead < bufferSize - 1 || (*ptrBuf)[totalBytesRead - 1] == '\n') {
1465
      break;
1466
    }
1467

1468
    bufferSize += 512;
1469
    void *newBuf = taosMemoryRealloc(*ptrBuf, bufferSize);
1470
    if (newBuf == NULL) {
1471
      taosMemoryFreeClear(*ptrBuf);
1472
      goto END;
1473
    }
1474

1475
    *ptrBuf = newBuf;
1476
  }
1477

1478
  (*ptrBuf)[totalBytesRead] = '\0';
1479
  ret = (totalBytesRead > 0 ? totalBytesRead : -1); // -1 means EOF
1480
#elif defined(TD_ASTRA)
1481
  size_t bufsize = 128;
1482
  if (*ptrBuf == NULL) {
1483
    *ptrBuf = (char *)taosMemoryMalloc(bufsize);
1484
    if (*ptrBuf == NULL) {
1485
      goto END;
1486
    }
1487
  }
1488
  size_t pos = 0;
1489
  int    c;
1490
  while ((c = fgetc(pFile->fp)) != EOF) {
1491
    if (pos + 1 >= bufsize) {
1492
      size_t new_size = bufsize << 1;
1493
      char  *new_line = (char *)taosMemoryRealloc(*ptrBuf, new_size);
1494
      if (new_line == NULL) {
1495
        goto END;
1496
      }
1497
      *ptrBuf = new_line;
1498
      bufsize = new_size;
1499
    }
1500
    (*ptrBuf)[pos++] = (char)c;
1501
    if (c == '\n') {
1502
      break;
1503
    }
1504
  }
1505
  if (pos == 0 && c == EOF) {
1506
    goto END;
1507
  }
1508
  (*ptrBuf)[pos] = '\0';
1509
  ret = pos;
1510
#else
1511
  size_t len = 0;
769,685✔
1512
  ret = getline(ptrBuf, &len, pFile->fp);
769,685✔
1513
  if (-1 == ret) {
769,685✔
1514
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
105✔
1515
  }
1516
#endif
1517

1518
END:
769,580✔
1519

1520
#if FILE_WITH_LOCK
1521
  (void)taosThreadRwlockUnlock(&(pFile->rwlock));
769,685✔
1522
#endif
1523

1524
  return ret;
769,685✔
1525
}
1526

1527
int64_t taosGetsFile(TdFilePtr pFile, int32_t maxSize, char *__restrict buf) {
121,734,420✔
1528
  if (pFile == NULL || buf == NULL) {
121,734,420!
1529
    terrno = TSDB_CODE_INVALID_PARA;
×
1530
    return terrno;
×
1531
  }
1532

1533
  if (pFile->fp == NULL) {
121,734,420!
1534
    terrno = TSDB_CODE_INVALID_PARA;
×
1535
    return terrno;
×
1536
  }
1537

1538
  if (fgets(buf, maxSize, pFile->fp) == NULL) {
121,734,420✔
1539
    if (feof(pFile->fp)) {
26,902!
1540
      return 0;
26,901✔
1541
    } else {
1542
      terrno = TAOS_SYSTEM_ERROR(ferror(pFile->fp));
×
1543
      return terrno;
×
1544
    }
1545
  }
1546

1547
  return strlen(buf);
121,707,518✔
1548
}
1549

1550
int32_t taosEOFFile(TdFilePtr pFile) {
28,227,205✔
1551
  if (pFile == NULL) {
28,227,205!
1552
    terrno = TSDB_CODE_INVALID_PARA;
×
1553
    return -1;
×
1554
  }
1555
  if (pFile->fp == NULL) {
28,227,205!
1556
    terrno = TSDB_CODE_INVALID_PARA;
×
1557
    return -1;
×
1558
  }
1559

1560
  return feof(pFile->fp);
28,227,205✔
1561
}
1562

1563
bool taosCheckAccessFile(const char *pathname, int32_t tdFileAccessOptions) {
796,708✔
1564
  if (pathname == NULL) {
796,708!
1565
    terrno = TSDB_CODE_INVALID_PARA;
×
1566
    return false;  // invalid parameter
×
1567
  }
1568
  int flags = 0;
796,708✔
1569

1570
  if (tdFileAccessOptions & TD_FILE_ACCESS_EXIST_OK) {
796,708!
1571
    flags |= F_OK;
796,778✔
1572
  }
1573

1574
  if (tdFileAccessOptions & TD_FILE_ACCESS_READ_OK) {
796,708✔
1575
    flags |= R_OK;
39,922✔
1576
  }
1577

1578
  if (tdFileAccessOptions & TD_FILE_ACCESS_WRITE_OK) {
796,708✔
1579
    flags |= W_OK;
39,922✔
1580
  }
1581

1582
  if (tdFileAccessOptions & TD_FILE_ACCESS_EXEC_OK) {
796,708!
1583
    flags |= X_OK;
×
1584
  }
1585
#ifdef WINDOWS
1586
  return _access(pathname, flags) == 0;
1587
#else
1588
  return access(pathname, flags) == 0;
796,708✔
1589
#endif
1590
}
1591

1592
bool taosCheckExistFile(const char *pathname) { return taosCheckAccessFile(pathname, TD_FILE_ACCESS_EXIST_OK); };
756,711✔
1593

1594
int32_t taosCompressFile(char *srcFileName, char *destFileName) {
2✔
1595
  OS_PARAM_CHECK(srcFileName);
2!
1596
  OS_PARAM_CHECK(destFileName);
2!
1597
  int32_t   compressSize = 163840;
2✔
1598
  int32_t   ret = 0;
2✔
1599
  int32_t   len = 0;
2✔
1600
  gzFile    dstFp = NULL;
2✔
1601
  TdFilePtr pSrcFile = NULL;
2✔
1602

1603
  char *data = taosMemoryMalloc(compressSize);
2!
1604
  if (NULL == data) {
2!
1605
    return terrno;
×
1606
  }
1607

1608
  pSrcFile = taosOpenFile(srcFileName, TD_FILE_READ | TD_FILE_STREAM);
2✔
1609
  if (pSrcFile == NULL) {
2!
1610
    ret = terrno;
×
1611
    goto cmp_end;
×
1612
  }
1613

1614
  int access = O_BINARY | O_WRONLY | O_TRUNC | O_CREAT;
2✔
1615
#ifdef WINDOWS
1616
  int32_t pmode = _S_IREAD | _S_IWRITE;
1617
#else
1618
  int32_t pmode = S_IRWXU | S_IRWXG | S_IRWXO;
2✔
1619
#endif
1620
  int fd = open(destFileName, access, pmode);
2✔
1621
  if (-1 == fd) {
2!
1622
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1623
    ret = terrno;
×
1624
    goto cmp_end;
×
1625
  }
1626

1627
  // Both gzclose() and fclose() will close the associated fd, so they need to have different fds.
1628
  FileFd gzFd = dup(fd);
2✔
1629
  if (-1 == gzFd) {
2!
1630
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1631
    ret = terrno;
×
1632
    goto cmp_end;
×
1633
  }
1634
  dstFp = gzdopen(gzFd, "wb6f");
2✔
1635
  if (dstFp == NULL) {
2!
1636
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1637
    ret = terrno;
×
1638
    (void)close(gzFd);
×
1639
    goto cmp_end;
×
1640
  }
1641

1642
  while (!feof(pSrcFile->fp)) {
4✔
1643
    len = (int32_t)fread(data, 1, compressSize, pSrcFile->fp);
2✔
1644
    if (len > 0) {
2!
1645
      if (gzwrite(dstFp, data, len) == 0) {
2!
1646
        terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1647
        ret = terrno;
×
1648
        goto cmp_end;
×
1649
      }
1650
    }
1651
  }
1652

1653
cmp_end:
2✔
1654

1655
  if (fd >= 0) {
2!
1656
    TAOS_SKIP_ERROR(close(fd));
2✔
1657
  }
1658
  if (pSrcFile) {
2!
1659
    TAOS_SKIP_ERROR(taosCloseFile(&pSrcFile));
2✔
1660
  }
1661

1662
  if (dstFp) {
2!
1663
    TAOS_SKIP_ERROR(gzclose(dstFp));
2✔
1664
  }
1665

1666
  taosMemoryFree(data);
2!
1667

1668
  return ret;
2✔
1669
}
1670

1671
int32_t taosSetFileHandlesLimit() {
21,739✔
1672
#ifdef WINDOWS
1673
  const int max_handles = 8192;
1674
  int       res = _setmaxstdio(max_handles);
1675
  return res == max_handles ? 0 : -1;
1676
#endif
1677
  return 0;
21,739✔
1678
}
1679

1680
int32_t taosLinkFile(char *src, char *dst) {
107✔
1681
#ifndef WINDOWS
1682
  if (-1 == link(src, dst)) {
107!
1683
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1684
    return terrno;
×
1685
  }
1686
#endif
1687
  return 0;
107✔
1688
}
1689

1690
FILE *taosOpenCFile(const char *filename, const char *mode) {
12,632✔
1691
  if (filename == NULL || mode == NULL) {
12,632!
1692
    terrno = TSDB_CODE_INVALID_PARA;
×
1693
    return NULL;
×
1694
  }
1695
  STUB_RAND_IO_ERR(NULL)
1696
  FILE *f = fopen(filename, mode);
12,642✔
1697
  if (NULL == f) {
12,645!
1698
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1699
  }
1700
  return f;
12,645✔
1701
}
1702

1703
int taosSeekCFile(FILE *file, int64_t offset, int whence) {
12,569✔
1704
  if (NULL == file) {
12,569!
1705
    terrno = TSDB_CODE_INVALID_PARA;
×
1706
    return terrno;
×
1707
  }
1708
#ifdef WINDOWS
1709
  return _fseeki64(file, offset, whence);
1710
#else
1711
  int     code = fseeko(file, offset, whence);
12,569✔
1712
  if (-1 == code) {
12,567!
1713
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1714
    code = terrno;
×
1715
  }
1716
  return code;
12,568✔
1717
#endif
1718
}
1719

1720
size_t taosReadFromCFile(void *buffer, size_t size, size_t count, FILE *stream) {
12,568✔
1721
  if (buffer == NULL || stream == NULL) {
12,568!
1722
    terrno = TSDB_CODE_INVALID_PARA;
×
1723
    return 0;
×
1724
  }
1725
  STUB_RAND_IO_ERR(terrno)
1726
  return fread(buffer, size, count, stream);
12,570✔
1727
}
1728

1729
#if 0
1730
size_t taosWriteToCFile(const void *ptr, size_t size, size_t nitems, FILE *stream) {
1731
  STUB_RAND_IO_ERR(terrno)
1732
  return fwrite(ptr, size, nitems, stream);
1733
}
1734
#endif
1735

1736
int taosCloseCFile(FILE *f) { return fclose(f); }
12,645✔
1737

1738
int taosSetAutoDelFile(char *path) {
12,645✔
1739
#ifdef WINDOWS
1740
  bool succ = SetFileAttributes(path, FILE_ATTRIBUTE_TEMPORARY);
1741
  if (succ) {
1742
    return 0;
1743
  } else {
1744
    terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
1745
    return terrno;
1746
  }
1747
#else
1748
  if (-1 == unlink(path)) {
12,645!
1749
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1750
    return terrno;
×
1751
  }
1752
  return 0;
12,645✔
1753
#endif
1754
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc