1*3117ece4Schristos /* 2*3117ece4Schristos * Copyright (c) Meta Platforms, Inc. and affiliates. 3*3117ece4Schristos * All rights reserved. 4*3117ece4Schristos * 5*3117ece4Schristos * This source code is licensed under both the BSD-style license (found in the 6*3117ece4Schristos * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7*3117ece4Schristos * in the COPYING file in the root directory of this source tree). 8*3117ece4Schristos * You may select, at your option, one of the above-listed licenses. 9*3117ece4Schristos */ 10*3117ece4Schristos 11*3117ece4Schristos #if defined (__cplusplus) 12*3117ece4Schristos extern "C" { 13*3117ece4Schristos #endif 14*3117ece4Schristos 15*3117ece4Schristos 16*3117ece4Schristos /*-**************************************** 17*3117ece4Schristos * Dependencies 18*3117ece4Schristos ******************************************/ 19*3117ece4Schristos #include "util.h" /* note : ensure that platform.h is included first ! */ 20*3117ece4Schristos #include <stdlib.h> /* malloc, realloc, free */ 21*3117ece4Schristos #include <stdio.h> /* fprintf */ 22*3117ece4Schristos #include <time.h> /* clock_t, clock, CLOCKS_PER_SEC, nanosleep */ 23*3117ece4Schristos #include <errno.h> 24*3117ece4Schristos #include <assert.h> 25*3117ece4Schristos 26*3117ece4Schristos #if defined(__FreeBSD__) 27*3117ece4Schristos #include <sys/param.h> /* __FreeBSD_version */ 28*3117ece4Schristos #endif /* #ifdef __FreeBSD__ */ 29*3117ece4Schristos 30*3117ece4Schristos #if defined(_WIN32) 31*3117ece4Schristos # include <sys/utime.h> /* utime */ 32*3117ece4Schristos # include <io.h> /* _chmod */ 33*3117ece4Schristos # define ZSTD_USE_UTIMENSAT 0 34*3117ece4Schristos #else 35*3117ece4Schristos # include <unistd.h> /* chown, stat */ 36*3117ece4Schristos # include <sys/stat.h> /* utimensat, st_mtime */ 37*3117ece4Schristos # if (PLATFORM_POSIX_VERSION >= 200809L && defined(st_mtime)) \ 38*3117ece4Schristos || (defined(__FreeBSD__) && __FreeBSD_version >= 1100056) 39*3117ece4Schristos # define ZSTD_USE_UTIMENSAT 1 40*3117ece4Schristos # else 41*3117ece4Schristos # define ZSTD_USE_UTIMENSAT 0 42*3117ece4Schristos # endif 43*3117ece4Schristos # if ZSTD_USE_UTIMENSAT 44*3117ece4Schristos # include <fcntl.h> /* AT_FDCWD */ 45*3117ece4Schristos # else 46*3117ece4Schristos # include <utime.h> /* utime */ 47*3117ece4Schristos # endif 48*3117ece4Schristos #endif 49*3117ece4Schristos 50*3117ece4Schristos #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) 51*3117ece4Schristos #include <direct.h> /* needed for _mkdir in windows */ 52*3117ece4Schristos #endif 53*3117ece4Schristos 54*3117ece4Schristos #if defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L) /* opendir, readdir require POSIX.1-2001 */ 55*3117ece4Schristos # include <dirent.h> /* opendir, readdir */ 56*3117ece4Schristos # include <string.h> /* strerror, memcpy */ 57*3117ece4Schristos #endif /* #ifdef _WIN32 */ 58*3117ece4Schristos 59*3117ece4Schristos /*-**************************************** 60*3117ece4Schristos * Internal Macros 61*3117ece4Schristos ******************************************/ 62*3117ece4Schristos 63*3117ece4Schristos /* CONTROL is almost like an assert(), but is never disabled. 64*3117ece4Schristos * It's designed for failures that may happen rarely, 65*3117ece4Schristos * but we don't want to maintain a specific error code path for them, 66*3117ece4Schristos * such as a malloc() returning NULL for example. 67*3117ece4Schristos * Since it's always active, this macro can trigger side effects. 68*3117ece4Schristos */ 69*3117ece4Schristos #define CONTROL(c) { \ 70*3117ece4Schristos if (!(c)) { \ 71*3117ece4Schristos UTIL_DISPLAYLEVEL(1, "Error : %s, %i : %s", \ 72*3117ece4Schristos __FILE__, __LINE__, #c); \ 73*3117ece4Schristos exit(1); \ 74*3117ece4Schristos } } 75*3117ece4Schristos 76*3117ece4Schristos /* console log */ 77*3117ece4Schristos #define UTIL_DISPLAY(...) fprintf(stderr, __VA_ARGS__) 78*3117ece4Schristos #define UTIL_DISPLAYLEVEL(l, ...) { if (g_utilDisplayLevel>=l) { UTIL_DISPLAY(__VA_ARGS__); } } 79*3117ece4Schristos 80*3117ece4Schristos static int g_traceDepth = 0; 81*3117ece4Schristos int g_traceFileStat = 0; 82*3117ece4Schristos 83*3117ece4Schristos #define UTIL_TRACE_CALL(...) \ 84*3117ece4Schristos { \ 85*3117ece4Schristos if (g_traceFileStat) { \ 86*3117ece4Schristos UTIL_DISPLAY("Trace:FileStat: %*s> ", g_traceDepth, ""); \ 87*3117ece4Schristos UTIL_DISPLAY(__VA_ARGS__); \ 88*3117ece4Schristos UTIL_DISPLAY("\n"); \ 89*3117ece4Schristos ++g_traceDepth; \ 90*3117ece4Schristos } \ 91*3117ece4Schristos } 92*3117ece4Schristos 93*3117ece4Schristos #define UTIL_TRACE_RET(ret) \ 94*3117ece4Schristos { \ 95*3117ece4Schristos if (g_traceFileStat) { \ 96*3117ece4Schristos --g_traceDepth; \ 97*3117ece4Schristos UTIL_DISPLAY("Trace:FileStat: %*s< %d\n", g_traceDepth, "", (ret)); \ 98*3117ece4Schristos } \ 99*3117ece4Schristos } 100*3117ece4Schristos 101*3117ece4Schristos /* A modified version of realloc(). 102*3117ece4Schristos * If UTIL_realloc() fails the original block is freed. 103*3117ece4Schristos */ 104*3117ece4Schristos UTIL_STATIC void* UTIL_realloc(void *ptr, size_t size) 105*3117ece4Schristos { 106*3117ece4Schristos void *newptr = realloc(ptr, size); 107*3117ece4Schristos if (newptr) return newptr; 108*3117ece4Schristos free(ptr); 109*3117ece4Schristos return NULL; 110*3117ece4Schristos } 111*3117ece4Schristos 112*3117ece4Schristos #if defined(_MSC_VER) 113*3117ece4Schristos #define chmod _chmod 114*3117ece4Schristos #endif 115*3117ece4Schristos 116*3117ece4Schristos #ifndef ZSTD_HAVE_FCHMOD 117*3117ece4Schristos #if PLATFORM_POSIX_VERSION >= 199309L 118*3117ece4Schristos #define ZSTD_HAVE_FCHMOD 119*3117ece4Schristos #endif 120*3117ece4Schristos #endif 121*3117ece4Schristos 122*3117ece4Schristos #ifndef ZSTD_HAVE_FCHOWN 123*3117ece4Schristos #if PLATFORM_POSIX_VERSION >= 200809L 124*3117ece4Schristos #define ZSTD_HAVE_FCHOWN 125*3117ece4Schristos #endif 126*3117ece4Schristos #endif 127*3117ece4Schristos 128*3117ece4Schristos /*-**************************************** 129*3117ece4Schristos * Console log 130*3117ece4Schristos ******************************************/ 131*3117ece4Schristos int g_utilDisplayLevel; 132*3117ece4Schristos 133*3117ece4Schristos int UTIL_requireUserConfirmation(const char* prompt, const char* abortMsg, 134*3117ece4Schristos const char* acceptableLetters, int hasStdinInput) { 135*3117ece4Schristos int ch, result; 136*3117ece4Schristos 137*3117ece4Schristos if (hasStdinInput) { 138*3117ece4Schristos UTIL_DISPLAY("stdin is an input - not proceeding.\n"); 139*3117ece4Schristos return 1; 140*3117ece4Schristos } 141*3117ece4Schristos 142*3117ece4Schristos UTIL_DISPLAY("%s", prompt); 143*3117ece4Schristos ch = getchar(); 144*3117ece4Schristos result = 0; 145*3117ece4Schristos if (strchr(acceptableLetters, ch) == NULL) { 146*3117ece4Schristos UTIL_DISPLAY("%s \n", abortMsg); 147*3117ece4Schristos result = 1; 148*3117ece4Schristos } 149*3117ece4Schristos /* flush the rest */ 150*3117ece4Schristos while ((ch!=EOF) && (ch!='\n')) 151*3117ece4Schristos ch = getchar(); 152*3117ece4Schristos return result; 153*3117ece4Schristos } 154*3117ece4Schristos 155*3117ece4Schristos 156*3117ece4Schristos /*-************************************* 157*3117ece4Schristos * Constants 158*3117ece4Schristos ***************************************/ 159*3117ece4Schristos #define LIST_SIZE_INCREASE (8*1024) 160*3117ece4Schristos #define MAX_FILE_OF_FILE_NAMES_SIZE (1<<20)*50 161*3117ece4Schristos 162*3117ece4Schristos 163*3117ece4Schristos /*-************************************* 164*3117ece4Schristos * Functions 165*3117ece4Schristos ***************************************/ 166*3117ece4Schristos 167*3117ece4Schristos void UTIL_traceFileStat(void) 168*3117ece4Schristos { 169*3117ece4Schristos g_traceFileStat = 1; 170*3117ece4Schristos } 171*3117ece4Schristos 172*3117ece4Schristos int UTIL_fstat(const int fd, const char* filename, stat_t* statbuf) 173*3117ece4Schristos { 174*3117ece4Schristos int ret; 175*3117ece4Schristos UTIL_TRACE_CALL("UTIL_stat(%d, %s)", fd, filename); 176*3117ece4Schristos #if defined(_MSC_VER) 177*3117ece4Schristos if (fd >= 0) { 178*3117ece4Schristos ret = !_fstat64(fd, statbuf); 179*3117ece4Schristos } else { 180*3117ece4Schristos ret = !_stat64(filename, statbuf); 181*3117ece4Schristos } 182*3117ece4Schristos #elif defined(__MINGW32__) && defined (__MSVCRT__) 183*3117ece4Schristos if (fd >= 0) { 184*3117ece4Schristos ret = !_fstati64(fd, statbuf); 185*3117ece4Schristos } else { 186*3117ece4Schristos ret = !_stati64(filename, statbuf); 187*3117ece4Schristos } 188*3117ece4Schristos #else 189*3117ece4Schristos if (fd >= 0) { 190*3117ece4Schristos ret = !fstat(fd, statbuf); 191*3117ece4Schristos } else { 192*3117ece4Schristos ret = !stat(filename, statbuf); 193*3117ece4Schristos } 194*3117ece4Schristos #endif 195*3117ece4Schristos UTIL_TRACE_RET(ret); 196*3117ece4Schristos return ret; 197*3117ece4Schristos } 198*3117ece4Schristos 199*3117ece4Schristos int UTIL_stat(const char* filename, stat_t* statbuf) 200*3117ece4Schristos { 201*3117ece4Schristos return UTIL_fstat(-1, filename, statbuf); 202*3117ece4Schristos } 203*3117ece4Schristos 204*3117ece4Schristos int UTIL_isRegularFile(const char* infilename) 205*3117ece4Schristos { 206*3117ece4Schristos stat_t statbuf; 207*3117ece4Schristos int ret; 208*3117ece4Schristos UTIL_TRACE_CALL("UTIL_isRegularFile(%s)", infilename); 209*3117ece4Schristos ret = UTIL_stat(infilename, &statbuf) && UTIL_isRegularFileStat(&statbuf); 210*3117ece4Schristos UTIL_TRACE_RET(ret); 211*3117ece4Schristos return ret; 212*3117ece4Schristos } 213*3117ece4Schristos 214*3117ece4Schristos int UTIL_isRegularFileStat(const stat_t* statbuf) 215*3117ece4Schristos { 216*3117ece4Schristos #if defined(_MSC_VER) 217*3117ece4Schristos return (statbuf->st_mode & S_IFREG) != 0; 218*3117ece4Schristos #else 219*3117ece4Schristos return S_ISREG(statbuf->st_mode) != 0; 220*3117ece4Schristos #endif 221*3117ece4Schristos } 222*3117ece4Schristos 223*3117ece4Schristos /* like chmod, but avoid changing permission of /dev/null */ 224*3117ece4Schristos int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions) 225*3117ece4Schristos { 226*3117ece4Schristos return UTIL_fchmod(-1, filename, statbuf, permissions); 227*3117ece4Schristos } 228*3117ece4Schristos 229*3117ece4Schristos int UTIL_fchmod(const int fd, char const* filename, const stat_t* statbuf, mode_t permissions) 230*3117ece4Schristos { 231*3117ece4Schristos stat_t localStatBuf; 232*3117ece4Schristos UTIL_TRACE_CALL("UTIL_chmod(%s, %#4o)", filename, (unsigned)permissions); 233*3117ece4Schristos if (statbuf == NULL) { 234*3117ece4Schristos if (!UTIL_fstat(fd, filename, &localStatBuf)) { 235*3117ece4Schristos UTIL_TRACE_RET(0); 236*3117ece4Schristos return 0; 237*3117ece4Schristos } 238*3117ece4Schristos statbuf = &localStatBuf; 239*3117ece4Schristos } 240*3117ece4Schristos if (!UTIL_isRegularFileStat(statbuf)) { 241*3117ece4Schristos UTIL_TRACE_RET(0); 242*3117ece4Schristos return 0; /* pretend success, but don't change anything */ 243*3117ece4Schristos } 244*3117ece4Schristos #ifdef ZSTD_HAVE_FCHMOD 245*3117ece4Schristos if (fd >= 0) { 246*3117ece4Schristos int ret; 247*3117ece4Schristos UTIL_TRACE_CALL("fchmod"); 248*3117ece4Schristos ret = fchmod(fd, permissions); 249*3117ece4Schristos UTIL_TRACE_RET(ret); 250*3117ece4Schristos UTIL_TRACE_RET(ret); 251*3117ece4Schristos return ret; 252*3117ece4Schristos } else 253*3117ece4Schristos #endif 254*3117ece4Schristos { 255*3117ece4Schristos int ret; 256*3117ece4Schristos UTIL_TRACE_CALL("chmod"); 257*3117ece4Schristos ret = chmod(filename, permissions); 258*3117ece4Schristos UTIL_TRACE_RET(ret); 259*3117ece4Schristos UTIL_TRACE_RET(ret); 260*3117ece4Schristos return ret; 261*3117ece4Schristos } 262*3117ece4Schristos } 263*3117ece4Schristos 264*3117ece4Schristos /* set access and modification times */ 265*3117ece4Schristos int UTIL_utime(const char* filename, const stat_t *statbuf) 266*3117ece4Schristos { 267*3117ece4Schristos int ret; 268*3117ece4Schristos UTIL_TRACE_CALL("UTIL_utime(%s)", filename); 269*3117ece4Schristos /* We check that st_mtime is a macro here in order to give us confidence 270*3117ece4Schristos * that struct stat has a struct timespec st_mtim member. We need this 271*3117ece4Schristos * check because there are some platforms that claim to be POSIX 2008 272*3117ece4Schristos * compliant but which do not have st_mtim... */ 273*3117ece4Schristos /* FreeBSD has implemented POSIX 2008 for a long time but still only 274*3117ece4Schristos * advertises support for POSIX 2001. They have a version macro that 275*3117ece4Schristos * lets us safely gate them in. 276*3117ece4Schristos * See https://docs.freebsd.org/en/books/porters-handbook/versions/. 277*3117ece4Schristos */ 278*3117ece4Schristos #if ZSTD_USE_UTIMENSAT 279*3117ece4Schristos { 280*3117ece4Schristos /* (atime, mtime) */ 281*3117ece4Schristos struct timespec timebuf[2] = { {0, UTIME_NOW} }; 282*3117ece4Schristos timebuf[1] = statbuf->st_mtim; 283*3117ece4Schristos ret = utimensat(AT_FDCWD, filename, timebuf, 0); 284*3117ece4Schristos } 285*3117ece4Schristos #else 286*3117ece4Schristos { 287*3117ece4Schristos struct utimbuf timebuf; 288*3117ece4Schristos timebuf.actime = time(NULL); 289*3117ece4Schristos timebuf.modtime = statbuf->st_mtime; 290*3117ece4Schristos ret = utime(filename, &timebuf); 291*3117ece4Schristos } 292*3117ece4Schristos #endif 293*3117ece4Schristos errno = 0; 294*3117ece4Schristos UTIL_TRACE_RET(ret); 295*3117ece4Schristos return ret; 296*3117ece4Schristos } 297*3117ece4Schristos 298*3117ece4Schristos int UTIL_setFileStat(const char *filename, const stat_t *statbuf) 299*3117ece4Schristos { 300*3117ece4Schristos return UTIL_setFDStat(-1, filename, statbuf); 301*3117ece4Schristos } 302*3117ece4Schristos 303*3117ece4Schristos int UTIL_setFDStat(const int fd, const char *filename, const stat_t *statbuf) 304*3117ece4Schristos { 305*3117ece4Schristos int res = 0; 306*3117ece4Schristos stat_t curStatBuf; 307*3117ece4Schristos UTIL_TRACE_CALL("UTIL_setFileStat(%d, %s)", fd, filename); 308*3117ece4Schristos 309*3117ece4Schristos if (!UTIL_fstat(fd, filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf)) { 310*3117ece4Schristos UTIL_TRACE_RET(-1); 311*3117ece4Schristos return -1; 312*3117ece4Schristos } 313*3117ece4Schristos 314*3117ece4Schristos /* Mimic gzip's behavior: 315*3117ece4Schristos * 316*3117ece4Schristos * "Change the group first, then the permissions, then the owner. 317*3117ece4Schristos * That way, the permissions will be correct on systems that allow 318*3117ece4Schristos * users to give away files, without introducing a security hole. 319*3117ece4Schristos * Security depends on permissions not containing the setuid or 320*3117ece4Schristos * setgid bits." */ 321*3117ece4Schristos 322*3117ece4Schristos #if !defined(_WIN32) 323*3117ece4Schristos #ifdef ZSTD_HAVE_FCHOWN 324*3117ece4Schristos if (fd >= 0) { 325*3117ece4Schristos res += fchown(fd, -1, statbuf->st_gid); /* Apply group ownership */ 326*3117ece4Schristos } else 327*3117ece4Schristos #endif 328*3117ece4Schristos { 329*3117ece4Schristos res += chown(filename, -1, statbuf->st_gid); /* Apply group ownership */ 330*3117ece4Schristos } 331*3117ece4Schristos #endif 332*3117ece4Schristos 333*3117ece4Schristos res += UTIL_fchmod(fd, filename, &curStatBuf, statbuf->st_mode & 0777); /* Copy file permissions */ 334*3117ece4Schristos 335*3117ece4Schristos #if !defined(_WIN32) 336*3117ece4Schristos #ifdef ZSTD_HAVE_FCHOWN 337*3117ece4Schristos if (fd >= 0) { 338*3117ece4Schristos res += fchown(fd, statbuf->st_uid, -1); /* Apply user ownership */ 339*3117ece4Schristos } else 340*3117ece4Schristos #endif 341*3117ece4Schristos { 342*3117ece4Schristos res += chown(filename, statbuf->st_uid, -1); /* Apply user ownership */ 343*3117ece4Schristos } 344*3117ece4Schristos #endif 345*3117ece4Schristos 346*3117ece4Schristos errno = 0; 347*3117ece4Schristos UTIL_TRACE_RET(-res); 348*3117ece4Schristos return -res; /* number of errors is returned */ 349*3117ece4Schristos } 350*3117ece4Schristos 351*3117ece4Schristos int UTIL_isDirectory(const char* infilename) 352*3117ece4Schristos { 353*3117ece4Schristos stat_t statbuf; 354*3117ece4Schristos int ret; 355*3117ece4Schristos UTIL_TRACE_CALL("UTIL_isDirectory(%s)", infilename); 356*3117ece4Schristos ret = UTIL_stat(infilename, &statbuf) && UTIL_isDirectoryStat(&statbuf); 357*3117ece4Schristos UTIL_TRACE_RET(ret); 358*3117ece4Schristos return ret; 359*3117ece4Schristos } 360*3117ece4Schristos 361*3117ece4Schristos int UTIL_isDirectoryStat(const stat_t* statbuf) 362*3117ece4Schristos { 363*3117ece4Schristos int ret; 364*3117ece4Schristos UTIL_TRACE_CALL("UTIL_isDirectoryStat()"); 365*3117ece4Schristos #if defined(_MSC_VER) 366*3117ece4Schristos ret = (statbuf->st_mode & _S_IFDIR) != 0; 367*3117ece4Schristos #else 368*3117ece4Schristos ret = S_ISDIR(statbuf->st_mode) != 0; 369*3117ece4Schristos #endif 370*3117ece4Schristos UTIL_TRACE_RET(ret); 371*3117ece4Schristos return ret; 372*3117ece4Schristos } 373*3117ece4Schristos 374*3117ece4Schristos int UTIL_compareStr(const void *p1, const void *p2) { 375*3117ece4Schristos return strcmp(* (char * const *) p1, * (char * const *) p2); 376*3117ece4Schristos } 377*3117ece4Schristos 378*3117ece4Schristos int UTIL_isSameFile(const char* fName1, const char* fName2) 379*3117ece4Schristos { 380*3117ece4Schristos int ret; 381*3117ece4Schristos assert(fName1 != NULL); assert(fName2 != NULL); 382*3117ece4Schristos UTIL_TRACE_CALL("UTIL_isSameFile(%s, %s)", fName1, fName2); 383*3117ece4Schristos #if defined(_MSC_VER) || defined(_WIN32) 384*3117ece4Schristos /* note : Visual does not support file identification by inode. 385*3117ece4Schristos * inode does not work on Windows, even with a posix layer, like msys2. 386*3117ece4Schristos * The following work-around is limited to detecting exact name repetition only, 387*3117ece4Schristos * aka `filename` is considered different from `subdir/../filename` */ 388*3117ece4Schristos ret = !strcmp(fName1, fName2); 389*3117ece4Schristos #else 390*3117ece4Schristos { stat_t file1Stat; 391*3117ece4Schristos stat_t file2Stat; 392*3117ece4Schristos ret = UTIL_stat(fName1, &file1Stat) 393*3117ece4Schristos && UTIL_stat(fName2, &file2Stat) 394*3117ece4Schristos && UTIL_isSameFileStat(fName1, fName2, &file1Stat, &file2Stat); 395*3117ece4Schristos } 396*3117ece4Schristos #endif 397*3117ece4Schristos UTIL_TRACE_RET(ret); 398*3117ece4Schristos return ret; 399*3117ece4Schristos } 400*3117ece4Schristos 401*3117ece4Schristos int UTIL_isSameFileStat( 402*3117ece4Schristos const char* fName1, const char* fName2, 403*3117ece4Schristos const stat_t* file1Stat, const stat_t* file2Stat) 404*3117ece4Schristos { 405*3117ece4Schristos int ret; 406*3117ece4Schristos assert(fName1 != NULL); assert(fName2 != NULL); 407*3117ece4Schristos UTIL_TRACE_CALL("UTIL_isSameFileStat(%s, %s)", fName1, fName2); 408*3117ece4Schristos #if defined(_MSC_VER) || defined(_WIN32) 409*3117ece4Schristos /* note : Visual does not support file identification by inode. 410*3117ece4Schristos * inode does not work on Windows, even with a posix layer, like msys2. 411*3117ece4Schristos * The following work-around is limited to detecting exact name repetition only, 412*3117ece4Schristos * aka `filename` is considered different from `subdir/../filename` */ 413*3117ece4Schristos (void)file1Stat; 414*3117ece4Schristos (void)file2Stat; 415*3117ece4Schristos ret = !strcmp(fName1, fName2); 416*3117ece4Schristos #else 417*3117ece4Schristos { 418*3117ece4Schristos ret = (file1Stat->st_dev == file2Stat->st_dev) 419*3117ece4Schristos && (file1Stat->st_ino == file2Stat->st_ino); 420*3117ece4Schristos } 421*3117ece4Schristos #endif 422*3117ece4Schristos UTIL_TRACE_RET(ret); 423*3117ece4Schristos return ret; 424*3117ece4Schristos } 425*3117ece4Schristos 426*3117ece4Schristos /* UTIL_isFIFO : distinguish named pipes */ 427*3117ece4Schristos int UTIL_isFIFO(const char* infilename) 428*3117ece4Schristos { 429*3117ece4Schristos UTIL_TRACE_CALL("UTIL_isFIFO(%s)", infilename); 430*3117ece4Schristos /* macro guards, as defined in : https://linux.die.net/man/2/lstat */ 431*3117ece4Schristos #if PLATFORM_POSIX_VERSION >= 200112L 432*3117ece4Schristos { 433*3117ece4Schristos stat_t statbuf; 434*3117ece4Schristos if (UTIL_stat(infilename, &statbuf) && UTIL_isFIFOStat(&statbuf)) { 435*3117ece4Schristos UTIL_TRACE_RET(1); 436*3117ece4Schristos return 1; 437*3117ece4Schristos } 438*3117ece4Schristos } 439*3117ece4Schristos #endif 440*3117ece4Schristos (void)infilename; 441*3117ece4Schristos UTIL_TRACE_RET(0); 442*3117ece4Schristos return 0; 443*3117ece4Schristos } 444*3117ece4Schristos 445*3117ece4Schristos /* UTIL_isFIFO : distinguish named pipes */ 446*3117ece4Schristos int UTIL_isFIFOStat(const stat_t* statbuf) 447*3117ece4Schristos { 448*3117ece4Schristos /* macro guards, as defined in : https://linux.die.net/man/2/lstat */ 449*3117ece4Schristos #if PLATFORM_POSIX_VERSION >= 200112L 450*3117ece4Schristos if (S_ISFIFO(statbuf->st_mode)) return 1; 451*3117ece4Schristos #endif 452*3117ece4Schristos (void)statbuf; 453*3117ece4Schristos return 0; 454*3117ece4Schristos } 455*3117ece4Schristos 456*3117ece4Schristos /* UTIL_isBlockDevStat : distinguish named pipes */ 457*3117ece4Schristos int UTIL_isBlockDevStat(const stat_t* statbuf) 458*3117ece4Schristos { 459*3117ece4Schristos /* macro guards, as defined in : https://linux.die.net/man/2/lstat */ 460*3117ece4Schristos #if PLATFORM_POSIX_VERSION >= 200112L 461*3117ece4Schristos if (S_ISBLK(statbuf->st_mode)) return 1; 462*3117ece4Schristos #endif 463*3117ece4Schristos (void)statbuf; 464*3117ece4Schristos return 0; 465*3117ece4Schristos } 466*3117ece4Schristos 467*3117ece4Schristos int UTIL_isLink(const char* infilename) 468*3117ece4Schristos { 469*3117ece4Schristos UTIL_TRACE_CALL("UTIL_isLink(%s)", infilename); 470*3117ece4Schristos /* macro guards, as defined in : https://linux.die.net/man/2/lstat */ 471*3117ece4Schristos #if PLATFORM_POSIX_VERSION >= 200112L 472*3117ece4Schristos { 473*3117ece4Schristos stat_t statbuf; 474*3117ece4Schristos int const r = lstat(infilename, &statbuf); 475*3117ece4Schristos if (!r && S_ISLNK(statbuf.st_mode)) { 476*3117ece4Schristos UTIL_TRACE_RET(1); 477*3117ece4Schristos return 1; 478*3117ece4Schristos } 479*3117ece4Schristos } 480*3117ece4Schristos #endif 481*3117ece4Schristos (void)infilename; 482*3117ece4Schristos UTIL_TRACE_RET(0); 483*3117ece4Schristos return 0; 484*3117ece4Schristos } 485*3117ece4Schristos 486*3117ece4Schristos static int g_fakeStdinIsConsole = 0; 487*3117ece4Schristos static int g_fakeStderrIsConsole = 0; 488*3117ece4Schristos static int g_fakeStdoutIsConsole = 0; 489*3117ece4Schristos 490*3117ece4Schristos int UTIL_isConsole(FILE* file) 491*3117ece4Schristos { 492*3117ece4Schristos int ret; 493*3117ece4Schristos UTIL_TRACE_CALL("UTIL_isConsole(%d)", fileno(file)); 494*3117ece4Schristos if (file == stdin && g_fakeStdinIsConsole) 495*3117ece4Schristos ret = 1; 496*3117ece4Schristos else if (file == stderr && g_fakeStderrIsConsole) 497*3117ece4Schristos ret = 1; 498*3117ece4Schristos else if (file == stdout && g_fakeStdoutIsConsole) 499*3117ece4Schristos ret = 1; 500*3117ece4Schristos else 501*3117ece4Schristos ret = IS_CONSOLE(file); 502*3117ece4Schristos UTIL_TRACE_RET(ret); 503*3117ece4Schristos return ret; 504*3117ece4Schristos } 505*3117ece4Schristos 506*3117ece4Schristos void UTIL_fakeStdinIsConsole(void) 507*3117ece4Schristos { 508*3117ece4Schristos g_fakeStdinIsConsole = 1; 509*3117ece4Schristos } 510*3117ece4Schristos void UTIL_fakeStdoutIsConsole(void) 511*3117ece4Schristos { 512*3117ece4Schristos g_fakeStdoutIsConsole = 1; 513*3117ece4Schristos } 514*3117ece4Schristos void UTIL_fakeStderrIsConsole(void) 515*3117ece4Schristos { 516*3117ece4Schristos g_fakeStderrIsConsole = 1; 517*3117ece4Schristos } 518*3117ece4Schristos 519*3117ece4Schristos U64 UTIL_getFileSize(const char* infilename) 520*3117ece4Schristos { 521*3117ece4Schristos stat_t statbuf; 522*3117ece4Schristos UTIL_TRACE_CALL("UTIL_getFileSize(%s)", infilename); 523*3117ece4Schristos if (!UTIL_stat(infilename, &statbuf)) { 524*3117ece4Schristos UTIL_TRACE_RET(-1); 525*3117ece4Schristos return UTIL_FILESIZE_UNKNOWN; 526*3117ece4Schristos } 527*3117ece4Schristos { 528*3117ece4Schristos U64 const size = UTIL_getFileSizeStat(&statbuf); 529*3117ece4Schristos UTIL_TRACE_RET((int)size); 530*3117ece4Schristos return size; 531*3117ece4Schristos } 532*3117ece4Schristos } 533*3117ece4Schristos 534*3117ece4Schristos U64 UTIL_getFileSizeStat(const stat_t* statbuf) 535*3117ece4Schristos { 536*3117ece4Schristos if (!UTIL_isRegularFileStat(statbuf)) return UTIL_FILESIZE_UNKNOWN; 537*3117ece4Schristos #if defined(_MSC_VER) 538*3117ece4Schristos if (!(statbuf->st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN; 539*3117ece4Schristos #elif defined(__MINGW32__) && defined (__MSVCRT__) 540*3117ece4Schristos if (!(statbuf->st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN; 541*3117ece4Schristos #else 542*3117ece4Schristos if (!S_ISREG(statbuf->st_mode)) return UTIL_FILESIZE_UNKNOWN; 543*3117ece4Schristos #endif 544*3117ece4Schristos return (U64)statbuf->st_size; 545*3117ece4Schristos } 546*3117ece4Schristos 547*3117ece4Schristos UTIL_HumanReadableSize_t UTIL_makeHumanReadableSize(U64 size) 548*3117ece4Schristos { 549*3117ece4Schristos UTIL_HumanReadableSize_t hrs; 550*3117ece4Schristos 551*3117ece4Schristos if (g_utilDisplayLevel > 3) { 552*3117ece4Schristos /* In verbose mode, do not scale sizes down, except in the case of 553*3117ece4Schristos * values that exceed the integral precision of a double. */ 554*3117ece4Schristos if (size >= (1ull << 53)) { 555*3117ece4Schristos hrs.value = (double)size / (1ull << 20); 556*3117ece4Schristos hrs.suffix = " MiB"; 557*3117ece4Schristos /* At worst, a double representation of a maximal size will be 558*3117ece4Schristos * accurate to better than tens of kilobytes. */ 559*3117ece4Schristos hrs.precision = 2; 560*3117ece4Schristos } else { 561*3117ece4Schristos hrs.value = (double)size; 562*3117ece4Schristos hrs.suffix = " B"; 563*3117ece4Schristos hrs.precision = 0; 564*3117ece4Schristos } 565*3117ece4Schristos } else { 566*3117ece4Schristos /* In regular mode, scale sizes down and use suffixes. */ 567*3117ece4Schristos if (size >= (1ull << 60)) { 568*3117ece4Schristos hrs.value = (double)size / (1ull << 60); 569*3117ece4Schristos hrs.suffix = " EiB"; 570*3117ece4Schristos } else if (size >= (1ull << 50)) { 571*3117ece4Schristos hrs.value = (double)size / (1ull << 50); 572*3117ece4Schristos hrs.suffix = " PiB"; 573*3117ece4Schristos } else if (size >= (1ull << 40)) { 574*3117ece4Schristos hrs.value = (double)size / (1ull << 40); 575*3117ece4Schristos hrs.suffix = " TiB"; 576*3117ece4Schristos } else if (size >= (1ull << 30)) { 577*3117ece4Schristos hrs.value = (double)size / (1ull << 30); 578*3117ece4Schristos hrs.suffix = " GiB"; 579*3117ece4Schristos } else if (size >= (1ull << 20)) { 580*3117ece4Schristos hrs.value = (double)size / (1ull << 20); 581*3117ece4Schristos hrs.suffix = " MiB"; 582*3117ece4Schristos } else if (size >= (1ull << 10)) { 583*3117ece4Schristos hrs.value = (double)size / (1ull << 10); 584*3117ece4Schristos hrs.suffix = " KiB"; 585*3117ece4Schristos } else { 586*3117ece4Schristos hrs.value = (double)size; 587*3117ece4Schristos hrs.suffix = " B"; 588*3117ece4Schristos } 589*3117ece4Schristos 590*3117ece4Schristos if (hrs.value >= 100 || (U64)hrs.value == size) { 591*3117ece4Schristos hrs.precision = 0; 592*3117ece4Schristos } else if (hrs.value >= 10) { 593*3117ece4Schristos hrs.precision = 1; 594*3117ece4Schristos } else if (hrs.value > 1) { 595*3117ece4Schristos hrs.precision = 2; 596*3117ece4Schristos } else { 597*3117ece4Schristos hrs.precision = 3; 598*3117ece4Schristos } 599*3117ece4Schristos } 600*3117ece4Schristos 601*3117ece4Schristos return hrs; 602*3117ece4Schristos } 603*3117ece4Schristos 604*3117ece4Schristos U64 UTIL_getTotalFileSize(const char* const * fileNamesTable, unsigned nbFiles) 605*3117ece4Schristos { 606*3117ece4Schristos U64 total = 0; 607*3117ece4Schristos unsigned n; 608*3117ece4Schristos UTIL_TRACE_CALL("UTIL_getTotalFileSize(%u)", nbFiles); 609*3117ece4Schristos for (n=0; n<nbFiles; n++) { 610*3117ece4Schristos U64 const size = UTIL_getFileSize(fileNamesTable[n]); 611*3117ece4Schristos if (size == UTIL_FILESIZE_UNKNOWN) { 612*3117ece4Schristos UTIL_TRACE_RET(-1); 613*3117ece4Schristos return UTIL_FILESIZE_UNKNOWN; 614*3117ece4Schristos } 615*3117ece4Schristos total += size; 616*3117ece4Schristos } 617*3117ece4Schristos UTIL_TRACE_RET((int)total); 618*3117ece4Schristos return total; 619*3117ece4Schristos } 620*3117ece4Schristos 621*3117ece4Schristos 622*3117ece4Schristos /* condition : @file must be valid, and not have reached its end. 623*3117ece4Schristos * @return : length of line written into @buf, ended with `\0` instead of '\n', 624*3117ece4Schristos * or 0, if there is no new line */ 625*3117ece4Schristos static size_t readLineFromFile(char* buf, size_t len, FILE* file) 626*3117ece4Schristos { 627*3117ece4Schristos assert(!feof(file)); 628*3117ece4Schristos if ( fgets(buf, (int) len, file) == NULL ) return 0; 629*3117ece4Schristos { size_t linelen = strlen(buf); 630*3117ece4Schristos if (strlen(buf)==0) return 0; 631*3117ece4Schristos if (buf[linelen-1] == '\n') linelen--; 632*3117ece4Schristos buf[linelen] = '\0'; 633*3117ece4Schristos return linelen+1; 634*3117ece4Schristos } 635*3117ece4Schristos } 636*3117ece4Schristos 637*3117ece4Schristos /* Conditions : 638*3117ece4Schristos * size of @inputFileName file must be < @dstCapacity 639*3117ece4Schristos * @dst must be initialized 640*3117ece4Schristos * @return : nb of lines 641*3117ece4Schristos * or -1 if there's an error 642*3117ece4Schristos */ 643*3117ece4Schristos static int 644*3117ece4Schristos readLinesFromFile(void* dst, size_t dstCapacity, 645*3117ece4Schristos const char* inputFileName) 646*3117ece4Schristos { 647*3117ece4Schristos int nbFiles = 0; 648*3117ece4Schristos size_t pos = 0; 649*3117ece4Schristos char* const buf = (char*)dst; 650*3117ece4Schristos FILE* const inputFile = fopen(inputFileName, "r"); 651*3117ece4Schristos 652*3117ece4Schristos assert(dst != NULL); 653*3117ece4Schristos 654*3117ece4Schristos if(!inputFile) { 655*3117ece4Schristos if (g_utilDisplayLevel >= 1) perror("zstd:util:readLinesFromFile"); 656*3117ece4Schristos return -1; 657*3117ece4Schristos } 658*3117ece4Schristos 659*3117ece4Schristos while ( !feof(inputFile) ) { 660*3117ece4Schristos size_t const lineLength = readLineFromFile(buf+pos, dstCapacity-pos, inputFile); 661*3117ece4Schristos if (lineLength == 0) break; 662*3117ece4Schristos assert(pos + lineLength <= dstCapacity); /* '=' for inputFile not terminated with '\n' */ 663*3117ece4Schristos pos += lineLength; 664*3117ece4Schristos ++nbFiles; 665*3117ece4Schristos } 666*3117ece4Schristos 667*3117ece4Schristos CONTROL( fclose(inputFile) == 0 ); 668*3117ece4Schristos 669*3117ece4Schristos return nbFiles; 670*3117ece4Schristos } 671*3117ece4Schristos 672*3117ece4Schristos /*Note: buf is not freed in case function successfully created table because filesTable->fileNames[0] = buf*/ 673*3117ece4Schristos FileNamesTable* 674*3117ece4Schristos UTIL_createFileNamesTable_fromFileName(const char* inputFileName) 675*3117ece4Schristos { 676*3117ece4Schristos size_t nbFiles = 0; 677*3117ece4Schristos char* buf; 678*3117ece4Schristos size_t bufSize; 679*3117ece4Schristos stat_t statbuf; 680*3117ece4Schristos 681*3117ece4Schristos if (!UTIL_stat(inputFileName, &statbuf) || !UTIL_isRegularFileStat(&statbuf)) 682*3117ece4Schristos return NULL; 683*3117ece4Schristos 684*3117ece4Schristos { U64 const inputFileSize = UTIL_getFileSizeStat(&statbuf); 685*3117ece4Schristos if(inputFileSize > MAX_FILE_OF_FILE_NAMES_SIZE) 686*3117ece4Schristos return NULL; 687*3117ece4Schristos bufSize = (size_t)(inputFileSize + 1); /* (+1) to add '\0' at the end of last filename */ 688*3117ece4Schristos } 689*3117ece4Schristos 690*3117ece4Schristos buf = (char*) malloc(bufSize); 691*3117ece4Schristos CONTROL( buf != NULL ); 692*3117ece4Schristos 693*3117ece4Schristos { int const ret_nbFiles = readLinesFromFile(buf, bufSize, inputFileName); 694*3117ece4Schristos 695*3117ece4Schristos if (ret_nbFiles <= 0) { 696*3117ece4Schristos free(buf); 697*3117ece4Schristos return NULL; 698*3117ece4Schristos } 699*3117ece4Schristos nbFiles = (size_t)ret_nbFiles; 700*3117ece4Schristos } 701*3117ece4Schristos 702*3117ece4Schristos { const char** filenamesTable = (const char**) malloc(nbFiles * sizeof(*filenamesTable)); 703*3117ece4Schristos CONTROL(filenamesTable != NULL); 704*3117ece4Schristos 705*3117ece4Schristos { size_t fnb, pos = 0; 706*3117ece4Schristos for (fnb = 0; fnb < nbFiles; fnb++) { 707*3117ece4Schristos filenamesTable[fnb] = buf+pos; 708*3117ece4Schristos pos += strlen(buf+pos)+1; /* +1 for the finishing `\0` */ 709*3117ece4Schristos } 710*3117ece4Schristos assert(pos <= bufSize); 711*3117ece4Schristos } 712*3117ece4Schristos 713*3117ece4Schristos return UTIL_assembleFileNamesTable(filenamesTable, nbFiles, buf); 714*3117ece4Schristos } 715*3117ece4Schristos } 716*3117ece4Schristos 717*3117ece4Schristos static FileNamesTable* 718*3117ece4Schristos UTIL_assembleFileNamesTable2(const char** filenames, size_t tableSize, size_t tableCapacity, char* buf) 719*3117ece4Schristos { 720*3117ece4Schristos FileNamesTable* const table = (FileNamesTable*) malloc(sizeof(*table)); 721*3117ece4Schristos CONTROL(table != NULL); 722*3117ece4Schristos table->fileNames = filenames; 723*3117ece4Schristos table->buf = buf; 724*3117ece4Schristos table->tableSize = tableSize; 725*3117ece4Schristos table->tableCapacity = tableCapacity; 726*3117ece4Schristos return table; 727*3117ece4Schristos } 728*3117ece4Schristos 729*3117ece4Schristos FileNamesTable* 730*3117ece4Schristos UTIL_assembleFileNamesTable(const char** filenames, size_t tableSize, char* buf) 731*3117ece4Schristos { 732*3117ece4Schristos return UTIL_assembleFileNamesTable2(filenames, tableSize, tableSize, buf); 733*3117ece4Schristos } 734*3117ece4Schristos 735*3117ece4Schristos void UTIL_freeFileNamesTable(FileNamesTable* table) 736*3117ece4Schristos { 737*3117ece4Schristos if (table==NULL) return; 738*3117ece4Schristos free((void*)table->fileNames); 739*3117ece4Schristos free(table->buf); 740*3117ece4Schristos free(table); 741*3117ece4Schristos } 742*3117ece4Schristos 743*3117ece4Schristos FileNamesTable* UTIL_allocateFileNamesTable(size_t tableSize) 744*3117ece4Schristos { 745*3117ece4Schristos const char** const fnTable = (const char**)malloc(tableSize * sizeof(*fnTable)); 746*3117ece4Schristos FileNamesTable* fnt; 747*3117ece4Schristos if (fnTable==NULL) return NULL; 748*3117ece4Schristos fnt = UTIL_assembleFileNamesTable(fnTable, tableSize, NULL); 749*3117ece4Schristos fnt->tableSize = 0; /* the table is empty */ 750*3117ece4Schristos return fnt; 751*3117ece4Schristos } 752*3117ece4Schristos 753*3117ece4Schristos int UTIL_searchFileNamesTable(FileNamesTable* table, char const* name) { 754*3117ece4Schristos size_t i; 755*3117ece4Schristos for(i=0 ;i < table->tableSize; i++) { 756*3117ece4Schristos if(!strcmp(table->fileNames[i], name)) { 757*3117ece4Schristos return (int)i; 758*3117ece4Schristos } 759*3117ece4Schristos } 760*3117ece4Schristos return -1; 761*3117ece4Schristos } 762*3117ece4Schristos 763*3117ece4Schristos void UTIL_refFilename(FileNamesTable* fnt, const char* filename) 764*3117ece4Schristos { 765*3117ece4Schristos assert(fnt->tableSize < fnt->tableCapacity); 766*3117ece4Schristos fnt->fileNames[fnt->tableSize] = filename; 767*3117ece4Schristos fnt->tableSize++; 768*3117ece4Schristos } 769*3117ece4Schristos 770*3117ece4Schristos static size_t getTotalTableSize(FileNamesTable* table) 771*3117ece4Schristos { 772*3117ece4Schristos size_t fnb, totalSize = 0; 773*3117ece4Schristos for(fnb = 0 ; fnb < table->tableSize && table->fileNames[fnb] ; ++fnb) { 774*3117ece4Schristos totalSize += strlen(table->fileNames[fnb]) + 1; /* +1 to add '\0' at the end of each fileName */ 775*3117ece4Schristos } 776*3117ece4Schristos return totalSize; 777*3117ece4Schristos } 778*3117ece4Schristos 779*3117ece4Schristos FileNamesTable* 780*3117ece4Schristos UTIL_mergeFileNamesTable(FileNamesTable* table1, FileNamesTable* table2) 781*3117ece4Schristos { 782*3117ece4Schristos unsigned newTableIdx = 0; 783*3117ece4Schristos size_t pos = 0; 784*3117ece4Schristos size_t newTotalTableSize; 785*3117ece4Schristos char* buf; 786*3117ece4Schristos 787*3117ece4Schristos FileNamesTable* const newTable = UTIL_assembleFileNamesTable(NULL, 0, NULL); 788*3117ece4Schristos CONTROL( newTable != NULL ); 789*3117ece4Schristos 790*3117ece4Schristos newTotalTableSize = getTotalTableSize(table1) + getTotalTableSize(table2); 791*3117ece4Schristos 792*3117ece4Schristos buf = (char*) calloc(newTotalTableSize, sizeof(*buf)); 793*3117ece4Schristos CONTROL ( buf != NULL ); 794*3117ece4Schristos 795*3117ece4Schristos newTable->buf = buf; 796*3117ece4Schristos newTable->tableSize = table1->tableSize + table2->tableSize; 797*3117ece4Schristos newTable->fileNames = (const char **) calloc(newTable->tableSize, sizeof(*(newTable->fileNames))); 798*3117ece4Schristos CONTROL ( newTable->fileNames != NULL ); 799*3117ece4Schristos 800*3117ece4Schristos { unsigned idx1; 801*3117ece4Schristos for( idx1=0 ; (idx1 < table1->tableSize) && table1->fileNames[idx1] && (pos < newTotalTableSize); ++idx1, ++newTableIdx) { 802*3117ece4Schristos size_t const curLen = strlen(table1->fileNames[idx1]); 803*3117ece4Schristos memcpy(buf+pos, table1->fileNames[idx1], curLen); 804*3117ece4Schristos assert(newTableIdx <= newTable->tableSize); 805*3117ece4Schristos newTable->fileNames[newTableIdx] = buf+pos; 806*3117ece4Schristos pos += curLen+1; 807*3117ece4Schristos } } 808*3117ece4Schristos 809*3117ece4Schristos { unsigned idx2; 810*3117ece4Schristos for( idx2=0 ; (idx2 < table2->tableSize) && table2->fileNames[idx2] && (pos < newTotalTableSize) ; ++idx2, ++newTableIdx) { 811*3117ece4Schristos size_t const curLen = strlen(table2->fileNames[idx2]); 812*3117ece4Schristos memcpy(buf+pos, table2->fileNames[idx2], curLen); 813*3117ece4Schristos assert(newTableIdx < newTable->tableSize); 814*3117ece4Schristos newTable->fileNames[newTableIdx] = buf+pos; 815*3117ece4Schristos pos += curLen+1; 816*3117ece4Schristos } } 817*3117ece4Schristos assert(pos <= newTotalTableSize); 818*3117ece4Schristos newTable->tableSize = newTableIdx; 819*3117ece4Schristos 820*3117ece4Schristos UTIL_freeFileNamesTable(table1); 821*3117ece4Schristos UTIL_freeFileNamesTable(table2); 822*3117ece4Schristos 823*3117ece4Schristos return newTable; 824*3117ece4Schristos } 825*3117ece4Schristos 826*3117ece4Schristos #ifdef _WIN32 827*3117ece4Schristos static int UTIL_prepareFileList(const char* dirName, 828*3117ece4Schristos char** bufStart, size_t* pos, 829*3117ece4Schristos char** bufEnd, int followLinks) 830*3117ece4Schristos { 831*3117ece4Schristos char* path; 832*3117ece4Schristos size_t dirLength, pathLength; 833*3117ece4Schristos int nbFiles = 0; 834*3117ece4Schristos WIN32_FIND_DATAA cFile; 835*3117ece4Schristos HANDLE hFile; 836*3117ece4Schristos 837*3117ece4Schristos dirLength = strlen(dirName); 838*3117ece4Schristos path = (char*) malloc(dirLength + 3); 839*3117ece4Schristos if (!path) return 0; 840*3117ece4Schristos 841*3117ece4Schristos memcpy(path, dirName, dirLength); 842*3117ece4Schristos path[dirLength] = '\\'; 843*3117ece4Schristos path[dirLength+1] = '*'; 844*3117ece4Schristos path[dirLength+2] = 0; 845*3117ece4Schristos 846*3117ece4Schristos hFile=FindFirstFileA(path, &cFile); 847*3117ece4Schristos if (hFile == INVALID_HANDLE_VALUE) { 848*3117ece4Schristos UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s'\n", dirName); 849*3117ece4Schristos return 0; 850*3117ece4Schristos } 851*3117ece4Schristos free(path); 852*3117ece4Schristos 853*3117ece4Schristos do { 854*3117ece4Schristos size_t const fnameLength = strlen(cFile.cFileName); 855*3117ece4Schristos path = (char*) malloc(dirLength + fnameLength + 2); 856*3117ece4Schristos if (!path) { FindClose(hFile); return 0; } 857*3117ece4Schristos memcpy(path, dirName, dirLength); 858*3117ece4Schristos path[dirLength] = '\\'; 859*3117ece4Schristos memcpy(path+dirLength+1, cFile.cFileName, fnameLength); 860*3117ece4Schristos pathLength = dirLength+1+fnameLength; 861*3117ece4Schristos path[pathLength] = 0; 862*3117ece4Schristos if (cFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 863*3117ece4Schristos if ( strcmp (cFile.cFileName, "..") == 0 864*3117ece4Schristos || strcmp (cFile.cFileName, ".") == 0 ) 865*3117ece4Schristos continue; 866*3117ece4Schristos /* Recursively call "UTIL_prepareFileList" with the new path. */ 867*3117ece4Schristos nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks); 868*3117ece4Schristos if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; } 869*3117ece4Schristos } else if ( (cFile.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) 870*3117ece4Schristos || (cFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) 871*3117ece4Schristos || (cFile.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ) { 872*3117ece4Schristos if (*bufStart + *pos + pathLength >= *bufEnd) { 873*3117ece4Schristos ptrdiff_t const newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE; 874*3117ece4Schristos *bufStart = (char*)UTIL_realloc(*bufStart, newListSize); 875*3117ece4Schristos if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; } 876*3117ece4Schristos *bufEnd = *bufStart + newListSize; 877*3117ece4Schristos } 878*3117ece4Schristos if (*bufStart + *pos + pathLength < *bufEnd) { 879*3117ece4Schristos memcpy(*bufStart + *pos, path, pathLength+1 /* include final \0 */); 880*3117ece4Schristos *pos += pathLength + 1; 881*3117ece4Schristos nbFiles++; 882*3117ece4Schristos } } 883*3117ece4Schristos free(path); 884*3117ece4Schristos } while (FindNextFileA(hFile, &cFile)); 885*3117ece4Schristos 886*3117ece4Schristos FindClose(hFile); 887*3117ece4Schristos return nbFiles; 888*3117ece4Schristos } 889*3117ece4Schristos 890*3117ece4Schristos #elif defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L) /* opendir, readdir require POSIX.1-2001 */ 891*3117ece4Schristos 892*3117ece4Schristos static int UTIL_prepareFileList(const char *dirName, 893*3117ece4Schristos char** bufStart, size_t* pos, 894*3117ece4Schristos char** bufEnd, int followLinks) 895*3117ece4Schristos { 896*3117ece4Schristos DIR* dir; 897*3117ece4Schristos struct dirent * entry; 898*3117ece4Schristos size_t dirLength; 899*3117ece4Schristos int nbFiles = 0; 900*3117ece4Schristos 901*3117ece4Schristos if (!(dir = opendir(dirName))) { 902*3117ece4Schristos UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s': %s\n", dirName, strerror(errno)); 903*3117ece4Schristos return 0; 904*3117ece4Schristos } 905*3117ece4Schristos 906*3117ece4Schristos dirLength = strlen(dirName); 907*3117ece4Schristos errno = 0; 908*3117ece4Schristos while ((entry = readdir(dir)) != NULL) { 909*3117ece4Schristos char* path; 910*3117ece4Schristos size_t fnameLength, pathLength; 911*3117ece4Schristos if (strcmp (entry->d_name, "..") == 0 || 912*3117ece4Schristos strcmp (entry->d_name, ".") == 0) continue; 913*3117ece4Schristos fnameLength = strlen(entry->d_name); 914*3117ece4Schristos path = (char*) malloc(dirLength + fnameLength + 2); 915*3117ece4Schristos if (!path) { closedir(dir); return 0; } 916*3117ece4Schristos memcpy(path, dirName, dirLength); 917*3117ece4Schristos 918*3117ece4Schristos path[dirLength] = '/'; 919*3117ece4Schristos memcpy(path+dirLength+1, entry->d_name, fnameLength); 920*3117ece4Schristos pathLength = dirLength+1+fnameLength; 921*3117ece4Schristos path[pathLength] = 0; 922*3117ece4Schristos 923*3117ece4Schristos if (!followLinks && UTIL_isLink(path)) { 924*3117ece4Schristos UTIL_DISPLAYLEVEL(2, "Warning : %s is a symbolic link, ignoring\n", path); 925*3117ece4Schristos free(path); 926*3117ece4Schristos continue; 927*3117ece4Schristos } 928*3117ece4Schristos 929*3117ece4Schristos if (UTIL_isDirectory(path)) { 930*3117ece4Schristos nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks); /* Recursively call "UTIL_prepareFileList" with the new path. */ 931*3117ece4Schristos if (*bufStart == NULL) { free(path); closedir(dir); return 0; } 932*3117ece4Schristos } else { 933*3117ece4Schristos if (*bufStart + *pos + pathLength >= *bufEnd) { 934*3117ece4Schristos ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE; 935*3117ece4Schristos assert(newListSize >= 0); 936*3117ece4Schristos *bufStart = (char*)UTIL_realloc(*bufStart, (size_t)newListSize); 937*3117ece4Schristos if (*bufStart != NULL) { 938*3117ece4Schristos *bufEnd = *bufStart + newListSize; 939*3117ece4Schristos } else { 940*3117ece4Schristos free(path); closedir(dir); return 0; 941*3117ece4Schristos } 942*3117ece4Schristos } 943*3117ece4Schristos if (*bufStart + *pos + pathLength < *bufEnd) { 944*3117ece4Schristos memcpy(*bufStart + *pos, path, pathLength + 1); /* with final \0 */ 945*3117ece4Schristos *pos += pathLength + 1; 946*3117ece4Schristos nbFiles++; 947*3117ece4Schristos } } 948*3117ece4Schristos free(path); 949*3117ece4Schristos errno = 0; /* clear errno after UTIL_isDirectory, UTIL_prepareFileList */ 950*3117ece4Schristos } 951*3117ece4Schristos 952*3117ece4Schristos if (errno != 0) { 953*3117ece4Schristos UTIL_DISPLAYLEVEL(1, "readdir(%s) error: %s \n", dirName, strerror(errno)); 954*3117ece4Schristos free(*bufStart); 955*3117ece4Schristos *bufStart = NULL; 956*3117ece4Schristos } 957*3117ece4Schristos closedir(dir); 958*3117ece4Schristos return nbFiles; 959*3117ece4Schristos } 960*3117ece4Schristos 961*3117ece4Schristos #else 962*3117ece4Schristos 963*3117ece4Schristos static int UTIL_prepareFileList(const char *dirName, 964*3117ece4Schristos char** bufStart, size_t* pos, 965*3117ece4Schristos char** bufEnd, int followLinks) 966*3117ece4Schristos { 967*3117ece4Schristos (void)bufStart; (void)bufEnd; (void)pos; (void)followLinks; 968*3117ece4Schristos UTIL_DISPLAYLEVEL(1, "Directory %s ignored (compiled without _WIN32 or _POSIX_C_SOURCE) \n", dirName); 969*3117ece4Schristos return 0; 970*3117ece4Schristos } 971*3117ece4Schristos 972*3117ece4Schristos #endif /* #ifdef _WIN32 */ 973*3117ece4Schristos 974*3117ece4Schristos int UTIL_isCompressedFile(const char *inputName, const char *extensionList[]) 975*3117ece4Schristos { 976*3117ece4Schristos const char* ext = UTIL_getFileExtension(inputName); 977*3117ece4Schristos while(*extensionList!=NULL) 978*3117ece4Schristos { 979*3117ece4Schristos const int isCompressedExtension = strcmp(ext,*extensionList); 980*3117ece4Schristos if(isCompressedExtension==0) 981*3117ece4Schristos return 1; 982*3117ece4Schristos ++extensionList; 983*3117ece4Schristos } 984*3117ece4Schristos return 0; 985*3117ece4Schristos } 986*3117ece4Schristos 987*3117ece4Schristos /*Utility function to get file extension from file */ 988*3117ece4Schristos const char* UTIL_getFileExtension(const char* infilename) 989*3117ece4Schristos { 990*3117ece4Schristos const char* extension = strrchr(infilename, '.'); 991*3117ece4Schristos if(!extension || extension==infilename) return ""; 992*3117ece4Schristos return extension; 993*3117ece4Schristos } 994*3117ece4Schristos 995*3117ece4Schristos static int pathnameHas2Dots(const char *pathname) 996*3117ece4Schristos { 997*3117ece4Schristos /* We need to figure out whether any ".." present in the path is a whole 998*3117ece4Schristos * path token, which is the case if it is bordered on both sides by either 999*3117ece4Schristos * the beginning/end of the path or by a directory separator. 1000*3117ece4Schristos */ 1001*3117ece4Schristos const char *needle = pathname; 1002*3117ece4Schristos while (1) { 1003*3117ece4Schristos needle = strstr(needle, ".."); 1004*3117ece4Schristos 1005*3117ece4Schristos if (needle == NULL) { 1006*3117ece4Schristos return 0; 1007*3117ece4Schristos } 1008*3117ece4Schristos 1009*3117ece4Schristos if ((needle == pathname || needle[-1] == PATH_SEP) 1010*3117ece4Schristos && (needle[2] == '\0' || needle[2] == PATH_SEP)) { 1011*3117ece4Schristos return 1; 1012*3117ece4Schristos } 1013*3117ece4Schristos 1014*3117ece4Schristos /* increment so we search for the next match */ 1015*3117ece4Schristos needle++; 1016*3117ece4Schristos }; 1017*3117ece4Schristos return 0; 1018*3117ece4Schristos } 1019*3117ece4Schristos 1020*3117ece4Schristos static int isFileNameValidForMirroredOutput(const char *filename) 1021*3117ece4Schristos { 1022*3117ece4Schristos return !pathnameHas2Dots(filename); 1023*3117ece4Schristos } 1024*3117ece4Schristos 1025*3117ece4Schristos 1026*3117ece4Schristos #define DIR_DEFAULT_MODE 0755 1027*3117ece4Schristos static mode_t getDirMode(const char *dirName) 1028*3117ece4Schristos { 1029*3117ece4Schristos stat_t st; 1030*3117ece4Schristos if (!UTIL_stat(dirName, &st)) { 1031*3117ece4Schristos UTIL_DISPLAY("zstd: failed to get DIR stats %s: %s\n", dirName, strerror(errno)); 1032*3117ece4Schristos return DIR_DEFAULT_MODE; 1033*3117ece4Schristos } 1034*3117ece4Schristos if (!UTIL_isDirectoryStat(&st)) { 1035*3117ece4Schristos UTIL_DISPLAY("zstd: expected directory: %s\n", dirName); 1036*3117ece4Schristos return DIR_DEFAULT_MODE; 1037*3117ece4Schristos } 1038*3117ece4Schristos return st.st_mode; 1039*3117ece4Schristos } 1040*3117ece4Schristos 1041*3117ece4Schristos static int makeDir(const char *dir, mode_t mode) 1042*3117ece4Schristos { 1043*3117ece4Schristos #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) 1044*3117ece4Schristos int ret = _mkdir(dir); 1045*3117ece4Schristos (void) mode; 1046*3117ece4Schristos #else 1047*3117ece4Schristos int ret = mkdir(dir, mode); 1048*3117ece4Schristos #endif 1049*3117ece4Schristos if (ret != 0) { 1050*3117ece4Schristos if (errno == EEXIST) 1051*3117ece4Schristos return 0; 1052*3117ece4Schristos UTIL_DISPLAY("zstd: failed to create DIR %s: %s\n", dir, strerror(errno)); 1053*3117ece4Schristos } 1054*3117ece4Schristos return ret; 1055*3117ece4Schristos } 1056*3117ece4Schristos 1057*3117ece4Schristos /* this function requires a mutable input string */ 1058*3117ece4Schristos static void convertPathnameToDirName(char *pathname) 1059*3117ece4Schristos { 1060*3117ece4Schristos size_t len = 0; 1061*3117ece4Schristos char* pos = NULL; 1062*3117ece4Schristos /* get dir name from pathname similar to 'dirname()' */ 1063*3117ece4Schristos assert(pathname != NULL); 1064*3117ece4Schristos 1065*3117ece4Schristos /* remove trailing '/' chars */ 1066*3117ece4Schristos len = strlen(pathname); 1067*3117ece4Schristos assert(len > 0); 1068*3117ece4Schristos while (pathname[len] == PATH_SEP) { 1069*3117ece4Schristos pathname[len] = '\0'; 1070*3117ece4Schristos len--; 1071*3117ece4Schristos } 1072*3117ece4Schristos if (len == 0) return; 1073*3117ece4Schristos 1074*3117ece4Schristos /* if input is a single file, return '.' instead. i.e. 1075*3117ece4Schristos * "xyz/abc/file.txt" => "xyz/abc" 1076*3117ece4Schristos "./file.txt" => "." 1077*3117ece4Schristos "file.txt" => "." 1078*3117ece4Schristos */ 1079*3117ece4Schristos pos = strrchr(pathname, PATH_SEP); 1080*3117ece4Schristos if (pos == NULL) { 1081*3117ece4Schristos pathname[0] = '.'; 1082*3117ece4Schristos pathname[1] = '\0'; 1083*3117ece4Schristos } else { 1084*3117ece4Schristos *pos = '\0'; 1085*3117ece4Schristos } 1086*3117ece4Schristos } 1087*3117ece4Schristos 1088*3117ece4Schristos /* pathname must be valid */ 1089*3117ece4Schristos static const char* trimLeadingRootChar(const char *pathname) 1090*3117ece4Schristos { 1091*3117ece4Schristos assert(pathname != NULL); 1092*3117ece4Schristos if (pathname[0] == PATH_SEP) 1093*3117ece4Schristos return pathname + 1; 1094*3117ece4Schristos return pathname; 1095*3117ece4Schristos } 1096*3117ece4Schristos 1097*3117ece4Schristos /* pathname must be valid */ 1098*3117ece4Schristos static const char* trimLeadingCurrentDirConst(const char *pathname) 1099*3117ece4Schristos { 1100*3117ece4Schristos assert(pathname != NULL); 1101*3117ece4Schristos if ((pathname[0] == '.') && (pathname[1] == PATH_SEP)) 1102*3117ece4Schristos return pathname + 2; 1103*3117ece4Schristos return pathname; 1104*3117ece4Schristos } 1105*3117ece4Schristos 1106*3117ece4Schristos static char* 1107*3117ece4Schristos trimLeadingCurrentDir(char *pathname) 1108*3117ece4Schristos { 1109*3117ece4Schristos /* 'union charunion' can do const-cast without compiler warning */ 1110*3117ece4Schristos union charunion { 1111*3117ece4Schristos char *chr; 1112*3117ece4Schristos const char* cchr; 1113*3117ece4Schristos } ptr; 1114*3117ece4Schristos ptr.cchr = trimLeadingCurrentDirConst(pathname); 1115*3117ece4Schristos return ptr.chr; 1116*3117ece4Schristos } 1117*3117ece4Schristos 1118*3117ece4Schristos /* remove leading './' or '/' chars here */ 1119*3117ece4Schristos static const char * trimPath(const char *pathname) 1120*3117ece4Schristos { 1121*3117ece4Schristos return trimLeadingRootChar( 1122*3117ece4Schristos trimLeadingCurrentDirConst(pathname)); 1123*3117ece4Schristos } 1124*3117ece4Schristos 1125*3117ece4Schristos static char* mallocAndJoin2Dir(const char *dir1, const char *dir2) 1126*3117ece4Schristos { 1127*3117ece4Schristos assert(dir1 != NULL && dir2 != NULL); 1128*3117ece4Schristos { const size_t dir1Size = strlen(dir1); 1129*3117ece4Schristos const size_t dir2Size = strlen(dir2); 1130*3117ece4Schristos char *outDirBuffer, *buffer; 1131*3117ece4Schristos 1132*3117ece4Schristos outDirBuffer = (char *) malloc(dir1Size + dir2Size + 2); 1133*3117ece4Schristos CONTROL(outDirBuffer != NULL); 1134*3117ece4Schristos 1135*3117ece4Schristos memcpy(outDirBuffer, dir1, dir1Size); 1136*3117ece4Schristos outDirBuffer[dir1Size] = '\0'; 1137*3117ece4Schristos 1138*3117ece4Schristos buffer = outDirBuffer + dir1Size; 1139*3117ece4Schristos if (dir1Size > 0 && *(buffer - 1) != PATH_SEP) { 1140*3117ece4Schristos *buffer = PATH_SEP; 1141*3117ece4Schristos buffer++; 1142*3117ece4Schristos } 1143*3117ece4Schristos memcpy(buffer, dir2, dir2Size); 1144*3117ece4Schristos buffer[dir2Size] = '\0'; 1145*3117ece4Schristos 1146*3117ece4Schristos return outDirBuffer; 1147*3117ece4Schristos } 1148*3117ece4Schristos } 1149*3117ece4Schristos 1150*3117ece4Schristos /* this function will return NULL if input srcFileName is not valid name for mirrored output path */ 1151*3117ece4Schristos char* UTIL_createMirroredDestDirName(const char* srcFileName, const char* outDirRootName) 1152*3117ece4Schristos { 1153*3117ece4Schristos char* pathname = NULL; 1154*3117ece4Schristos if (!isFileNameValidForMirroredOutput(srcFileName)) 1155*3117ece4Schristos return NULL; 1156*3117ece4Schristos 1157*3117ece4Schristos pathname = mallocAndJoin2Dir(outDirRootName, trimPath(srcFileName)); 1158*3117ece4Schristos 1159*3117ece4Schristos convertPathnameToDirName(pathname); 1160*3117ece4Schristos return pathname; 1161*3117ece4Schristos } 1162*3117ece4Schristos 1163*3117ece4Schristos static int 1164*3117ece4Schristos mirrorSrcDir(char* srcDirName, const char* outDirName) 1165*3117ece4Schristos { 1166*3117ece4Schristos mode_t srcMode; 1167*3117ece4Schristos int status = 0; 1168*3117ece4Schristos char* newDir = mallocAndJoin2Dir(outDirName, trimPath(srcDirName)); 1169*3117ece4Schristos if (!newDir) 1170*3117ece4Schristos return -ENOMEM; 1171*3117ece4Schristos 1172*3117ece4Schristos srcMode = getDirMode(srcDirName); 1173*3117ece4Schristos status = makeDir(newDir, srcMode); 1174*3117ece4Schristos free(newDir); 1175*3117ece4Schristos return status; 1176*3117ece4Schristos } 1177*3117ece4Schristos 1178*3117ece4Schristos static int 1179*3117ece4Schristos mirrorSrcDirRecursive(char* srcDirName, const char* outDirName) 1180*3117ece4Schristos { 1181*3117ece4Schristos int status = 0; 1182*3117ece4Schristos char* pp = trimLeadingCurrentDir(srcDirName); 1183*3117ece4Schristos char* sp = NULL; 1184*3117ece4Schristos 1185*3117ece4Schristos while ((sp = strchr(pp, PATH_SEP)) != NULL) { 1186*3117ece4Schristos if (sp != pp) { 1187*3117ece4Schristos *sp = '\0'; 1188*3117ece4Schristos status = mirrorSrcDir(srcDirName, outDirName); 1189*3117ece4Schristos if (status != 0) 1190*3117ece4Schristos return status; 1191*3117ece4Schristos *sp = PATH_SEP; 1192*3117ece4Schristos } 1193*3117ece4Schristos pp = sp + 1; 1194*3117ece4Schristos } 1195*3117ece4Schristos status = mirrorSrcDir(srcDirName, outDirName); 1196*3117ece4Schristos return status; 1197*3117ece4Schristos } 1198*3117ece4Schristos 1199*3117ece4Schristos static void 1200*3117ece4Schristos makeMirroredDestDirsWithSameSrcDirMode(char** srcDirNames, unsigned nbFile, const char* outDirName) 1201*3117ece4Schristos { 1202*3117ece4Schristos unsigned int i = 0; 1203*3117ece4Schristos for (i = 0; i < nbFile; i++) 1204*3117ece4Schristos mirrorSrcDirRecursive(srcDirNames[i], outDirName); 1205*3117ece4Schristos } 1206*3117ece4Schristos 1207*3117ece4Schristos static int 1208*3117ece4Schristos firstIsParentOrSameDirOfSecond(const char* firstDir, const char* secondDir) 1209*3117ece4Schristos { 1210*3117ece4Schristos size_t firstDirLen = strlen(firstDir), 1211*3117ece4Schristos secondDirLen = strlen(secondDir); 1212*3117ece4Schristos return firstDirLen <= secondDirLen && 1213*3117ece4Schristos (secondDir[firstDirLen] == PATH_SEP || secondDir[firstDirLen] == '\0') && 1214*3117ece4Schristos 0 == strncmp(firstDir, secondDir, firstDirLen); 1215*3117ece4Schristos } 1216*3117ece4Schristos 1217*3117ece4Schristos static int compareDir(const void* pathname1, const void* pathname2) { 1218*3117ece4Schristos /* sort it after remove the leading '/' or './'*/ 1219*3117ece4Schristos const char* s1 = trimPath(*(char * const *) pathname1); 1220*3117ece4Schristos const char* s2 = trimPath(*(char * const *) pathname2); 1221*3117ece4Schristos return strcmp(s1, s2); 1222*3117ece4Schristos } 1223*3117ece4Schristos 1224*3117ece4Schristos static void 1225*3117ece4Schristos makeUniqueMirroredDestDirs(char** srcDirNames, unsigned nbFile, const char* outDirName) 1226*3117ece4Schristos { 1227*3117ece4Schristos unsigned int i = 0, uniqueDirNr = 0; 1228*3117ece4Schristos char** uniqueDirNames = NULL; 1229*3117ece4Schristos 1230*3117ece4Schristos if (nbFile == 0) 1231*3117ece4Schristos return; 1232*3117ece4Schristos 1233*3117ece4Schristos uniqueDirNames = (char** ) malloc(nbFile * sizeof (char *)); 1234*3117ece4Schristos CONTROL(uniqueDirNames != NULL); 1235*3117ece4Schristos 1236*3117ece4Schristos /* if dirs is "a/b/c" and "a/b/c/d", we only need call: 1237*3117ece4Schristos * we just need "a/b/c/d" */ 1238*3117ece4Schristos qsort((void *)srcDirNames, nbFile, sizeof(char*), compareDir); 1239*3117ece4Schristos 1240*3117ece4Schristos uniqueDirNr = 1; 1241*3117ece4Schristos uniqueDirNames[uniqueDirNr - 1] = srcDirNames[0]; 1242*3117ece4Schristos for (i = 1; i < nbFile; i++) { 1243*3117ece4Schristos char* prevDirName = srcDirNames[i - 1]; 1244*3117ece4Schristos char* currDirName = srcDirNames[i]; 1245*3117ece4Schristos 1246*3117ece4Schristos /* note: we always compare trimmed path, i.e.: 1247*3117ece4Schristos * src dir of "./foo" and "/foo" will be both saved into: 1248*3117ece4Schristos * "outDirName/foo/" */ 1249*3117ece4Schristos if (!firstIsParentOrSameDirOfSecond(trimPath(prevDirName), 1250*3117ece4Schristos trimPath(currDirName))) 1251*3117ece4Schristos uniqueDirNr++; 1252*3117ece4Schristos 1253*3117ece4Schristos /* we need to maintain original src dir name instead of trimmed 1254*3117ece4Schristos * dir, so we can retrieve the original src dir's mode_t */ 1255*3117ece4Schristos uniqueDirNames[uniqueDirNr - 1] = currDirName; 1256*3117ece4Schristos } 1257*3117ece4Schristos 1258*3117ece4Schristos makeMirroredDestDirsWithSameSrcDirMode(uniqueDirNames, uniqueDirNr, outDirName); 1259*3117ece4Schristos 1260*3117ece4Schristos free(uniqueDirNames); 1261*3117ece4Schristos } 1262*3117ece4Schristos 1263*3117ece4Schristos static void 1264*3117ece4Schristos makeMirroredDestDirs(char** srcFileNames, unsigned nbFile, const char* outDirName) 1265*3117ece4Schristos { 1266*3117ece4Schristos unsigned int i = 0; 1267*3117ece4Schristos for (i = 0; i < nbFile; ++i) 1268*3117ece4Schristos convertPathnameToDirName(srcFileNames[i]); 1269*3117ece4Schristos makeUniqueMirroredDestDirs(srcFileNames, nbFile, outDirName); 1270*3117ece4Schristos } 1271*3117ece4Schristos 1272*3117ece4Schristos void UTIL_mirrorSourceFilesDirectories(const char** inFileNames, unsigned int nbFile, const char* outDirName) 1273*3117ece4Schristos { 1274*3117ece4Schristos unsigned int i = 0, validFilenamesNr = 0; 1275*3117ece4Schristos char** srcFileNames = (char **) malloc(nbFile * sizeof (char *)); 1276*3117ece4Schristos CONTROL(srcFileNames != NULL); 1277*3117ece4Schristos 1278*3117ece4Schristos /* check input filenames is valid */ 1279*3117ece4Schristos for (i = 0; i < nbFile; ++i) { 1280*3117ece4Schristos if (isFileNameValidForMirroredOutput(inFileNames[i])) { 1281*3117ece4Schristos char* fname = STRDUP(inFileNames[i]); 1282*3117ece4Schristos CONTROL(fname != NULL); 1283*3117ece4Schristos srcFileNames[validFilenamesNr++] = fname; 1284*3117ece4Schristos } 1285*3117ece4Schristos } 1286*3117ece4Schristos 1287*3117ece4Schristos if (validFilenamesNr > 0) { 1288*3117ece4Schristos makeDir(outDirName, DIR_DEFAULT_MODE); 1289*3117ece4Schristos makeMirroredDestDirs(srcFileNames, validFilenamesNr, outDirName); 1290*3117ece4Schristos } 1291*3117ece4Schristos 1292*3117ece4Schristos for (i = 0; i < validFilenamesNr; i++) 1293*3117ece4Schristos free(srcFileNames[i]); 1294*3117ece4Schristos free(srcFileNames); 1295*3117ece4Schristos } 1296*3117ece4Schristos 1297*3117ece4Schristos FileNamesTable* 1298*3117ece4Schristos UTIL_createExpandedFNT(const char* const* inputNames, size_t nbIfns, int followLinks) 1299*3117ece4Schristos { 1300*3117ece4Schristos unsigned nbFiles; 1301*3117ece4Schristos char* buf = (char*)malloc(LIST_SIZE_INCREASE); 1302*3117ece4Schristos char* bufend = buf + LIST_SIZE_INCREASE; 1303*3117ece4Schristos 1304*3117ece4Schristos if (!buf) return NULL; 1305*3117ece4Schristos 1306*3117ece4Schristos { size_t ifnNb, pos; 1307*3117ece4Schristos for (ifnNb=0, pos=0, nbFiles=0; ifnNb<nbIfns; ifnNb++) { 1308*3117ece4Schristos if (!UTIL_isDirectory(inputNames[ifnNb])) { 1309*3117ece4Schristos size_t const len = strlen(inputNames[ifnNb]); 1310*3117ece4Schristos if (buf + pos + len >= bufend) { 1311*3117ece4Schristos ptrdiff_t newListSize = (bufend - buf) + LIST_SIZE_INCREASE; 1312*3117ece4Schristos assert(newListSize >= 0); 1313*3117ece4Schristos buf = (char*)UTIL_realloc(buf, (size_t)newListSize); 1314*3117ece4Schristos if (!buf) return NULL; 1315*3117ece4Schristos bufend = buf + newListSize; 1316*3117ece4Schristos } 1317*3117ece4Schristos if (buf + pos + len < bufend) { 1318*3117ece4Schristos memcpy(buf+pos, inputNames[ifnNb], len+1); /* including final \0 */ 1319*3117ece4Schristos pos += len + 1; 1320*3117ece4Schristos nbFiles++; 1321*3117ece4Schristos } 1322*3117ece4Schristos } else { 1323*3117ece4Schristos nbFiles += (unsigned)UTIL_prepareFileList(inputNames[ifnNb], &buf, &pos, &bufend, followLinks); 1324*3117ece4Schristos if (buf == NULL) return NULL; 1325*3117ece4Schristos } } } 1326*3117ece4Schristos 1327*3117ece4Schristos /* note : even if nbFiles==0, function returns a valid, though empty, FileNamesTable* object */ 1328*3117ece4Schristos 1329*3117ece4Schristos { size_t ifnNb, pos; 1330*3117ece4Schristos size_t const fntCapacity = nbFiles + 1; /* minimum 1, allows adding one reference, typically stdin */ 1331*3117ece4Schristos const char** const fileNamesTable = (const char**)malloc(fntCapacity * sizeof(*fileNamesTable)); 1332*3117ece4Schristos if (!fileNamesTable) { free(buf); return NULL; } 1333*3117ece4Schristos 1334*3117ece4Schristos for (ifnNb = 0, pos = 0; ifnNb < nbFiles; ifnNb++) { 1335*3117ece4Schristos fileNamesTable[ifnNb] = buf + pos; 1336*3117ece4Schristos if (buf + pos > bufend) { free(buf); free((void*)fileNamesTable); return NULL; } 1337*3117ece4Schristos pos += strlen(fileNamesTable[ifnNb]) + 1; 1338*3117ece4Schristos } 1339*3117ece4Schristos return UTIL_assembleFileNamesTable2(fileNamesTable, nbFiles, fntCapacity, buf); 1340*3117ece4Schristos } 1341*3117ece4Schristos } 1342*3117ece4Schristos 1343*3117ece4Schristos 1344*3117ece4Schristos void UTIL_expandFNT(FileNamesTable** fnt, int followLinks) 1345*3117ece4Schristos { 1346*3117ece4Schristos FileNamesTable* const newFNT = UTIL_createExpandedFNT((*fnt)->fileNames, (*fnt)->tableSize, followLinks); 1347*3117ece4Schristos CONTROL(newFNT != NULL); 1348*3117ece4Schristos UTIL_freeFileNamesTable(*fnt); 1349*3117ece4Schristos *fnt = newFNT; 1350*3117ece4Schristos } 1351*3117ece4Schristos 1352*3117ece4Schristos FileNamesTable* UTIL_createFNT_fromROTable(const char** filenames, size_t nbFilenames) 1353*3117ece4Schristos { 1354*3117ece4Schristos size_t const sizeof_FNTable = nbFilenames * sizeof(*filenames); 1355*3117ece4Schristos const char** const newFNTable = (const char**)malloc(sizeof_FNTable); 1356*3117ece4Schristos if (newFNTable==NULL) return NULL; 1357*3117ece4Schristos memcpy((void*)newFNTable, filenames, sizeof_FNTable); /* void* : mitigate a Visual compiler bug or limitation */ 1358*3117ece4Schristos return UTIL_assembleFileNamesTable(newFNTable, nbFilenames, NULL); 1359*3117ece4Schristos } 1360*3117ece4Schristos 1361*3117ece4Schristos 1362*3117ece4Schristos /*-**************************************** 1363*3117ece4Schristos * count the number of cores 1364*3117ece4Schristos ******************************************/ 1365*3117ece4Schristos 1366*3117ece4Schristos #if defined(_WIN32) || defined(WIN32) 1367*3117ece4Schristos 1368*3117ece4Schristos #include <windows.h> 1369*3117ece4Schristos 1370*3117ece4Schristos typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); 1371*3117ece4Schristos 1372*3117ece4Schristos DWORD CountSetBits(ULONG_PTR bitMask) 1373*3117ece4Schristos { 1374*3117ece4Schristos DWORD LSHIFT = sizeof(ULONG_PTR)*8 - 1; 1375*3117ece4Schristos DWORD bitSetCount = 0; 1376*3117ece4Schristos ULONG_PTR bitTest = (ULONG_PTR)1 << LSHIFT; 1377*3117ece4Schristos DWORD i; 1378*3117ece4Schristos 1379*3117ece4Schristos for (i = 0; i <= LSHIFT; ++i) 1380*3117ece4Schristos { 1381*3117ece4Schristos bitSetCount += ((bitMask & bitTest)?1:0); 1382*3117ece4Schristos bitTest/=2; 1383*3117ece4Schristos } 1384*3117ece4Schristos 1385*3117ece4Schristos return bitSetCount; 1386*3117ece4Schristos } 1387*3117ece4Schristos 1388*3117ece4Schristos int UTIL_countCores(int logical) 1389*3117ece4Schristos { 1390*3117ece4Schristos static int numCores = 0; 1391*3117ece4Schristos if (numCores != 0) return numCores; 1392*3117ece4Schristos 1393*3117ece4Schristos { LPFN_GLPI glpi; 1394*3117ece4Schristos BOOL done = FALSE; 1395*3117ece4Schristos PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL; 1396*3117ece4Schristos PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL; 1397*3117ece4Schristos DWORD returnLength = 0; 1398*3117ece4Schristos size_t byteOffset = 0; 1399*3117ece4Schristos 1400*3117ece4Schristos #if defined(_MSC_VER) 1401*3117ece4Schristos /* Visual Studio does not like the following cast */ 1402*3117ece4Schristos # pragma warning( disable : 4054 ) /* conversion from function ptr to data ptr */ 1403*3117ece4Schristos # pragma warning( disable : 4055 ) /* conversion from data ptr to function ptr */ 1404*3117ece4Schristos #endif 1405*3117ece4Schristos glpi = (LPFN_GLPI)(void*)GetProcAddress(GetModuleHandle(TEXT("kernel32")), 1406*3117ece4Schristos "GetLogicalProcessorInformation"); 1407*3117ece4Schristos 1408*3117ece4Schristos if (glpi == NULL) { 1409*3117ece4Schristos goto failed; 1410*3117ece4Schristos } 1411*3117ece4Schristos 1412*3117ece4Schristos while(!done) { 1413*3117ece4Schristos DWORD rc = glpi(buffer, &returnLength); 1414*3117ece4Schristos if (FALSE == rc) { 1415*3117ece4Schristos if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 1416*3117ece4Schristos if (buffer) 1417*3117ece4Schristos free(buffer); 1418*3117ece4Schristos buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(returnLength); 1419*3117ece4Schristos 1420*3117ece4Schristos if (buffer == NULL) { 1421*3117ece4Schristos perror("zstd"); 1422*3117ece4Schristos exit(1); 1423*3117ece4Schristos } 1424*3117ece4Schristos } else { 1425*3117ece4Schristos /* some other error */ 1426*3117ece4Schristos goto failed; 1427*3117ece4Schristos } 1428*3117ece4Schristos } else { 1429*3117ece4Schristos done = TRUE; 1430*3117ece4Schristos } } 1431*3117ece4Schristos 1432*3117ece4Schristos ptr = buffer; 1433*3117ece4Schristos 1434*3117ece4Schristos while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) { 1435*3117ece4Schristos 1436*3117ece4Schristos if (ptr->Relationship == RelationProcessorCore) { 1437*3117ece4Schristos if (logical) 1438*3117ece4Schristos numCores += CountSetBits(ptr->ProcessorMask); 1439*3117ece4Schristos else 1440*3117ece4Schristos numCores++; 1441*3117ece4Schristos } 1442*3117ece4Schristos 1443*3117ece4Schristos ptr++; 1444*3117ece4Schristos byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); 1445*3117ece4Schristos } 1446*3117ece4Schristos 1447*3117ece4Schristos free(buffer); 1448*3117ece4Schristos 1449*3117ece4Schristos return numCores; 1450*3117ece4Schristos } 1451*3117ece4Schristos 1452*3117ece4Schristos failed: 1453*3117ece4Schristos /* try to fall back on GetSystemInfo */ 1454*3117ece4Schristos { SYSTEM_INFO sysinfo; 1455*3117ece4Schristos GetSystemInfo(&sysinfo); 1456*3117ece4Schristos numCores = sysinfo.dwNumberOfProcessors; 1457*3117ece4Schristos if (numCores == 0) numCores = 1; /* just in case */ 1458*3117ece4Schristos } 1459*3117ece4Schristos return numCores; 1460*3117ece4Schristos } 1461*3117ece4Schristos 1462*3117ece4Schristos #elif defined(__APPLE__) 1463*3117ece4Schristos 1464*3117ece4Schristos #include <sys/sysctl.h> 1465*3117ece4Schristos 1466*3117ece4Schristos /* Use apple-provided syscall 1467*3117ece4Schristos * see: man 3 sysctl */ 1468*3117ece4Schristos int UTIL_countCores(int logical) 1469*3117ece4Schristos { 1470*3117ece4Schristos static S32 numCores = 0; /* apple specifies int32_t */ 1471*3117ece4Schristos if (numCores != 0) return numCores; 1472*3117ece4Schristos 1473*3117ece4Schristos { size_t size = sizeof(S32); 1474*3117ece4Schristos int const ret = sysctlbyname(logical ? "hw.logicalcpu" : "hw.physicalcpu", &numCores, &size, NULL, 0); 1475*3117ece4Schristos if (ret != 0) { 1476*3117ece4Schristos if (errno == ENOENT) { 1477*3117ece4Schristos /* entry not present, fall back on 1 */ 1478*3117ece4Schristos numCores = 1; 1479*3117ece4Schristos } else { 1480*3117ece4Schristos perror("zstd: can't get number of cpus"); 1481*3117ece4Schristos exit(1); 1482*3117ece4Schristos } 1483*3117ece4Schristos } 1484*3117ece4Schristos 1485*3117ece4Schristos return numCores; 1486*3117ece4Schristos } 1487*3117ece4Schristos } 1488*3117ece4Schristos 1489*3117ece4Schristos #elif defined(__linux__) 1490*3117ece4Schristos 1491*3117ece4Schristos /* parse /proc/cpuinfo 1492*3117ece4Schristos * siblings / cpu cores should give hyperthreading ratio 1493*3117ece4Schristos * otherwise fall back on sysconf */ 1494*3117ece4Schristos int UTIL_countCores(int logical) 1495*3117ece4Schristos { 1496*3117ece4Schristos static int numCores = 0; 1497*3117ece4Schristos 1498*3117ece4Schristos if (numCores != 0) return numCores; 1499*3117ece4Schristos 1500*3117ece4Schristos numCores = (int)sysconf(_SC_NPROCESSORS_ONLN); 1501*3117ece4Schristos if (numCores == -1) { 1502*3117ece4Schristos /* value not queryable, fall back on 1 */ 1503*3117ece4Schristos return numCores = 1; 1504*3117ece4Schristos } 1505*3117ece4Schristos 1506*3117ece4Schristos /* try to determine if there's hyperthreading */ 1507*3117ece4Schristos { FILE* const cpuinfo = fopen("/proc/cpuinfo", "r"); 1508*3117ece4Schristos #define BUF_SIZE 80 1509*3117ece4Schristos char buff[BUF_SIZE]; 1510*3117ece4Schristos 1511*3117ece4Schristos int siblings = 0; 1512*3117ece4Schristos int cpu_cores = 0; 1513*3117ece4Schristos int ratio = 1; 1514*3117ece4Schristos 1515*3117ece4Schristos if (cpuinfo == NULL) { 1516*3117ece4Schristos /* fall back on the sysconf value */ 1517*3117ece4Schristos return numCores; 1518*3117ece4Schristos } 1519*3117ece4Schristos 1520*3117ece4Schristos /* assume the cpu cores/siblings values will be constant across all 1521*3117ece4Schristos * present processors */ 1522*3117ece4Schristos while (!feof(cpuinfo)) { 1523*3117ece4Schristos if (fgets(buff, BUF_SIZE, cpuinfo) != NULL) { 1524*3117ece4Schristos if (strncmp(buff, "siblings", 8) == 0) { 1525*3117ece4Schristos const char* const sep = strchr(buff, ':'); 1526*3117ece4Schristos if (sep == NULL || *sep == '\0') { 1527*3117ece4Schristos /* formatting was broken? */ 1528*3117ece4Schristos goto failed; 1529*3117ece4Schristos } 1530*3117ece4Schristos 1531*3117ece4Schristos siblings = atoi(sep + 1); 1532*3117ece4Schristos } 1533*3117ece4Schristos if (strncmp(buff, "cpu cores", 9) == 0) { 1534*3117ece4Schristos const char* const sep = strchr(buff, ':'); 1535*3117ece4Schristos if (sep == NULL || *sep == '\0') { 1536*3117ece4Schristos /* formatting was broken? */ 1537*3117ece4Schristos goto failed; 1538*3117ece4Schristos } 1539*3117ece4Schristos 1540*3117ece4Schristos cpu_cores = atoi(sep + 1); 1541*3117ece4Schristos } 1542*3117ece4Schristos } else if (ferror(cpuinfo)) { 1543*3117ece4Schristos /* fall back on the sysconf value */ 1544*3117ece4Schristos goto failed; 1545*3117ece4Schristos } } 1546*3117ece4Schristos if (siblings && cpu_cores && siblings > cpu_cores) { 1547*3117ece4Schristos ratio = siblings / cpu_cores; 1548*3117ece4Schristos } 1549*3117ece4Schristos 1550*3117ece4Schristos if (ratio && numCores > ratio && !logical) { 1551*3117ece4Schristos numCores = numCores / ratio; 1552*3117ece4Schristos } 1553*3117ece4Schristos 1554*3117ece4Schristos failed: 1555*3117ece4Schristos fclose(cpuinfo); 1556*3117ece4Schristos return numCores; 1557*3117ece4Schristos } 1558*3117ece4Schristos } 1559*3117ece4Schristos 1560*3117ece4Schristos #elif defined(__FreeBSD__) 1561*3117ece4Schristos 1562*3117ece4Schristos #include <sys/sysctl.h> 1563*3117ece4Schristos 1564*3117ece4Schristos /* Use physical core sysctl when available 1565*3117ece4Schristos * see: man 4 smp, man 3 sysctl */ 1566*3117ece4Schristos int UTIL_countCores(int logical) 1567*3117ece4Schristos { 1568*3117ece4Schristos static int numCores = 0; /* freebsd sysctl is native int sized */ 1569*3117ece4Schristos #if __FreeBSD_version >= 1300008 1570*3117ece4Schristos static int perCore = 1; 1571*3117ece4Schristos #endif 1572*3117ece4Schristos if (numCores != 0) return numCores; 1573*3117ece4Schristos 1574*3117ece4Schristos #if __FreeBSD_version >= 1300008 1575*3117ece4Schristos { size_t size = sizeof(numCores); 1576*3117ece4Schristos int ret = sysctlbyname("kern.smp.cores", &numCores, &size, NULL, 0); 1577*3117ece4Schristos if (ret == 0) { 1578*3117ece4Schristos if (logical) { 1579*3117ece4Schristos ret = sysctlbyname("kern.smp.threads_per_core", &perCore, &size, NULL, 0); 1580*3117ece4Schristos /* default to physical cores if logical cannot be read */ 1581*3117ece4Schristos if (ret == 0) 1582*3117ece4Schristos numCores *= perCore; 1583*3117ece4Schristos } 1584*3117ece4Schristos 1585*3117ece4Schristos return numCores; 1586*3117ece4Schristos } 1587*3117ece4Schristos if (errno != ENOENT) { 1588*3117ece4Schristos perror("zstd: can't get number of cpus"); 1589*3117ece4Schristos exit(1); 1590*3117ece4Schristos } 1591*3117ece4Schristos /* sysctl not present, fall through to older sysconf method */ 1592*3117ece4Schristos } 1593*3117ece4Schristos #else 1594*3117ece4Schristos /* suppress unused parameter warning */ 1595*3117ece4Schristos (void) logical; 1596*3117ece4Schristos #endif 1597*3117ece4Schristos 1598*3117ece4Schristos numCores = (int)sysconf(_SC_NPROCESSORS_ONLN); 1599*3117ece4Schristos if (numCores == -1) { 1600*3117ece4Schristos /* value not queryable, fall back on 1 */ 1601*3117ece4Schristos numCores = 1; 1602*3117ece4Schristos } 1603*3117ece4Schristos return numCores; 1604*3117ece4Schristos } 1605*3117ece4Schristos 1606*3117ece4Schristos #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__CYGWIN__) 1607*3117ece4Schristos 1608*3117ece4Schristos /* Use POSIX sysconf 1609*3117ece4Schristos * see: man 3 sysconf */ 1610*3117ece4Schristos int UTIL_countCores(int logical) 1611*3117ece4Schristos { 1612*3117ece4Schristos static int numCores = 0; 1613*3117ece4Schristos 1614*3117ece4Schristos /* suppress unused parameter warning */ 1615*3117ece4Schristos (void)logical; 1616*3117ece4Schristos 1617*3117ece4Schristos if (numCores != 0) return numCores; 1618*3117ece4Schristos 1619*3117ece4Schristos numCores = (int)sysconf(_SC_NPROCESSORS_ONLN); 1620*3117ece4Schristos if (numCores == -1) { 1621*3117ece4Schristos /* value not queryable, fall back on 1 */ 1622*3117ece4Schristos return numCores = 1; 1623*3117ece4Schristos } 1624*3117ece4Schristos return numCores; 1625*3117ece4Schristos } 1626*3117ece4Schristos 1627*3117ece4Schristos #else 1628*3117ece4Schristos 1629*3117ece4Schristos int UTIL_countCores(int logical) 1630*3117ece4Schristos { 1631*3117ece4Schristos /* suppress unused parameter warning */ 1632*3117ece4Schristos (void)logical; 1633*3117ece4Schristos 1634*3117ece4Schristos /* assume 1 */ 1635*3117ece4Schristos return 1; 1636*3117ece4Schristos } 1637*3117ece4Schristos 1638*3117ece4Schristos #endif 1639*3117ece4Schristos 1640*3117ece4Schristos int UTIL_countPhysicalCores(void) 1641*3117ece4Schristos { 1642*3117ece4Schristos return UTIL_countCores(0); 1643*3117ece4Schristos } 1644*3117ece4Schristos 1645*3117ece4Schristos int UTIL_countLogicalCores(void) 1646*3117ece4Schristos { 1647*3117ece4Schristos return UTIL_countCores(1); 1648*3117ece4Schristos } 1649*3117ece4Schristos 1650*3117ece4Schristos #if defined (__cplusplus) 1651*3117ece4Schristos } 1652*3117ece4Schristos #endif 1653