diff options
Diffstat (limited to 'programs/util.c')
| -rw-r--r-- | programs/util.c | 408 |
1 files changed, 326 insertions, 82 deletions
diff --git a/programs/util.c b/programs/util.c index d69b72a37ca6..065a35855f27 100644 --- a/programs/util.c +++ b/programs/util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Przemyslaw Skibinski, Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -8,11 +8,6 @@ * You may select, at your option, one of the above-listed licenses. */ -#if defined (__cplusplus) -extern "C" { -#endif - - /*-**************************************** * Dependencies ******************************************/ @@ -23,16 +18,27 @@ extern "C" { #include <errno.h> #include <assert.h> +#if defined(__FreeBSD__) +#include <sys/param.h> /* __FreeBSD_version */ +#endif /* #ifdef __FreeBSD__ */ + #if defined(_WIN32) # include <sys/utime.h> /* utime */ # include <io.h> /* _chmod */ +# define ZSTD_USE_UTIMENSAT 0 #else # include <unistd.h> /* chown, stat */ -# if PLATFORM_POSIX_VERSION < 200809L || !defined(st_mtime) -# include <utime.h> /* utime */ +# include <sys/stat.h> /* utimensat, st_mtime */ +# if (PLATFORM_POSIX_VERSION >= 200809L && defined(st_mtime)) \ + || (defined(__FreeBSD__) && __FreeBSD_version >= 1100056) +# define ZSTD_USE_UTIMENSAT 1 # else +# define ZSTD_USE_UTIMENSAT 0 +# endif +# if ZSTD_USE_UTIMENSAT # include <fcntl.h> /* AT_FDCWD */ -# include <sys/stat.h> /* utimensat */ +# else +# include <utime.h> /* utime */ # endif #endif @@ -66,6 +72,27 @@ extern "C" { #define UTIL_DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define UTIL_DISPLAYLEVEL(l, ...) { if (g_utilDisplayLevel>=l) { UTIL_DISPLAY(__VA_ARGS__); } } +static int g_traceDepth = 0; +int g_traceFileStat = 0; + +#define UTIL_TRACE_CALL(...) \ + { \ + if (g_traceFileStat) { \ + UTIL_DISPLAY("Trace:FileStat: %*s> ", g_traceDepth, ""); \ + UTIL_DISPLAY(__VA_ARGS__); \ + UTIL_DISPLAY("\n"); \ + ++g_traceDepth; \ + } \ + } + +#define UTIL_TRACE_RET(ret) \ + { \ + if (g_traceFileStat) { \ + --g_traceDepth; \ + UTIL_DISPLAY("Trace:FileStat: %*s< %d\n", g_traceDepth, "", (ret)); \ + } \ + } + /* A modified version of realloc(). * If UTIL_realloc() fails the original block is freed. */ @@ -81,6 +108,17 @@ UTIL_STATIC void* UTIL_realloc(void *ptr, size_t size) #define chmod _chmod #endif +#ifndef ZSTD_HAVE_FCHMOD +#if PLATFORM_POSIX_VERSION >= 199309L +#define ZSTD_HAVE_FCHMOD +#endif +#endif + +#ifndef ZSTD_HAVE_FCHOWN +#if PLATFORM_POSIX_VERSION >= 200809L +#define ZSTD_HAVE_FCHOWN +#endif +#endif /*-**************************************** * Console log @@ -100,7 +138,7 @@ int UTIL_requireUserConfirmation(const char* prompt, const char* abortMsg, ch = getchar(); result = 0; if (strchr(acceptableLetters, ch) == NULL) { - UTIL_DISPLAY("%s", abortMsg); + UTIL_DISPLAY("%s \n", abortMsg); result = 1; } /* flush the rest */ @@ -121,21 +159,51 @@ int UTIL_requireUserConfirmation(const char* prompt, const char* abortMsg, * Functions ***************************************/ -int UTIL_stat(const char* filename, stat_t* statbuf) +void UTIL_traceFileStat(void) { + g_traceFileStat = 1; +} + +int UTIL_fstat(const int fd, const char* filename, stat_t* statbuf) +{ + int ret; + UTIL_TRACE_CALL("UTIL_stat(%d, %s)", fd, filename); #if defined(_MSC_VER) - return !_stat64(filename, statbuf); + if (fd >= 0) { + ret = !_fstat64(fd, statbuf); + } else { + ret = !_stat64(filename, statbuf); + } #elif defined(__MINGW32__) && defined (__MSVCRT__) - return !_stati64(filename, statbuf); + if (fd >= 0) { + ret = !_fstati64(fd, statbuf); + } else { + ret = !_stati64(filename, statbuf); + } #else - return !stat(filename, statbuf); + if (fd >= 0) { + ret = !fstat(fd, statbuf); + } else { + ret = !stat(filename, statbuf); + } #endif + UTIL_TRACE_RET(ret); + return ret; +} + +int UTIL_stat(const char* filename, stat_t* statbuf) +{ + return UTIL_fstat(-1, filename, statbuf); } int UTIL_isRegularFile(const char* infilename) { stat_t statbuf; - return UTIL_stat(infilename, &statbuf) && UTIL_isRegularFileStat(&statbuf); + int ret; + UTIL_TRACE_CALL("UTIL_isRegularFile(%s)", infilename); + ret = UTIL_stat(infilename, &statbuf) && UTIL_isRegularFileStat(&statbuf); + UTIL_TRACE_RET(ret); + return ret; } int UTIL_isRegularFileStat(const stat_t* statbuf) @@ -150,72 +218,152 @@ int UTIL_isRegularFileStat(const stat_t* statbuf) /* like chmod, but avoid changing permission of /dev/null */ int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions) { + return UTIL_fchmod(-1, filename, statbuf, permissions); +} + +int UTIL_fchmod(const int fd, char const* filename, const stat_t* statbuf, mode_t permissions) +{ stat_t localStatBuf; + UTIL_TRACE_CALL("UTIL_chmod(%s, %#4o)", filename, (unsigned)permissions); if (statbuf == NULL) { - if (!UTIL_stat(filename, &localStatBuf)) return 0; + if (!UTIL_fstat(fd, filename, &localStatBuf)) { + UTIL_TRACE_RET(0); + return 0; + } statbuf = &localStatBuf; } - if (!UTIL_isRegularFileStat(statbuf)) return 0; /* pretend success, but don't change anything */ - return chmod(filename, permissions); + if (!UTIL_isRegularFileStat(statbuf)) { + UTIL_TRACE_RET(0); + return 0; /* pretend success, but don't change anything */ + } +#ifdef ZSTD_HAVE_FCHMOD + if (fd >= 0) { + int ret; + UTIL_TRACE_CALL("fchmod"); + ret = fchmod(fd, permissions); + UTIL_TRACE_RET(ret); + UTIL_TRACE_RET(ret); + return ret; + } else +#endif + { + int ret; + UTIL_TRACE_CALL("chmod"); + ret = chmod(filename, permissions); + UTIL_TRACE_RET(ret); + UTIL_TRACE_RET(ret); + return ret; + } } /* set access and modification times */ int UTIL_utime(const char* filename, const stat_t *statbuf) { int ret; + UTIL_TRACE_CALL("UTIL_utime(%s)", filename); /* We check that st_mtime is a macro here in order to give us confidence * that struct stat has a struct timespec st_mtim member. We need this * check because there are some platforms that claim to be POSIX 2008 * compliant but which do not have st_mtim... */ -#if (PLATFORM_POSIX_VERSION >= 200809L) && defined(st_mtime) - /* (atime, mtime) */ - struct timespec timebuf[2] = { {0, UTIME_NOW} }; - timebuf[1] = statbuf->st_mtim; - ret = utimensat(AT_FDCWD, filename, timebuf, 0); + /* FreeBSD has implemented POSIX 2008 for a long time but still only + * advertises support for POSIX 2001. They have a version macro that + * lets us safely gate them in. + * See https://docs.freebsd.org/en/books/porters-handbook/versions/. + */ +#if ZSTD_USE_UTIMENSAT + { + /* (atime, mtime) */ + struct timespec timebuf[2] = { {0, UTIME_NOW} }; + timebuf[1] = statbuf->st_mtim; + ret = utimensat(AT_FDCWD, filename, timebuf, 0); + } #else - struct utimbuf timebuf; - timebuf.actime = time(NULL); - timebuf.modtime = statbuf->st_mtime; - ret = utime(filename, &timebuf); + { + struct utimbuf timebuf; + timebuf.actime = time(NULL); + timebuf.modtime = statbuf->st_mtime; + ret = utime(filename, &timebuf); + } #endif errno = 0; + UTIL_TRACE_RET(ret); return ret; } int UTIL_setFileStat(const char *filename, const stat_t *statbuf) { - int res = 0; + return UTIL_setFDStat(-1, filename, statbuf); +} +int UTIL_setFDStat(const int fd, const char *filename, const stat_t *statbuf) +{ + int res = 0; stat_t curStatBuf; - if (!UTIL_stat(filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf)) + UTIL_TRACE_CALL("UTIL_setFileStat(%d, %s)", fd, filename); + + if (!UTIL_fstat(fd, filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf)) { + UTIL_TRACE_RET(-1); return -1; + } - /* set access and modification times */ - res += UTIL_utime(filename, statbuf); + /* Mimic gzip's behavior: + * + * "Change the group first, then the permissions, then the owner. + * That way, the permissions will be correct on systems that allow + * users to give away files, without introducing a security hole. + * Security depends on permissions not containing the setuid or + * setgid bits." */ #if !defined(_WIN32) - res += chown(filename, statbuf->st_uid, statbuf->st_gid); /* Copy ownership */ +#ifdef ZSTD_HAVE_FCHOWN + if (fd >= 0) { + res += fchown(fd, -1, statbuf->st_gid); /* Apply group ownership */ + } else +#endif + { + res += chown(filename, -1, statbuf->st_gid); /* Apply group ownership */ + } #endif - res += UTIL_chmod(filename, &curStatBuf, statbuf->st_mode & 07777); /* Copy file permissions */ + res += UTIL_fchmod(fd, filename, &curStatBuf, statbuf->st_mode & 0777); /* Copy file permissions */ + +#if !defined(_WIN32) +#ifdef ZSTD_HAVE_FCHOWN + if (fd >= 0) { + res += fchown(fd, statbuf->st_uid, -1); /* Apply user ownership */ + } else +#endif + { + res += chown(filename, statbuf->st_uid, -1); /* Apply user ownership */ + } +#endif errno = 0; + UTIL_TRACE_RET(-res); return -res; /* number of errors is returned */ } int UTIL_isDirectory(const char* infilename) { stat_t statbuf; - return UTIL_stat(infilename, &statbuf) && UTIL_isDirectoryStat(&statbuf); + int ret; + UTIL_TRACE_CALL("UTIL_isDirectory(%s)", infilename); + ret = UTIL_stat(infilename, &statbuf) && UTIL_isDirectoryStat(&statbuf); + UTIL_TRACE_RET(ret); + return ret; } int UTIL_isDirectoryStat(const stat_t* statbuf) { + int ret; + UTIL_TRACE_CALL("UTIL_isDirectoryStat()"); #if defined(_MSC_VER) - return (statbuf->st_mode & _S_IFDIR) != 0; + ret = (statbuf->st_mode & _S_IFDIR) != 0; #else - return S_ISDIR(statbuf->st_mode) != 0; + ret = S_ISDIR(statbuf->st_mode) != 0; #endif + UTIL_TRACE_RET(ret); + return ret; } int UTIL_compareStr(const void *p1, const void *p2) { @@ -224,33 +372,68 @@ int UTIL_compareStr(const void *p1, const void *p2) { int UTIL_isSameFile(const char* fName1, const char* fName2) { + int ret; assert(fName1 != NULL); assert(fName2 != NULL); + UTIL_TRACE_CALL("UTIL_isSameFile(%s, %s)", fName1, fName2); #if defined(_MSC_VER) || defined(_WIN32) /* note : Visual does not support file identification by inode. * inode does not work on Windows, even with a posix layer, like msys2. * The following work-around is limited to detecting exact name repetition only, * aka `filename` is considered different from `subdir/../filename` */ - return !strcmp(fName1, fName2); + ret = !strcmp(fName1, fName2); #else { stat_t file1Stat; stat_t file2Stat; - return UTIL_stat(fName1, &file1Stat) + ret = UTIL_stat(fName1, &file1Stat) && UTIL_stat(fName2, &file2Stat) - && (file1Stat.st_dev == file2Stat.st_dev) - && (file1Stat.st_ino == file2Stat.st_ino); + && UTIL_isSameFileStat(fName1, fName2, &file1Stat, &file2Stat); } #endif + UTIL_TRACE_RET(ret); + return ret; +} + +int UTIL_isSameFileStat( + const char* fName1, const char* fName2, + const stat_t* file1Stat, const stat_t* file2Stat) +{ + int ret; + assert(fName1 != NULL); assert(fName2 != NULL); + UTIL_TRACE_CALL("UTIL_isSameFileStat(%s, %s)", fName1, fName2); +#if defined(_MSC_VER) || defined(_WIN32) + /* note : Visual does not support file identification by inode. + * inode does not work on Windows, even with a posix layer, like msys2. + * The following work-around is limited to detecting exact name repetition only, + * aka `filename` is considered different from `subdir/../filename` */ + (void)file1Stat; + (void)file2Stat; + ret = !strcmp(fName1, fName2); +#else + { + ret = (file1Stat->st_dev == file2Stat->st_dev) + && (file1Stat->st_ino == file2Stat->st_ino); + } +#endif + UTIL_TRACE_RET(ret); + return ret; } /* UTIL_isFIFO : distinguish named pipes */ int UTIL_isFIFO(const char* infilename) { + UTIL_TRACE_CALL("UTIL_isFIFO(%s)", infilename); /* macro guards, as defined in : https://linux.die.net/man/2/lstat */ #if PLATFORM_POSIX_VERSION >= 200112L - stat_t statbuf; - if (UTIL_stat(infilename, &statbuf) && UTIL_isFIFOStat(&statbuf)) return 1; + { + stat_t statbuf; + if (UTIL_stat(infilename, &statbuf) && UTIL_isFIFOStat(&statbuf)) { + UTIL_TRACE_RET(1); + return 1; + } + } #endif (void)infilename; + UTIL_TRACE_RET(0); return 0; } @@ -278,21 +461,69 @@ int UTIL_isBlockDevStat(const stat_t* statbuf) int UTIL_isLink(const char* infilename) { + UTIL_TRACE_CALL("UTIL_isLink(%s)", infilename); /* macro guards, as defined in : https://linux.die.net/man/2/lstat */ #if PLATFORM_POSIX_VERSION >= 200112L - stat_t statbuf; - int const r = lstat(infilename, &statbuf); - if (!r && S_ISLNK(statbuf.st_mode)) return 1; + { + stat_t statbuf; + int const r = lstat(infilename, &statbuf); + if (!r && S_ISLNK(statbuf.st_mode)) { + UTIL_TRACE_RET(1); + return 1; + } + } #endif (void)infilename; + UTIL_TRACE_RET(0); return 0; } +static int g_fakeStdinIsConsole = 0; +static int g_fakeStderrIsConsole = 0; +static int g_fakeStdoutIsConsole = 0; + +int UTIL_isConsole(FILE* file) +{ + int ret; + UTIL_TRACE_CALL("UTIL_isConsole(%d)", fileno(file)); + if (file == stdin && g_fakeStdinIsConsole) + ret = 1; + else if (file == stderr && g_fakeStderrIsConsole) + ret = 1; + else if (file == stdout && g_fakeStdoutIsConsole) + ret = 1; + else + ret = IS_CONSOLE(file); + UTIL_TRACE_RET(ret); + return ret; +} + +void UTIL_fakeStdinIsConsole(void) +{ + g_fakeStdinIsConsole = 1; +} +void UTIL_fakeStdoutIsConsole(void) +{ + g_fakeStdoutIsConsole = 1; +} +void UTIL_fakeStderrIsConsole(void) +{ + g_fakeStderrIsConsole = 1; +} + U64 UTIL_getFileSize(const char* infilename) { stat_t statbuf; - if (!UTIL_stat(infilename, &statbuf)) return UTIL_FILESIZE_UNKNOWN; - return UTIL_getFileSizeStat(&statbuf); + UTIL_TRACE_CALL("UTIL_getFileSize(%s)", infilename); + if (!UTIL_stat(infilename, &statbuf)) { + UTIL_TRACE_RET(-1); + return UTIL_FILESIZE_UNKNOWN; + } + { + U64 const size = UTIL_getFileSizeStat(&statbuf); + UTIL_TRACE_RET((int)size); + return size; + } } U64 UTIL_getFileSizeStat(const stat_t* statbuf) @@ -369,11 +600,16 @@ U64 UTIL_getTotalFileSize(const char* const * fileNamesTable, unsigned nbFiles) { U64 total = 0; unsigned n; + UTIL_TRACE_CALL("UTIL_getTotalFileSize(%u)", nbFiles); for (n=0; n<nbFiles; n++) { U64 const size = UTIL_getFileSize(fileNamesTable[n]); - if (size == UTIL_FILESIZE_UNKNOWN) return UTIL_FILESIZE_UNKNOWN; + if (size == UTIL_FILESIZE_UNKNOWN) { + UTIL_TRACE_RET(-1); + return UTIL_FILESIZE_UNKNOWN; + } total += size; } + UTIL_TRACE_RET((int)total); return total; } @@ -418,7 +654,7 @@ readLinesFromFile(void* dst, size_t dstCapacity, while ( !feof(inputFile) ) { size_t const lineLength = readLineFromFile(buf+pos, dstCapacity-pos, inputFile); if (lineLength == 0) break; - assert(pos + lineLength < dstCapacity); + assert(pos + lineLength <= dstCapacity); /* '=' for inputFile not terminated with '\n' */ pos += lineLength; ++nbFiles; } @@ -435,7 +671,6 @@ UTIL_createFileNamesTable_fromFileName(const char* inputFileName) size_t nbFiles = 0; char* buf; size_t bufSize; - size_t pos = 0; stat_t statbuf; if (!UTIL_stat(inputFileName, &statbuf) || !UTIL_isRegularFileStat(&statbuf)) @@ -462,12 +697,13 @@ UTIL_createFileNamesTable_fromFileName(const char* inputFileName) { const char** filenamesTable = (const char**) malloc(nbFiles * sizeof(*filenamesTable)); CONTROL(filenamesTable != NULL); - { size_t fnb; - for (fnb = 0, pos = 0; fnb < nbFiles; fnb++) { + { size_t fnb, pos = 0; + for (fnb = 0; fnb < nbFiles; fnb++) { filenamesTable[fnb] = buf+pos; pos += strlen(buf+pos)+1; /* +1 for the finishing `\0` */ - } } + } assert(pos <= bufSize); + } return UTIL_assembleFileNamesTable(filenamesTable, nbFiles, buf); } @@ -509,6 +745,16 @@ FileNamesTable* UTIL_allocateFileNamesTable(size_t tableSize) return fnt; } +int UTIL_searchFileNamesTable(FileNamesTable* table, char const* name) { + size_t i; + for(i=0 ;i < table->tableSize; i++) { + if(!strcmp(table->fileNames[i], name)) { + return (int)i; + } + } + return -1; +} + void UTIL_refFilename(FileNamesTable* fnt, const char* filename) { assert(fnt->tableSize < fnt->tableCapacity); @@ -518,7 +764,7 @@ void UTIL_refFilename(FileNamesTable* fnt, const char* filename) static size_t getTotalTableSize(FileNamesTable* table) { - size_t fnb = 0, totalSize = 0; + size_t fnb, totalSize = 0; for(fnb = 0 ; fnb < table->tableSize && table->fileNames[fnb] ; ++fnb) { totalSize += strlen(table->fileNames[fnb]) + 1; /* +1 to add '\0' at the end of each fileName */ } @@ -559,7 +805,7 @@ UTIL_mergeFileNamesTable(FileNamesTable* table1, FileNamesTable* table2) for( idx2=0 ; (idx2 < table2->tableSize) && table2->fileNames[idx2] && (pos < newTotalTableSize) ; ++idx2, ++newTableIdx) { size_t const curLen = strlen(table2->fileNames[idx2]); memcpy(buf+pos, table2->fileNames[idx2], curLen); - assert(newTableIdx <= newTable->tableSize); + assert(newTableIdx < newTable->tableSize); newTable->fileNames[newTableIdx] = buf+pos; pos += curLen+1; } } @@ -683,8 +929,11 @@ static int UTIL_prepareFileList(const char *dirName, ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE; assert(newListSize >= 0); *bufStart = (char*)UTIL_realloc(*bufStart, (size_t)newListSize); - *bufEnd = *bufStart + newListSize; - if (*bufStart == NULL) { free(path); closedir(dir); return 0; } + if (*bufStart != NULL) { + *bufEnd = *bufStart + newListSize; + } else { + free(path); closedir(dir); return 0; + } } if (*bufStart + *pos + pathLength < *bufEnd) { memcpy(*bufStart + *pos, path, pathLength + 1); /* with final \0 */ @@ -870,30 +1119,27 @@ static const char * trimPath(const char *pathname) static char* mallocAndJoin2Dir(const char *dir1, const char *dir2) { - const size_t dir1Size = strlen(dir1); - const size_t dir2Size = strlen(dir2); - char *outDirBuffer, *buffer, trailingChar; - assert(dir1 != NULL && dir2 != NULL); - outDirBuffer = (char *) malloc(dir1Size + dir2Size + 2); - CONTROL(outDirBuffer != NULL); + { const size_t dir1Size = strlen(dir1); + const size_t dir2Size = strlen(dir2); + char *outDirBuffer, *buffer; - memcpy(outDirBuffer, dir1, dir1Size); - outDirBuffer[dir1Size] = '\0'; + outDirBuffer = (char *) malloc(dir1Size + dir2Size + 2); + CONTROL(outDirBuffer != NULL); - if (dir2[0] == '.') - return outDirBuffer; + memcpy(outDirBuffer, dir1, dir1Size); + outDirBuffer[dir1Size] = '\0'; - buffer = outDirBuffer + dir1Size; - trailingChar = *(buffer - 1); - if (trailingChar != PATH_SEP) { - *buffer = PATH_SEP; - buffer++; - } - memcpy(buffer, dir2, dir2Size); - buffer[dir2Size] = '\0'; + buffer = outDirBuffer + dir1Size; + if (dir1Size > 0 && *(buffer - 1) != PATH_SEP) { + *buffer = PATH_SEP; + buffer++; + } + memcpy(buffer, dir2, dir2Size); + buffer[dir2Size] = '\0'; - return outDirBuffer; + return outDirBuffer; + } } /* this function will return NULL if input srcFileName is not valid name for mirrored output path */ @@ -999,7 +1245,7 @@ makeUniqueMirroredDestDirs(char** srcDirNames, unsigned nbFile, const char* outD trimPath(currDirName))) uniqueDirNr++; - /* we need maintain original src dir name instead of trimmed + /* we need to maintain original src dir name instead of trimmed * dir, so we can retrieve the original src dir's mode_t */ uniqueDirNames[uniqueDirNr - 1] = currDirName; } @@ -1308,7 +1554,6 @@ failed: #elif defined(__FreeBSD__) -#include <sys/param.h> #include <sys/sysctl.h> /* Use physical core sysctl when available @@ -1378,6 +1623,9 @@ int UTIL_countCores(int logical) int UTIL_countCores(int logical) { + /* suppress unused parameter warning */ + (void)logical; + /* assume 1 */ return 1; } @@ -1393,7 +1641,3 @@ int UTIL_countLogicalCores(void) { return UTIL_countCores(1); } - -#if defined (__cplusplus) -} -#endif |
