diff options
| author | Yoichi Nakayama <yoichi.nakayama@gmail.com> | 2021-11-23 10:12:29 +0900 |
|---|---|---|
| committer | Yoichi Nakayama <yoichi.nakayama@gmail.com> | 2021-11-23 10:20:04 +0900 |
| commit | 7bb206a76dca70b7db236315d348c6c9fc325cab (patch) | |
| tree | e38bb2d225746de8343db08837840e84aad3ece0 /src | |
| parent | 540b02f3b97baf2bc1d62210a23d522b5d73e5eb (diff) | |
| parent | f9c4dc10d90732cfbe2271dd58b01dd8f4003d15 (diff) | |
| download | libgit2-7bb206a76dca70b7db236315d348c6c9fc325cab.tar.gz | |
Merge branch 'main' into better-compatiblity-for-at-time-notation
Conflicts:
src/revparse.c
Diffstat (limited to 'src')
81 files changed, 3214 insertions, 3096 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9812fc0e4..d6a103667 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,10 @@ add_library(git2internal OBJECT) set_target_properties(git2internal PROPERTIES C_STANDARD 90) +if(DEPRECATE_HARD) + add_definitions(-DGIT_DEPRECATE_HARD) +endif() + if(DEBUG_POOL) set(GIT_DEBUG_POOL 1) endif() @@ -34,10 +38,6 @@ set(LIBGIT2_INCLUDES "${CMAKE_CURRENT_BINARY_DIR}" "${libgit2_SOURCE_DIR}/src" "${libgit2_SOURCE_DIR}/include") -set(LIBGIT2_SYSTEM_INCLUDES "") -set(LIBGIT2_LIBS "") - -enable_warnings(missing-declarations) if(HAVE_FUTIMENS) set(GIT_USE_FUTIMENS 1) @@ -46,46 +46,37 @@ add_feature_info(futimens GIT_USE_FUTIMENS "futimens support") check_prototype_definition(qsort_r "void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *))" - "" "stdlib.h" HAVE_QSORT_R_BSD) -if(HAVE_QSORT_R_BSD) - target_compile_definitions(git2internal PRIVATE HAVE_QSORT_R_BSD) -endif() + "" "stdlib.h" GIT_QSORT_R_BSD) check_prototype_definition(qsort_r "void qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg)" - "" "stdlib.h" HAVE_QSORT_R_GNU) -if(HAVE_QSORT_R_GNU) - target_compile_definitions(git2internal PRIVATE HAVE_QSORT_R_GNU) -endif() + "" "stdlib.h" GIT_QSORT_R_GNU) -check_function_exists(qsort_s HAVE_QSORT_S) -if(HAVE_QSORT_S) - target_compile_definitions(git2internal PRIVATE HAVE_QSORT_S) -endif() +check_function_exists(qsort_s GIT_QSORT_S) # Find required dependencies if(WIN32) - list(APPEND LIBGIT2_LIBS ws2_32) + list(APPEND LIBGIT2_SYSTEM_LIBS ws2_32) elseif(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") - list(APPEND LIBGIT2_LIBS socket nsl) + list(APPEND LIBGIT2_SYSTEM_LIBS socket nsl) list(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl") elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku") - list(APPEND LIBGIT2_LIBS network) + list(APPEND LIBGIT2_SYSTEM_LIBS network) list(APPEND LIBGIT2_PC_LIBS "-lnetwork") endif() check_library_exists(rt clock_gettime "time.h" NEED_LIBRT) if(NEED_LIBRT) - list(APPEND LIBGIT2_LIBS rt) + list(APPEND LIBGIT2_SYSTEM_LIBS rt) list(APPEND LIBGIT2_PC_LIBS "-lrt") endif() -if(THREADSAFE) - list(APPEND LIBGIT2_LIBS ${CMAKE_THREAD_LIBS_INIT}) +if(USE_THREADS) + list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT}) list(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT}) endif() -add_feature_info(threadsafe THREADSAFE "threadsafe support") +add_feature_info(threadsafe USE_THREADS "threadsafe support") if(WIN32 AND EMBED_SSH_PATH) @@ -98,165 +89,37 @@ if(WIN32 AND EMBED_SSH_PATH) set(GIT_SSH 1) endif() -if(WIN32 AND WINHTTP) - set(GIT_WINHTTP 1) - - # Since MinGW does not come with headers or an import library for winhttp, - # we have to include a private header and generate our own import library - if(MINGW) - add_subdirectory("${libgit2_SOURCE_DIR}/deps/winhttp" "${libgit2_BINARY_DIR}/deps/winhttp") - list(APPEND LIBGIT2_LIBS winhttp) - list(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/winhttp") - else() - list(APPEND LIBGIT2_LIBS "winhttp") - list(APPEND LIBGIT2_PC_LIBS "-lwinhttp") - endif() - - list(APPEND LIBGIT2_LIBS "rpcrt4" "crypt32" "ole32") - list(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32") -endif() - include(SelectHTTPSBackend) include(SelectHashes) +include(SelectHTTPParser) +include(SelectRegex) +include(SelectSSH) +include(SelectWinHTTP) +include(SelectZlib) + + +if(USE_SHA1 STREQUAL "CollisionDetection") + file(GLOB SRC_SHA1 hash/sha1/collisiondetect.* hash/sha1/sha1dc/*) +elseif(USE_SHA1 STREQUAL "OpenSSL") + file(GLOB SRC_SHA1 hash/sha1/openssl.*) +elseif(USE_SHA1 STREQUAL "CommonCrypto") + file(GLOB SRC_SHA1 hash/sha1/common_crypto.*) +elseif(USE_SHA1 STREQUAL "mbedTLS") + file(GLOB SRC_SHA1 hash/sha1/mbedtls.*) +elseif(USE_SHA1 STREQUAL "Win32") + file(GLOB SRC_SHA1 hash/sha1/win32.*) +elseif(USE_SHA1 STREQUAL "Generic") + file(GLOB SRC_SHA1 hash/sha1/generic.*) +endif() +list(APPEND SRC_SHA1 "hash/sha1.h") target_sources(git2internal PRIVATE ${SRC_SHA1}) -# Specify regular expression implementation -find_package(PCRE) - -if(REGEX_BACKEND STREQUAL "") - check_symbol_exists(regcomp_l "regex.h;xlocale.h" HAVE_REGCOMP_L) - - if(HAVE_REGCOMP_L) - set(REGEX_BACKEND "regcomp_l") - elseif(PCRE_FOUND) - set(REGEX_BACKEND "pcre") - else() - set(REGEX_BACKEND "builtin") - endif() -endif() - -if(REGEX_BACKEND STREQUAL "regcomp_l") - add_feature_info(regex ON "using system regcomp_l") - set(GIT_REGEX_REGCOMP_L 1) -elseif(REGEX_BACKEND STREQUAL "pcre2") - find_package(PCRE2) - - if(NOT PCRE2_FOUND) - MESSAGE(FATAL_ERROR "PCRE2 support was requested but not found") - endif() - - add_feature_info(regex ON "using system PCRE2") - set(GIT_REGEX_PCRE2 1) - - list(APPEND LIBGIT2_SYSTEM_INCLUDES ${PCRE2_INCLUDE_DIRS}) - list(APPEND LIBGIT2_LIBS ${PCRE2_LIBRARIES}) - list(APPEND LIBGIT2_PC_REQUIRES "libpcre2-8") -elseif(REGEX_BACKEND STREQUAL "pcre") - add_feature_info(regex ON "using system PCRE") - set(GIT_REGEX_PCRE 1) - - list(APPEND LIBGIT2_SYSTEM_INCLUDES ${PCRE_INCLUDE_DIRS}) - list(APPEND LIBGIT2_LIBS ${PCRE_LIBRARIES}) - list(APPEND LIBGIT2_PC_REQUIRES "libpcre") -elseif(REGEX_BACKEND STREQUAL "regcomp") - add_feature_info(regex ON "using system regcomp") - set(GIT_REGEX_REGCOMP 1) -elseif(REGEX_BACKEND STREQUAL "builtin") - add_feature_info(regex ON "using bundled PCRE") - set(GIT_REGEX_BUILTIN 1) - - add_subdirectory("${libgit2_SOURCE_DIR}/deps/pcre" "${libgit2_BINARY_DIR}/deps/pcre") - list(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/pcre") - list(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:pcre>) -else() - message(FATAL_ERROR "The REGEX_BACKEND option provided is not supported") -endif() - -# Optional external dependency: http-parser -if(USE_HTTP_PARSER STREQUAL "system") - find_package(HTTP_Parser) - - if(HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2) - list(APPEND LIBGIT2_SYSTEM_INCLUDES ${HTTP_PARSER_INCLUDE_DIRS}) - list(APPEND LIBGIT2_LIBS ${HTTP_PARSER_LIBRARIES}) - list(APPEND LIBGIT2_PC_LIBS "-lhttp_parser") - add_feature_info(http-parser ON "http-parser support (system)") - else() - message(FATAL_ERROR "http-parser support was requested but not found") - endif() -else() - message(STATUS "http-parser version 2 was not found or disabled; using bundled 3rd-party sources.") - add_subdirectory("${libgit2_SOURCE_DIR}/deps/http-parser" "${libgit2_BINARY_DIR}/deps/http-parser") - list(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/http-parser") - list(APPEND LIBGIT2_OBJECTS "$<TARGET_OBJECTS:http-parser>") - add_feature_info(http-parser ON "http-parser support (bundled)") -endif() - -# Optional external dependency: zlib -SanitizeBool(USE_BUNDLED_ZLIB) -if(USE_BUNDLED_ZLIB STREQUAL ON) - set(USE_BUNDLED_ZLIB "Bundled") -endif() - -if(USE_BUNDLED_ZLIB STREQUAL "OFF") - find_package(ZLIB) - if(ZLIB_FOUND) - list(APPEND LIBGIT2_SYSTEM_INCLUDES ${ZLIB_INCLUDE_DIRS}) - list(APPEND LIBGIT2_LIBS ${ZLIB_LIBRARIES}) - if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - list(APPEND LIBGIT2_PC_LIBS "-lz") - else() - list(APPEND LIBGIT2_PC_REQUIRES "zlib") - endif() - add_feature_info(zlib ON "using system zlib") - else() - message(STATUS "zlib was not found; using bundled 3rd-party sources." ) - endif() -endif() -if(USE_BUNDLED_ZLIB STREQUAL "Chromium") - add_subdirectory("${libgit2_SOURCE_DIR}/deps/chromium-zlib" "${libgit2_BINARY_DIR}/deps/chromium-zlib") - list(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/chromium-zlib") - list(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:chromium_zlib>) - add_feature_info(zlib ON "using (Chromium) bundled zlib") -elseif(USE_BUNDLED_ZLIB OR NOT ZLIB_FOUND) - add_subdirectory("${libgit2_SOURCE_DIR}/deps/zlib" "${libgit2_BINARY_DIR}/deps/zlib") - list(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/zlib") - list(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:zlib>) - add_feature_info(zlib ON "using bundled zlib") -endif() - -# Optional external dependency: libssh2 -if(USE_SSH) - find_pkglibraries(LIBSSH2 libssh2) - if(NOT LIBSSH2_FOUND) - find_package(LibSSH2) - set(LIBSSH2_INCLUDE_DIRS ${LIBSSH2_INCLUDE_DIR}) - get_filename_component(LIBSSH2_LIBRARY_DIRS "${LIBSSH2_LIBRARY}" DIRECTORY) - set(LIBSSH2_LIBRARIES ${LIBSSH2_LIBRARY}) - set(LIBSSH2_LDFLAGS "-lssh2") - endif() -endif() -if(LIBSSH2_FOUND) - set(GIT_SSH 1) - list(APPEND LIBGIT2_SYSTEM_INCLUDES ${LIBSSH2_INCLUDE_DIRS}) - list(APPEND LIBGIT2_LIBS ${LIBSSH2_LIBRARIES}) - list(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS}) - - check_library_exists("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS) - if(HAVE_LIBSSH2_MEMORY_CREDENTIALS) - set(GIT_SSH_MEMORY_CREDENTIALS 1) - endif() -else() - message(STATUS "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.") -endif() -add_feature_info(SSH GIT_SSH "SSH transport support") - # Optional external dependency: ntlmclient if(USE_NTLMCLIENT) set(GIT_NTLM 1) add_subdirectory("${libgit2_SOURCE_DIR}/deps/ntlmclient" "${libgit2_BINARY_DIR}/deps/ntlmclient") - list(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/ntlmclient") - list(APPEND LIBGIT2_OBJECTS "$<TARGET_OBJECTS:ntlmclient>") + list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${libgit2_SOURCE_DIR}/deps/ntlmclient") + list(APPEND LIBGIT2_DEPENDENCY_OBJECTS "$<TARGET_OBJECTS:ntlmclient>") endif() add_feature_info(ntlmclient GIT_NTLM "NTLM authentication support for Unix") @@ -271,13 +134,13 @@ endif() if(ICONV_FOUND) set(GIT_USE_ICONV 1) list(APPEND LIBGIT2_SYSTEM_INCLUDES ${ICONV_INCLUDE_DIR}) - list(APPEND LIBGIT2_LIBS ${ICONV_LIBRARIES}) + list(APPEND LIBGIT2_SYSTEM_LIBS ${ICONV_LIBRARIES}) list(APPEND LIBGIT2_PC_LIBS ${ICONV_LIBRARIES}) endif() add_feature_info(iconv GIT_USE_ICONV "iconv encoding conversion support") -if(THREADSAFE) +if(USE_THREADS) if(NOT WIN32) find_package(Threads REQUIRED) endif() @@ -360,15 +223,17 @@ endif() configure_file(features.h.in git2/sys/features.h) ide_split_sources(git2internal) -list(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:git2internal>) +list(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:git2internal> ${LIBGIT2_DEPENDENCY_OBJECTS}) -target_include_directories(git2internal PRIVATE ${LIBGIT2_INCLUDES} PUBLIC ${libgit2_SOURCE_DIR}/include) +target_include_directories(git2internal PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${libgit2_SOURCE_DIR}/include) target_include_directories(git2internal SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES}) -set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE) set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE) +set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE) +set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE) +set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE) set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE) -set(LIBGIT2_LIBS ${LIBGIT2_LIBS} PARENT_SCOPE) +set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE) if(XCODE_VERSION) # This is required for Xcode to actually link the libgit2 library @@ -379,7 +244,7 @@ endif() # Compile and link libgit2 add_library(git2 ${WIN_RC} ${LIBGIT2_OBJECTS}) -target_link_libraries(git2 ${LIBGIT2_LIBS}) +target_link_libraries(git2 ${LIBGIT2_SYSTEM_LIBS}) set_target_properties(git2 PROPERTIES C_STANDARD 90) set_target_properties(git2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR}) diff --git a/src/annotated_commit.h b/src/annotated_commit.h index b390066b2..444a2ed10 100644 --- a/src/annotated_commit.h +++ b/src/annotated_commit.h @@ -15,7 +15,7 @@ typedef enum { GIT_ANNOTATED_COMMIT_REAL = 1, - GIT_ANNOTATED_COMMIT_VIRTUAL = 2, + GIT_ANNOTATED_COMMIT_VIRTUAL = 2 } git_annotated_commit_t; /** diff --git a/src/attr.c b/src/attr.c index 5849e701f..409c30b01 100644 --- a/src/attr.c +++ b/src/attr.c @@ -629,7 +629,7 @@ static int collect_attr_files( const char *workdir = git_repository_workdir(repo); attr_walk_up_info info = { NULL }; - GIT_ASSERT(!git_path_is_absolute(path)); + GIT_ASSERT(!git_fs_path_is_absolute(path)); if ((error = attr_setup(repo, attr_session, opts)) < 0) return error; @@ -637,10 +637,10 @@ static int collect_attr_files( /* Resolve path in a non-bare repo */ if (workdir != NULL) { if (!(error = git_repository_workdir_path(&dir, repo, path))) - error = git_path_find_dir(&dir); + error = git_fs_path_find_dir(&dir); } else { - error = git_path_dirname_r(&dir, path); + error = git_fs_path_dirname_r(&dir, path); } if (error < 0) @@ -670,7 +670,7 @@ static int collect_attr_files( if (!strcmp(dir.ptr, ".")) error = push_one_attr(&info, ""); else - error = git_path_walk_up(&dir, workdir, push_one_attr, &info); + error = git_fs_path_walk_up(&dir, workdir, push_one_attr, &info); if (error < 0) goto cleanup; diff --git a/src/attr_file.c b/src/attr_file.c index 09f0ce1b8..0eb881a9b 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -345,7 +345,7 @@ int git_attr_file__parse_buffer( int error = 0; /* If subdir file path, convert context for file paths */ - if (attrs->entry && git_path_root(attrs->entry->path) < 0 && + if (attrs->entry && git_fs_path_root(attrs->entry->path) < 0 && !git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE)) context = attrs->entry->path; @@ -560,7 +560,7 @@ int git_attr_path__init( /* build full path as best we can */ git_str_init(&info->full, 0); - if (git_path_join_unrooted(&info->full, path, base, &root) < 0) + if (git_fs_path_join_unrooted(&info->full, path, base, &root) < 0) return -1; info->path = info->full.ptr + root; @@ -596,7 +596,7 @@ int git_attr_path__init( case GIT_DIR_FLAG_UNKNOWN: default: - info->is_dir = (int)git_path_isdir(info->full.ptr); + info->is_dir = (int)git_fs_path_isdir(info->full.ptr); break; } diff --git a/src/attrcache.c b/src/attrcache.c index 98d73cbc3..b16d95c3c 100644 --- a/src/attrcache.c +++ b/src/attrcache.c @@ -12,6 +12,7 @@ #include "config.h" #include "sysdir.h" #include "ignore.h" +#include "path.h" GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache) { @@ -43,11 +44,12 @@ int git_attr_cache__alloc_file_entry( const char *path, git_pool *pool) { + git_str fullpath_str = GIT_STR_INIT; size_t baselen = 0, pathlen = strlen(path); size_t cachesize = sizeof(git_attr_file_entry) + pathlen + 1; git_attr_file_entry *ce; - if (base != NULL && git_path_root(path) < 0) { + if (base != NULL && git_fs_path_root(path) < 0) { baselen = strlen(base); cachesize += baselen; @@ -66,7 +68,10 @@ int git_attr_cache__alloc_file_entry( } memcpy(&ce->fullpath[baselen], path, pathlen); - if (git_path_validate_workdir_with_len(repo, ce->fullpath, pathlen + baselen) < 0) + fullpath_str.ptr = ce->fullpath; + fullpath_str.size = pathlen + baselen; + + if (git_path_validate_str_length(repo, &fullpath_str) < 0) return -1; ce->path = &ce->fullpath[baselen]; @@ -169,11 +174,11 @@ static int attr_cache_lookup( git_attr_file *file = NULL; /* join base and path as needed */ - if (source->base != NULL && git_path_root(source->filename) < 0) { + if (source->base != NULL && git_fs_path_root(source->filename) < 0) { git_str *p = attr_session ? &attr_session->tmp : &path; if (git_str_joinpath(p, source->base, source->filename) < 0 || - git_path_validate_workdir_buf(repo, p) < 0) + git_path_validate_str_length(repo, p) < 0) return -1; filename = p->ptr; diff --git a/src/blob.c b/src/blob.c index 6f57d09e4..65841ab03 100644 --- a/src/blob.c +++ b/src/blob.c @@ -205,7 +205,7 @@ int git_blob__create_from_paths( content_path = path.ptr; } - if ((error = git_path_lstat(content_path, &st)) < 0 || + if ((error = git_fs_path_lstat(content_path, &st)) < 0 || (error = git_repository_odb(&odb, repo)) < 0) goto done; @@ -280,7 +280,7 @@ int git_blob_create_from_disk( git_str full_path = GIT_STR_INIT; const char *workdir, *hintpath = NULL; - if ((error = git_path_prettify(&full_path, path, NULL)) < 0) { + if ((error = git_fs_path_prettify(&full_path, path, NULL)) < 0) { git_str_dispose(&full_path); return error; } diff --git a/src/checkout.c b/src/checkout.c index b31918fc8..11be2e84f 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -27,10 +27,11 @@ #include "diff_generate.h" #include "pathspec.h" #include "diff_xdiff.h" -#include "path.h" +#include "fs_path.h" #include "attr.h" #include "pool.h" #include "strmap.h" +#include "path.h" /* See docs/checkout-internals.md for more information */ @@ -44,7 +45,7 @@ enum { CHECKOUT_ACTION__UPDATE_CONFLICT = 32, CHECKOUT_ACTION__MAX = 32, CHECKOUT_ACTION__REMOVE_AND_UPDATE = - (CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE), + (CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE) }; typedef struct { @@ -328,7 +329,7 @@ static int checkout_target_fullpath( if (path && git_str_puts(&data->target_path, path) < 0) return -1; - if (git_path_validate_workdir_buf(data->repo, &data->target_path) < 0) + if (git_path_validate_str_length(data->repo, &data->target_path) < 0) return -1; *out = &data->target_path; @@ -347,7 +348,7 @@ static bool wd_item_is_removable( if (checkout_target_fullpath(&full, data, wd->path) < 0) return false; - return !full || !git_path_contains(full, DOT_GIT); + return !full || !git_fs_path_contains(full, DOT_GIT); } static int checkout_queue_remove(checkout_data *data, const char *path) @@ -481,7 +482,7 @@ static bool checkout_is_empty_dir(checkout_data *data, const char *path) if (checkout_target_fullpath(&fullpath, data, path) < 0) return false; - return git_path_is_empty_dir(fullpath->ptr); + return git_fs_path_is_empty_dir(fullpath->ptr); } static int checkout_action_with_wd( @@ -1201,12 +1202,12 @@ static int checkout_conflicts_mark_directoryfile( goto done; } - prefixed = git_path_equal_or_prefixed(path, entry->path, NULL); + prefixed = git_fs_path_equal_or_prefixed(path, entry->path, NULL); - if (prefixed == GIT_PATH_EQUAL) + if (prefixed == GIT_FS_PATH_EQUAL) continue; - if (prefixed == GIT_PATH_PREFIX) + if (prefixed == GIT_FS_PATH_PREFIX) conflict->directoryfile = 1; break; @@ -1280,14 +1281,14 @@ static int checkout_verify_paths( unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS; if (action & CHECKOUT_ACTION__REMOVE) { - if (!git_path_validate(repo, delta->old_file.path, delta->old_file.mode, flags)) { + if (!git_path_is_valid(repo, delta->old_file.path, delta->old_file.mode, flags)) { git_error_set(GIT_ERROR_CHECKOUT, "cannot remove invalid path '%s'", delta->old_file.path); return -1; } } if (action & ~CHECKOUT_ACTION__REMOVE) { - if (!git_path_validate(repo, delta->new_file.path, delta->new_file.mode, flags)) { + if (!git_path_is_valid(repo, delta->new_file.path, delta->new_file.mode, flags)) { git_error_set(GIT_ERROR_CHECKOUT, "cannot checkout to invalid path '%s'", delta->new_file.path); return -1; } @@ -1949,7 +1950,7 @@ static int checkout_path_suffixed(git_str *path, const char *suffix) path_len = git_str_len(path); - while (git_path_exists(git_str_cstr(path)) && i < INT_MAX) { + while (git_fs_path_exists(git_str_cstr(path)) && i < INT_MAX) { git_str_truncate(path, path_len); if ((error = git_str_putc(path, '_')) < 0 || @@ -2034,7 +2035,7 @@ static int checkout_merge_path( int error = 0; if ((error = git_str_joinpath(out, data->opts.target_directory, result->path)) < 0 || - (error = git_path_validate_workdir_buf(data->repo, out)) < 0) + (error = git_path_validate_str_length(data->repo, out)) < 0) return error; /* Most conflicts simply use the filename in the index */ @@ -2337,10 +2338,10 @@ static int validate_target_directory(checkout_data *data) { int error; - if ((error = git_path_validate_workdir(data->repo, data->opts.target_directory)) < 0) + if ((error = git_path_validate_length(data->repo, data->opts.target_directory)) < 0) return error; - if (git_path_isdir(data->opts.target_directory)) + if (git_fs_path_isdir(data->opts.target_directory)) return 0; error = checkout_mkdir(data, data->opts.target_directory, NULL, @@ -2507,7 +2508,7 @@ static int checkout_data_init( (error = git_vector_init(&data->remove_conflicts, 0, NULL)) < 0 || (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 || (error = git_str_puts(&data->target_path, data->opts.target_directory)) < 0 || - (error = git_path_to_dir(&data->target_path)) < 0 || + (error = git_fs_path_to_dir(&data->target_path)) < 0 || (error = git_strmap_new(&data->mkdir_map)) < 0) goto cleanup; diff --git a/src/clone.c b/src/clone.c index cf4cc3c7f..1843875f8 100644 --- a/src/clone.c +++ b/src/clone.c @@ -19,7 +19,7 @@ #include "remote.h" #include "futils.h" #include "refs.h" -#include "path.h" +#include "fs_path.h" #include "repository.h" #include "odb.h" @@ -333,7 +333,7 @@ static int create_and_configure_origin( void *payload = options->remote_cb_payload; /* If the path exists and is a dir, the url should be the absolute path */ - if (git_path_root(url) < 0 && git_path_exists(url) && git_path_isdir(url)) { + if (git_fs_path_root(url) < 0 && git_fs_path_exists(url) && git_fs_path_isdir(url)) { if (p_realpath(url, buf) == NULL) return -1; @@ -433,8 +433,8 @@ int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t loc if (local == GIT_CLONE_NO_LOCAL) return 0; - if ((is_url = git_path_is_local_file_url(url_or_path)) != 0) { - if (git_path_fromurl(&fromurl, url_or_path) < 0) { + if ((is_url = git_fs_path_is_local_file_url(url_or_path)) != 0) { + if (git_fs_path_fromurl(&fromurl, url_or_path) < 0) { is_local = -1; goto done; } @@ -443,7 +443,7 @@ int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t loc } is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) && - git_path_isdir(path); + git_fs_path_isdir(path); done: git_str_dispose(&fromurl); @@ -474,14 +474,14 @@ static int git__clone( GIT_ERROR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options"); /* Only clone to a new directory or an empty directory */ - if (git_path_exists(local_path) && !use_existing && !git_path_is_empty_dir(local_path)) { + if (git_fs_path_exists(local_path) && !use_existing && !git_fs_path_is_empty_dir(local_path)) { git_error_set(GIT_ERROR_INVALID, "'%s' exists and is not an empty directory", local_path); return GIT_EEXISTS; } /* Only remove the root directory on failure if we create it */ - if (git_path_exists(local_path)) + if (git_fs_path_exists(local_path)) rmdir_flags |= GIT_RMDIR_SKIP_ROOT; if (options.repository_cb) @@ -602,7 +602,7 @@ static int clone_local_into(git_repository *repo, git_remote *remote, const git_ * repo, if it's not rooted, the path should be relative to * the repository's worktree/gitdir. */ - if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0) + if ((error = git_fs_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0) return error; /* Copy .git/objects/ from the source to the target */ diff --git a/src/commit.h b/src/commit.h index 9378eab2d..7a2454e61 100644 --- a/src/commit.h +++ b/src/commit.h @@ -61,7 +61,7 @@ int git_commit__parse(void *commit, git_odb_object *obj); int git_commit__parse_raw(void *commit, const char *data, size_t size); typedef enum { - GIT_COMMIT_PARSE_QUICK = (1 << 0), /**< Only parse parents and committer info */ + GIT_COMMIT_PARSE_QUICK = (1 << 0) /**< Only parse parents and committer info */ } git_commit__parse_flags; int git_commit__parse_ext(git_commit *commit, git_odb_object *odb_obj, unsigned int flags); diff --git a/src/commit_graph.c b/src/commit_graph.c index f9a4bd2b2..924a3992b 100644 --- a/src/commit_graph.c +++ b/src/commit_graph.c @@ -754,7 +754,7 @@ enum generation_number_commit_state { GENERATION_NUMBER_COMMIT_STATE_UNVISITED = 0, GENERATION_NUMBER_COMMIT_STATE_ADDED = 1, GENERATION_NUMBER_COMMIT_STATE_EXPANDED = 2, - GENERATION_NUMBER_COMMIT_STATE_VISITED = 3, + GENERATION_NUMBER_COMMIT_STATE_VISITED = 3 }; static int compute_generation_numbers(git_vector *commits) diff --git a/src/config.c b/src/config.c index 9033a92c5..88da34c5e 100644 --- a/src/config.c +++ b/src/config.c @@ -1177,7 +1177,7 @@ int git_config__find_programdata(git_str *path) if (ret != GIT_OK) return ret; - return git_path_validate_system_file_ownership(path->ptr); + return git_fs_path_validate_system_file_ownership(path->ptr); } int git_config__global_location(git_str *buf) diff --git a/src/config_file.c b/src/config_file.c index 9c3d2ceb8..11b444094 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -108,7 +108,7 @@ static int config_file_open(git_config_backend *cfg, git_config_level_t level, c if ((res = git_config_entries_new(&b->entries)) < 0) return res; - if (!git_path_exists(b->file.path)) + if (!git_fs_path_exists(b->file.path)) return 0; /* @@ -529,7 +529,7 @@ static int included_path(git_str *out, const char *dir, const char *path) if (path[0] == '~' && path[1] == '/') return git_sysdir_expand_global_file(out, &path[1]); - return git_path_join_unrooted(out, path, dir, NULL); + return git_fs_path_join_unrooted(out, path, dir, NULL); } /* Escape the values to write them to the file */ @@ -574,7 +574,7 @@ static int parse_include(config_file_parse_data *parse_data, const char *file) if (!file) return 0; - if ((result = git_path_dirname_r(&path, parse_data->file->path)) < 0) + if ((result = git_fs_path_dirname_r(&path, parse_data->file->path)) < 0) return result; dir = git_str_detach(&path); @@ -611,17 +611,17 @@ static int do_match_gitdir( git_str pattern = GIT_STR_INIT, gitdir = GIT_STR_INIT; int error; - if (condition[0] == '.' && git_path_is_dirsep(condition[1])) { - git_path_dirname_r(&pattern, cfg_file); + if (condition[0] == '.' && git_fs_path_is_dirsep(condition[1])) { + git_fs_path_dirname_r(&pattern, cfg_file); git_str_joinpath(&pattern, pattern.ptr, condition + 2); - } else if (condition[0] == '~' && git_path_is_dirsep(condition[1])) + } else if (condition[0] == '~' && git_fs_path_is_dirsep(condition[1])) git_sysdir_expand_global_file(&pattern, condition + 1); - else if (!git_path_is_absolute(condition)) + else if (!git_fs_path_is_absolute(condition)) git_str_joinpath(&pattern, "**", condition); else git_str_sets(&pattern, condition); - if (git_path_is_dirsep(condition[strlen(condition) - 1])) + if (git_fs_path_is_dirsep(condition[strlen(condition) - 1])) git_str_puts(&pattern, "**"); if (git_str_oom(&pattern)) { @@ -632,7 +632,7 @@ static int do_match_gitdir( if ((error = git_repository__item_path(&gitdir, repo, GIT_REPOSITORY_ITEM_GITDIR)) < 0) goto out; - if (git_path_is_dirsep(gitdir.ptr[gitdir.size - 1])) + if (git_fs_path_is_dirsep(gitdir.ptr[gitdir.size - 1])) git_str_truncate(&gitdir, gitdir.size - 1); *matches = wildmatch(pattern.ptr, gitdir.ptr, @@ -699,7 +699,7 @@ static int conditional_match_onbranch( */ if ((error = git_str_sets(&buf, condition)) < 0) goto out; - if (git_path_is_dirsep(condition[strlen(condition) - 1]) && + if (git_fs_path_is_dirsep(condition[strlen(condition) - 1]) && (error = git_str_puts(&buf, "**")) < 0) goto out; @@ -861,7 +861,7 @@ static int config_file_read( int error; if (p_stat(file->path, &st) < 0) { - error = git_path_set_error(errno, file->path, "stat"); + error = git_fs_path_set_error(errno, file->path, "stat"); goto out; } diff --git a/src/crlf.c b/src/crlf.c index 7895ddec2..1e1f1e845 100644 --- a/src/crlf.c +++ b/src/crlf.c @@ -26,7 +26,7 @@ typedef enum { GIT_CRLF_TEXT_CRLF, GIT_CRLF_AUTO, GIT_CRLF_AUTO_INPUT, - GIT_CRLF_AUTO_CRLF, + GIT_CRLF_AUTO_CRLF } git_crlf_t; struct crlf_attrs { diff --git a/src/date.c b/src/date.c index af6694e30..7b52fa728 100644 --- a/src/date.c +++ b/src/date.c @@ -13,6 +13,7 @@ #include "util.h" #include "cache.h" #include "posix.h" +#include "date.h" #include <ctype.h> #include <time.h> @@ -857,7 +858,7 @@ static git_time_t approxidate_str(const char *date, return update_tm(&tm, &now, 0); } -int git__date_parse(git_time_t *out, const char *date) +int git_date_parse(git_time_t *out, const char *date) { time_t time_sec; git_time_t timestamp; @@ -875,31 +876,24 @@ int git__date_parse(git_time_t *out, const char *date) return error_ret; } -int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date) +int git_date_rfc2822_fmt(git_str *out, git_time_t time, int offset) { - int written; - struct tm gmt; time_t t; + struct tm gmt; GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(date); - t = (time_t) (date->time + date->offset * 60); + t = (time_t) (time + offset * 60); - if (p_gmtime_r (&t, &gmt) == NULL) + if (p_gmtime_r(&t, &gmt) == NULL) return -1; - written = p_snprintf(out, len, "%.3s, %u %.3s %.4u %02u:%02u:%02u %+03d%02d", + return git_str_printf(out, "%.3s, %u %.3s %.4u %02u:%02u:%02u %+03d%02d", weekday_names[gmt.tm_wday], gmt.tm_mday, month_names[gmt.tm_mon], gmt.tm_year + 1900, gmt.tm_hour, gmt.tm_min, gmt.tm_sec, - date->offset / 60, date->offset % 60); - - if (written < 0 || (written > (int) len - 1)) - return -1; - - return 0; + offset / 60, offset % 60); } diff --git a/src/date.h b/src/date.h new file mode 100644 index 000000000..7ebd3c30e --- /dev/null +++ b/src/date.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_date_h__ +#define INCLUDE_date_h__ + +#include "util.h" +#include "str.h" + +/* + * Parse a string into a value as a git_time_t. + * + * Sample valid input: + * - "yesterday" + * - "July 17, 2003" + * - "2003-7-17 08:23" + */ +extern int git_date_parse(git_time_t *out, const char *date); + +/* + * Format a git_time as a RFC2822 string + * + * @param out buffer to store formatted date + * @param time the time to be formatted + * @param offset the timezone offset + * @return 0 if successful; -1 on error + */ +extern int git_date_rfc2822_fmt(git_str *out, git_time_t time, int offset); + +#endif diff --git a/src/diff.h b/src/diff.h index 4b0339809..2cc35e65b 100644 --- a/src/diff.h +++ b/src/diff.h @@ -26,7 +26,7 @@ typedef enum { GIT_DIFF_TYPE_UNKNOWN = 0, GIT_DIFF_TYPE_GENERATED = 1, - GIT_DIFF_TYPE_PARSED = 2, + GIT_DIFF_TYPE_PARSED = 2 } git_diff_origin_t; struct git_diff { diff --git a/src/diff_driver.c b/src/diff_driver.c index f6b51d8ba..a20723fcc 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -21,7 +21,7 @@ typedef enum { DIFF_DRIVER_AUTO = 0, DIFF_DRIVER_BINARY = 1, DIFF_DRIVER_TEXT = 2, - DIFF_DRIVER_PATTERNLIST = 3, + DIFF_DRIVER_PATTERNLIST = 3 } git_diff_driver_t; typedef struct { diff --git a/src/diff_generate.c b/src/diff_generate.c index dc690aa9b..dca16d51a 100644 --- a/src/diff_generate.c +++ b/src/diff_generate.c @@ -605,7 +605,7 @@ int git_diff__oid_for_entry( diff->base.perf.stat_calls++; if (p_stat(full_path.ptr, &st) < 0) { - error = git_path_set_error(errno, entry.path, "stat"); + error = git_fs_path_set_error(errno, entry.path, "stat"); git_str_dispose(&full_path); return error; } @@ -1026,7 +1026,7 @@ static int handle_unmatched_new_item( git_str *full = NULL; if (git_iterator_current_workdir_path(&full, info->new_iter) < 0) return -1; - if (full && git_path_contains(full, DOT_GIT)) { + if (full && git_fs_path_contains(full, DOT_GIT)) { /* TODO: warning if not a valid git repository */ recurse_into_dir = false; } diff --git a/src/diff_generate.h b/src/diff_generate.h index 5186d552c..f39bf6b2a 100644 --- a/src/diff_generate.h +++ b/src/diff_generate.h @@ -18,7 +18,7 @@ enum { GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */ GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */ GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */ - GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */ + GIT_DIFFCAPS_USE_DEV = (1 << 4) /* use st_dev? */ }; #define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY) @@ -36,7 +36,7 @@ enum { GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */ GIT_DIFF_FLAG__IS_RENAME_TARGET = (1 << 18), GIT_DIFF_FLAG__IS_RENAME_SOURCE = (1 << 19), - GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20), + GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20) }; #define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF) diff --git a/src/diff_stats.c b/src/diff_stats.c index 228f6f892..259939844 100644 --- a/src/diff_stats.c +++ b/src/diff_stats.c @@ -70,7 +70,7 @@ static int diff_file_stats_full_to_buf( padding = stats->max_name - strlen(old_path) - strlen(new_path); - if ((common_dirlen = git_path_common_dirlen(old_path, new_path)) && + if ((common_dirlen = git_fs_path_common_dirlen(old_path, new_path)) && common_dirlen <= INT_MAX) { error = git_str_printf(out, " %.*s{%s"DIFF_RENAME_FILE_SEPARATOR"%s}", (int) common_dirlen, old_path, diff --git a/src/diff_tform.c b/src/diff_tform.c index be55de6c3..d14134071 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -13,7 +13,7 @@ #include "diff.h" #include "diff_generate.h" -#include "path.h" +#include "fs_path.h" #include "futils.h" #include "config.h" @@ -481,7 +481,7 @@ static int similarity_sig( return error; /* if path is not a regular file, just skip this item */ - if (!git_path_isfile(info->data.ptr)) + if (!git_fs_path_isfile(info->data.ptr)) return 0; /* TODO: apply wd-to-odb filters to file data if necessary */ diff --git a/src/email.c b/src/email.c index 3459c0540..e19a2928c 100644 --- a/src/email.c +++ b/src/email.c @@ -12,6 +12,7 @@ #include "diff_generate.h" #include "diff_stats.h" #include "patch.h" +#include "date.h" #include "git2/email.h" #include "git2/patch.h" @@ -72,6 +73,19 @@ static int append_prefix( return git_str_oom(out) ? -1 : 0; } +static int append_date( + git_str *out, + const git_time *date) +{ + int error; + + if ((error = git_str_printf(out, "Date: ")) == 0 && + (error = git_date_rfc2822_fmt(out, date->time, date->offset)) == 0) + error = git_str_putc(out, '\n'); + + return error; +} + static int append_subject( git_str *out, size_t patch_idx, @@ -117,14 +131,12 @@ static int append_header( git_email_create_options *opts) { char id[GIT_OID_HEXSZ]; - char date[GIT_DATE_RFC2822_SZ]; int error; if ((error = git_oid_fmt(id, commit_id)) < 0 || (error = git_str_printf(out, "From %.*s %s\n", GIT_OID_HEXSZ, id, EMAIL_TIMESTAMP)) < 0 || (error = git_str_printf(out, "From: %s <%s>\n", author->name, author->email)) < 0 || - (error = git__date_rfc2822_fmt(date, sizeof(date), &author->when)) < 0 || - (error = git_str_printf(out, "Date: %s\n", date)) < 0 || + (error = append_date(out, &author->when)) < 0 || (error = append_subject(out, patch_idx, patch_count, summary, opts)) < 0) return error; diff --git a/src/features.h.in b/src/features.h.in index a40b6085e..81a8ae023 100644 --- a/src/features.h.in +++ b/src/features.h.in @@ -24,6 +24,10 @@ #cmakedefine GIT_REGEX_PCRE2 #cmakedefine GIT_REGEX_BUILTIN 1 +#cmakedefine GIT_QSORT_R_BSD +#cmakedefine GIT_QSORT_R_GNU +#cmakedefine GIT_QSORT_S + #cmakedefine GIT_SSH 1 #cmakedefine GIT_SSH_MEMORY_CREDENTIALS 1 diff --git a/src/filebuf.c b/src/filebuf.c index 10f8c5813..f0bd0004f 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -43,7 +43,7 @@ static int verify_last_error(git_filebuf *file) static int lock_file(git_filebuf *file, int flags, mode_t mode) { - if (git_path_exists(file->path_lock) == true) { + if (git_fs_path_exists(file->path_lock) == true) { git_error_clear(); /* actual OS error code just confuses */ git_error_set(GIT_ERROR_OS, "failed to lock file '%s' for writing", file->path_lock); @@ -63,7 +63,7 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode) file->fd_is_open = true; - if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) { + if ((flags & GIT_FILEBUF_APPEND) && git_fs_path_exists(file->path_original) == true) { git_file source; char buffer[FILEIO_BUFSIZE]; ssize_t read_bytes; @@ -103,7 +103,7 @@ void git_filebuf_cleanup(git_filebuf *file) if (file->fd_is_open && file->fd >= 0) p_close(file->fd); - if (file->created_lock && !file->did_rename && file->path_lock && git_path_exists(file->path_lock)) + if (file->created_lock && !file->did_rename && file->path_lock && git_fs_path_exists(file->path_lock)) p_unlink(file->path_lock); if (file->compute_digest) { @@ -241,20 +241,20 @@ static int resolve_symlink(git_str *out, const char *path) target.ptr[ret] = '\0'; target.size = ret; - root = git_path_root(target.ptr); + root = git_fs_path_root(target.ptr); if (root >= 0) { if ((error = git_str_sets(&curpath, target.ptr)) < 0) goto cleanup; } else { git_str dir = GIT_STR_INIT; - if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0) + if ((error = git_fs_path_dirname_r(&dir, curpath.ptr)) < 0) goto cleanup; git_str_swap(&curpath, &dir); git_str_dispose(&dir); - if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0) + if ((error = git_fs_path_apply_relative(&curpath, target.ptr)) < 0) goto cleanup; } } @@ -366,7 +366,7 @@ int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mo memcpy(file->path_lock, file->path_original, path_len); memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH); - if (git_path_isdir(file->path_original)) { + if (git_fs_path_isdir(file->path_original)) { git_error_set(GIT_ERROR_FILESYSTEM, "path '%s' is a directory", file->path_original); error = GIT_EDIRECTORY; goto cleanup; diff --git a/src/filter.c b/src/filter.c index 417d9cb8b..2712e8c60 100644 --- a/src/filter.c +++ b/src/filter.c @@ -18,6 +18,7 @@ #include "blob.h" #include "attr_file.h" #include "array.h" +#include "path.h" struct git_filter_source { git_repository *repo; @@ -1094,8 +1095,8 @@ int git_filter_list_stream_file( if ((error = stream_list_init( &stream_start, &filter_streams, filters, target)) < 0 || - (error = git_path_join_unrooted(&abspath, path, base, NULL)) < 0 || - (error = git_path_validate_workdir_buf(repo, &abspath)) < 0) + (error = git_fs_path_join_unrooted(&abspath, path, base, NULL)) < 0 || + (error = git_path_validate_str_length(repo, &abspath)) < 0) goto done; initialized = 1; diff --git a/src/fs_path.c b/src/fs_path.c new file mode 100644 index 000000000..957f389cc --- /dev/null +++ b/src/fs_path.c @@ -0,0 +1,1853 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "fs_path.h" + +#include "posix.h" +#include "repository.h" +#ifdef GIT_WIN32 +#include "win32/posix.h" +#include "win32/w32_buffer.h" +#include "win32/w32_util.h" +#include "win32/version.h" +#include <aclapi.h> +#else +#include <dirent.h> +#endif +#include <stdio.h> +#include <ctype.h> + +static int dos_drive_prefix_length(const char *path) +{ + int i; + + /* + * Does it start with an ASCII letter (i.e. highest bit not set), + * followed by a colon? + */ + if (!(0x80 & (unsigned char)*path)) + return *path && path[1] == ':' ? 2 : 0; + + /* + * While drive letters must be letters of the English alphabet, it is + * possible to assign virtually _any_ Unicode character via `subst` as + * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff + * like this: + * + * subst ֍: %USERPROFILE%\Desktop + */ + for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++) + ; /* skip first UTF-8 character */ + return path[i] == ':' ? i + 1 : 0; +} + +#ifdef GIT_WIN32 +static bool looks_like_network_computer_name(const char *path, int pos) +{ + if (pos < 3) + return false; + + if (path[0] != '/' || path[1] != '/') + return false; + + while (pos-- > 2) { + if (path[pos] == '/') + return false; + } + + return true; +} +#endif + +/* + * Based on the Android implementation, BSD licensed. + * http://android.git.kernel.org/ + * + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +int git_fs_path_basename_r(git_str *buffer, const char *path) +{ + const char *endp, *startp; + int len, result; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + startp = "."; + len = 1; + goto Exit; + } + + /* Strip trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + /* All slashes becomes "/" */ + if (endp == path && *endp == '/') { + startp = "/"; + len = 1; + goto Exit; + } + + /* Find the start of the base */ + startp = endp; + while (startp > path && *(startp - 1) != '/') + startp--; + + /* Cast is safe because max path < max int */ + len = (int)(endp - startp + 1); + +Exit: + result = len; + + if (buffer != NULL && git_str_set(buffer, startp, len) < 0) + return -1; + + return result; +} + +/* + * Determine if the path is a Windows prefix and, if so, returns + * its actual lentgh. If it is not a prefix, returns -1. + */ +static int win32_prefix_length(const char *path, int len) +{ +#ifndef GIT_WIN32 + GIT_UNUSED(path); + GIT_UNUSED(len); +#else + /* + * Mimic unix behavior where '/.git' returns '/': 'C:/.git' + * will return 'C:/' here + */ + if (dos_drive_prefix_length(path) == len) + return len; + + /* + * Similarly checks if we're dealing with a network computer name + * '//computername/.git' will return '//computername/' + */ + if (looks_like_network_computer_name(path, len)) + return len; +#endif + + return -1; +} + +/* + * Based on the Android implementation, BSD licensed. + * Check http://android.git.kernel.org/ + */ +int git_fs_path_dirname_r(git_str *buffer, const char *path) +{ + const char *endp; + int is_prefix = 0, len; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + path = "."; + len = 1; + goto Exit; + } + + /* Strip trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + if (endp - path + 1 > INT_MAX) { + git_error_set(GIT_ERROR_INVALID, "path too long"); + len = -1; + goto Exit; + } + + if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) { + is_prefix = 1; + goto Exit; + } + + /* Find the start of the dir */ + while (endp > path && *endp != '/') + endp--; + + /* Either the dir is "/" or there are no slashes */ + if (endp == path) { + path = (*endp == '/') ? "/" : "."; + len = 1; + goto Exit; + } + + do { + endp--; + } while (endp > path && *endp == '/'); + + if (endp - path + 1 > INT_MAX) { + git_error_set(GIT_ERROR_INVALID, "path too long"); + len = -1; + goto Exit; + } + + if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) { + is_prefix = 1; + goto Exit; + } + + /* Cast is safe because max path < max int */ + len = (int)(endp - path + 1); + +Exit: + if (buffer) { + if (git_str_set(buffer, path, len) < 0) + return -1; + if (is_prefix && git_str_putc(buffer, '/') < 0) + return -1; + } + + return len; +} + + +char *git_fs_path_dirname(const char *path) +{ + git_str buf = GIT_STR_INIT; + char *dirname; + + git_fs_path_dirname_r(&buf, path); + dirname = git_str_detach(&buf); + git_str_dispose(&buf); /* avoid memleak if error occurs */ + + return dirname; +} + +char *git_fs_path_basename(const char *path) +{ + git_str buf = GIT_STR_INIT; + char *basename; + + git_fs_path_basename_r(&buf, path); + basename = git_str_detach(&buf); + git_str_dispose(&buf); /* avoid memleak if error occurs */ + + return basename; +} + +size_t git_fs_path_basename_offset(git_str *buffer) +{ + ssize_t slash; + + if (!buffer || buffer->size <= 0) + return 0; + + slash = git_str_rfind_next(buffer, '/'); + + if (slash >= 0 && buffer->ptr[slash] == '/') + return (size_t)(slash + 1); + + return 0; +} + +int git_fs_path_root(const char *path) +{ + int offset = 0, prefix_len; + + /* Does the root of the path look like a windows drive ? */ + if ((prefix_len = dos_drive_prefix_length(path))) + offset += prefix_len; + +#ifdef GIT_WIN32 + /* Are we dealing with a windows network path? */ + else if ((path[0] == '/' && path[1] == '/' && path[2] != '/') || + (path[0] == '\\' && path[1] == '\\' && path[2] != '\\')) + { + offset += 2; + + /* Skip the computer name segment */ + while (path[offset] && path[offset] != '/' && path[offset] != '\\') + offset++; + } + + if (path[offset] == '\\') + return offset; +#endif + + if (path[offset] == '/') + return offset; + + return -1; /* Not a real error - signals that path is not rooted */ +} + +static void path_trim_slashes(git_str *path) +{ + int ceiling = git_fs_path_root(path->ptr) + 1; + + if (ceiling < 0) + return; + + while (path->size > (size_t)ceiling) { + if (path->ptr[path->size-1] != '/') + break; + + path->ptr[path->size-1] = '\0'; + path->size--; + } +} + +int git_fs_path_join_unrooted( + git_str *path_out, const char *path, const char *base, ssize_t *root_at) +{ + ssize_t root; + + GIT_ASSERT_ARG(path_out); + GIT_ASSERT_ARG(path); + + root = (ssize_t)git_fs_path_root(path); + + if (base != NULL && root < 0) { + if (git_str_joinpath(path_out, base, path) < 0) + return -1; + + root = (ssize_t)strlen(base); + } else { + if (git_str_sets(path_out, path) < 0) + return -1; + + if (root < 0) + root = 0; + else if (base) + git_fs_path_equal_or_prefixed(base, path, &root); + } + + if (root_at) + *root_at = root; + + return 0; +} + +void git_fs_path_squash_slashes(git_str *path) +{ + char *p, *q; + + if (path->size == 0) + return; + + for (p = path->ptr, q = path->ptr; *q; p++, q++) { + *p = *q; + + while (*q == '/' && *(q+1) == '/') { + path->size--; + q++; + } + } + + *p = '\0'; +} + +int git_fs_path_prettify(git_str *path_out, const char *path, const char *base) +{ + char buf[GIT_PATH_MAX]; + + GIT_ASSERT_ARG(path_out); + GIT_ASSERT_ARG(path); + + /* construct path if needed */ + if (base != NULL && git_fs_path_root(path) < 0) { + if (git_str_joinpath(path_out, base, path) < 0) + return -1; + path = path_out->ptr; + } + + if (p_realpath(path, buf) == NULL) { + /* git_error_set resets the errno when dealing with a GIT_ERROR_OS kind of error */ + int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1; + git_error_set(GIT_ERROR_OS, "failed to resolve path '%s'", path); + + git_str_clear(path_out); + + return error; + } + + return git_str_sets(path_out, buf); +} + +int git_fs_path_prettify_dir(git_str *path_out, const char *path, const char *base) +{ + int error = git_fs_path_prettify(path_out, path, base); + return (error < 0) ? error : git_fs_path_to_dir(path_out); +} + +int git_fs_path_to_dir(git_str *path) +{ + if (path->asize > 0 && + git_str_len(path) > 0 && + path->ptr[git_str_len(path) - 1] != '/') + git_str_putc(path, '/'); + + return git_str_oom(path) ? -1 : 0; +} + +void git_fs_path_string_to_dir(char *path, size_t size) +{ + size_t end = strlen(path); + + if (end && path[end - 1] != '/' && end < size) { + path[end] = '/'; + path[end + 1] = '\0'; + } +} + +int git__percent_decode(git_str *decoded_out, const char *input) +{ + int len, hi, lo, i; + + GIT_ASSERT_ARG(decoded_out); + GIT_ASSERT_ARG(input); + + len = (int)strlen(input); + git_str_clear(decoded_out); + + for(i = 0; i < len; i++) + { + char c = input[i]; + + if (c != '%') + goto append; + + if (i >= len - 2) + goto append; + + hi = git__fromhex(input[i + 1]); + lo = git__fromhex(input[i + 2]); + + if (hi < 0 || lo < 0) + goto append; + + c = (char)(hi << 4 | lo); + i += 2; + +append: + if (git_str_putc(decoded_out, c) < 0) + return -1; + } + + return 0; +} + +static int error_invalid_local_file_uri(const char *uri) +{ + git_error_set(GIT_ERROR_CONFIG, "'%s' is not a valid local file URI", uri); + return -1; +} + +static int local_file_url_prefixlen(const char *file_url) +{ + int len = -1; + + if (git__prefixcmp(file_url, "file://") == 0) { + if (file_url[7] == '/') + len = 8; + else if (git__prefixcmp(file_url + 7, "localhost/") == 0) + len = 17; + } + + return len; +} + +bool git_fs_path_is_local_file_url(const char *file_url) +{ + return (local_file_url_prefixlen(file_url) > 0); +} + +int git_fs_path_fromurl(git_str *local_path_out, const char *file_url) +{ + int offset; + + GIT_ASSERT_ARG(local_path_out); + GIT_ASSERT_ARG(file_url); + + if ((offset = local_file_url_prefixlen(file_url)) < 0 || + file_url[offset] == '\0' || file_url[offset] == '/') + return error_invalid_local_file_uri(file_url); + +#ifndef GIT_WIN32 + offset--; /* A *nix absolute path starts with a forward slash */ +#endif + + git_str_clear(local_path_out); + return git__percent_decode(local_path_out, file_url + offset); +} + +int git_fs_path_walk_up( + git_str *path, + const char *ceiling, + int (*cb)(void *data, const char *), + void *data) +{ + int error = 0; + git_str iter; + ssize_t stop = 0, scan; + char oldc = '\0'; + + GIT_ASSERT_ARG(path); + GIT_ASSERT_ARG(cb); + + if (ceiling != NULL) { + if (git__prefixcmp(path->ptr, ceiling) == 0) + stop = (ssize_t)strlen(ceiling); + else + stop = git_str_len(path); + } + scan = git_str_len(path); + + /* empty path: yield only once */ + if (!scan) { + error = cb(data, ""); + if (error) + git_error_set_after_callback(error); + return error; + } + + iter.ptr = path->ptr; + iter.size = git_str_len(path); + iter.asize = path->asize; + + while (scan >= stop) { + error = cb(data, iter.ptr); + iter.ptr[scan] = oldc; + + if (error) { + git_error_set_after_callback(error); + break; + } + + scan = git_str_rfind_next(&iter, '/'); + if (scan >= 0) { + scan++; + oldc = iter.ptr[scan]; + iter.size = scan; + iter.ptr[scan] = '\0'; + } + } + + if (scan >= 0) + iter.ptr[scan] = oldc; + + /* relative path: yield for the last component */ + if (!error && stop == 0 && iter.ptr[0] != '/') { + error = cb(data, ""); + if (error) + git_error_set_after_callback(error); + } + + return error; +} + +bool git_fs_path_exists(const char *path) +{ + GIT_ASSERT_ARG_WITH_RETVAL(path, false); + return p_access(path, F_OK) == 0; +} + +bool git_fs_path_isdir(const char *path) +{ + struct stat st; + if (p_stat(path, &st) < 0) + return false; + + return S_ISDIR(st.st_mode) != 0; +} + +bool git_fs_path_isfile(const char *path) +{ + struct stat st; + + GIT_ASSERT_ARG_WITH_RETVAL(path, false); + if (p_stat(path, &st) < 0) + return false; + + return S_ISREG(st.st_mode) != 0; +} + +bool git_fs_path_islink(const char *path) +{ + struct stat st; + + GIT_ASSERT_ARG_WITH_RETVAL(path, false); + if (p_lstat(path, &st) < 0) + return false; + + return S_ISLNK(st.st_mode) != 0; +} + +#ifdef GIT_WIN32 + +bool git_fs_path_is_empty_dir(const char *path) +{ + git_win32_path filter_w; + bool empty = false; + + if (git_win32__findfirstfile_filter(filter_w, path)) { + WIN32_FIND_DATAW findData; + HANDLE hFind = FindFirstFileW(filter_w, &findData); + + /* FindFirstFile will fail if there are no children to the given + * path, which can happen if the given path is a file (and obviously + * has no children) or if the given path is an empty mount point. + * (Most directories have at least directory entries '.' and '..', + * but ridiculously another volume mounted in another drive letter's + * path space do not, and thus have nothing to enumerate.) If + * FindFirstFile fails, check if this is a directory-like thing + * (a mount point). + */ + if (hFind == INVALID_HANDLE_VALUE) + return git_fs_path_isdir(path); + + /* If the find handle was created successfully, then it's a directory */ + empty = true; + + do { + /* Allow the enumeration to return . and .. and still be considered + * empty. In the special case of drive roots (i.e. C:\) where . and + * .. do not occur, we can still consider the path to be an empty + * directory if there's nothing there. */ + if (!git_fs_path_is_dot_or_dotdotW(findData.cFileName)) { + empty = false; + break; + } + } while (FindNextFileW(hFind, &findData)); + + FindClose(hFind); + } + + return empty; +} + +#else + +static int path_found_entry(void *payload, git_str *path) +{ + GIT_UNUSED(payload); + return !git_fs_path_is_dot_or_dotdot(path->ptr); +} + +bool git_fs_path_is_empty_dir(const char *path) +{ + int error; + git_str dir = GIT_STR_INIT; + + if (!git_fs_path_isdir(path)) + return false; + + if ((error = git_str_sets(&dir, path)) != 0) + git_error_clear(); + else + error = git_fs_path_direach(&dir, 0, path_found_entry, NULL); + + git_str_dispose(&dir); + + return !error; +} + +#endif + +int git_fs_path_set_error(int errno_value, const char *path, const char *action) +{ + switch (errno_value) { + case ENOENT: + case ENOTDIR: + git_error_set(GIT_ERROR_OS, "could not find '%s' to %s", path, action); + return GIT_ENOTFOUND; + + case EINVAL: + case ENAMETOOLONG: + git_error_set(GIT_ERROR_OS, "invalid path for filesystem '%s'", path); + return GIT_EINVALIDSPEC; + + case EEXIST: + git_error_set(GIT_ERROR_OS, "failed %s - '%s' already exists", action, path); + return GIT_EEXISTS; + + case EACCES: + git_error_set(GIT_ERROR_OS, "failed %s - '%s' is locked", action, path); + return GIT_ELOCKED; + + default: + git_error_set(GIT_ERROR_OS, "could not %s '%s'", action, path); + return -1; + } +} + +int git_fs_path_lstat(const char *path, struct stat *st) +{ + if (p_lstat(path, st) == 0) + return 0; + + return git_fs_path_set_error(errno, path, "stat"); +} + +static bool _check_dir_contents( + git_str *dir, + const char *sub, + bool (*predicate)(const char *)) +{ + bool result; + size_t dir_size = git_str_len(dir); + size_t sub_size = strlen(sub); + size_t alloc_size; + + /* leave base valid even if we could not make space for subdir */ + if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) || + GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) || + git_str_try_grow(dir, alloc_size, false) < 0) + return false; + + /* save excursion */ + if (git_str_joinpath(dir, dir->ptr, sub) < 0) + return false; + + result = predicate(dir->ptr); + + /* restore path */ + git_str_truncate(dir, dir_size); + return result; +} + +bool git_fs_path_contains(git_str *dir, const char *item) +{ + return _check_dir_contents(dir, item, &git_fs_path_exists); +} + +bool git_fs_path_contains_dir(git_str *base, const char *subdir) +{ + return _check_dir_contents(base, subdir, &git_fs_path_isdir); +} + +bool git_fs_path_contains_file(git_str *base, const char *file) +{ + return _check_dir_contents(base, file, &git_fs_path_isfile); +} + +int git_fs_path_find_dir(git_str *dir) +{ + int error = 0; + char buf[GIT_PATH_MAX]; + + if (p_realpath(dir->ptr, buf) != NULL) + error = git_str_sets(dir, buf); + + /* call dirname if this is not a directory */ + if (!error) /* && git_fs_path_isdir(dir->ptr) == false) */ + error = (git_fs_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0; + + if (!error) + error = git_fs_path_to_dir(dir); + + return error; +} + +int git_fs_path_resolve_relative(git_str *path, size_t ceiling) +{ + char *base, *to, *from, *next; + size_t len; + + GIT_ERROR_CHECK_ALLOC_STR(path); + + if (ceiling > path->size) + ceiling = path->size; + + /* recognize drive prefixes, etc. that should not be backed over */ + if (ceiling == 0) + ceiling = git_fs_path_root(path->ptr) + 1; + + /* recognize URL prefixes that should not be backed over */ + if (ceiling == 0) { + for (next = path->ptr; *next && git__isalpha(*next); ++next); + if (next[0] == ':' && next[1] == '/' && next[2] == '/') + ceiling = (next + 3) - path->ptr; + } + + base = to = from = path->ptr + ceiling; + + while (*from) { + for (next = from; *next && *next != '/'; ++next); + + len = next - from; + + if (len == 1 && from[0] == '.') + /* do nothing with singleton dot */; + + else if (len == 2 && from[0] == '.' && from[1] == '.') { + /* error out if trying to up one from a hard base */ + if (to == base && ceiling != 0) { + git_error_set(GIT_ERROR_INVALID, + "cannot strip root component off url"); + return -1; + } + + /* no more path segments to strip, + * use '../' as a new base path */ + if (to == base) { + if (*next == '/') + len++; + + if (to != from) + memmove(to, from, len); + + to += len; + /* this is now the base, can't back up from a + * relative prefix */ + base = to; + } else { + /* back up a path segment */ + while (to > base && to[-1] == '/') to--; + while (to > base && to[-1] != '/') to--; + } + } else { + if (*next == '/' && *from != '/') + len++; + + if (to != from) + memmove(to, from, len); + + to += len; + } + + from += len; + + while (*from == '/') from++; + } + + *to = '\0'; + + path->size = to - path->ptr; + + return 0; +} + +int git_fs_path_apply_relative(git_str *target, const char *relpath) +{ + return git_str_joinpath(target, git_str_cstr(target), relpath) || + git_fs_path_resolve_relative(target, 0); +} + +int git_fs_path_cmp( + const char *name1, size_t len1, int isdir1, + const char *name2, size_t len2, int isdir2, + int (*compare)(const char *, const char *, size_t)) +{ + unsigned char c1, c2; + size_t len = len1 < len2 ? len1 : len2; + int cmp; + + cmp = compare(name1, name2, len); + if (cmp) + return cmp; + + c1 = name1[len]; + c2 = name2[len]; + + if (c1 == '\0' && isdir1) + c1 = '/'; + + if (c2 == '\0' && isdir2) + c2 = '/'; + + return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; +} + +size_t git_fs_path_common_dirlen(const char *one, const char *two) +{ + const char *p, *q, *dirsep = NULL; + + for (p = one, q = two; *p && *q; p++, q++) { + if (*p == '/' && *q == '/') + dirsep = p; + else if (*p != *q) + break; + } + + return dirsep ? (dirsep - one) + 1 : 0; +} + +int git_fs_path_make_relative(git_str *path, const char *parent) +{ + const char *p, *q, *p_dirsep, *q_dirsep; + size_t plen = path->size, newlen, alloclen, depth = 1, i, offset; + + for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) { + if (*p == '/' && *q == '/') { + p_dirsep = p; + q_dirsep = q; + } + else if (*p != *q) + break; + } + + /* need at least 1 common path segment */ + if ((p_dirsep == path->ptr || q_dirsep == parent) && + (*p_dirsep != '/' || *q_dirsep != '/')) { + git_error_set(GIT_ERROR_INVALID, + "%s is not a parent of %s", parent, path->ptr); + return GIT_ENOTFOUND; + } + + if (*p == '/' && !*q) + p++; + else if (!*p && *q == '/') + q++; + else if (!*p && !*q) + return git_str_clear(path), 0; + else { + p = p_dirsep + 1; + q = q_dirsep + 1; + } + + plen -= (p - path->ptr); + + if (!*q) + return git_str_set(path, p, plen); + + for (; (q = strchr(q, '/')) && *(q + 1); q++) + depth++; + + GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3); + GIT_ERROR_CHECK_ALLOC_ADD(&newlen, newlen, plen); + + GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, newlen, 1); + + /* save the offset as we might realllocate the pointer */ + offset = p - path->ptr; + if (git_str_try_grow(path, alloclen, 1) < 0) + return -1; + p = path->ptr + offset; + + memmove(path->ptr + (depth * 3), p, plen + 1); + + for (i = 0; i < depth; i++) + memcpy(path->ptr + (i * 3), "../", 3); + + path->size = newlen; + return 0; +} + +bool git_fs_path_has_non_ascii(const char *path, size_t pathlen) +{ + const uint8_t *scan = (const uint8_t *)path, *end; + + for (end = scan + pathlen; scan < end; ++scan) + if (*scan & 0x80) + return true; + + return false; +} + +#ifdef GIT_USE_ICONV + +int git_fs_path_iconv_init_precompose(git_fs_path_iconv_t *ic) +{ + git_str_init(&ic->buf, 0); + ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING); + return 0; +} + +void git_fs_path_iconv_clear(git_fs_path_iconv_t *ic) +{ + if (ic) { + if (ic->map != (iconv_t)-1) + iconv_close(ic->map); + git_str_dispose(&ic->buf); + } +} + +int git_fs_path_iconv(git_fs_path_iconv_t *ic, const char **in, size_t *inlen) +{ + char *nfd = (char*)*in, *nfc; + size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv; + int retry = 1; + + if (!ic || ic->map == (iconv_t)-1 || + !git_fs_path_has_non_ascii(*in, *inlen)) + return 0; + + git_str_clear(&ic->buf); + + while (1) { + GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, wantlen, 1); + if (git_str_grow(&ic->buf, alloclen) < 0) + return -1; + + nfc = ic->buf.ptr + ic->buf.size; + nfclen = ic->buf.asize - ic->buf.size; + + rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen); + + ic->buf.size = (nfc - ic->buf.ptr); + + if (rv != (size_t)-1) + break; + + /* if we cannot convert the data (probably because iconv thinks + * it is not valid UTF-8 source data), then use original data + */ + if (errno != E2BIG) + return 0; + + /* make space for 2x the remaining data to be converted + * (with per retry overhead to avoid infinite loops) + */ + wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4); + + if (retry++ > 4) + goto fail; + } + + ic->buf.ptr[ic->buf.size] = '\0'; + + *in = ic->buf.ptr; + *inlen = ic->buf.size; + + return 0; + +fail: + git_error_set(GIT_ERROR_OS, "unable to convert unicode path data"); + return -1; +} + +static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX"; +static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX"; + +/* Check if the platform is decomposing unicode data for us. We will + * emulate core Git and prefer to use precomposed unicode data internally + * on these platforms, composing the decomposed unicode on the fly. + * + * This mainly happens on the Mac where HDFS stores filenames as + * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will + * return decomposed unicode from readdir() even when the actual + * filesystem is storing precomposed unicode. + */ +bool git_fs_path_does_decompose_unicode(const char *root) +{ + git_str path = GIT_STR_INIT; + int fd; + bool found_decomposed = false; + char tmp[6]; + + /* Create a file using a precomposed path and then try to find it + * using the decomposed name. If the lookup fails, then we will mark + * that we should precompose unicode for this repository. + */ + if (git_str_joinpath(&path, root, nfc_file) < 0 || + (fd = p_mkstemp(path.ptr)) < 0) + goto done; + p_close(fd); + + /* record trailing digits generated by mkstemp */ + memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp)); + + /* try to look up as NFD path */ + if (git_str_joinpath(&path, root, nfd_file) < 0) + goto done; + memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); + + found_decomposed = git_fs_path_exists(path.ptr); + + /* remove temporary file (using original precomposed path) */ + if (git_str_joinpath(&path, root, nfc_file) < 0) + goto done; + memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); + + (void)p_unlink(path.ptr); + +done: + git_str_dispose(&path); + return found_decomposed; +} + +#else + +bool git_fs_path_does_decompose_unicode(const char *root) +{ + GIT_UNUSED(root); + return false; +} + +#endif + +#if defined(__sun) || defined(__GNU__) +typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1]; +#else +typedef struct dirent path_dirent_data; +#endif + +int git_fs_path_direach( + git_str *path, + uint32_t flags, + int (*fn)(void *, git_str *), + void *arg) +{ + int error = 0; + ssize_t wd_len; + DIR *dir; + struct dirent *de; + +#ifdef GIT_USE_ICONV + git_fs_path_iconv_t ic = GIT_PATH_ICONV_INIT; +#endif + + GIT_UNUSED(flags); + + if (git_fs_path_to_dir(path) < 0) + return -1; + + wd_len = git_str_len(path); + + if ((dir = opendir(path->ptr)) == NULL) { + git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path->ptr); + if (errno == ENOENT) + return GIT_ENOTFOUND; + + return -1; + } + +#ifdef GIT_USE_ICONV + if ((flags & GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE) != 0) + (void)git_fs_path_iconv_init_precompose(&ic); +#endif + + while ((de = readdir(dir)) != NULL) { + const char *de_path = de->d_name; + size_t de_len = strlen(de_path); + + if (git_fs_path_is_dot_or_dotdot(de_path)) + continue; + +#ifdef GIT_USE_ICONV + if ((error = git_fs_path_iconv(&ic, &de_path, &de_len)) < 0) + break; +#endif + + if ((error = git_str_put(path, de_path, de_len)) < 0) + break; + + git_error_clear(); + error = fn(arg, path); + + git_str_truncate(path, wd_len); /* restore path */ + + /* Only set our own error if the callback did not set one already */ + if (error != 0) { + if (!git_error_last()) + git_error_set_after_callback(error); + + break; + } + } + + closedir(dir); + +#ifdef GIT_USE_ICONV + git_fs_path_iconv_clear(&ic); +#endif + + return error; +} + +#if defined(GIT_WIN32) && !defined(__MINGW32__) + +/* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7 + * and better. + */ +#ifndef FIND_FIRST_EX_LARGE_FETCH +# define FIND_FIRST_EX_LARGE_FETCH 2 +#endif + +int git_fs_path_diriter_init( + git_fs_path_diriter *diriter, + const char *path, + unsigned int flags) +{ + git_win32_path path_filter; + + static int is_win7_or_later = -1; + if (is_win7_or_later < 0) + is_win7_or_later = git_has_win32_version(6, 1, 0); + + GIT_ASSERT_ARG(diriter); + GIT_ASSERT_ARG(path); + + memset(diriter, 0, sizeof(git_fs_path_diriter)); + diriter->handle = INVALID_HANDLE_VALUE; + + if (git_str_puts(&diriter->path_utf8, path) < 0) + return -1; + + path_trim_slashes(&diriter->path_utf8); + + if (diriter->path_utf8.size == 0) { + git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path); + return -1; + } + + if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 || + !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) { + git_error_set(GIT_ERROR_OS, "could not parse the directory path '%s'", path); + return -1; + } + + diriter->handle = FindFirstFileExW( + path_filter, + is_win7_or_later ? FindExInfoBasic : FindExInfoStandard, + &diriter->current, + FindExSearchNameMatch, + NULL, + is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0); + + if (diriter->handle == INVALID_HANDLE_VALUE) { + git_error_set(GIT_ERROR_OS, "could not open directory '%s'", path); + return -1; + } + + diriter->parent_utf8_len = diriter->path_utf8.size; + diriter->flags = flags; + return 0; +} + +static int diriter_update_paths(git_fs_path_diriter *diriter) +{ + size_t filename_len, path_len; + + filename_len = wcslen(diriter->current.cFileName); + + if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) || + GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2)) + return -1; + + if (path_len > GIT_WIN_PATH_UTF16) { + git_error_set(GIT_ERROR_FILESYSTEM, + "invalid path '%.*ls\\%ls' (path too long)", + diriter->parent_len, diriter->path, diriter->current.cFileName); + return -1; + } + + diriter->path[diriter->parent_len] = L'\\'; + memcpy(&diriter->path[diriter->parent_len+1], + diriter->current.cFileName, filename_len * sizeof(wchar_t)); + diriter->path[path_len-1] = L'\0'; + + git_str_truncate(&diriter->path_utf8, diriter->parent_utf8_len); + + if (diriter->parent_utf8_len > 0 && + diriter->path_utf8.ptr[diriter->parent_utf8_len-1] != '/') + git_str_putc(&diriter->path_utf8, '/'); + + git_str_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len); + + if (git_str_oom(&diriter->path_utf8)) + return -1; + + return 0; +} + +int git_fs_path_diriter_next(git_fs_path_diriter *diriter) +{ + bool skip_dot = !(diriter->flags & GIT_FS_PATH_DIR_INCLUDE_DOT_AND_DOTDOT); + + do { + /* Our first time through, we already have the data from + * FindFirstFileW. Use it, otherwise get the next file. + */ + if (!diriter->needs_next) + diriter->needs_next = 1; + else if (!FindNextFileW(diriter->handle, &diriter->current)) + return GIT_ITEROVER; + } while (skip_dot && git_fs_path_is_dot_or_dotdotW(diriter->current.cFileName)); + + if (diriter_update_paths(diriter) < 0) + return -1; + + return 0; +} + +int git_fs_path_diriter_filename( + const char **out, + size_t *out_len, + git_fs_path_diriter *diriter) +{ + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(out_len); + GIT_ASSERT_ARG(diriter); + GIT_ASSERT(diriter->path_utf8.size > diriter->parent_utf8_len); + + *out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1]; + *out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1; + return 0; +} + +int git_fs_path_diriter_fullpath( + const char **out, + size_t *out_len, + git_fs_path_diriter *diriter) +{ + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(out_len); + GIT_ASSERT_ARG(diriter); + + *out = diriter->path_utf8.ptr; + *out_len = diriter->path_utf8.size; + return 0; +} + +int git_fs_path_diriter_stat(struct stat *out, git_fs_path_diriter *diriter) +{ + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(diriter); + + return git_win32__file_attribute_to_stat(out, + (WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current, + diriter->path); +} + +void git_fs_path_diriter_free(git_fs_path_diriter *diriter) +{ + if (diriter == NULL) + return; + + git_str_dispose(&diriter->path_utf8); + + if (diriter->handle != INVALID_HANDLE_VALUE) { + FindClose(diriter->handle); + diriter->handle = INVALID_HANDLE_VALUE; + } +} + +#else + +int git_fs_path_diriter_init( + git_fs_path_diriter *diriter, + const char *path, + unsigned int flags) +{ + GIT_ASSERT_ARG(diriter); + GIT_ASSERT_ARG(path); + + memset(diriter, 0, sizeof(git_fs_path_diriter)); + + if (git_str_puts(&diriter->path, path) < 0) + return -1; + + path_trim_slashes(&diriter->path); + + if (diriter->path.size == 0) { + git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path); + return -1; + } + + if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) { + git_str_dispose(&diriter->path); + + git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path); + return -1; + } + +#ifdef GIT_USE_ICONV + if ((flags & GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE) != 0) + (void)git_fs_path_iconv_init_precompose(&diriter->ic); +#endif + + diriter->parent_len = diriter->path.size; + diriter->flags = flags; + + return 0; +} + +int git_fs_path_diriter_next(git_fs_path_diriter *diriter) +{ + struct dirent *de; + const char *filename; + size_t filename_len; + bool skip_dot = !(diriter->flags & GIT_FS_PATH_DIR_INCLUDE_DOT_AND_DOTDOT); + int error = 0; + + GIT_ASSERT_ARG(diriter); + + errno = 0; + + do { + if ((de = readdir(diriter->dir)) == NULL) { + if (!errno) + return GIT_ITEROVER; + + git_error_set(GIT_ERROR_OS, + "could not read directory '%s'", diriter->path.ptr); + return -1; + } + } while (skip_dot && git_fs_path_is_dot_or_dotdot(de->d_name)); + + filename = de->d_name; + filename_len = strlen(filename); + +#ifdef GIT_USE_ICONV + if ((diriter->flags & GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE) != 0 && + (error = git_fs_path_iconv(&diriter->ic, &filename, &filename_len)) < 0) + return error; +#endif + + git_str_truncate(&diriter->path, diriter->parent_len); + + if (diriter->parent_len > 0 && + diriter->path.ptr[diriter->parent_len-1] != '/') + git_str_putc(&diriter->path, '/'); + + git_str_put(&diriter->path, filename, filename_len); + + if (git_str_oom(&diriter->path)) + return -1; + + return error; +} + +int git_fs_path_diriter_filename( + const char **out, + size_t *out_len, + git_fs_path_diriter *diriter) +{ + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(out_len); + GIT_ASSERT_ARG(diriter); + GIT_ASSERT(diriter->path.size > diriter->parent_len); + + *out = &diriter->path.ptr[diriter->parent_len+1]; + *out_len = diriter->path.size - diriter->parent_len - 1; + return 0; +} + +int git_fs_path_diriter_fullpath( + const char **out, + size_t *out_len, + git_fs_path_diriter *diriter) +{ + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(out_len); + GIT_ASSERT_ARG(diriter); + + *out = diriter->path.ptr; + *out_len = diriter->path.size; + return 0; +} + +int git_fs_path_diriter_stat(struct stat *out, git_fs_path_diriter *diriter) +{ + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(diriter); + + return git_fs_path_lstat(diriter->path.ptr, out); +} + +void git_fs_path_diriter_free(git_fs_path_diriter *diriter) +{ + if (diriter == NULL) + return; + + if (diriter->dir) { + closedir(diriter->dir); + diriter->dir = NULL; + } + +#ifdef GIT_USE_ICONV + git_fs_path_iconv_clear(&diriter->ic); +#endif + + git_str_dispose(&diriter->path); +} + +#endif + +int git_fs_path_dirload( + git_vector *contents, + const char *path, + size_t prefix_len, + uint32_t flags) +{ + git_fs_path_diriter iter = GIT_FS_PATH_DIRITER_INIT; + const char *name; + size_t name_len; + char *dup; + int error; + + GIT_ASSERT_ARG(contents); + GIT_ASSERT_ARG(path); + + if ((error = git_fs_path_diriter_init(&iter, path, flags)) < 0) + return error; + + while ((error = git_fs_path_diriter_next(&iter)) == 0) { + if ((error = git_fs_path_diriter_fullpath(&name, &name_len, &iter)) < 0) + break; + + GIT_ASSERT(name_len > prefix_len); + + dup = git__strndup(name + prefix_len, name_len - prefix_len); + GIT_ERROR_CHECK_ALLOC(dup); + + if ((error = git_vector_insert(contents, dup)) < 0) + break; + } + + if (error == GIT_ITEROVER) + error = 0; + + git_fs_path_diriter_free(&iter); + return error; +} + +int git_fs_path_from_url_or_path(git_str *local_path_out, const char *url_or_path) +{ + if (git_fs_path_is_local_file_url(url_or_path)) + return git_fs_path_fromurl(local_path_out, url_or_path); + else + return git_str_sets(local_path_out, url_or_path); +} + +/* Reject paths like AUX or COM1, or those versions that end in a dot or + * colon. ("AUX." or "AUX:") + */ +GIT_INLINE(bool) validate_dospath( + const char *component, + size_t len, + const char dospath[3], + bool trailing_num) +{ + size_t last = trailing_num ? 4 : 3; + + if (len < last || git__strncasecmp(component, dospath, 3) != 0) + return true; + + if (trailing_num && (component[3] < '1' || component[3] > '9')) + return true; + + return (len > last && + component[last] != '.' && + component[last] != ':'); +} + +GIT_INLINE(bool) validate_char(unsigned char c, unsigned int flags) +{ + if ((flags & GIT_FS_PATH_REJECT_BACKSLASH) && c == '\\') + return false; + + if ((flags & GIT_FS_PATH_REJECT_SLASH) && c == '/') + return false; + + if (flags & GIT_FS_PATH_REJECT_NT_CHARS) { + if (c < 32) + return false; + + switch (c) { + case '<': + case '>': + case ':': + case '"': + case '|': + case '?': + case '*': + return false; + } + } + + return true; +} + +/* + * We fundamentally don't like some paths when dealing with user-inputted + * strings (to avoid escaping a sandbox): we don't want dot or dot-dot + * anywhere, we want to avoid writing weird paths on Windows that can't + * be handled by tools that use the non-\\?\ APIs, we don't want slashes + * or double slashes at the end of paths that can make them ambiguous. + * + * For checkout, we don't want to recurse into ".git" either. + */ +static bool validate_component( + const char *component, + size_t len, + unsigned int flags) +{ + if (len == 0) + return !(flags & GIT_FS_PATH_REJECT_EMPTY_COMPONENT); + + if ((flags & GIT_FS_PATH_REJECT_TRAVERSAL) && + len == 1 && component[0] == '.') + return false; + + if ((flags & GIT_FS_PATH_REJECT_TRAVERSAL) && + len == 2 && component[0] == '.' && component[1] == '.') + return false; + + if ((flags & GIT_FS_PATH_REJECT_TRAILING_DOT) && + component[len - 1] == '.') + return false; + + if ((flags & GIT_FS_PATH_REJECT_TRAILING_SPACE) && + component[len - 1] == ' ') + return false; + + if ((flags & GIT_FS_PATH_REJECT_TRAILING_COLON) && + component[len - 1] == ':') + return false; + + if (flags & GIT_FS_PATH_REJECT_DOS_PATHS) { + if (!validate_dospath(component, len, "CON", false) || + !validate_dospath(component, len, "PRN", false) || + !validate_dospath(component, len, "AUX", false) || + !validate_dospath(component, len, "NUL", false) || + !validate_dospath(component, len, "COM", true) || + !validate_dospath(component, len, "LPT", true)) + return false; + } + + return true; +} + +#ifdef GIT_WIN32 +GIT_INLINE(bool) validate_length( + const char *path, + size_t len, + size_t utf8_char_len) +{ + GIT_UNUSED(path); + GIT_UNUSED(len); + + return (utf8_char_len <= MAX_PATH); +} +#endif + +bool git_fs_path_str_is_valid_ext( + const git_str *path, + unsigned int flags, + bool (*validate_char_cb)(char ch, void *payload), + bool (*validate_component_cb)(const char *component, size_t len, void *payload), + bool (*validate_length_cb)(const char *path, size_t len, size_t utf8_char_len), + void *payload) +{ + const char *start, *c; + size_t len = 0; + + if (!flags) + return true; + + for (start = c = path->ptr; *c && len < path->size; c++, len++) { + if (!validate_char(*c, flags)) + return false; + + if (validate_char_cb && !validate_char_cb(*c, payload)) + return false; + + if (*c != '/') + continue; + + if (!validate_component(start, (c - start), flags)) + return false; + + if (validate_component_cb && + !validate_component_cb(start, (c - start), payload)) + return false; + + start = c + 1; + } + + /* + * We want to support paths specified as either `const char *` + * or `git_str *`; we pass size as `SIZE_MAX` when we use a + * `const char *` to avoid a `strlen`. Ensure that we didn't + * have a NUL in the buffer if there was a non-SIZE_MAX length. + */ + if (path->size != SIZE_MAX && len != path->size) + return false; + + if (!validate_component(start, (c - start), flags)) + return false; + + if (validate_component_cb && + !validate_component_cb(start, (c - start), payload)) + return false; + +#ifdef GIT_WIN32 + if ((flags & GIT_FS_PATH_REJECT_LONG_PATHS) != 0) { + size_t utf8_len = git_utf8_char_length(path->ptr, len); + + if (!validate_length(path->ptr, len, utf8_len)) + return false; + + if (validate_length_cb && + !validate_length_cb(path->ptr, len, utf8_len)) + return false; + } +#else + GIT_UNUSED(validate_length_cb); +#endif + + return true; +} + +int git_fs_path_validate_str_length_with_suffix( + git_str *path, + size_t suffix_len) +{ +#ifdef GIT_WIN32 + size_t utf8_len = git_utf8_char_length(path->ptr, path->size); + size_t total_len; + + if (GIT_ADD_SIZET_OVERFLOW(&total_len, utf8_len, suffix_len) || + total_len > MAX_PATH) { + + git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%.*s'", + (int)path->size, path->ptr); + return -1; + } +#else + GIT_UNUSED(path); + GIT_UNUSED(suffix_len); +#endif + + return 0; +} + +int git_fs_path_normalize_slashes(git_str *out, const char *path) +{ + int error; + char *p; + + if ((error = git_str_puts(out, path)) < 0) + return error; + + for (p = out->ptr; *p; p++) { + if (*p == '\\') + *p = '/'; + } + + return 0; +} + +bool git_fs_path_supports_symlinks(const char *dir) +{ + git_str path = GIT_STR_INIT; + bool supported = false; + struct stat st; + int fd; + + if ((fd = git_futils_mktmp(&path, dir, 0666)) < 0 || + p_close(fd) < 0 || + p_unlink(path.ptr) < 0 || + p_symlink("testing", path.ptr) < 0 || + p_lstat(path.ptr, &st) < 0) + goto done; + + supported = (S_ISLNK(st.st_mode) != 0); +done: + if (path.size) + (void)p_unlink(path.ptr); + git_str_dispose(&path); + return supported; +} + +int git_fs_path_validate_system_file_ownership(const char *path) +{ +#ifndef GIT_WIN32 + GIT_UNUSED(path); + return GIT_OK; +#else + git_win32_path buf; + PSID owner_sid; + PSECURITY_DESCRIPTOR descriptor = NULL; + HANDLE token; + TOKEN_USER *info = NULL; + DWORD err, len; + int ret; + + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; + + err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION, + &owner_sid, NULL, NULL, NULL, &descriptor); + + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { + ret = GIT_ENOTFOUND; + goto cleanup; + } + + if (err != ERROR_SUCCESS) { + git_error_set(GIT_ERROR_OS, "failed to get security information"); + ret = GIT_ERROR; + goto cleanup; + } + + if (!IsValidSid(owner_sid)) { + git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown"); + ret = GIT_ERROR; + goto cleanup; + } + + if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || + IsWellKnownSid(owner_sid, WinLocalSystemSid)) { + ret = GIT_OK; + goto cleanup; + } + + /* Obtain current user's SID */ + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) && + !GetTokenInformation(token, TokenUser, NULL, 0, &len)) { + info = git__malloc(len); + GIT_ERROR_CHECK_ALLOC(info); + if (!GetTokenInformation(token, TokenUser, info, len, &len)) { + git__free(info); + info = NULL; + } + } + + /* + * If the file is owned by the same account that is running the current + * process, it's okay to read from that file. + */ + if (info && EqualSid(owner_sid, info->User.Sid)) + ret = GIT_OK; + else { + git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is not valid"); + ret = GIT_ERROR; + } + git__free(info); + +cleanup: + if (descriptor) + LocalFree(descriptor); + + return ret; +#endif +} diff --git a/src/fs_path.h b/src/fs_path.h new file mode 100644 index 000000000..188dcf303 --- /dev/null +++ b/src/fs_path.h @@ -0,0 +1,748 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_fs_path_h__ +#define INCLUDE_fs_path_h__ + +#include "common.h" + +#include "posix.h" +#include "str.h" +#include "vector.h" +#include "utf8.h" + +#include "git2/sys/path.h" + +/** + * Path manipulation utils + * + * These are path utilities that munge paths without actually + * looking at the real filesystem. + */ + +/* + * The dirname() function shall take a pointer to a character string + * that contains a pathname, and return a pointer to a string that is a + * pathname of the parent directory of that file. Trailing '/' characters + * in the path are not counted as part of the path. + * + * If path does not contain a '/', then dirname() shall return a pointer to + * the string ".". If path is a null pointer or points to an empty string, + * dirname() shall return a pointer to the string "." . + * + * The `git_fs_path_dirname` implementation is thread safe. The returned + * string must be manually free'd. + * + * The `git_fs_path_dirname_r` implementation writes the dirname to a `git_str` + * if the buffer pointer is not NULL. + * It returns an error code < 0 if there is an allocation error, otherwise + * the length of the dirname (which will be > 0). + */ +extern char *git_fs_path_dirname(const char *path); +extern int git_fs_path_dirname_r(git_str *buffer, const char *path); + +/* + * This function returns the basename of the file, which is the last + * part of its full name given by fname, with the drive letter and + * leading directories stripped off. For example, the basename of + * c:/foo/bar/file.ext is file.ext, and the basename of a:foo is foo. + * + * Trailing slashes and backslashes are significant: the basename of + * c:/foo/bar/ is an empty string after the rightmost slash. + * + * The `git_fs_path_basename` implementation is thread safe. The returned + * string must be manually free'd. + * + * The `git_fs_path_basename_r` implementation writes the basename to a `git_str`. + * It returns an error code < 0 if there is an allocation error, otherwise + * the length of the basename (which will be >= 0). + */ +extern char *git_fs_path_basename(const char *path); +extern int git_fs_path_basename_r(git_str *buffer, const char *path); + +/* Return the offset of the start of the basename. Unlike the other + * basename functions, this returns 0 if the path is empty. + */ +extern size_t git_fs_path_basename_offset(git_str *buffer); + +/** + * Find offset to root of path if path has one. + * + * This will return a number >= 0 which is the offset to the start of the + * path, if the path is rooted (i.e. "/rooted/path" returns 0 and + * "c:/windows/rooted/path" returns 2). If the path is not rooted, this + * returns -1. + */ +extern int git_fs_path_root(const char *path); + +/** + * Ensure path has a trailing '/'. + */ +extern int git_fs_path_to_dir(git_str *path); + +/** + * Ensure string has a trailing '/' if there is space for it. + */ +extern void git_fs_path_string_to_dir(char *path, size_t size); + +/** + * Taken from git.git; returns nonzero if the given path is "." or "..". + */ +GIT_INLINE(int) git_fs_path_is_dot_or_dotdot(const char *name) +{ + return (name[0] == '.' && + (name[1] == '\0' || + (name[1] == '.' && name[2] == '\0'))); +} + +#ifdef GIT_WIN32 +GIT_INLINE(int) git_fs_path_is_dot_or_dotdotW(const wchar_t *name) +{ + return (name[0] == L'.' && + (name[1] == L'\0' || + (name[1] == L'.' && name[2] == L'\0'))); +} + +#define git_fs_path_is_absolute(p) \ + (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/')) + +#define git_fs_path_is_dirsep(p) \ + ((p) == '/' || (p) == '\\') + +/** + * Convert backslashes in path to forward slashes. + */ +GIT_INLINE(void) git_fs_path_mkposix(char *path) +{ + while (*path) { + if (*path == '\\') + *path = '/'; + + path++; + } +} +#else +# define git_fs_path_mkposix(p) /* blank */ + +#define git_fs_path_is_absolute(p) \ + ((p)[0] == '/') + +#define git_fs_path_is_dirsep(p) \ + ((p) == '/') + +#endif + +/** + * Check if string is a relative path (i.e. starts with "./" or "../") + */ +GIT_INLINE(int) git_fs_path_is_relative(const char *p) +{ + return (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/'))); +} + +/** + * Check if string is at end of path segment (i.e. looking at '/' or '\0') + */ +GIT_INLINE(int) git_fs_path_at_end_of_segment(const char *p) +{ + return !*p || *p == '/'; +} + +extern int git__percent_decode(git_str *decoded_out, const char *input); + +/** + * Extract path from file:// URL. + */ +extern int git_fs_path_fromurl(git_str *local_path_out, const char *file_url); + + +/** + * Path filesystem utils + * + * These are path utilities that actually access the filesystem. + */ + +/** + * Check if a file exists and can be accessed. + * @return true or false + */ +extern bool git_fs_path_exists(const char *path); + +/** + * Check if the given path points to a directory. + * @return true or false + */ +extern bool git_fs_path_isdir(const char *path); + +/** + * Check if the given path points to a regular file. + * @return true or false + */ +extern bool git_fs_path_isfile(const char *path); + +/** + * Check if the given path points to a symbolic link. + * @return true or false + */ +extern bool git_fs_path_islink(const char *path); + +/** + * Check if the given path is a directory, and is empty. + */ +extern bool git_fs_path_is_empty_dir(const char *path); + +/** + * Stat a file and/or link and set error if needed. + */ +extern int git_fs_path_lstat(const char *path, struct stat *st); + +/** + * Check if the parent directory contains the item. + * + * @param dir Directory to check. + * @param item Item that might be in the directory. + * @return 0 if item exists in directory, <0 otherwise. + */ +extern bool git_fs_path_contains(git_str *dir, const char *item); + +/** + * Check if the given path contains the given subdirectory. + * + * @param parent Directory path that might contain subdir + * @param subdir Subdirectory name to look for in parent + * @return true if subdirectory exists, false otherwise. + */ +extern bool git_fs_path_contains_dir(git_str *parent, const char *subdir); + +/** + * Determine the common directory length between two paths, including + * the final path separator. For example, given paths 'a/b/c/1.txt + * and 'a/b/c/d/2.txt', the common directory is 'a/b/c/', and this + * will return the length of the string 'a/b/c/', which is 6. + * + * @param one The first path + * @param two The second path + * @return The length of the common directory + */ +extern size_t git_fs_path_common_dirlen(const char *one, const char *two); + +/** + * Make the path relative to the given parent path. + * + * @param path The path to make relative + * @param parent The parent path to make path relative to + * @return 0 if path was made relative, GIT_ENOTFOUND + * if there was not common root between the paths, + * or <0. + */ +extern int git_fs_path_make_relative(git_str *path, const char *parent); + +/** + * Check if the given path contains the given file. + * + * @param dir Directory path that might contain file + * @param file File name to look for in parent + * @return true if file exists, false otherwise. + */ +extern bool git_fs_path_contains_file(git_str *dir, const char *file); + +/** + * Prepend base to unrooted path or just copy path over. + * + * This will optionally return the index into the path where the "root" + * is, either the end of the base directory prefix or the path root. + */ +extern int git_fs_path_join_unrooted( + git_str *path_out, const char *path, const char *base, ssize_t *root_at); + +/** + * Removes multiple occurrences of '/' in a row, squashing them into a + * single '/'. + */ +extern void git_fs_path_squash_slashes(git_str *path); + +/** + * Clean up path, prepending base if it is not already rooted. + */ +extern int git_fs_path_prettify(git_str *path_out, const char *path, const char *base); + +/** + * Clean up path, prepending base if it is not already rooted and + * appending a slash. + */ +extern int git_fs_path_prettify_dir(git_str *path_out, const char *path, const char *base); + +/** + * Get a directory from a path. + * + * If path is a directory, this acts like `git_fs_path_prettify_dir` + * (cleaning up path and appending a '/'). If path is a normal file, + * this prettifies it, then removed the filename a la dirname and + * appends the trailing '/'. If the path does not exist, it is + * treated like a regular filename. + */ +extern int git_fs_path_find_dir(git_str *dir); + +/** + * Resolve relative references within a path. + * + * This eliminates "./" and "../" relative references inside a path, + * as well as condensing multiple slashes into single ones. It will + * not touch the path before the "ceiling" length. + * + * Additionally, this will recognize an "c:/" drive prefix or a "xyz://" URL + * prefix and not touch that part of the path. + */ +extern int git_fs_path_resolve_relative(git_str *path, size_t ceiling); + +/** + * Apply a relative path to base path. + * + * Note that the base path could be a filename or a URL and this + * should still work. The relative path is walked segment by segment + * with three rules: series of slashes will be condensed to a single + * slash, "." will be eaten with no change, and ".." will remove a + * segment from the base path. + */ +extern int git_fs_path_apply_relative(git_str *target, const char *relpath); + +enum { + GIT_FS_PATH_DIR_IGNORE_CASE = (1u << 0), + GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1), + GIT_FS_PATH_DIR_INCLUDE_DOT_AND_DOTDOT = (1u << 2), +}; + +/** + * Walk each directory entry, except '.' and '..', calling fn(state). + * + * @param pathbuf Buffer the function reads the initial directory + * path from, and updates with each successive entry's name. + * @param flags Combination of GIT_FS_PATH_DIR flags. + * @param callback Callback for each entry. Passed the `payload` and each + * successive path inside the directory as a full path. This may + * safely append text to the pathbuf if needed. Return non-zero to + * cancel iteration (and return value will be propagated back). + * @param payload Passed to callback as first argument. + * @return 0 on success or error code from OS error or from callback + */ +extern int git_fs_path_direach( + git_str *pathbuf, + uint32_t flags, + int (*callback)(void *payload, git_str *path), + void *payload); + +/** + * Sort function to order two paths + */ +extern int git_fs_path_cmp( + const char *name1, size_t len1, int isdir1, + const char *name2, size_t len2, int isdir2, + int (*compare)(const char *, const char *, size_t)); + +/** + * Invoke callback up path directory by directory until the ceiling is + * reached (inclusive of a final call at the root_path). + * + * Returning anything other than 0 from the callback function + * will stop the iteration and propagate the error to the caller. + * + * @param pathbuf Buffer the function reads the directory from and + * and updates with each successive name. + * @param ceiling Prefix of path at which to stop walking up. If NULL, + * this will walk all the way up to the root. If not a prefix of + * pathbuf, the callback will be invoked a single time on the + * original input path. + * @param callback Function to invoke on each path. Passed the `payload` + * and the buffer containing the current path. The path should not + * be modified in any way. Return non-zero to stop iteration. + * @param payload Passed to fn as the first ath. + */ +extern int git_fs_path_walk_up( + git_str *pathbuf, + const char *ceiling, + int (*callback)(void *payload, const char *path), + void *payload); + + +enum { + GIT_FS_PATH_NOTEQUAL = 0, + GIT_FS_PATH_EQUAL = 1, + GIT_FS_PATH_PREFIX = 2 +}; + +/* + * Determines if a path is equal to or potentially a child of another. + * @param parent The possible parent + * @param child The possible child + */ +GIT_INLINE(int) git_fs_path_equal_or_prefixed( + const char *parent, + const char *child, + ssize_t *prefixlen) +{ + const char *p = parent, *c = child; + int lastslash = 0; + + while (*p && *c) { + lastslash = (*p == '/'); + + if (*p++ != *c++) + return GIT_FS_PATH_NOTEQUAL; + } + + if (*p != '\0') + return GIT_FS_PATH_NOTEQUAL; + + if (*c == '\0') { + if (prefixlen) + *prefixlen = p - parent; + + return GIT_FS_PATH_EQUAL; + } + + if (*c == '/' || lastslash) { + if (prefixlen) + *prefixlen = (p - parent) - lastslash; + + return GIT_FS_PATH_PREFIX; + } + + return GIT_FS_PATH_NOTEQUAL; +} + +/* translate errno to libgit2 error code and set error message */ +extern int git_fs_path_set_error( + int errno_value, const char *path, const char *action); + +/* check if non-ascii characters are present in filename */ +extern bool git_fs_path_has_non_ascii(const char *path, size_t pathlen); + +#define GIT_PATH_REPO_ENCODING "UTF-8" + +#ifdef __APPLE__ +#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC" +#else +#define GIT_PATH_NATIVE_ENCODING "UTF-8" +#endif + +#ifdef GIT_USE_ICONV + +#include <iconv.h> + +typedef struct { + iconv_t map; + git_str buf; +} git_fs_path_iconv_t; + +#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_STR_INIT } + +/* Init iconv data for converting decomposed UTF-8 to precomposed */ +extern int git_fs_path_iconv_init_precompose(git_fs_path_iconv_t *ic); + +/* Clear allocated iconv data */ +extern void git_fs_path_iconv_clear(git_fs_path_iconv_t *ic); + +/* + * Rewrite `in` buffer using iconv map if necessary, replacing `in` + * pointer internal iconv buffer if rewrite happened. The `in` pointer + * will be left unchanged if no rewrite was needed. + */ +extern int git_fs_path_iconv(git_fs_path_iconv_t *ic, const char **in, size_t *inlen); + +#endif /* GIT_USE_ICONV */ + +extern bool git_fs_path_does_decompose_unicode(const char *root); + + +typedef struct git_fs_path_diriter git_fs_path_diriter; + +#if defined(GIT_WIN32) && !defined(__MINGW32__) + +struct git_fs_path_diriter +{ + git_win32_path path; + size_t parent_len; + + git_str path_utf8; + size_t parent_utf8_len; + + HANDLE handle; + + unsigned int flags; + + WIN32_FIND_DATAW current; + unsigned int needs_next; +}; + +#define GIT_FS_PATH_DIRITER_INIT { {0}, 0, GIT_STR_INIT, 0, INVALID_HANDLE_VALUE } + +#else + +struct git_fs_path_diriter +{ + git_str path; + size_t parent_len; + + unsigned int flags; + + DIR *dir; + +#ifdef GIT_USE_ICONV + git_fs_path_iconv_t ic; +#endif +}; + +#define GIT_FS_PATH_DIRITER_INIT { GIT_STR_INIT } + +#endif + +/** + * Initialize a directory iterator. + * + * @param diriter Pointer to a diriter structure that will be setup. + * @param path The path that will be iterated over + * @param flags Directory reader flags + * @return 0 or an error code + */ +extern int git_fs_path_diriter_init( + git_fs_path_diriter *diriter, + const char *path, + unsigned int flags); + +/** + * Advance the directory iterator. Will return GIT_ITEROVER when + * the iteration has completed successfully. + * + * @param diriter The directory iterator + * @return 0, GIT_ITEROVER, or an error code + */ +extern int git_fs_path_diriter_next(git_fs_path_diriter *diriter); + +/** + * Returns the file name of the current item in the iterator. + * + * @param out Pointer to store the path in + * @param out_len Pointer to store the length of the path in + * @param diriter The directory iterator + * @return 0 or an error code + */ +extern int git_fs_path_diriter_filename( + const char **out, + size_t *out_len, + git_fs_path_diriter *diriter); + +/** + * Returns the full path of the current item in the iterator; that + * is the current filename plus the path of the directory that the + * iterator was constructed with. + * + * @param out Pointer to store the path in + * @param out_len Pointer to store the length of the path in + * @param diriter The directory iterator + * @return 0 or an error code + */ +extern int git_fs_path_diriter_fullpath( + const char **out, + size_t *out_len, + git_fs_path_diriter *diriter); + +/** + * Performs an `lstat` on the current item in the iterator. + * + * @param out Pointer to store the stat data in + * @param diriter The directory iterator + * @return 0 or an error code + */ +extern int git_fs_path_diriter_stat(struct stat *out, git_fs_path_diriter *diriter); + +/** + * Closes the directory iterator. + * + * @param diriter The directory iterator + */ +extern void git_fs_path_diriter_free(git_fs_path_diriter *diriter); + +/** + * Load all directory entries (except '.' and '..') into a vector. + * + * For cases where `git_fs_path_direach()` is not appropriate, this + * allows you to load the filenames in a directory into a vector + * of strings. That vector can then be sorted, iterated, or whatever. + * Remember to free alloc of the allocated strings when you are done. + * + * @param contents Vector to fill with directory entry names. + * @param path The directory to read from. + * @param prefix_len When inserting entries, the trailing part of path + * will be prefixed after this length. I.e. given path "/a/b" and + * prefix_len 3, the entries will look like "b/e1", "b/e2", etc. + * @param flags Combination of GIT_FS_PATH_DIR flags. + */ +extern int git_fs_path_dirload( + git_vector *contents, + const char *path, + size_t prefix_len, + uint32_t flags); + + +/* Used for paths to repositories on the filesystem */ +extern bool git_fs_path_is_local_file_url(const char *file_url); +extern int git_fs_path_from_url_or_path(git_str *local_path_out, const char *url_or_path); + +/* Flags to determine path validity in `git_fs_path_isvalid` */ +#define GIT_FS_PATH_REJECT_EMPTY_COMPONENT (1 << 0) +#define GIT_FS_PATH_REJECT_TRAVERSAL (1 << 1) +#define GIT_FS_PATH_REJECT_SLASH (1 << 2) +#define GIT_FS_PATH_REJECT_BACKSLASH (1 << 3) +#define GIT_FS_PATH_REJECT_TRAILING_DOT (1 << 4) +#define GIT_FS_PATH_REJECT_TRAILING_SPACE (1 << 5) +#define GIT_FS_PATH_REJECT_TRAILING_COLON (1 << 6) +#define GIT_FS_PATH_REJECT_DOS_PATHS (1 << 7) +#define GIT_FS_PATH_REJECT_NT_CHARS (1 << 8) +#define GIT_FS_PATH_REJECT_LONG_PATHS (1 << 9) + +#define GIT_FS_PATH_REJECT_MAX (1 << 9) + +/* Default path safety for writing files to disk: since we use the + * Win32 "File Namespace" APIs ("\\?\") we need to protect from + * paths that the normal Win32 APIs would not write. + */ +#ifdef GIT_WIN32 +# define GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS \ + GIT_FS_PATH_REJECT_EMPTY_COMPONENT | \ + GIT_FS_PATH_REJECT_TRAVERSAL | \ + GIT_FS_PATH_REJECT_BACKSLASH | \ + GIT_FS_PATH_REJECT_TRAILING_DOT | \ + GIT_FS_PATH_REJECT_TRAILING_SPACE | \ + GIT_FS_PATH_REJECT_TRAILING_COLON | \ + GIT_FS_PATH_REJECT_DOS_PATHS | \ + GIT_FS_PATH_REJECT_NT_CHARS +#else +# define GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS \ + GIT_FS_PATH_REJECT_EMPTY_COMPONENT | \ + GIT_FS_PATH_REJECT_TRAVERSAL +#endif + +/** + * Validate a filesystem path; with custom callbacks per-character and + * per-path component. + */ +extern bool git_fs_path_str_is_valid_ext( + const git_str *path, + unsigned int flags, + bool (*validate_char_cb)(char ch, void *payload), + bool (*validate_component_cb)(const char *component, size_t len, void *payload), + bool (*validate_length_cb)(const char *component, size_t len, size_t utf8_char_len), + void *payload); + +GIT_INLINE(bool) git_fs_path_is_valid_ext( + const char *path, + unsigned int flags, + bool (*validate_char_cb)(char ch, void *payload), + bool (*validate_component_cb)(const char *component, size_t len, void *payload), + bool (*validate_length_cb)(const char *component, size_t len, size_t utf8_char_len), + void *payload) +{ + const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX); + return git_fs_path_str_is_valid_ext( + &str, + flags, + validate_char_cb, + validate_component_cb, + validate_length_cb, + payload); +} + +/** + * Validate a filesystem path. This ensures that the given path is legal + * and does not contain any "unsafe" components like path traversal ('.' + * or '..'), characters that are inappropriate for lesser filesystems + * (trailing ' ' or ':' characters), or filenames ("component names") + * that are not supported ('AUX', 'COM1"). + */ +GIT_INLINE(bool) git_fs_path_is_valid( + const char *path, + unsigned int flags) +{ + const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX); + return git_fs_path_str_is_valid_ext(&str, flags, NULL, NULL, NULL, NULL); +} + +/** Validate a filesystem path in a `git_str`. */ +GIT_INLINE(bool) git_fs_path_str_is_valid( + const git_str *path, + unsigned int flags) +{ + return git_fs_path_str_is_valid_ext(path, flags, NULL, NULL, NULL, NULL); +} + +extern int git_fs_path_validate_str_length_with_suffix( + git_str *path, + size_t suffix_len); + +/** + * Validate an on-disk path, taking into account that it will have a + * suffix appended (eg, `.lock`). + */ +GIT_INLINE(int) git_fs_path_validate_filesystem_with_suffix( + const char *path, + size_t path_len, + size_t suffix_len) +{ +#ifdef GIT_WIN32 + size_t path_chars, total_chars; + + path_chars = git_utf8_char_length(path, path_len); + + if (GIT_ADD_SIZET_OVERFLOW(&total_chars, path_chars, suffix_len) || + total_chars > MAX_PATH) { + git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%s'", path); + return -1; + } + return 0; +#else + GIT_UNUSED(path); + GIT_UNUSED(path_len); + GIT_UNUSED(suffix_len); + return 0; +#endif +} + +/** + * Validate an path on the filesystem. This ensures that the given + * path is valid for the operating system/platform; for example, this + * will ensure that the given absolute path is smaller than MAX_PATH on + * Windows. + * + * For paths within the working directory, you should use ensure that + * `core.longpaths` is obeyed. Use `git_fs_path_validate_workdir`. + */ +GIT_INLINE(int) git_fs_path_validate_filesystem( + const char *path, + size_t path_len) +{ + return git_fs_path_validate_filesystem_with_suffix(path, path_len, 0); +} + +/** + * Convert any backslashes into slashes + */ +int git_fs_path_normalize_slashes(git_str *out, const char *path); + +bool git_fs_path_supports_symlinks(const char *dir); + +/** + * Validate a system file's ownership + * + * Verify that the file in question is owned by an administrator or system + * account, or at least by the current user. + * + * This function returns 0 if successful. If the file is not owned by any of + * these, or any other if there have been problems determining the file + * ownership, it returns -1. + */ +int git_fs_path_validate_system_file_ownership(const char *path); + +#endif diff --git a/src/futils.c b/src/futils.c index 9a15ceeb9..7ec1009bd 100644 --- a/src/futils.c +++ b/src/futils.c @@ -99,7 +99,7 @@ int git_futils_open_ro(const char *path) { int fd = p_open(path, O_RDONLY); if (fd < 0) - return git_path_set_error(errno, path, "open"); + return git_fs_path_set_error(errno, path, "open"); return fd; } @@ -107,7 +107,7 @@ int git_futils_truncate(const char *path, int mode) { int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode); if (fd < 0) - return git_path_set_error(errno, path, "open"); + return git_fs_path_set_error(errno, path, "open"); close(fd); return 0; @@ -195,7 +195,7 @@ int git_futils_readbuffer_updated( *updated = 0; if (p_stat(path, &st) < 0) - return git_path_set_error(errno, path, "stat"); + return git_fs_path_set_error(errno, path, "stat"); if (S_ISDIR(st.st_mode)) { @@ -429,7 +429,7 @@ GIT_INLINE(int) mkdir_canonicalize( } /* Trim trailing slashes (except the root) */ - if ((root_len = git_path_root(path->ptr)) < 0) + if ((root_len = git_fs_path_root(path->ptr)) < 0) root_len = 0; else root_len++; @@ -439,11 +439,11 @@ GIT_INLINE(int) mkdir_canonicalize( /* if we are not supposed to made the last element, truncate it */ if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) { - git_path_dirname_r(path, path->ptr); + git_fs_path_dirname_r(path, path->ptr); flags |= GIT_MKDIR_SKIP_LAST; } if ((flags & GIT_MKDIR_SKIP_LAST) != 0) { - git_path_dirname_r(path, path->ptr); + git_fs_path_dirname_r(path, path->ptr); } /* We were either given the root path (or trimmed it to @@ -473,7 +473,7 @@ int git_futils_mkdir( make_path.size == 0) goto done; - root_len = git_path_root(make_path.ptr); + root_len = git_fs_path_root(make_path.ptr); /* find the first parent directory that exists. this will be used * as the base to dirname_relative. @@ -492,7 +492,7 @@ int git_futils_mkdir( depth++; /* examine the parent of the current path */ - if ((len = git_path_dirname_r(&parent_path, parent_path.ptr)) < 0) { + if ((len = git_fs_path_dirname_r(&parent_path, parent_path.ptr)) < 0) { error = len; goto done; } @@ -569,7 +569,7 @@ int git_futils_mkdir_relative( opts = &empty_opts; /* build path and find "root" where we should start calling mkdir */ - if (git_path_join_unrooted(&make_path, relative_path, base, &root) < 0) + if (git_fs_path_join_unrooted(&make_path, relative_path, base, &root) < 0) return -1; if ((error = mkdir_canonicalize(&make_path, flags)) < 0 || @@ -581,7 +581,7 @@ int git_futils_mkdir_relative( root = git_str_rfind(&make_path, '/'); /* advance root past drive name or network mount prefix */ - min_root_len = git_path_root(make_path.ptr); + min_root_len = git_fs_path_root(make_path.ptr); if (root < min_root_len) root = min_root_len; while (root >= 0 && make_path.ptr[root] == '/') @@ -744,13 +744,13 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_str *path) path->ptr, "parent is not directory"); } else - error = git_path_set_error(errno, path->ptr, "rmdir"); + error = git_fs_path_set_error(errno, path->ptr, "rmdir"); } else if (S_ISDIR(st.st_mode)) { data->depth++; - error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data); + error = git_fs_path_direach(path, 0, futils__rmdir_recurs_foreach, data); data->depth--; @@ -765,13 +765,13 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_str *path) (errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY)) error = 0; else - error = git_path_set_error(errno, path->ptr, "rmdir"); + error = git_fs_path_set_error(errno, path->ptr, "rmdir"); } } else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) { if (p_unlink(path->ptr) < 0) - error = git_path_set_error(errno, path->ptr, "remove"); + error = git_fs_path_set_error(errno, path->ptr, "remove"); } else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0) @@ -795,11 +795,11 @@ static int futils__rmdir_empty_parent(void *opaque, const char *path) /* do nothing */ } else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0 && en == EBUSY) { - error = git_path_set_error(errno, path, "rmdir"); + error = git_fs_path_set_error(errno, path, "rmdir"); } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { error = GIT_ITEROVER; } else { - error = git_path_set_error(errno, path, "rmdir"); + error = git_fs_path_set_error(errno, path, "rmdir"); } } @@ -814,7 +814,7 @@ int git_futils_rmdir_r( futils__rmdir_data data; /* build path and find "root" where we should start calling mkdir */ - if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0) + if (git_fs_path_join_unrooted(&fullpath, path, base, NULL) < 0) return -1; memset(&data, 0, sizeof(data)); @@ -826,7 +826,7 @@ int git_futils_rmdir_r( /* remove now-empty parents if requested */ if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) - error = git_path_walk_up( + error = git_fs_path_walk_up( &fullpath, base, futils__rmdir_empty_parent, &data); if (error == GIT_ITEROVER) { @@ -887,7 +887,7 @@ int git_futils_cp(const char *from, const char *to, mode_t filemode) if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) { p_close(ifd); - return git_path_set_error(errno, to, "open for writing"); + return git_fs_path_set_error(errno, to, "open for writing"); } return cp_by_fd(ifd, ofd, true); @@ -903,7 +903,7 @@ int git_futils_touch(const char *path, time_t *when) ret = p_utimes(path, times); - return (ret < 0) ? git_path_set_error(errno, path, "touch") : 0; + return (ret < 0) ? git_fs_path_set_error(errno, path, "touch") : 0; } static int cp_link(const char *from, const char *to, size_t link_size) @@ -977,14 +977,14 @@ static int _cp_r_callback(void *ref, git_str *from) bool exists = false; if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 && - from->ptr[git_path_basename_offset(from)] == '.') + from->ptr[git_fs_path_basename_offset(from)] == '.') return 0; if ((error = git_str_joinpath( &info->to, info->to_root, from->ptr + info->from_prefix)) < 0) return error; - if (!(error = git_path_lstat(info->to.ptr, &to_st))) + if (!(error = git_fs_path_lstat(info->to.ptr, &to_st))) exists = true; else if (error != GIT_ENOTFOUND) return error; @@ -993,7 +993,7 @@ static int _cp_r_callback(void *ref, git_str *from) error = 0; } - if ((error = git_path_lstat(from->ptr, &from_st)) < 0) + if ((error = git_fs_path_lstat(from->ptr, &from_st)) < 0) return error; if (S_ISDIR(from_st.st_mode)) { @@ -1009,7 +1009,7 @@ static int _cp_r_callback(void *ref, git_str *from) /* recurse onto target directory */ if (!error && (!exists || S_ISDIR(to_st.st_mode))) - error = git_path_direach(from, 0, _cp_r_callback, info); + error = git_fs_path_direach(from, 0, _cp_r_callback, info); if (oldmode != 0) info->dirmode = oldmode; @@ -1182,7 +1182,7 @@ int git_futils_fsync_parent(const char *path) char *parent; int error; - if ((parent = git_path_dirname(path)) == NULL) + if ((parent = git_fs_path_dirname(path)) == NULL) return -1; error = git_futils_fsync_dir(parent); diff --git a/src/futils.h b/src/futils.h index eea69adde..1386dc482 100644 --- a/src/futils.h +++ b/src/futils.h @@ -11,7 +11,7 @@ #include "map.h" #include "posix.h" -#include "path.h" +#include "fs_path.h" #include "pool.h" #include "strmap.h" #include "hash.h" @@ -96,7 +96,7 @@ typedef enum { GIT_MKDIR_SKIP_LAST2 = 32, GIT_MKDIR_VERIFY_DIR = 64, GIT_MKDIR_REMOVE_FILES = 128, - GIT_MKDIR_REMOVE_SYMLINKS = 256, + GIT_MKDIR_REMOVE_SYMLINKS = 256 } git_futils_mkdir_flags; struct git_futils_mkdir_perfdata @@ -159,7 +159,7 @@ typedef enum { GIT_RMDIR_SKIP_NONEMPTY = (1 << 1), GIT_RMDIR_EMPTY_PARENTS = (1 << 2), GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3), - GIT_RMDIR_SKIP_ROOT = (1 << 4), + GIT_RMDIR_SKIP_ROOT = (1 << 4) } git_futils_rmdir_flags; /** @@ -224,7 +224,7 @@ typedef enum { GIT_CPDIR_OVERWRITE = (1u << 3), GIT_CPDIR_CHMOD_DIRS = (1u << 4), GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5), - GIT_CPDIR_LINK_FILES = (1u << 6), + GIT_CPDIR_LINK_FILES = (1u << 6) } git_futils_cpdir_flags; /** diff --git a/src/ignore.c b/src/ignore.c index eb9fd8a9e..cee58d7f1 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -10,9 +10,10 @@ #include "git2/ignore.h" #include "common.h" #include "attrcache.h" -#include "path.h" +#include "fs_path.h" #include "config.h" #include "wildmatch.h" +#include "path.h" #define GIT_IGNORE_INTERNAL "[internal]exclude" @@ -181,7 +182,7 @@ static int parse_ignore_file( /* if subdir file path, convert context for file paths */ if (attrs->entry && - git_path_root(attrs->entry->path) < 0 && + git_fs_path_root(attrs->entry->path) < 0 && !git__suffixcmp(attrs->entry->path, "/" GIT_IGNORE_FILE)) context = attrs->entry->path; @@ -313,21 +314,21 @@ int git_ignore__for_path( goto cleanup; /* given a unrooted path in a non-bare repo, resolve it */ - if (workdir && git_path_root(path) < 0) { + if (workdir && git_fs_path_root(path) < 0) { git_str local = GIT_STR_INIT; - if ((error = git_path_dirname_r(&local, path)) < 0 || - (error = git_path_resolve_relative(&local, 0)) < 0 || - (error = git_path_to_dir(&local)) < 0 || + if ((error = git_fs_path_dirname_r(&local, path)) < 0 || + (error = git_fs_path_resolve_relative(&local, 0)) < 0 || + (error = git_fs_path_to_dir(&local)) < 0 || (error = git_str_joinpath(&ignores->dir, workdir, local.ptr)) < 0 || - (error = git_path_validate_workdir_buf(repo, &ignores->dir)) < 0) { + (error = git_path_validate_str_length(repo, &ignores->dir)) < 0) { /* Nothing, we just want to stop on the first error */ } git_str_dispose(&local); } else { if (!(error = git_str_joinpath(&ignores->dir, path, ""))) - error = git_path_validate_filesystem(ignores->dir.ptr, ignores->dir.size); + error = git_path_validate_str_length(NULL, &ignores->dir); } if (error < 0) @@ -342,7 +343,7 @@ int git_ignore__for_path( /* load .gitignore up the path */ if (workdir != NULL) { - error = git_path_walk_up( + error = git_fs_path_walk_up( &ignores->dir, workdir, push_one_ignore, ignores); if (error < 0) goto cleanup; @@ -410,7 +411,7 @@ int git_ignore__pop_dir(git_ignores *ign) if (--ign->depth > 0) { git_str_rtruncate_at_char(&ign->dir, '/'); - git_path_to_dir(&ign->dir); + git_fs_path_to_dir(&ign->dir); } return 0; @@ -629,7 +630,7 @@ int git_ignore__check_pathspec_for_exact_ignores( break; /* is there a file on disk that matches this exactly? */ - if (!git_path_isfile(path.ptr)) + if (!git_fs_path_isfile(path.ptr)) continue; /* is that file ignored? */ diff --git a/src/ignore.h b/src/ignore.h index 799195258..aa5ca62b7 100644 --- a/src/ignore.h +++ b/src/ignore.h @@ -48,7 +48,7 @@ enum { GIT_IGNORE_UNCHECKED = -2, GIT_IGNORE_NOTFOUND = -1, GIT_IGNORE_FALSE = 0, - GIT_IGNORE_TRUE = 1, + GIT_IGNORE_TRUE = 1 }; extern int git_ignore__lookup(int *out, git_ignores *ign, const char *path, git_dir_flag dir_flag); diff --git a/src/index.c b/src/index.c index b8aa310d3..7ade4341f 100644 --- a/src/index.c +++ b/src/index.c @@ -20,6 +20,7 @@ #include "idxmap.h" #include "diff.h" #include "varint.h" +#include "path.h" #include "git2/odb.h" #include "git2/oid.h" @@ -420,7 +421,7 @@ int git_index_open(git_index **index_out, const char *index_path) goto fail; /* Check if index file is stored on disk already */ - if (git_path_exists(index->index_file_path) == true) + if (git_fs_path_exists(index->index_file_path) == true) index->on_disk = 1; } @@ -648,7 +649,7 @@ int git_index_read(git_index *index, int force) return create_index_error(-1, "failed to read index: The index is in-memory only"); - index->on_disk = git_path_exists(index->index_file_path); + index->on_disk = git_fs_path_exists(index->index_file_path); if (!index->on_disk) { if (force && (error = git_index_clear(index)) < 0) @@ -944,7 +945,7 @@ static int index_entry_create( if (st) mode = st->st_mode; - if (!git_path_validate(repo, path, mode, path_valid_flags)) { + if (!git_path_is_valid(repo, path, mode, path_valid_flags)) { git_error_set(GIT_ERROR_INDEX, "invalid path: '%s'", path); return -1; } @@ -991,7 +992,7 @@ static int index_entry_init( if (git_repository_workdir_path(&path, repo, rel_path) < 0) return -1; - error = git_path_lstat(path.ptr, &st); + error = git_fs_path_lstat(path.ptr, &st); git_str_dispose(&path); if (error < 0) @@ -1728,7 +1729,7 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage) git_index_entry *entry; if (!(error = git_str_sets(&pfx, dir)) && - !(error = git_path_to_dir(&pfx))) + !(error = git_fs_path_to_dir(&pfx))) index_find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY); while (!error) { @@ -3385,7 +3386,7 @@ enum { INDEX_ACTION_NONE = 0, INDEX_ACTION_UPDATE = 1, INDEX_ACTION_REMOVE = 2, - INDEX_ACTION_ADDALL = 3, + INDEX_ACTION_ADDALL = 3 }; int git_index_add_all( diff --git a/src/iterator.c b/src/iterator.c index 5549c636a..15bb63dc8 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -9,6 +9,7 @@ #include "tree.h" #include "index.h" +#include "path.h" #define GIT_ITERATOR_FIRST_ACCESS (1 << 15) #define GIT_ITERATOR_HONOR_IGNORES (1 << 16) @@ -292,7 +293,7 @@ typedef enum { ITERATOR_PATHLIST_IS_FILE = 1, ITERATOR_PATHLIST_IS_DIR = 2, ITERATOR_PATHLIST_IS_PARENT = 3, - ITERATOR_PATHLIST_FULL = 4, + ITERATOR_PATHLIST_FULL = 4 } iterator_pathlist_search_t; static iterator_pathlist_search_t iterator_pathlist_search( @@ -472,7 +473,7 @@ GIT_INLINE(tree_iterator_frame *) tree_iterator_current_frame( GIT_INLINE(int) tree_entry_cmp( const git_tree_entry *a, const git_tree_entry *b, bool icase) { - return git_path_cmp( + return git_fs_path_cmp( a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE, b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE, icase ? git__strncasecmp : git__strncmp); @@ -1279,7 +1280,7 @@ static int filesystem_iterator_entry_hash( iter->base.repo, entry->path, GIT_OBJECT_BLOB, NULL); if (!(error = git_str_joinpath(&fullpath, iter->root, entry->path)) && - !(error = git_path_validate_workdir_buf(iter->base.repo, &fullpath))) + !(error = git_path_validate_str_length(iter->base.repo, &fullpath))) error = git_odb_hashfile(&entry->id, fullpath.ptr, GIT_OBJECT_BLOB); git_str_dispose(&fullpath); @@ -1336,7 +1337,7 @@ static int filesystem_iterator_frame_push( filesystem_iterator_entry *frame_entry) { filesystem_iterator_frame *new_frame = NULL; - git_path_diriter diriter = GIT_PATH_DIRITER_INIT; + git_fs_path_diriter diriter = GIT_FS_PATH_DIRITER_INIT; git_str root = GIT_STR_INIT; const char *path; filesystem_iterator_entry *entry; @@ -1361,7 +1362,7 @@ static int filesystem_iterator_frame_push( git_str_puts(&root, iter->root); if (git_str_oom(&root) || - git_path_validate_workdir_buf(iter->base.repo, &root) < 0) { + git_path_validate_str_length(iter->base.repo, &root) < 0) { error = -1; goto done; } @@ -1369,7 +1370,7 @@ static int filesystem_iterator_frame_push( new_frame->path_len = frame_entry ? frame_entry->path_len : 0; /* Any error here is equivalent to the dir not existing, skip over it */ - if ((error = git_path_diriter_init( + if ((error = git_fs_path_diriter_init( &diriter, root.ptr, iter->dirload_flags)) < 0) { error = GIT_ENOTFOUND; goto done; @@ -1387,12 +1388,18 @@ static int filesystem_iterator_frame_push( /* check if this directory is ignored */ filesystem_iterator_frame_push_ignores(iter, frame_entry, new_frame); - while ((error = git_path_diriter_next(&diriter)) == 0) { + while ((error = git_fs_path_diriter_next(&diriter)) == 0) { iterator_pathlist_search_t pathlist_match = ITERATOR_PATHLIST_FULL; + git_str path_str = GIT_STR_INIT; bool dir_expected = false; - if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0 || - (error = git_path_validate_workdir_with_len(iter->base.repo, path, path_len)) < 0) + if ((error = git_fs_path_diriter_fullpath(&path, &path_len, &diriter)) < 0) + goto done; + + path_str.ptr = (char *)path; + path_str.size = path_len; + + if ((error = git_path_validate_str_length(iter->base.repo, &path_str)) < 0) goto done; GIT_ASSERT(path_len > iter->root_len); @@ -1414,7 +1421,7 @@ static int filesystem_iterator_frame_push( * we have an index, we can just copy the data out of it. */ - if ((error = git_path_diriter_stat(&statbuf, &diriter)) < 0) { + if ((error = git_fs_path_diriter_stat(&statbuf, &diriter)) < 0) { /* file was removed between readdir and lstat */ if (error == GIT_ENOTFOUND) continue; @@ -1472,7 +1479,7 @@ done: git_array_pop(iter->frames); git_str_dispose(&root); - git_path_diriter_free(&diriter); + git_fs_path_diriter_free(&diriter); return error; } @@ -1565,7 +1572,7 @@ static int filesystem_iterator_is_dir( } if ((error = git_str_joinpath(&fullpath, iter->root, entry->path)) < 0 || - (error = git_path_validate_workdir_buf(iter->base.repo, &fullpath)) < 0 || + (error = git_path_validate_str_length(iter->base.repo, &fullpath)) < 0 || (error = p_stat(fullpath.ptr, &st)) < 0) goto done; @@ -1961,9 +1968,10 @@ static int iterator_for_filesystem( iter->index = index; iter->dirload_flags = - (iterator__ignore_case(&iter->base) ? GIT_PATH_DIR_IGNORE_CASE : 0) | + (iterator__ignore_case(&iter->base) ? + GIT_FS_PATH_DIR_IGNORE_CASE : 0) | (iterator__flag(&iter->base, PRECOMPOSE_UNICODE) ? - GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0); + GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE : 0); if ((error = filesystem_iterator_init(iter)) < 0) goto on_error; @@ -2058,7 +2066,7 @@ static bool index_iterator_create_pseudotree( prev_path = iter->entry ? iter->entry->path : ""; /* determine if the new path is in a different directory from the old */ - common_len = git_path_common_dirlen(prev_path, path); + common_len = git_fs_path_common_dirlen(prev_path, path); relative_path = path + common_len; if ((dirsep = strchr(relative_path, '/')) == NULL) diff --git a/src/iterator.h b/src/iterator.h index e55c1047a..6bb8489d0 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -21,7 +21,7 @@ typedef enum { GIT_ITERATOR_TREE = 1, GIT_ITERATOR_INDEX = 2, GIT_ITERATOR_WORKDIR = 3, - GIT_ITERATOR_FS = 4, + GIT_ITERATOR_FS = 4 } git_iterator_t; typedef enum { @@ -42,7 +42,7 @@ typedef enum { /** descend into symlinked directories */ GIT_ITERATOR_DESCEND_SYMLINKS = (1u << 7), /** hash files in workdir or filesystem iterators */ - GIT_ITERATOR_INCLUDE_HASH = (1u << 8), + GIT_ITERATOR_INCLUDE_HASH = (1u << 8) } git_iterator_flag_t; typedef enum { diff --git a/src/mailmap.c b/src/mailmap.c index 38ae01645..4336fe3e5 100644 --- a/src/mailmap.c +++ b/src/mailmap.c @@ -9,13 +9,14 @@ #include "common.h" #include "config.h" -#include "path.h" +#include "fs_path.h" #include "repository.h" #include "signature.h" #include "git2/config.h" #include "git2/revparse.h" #include "blob.h" #include "parse.h" +#include "path.h" #define MM_FILE ".mailmap" #define MM_FILE_CONFIG "mailmap.file" @@ -327,11 +328,11 @@ static int mailmap_add_file_ondisk( git_str content = GIT_STR_INIT; int error; - error = git_path_join_unrooted(&fullpath, path, base, NULL); + error = git_fs_path_join_unrooted(&fullpath, path, base, NULL); if (error < 0) goto cleanup; - error = git_path_validate_workdir_buf(repo, &fullpath); + error = git_path_validate_str_length(repo, &fullpath); if (error < 0) goto cleanup; diff --git a/src/merge.c b/src/merge.c index ae1d453ec..ec7e5bfd1 100644 --- a/src/merge.c +++ b/src/merge.c @@ -12,7 +12,7 @@ #include "repository.h" #include "revwalk.h" #include "commit_list.h" -#include "path.h" +#include "fs_path.h" #include "refs.h" #include "object.h" #include "iterator.h" diff --git a/src/merge.h b/src/merge.h index 3e7f80c6e..632f2d8e3 100644 --- a/src/merge.h +++ b/src/merge.h @@ -29,12 +29,12 @@ /** Internal merge flags. */ enum { /** The merge is for a virtual base in a recursive merge. */ - GIT_MERGE__VIRTUAL_BASE = (1 << 31), + GIT_MERGE__VIRTUAL_BASE = (1 << 31) }; enum { /** Accept the conflict file, staging it as the merge result. */ - GIT_MERGE_FILE_FAVOR__CONFLICTED = 4, + GIT_MERGE_FILE_FAVOR__CONFLICTED = 4 }; @@ -83,7 +83,7 @@ typedef enum { GIT_MERGE_DIFF_DIRECTORY_FILE = (1 << 10), /* The child of a folder that is in a directory/file conflict. */ - GIT_MERGE_DIFF_DF_CHILD = (1 << 11), + GIT_MERGE_DIFF_DF_CHILD = (1 << 11) } git_merge_diff_t; typedef struct { diff --git a/src/midx.c b/src/midx.c index b8da98986..d4f9bd5a0 100644 --- a/src/midx.c +++ b/src/midx.c @@ -14,7 +14,7 @@ #include "hash.h" #include "odb.h" #include "pack.h" -#include "path.h" +#include "fs_path.h" #include "repository.h" #include "str.h" @@ -502,7 +502,7 @@ int git_midx_writer_new( git__free(w); return -1; } - git_path_squash_slashes(&w->pack_dir); + git_fs_path_squash_slashes(&w->pack_dir); if (git_vector_init(&w->packs, 0, packfile__cmp) < 0) { git_str_dispose(&w->pack_dir); @@ -537,7 +537,7 @@ int git_midx_writer_add( int error; struct git_pack_file *p; - error = git_path_prettify(&idx_path_buf, idx_path, git_str_cstr(&w->pack_dir)); + error = git_fs_path_prettify(&idx_path_buf, idx_path, git_str_cstr(&w->pack_dir)); if (error < 0) return error; @@ -687,7 +687,7 @@ static int midx_write( error = git_str_sets(&relative_index, p->pack_name); if (error < 0) goto cleanup; - error = git_path_make_relative(&relative_index, git_str_cstr(&w->pack_dir)); + error = git_fs_path_make_relative(&relative_index, git_str_cstr(&w->pack_dir)); if (error < 0) { git_str_dispose(&relative_index); goto cleanup; diff --git a/src/netops.h b/src/netops.h index 7140b39bc..56f968534 100644 --- a/src/netops.h +++ b/src/netops.h @@ -42,7 +42,7 @@ typedef struct gitno_buffer { /* Flags to gitno_connect */ enum { /* Attempt to create an SSL connection. */ - GITNO_CONNECT_SSL = 1, + GITNO_CONNECT_SSL = 1 }; /** @@ -277,7 +277,7 @@ int git_odb__hashlink(git_oid *out, const char *path) int size; int result; - if (git_path_lstat(path, &st) < 0) + if (git_fs_path_lstat(path, &st) < 0) return -1; if (!git__is_int(st.st_size) || (int)st.st_size < 0) { @@ -649,7 +649,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ if (git_str_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0) return -1; - if (git_path_exists(alternates_path.ptr) == false) { + if (git_fs_path_exists(alternates_path.ptr) == false) { git_str_dispose(&alternates_path); return 0; } @@ -883,6 +883,11 @@ int git_odb__freshen(git_odb *db, const git_oid *id) int git_odb_exists(git_odb *db, const git_oid *id) { + return git_odb_exists_ext(db, id, 0); +} + +int git_odb_exists_ext(git_odb *db, const git_oid *id, unsigned int flags) +{ git_odb_object *object; GIT_ASSERT_ARG(db); @@ -899,7 +904,7 @@ int git_odb_exists(git_odb *db, const git_oid *id) if (odb_exists_1(db, id, false)) return 1; - if (!git_odb_refresh(db)) + if (!(flags & GIT_ODB_LOOKUP_NO_REFRESH) && !git_odb_refresh(db)) return odb_exists_1(db, id, true); /* Failed to refresh, hence not found */ @@ -53,7 +53,7 @@ struct git_odb { }; typedef enum { - GIT_ODB_CAP_FROM_OWNER = -1, + GIT_ODB_CAP_FROM_OWNER = -1 } git_odb_cap_t; /* diff --git a/src/odb_loose.c b/src/odb_loose.c index f0c3ac2c8..463e24fa5 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -87,7 +87,7 @@ static int object_file_name( return -1; git_str_set(name, be->objects_dir, be->objects_dirlen); - git_path_to_dir(name); + git_fs_path_to_dir(name); /* loose object filename: aa/aaa... (41 bytes) */ git_oid_pathfmt(name->ptr + name->size, id); @@ -452,7 +452,7 @@ static int locate_object( { int error = object_file_name(object_location, backend, oid); - if (!error && !git_path_exists(object_location->ptr)) + if (!error && !git_fs_path_exists(object_location->ptr)) return GIT_ENOTFOUND; return error; @@ -467,7 +467,7 @@ static int fn_locate_object_short_oid(void *state, git_str *pathbuf) { return 0; } - if (git_path_isdir(pathbuf->ptr) == false) { + if (git_fs_path_isdir(pathbuf->ptr) == false) { /* We are already in the directory matching the 2 first hex characters, * compare the first ncmp characters of the oids */ if (!memcmp(sstate->short_oid + 2, @@ -509,7 +509,7 @@ static int locate_object_short_oid( return -1; git_str_set(object_location, objects_dir, dir_len); - git_path_to_dir(object_location); + git_fs_path_to_dir(object_location); /* save adjusted position at end of dir so it can be restored later */ dir_len = git_str_len(object_location); @@ -523,7 +523,7 @@ static int locate_object_short_oid( object_location->ptr[object_location->size - 1] = '/'; /* Check that directory exists */ - if (git_path_isdir(object_location->ptr) == false) + if (git_fs_path_isdir(object_location->ptr) == false) return git_odb__error_notfound("no matching loose object for prefix", short_oid, len); @@ -532,7 +532,7 @@ static int locate_object_short_oid( state.found = 0; /* Explore directory to find a unique object matching short_oid */ - error = git_path_direach( + error = git_fs_path_direach( object_location, 0, fn_locate_object_short_oid, &state); if (error < 0 && error != GIT_EAMBIGUOUS) return error; @@ -753,10 +753,10 @@ static int foreach_cb(void *_state, git_str *path) struct foreach_state *state = (struct foreach_state *) _state; /* non-dir is some stray file, ignore it */ - if (!git_path_isdir(git_str_cstr(path))) + if (!git_fs_path_isdir(git_str_cstr(path))) return 0; - return git_path_direach(path, 0, foreach_object_dir_cb, state); + return git_fs_path_direach(path, 0, foreach_object_dir_cb, state); } static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data) @@ -773,7 +773,7 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb objects_dir = backend->objects_dir; git_str_sets(&buf, objects_dir); - git_path_to_dir(&buf); + git_fs_path_to_dir(&buf); if (git_str_oom(&buf)) return -1; @@ -782,7 +782,7 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb state.data = data; state.dir_len = git_str_len(&buf); - error = git_path_direach(&buf, 0, foreach_cb, &state); + error = git_fs_path_direach(&buf, 0, foreach_cb, &state); git_str_dispose(&buf); diff --git a/src/odb_pack.c b/src/odb_pack.c index f2c47adbe..5b7521029 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -524,7 +524,7 @@ static int pack_backend__refresh(git_odb_backend *backend_) /* reload all packs */ git_str_sets(&path, backend->pack_folder); - error = git_path_direach(&path, 0, packfile_load__cb, backend); + error = git_fs_path_direach(&path, 0, packfile_load__cb, backend); git_str_dispose(&path); git_vector_sort(&backend->packs); @@ -750,7 +750,7 @@ static int get_idx_path( size_t path_len; int error; - error = git_path_prettify(idx_path, p->pack_name, backend->pack_folder); + error = git_fs_path_prettify(idx_path, p->pack_name, backend->pack_folder); if (error < 0) return error; path_len = git_str_len(idx_path); @@ -902,7 +902,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) return -1; if (!(error = git_str_joinpath(&path, objects_dir, "pack")) && - git_path_isdir(git_str_cstr(&path))) + git_fs_path_isdir(git_str_cstr(&path))) { backend->pack_folder = git_str_detach(&path); error = pack_backend__refresh((git_odb_backend *)backend); diff --git a/src/pack.c b/src/pack.c index e17d20f8c..5c0cba7e8 100644 --- a/src/pack.c +++ b/src/pack.c @@ -1198,7 +1198,7 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) if (!git_disable_pack_keep_file_checks) { memcpy(p->pack_name + root_len, ".keep", sizeof(".keep")); - if (git_path_exists(p->pack_name) == true) + if (git_fs_path_exists(p->pack_name) == true) p->pack_keep = 1; } diff --git a/src/patch_generate.h b/src/patch_generate.h index 20f78cbfa..0e09d27ca 100644 --- a/src/patch_generate.h +++ b/src/patch_generate.h @@ -21,7 +21,7 @@ enum { GIT_PATCH_GENERATED_DIFFABLE = (1 << 3), /* the difference between the two sides has been computed */ GIT_PATCH_GENERATED_DIFFED = (1 << 4), - GIT_PATCH_GENERATED_FLATTENED = (1 << 5), + GIT_PATCH_GENERATED_FLATTENED = (1 << 5) }; struct git_patch_generated { diff --git a/src/patch_parse.c b/src/patch_parse.c index fce4bc9e4..37822fccc 100644 --- a/src/patch_parse.c +++ b/src/patch_parse.c @@ -10,7 +10,7 @@ #include "git2/patch.h" #include "patch.h" #include "diff_parse.h" -#include "path.h" +#include "fs_path.h" typedef struct { git_patch base; @@ -80,7 +80,7 @@ static int parse_header_path_buf(git_str *path, git_patch_parse_ctx *ctx, size_t (error = git_str_unquote(path)) < 0) return error; - git_path_squash_slashes(path); + git_fs_path_squash_slashes(path); if (!path->size) return git_parse_err("patch contains empty path at line %"PRIuZ, @@ -382,7 +382,7 @@ typedef enum { STATE_RENAME, STATE_COPY, - STATE_END, + STATE_END } parse_header_state; typedef struct { diff --git a/src/path.c b/src/path.c index d8d33a141..05a3dc2cf 100644 --- a/src/path.c +++ b/src/path.c @@ -7,1555 +7,14 @@ #include "path.h" -#include "posix.h" #include "repository.h" -#ifdef GIT_WIN32 -#include "win32/posix.h" -#include "win32/w32_buffer.h" -#include "win32/w32_util.h" -#include "win32/version.h" -#include <aclapi.h> -#else -#include <dirent.h> -#endif -#include <stdio.h> -#include <ctype.h> - -static int dos_drive_prefix_length(const char *path) -{ - int i; - - /* - * Does it start with an ASCII letter (i.e. highest bit not set), - * followed by a colon? - */ - if (!(0x80 & (unsigned char)*path)) - return *path && path[1] == ':' ? 2 : 0; - - /* - * While drive letters must be letters of the English alphabet, it is - * possible to assign virtually _any_ Unicode character via `subst` as - * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff - * like this: - * - * subst ֍: %USERPROFILE%\Desktop - */ - for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++) - ; /* skip first UTF-8 character */ - return path[i] == ':' ? i + 1 : 0; -} +#include "fs_path.h" -#ifdef GIT_WIN32 -static bool looks_like_network_computer_name(const char *path, int pos) -{ - if (pos < 3) - return false; - - if (path[0] != '/' || path[1] != '/') - return false; - - while (pos-- > 2) { - if (path[pos] == '/') - return false; - } - - return true; -} -#endif - -/* - * Based on the Android implementation, BSD licensed. - * http://android.git.kernel.org/ - * - * Copyright (C) 2008 The Android Open Source Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -int git_path_basename_r(git_str *buffer, const char *path) -{ - const char *endp, *startp; - int len, result; - - /* Empty or NULL string gets treated as "." */ - if (path == NULL || *path == '\0') { - startp = "."; - len = 1; - goto Exit; - } - - /* Strip trailing slashes */ - endp = path + strlen(path) - 1; - while (endp > path && *endp == '/') - endp--; - - /* All slashes becomes "/" */ - if (endp == path && *endp == '/') { - startp = "/"; - len = 1; - goto Exit; - } - - /* Find the start of the base */ - startp = endp; - while (startp > path && *(startp - 1) != '/') - startp--; - - /* Cast is safe because max path < max int */ - len = (int)(endp - startp + 1); - -Exit: - result = len; - - if (buffer != NULL && git_str_set(buffer, startp, len) < 0) - return -1; - - return result; -} - -/* - * Determine if the path is a Windows prefix and, if so, returns - * its actual lentgh. If it is not a prefix, returns -1. - */ -static int win32_prefix_length(const char *path, int len) -{ -#ifndef GIT_WIN32 - GIT_UNUSED(path); - GIT_UNUSED(len); -#else - /* - * Mimic unix behavior where '/.git' returns '/': 'C:/.git' - * will return 'C:/' here - */ - if (dos_drive_prefix_length(path) == len) - return len; - - /* - * Similarly checks if we're dealing with a network computer name - * '//computername/.git' will return '//computername/' - */ - if (looks_like_network_computer_name(path, len)) - return len; -#endif - - return -1; -} - -/* - * Based on the Android implementation, BSD licensed. - * Check http://android.git.kernel.org/ - */ -int git_path_dirname_r(git_str *buffer, const char *path) -{ - const char *endp; - int is_prefix = 0, len; - - /* Empty or NULL string gets treated as "." */ - if (path == NULL || *path == '\0') { - path = "."; - len = 1; - goto Exit; - } - - /* Strip trailing slashes */ - endp = path + strlen(path) - 1; - while (endp > path && *endp == '/') - endp--; - - if (endp - path + 1 > INT_MAX) { - git_error_set(GIT_ERROR_INVALID, "path too long"); - len = -1; - goto Exit; - } - - if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) { - is_prefix = 1; - goto Exit; - } - - /* Find the start of the dir */ - while (endp > path && *endp != '/') - endp--; - - /* Either the dir is "/" or there are no slashes */ - if (endp == path) { - path = (*endp == '/') ? "/" : "."; - len = 1; - goto Exit; - } - - do { - endp--; - } while (endp > path && *endp == '/'); - - if (endp - path + 1 > INT_MAX) { - git_error_set(GIT_ERROR_INVALID, "path too long"); - len = -1; - goto Exit; - } - - if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) { - is_prefix = 1; - goto Exit; - } - - /* Cast is safe because max path < max int */ - len = (int)(endp - path + 1); - -Exit: - if (buffer) { - if (git_str_set(buffer, path, len) < 0) - return -1; - if (is_prefix && git_str_putc(buffer, '/') < 0) - return -1; - } - - return len; -} - - -char *git_path_dirname(const char *path) -{ - git_str buf = GIT_STR_INIT; - char *dirname; - - git_path_dirname_r(&buf, path); - dirname = git_str_detach(&buf); - git_str_dispose(&buf); /* avoid memleak if error occurs */ - - return dirname; -} - -char *git_path_basename(const char *path) -{ - git_str buf = GIT_STR_INIT; - char *basename; - - git_path_basename_r(&buf, path); - basename = git_str_detach(&buf); - git_str_dispose(&buf); /* avoid memleak if error occurs */ - - return basename; -} - -size_t git_path_basename_offset(git_str *buffer) -{ - ssize_t slash; - - if (!buffer || buffer->size <= 0) - return 0; - - slash = git_str_rfind_next(buffer, '/'); - - if (slash >= 0 && buffer->ptr[slash] == '/') - return (size_t)(slash + 1); - - return 0; -} - -int git_path_root(const char *path) -{ - int offset = 0, prefix_len; - - /* Does the root of the path look like a windows drive ? */ - if ((prefix_len = dos_drive_prefix_length(path))) - offset += prefix_len; - -#ifdef GIT_WIN32 - /* Are we dealing with a windows network path? */ - else if ((path[0] == '/' && path[1] == '/' && path[2] != '/') || - (path[0] == '\\' && path[1] == '\\' && path[2] != '\\')) - { - offset += 2; - - /* Skip the computer name segment */ - while (path[offset] && path[offset] != '/' && path[offset] != '\\') - offset++; - } - - if (path[offset] == '\\') - return offset; -#endif - - if (path[offset] == '/') - return offset; - - return -1; /* Not a real error - signals that path is not rooted */ -} - -static void path_trim_slashes(git_str *path) -{ - int ceiling = git_path_root(path->ptr) + 1; - - if (ceiling < 0) - return; - - while (path->size > (size_t)ceiling) { - if (path->ptr[path->size-1] != '/') - break; - - path->ptr[path->size-1] = '\0'; - path->size--; - } -} - -int git_path_join_unrooted( - git_str *path_out, const char *path, const char *base, ssize_t *root_at) -{ - ssize_t root; - - GIT_ASSERT_ARG(path_out); - GIT_ASSERT_ARG(path); - - root = (ssize_t)git_path_root(path); - - if (base != NULL && root < 0) { - if (git_str_joinpath(path_out, base, path) < 0) - return -1; - - root = (ssize_t)strlen(base); - } else { - if (git_str_sets(path_out, path) < 0) - return -1; - - if (root < 0) - root = 0; - else if (base) - git_path_equal_or_prefixed(base, path, &root); - } - - if (root_at) - *root_at = root; - - return 0; -} - -void git_path_squash_slashes(git_str *path) -{ - char *p, *q; - - if (path->size == 0) - return; - - for (p = path->ptr, q = path->ptr; *q; p++, q++) { - *p = *q; - - while (*q == '/' && *(q+1) == '/') { - path->size--; - q++; - } - } - - *p = '\0'; -} - -int git_path_prettify(git_str *path_out, const char *path, const char *base) -{ - char buf[GIT_PATH_MAX]; - - GIT_ASSERT_ARG(path_out); - GIT_ASSERT_ARG(path); - - /* construct path if needed */ - if (base != NULL && git_path_root(path) < 0) { - if (git_str_joinpath(path_out, base, path) < 0) - return -1; - path = path_out->ptr; - } - - if (p_realpath(path, buf) == NULL) { - /* git_error_set resets the errno when dealing with a GIT_ERROR_OS kind of error */ - int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1; - git_error_set(GIT_ERROR_OS, "failed to resolve path '%s'", path); - - git_str_clear(path_out); - - return error; - } - - return git_str_sets(path_out, buf); -} - -int git_path_prettify_dir(git_str *path_out, const char *path, const char *base) -{ - int error = git_path_prettify(path_out, path, base); - return (error < 0) ? error : git_path_to_dir(path_out); -} - -int git_path_to_dir(git_str *path) -{ - if (path->asize > 0 && - git_str_len(path) > 0 && - path->ptr[git_str_len(path) - 1] != '/') - git_str_putc(path, '/'); - - return git_str_oom(path) ? -1 : 0; -} - -void git_path_string_to_dir(char *path, size_t size) -{ - size_t end = strlen(path); - - if (end && path[end - 1] != '/' && end < size) { - path[end] = '/'; - path[end + 1] = '\0'; - } -} - -int git__percent_decode(git_str *decoded_out, const char *input) -{ - int len, hi, lo, i; - - GIT_ASSERT_ARG(decoded_out); - GIT_ASSERT_ARG(input); - - len = (int)strlen(input); - git_str_clear(decoded_out); - - for(i = 0; i < len; i++) - { - char c = input[i]; - - if (c != '%') - goto append; - - if (i >= len - 2) - goto append; - - hi = git__fromhex(input[i + 1]); - lo = git__fromhex(input[i + 2]); - - if (hi < 0 || lo < 0) - goto append; - - c = (char)(hi << 4 | lo); - i += 2; - -append: - if (git_str_putc(decoded_out, c) < 0) - return -1; - } - - return 0; -} - -static int error_invalid_local_file_uri(const char *uri) -{ - git_error_set(GIT_ERROR_CONFIG, "'%s' is not a valid local file URI", uri); - return -1; -} - -static int local_file_url_prefixlen(const char *file_url) -{ - int len = -1; - - if (git__prefixcmp(file_url, "file://") == 0) { - if (file_url[7] == '/') - len = 8; - else if (git__prefixcmp(file_url + 7, "localhost/") == 0) - len = 17; - } - - return len; -} - -bool git_path_is_local_file_url(const char *file_url) -{ - return (local_file_url_prefixlen(file_url) > 0); -} - -int git_path_fromurl(git_str *local_path_out, const char *file_url) -{ - int offset; - - GIT_ASSERT_ARG(local_path_out); - GIT_ASSERT_ARG(file_url); - - if ((offset = local_file_url_prefixlen(file_url)) < 0 || - file_url[offset] == '\0' || file_url[offset] == '/') - return error_invalid_local_file_uri(file_url); - -#ifndef GIT_WIN32 - offset--; /* A *nix absolute path starts with a forward slash */ -#endif - - git_str_clear(local_path_out); - return git__percent_decode(local_path_out, file_url + offset); -} - -int git_path_walk_up( - git_str *path, - const char *ceiling, - int (*cb)(void *data, const char *), - void *data) -{ - int error = 0; - git_str iter; - ssize_t stop = 0, scan; - char oldc = '\0'; - - GIT_ASSERT_ARG(path); - GIT_ASSERT_ARG(cb); - - if (ceiling != NULL) { - if (git__prefixcmp(path->ptr, ceiling) == 0) - stop = (ssize_t)strlen(ceiling); - else - stop = git_str_len(path); - } - scan = git_str_len(path); - - /* empty path: yield only once */ - if (!scan) { - error = cb(data, ""); - if (error) - git_error_set_after_callback(error); - return error; - } - - iter.ptr = path->ptr; - iter.size = git_str_len(path); - iter.asize = path->asize; - - while (scan >= stop) { - error = cb(data, iter.ptr); - iter.ptr[scan] = oldc; - - if (error) { - git_error_set_after_callback(error); - break; - } - - scan = git_str_rfind_next(&iter, '/'); - if (scan >= 0) { - scan++; - oldc = iter.ptr[scan]; - iter.size = scan; - iter.ptr[scan] = '\0'; - } - } - - if (scan >= 0) - iter.ptr[scan] = oldc; - - /* relative path: yield for the last component */ - if (!error && stop == 0 && iter.ptr[0] != '/') { - error = cb(data, ""); - if (error) - git_error_set_after_callback(error); - } - - return error; -} - -bool git_path_exists(const char *path) -{ - GIT_ASSERT_ARG_WITH_RETVAL(path, false); - return p_access(path, F_OK) == 0; -} - -bool git_path_isdir(const char *path) -{ - struct stat st; - if (p_stat(path, &st) < 0) - return false; - - return S_ISDIR(st.st_mode) != 0; -} - -bool git_path_isfile(const char *path) -{ - struct stat st; - - GIT_ASSERT_ARG_WITH_RETVAL(path, false); - if (p_stat(path, &st) < 0) - return false; - - return S_ISREG(st.st_mode) != 0; -} - -bool git_path_islink(const char *path) -{ - struct stat st; - - GIT_ASSERT_ARG_WITH_RETVAL(path, false); - if (p_lstat(path, &st) < 0) - return false; - - return S_ISLNK(st.st_mode) != 0; -} - -#ifdef GIT_WIN32 - -bool git_path_is_empty_dir(const char *path) -{ - git_win32_path filter_w; - bool empty = false; - - if (git_win32__findfirstfile_filter(filter_w, path)) { - WIN32_FIND_DATAW findData; - HANDLE hFind = FindFirstFileW(filter_w, &findData); - - /* FindFirstFile will fail if there are no children to the given - * path, which can happen if the given path is a file (and obviously - * has no children) or if the given path is an empty mount point. - * (Most directories have at least directory entries '.' and '..', - * but ridiculously another volume mounted in another drive letter's - * path space do not, and thus have nothing to enumerate.) If - * FindFirstFile fails, check if this is a directory-like thing - * (a mount point). - */ - if (hFind == INVALID_HANDLE_VALUE) - return git_path_isdir(path); - - /* If the find handle was created successfully, then it's a directory */ - empty = true; - - do { - /* Allow the enumeration to return . and .. and still be considered - * empty. In the special case of drive roots (i.e. C:\) where . and - * .. do not occur, we can still consider the path to be an empty - * directory if there's nothing there. */ - if (!git_path_is_dot_or_dotdotW(findData.cFileName)) { - empty = false; - break; - } - } while (FindNextFileW(hFind, &findData)); - - FindClose(hFind); - } - - return empty; -} - -#else - -static int path_found_entry(void *payload, git_str *path) -{ - GIT_UNUSED(payload); - return !git_path_is_dot_or_dotdot(path->ptr); -} - -bool git_path_is_empty_dir(const char *path) -{ - int error; - git_str dir = GIT_STR_INIT; - - if (!git_path_isdir(path)) - return false; - - if ((error = git_str_sets(&dir, path)) != 0) - git_error_clear(); - else - error = git_path_direach(&dir, 0, path_found_entry, NULL); - - git_str_dispose(&dir); - - return !error; -} - -#endif - -int git_path_set_error(int errno_value, const char *path, const char *action) -{ - switch (errno_value) { - case ENOENT: - case ENOTDIR: - git_error_set(GIT_ERROR_OS, "could not find '%s' to %s", path, action); - return GIT_ENOTFOUND; - - case EINVAL: - case ENAMETOOLONG: - git_error_set(GIT_ERROR_OS, "invalid path for filesystem '%s'", path); - return GIT_EINVALIDSPEC; - - case EEXIST: - git_error_set(GIT_ERROR_OS, "failed %s - '%s' already exists", action, path); - return GIT_EEXISTS; - - case EACCES: - git_error_set(GIT_ERROR_OS, "failed %s - '%s' is locked", action, path); - return GIT_ELOCKED; - - default: - git_error_set(GIT_ERROR_OS, "could not %s '%s'", action, path); - return -1; - } -} - -int git_path_lstat(const char *path, struct stat *st) -{ - if (p_lstat(path, st) == 0) - return 0; - - return git_path_set_error(errno, path, "stat"); -} - -static bool _check_dir_contents( - git_str *dir, - const char *sub, - bool (*predicate)(const char *)) -{ - bool result; - size_t dir_size = git_str_len(dir); - size_t sub_size = strlen(sub); - size_t alloc_size; - - /* leave base valid even if we could not make space for subdir */ - if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) || - GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) || - git_str_try_grow(dir, alloc_size, false) < 0) - return false; - - /* save excursion */ - if (git_str_joinpath(dir, dir->ptr, sub) < 0) - return false; - - result = predicate(dir->ptr); - - /* restore path */ - git_str_truncate(dir, dir_size); - return result; -} - -bool git_path_contains(git_str *dir, const char *item) -{ - return _check_dir_contents(dir, item, &git_path_exists); -} - -bool git_path_contains_dir(git_str *base, const char *subdir) -{ - return _check_dir_contents(base, subdir, &git_path_isdir); -} - -bool git_path_contains_file(git_str *base, const char *file) -{ - return _check_dir_contents(base, file, &git_path_isfile); -} - -int git_path_find_dir(git_str *dir) -{ - int error = 0; - char buf[GIT_PATH_MAX]; - - if (p_realpath(dir->ptr, buf) != NULL) - error = git_str_sets(dir, buf); - - /* call dirname if this is not a directory */ - if (!error) /* && git_path_isdir(dir->ptr) == false) */ - error = (git_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0; - - if (!error) - error = git_path_to_dir(dir); - - return error; -} - -int git_path_resolve_relative(git_str *path, size_t ceiling) -{ - char *base, *to, *from, *next; - size_t len; - - GIT_ERROR_CHECK_ALLOC_STR(path); - - if (ceiling > path->size) - ceiling = path->size; - - /* recognize drive prefixes, etc. that should not be backed over */ - if (ceiling == 0) - ceiling = git_path_root(path->ptr) + 1; - - /* recognize URL prefixes that should not be backed over */ - if (ceiling == 0) { - for (next = path->ptr; *next && git__isalpha(*next); ++next); - if (next[0] == ':' && next[1] == '/' && next[2] == '/') - ceiling = (next + 3) - path->ptr; - } - - base = to = from = path->ptr + ceiling; - - while (*from) { - for (next = from; *next && *next != '/'; ++next); - - len = next - from; - - if (len == 1 && from[0] == '.') - /* do nothing with singleton dot */; - - else if (len == 2 && from[0] == '.' && from[1] == '.') { - /* error out if trying to up one from a hard base */ - if (to == base && ceiling != 0) { - git_error_set(GIT_ERROR_INVALID, - "cannot strip root component off url"); - return -1; - } - - /* no more path segments to strip, - * use '../' as a new base path */ - if (to == base) { - if (*next == '/') - len++; - - if (to != from) - memmove(to, from, len); - - to += len; - /* this is now the base, can't back up from a - * relative prefix */ - base = to; - } else { - /* back up a path segment */ - while (to > base && to[-1] == '/') to--; - while (to > base && to[-1] != '/') to--; - } - } else { - if (*next == '/' && *from != '/') - len++; - - if (to != from) - memmove(to, from, len); - - to += len; - } - - from += len; - - while (*from == '/') from++; - } - - *to = '\0'; - - path->size = to - path->ptr; - - return 0; -} - -int git_path_apply_relative(git_str *target, const char *relpath) -{ - return git_str_joinpath(target, git_str_cstr(target), relpath) || - git_path_resolve_relative(target, 0); -} - -int git_path_cmp( - const char *name1, size_t len1, int isdir1, - const char *name2, size_t len2, int isdir2, - int (*compare)(const char *, const char *, size_t)) -{ - unsigned char c1, c2; - size_t len = len1 < len2 ? len1 : len2; - int cmp; - - cmp = compare(name1, name2, len); - if (cmp) - return cmp; - - c1 = name1[len]; - c2 = name2[len]; - - if (c1 == '\0' && isdir1) - c1 = '/'; - - if (c2 == '\0' && isdir2) - c2 = '/'; - - return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; -} - -size_t git_path_common_dirlen(const char *one, const char *two) -{ - const char *p, *q, *dirsep = NULL; - - for (p = one, q = two; *p && *q; p++, q++) { - if (*p == '/' && *q == '/') - dirsep = p; - else if (*p != *q) - break; - } - - return dirsep ? (dirsep - one) + 1 : 0; -} - -int git_path_make_relative(git_str *path, const char *parent) -{ - const char *p, *q, *p_dirsep, *q_dirsep; - size_t plen = path->size, newlen, alloclen, depth = 1, i, offset; - - for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) { - if (*p == '/' && *q == '/') { - p_dirsep = p; - q_dirsep = q; - } - else if (*p != *q) - break; - } - - /* need at least 1 common path segment */ - if ((p_dirsep == path->ptr || q_dirsep == parent) && - (*p_dirsep != '/' || *q_dirsep != '/')) { - git_error_set(GIT_ERROR_INVALID, - "%s is not a parent of %s", parent, path->ptr); - return GIT_ENOTFOUND; - } - - if (*p == '/' && !*q) - p++; - else if (!*p && *q == '/') - q++; - else if (!*p && !*q) - return git_str_clear(path), 0; - else { - p = p_dirsep + 1; - q = q_dirsep + 1; - } - - plen -= (p - path->ptr); - - if (!*q) - return git_str_set(path, p, plen); - - for (; (q = strchr(q, '/')) && *(q + 1); q++) - depth++; - - GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3); - GIT_ERROR_CHECK_ALLOC_ADD(&newlen, newlen, plen); - - GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, newlen, 1); - - /* save the offset as we might realllocate the pointer */ - offset = p - path->ptr; - if (git_str_try_grow(path, alloclen, 1) < 0) - return -1; - p = path->ptr + offset; - - memmove(path->ptr + (depth * 3), p, plen + 1); - - for (i = 0; i < depth; i++) - memcpy(path->ptr + (i * 3), "../", 3); - - path->size = newlen; - return 0; -} - -bool git_path_has_non_ascii(const char *path, size_t pathlen) -{ - const uint8_t *scan = (const uint8_t *)path, *end; - - for (end = scan + pathlen; scan < end; ++scan) - if (*scan & 0x80) - return true; - - return false; -} - -#ifdef GIT_USE_ICONV - -int git_path_iconv_init_precompose(git_path_iconv_t *ic) -{ - git_str_init(&ic->buf, 0); - ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING); - return 0; -} - -void git_path_iconv_clear(git_path_iconv_t *ic) -{ - if (ic) { - if (ic->map != (iconv_t)-1) - iconv_close(ic->map); - git_str_dispose(&ic->buf); - } -} - -int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen) -{ - char *nfd = (char*)*in, *nfc; - size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv; - int retry = 1; - - if (!ic || ic->map == (iconv_t)-1 || - !git_path_has_non_ascii(*in, *inlen)) - return 0; - - git_str_clear(&ic->buf); - - while (1) { - GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, wantlen, 1); - if (git_str_grow(&ic->buf, alloclen) < 0) - return -1; - - nfc = ic->buf.ptr + ic->buf.size; - nfclen = ic->buf.asize - ic->buf.size; - - rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen); - - ic->buf.size = (nfc - ic->buf.ptr); - - if (rv != (size_t)-1) - break; - - /* if we cannot convert the data (probably because iconv thinks - * it is not valid UTF-8 source data), then use original data - */ - if (errno != E2BIG) - return 0; - - /* make space for 2x the remaining data to be converted - * (with per retry overhead to avoid infinite loops) - */ - wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4); - - if (retry++ > 4) - goto fail; - } - - ic->buf.ptr[ic->buf.size] = '\0'; - - *in = ic->buf.ptr; - *inlen = ic->buf.size; - - return 0; - -fail: - git_error_set(GIT_ERROR_OS, "unable to convert unicode path data"); - return -1; -} - -static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX"; -static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX"; - -/* Check if the platform is decomposing unicode data for us. We will - * emulate core Git and prefer to use precomposed unicode data internally - * on these platforms, composing the decomposed unicode on the fly. - * - * This mainly happens on the Mac where HDFS stores filenames as - * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will - * return decomposed unicode from readdir() even when the actual - * filesystem is storing precomposed unicode. - */ -bool git_path_does_fs_decompose_unicode(const char *root) -{ - git_str path = GIT_STR_INIT; - int fd; - bool found_decomposed = false; - char tmp[6]; - - /* Create a file using a precomposed path and then try to find it - * using the decomposed name. If the lookup fails, then we will mark - * that we should precompose unicode for this repository. - */ - if (git_str_joinpath(&path, root, nfc_file) < 0 || - (fd = p_mkstemp(path.ptr)) < 0) - goto done; - p_close(fd); - - /* record trailing digits generated by mkstemp */ - memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp)); - - /* try to look up as NFD path */ - if (git_str_joinpath(&path, root, nfd_file) < 0) - goto done; - memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); - - found_decomposed = git_path_exists(path.ptr); - - /* remove temporary file (using original precomposed path) */ - if (git_str_joinpath(&path, root, nfc_file) < 0) - goto done; - memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); - - (void)p_unlink(path.ptr); - -done: - git_str_dispose(&path); - return found_decomposed; -} - -#else - -bool git_path_does_fs_decompose_unicode(const char *root) -{ - GIT_UNUSED(root); - return false; -} - -#endif - -#if defined(__sun) || defined(__GNU__) -typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1]; -#else -typedef struct dirent path_dirent_data; -#endif - -int git_path_direach( - git_str *path, - uint32_t flags, - int (*fn)(void *, git_str *), - void *arg) -{ - int error = 0; - ssize_t wd_len; - DIR *dir; - struct dirent *de; - -#ifdef GIT_USE_ICONV - git_path_iconv_t ic = GIT_PATH_ICONV_INIT; -#endif - - GIT_UNUSED(flags); - - if (git_path_to_dir(path) < 0) - return -1; - - wd_len = git_str_len(path); - - if ((dir = opendir(path->ptr)) == NULL) { - git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path->ptr); - if (errno == ENOENT) - return GIT_ENOTFOUND; - - return -1; - } - -#ifdef GIT_USE_ICONV - if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0) - (void)git_path_iconv_init_precompose(&ic); -#endif - - while ((de = readdir(dir)) != NULL) { - const char *de_path = de->d_name; - size_t de_len = strlen(de_path); - - if (git_path_is_dot_or_dotdot(de_path)) - continue; - -#ifdef GIT_USE_ICONV - if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0) - break; -#endif - - if ((error = git_str_put(path, de_path, de_len)) < 0) - break; - - git_error_clear(); - error = fn(arg, path); - - git_str_truncate(path, wd_len); /* restore path */ - - /* Only set our own error if the callback did not set one already */ - if (error != 0) { - if (!git_error_last()) - git_error_set_after_callback(error); - - break; - } - } - - closedir(dir); - -#ifdef GIT_USE_ICONV - git_path_iconv_clear(&ic); -#endif - - return error; -} - -#if defined(GIT_WIN32) && !defined(__MINGW32__) - -/* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7 - * and better. - */ -#ifndef FIND_FIRST_EX_LARGE_FETCH -# define FIND_FIRST_EX_LARGE_FETCH 2 -#endif - -int git_path_diriter_init( - git_path_diriter *diriter, - const char *path, - unsigned int flags) -{ - git_win32_path path_filter; - - static int is_win7_or_later = -1; - if (is_win7_or_later < 0) - is_win7_or_later = git_has_win32_version(6, 1, 0); - - GIT_ASSERT_ARG(diriter); - GIT_ASSERT_ARG(path); - - memset(diriter, 0, sizeof(git_path_diriter)); - diriter->handle = INVALID_HANDLE_VALUE; - - if (git_str_puts(&diriter->path_utf8, path) < 0) - return -1; - - path_trim_slashes(&diriter->path_utf8); - - if (diriter->path_utf8.size == 0) { - git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path); - return -1; - } - - if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 || - !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) { - git_error_set(GIT_ERROR_OS, "could not parse the directory path '%s'", path); - return -1; - } - - diriter->handle = FindFirstFileExW( - path_filter, - is_win7_or_later ? FindExInfoBasic : FindExInfoStandard, - &diriter->current, - FindExSearchNameMatch, - NULL, - is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0); - - if (diriter->handle == INVALID_HANDLE_VALUE) { - git_error_set(GIT_ERROR_OS, "could not open directory '%s'", path); - return -1; - } - - diriter->parent_utf8_len = diriter->path_utf8.size; - diriter->flags = flags; - return 0; -} - -static int diriter_update_paths(git_path_diriter *diriter) -{ - size_t filename_len, path_len; - - filename_len = wcslen(diriter->current.cFileName); - - if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) || - GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2)) - return -1; - - if (path_len > GIT_WIN_PATH_UTF16) { - git_error_set(GIT_ERROR_FILESYSTEM, - "invalid path '%.*ls\\%ls' (path too long)", - diriter->parent_len, diriter->path, diriter->current.cFileName); - return -1; - } - - diriter->path[diriter->parent_len] = L'\\'; - memcpy(&diriter->path[diriter->parent_len+1], - diriter->current.cFileName, filename_len * sizeof(wchar_t)); - diriter->path[path_len-1] = L'\0'; - - git_str_truncate(&diriter->path_utf8, diriter->parent_utf8_len); - - if (diriter->parent_utf8_len > 0 && - diriter->path_utf8.ptr[diriter->parent_utf8_len-1] != '/') - git_str_putc(&diriter->path_utf8, '/'); - - git_str_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len); - - if (git_str_oom(&diriter->path_utf8)) - return -1; - - return 0; -} - -int git_path_diriter_next(git_path_diriter *diriter) -{ - bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT); - - do { - /* Our first time through, we already have the data from - * FindFirstFileW. Use it, otherwise get the next file. - */ - if (!diriter->needs_next) - diriter->needs_next = 1; - else if (!FindNextFileW(diriter->handle, &diriter->current)) - return GIT_ITEROVER; - } while (skip_dot && git_path_is_dot_or_dotdotW(diriter->current.cFileName)); - - if (diriter_update_paths(diriter) < 0) - return -1; - - return 0; -} - -int git_path_diriter_filename( - const char **out, - size_t *out_len, - git_path_diriter *diriter) -{ - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(out_len); - GIT_ASSERT_ARG(diriter); - GIT_ASSERT(diriter->path_utf8.size > diriter->parent_utf8_len); - - *out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1]; - *out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1; - return 0; -} - -int git_path_diriter_fullpath( - const char **out, - size_t *out_len, - git_path_diriter *diriter) -{ - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(out_len); - GIT_ASSERT_ARG(diriter); - - *out = diriter->path_utf8.ptr; - *out_len = diriter->path_utf8.size; - return 0; -} - -int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter) -{ - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(diriter); - - return git_win32__file_attribute_to_stat(out, - (WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current, - diriter->path); -} - -void git_path_diriter_free(git_path_diriter *diriter) -{ - if (diriter == NULL) - return; - - git_str_dispose(&diriter->path_utf8); - - if (diriter->handle != INVALID_HANDLE_VALUE) { - FindClose(diriter->handle); - diriter->handle = INVALID_HANDLE_VALUE; - } -} - -#else - -int git_path_diriter_init( - git_path_diriter *diriter, - const char *path, - unsigned int flags) -{ - GIT_ASSERT_ARG(diriter); - GIT_ASSERT_ARG(path); - - memset(diriter, 0, sizeof(git_path_diriter)); - - if (git_str_puts(&diriter->path, path) < 0) - return -1; - - path_trim_slashes(&diriter->path); - - if (diriter->path.size == 0) { - git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path); - return -1; - } - - if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) { - git_str_dispose(&diriter->path); - - git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path); - return -1; - } - -#ifdef GIT_USE_ICONV - if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0) - (void)git_path_iconv_init_precompose(&diriter->ic); -#endif - - diriter->parent_len = diriter->path.size; - diriter->flags = flags; - - return 0; -} - -int git_path_diriter_next(git_path_diriter *diriter) -{ - struct dirent *de; - const char *filename; - size_t filename_len; - bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT); - int error = 0; - - GIT_ASSERT_ARG(diriter); - - errno = 0; - - do { - if ((de = readdir(diriter->dir)) == NULL) { - if (!errno) - return GIT_ITEROVER; - - git_error_set(GIT_ERROR_OS, - "could not read directory '%s'", diriter->path.ptr); - return -1; - } - } while (skip_dot && git_path_is_dot_or_dotdot(de->d_name)); - - filename = de->d_name; - filename_len = strlen(filename); - -#ifdef GIT_USE_ICONV - if ((diriter->flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0 && - (error = git_path_iconv(&diriter->ic, &filename, &filename_len)) < 0) - return error; -#endif - - git_str_truncate(&diriter->path, diriter->parent_len); - - if (diriter->parent_len > 0 && - diriter->path.ptr[diriter->parent_len-1] != '/') - git_str_putc(&diriter->path, '/'); - - git_str_put(&diriter->path, filename, filename_len); - - if (git_str_oom(&diriter->path)) - return -1; - - return error; -} - -int git_path_diriter_filename( - const char **out, - size_t *out_len, - git_path_diriter *diriter) -{ - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(out_len); - GIT_ASSERT_ARG(diriter); - GIT_ASSERT(diriter->path.size > diriter->parent_len); - - *out = &diriter->path.ptr[diriter->parent_len+1]; - *out_len = diriter->path.size - diriter->parent_len - 1; - return 0; -} - -int git_path_diriter_fullpath( - const char **out, - size_t *out_len, - git_path_diriter *diriter) -{ - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(out_len); - GIT_ASSERT_ARG(diriter); - - *out = diriter->path.ptr; - *out_len = diriter->path.size; - return 0; -} - -int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter) -{ - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(diriter); - - return git_path_lstat(diriter->path.ptr, out); -} - -void git_path_diriter_free(git_path_diriter *diriter) -{ - if (diriter == NULL) - return; - - if (diriter->dir) { - closedir(diriter->dir); - diriter->dir = NULL; - } - -#ifdef GIT_USE_ICONV - git_path_iconv_clear(&diriter->ic); -#endif - - git_str_dispose(&diriter->path); -} - -#endif - -int git_path_dirload( - git_vector *contents, - const char *path, - size_t prefix_len, - uint32_t flags) -{ - git_path_diriter iter = GIT_PATH_DIRITER_INIT; - const char *name; - size_t name_len; - char *dup; - int error; - - GIT_ASSERT_ARG(contents); - GIT_ASSERT_ARG(path); - - if ((error = git_path_diriter_init(&iter, path, flags)) < 0) - return error; - - while ((error = git_path_diriter_next(&iter)) == 0) { - if ((error = git_path_diriter_fullpath(&name, &name_len, &iter)) < 0) - break; - - GIT_ASSERT(name_len > prefix_len); - - dup = git__strndup(name + prefix_len, name_len - prefix_len); - GIT_ERROR_CHECK_ALLOC(dup); - - if ((error = git_vector_insert(contents, dup)) < 0) - break; - } - - if (error == GIT_ITEROVER) - error = 0; - - git_path_diriter_free(&iter); - return error; -} - -int git_path_from_url_or_path(git_str *local_path_out, const char *url_or_path) -{ - if (git_path_is_local_file_url(url_or_path)) - return git_path_fromurl(local_path_out, url_or_path); - else - return git_str_sets(local_path_out, url_or_path); -} - -/* Reject paths like AUX or COM1, or those versions that end in a dot or - * colon. ("AUX." or "AUX:") - */ -GIT_INLINE(bool) verify_dospath( - const char *component, - size_t len, - const char dospath[3], - bool trailing_num) -{ - size_t last = trailing_num ? 4 : 3; - - if (len < last || git__strncasecmp(component, dospath, 3) != 0) - return true; - - if (trailing_num && (component[3] < '1' || component[3] > '9')) - return true; - - return (len > last && - component[last] != '.' && - component[last] != ':'); -} +typedef struct { + git_repository *repo; + uint16_t file_mode; + unsigned int flags; +} repository_path_validate_data; static int32_t next_hfs_char(const char **in, size_t *len) { @@ -1598,7 +57,11 @@ static int32_t next_hfs_char(const char **in, size_t *len) return 0; /* NULL byte -- end of string */ } -static bool verify_dotgit_hfs_generic(const char *path, size_t len, const char *needle, size_t needle_len) +static bool validate_dotgit_hfs_generic( + const char *path, + size_t len, + const char *needle, + size_t needle_len) { size_t i; char c; @@ -1618,12 +81,15 @@ static bool verify_dotgit_hfs_generic(const char *path, size_t len, const char * return false; } -static bool verify_dotgit_hfs(const char *path, size_t len) +static bool validate_dotgit_hfs(const char *path, size_t len) { - return verify_dotgit_hfs_generic(path, len, "git", CONST_STRLEN("git")); + return validate_dotgit_hfs_generic(path, len, "git", CONST_STRLEN("git")); } -GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len) +GIT_INLINE(bool) validate_dotgit_ntfs( + git_repository *repo, + const char *path, + size_t len) { git_str *reserved = git_repository__reserved_names_win32; size_t reserved_len = git_repository__reserved_names_win32_len; @@ -1685,7 +151,12 @@ GIT_INLINE(bool) ntfs_end_of_filename(const char *path) return true; } -GIT_INLINE(bool) verify_dotgit_ntfs_generic(const char *name, size_t len, const char *dotgit_name, size_t dotgit_len, const char *shortname_pfix) +GIT_INLINE(bool) validate_dotgit_ntfs_generic( + const char *name, + size_t len, + const char *dotgit_name, + size_t dotgit_len, + const char *shortname_pfix) { int i, saw_tilde; @@ -1722,33 +193,6 @@ GIT_INLINE(bool) verify_dotgit_ntfs_generic(const char *name, size_t len, const return !ntfs_end_of_filename(name + i); } -GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags) -{ - if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\') - return false; - - if ((flags & GIT_PATH_REJECT_SLASH) && c == '/') - return false; - - if (flags & GIT_PATH_REJECT_NT_CHARS) { - if (c < 32) - return false; - - switch (c) { - case '<': - case '>': - case ':': - case '"': - case '|': - case '?': - case '*': - return false; - } - } - - return true; -} - /* * Return the length of the common prefix between str and prefix, comparing them * case-insensitively (must be ASCII to match). @@ -1757,7 +201,7 @@ GIT_INLINE(size_t) common_prefix_icase(const char *str, size_t len, const char * { size_t count = 0; - while (len >0 && tolower(*str) == tolower(*prefix)) { + while (len > 0 && tolower(*str) == tolower(*prefix)) { count++; str++; prefix++; @@ -1767,72 +211,37 @@ GIT_INLINE(size_t) common_prefix_icase(const char *str, size_t len, const char * return count; } -/* - * We fundamentally don't like some paths when dealing with user-inputted - * strings (in checkout or ref names): we don't want dot or dot-dot - * anywhere, we want to avoid writing weird paths on Windows that can't - * be handled by tools that use the non-\\?\ APIs, we don't want slashes - * or double slashes at the end of paths that can make them ambiguous. - * - * For checkout, we don't want to recurse into ".git" either. - */ -static bool verify_component( - git_repository *repo, +static bool validate_repo_component( const char *component, size_t len, - uint16_t mode, - unsigned int flags) + void *payload) { - if (len == 0) - return false; + repository_path_validate_data *data = (repository_path_validate_data *)payload; - if ((flags & GIT_PATH_REJECT_TRAVERSAL) && - len == 1 && component[0] == '.') - return false; - - if ((flags & GIT_PATH_REJECT_TRAVERSAL) && - len == 2 && component[0] == '.' && component[1] == '.') - return false; - - if ((flags & GIT_PATH_REJECT_TRAILING_DOT) && component[len-1] == '.') - return false; - - if ((flags & GIT_PATH_REJECT_TRAILING_SPACE) && component[len-1] == ' ') - return false; - - if ((flags & GIT_PATH_REJECT_TRAILING_COLON) && component[len-1] == ':') - return false; - - if (flags & GIT_PATH_REJECT_DOS_PATHS) { - if (!verify_dospath(component, len, "CON", false) || - !verify_dospath(component, len, "PRN", false) || - !verify_dospath(component, len, "AUX", false) || - !verify_dospath(component, len, "NUL", false) || - !verify_dospath(component, len, "COM", true) || - !verify_dospath(component, len, "LPT", true)) + if (data->flags & GIT_PATH_REJECT_DOT_GIT_HFS) { + if (!validate_dotgit_hfs(component, len)) return false; - } - if (flags & GIT_PATH_REJECT_DOT_GIT_HFS) { - if (!verify_dotgit_hfs(component, len)) - return false; - if (S_ISLNK(mode) && git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_HFS)) + if (S_ISLNK(data->file_mode) && + git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_HFS)) return false; } - if (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) { - if (!verify_dotgit_ntfs(repo, component, len)) + if (data->flags & GIT_PATH_REJECT_DOT_GIT_NTFS) { + if (!validate_dotgit_ntfs(data->repo, component, len)) return false; - if (S_ISLNK(mode) && git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_NTFS)) + + if (S_ISLNK(data->file_mode) && + git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_NTFS)) return false; } /* don't bother rerunning the `.git` test if we ran the HFS or NTFS * specific tests, they would have already rejected `.git`. */ - if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 && - (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 && - (flags & GIT_PATH_REJECT_DOT_GIT_LITERAL)) { + if ((data->flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 && + (data->flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 && + (data->flags & GIT_PATH_REJECT_DOT_GIT_LITERAL)) { if (len >= 4 && component[0] == '.' && (component[1] == 'g' || component[1] == 'G') && @@ -1841,10 +250,11 @@ static bool verify_component( if (len == 4) return false; - if (S_ISLNK(mode) && common_prefix_icase(component, len, ".gitmodules") == len) + if (S_ISLNK(data->file_mode) && + common_prefix_icase(component, len, ".gitmodules") == len) return false; } - } + } return true; } @@ -1875,93 +285,49 @@ GIT_INLINE(unsigned int) dotgit_flags( return flags; } -bool git_path_validate( +GIT_INLINE(unsigned int) length_flags( git_repository *repo, - const char *path, - uint16_t mode, unsigned int flags) { - const char *start, *c; - - /* Upgrade the ".git" checks based on platform */ - if ((flags & GIT_PATH_REJECT_DOT_GIT)) - flags = dotgit_flags(repo, flags); - - for (start = c = path; *c; c++) { - if (!verify_char(*c, flags)) - return false; - - if (*c == '/') { - if (!verify_component(repo, start, (c - start), mode, flags)) - return false; - - start = c+1; - } - } - - return verify_component(repo, start, (c - start), mode, flags); -} - #ifdef GIT_WIN32 -GIT_INLINE(bool) should_validate_longpaths(git_repository *repo) -{ - int longpaths = 0; + int allow = 0; if (repo && - git_repository__configmap_lookup(&longpaths, repo, GIT_CONFIGMAP_LONGPATHS) < 0) - longpaths = 0; + git_repository__configmap_lookup(&allow, repo, GIT_CONFIGMAP_LONGPATHS) < 0) + allow = 0; - return (longpaths == 0); -} + if (allow) + flags &= ~GIT_FS_PATH_REJECT_LONG_PATHS; #else - -GIT_INLINE(bool) should_validate_longpaths(git_repository *repo) -{ GIT_UNUSED(repo); - - return false; -} + flags &= ~GIT_FS_PATH_REJECT_LONG_PATHS; #endif -int git_path_validate_workdir(git_repository *repo, const char *path) -{ - if (should_validate_longpaths(repo)) - return git_path_validate_filesystem(path, strlen(path)); - - return 0; + return flags; } -int git_path_validate_workdir_with_len( +bool git_path_str_is_valid( git_repository *repo, - const char *path, - size_t path_len) -{ - if (should_validate_longpaths(repo)) - return git_path_validate_filesystem(path, path_len); - - return 0; -} - -int git_path_validate_workdir_buf(git_repository *repo, git_str *path) + const git_str *path, + uint16_t file_mode, + unsigned int flags) { - return git_path_validate_workdir_with_len(repo, path->ptr, path->size); -} + repository_path_validate_data data = {0}; -int git_path_normalize_slashes(git_str *out, const char *path) -{ - int error; - char *p; + /* Upgrade the ".git" checks based on platform */ + if ((flags & GIT_PATH_REJECT_DOT_GIT)) + flags = dotgit_flags(repo, flags); - if ((error = git_str_puts(out, path)) < 0) - return error; + /* Update the length checks based on platform */ + if ((flags & GIT_FS_PATH_REJECT_LONG_PATHS)) + flags = length_flags(repo, flags); - for (p = out->ptr; *p; p++) { - if (*p == '\\') - *p = '/'; - } + data.repo = repo; + data.file_mode = file_mode; + data.flags = flags; - return 0; + return git_fs_path_str_is_valid_ext(path, flags, NULL, validate_repo_component, NULL, &data); } static const struct { @@ -1974,7 +340,11 @@ static const struct { { "gitattributes", "gi7d29", CONST_STRLEN("gitattributes") } }; -extern int git_path_is_gitfile(const char *path, size_t pathlen, git_path_gitfile gitfile, git_path_fs fs) +extern int git_path_is_gitfile( + const char *path, + size_t pathlen, + git_path_gitfile gitfile, + git_path_fs fs) { const char *file, *hash; size_t filelen; @@ -1990,112 +360,15 @@ extern int git_path_is_gitfile(const char *path, size_t pathlen, git_path_gitfil switch (fs) { case GIT_PATH_FS_GENERIC: - return !verify_dotgit_ntfs_generic(path, pathlen, file, filelen, hash) || - !verify_dotgit_hfs_generic(path, pathlen, file, filelen); + return !validate_dotgit_ntfs_generic(path, pathlen, file, filelen, hash) || + !validate_dotgit_hfs_generic(path, pathlen, file, filelen); case GIT_PATH_FS_NTFS: - return !verify_dotgit_ntfs_generic(path, pathlen, file, filelen, hash); + return !validate_dotgit_ntfs_generic(path, pathlen, file, filelen, hash); case GIT_PATH_FS_HFS: - return !verify_dotgit_hfs_generic(path, pathlen, file, filelen); + return !validate_dotgit_hfs_generic(path, pathlen, file, filelen); default: git_error_set(GIT_ERROR_OS, "invalid filesystem for path validation"); return -1; } } -bool git_path_supports_symlinks(const char *dir) -{ - git_str path = GIT_STR_INIT; - bool supported = false; - struct stat st; - int fd; - - if ((fd = git_futils_mktmp(&path, dir, 0666)) < 0 || - p_close(fd) < 0 || - p_unlink(path.ptr) < 0 || - p_symlink("testing", path.ptr) < 0 || - p_lstat(path.ptr, &st) < 0) - goto done; - - supported = (S_ISLNK(st.st_mode) != 0); -done: - if (path.size) - (void)p_unlink(path.ptr); - git_str_dispose(&path); - return supported; -} - -int git_path_validate_system_file_ownership(const char *path) -{ -#ifndef GIT_WIN32 - GIT_UNUSED(path); - return GIT_OK; -#else - git_win32_path buf; - PSID owner_sid; - PSECURITY_DESCRIPTOR descriptor = NULL; - HANDLE token; - TOKEN_USER *info = NULL; - DWORD err, len; - int ret; - - if (git_win32_path_from_utf8(buf, path) < 0) - return -1; - - err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT, - OWNER_SECURITY_INFORMATION | - DACL_SECURITY_INFORMATION, - &owner_sid, NULL, NULL, NULL, &descriptor); - - if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { - ret = GIT_ENOTFOUND; - goto cleanup; - } - - if (err != ERROR_SUCCESS) { - git_error_set(GIT_ERROR_OS, "failed to get security information"); - ret = GIT_ERROR; - goto cleanup; - } - - if (!IsValidSid(owner_sid)) { - git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown"); - ret = GIT_ERROR; - goto cleanup; - } - - if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || - IsWellKnownSid(owner_sid, WinLocalSystemSid)) { - ret = GIT_OK; - goto cleanup; - } - - /* Obtain current user's SID */ - if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) && - !GetTokenInformation(token, TokenUser, NULL, 0, &len)) { - info = git__malloc(len); - GIT_ERROR_CHECK_ALLOC(info); - if (!GetTokenInformation(token, TokenUser, info, len, &len)) { - git__free(info); - info = NULL; - } - } - - /* - * If the file is owned by the same account that is running the current - * process, it's okay to read from that file. - */ - if (info && EqualSid(owner_sid, info->User.Sid)) - ret = GIT_OK; - else { - git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is not valid"); - ret = GIT_ERROR; - } - git__free(info); - -cleanup: - if (descriptor) - LocalFree(descriptor); - - return ret; -#endif -} diff --git a/src/path.h b/src/path.h index 4074c3425..c4a2c4250 100644 --- a/src/path.h +++ b/src/path.h @@ -9,729 +9,60 @@ #include "common.h" -#include "posix.h" -#include "str.h" -#include "vector.h" +#include "fs_path.h" +#include <git2/sys/path.h> -#include "git2/sys/path.h" +#define GIT_PATH_REJECT_DOT_GIT (GIT_FS_PATH_REJECT_MAX << 1) +#define GIT_PATH_REJECT_DOT_GIT_LITERAL (GIT_FS_PATH_REJECT_MAX << 2) +#define GIT_PATH_REJECT_DOT_GIT_HFS (GIT_FS_PATH_REJECT_MAX << 3) +#define GIT_PATH_REJECT_DOT_GIT_NTFS (GIT_FS_PATH_REJECT_MAX << 4) -/** - * Path manipulation utils - * - * These are path utilities that munge paths without actually - * looking at the real filesystem. - */ - -/* - * The dirname() function shall take a pointer to a character string - * that contains a pathname, and return a pointer to a string that is a - * pathname of the parent directory of that file. Trailing '/' characters - * in the path are not counted as part of the path. - * - * If path does not contain a '/', then dirname() shall return a pointer to - * the string ".". If path is a null pointer or points to an empty string, - * dirname() shall return a pointer to the string "." . - * - * The `git_path_dirname` implementation is thread safe. The returned - * string must be manually free'd. - * - * The `git_path_dirname_r` implementation writes the dirname to a `git_str` - * if the buffer pointer is not NULL. - * It returns an error code < 0 if there is an allocation error, otherwise - * the length of the dirname (which will be > 0). - */ -extern char *git_path_dirname(const char *path); -extern int git_path_dirname_r(git_str *buffer, const char *path); - -/* - * This function returns the basename of the file, which is the last - * part of its full name given by fname, with the drive letter and - * leading directories stripped off. For example, the basename of - * c:/foo/bar/file.ext is file.ext, and the basename of a:foo is foo. - * - * Trailing slashes and backslashes are significant: the basename of - * c:/foo/bar/ is an empty string after the rightmost slash. - * - * The `git_path_basename` implementation is thread safe. The returned - * string must be manually free'd. - * - * The `git_path_basename_r` implementation writes the basename to a `git_str`. - * It returns an error code < 0 if there is an allocation error, otherwise - * the length of the basename (which will be >= 0). - */ -extern char *git_path_basename(const char *path); -extern int git_path_basename_r(git_str *buffer, const char *path); - -/* Return the offset of the start of the basename. Unlike the other - * basename functions, this returns 0 if the path is empty. - */ -extern size_t git_path_basename_offset(git_str *buffer); - -/** - * Find offset to root of path if path has one. - * - * This will return a number >= 0 which is the offset to the start of the - * path, if the path is rooted (i.e. "/rooted/path" returns 0 and - * "c:/windows/rooted/path" returns 2). If the path is not rooted, this - * returns -1. - */ -extern int git_path_root(const char *path); - -/** - * Ensure path has a trailing '/'. - */ -extern int git_path_to_dir(git_str *path); - -/** - * Ensure string has a trailing '/' if there is space for it. - */ -extern void git_path_string_to_dir(char *path, size_t size); - -/** - * Taken from git.git; returns nonzero if the given path is "." or "..". - */ -GIT_INLINE(int) git_path_is_dot_or_dotdot(const char *name) -{ - return (name[0] == '.' && - (name[1] == '\0' || - (name[1] == '.' && name[2] == '\0'))); -} - -#ifdef GIT_WIN32 -GIT_INLINE(int) git_path_is_dot_or_dotdotW(const wchar_t *name) -{ - return (name[0] == L'.' && - (name[1] == L'\0' || - (name[1] == L'.' && name[2] == L'\0'))); -} - -#define git_path_is_absolute(p) \ - (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/')) - -#define git_path_is_dirsep(p) \ - ((p) == '/' || (p) == '\\') - -/** - * Convert backslashes in path to forward slashes. - */ -GIT_INLINE(void) git_path_mkposix(char *path) -{ - while (*path) { - if (*path == '\\') - *path = '/'; - - path++; - } -} -#else -# define git_path_mkposix(p) /* blank */ - -#define git_path_is_absolute(p) \ - ((p)[0] == '/') - -#define git_path_is_dirsep(p) \ - ((p) == '/') - -#endif - -/** - * Check if string is a relative path (i.e. starts with "./" or "../") - */ -GIT_INLINE(int) git_path_is_relative(const char *p) -{ - return (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/'))); -} - -/** - * Check if string is at end of path segment (i.e. looking at '/' or '\0') - */ -GIT_INLINE(int) git_path_at_end_of_segment(const char *p) -{ - return !*p || *p == '/'; -} - -extern int git__percent_decode(git_str *decoded_out, const char *input); - -/** - * Extract path from file:// URL. - */ -extern int git_path_fromurl(git_str *local_path_out, const char *file_url); - - -/** - * Path filesystem utils - * - * These are path utilities that actually access the filesystem. - */ - -/** - * Check if a file exists and can be accessed. - * @return true or false - */ -extern bool git_path_exists(const char *path); - -/** - * Check if the given path points to a directory. - * @return true or false - */ -extern bool git_path_isdir(const char *path); - -/** - * Check if the given path points to a regular file. - * @return true or false - */ -extern bool git_path_isfile(const char *path); - -/** - * Check if the given path points to a symbolic link. - * @return true or false - */ -extern bool git_path_islink(const char *path); - -/** - * Check if the given path is a directory, and is empty. - */ -extern bool git_path_is_empty_dir(const char *path); - -/** - * Stat a file and/or link and set error if needed. - */ -extern int git_path_lstat(const char *path, struct stat *st); - -/** - * Check if the parent directory contains the item. - * - * @param dir Directory to check. - * @param item Item that might be in the directory. - * @return 0 if item exists in directory, <0 otherwise. - */ -extern bool git_path_contains(git_str *dir, const char *item); - -/** - * Check if the given path contains the given subdirectory. - * - * @param parent Directory path that might contain subdir - * @param subdir Subdirectory name to look for in parent - * @return true if subdirectory exists, false otherwise. - */ -extern bool git_path_contains_dir(git_str *parent, const char *subdir); - -/** - * Determine the common directory length between two paths, including - * the final path separator. For example, given paths 'a/b/c/1.txt - * and 'a/b/c/d/2.txt', the common directory is 'a/b/c/', and this - * will return the length of the string 'a/b/c/', which is 6. - * - * @param one The first path - * @param two The second path - * @return The length of the common directory - */ -extern size_t git_path_common_dirlen(const char *one, const char *two); - -/** - * Make the path relative to the given parent path. - * - * @param path The path to make relative - * @param parent The parent path to make path relative to - * @return 0 if path was made relative, GIT_ENOTFOUND - * if there was not common root between the paths, - * or <0. - */ -extern int git_path_make_relative(git_str *path, const char *parent); - -/** - * Check if the given path contains the given file. - * - * @param dir Directory path that might contain file - * @param file File name to look for in parent - * @return true if file exists, false otherwise. - */ -extern bool git_path_contains_file(git_str *dir, const char *file); - -/** - * Prepend base to unrooted path or just copy path over. - * - * This will optionally return the index into the path where the "root" - * is, either the end of the base directory prefix or the path root. - */ -extern int git_path_join_unrooted( - git_str *path_out, const char *path, const char *base, ssize_t *root_at); - -/** - * Removes multiple occurrences of '/' in a row, squashing them into a - * single '/'. - */ -extern void git_path_squash_slashes(git_str *path); - -/** - * Clean up path, prepending base if it is not already rooted. - */ -extern int git_path_prettify(git_str *path_out, const char *path, const char *base); - -/** - * Clean up path, prepending base if it is not already rooted and - * appending a slash. - */ -extern int git_path_prettify_dir(git_str *path_out, const char *path, const char *base); - -/** - * Get a directory from a path. - * - * If path is a directory, this acts like `git_path_prettify_dir` - * (cleaning up path and appending a '/'). If path is a normal file, - * this prettifies it, then removed the filename a la dirname and - * appends the trailing '/'. If the path does not exist, it is - * treated like a regular filename. - */ -extern int git_path_find_dir(git_str *dir); - -/** - * Resolve relative references within a path. - * - * This eliminates "./" and "../" relative references inside a path, - * as well as condensing multiple slashes into single ones. It will - * not touch the path before the "ceiling" length. - * - * Additionally, this will recognize an "c:/" drive prefix or a "xyz://" URL - * prefix and not touch that part of the path. - */ -extern int git_path_resolve_relative(git_str *path, size_t ceiling); - -/** - * Apply a relative path to base path. - * - * Note that the base path could be a filename or a URL and this - * should still work. The relative path is walked segment by segment - * with three rules: series of slashes will be condensed to a single - * slash, "." will be eaten with no change, and ".." will remove a - * segment from the base path. - */ -extern int git_path_apply_relative(git_str *target, const char *relpath); - -enum { - GIT_PATH_DIR_IGNORE_CASE = (1u << 0), - GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1), - GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT = (1u << 2), -}; - -/** - * Walk each directory entry, except '.' and '..', calling fn(state). - * - * @param pathbuf Buffer the function reads the initial directory - * path from, and updates with each successive entry's name. - * @param flags Combination of GIT_PATH_DIR flags. - * @param callback Callback for each entry. Passed the `payload` and each - * successive path inside the directory as a full path. This may - * safely append text to the pathbuf if needed. Return non-zero to - * cancel iteration (and return value will be propagated back). - * @param payload Passed to callback as first argument. - * @return 0 on success or error code from OS error or from callback - */ -extern int git_path_direach( - git_str *pathbuf, - uint32_t flags, - int (*callback)(void *payload, git_str *path), - void *payload); - -/** - * Sort function to order two paths - */ -extern int git_path_cmp( - const char *name1, size_t len1, int isdir1, - const char *name2, size_t len2, int isdir2, - int (*compare)(const char *, const char *, size_t)); - -/** - * Invoke callback up path directory by directory until the ceiling is - * reached (inclusive of a final call at the root_path). - * - * Returning anything other than 0 from the callback function - * will stop the iteration and propagate the error to the caller. - * - * @param pathbuf Buffer the function reads the directory from and - * and updates with each successive name. - * @param ceiling Prefix of path at which to stop walking up. If NULL, - * this will walk all the way up to the root. If not a prefix of - * pathbuf, the callback will be invoked a single time on the - * original input path. - * @param callback Function to invoke on each path. Passed the `payload` - * and the buffer containing the current path. The path should not - * be modified in any way. Return non-zero to stop iteration. - * @param payload Passed to fn as the first ath. - */ -extern int git_path_walk_up( - git_str *pathbuf, - const char *ceiling, - int (*callback)(void *payload, const char *path), - void *payload); - - -enum { GIT_PATH_NOTEQUAL = 0, GIT_PATH_EQUAL = 1, GIT_PATH_PREFIX = 2 }; - -/* - * Determines if a path is equal to or potentially a child of another. - * @param parent The possible parent - * @param child The possible child - */ -GIT_INLINE(int) git_path_equal_or_prefixed( - const char *parent, - const char *child, - ssize_t *prefixlen) -{ - const char *p = parent, *c = child; - int lastslash = 0; - - while (*p && *c) { - lastslash = (*p == '/'); - - if (*p++ != *c++) - return GIT_PATH_NOTEQUAL; - } - - if (*p != '\0') - return GIT_PATH_NOTEQUAL; - - if (*c == '\0') { - if (prefixlen) - *prefixlen = p - parent; - - return GIT_PATH_EQUAL; - } - - if (*c == '/' || lastslash) { - if (prefixlen) - *prefixlen = (p - parent) - lastslash; - - return GIT_PATH_PREFIX; - } - - return GIT_PATH_NOTEQUAL; -} - -/* translate errno to libgit2 error code and set error message */ -extern int git_path_set_error( - int errno_value, const char *path, const char *action); - -/* check if non-ascii characters are present in filename */ -extern bool git_path_has_non_ascii(const char *path, size_t pathlen); - -#define GIT_PATH_REPO_ENCODING "UTF-8" - -#ifdef __APPLE__ -#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC" -#else -#define GIT_PATH_NATIVE_ENCODING "UTF-8" -#endif - -#ifdef GIT_USE_ICONV - -#include <iconv.h> - -typedef struct { - iconv_t map; - git_str buf; -} git_path_iconv_t; - -#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_STR_INIT } - -/* Init iconv data for converting decomposed UTF-8 to precomposed */ -extern int git_path_iconv_init_precompose(git_path_iconv_t *ic); - -/* Clear allocated iconv data */ -extern void git_path_iconv_clear(git_path_iconv_t *ic); - -/* - * Rewrite `in` buffer using iconv map if necessary, replacing `in` - * pointer internal iconv buffer if rewrite happened. The `in` pointer - * will be left unchanged if no rewrite was needed. - */ -extern int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen); - -#endif /* GIT_USE_ICONV */ - -extern bool git_path_does_fs_decompose_unicode(const char *root); - - -typedef struct git_path_diriter git_path_diriter; - -#if defined(GIT_WIN32) && !defined(__MINGW32__) - -struct git_path_diriter -{ - git_win32_path path; - size_t parent_len; - - git_str path_utf8; - size_t parent_utf8_len; - - HANDLE handle; - - unsigned int flags; - - WIN32_FIND_DATAW current; - unsigned int needs_next; -}; - -#define GIT_PATH_DIRITER_INIT { {0}, 0, GIT_STR_INIT, 0, INVALID_HANDLE_VALUE } - -#else - -struct git_path_diriter -{ - git_str path; - size_t parent_len; - - unsigned int flags; - - DIR *dir; - -#ifdef GIT_USE_ICONV - git_path_iconv_t ic; -#endif -}; - -#define GIT_PATH_DIRITER_INIT { GIT_STR_INIT } - -#endif - -/** - * Initialize a directory iterator. - * - * @param diriter Pointer to a diriter structure that will be setup. - * @param path The path that will be iterated over - * @param flags Directory reader flags - * @return 0 or an error code - */ -extern int git_path_diriter_init( - git_path_diriter *diriter, - const char *path, - unsigned int flags); - -/** - * Advance the directory iterator. Will return GIT_ITEROVER when - * the iteration has completed successfully. - * - * @param diriter The directory iterator - * @return 0, GIT_ITEROVER, or an error code - */ -extern int git_path_diriter_next(git_path_diriter *diriter); - -/** - * Returns the file name of the current item in the iterator. - * - * @param out Pointer to store the path in - * @param out_len Pointer to store the length of the path in - * @param diriter The directory iterator - * @return 0 or an error code - */ -extern int git_path_diriter_filename( - const char **out, - size_t *out_len, - git_path_diriter *diriter); - -/** - * Returns the full path of the current item in the iterator; that - * is the current filename plus the path of the directory that the - * iterator was constructed with. - * - * @param out Pointer to store the path in - * @param out_len Pointer to store the length of the path in - * @param diriter The directory iterator - * @return 0 or an error code - */ -extern int git_path_diriter_fullpath( - const char **out, - size_t *out_len, - git_path_diriter *diriter); - -/** - * Performs an `lstat` on the current item in the iterator. - * - * @param out Pointer to store the stat data in - * @param diriter The directory iterator - * @return 0 or an error code - */ -extern int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter); - -/** - * Closes the directory iterator. - * - * @param diriter The directory iterator - */ -extern void git_path_diriter_free(git_path_diriter *diriter); - -/** - * Load all directory entries (except '.' and '..') into a vector. - * - * For cases where `git_path_direach()` is not appropriate, this - * allows you to load the filenames in a directory into a vector - * of strings. That vector can then be sorted, iterated, or whatever. - * Remember to free alloc of the allocated strings when you are done. - * - * @param contents Vector to fill with directory entry names. - * @param path The directory to read from. - * @param prefix_len When inserting entries, the trailing part of path - * will be prefixed after this length. I.e. given path "/a/b" and - * prefix_len 3, the entries will look like "b/e1", "b/e2", etc. - * @param flags Combination of GIT_PATH_DIR flags. - */ -extern int git_path_dirload( - git_vector *contents, - const char *path, - size_t prefix_len, - uint32_t flags); - - -/* Used for paths to repositories on the filesystem */ -extern bool git_path_is_local_file_url(const char *file_url); -extern int git_path_from_url_or_path(git_str *local_path_out, const char *url_or_path); - -/* Flags to determine path validity in `git_path_isvalid` */ -#define GIT_PATH_REJECT_TRAVERSAL (1 << 0) -#define GIT_PATH_REJECT_DOT_GIT (1 << 1) -#define GIT_PATH_REJECT_SLASH (1 << 2) -#define GIT_PATH_REJECT_BACKSLASH (1 << 3) -#define GIT_PATH_REJECT_TRAILING_DOT (1 << 4) -#define GIT_PATH_REJECT_TRAILING_SPACE (1 << 5) -#define GIT_PATH_REJECT_TRAILING_COLON (1 << 6) -#define GIT_PATH_REJECT_DOS_PATHS (1 << 7) -#define GIT_PATH_REJECT_NT_CHARS (1 << 8) -#define GIT_PATH_REJECT_DOT_GIT_LITERAL (1 << 9) -#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 10) -#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 11) - -/* Default path safety for writing files to disk: since we use the - * Win32 "File Namespace" APIs ("\\?\") we need to protect from - * paths that the normal Win32 APIs would not write. - */ -#ifdef GIT_WIN32 -# define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \ - GIT_PATH_REJECT_TRAVERSAL | \ - GIT_PATH_REJECT_BACKSLASH | \ - GIT_PATH_REJECT_TRAILING_DOT | \ - GIT_PATH_REJECT_TRAILING_SPACE | \ - GIT_PATH_REJECT_TRAILING_COLON | \ - GIT_PATH_REJECT_DOS_PATHS | \ - GIT_PATH_REJECT_NT_CHARS -#else -# define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \ - GIT_PATH_REJECT_TRAVERSAL -#endif - - /* Paths that should never be written into the working directory. */ +/* Paths that should never be written into the working directory. */ #define GIT_PATH_REJECT_WORKDIR_DEFAULTS \ - GIT_PATH_REJECT_FILESYSTEM_DEFAULTS | GIT_PATH_REJECT_DOT_GIT + GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS | GIT_PATH_REJECT_DOT_GIT /* Paths that should never be written to the index. */ #define GIT_PATH_REJECT_INDEX_DEFAULTS \ - GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT + GIT_FS_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT -/** - * Validate a "bare" git path. This ensures that the given path is legal - * to place in the index or a tree. This should be checked by mechanisms - * like `git_index_add` and `git_treebuilder_insert` when taking user - * data, and by `git_checkout` before constructing on-disk paths. - * - * This will ensure that a git path does not contain any "unsafe" components, - * a '.' or '..' component, or a component that is ".git" (in any case). - * - * (Note: if you take or construct an on-disk path -- a workdir path, - * a path to a git repository or a reference name that could be a loose - * ref -- you should _also_ validate that with `git_path_validate_workdir`.) - * - * `repo` is optional. If specified, it will be used to determine the short - * path name to reject (if `GIT_PATH_REJECT_DOS_SHORTNAME` is specified), - * in addition to the default of "git~1". - */ -extern bool git_path_validate( +extern bool git_path_str_is_valid( git_repository *repo, - const char *path, - uint16_t mode, + const git_str *path, + uint16_t file_mode, unsigned int flags); -/** - * Validate an on-disk path, taking into account that it will have a - * suffix appended (eg, `.lock`). - */ -GIT_INLINE(int) git_path_validate_filesystem_with_suffix( +GIT_INLINE(bool) git_path_is_valid( + git_repository *repo, const char *path, - size_t path_len, - size_t suffix_len) + uint16_t file_mode, + unsigned int flags) { -#ifdef GIT_WIN32 - size_t path_chars, total_chars; + git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX); + return git_path_str_is_valid(repo, &str, file_mode, flags); +} - path_chars = git_utf8_char_length(path, path_len); +GIT_INLINE(int) git_path_validate_str_length( + git_repository *repo, + const git_str *path) +{ + if (!git_path_str_is_valid(repo, path, 0, GIT_FS_PATH_REJECT_LONG_PATHS)) { + if (path->size == SIZE_MAX) + git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%s'", path->ptr); + else + git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%.*s'", (int)path->size, path->ptr); - if (GIT_ADD_SIZET_OVERFLOW(&total_chars, path_chars, suffix_len) || - total_chars > MAX_PATH) { - git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%s'", path); return -1; } + return 0; -#else - GIT_UNUSED(path); - GIT_UNUSED(path_len); - GIT_UNUSED(suffix_len); - return 0; -#endif } -/** - * Validate an path on the filesystem. This ensures that the given - * path is valid for the operating system/platform; for example, this - * will ensure that the given absolute path is smaller than MAX_PATH on - * Windows. - * - * For paths within the working directory, you should use ensure that - * `core.longpaths` is obeyed. Use `git_path_validate_workdir`. - */ -GIT_INLINE(int) git_path_validate_filesystem( - const char *path, - size_t path_len) +GIT_INLINE(int) git_path_validate_length( + git_repository *repo, + const char *path) { - return git_path_validate_filesystem_with_suffix(path, path_len, 0); + git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX); + return git_path_validate_str_length(repo, &str); } -/** - * Validate a path relative to the repo's worktree. This ensures that - * the given working tree path is valid for the operating system/platform. - * This will ensure that an absolute path is smaller than MAX_PATH on - * Windows, while keeping `core.longpaths` configuration settings in mind. - * - * This should be checked by mechamisms like `git_checkout` after - * contructing on-disk paths and before trying to write them. - * - * If the repository is null, no repository configuration is applied. - */ -extern int git_path_validate_workdir( - git_repository *repo, - const char *path); -extern int git_path_validate_workdir_with_len( - git_repository *repo, - const char *path, - size_t path_len); -extern int git_path_validate_workdir_buf( - git_repository *repo, - git_str *buf); - -/** - * Convert any backslashes into slashes - */ -int git_path_normalize_slashes(git_str *out, const char *path); - -bool git_path_supports_symlinks(const char *dir); - -/** - * Validate a system file's ownership - * - * Verify that the file in question is owned by an administrator or system - * account, or at least by the current user. - * - * This function returns 0 if successful. If the file is not owned by any of - * these, or any other if there have been problems determining the file - * ownership, it returns -1. - */ -int git_path_validate_system_file_ownership(const char *path); - #endif diff --git a/src/pathspec.h b/src/pathspec.h index bfdcd48a7..0256cb927 100644 --- a/src/pathspec.h +++ b/src/pathspec.h @@ -25,7 +25,7 @@ struct git_pathspec { enum { PATHSPEC_DATATYPE_STRINGS = 0, - PATHSPEC_DATATYPE_DIFF = 1, + PATHSPEC_DATATYPE_DIFF = 1 }; typedef git_array_t(char *) git_pathspec_string_array_t; diff --git a/src/posix.c b/src/posix.c index c40134824..b1f85dc94 100644 --- a/src/posix.c +++ b/src/posix.c @@ -7,7 +7,7 @@ #include "posix.h" -#include "path.h" +#include "fs_path.h" #include <stdio.h> #include <ctype.h> @@ -144,8 +144,8 @@ int p_getcwd(char *buffer_out, size_t size) if (cwd_buffer == NULL) return -1; - git_path_mkposix(buffer_out); - git_path_string_to_dir(buffer_out, size); /* append trailing slash */ + git_fs_path_mkposix(buffer_out); + git_fs_path_string_to_dir(buffer_out, size); /* append trailing slash */ return 0; } diff --git a/src/pqueue.h b/src/pqueue.h index c0a6cd49e..4db74ea03 100644 --- a/src/pqueue.h +++ b/src/pqueue.h @@ -15,7 +15,7 @@ typedef git_vector git_pqueue; enum { /* flag meaning: don't grow heap, keep highest values only */ - GIT_PQUEUE_FIXED_SIZE = (GIT_VECTOR_FLAG_MAX << 1), + GIT_PQUEUE_FIXED_SIZE = (GIT_VECTOR_FLAG_MAX << 1) }; /** diff --git a/src/rebase.c b/src/rebase.c index 302fc81fc..4ad37643b 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -53,7 +53,7 @@ typedef enum { GIT_REBASE_NONE = 0, GIT_REBASE_APPLY = 1, GIT_REBASE_MERGE = 2, - GIT_REBASE_INTERACTIVE = 3, + GIT_REBASE_INTERACTIVE = 3 } git_rebase_t; struct git_rebase { @@ -97,7 +97,7 @@ static int rebase_state_type( if (git_str_joinpath(&path, repo->gitdir, REBASE_APPLY_DIR) < 0) return -1; - if (git_path_isdir(git_str_cstr(&path))) { + if (git_fs_path_isdir(git_str_cstr(&path))) { type = GIT_REBASE_APPLY; goto done; } @@ -106,7 +106,7 @@ static int rebase_state_type( if (git_str_joinpath(&path, repo->gitdir, REBASE_MERGE_DIR) < 0) return -1; - if (git_path_isdir(git_str_cstr(&path))) { + if (git_fs_path_isdir(git_str_cstr(&path))) { type = GIT_REBASE_MERGE; goto done; } @@ -340,7 +340,7 @@ int git_rebase_open( if ((error = git_str_joinpath(&path, path.ptr, ORIG_HEAD_FILE)) < 0) goto done; - if (!git_path_isfile(path.ptr)) { + if (!git_fs_path_isfile(path.ptr)) { /* Previous versions of git.git used 'head' here; support that. */ git_str_truncate(&path, state_path_len); @@ -404,7 +404,7 @@ static int rebase_cleanup(git_rebase *rebase) if (!rebase || rebase->inmemory) return 0; - return git_path_isdir(rebase->state_path) ? + return git_fs_path_isdir(rebase->state_path) ? git_futils_rmdir_r(rebase->state_path, NULL, GIT_RMDIR_REMOVE_FILES) : 0; } diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 37eb85ecc..055ca2559 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -18,6 +18,7 @@ #include "sortedcache.h" #include "signature.h" #include "wildmatch.h" +#include "path.h" #include <git2/tag.h> #include <git2/object.h> @@ -34,7 +35,7 @@ enum { PACKREF_HAS_PEEL = 1, PACKREF_WAS_LOOSE = 2, PACKREF_CANNOT_PEEL = 4, - PACKREF_SHADOWED = 8, + PACKREF_SHADOWED = 8 }; enum { @@ -76,7 +77,7 @@ GIT_INLINE(int) loose_path( if (git_str_joinpath(out, base, refname) < 0) return -1; - return git_path_validate_filesystem_with_suffix(out->ptr, out->size, + return git_fs_path_validate_str_length_with_suffix(out, CONST_STRLEN(".lock")); } @@ -307,8 +308,8 @@ static int _dirent_loose_load(void *payload, git_str *full_path) if (git__suffixcmp(full_path->ptr, ".lock") == 0) return 0; - if (git_path_isdir(full_path->ptr)) { - int error = git_path_direach( + if (git_fs_path_isdir(full_path->ptr)) { + int error = git_fs_path_direach( full_path, backend->direach_flags, _dirent_loose_load, backend); /* Race with the filesystem, ignore it */ if (error == GIT_ENOTFOUND) { @@ -343,7 +344,7 @@ static int packed_loadloose(refdb_fs_backend *backend) * This will overwrite any old packed entries with their * updated loose versions */ - error = git_path_direach( + error = git_fs_path_direach( &refs_path, backend->direach_flags, _dirent_loose_load, backend); git_str_dispose(&refs_path); @@ -367,7 +368,7 @@ static int refdb_fs_backend__exists( if ((error = loose_path(&ref_path, backend->gitpath, ref_name)) < 0) goto out; - if (git_path_isfile(ref_path.ptr)) { + if (git_fs_path_isfile(ref_path.ptr)) { *exists = 1; goto out; } @@ -817,7 +818,7 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char * GIT_ASSERT_ARG(backend); GIT_ASSERT_ARG(name); - if (!git_path_validate(backend->repo, name, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { + if (!git_path_is_valid(backend->repo, name, 0, GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS)) { git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", name); return GIT_EINVALIDSPEC; } @@ -1344,10 +1345,10 @@ static int refdb_fs_backend__prune_refs( if ((error = git_str_sets(&relative_path, ref_name)) < 0) goto cleanup; - git_path_squash_slashes(&relative_path); - if ((commonlen = git_path_common_dirlen("refs/heads/", git_str_cstr(&relative_path))) == strlen("refs/heads/") || - (commonlen = git_path_common_dirlen("refs/tags/", git_str_cstr(&relative_path))) == strlen("refs/tags/") || - (commonlen = git_path_common_dirlen("refs/remotes/", git_str_cstr(&relative_path))) == strlen("refs/remotes/")) { + git_fs_path_squash_slashes(&relative_path); + if ((commonlen = git_fs_path_common_dirlen("refs/heads/", git_str_cstr(&relative_path))) == strlen("refs/heads/") || + (commonlen = git_fs_path_common_dirlen("refs/tags/", git_str_cstr(&relative_path))) == strlen("refs/tags/") || + (commonlen = git_fs_path_common_dirlen("refs/remotes/", git_str_cstr(&relative_path))) == strlen("refs/remotes/")) { git_str_truncate(&relative_path, commonlen); @@ -1361,7 +1362,7 @@ static int refdb_fs_backend__prune_refs( git_str_cstr(&relative_path)); if (!error) - error = git_path_validate_filesystem(base_path.ptr, base_path.size); + error = git_path_validate_str_length(NULL, &base_path); if (error < 0) goto cleanup; @@ -1741,7 +1742,7 @@ static int has_reflog(git_repository *repo, const char *name) if (reflog_path(&path, repo, name) < 0) goto cleanup; - ret = git_path_isfile(git_str_cstr(&path)); + ret = git_fs_path_isfile(git_str_cstr(&path)); cleanup: git_str_dispose(&path); @@ -1856,7 +1857,7 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char repo = backend->repo; - if (!git_path_validate(backend->repo, refname, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { + if (!git_path_is_valid(backend->repo, refname, 0, GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS)) { git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", refname); return GIT_EINVALIDSPEC; } @@ -1864,7 +1865,7 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char if (reflog_path(&log_path, repo, refname) < 0) return -1; - if (!git_path_isfile(git_str_cstr(&log_path))) { + if (!git_fs_path_isfile(git_str_cstr(&log_path))) { git_error_set(GIT_ERROR_INVALID, "log file for reference '%s' doesn't exist", refname); error = -1; @@ -1973,11 +1974,11 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co /* If the new branch matches part of the namespace of a previously deleted branch, * there maybe an obsolete/unused directory (or directory hierarchy) in the way. */ - if (git_path_isdir(git_str_cstr(&path))) { + if (git_fs_path_isdir(git_str_cstr(&path))) { if ((error = git_futils_rmdir_r(git_str_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) { if (error == GIT_ENOTFOUND) error = 0; - } else if (git_path_isdir(git_str_cstr(&path))) { + } else if (git_fs_path_isdir(git_str_cstr(&path))) { git_error_set(GIT_ERROR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder", ref->name); error = GIT_EDIRECTORY; @@ -2031,7 +2032,7 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_ if ((error = loose_path(&new_path, git_str_cstr(&temp_path), git_str_cstr(&normalized))) < 0) return error; - if (!git_path_exists(git_str_cstr(&old_path))) { + if (!git_fs_path_exists(git_str_cstr(&old_path))) { error = GIT_ENOTFOUND; goto cleanup; } @@ -2059,7 +2060,7 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_ goto cleanup; } - if (git_path_isdir(git_str_cstr(&new_path)) && + if (git_fs_path_isdir(git_str_cstr(&new_path)) && (git_futils_rmdir_r(git_str_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) { error = -1; goto cleanup; @@ -2096,7 +2097,7 @@ static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name if ((error = reflog_path(&path, backend->repo, name)) < 0) goto out; - if (!git_path_exists(path.ptr)) + if (!git_fs_path_exists(path.ptr)) goto out; if ((error = p_unlink(path.ptr)) < 0) @@ -2150,11 +2151,11 @@ int git_refdb_backend_fs( if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_IGNORECASE) && t) { backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE; - backend->direach_flags |= GIT_PATH_DIR_IGNORE_CASE; + backend->direach_flags |= GIT_FS_PATH_DIR_IGNORE_CASE; } if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_PRECOMPOSE) && t) { backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE; - backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE; + backend->direach_flags |= GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE; } if ((!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t) || git_repository__fsync_gitdir) diff --git a/src/refs.c b/src/refs.c index 0ac455d24..5c875b95b 100644 --- a/src/refs.c +++ b/src/refs.c @@ -902,7 +902,7 @@ int git_reference__normalize_name( bool validate = (flags & GIT_REFERENCE_FORMAT__VALIDATION_DISABLE) == 0; #ifdef GIT_USE_ICONV - git_path_iconv_t ic = GIT_PATH_ICONV_INIT; + git_fs_path_iconv_t ic = GIT_PATH_ICONV_INIT; #endif GIT_ASSERT_ARG(name); @@ -919,8 +919,8 @@ int git_reference__normalize_name( #ifdef GIT_USE_ICONV if ((flags & GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE) != 0) { size_t namelen = strlen(current); - if ((error = git_path_iconv_init_precompose(&ic)) < 0 || - (error = git_path_iconv(&ic, ¤t, &namelen)) < 0) + if ((error = git_fs_path_iconv_init_precompose(&ic)) < 0 || + (error = git_fs_path_iconv(&ic, ¤t, &namelen)) < 0) goto cleanup; error = GIT_EINVALIDSPEC; } @@ -1011,7 +1011,7 @@ cleanup: git_str_dispose(buf); #ifdef GIT_USE_ICONV - git_path_iconv_clear(&ic); + git_fs_path_iconv_clear(&ic); #endif return error; diff --git a/src/repository.c b/src/repository.c index 29684e463..8059f1081 100644 --- a/src/repository.c +++ b/src/repository.c @@ -32,7 +32,7 @@ #include "annotated_commit.h" #include "submodule.h" #include "worktree.h" - +#include "path.h" #include "strmap.h" #ifdef GIT_WIN32 @@ -197,9 +197,9 @@ static int lookup_commondir(bool *separate, git_str *commondir, git_str *reposit * If there's no commondir file, the repository path is the * common path, but it needs a trailing slash. */ - if (!git_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) { + if (!git_fs_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) { if ((error = git_str_set(commondir, repository_path->ptr, repository_path->size)) == 0) - error = git_path_to_dir(commondir); + error = git_fs_path_to_dir(commondir); *separate = false; goto done; @@ -212,7 +212,7 @@ static int lookup_commondir(bool *separate, git_str *commondir, git_str *reposit goto done; git_str_rtrim(&common_link); - if (git_path_is_relative(common_link.ptr)) { + if (git_fs_path_is_relative(common_link.ptr)) { if ((error = git_str_joinpath(commondir, repository_path->ptr, common_link.ptr)) < 0) goto done; } else { @@ -222,7 +222,7 @@ static int lookup_commondir(bool *separate, git_str *commondir, git_str *reposit git_str_dispose(&common_link); /* Make sure the commondir path always has a trailing slash */ - error = git_path_prettify_dir(commondir, commondir->ptr, NULL); + error = git_fs_path_prettify_dir(commondir, commondir->ptr, NULL); done: return error; @@ -240,8 +240,8 @@ GIT_INLINE(int) validate_repo_path(git_str *path) CONST_STRLEN("objects/pack/pack-.pack.lock") + GIT_OID_HEXSZ; - return git_path_validate_filesystem_with_suffix( - path->ptr, path->size, suffix_len); + return git_fs_path_validate_str_length_with_suffix( + path, suffix_len); } /* @@ -260,13 +260,13 @@ static int is_valid_repository_path(bool *out, git_str *repository_path, git_str return error; /* Ensure HEAD file exists */ - if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false) + if (git_fs_path_contains_file(repository_path, GIT_HEAD_FILE) == false) return 0; /* Check files in common dir */ - if (git_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false) + if (git_fs_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false) return 0; - if (git_path_contains_dir(common_path, GIT_REFS_DIR) == false) + if (git_fs_path_contains_dir(common_path, GIT_REFS_DIR) == false) return 0; /* Ensure the repo (and commondir) are valid paths */ @@ -357,8 +357,8 @@ static int load_workdir(git_repository *repo, git_config *config, git_str *paren git_str_attach(&worktree, gitlink, 0); - if ((git_path_dirname_r(&worktree, worktree.ptr)) < 0 || - git_path_to_dir(&worktree) < 0) { + if ((git_fs_path_dirname_r(&worktree, worktree.ptr)) < 0 || + git_fs_path_to_dir(&worktree) < 0) { error = -1; goto cleanup; } @@ -366,17 +366,17 @@ static int load_workdir(git_repository *repo, git_config *config, git_str *paren repo->workdir = git_str_detach(&worktree); } else if (ce && ce->value) { - if ((error = git_path_prettify_dir( + if ((error = git_fs_path_prettify_dir( &worktree, ce->value, repo->gitdir)) < 0) goto cleanup; repo->workdir = git_str_detach(&worktree); } - else if (parent_path && git_path_isdir(parent_path->ptr)) + else if (parent_path && git_fs_path_isdir(parent_path->ptr)) repo->workdir = git_str_detach(parent_path); else { - if (git_path_dirname_r(&worktree, repo->gitdir) < 0 || - git_path_to_dir(&worktree) < 0) { + if (git_fs_path_dirname_r(&worktree, repo->gitdir) < 0 || + git_fs_path_to_dir(&worktree) < 0) { error = -1; goto cleanup; } @@ -410,7 +410,7 @@ static size_t find_ceiling_dir_offset( GIT_ASSERT_ARG(path); - min_len = (size_t)(git_path_root(path) + 1); + min_len = (size_t)(git_fs_path_root(path) + 1); if (ceiling_directories == NULL || min_len == 0) return min_len; @@ -419,7 +419,7 @@ static size_t find_ceiling_dir_offset( for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++); len = sep - ceil; - if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1) + if (len == 0 || len >= sizeof(buf) || git_fs_path_root(ceil) == -1) continue; strncpy(buf, ceil, len); @@ -462,7 +462,7 @@ static int read_gitfile(git_str *path_out, const char *file_path) git_str_rtrim(&file); /* apparently on Windows, some people use backslashes in paths */ - git_path_mkposix(file.ptr); + git_fs_path_mkposix(file.ptr); if (git_str_len(&file) <= prefix_len || memcmp(git_str_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) @@ -471,11 +471,11 @@ static int read_gitfile(git_str *path_out, const char *file_path) "the `.git` file at '%s' is malformed", file_path); error = -1; } - else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { + else if ((error = git_fs_path_dirname_r(path_out, file_path)) >= 0) { const char *gitlink = git_str_cstr(&file) + prefix_len; while (*gitlink && git__isspace(*gitlink)) gitlink++; - error = git_path_prettify_dir( + error = git_fs_path_prettify_dir( path_out, gitlink, git_str_cstr(path_out)); } @@ -504,7 +504,7 @@ static int find_repo( git_str_clear(gitdir_path); - error = git_path_prettify(&path, start_path, NULL); + error = git_fs_path_prettify(&path, start_path, NULL); if (error < 0) return error; @@ -545,7 +545,7 @@ static int find_repo( goto out; if (is_valid) { - if ((error = git_path_to_dir(&path)) < 0 || + if ((error = git_fs_path_to_dir(&path)) < 0 || (error = git_str_set(gitdir_path, path.ptr, path.size)) < 0) goto out; @@ -578,7 +578,7 @@ static int find_repo( /* Move up one directory. If we're in_dot_git, we'll search the * parent itself next. If we're !in_dot_git, we'll search .git * in the parent directory next (added at the top of the loop). */ - if ((error = git_path_dirname_r(&path, path.ptr)) < 0) + if ((error = git_fs_path_dirname_r(&path, path.ptr)) < 0) goto out; /* Once we've checked the directory (and .git if applicable), @@ -595,8 +595,8 @@ static int find_repo( if (workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { if (!git_str_len(gitdir_path)) git_str_clear(workdir_path); - else if ((error = git_path_dirname_r(workdir_path, path.ptr)) < 0 || - (error = git_path_to_dir(workdir_path)) < 0) + else if ((error = git_fs_path_dirname_r(workdir_path, path.ptr)) < 0 || + (error = git_fs_path_to_dir(workdir_path)) < 0) goto out; } @@ -624,7 +624,7 @@ int git_repository_open_bare( bool is_valid; int error; - if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0 || + if ((error = git_fs_path_prettify_dir(&path, bare_path, NULL)) < 0 || (error = is_valid_repository_path(&is_valid, &path, &common_path)) < 0) return error; @@ -838,7 +838,7 @@ static int repo_is_worktree(unsigned *out, const git_repository *repo) /* A 'gitdir' file inside a git directory is currently * only used when the repository is a working tree. */ - *out = !!git_path_exists(gitdir_link.ptr); + *out = !!git_fs_path_exists(gitdir_link.ptr); git_str_dispose(&gitdir_link); return error; @@ -1600,7 +1600,7 @@ static bool is_filesystem_case_insensitive(const char *gitdir_path) int is_insensitive = -1; if (!git_str_joinpath(&path, gitdir_path, "CoNfIg")) - is_insensitive = git_path_exists(git_str_cstr(&path)); + is_insensitive = git_fs_path_exists(git_str_cstr(&path)); git_str_dispose(&path); return is_insensitive; @@ -1639,7 +1639,7 @@ static bool are_symlinks_supported(const char *wd_path) goto done; #endif - if (!(symlinks = git_path_supports_symlinks(wd_path))) + if (!(symlinks = git_fs_path_supports_symlinks(wd_path))) goto done; done: @@ -1683,7 +1683,7 @@ static int repo_local_config( cfg_path = git_str_cstr(config_dir); /* make LOCAL config if missing */ - if (!git_path_isfile(cfg_path) && + if (!git_fs_path_isfile(cfg_path) && (error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0) return error; @@ -1741,7 +1741,7 @@ static int repo_init_fs_configs( #ifdef GIT_USE_ICONV if ((error = git_config_set_bool( cfg, "core.precomposeunicode", - git_path_does_fs_decompose_unicode(work_dir))) < 0) + git_fs_path_does_decompose_unicode(work_dir))) < 0) return error; /* on non-iconv platforms, don't even set core.precomposeunicode */ #endif @@ -1790,7 +1790,7 @@ static int repo_init_config( goto cleanup; if ((flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK)) - if ((error = git_path_make_relative(&worktree_path, repo_dir)) < 0) + if ((error = git_fs_path_make_relative(&worktree_path, repo_dir)) < 0) goto cleanup; SET_REPO_CONFIG(string, "core.worktree", worktree_path.ptr); @@ -1907,8 +1907,8 @@ static int repo_write_gitlink( git_str path_to_repo = GIT_STR_INIT; struct stat st; - git_path_dirname_r(&buf, to_repo); - git_path_to_dir(&buf); + git_fs_path_dirname_r(&buf, to_repo); + git_fs_path_to_dir(&buf); if (git_str_oom(&buf)) return -1; @@ -1935,7 +1935,7 @@ static int repo_write_gitlink( error = git_str_sets(&path_to_repo, to_repo); if (!error && use_relative_path) - error = git_path_make_relative(&path_to_repo, in_dir); + error = git_fs_path_make_relative(&path_to_repo, in_dir); if (!error) error = git_str_join(&buf, ' ', GIT_FILE_CONTENT_PREFIX, path_to_repo.ptr); @@ -2032,8 +2032,13 @@ static int repo_init_structure( git_str_dispose(&template_buf); git_config_free(cfg); + /* If tdir does not exist, then do not error out. This matches the + * behaviour of git(1), which just prints a warning and continues. + * TODO: issue warning when warning API is available. + * `git` prints to stderr: 'warning: templates not found in /path/to/tdir' + */ if (error < 0) { - if (!default_template) + if (!default_template && error != GIT_ENOTFOUND) return error; /* if template was default, ignore error and use internal */ @@ -2132,11 +2137,11 @@ static int repo_init_directories( if (!is_bare) { if (opts->workdir_path) { - if (git_path_join_unrooted( + if (git_fs_path_join_unrooted( wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0) return -1; } else if (has_dotgit) { - if (git_path_dirname_r(wd_path, repo_path->ptr) < 0) + if (git_fs_path_dirname_r(wd_path, repo_path->ptr) < 0) return -1; } else { git_error_set(GIT_ERROR_REPOSITORY, "cannot pick working directory" @@ -2144,7 +2149,7 @@ static int repo_init_directories( return -1; } - if (git_path_to_dir(wd_path) < 0) + if (git_fs_path_to_dir(wd_path) < 0) return -1; } else { git_str_clear(wd_path); @@ -2204,10 +2209,10 @@ static int repo_init_directories( /* prettify both directories now that they are created */ if (!error) { - error = git_path_prettify_dir(repo_path, repo_path->ptr, NULL); + error = git_fs_path_prettify_dir(repo_path, repo_path->ptr, NULL); if (!error && wd_path->size > 0) - error = git_path_prettify_dir(wd_path, wd_path->ptr, NULL); + error = git_fs_path_prettify_dir(wd_path, wd_path->ptr, NULL); } return error; @@ -2227,7 +2232,7 @@ static int repo_init_head(const char *repo_dir, const char *given) * A template may have set a HEAD; use that unless it's been * overridden by the caller's given initial head setting. */ - if (git_path_exists(head_path.ptr) && !given) + if (git_fs_path_exists(head_path.ptr) && !given) goto out; if (given) { @@ -2628,7 +2633,7 @@ int git_repository__item_path( } if (items[item].directory) { - if (git_path_to_dir(out) < 0) + if (git_fs_path_to_dir(out) < 0) return -1; } @@ -2662,7 +2667,7 @@ int git_repository_workdir_path( } if (!(error = git_str_joinpath(out, repo->workdir, path))) - error = git_path_validate_workdir_buf(repo, out); + error = git_path_validate_str_length(repo, out); return error; } @@ -2682,7 +2687,7 @@ int git_repository_set_workdir( GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(workdir); - if (git_path_prettify_dir(&path, workdir, NULL) < 0) + if (git_fs_path_prettify_dir(&path, workdir, NULL) < 0) return -1; if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0) @@ -2857,8 +2862,8 @@ int git_repository_hashfile( GIT_ASSERT_ARG(path); GIT_ASSERT_ARG(repo); - if ((error = git_path_join_unrooted(&full_path, path, workdir, NULL)) < 0 || - (error = git_path_validate_workdir_buf(repo, &full_path)) < 0) + if ((error = git_fs_path_join_unrooted(&full_path, path, workdir, NULL)) < 0 || + (error = git_path_validate_str_length(repo, &full_path)) < 0) return error; /* @@ -3084,29 +3089,29 @@ int git_repository_state(git_repository *repo) if (git_str_puts(&repo_path, repo->gitdir) < 0) return -1; - if (git_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE)) + if (git_fs_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE)) state = GIT_REPOSITORY_STATE_REBASE_INTERACTIVE; - else if (git_path_contains_dir(&repo_path, GIT_REBASE_MERGE_DIR)) + else if (git_fs_path_contains_dir(&repo_path, GIT_REBASE_MERGE_DIR)) state = GIT_REPOSITORY_STATE_REBASE_MERGE; - else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_REBASING_FILE)) + else if (git_fs_path_contains_file(&repo_path, GIT_REBASE_APPLY_REBASING_FILE)) state = GIT_REPOSITORY_STATE_REBASE; - else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_APPLYING_FILE)) + else if (git_fs_path_contains_file(&repo_path, GIT_REBASE_APPLY_APPLYING_FILE)) state = GIT_REPOSITORY_STATE_APPLY_MAILBOX; - else if (git_path_contains_dir(&repo_path, GIT_REBASE_APPLY_DIR)) + else if (git_fs_path_contains_dir(&repo_path, GIT_REBASE_APPLY_DIR)) state = GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE; - else if (git_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE)) + else if (git_fs_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE)) state = GIT_REPOSITORY_STATE_MERGE; - else if (git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) { + else if (git_fs_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) { state = GIT_REPOSITORY_STATE_REVERT; - if (git_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) { + if (git_fs_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) { state = GIT_REPOSITORY_STATE_REVERT_SEQUENCE; } - } else if (git_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE)) { + } else if (git_fs_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE)) { state = GIT_REPOSITORY_STATE_CHERRYPICK; - if (git_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) { + if (git_fs_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) { state = GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE; } - } else if (git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE)) + } else if (git_fs_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE)) state = GIT_REPOSITORY_STATE_BISECT; git_str_dispose(&repo_path); @@ -3128,9 +3133,9 @@ int git_repository__cleanup_files( path = git_str_cstr(&buf); - if (git_path_isfile(path)) { + if (git_fs_path_isfile(path)) { error = p_unlink(path); - } else if (git_path_isdir(path)) { + } else if (git_fs_path_isdir(path)) { error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS); } @@ -3170,7 +3175,7 @@ int git_repository_is_shallow(git_repository *repo) if ((error = git_str_joinpath(&path, repo->gitdir, "shallow")) < 0) return error; - error = git_path_lstat(path.ptr, &st); + error = git_fs_path_lstat(path.ptr, &st); git_str_dispose(&path); if (error == GIT_ENOTFOUND) { diff --git a/src/repository.h b/src/repository.h index 1ea5a44f7..3c3aa1e8e 100644 --- a/src/repository.h +++ b/src/repository.h @@ -118,14 +118,14 @@ typedef enum { /* core.fsyncObjectFiles */ GIT_FSYNCOBJECTFILES_DEFAULT = GIT_CONFIGMAP_FALSE, /* core.longpaths */ - GIT_LONGPATHS_DEFAULT = GIT_CONFIGMAP_FALSE, + GIT_LONGPATHS_DEFAULT = GIT_CONFIGMAP_FALSE } git_configmap_value; /* internal repository init flags */ enum { GIT_REPOSITORY_INIT__HAS_DOTGIT = (1u << 16), GIT_REPOSITORY_INIT__NATURAL_WD = (1u << 17), - GIT_REPOSITORY_INIT__IS_REINIT = (1u << 18), + GIT_REPOSITORY_INIT__IS_REINIT = (1u << 18) }; /** Internal structure for repository object */ diff --git a/src/revparse.c b/src/revparse.c index 61e836aa8..5d3ff77ed 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -11,6 +11,7 @@ #include "tree.h" #include "refdb.h" #include "regexp.h" +#include "date.h" #include "git2.h" @@ -351,7 +352,7 @@ static int handle_at_syntax(git_object **out, git_reference **ref, const char *s goto cleanup; } - if (git__date_parse(×tamp, curly_braces_content) < 0) { + if (git_date_parse(×tamp, curly_braces_content) < 0) { error = GIT_EINVALIDSPEC; goto cleanup; } @@ -132,13 +132,6 @@ void git_str_dispose(git_str *buf) git_str_init(buf, 0); } -#ifndef GIT_DEPRECATE_HARD -void git_str_free(git_str *buf) -{ - git_str_dispose(buf); -} -#endif - void git_str_clear(git_str *buf) { buf->size = 0; diff --git a/src/submodule.c b/src/submodule.c index b0f7294be..0f4f0726c 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -16,11 +16,12 @@ #include "repository.h" #include "tree.h" #include "iterator.h" -#include "path.h" +#include "fs_path.h" #include "str.h" #include "index.h" #include "worktree.h" #include "clone.h" +#include "path.h" #include "git2/config.h" #include "git2/sys/config.h" @@ -60,7 +61,7 @@ enum { }; enum { GITMODULES_EXISTING = 0, - GITMODULES_CREATE = 1, + GITMODULES_CREATE = 1 }; static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name); @@ -149,7 +150,7 @@ static int is_path_occupied(bool *occupied, git_repository *repo, const char *pa if ((error = git_str_sets(&dir, path)) < 0) goto out; - if ((error = git_path_to_dir(&dir)) < 0) + if ((error = git_fs_path_to_dir(&dir)) < 0) goto out; if ((error = git_index_find_prefix(NULL, index, dir.ptr)) != GIT_ENOTFOUND) { @@ -385,10 +386,10 @@ int git_submodule__lookup_with_cache( if (git_str_join3(&path, '/', git_repository_workdir(repo), name, DOT_GIT) < 0 || - git_path_validate_workdir_buf(NULL, &path) < 0) + git_path_validate_str_length(NULL, &path) < 0) return -1; - if (git_path_exists(path.ptr)) + if (git_fs_path_exists(path.ptr)) error = GIT_EEXISTS; git_str_dispose(&path); @@ -412,17 +413,17 @@ int git_submodule_name_is_valid(git_repository *repo, const char *name, int flag int error, isvalid; if (flags == 0) - flags = GIT_PATH_REJECT_FILESYSTEM_DEFAULTS; + flags = GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS; /* Avoid allocating a new string if we can avoid it */ if (strchr(name, '\\') != NULL) { - if ((error = git_path_normalize_slashes(&buf, name)) < 0) + if ((error = git_fs_path_normalize_slashes(&buf, name)) < 0) return error; } else { git_str_attach_notowned(&buf, name, strlen(name)); } - isvalid = git_path_validate(repo, buf.ptr, 0, flags); + isvalid = git_path_is_valid(repo, buf.ptr, 0, flags); git_str_dispose(&buf); return isvalid; @@ -743,16 +744,16 @@ static int git_submodule__resolve_url( /* We do this in all platforms in case someone on Windows created the .gitmodules */ if (strchr(url, '\\')) { - if ((error = git_path_normalize_slashes(&normalized, url)) < 0) + if ((error = git_fs_path_normalize_slashes(&normalized, url)) < 0) return error; url = normalized.ptr; } - if (git_path_is_relative(url)) { + if (git_fs_path_is_relative(url)) { if (!(error = get_url_base(out, repo))) - error = git_path_apply_relative(out, url); + error = git_fs_path_apply_relative(out, url); } else if (strchr(url, ':') != NULL || url[0] == '/') { error = git_str_sets(out, url); } else { @@ -805,7 +806,7 @@ int git_submodule_add_setup( if (git__prefixcmp(path, git_repository_workdir(repo)) == 0) path += strlen(git_repository_workdir(repo)); - if (git_path_root(path) >= 0) { + if (git_fs_path_root(path) >= 0) { git_error_set(GIT_ERROR_SUBMODULE, "submodule path must be a relative path"); error = -1; goto cleanup; @@ -846,8 +847,8 @@ int git_submodule_add_setup( /* if the repo does not already exist, then init a new repo and add it. * Otherwise, just add the existing repo. */ - if (!(git_path_exists(name.ptr) && - git_path_contains(&name, DOT_GIT))) { + if (!(git_fs_path_exists(name.ptr) && + git_fs_path_contains(&name, DOT_GIT))) { /* resolve the actual URL to use */ if ((error = git_submodule__resolve_url(&real_url, repo, url)) < 0) @@ -1568,13 +1569,13 @@ static int git_submodule__open( sm->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID; else git_error_clear(); - } else if (git_path_exists(path.ptr)) { + } else if (git_fs_path_exists(path.ptr)) { sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED | GIT_SUBMODULE_STATUS_IN_WD; } else { git_str_rtruncate_at_char(&path, '/'); /* remove "/.git" */ - if (git_path_isdir(path.ptr)) + if (git_fs_path_isdir(path.ptr)) sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED; } @@ -2105,10 +2106,10 @@ static int submodule_load_from_wd_lite(git_submodule *sm) if (git_repository_workdir_path(&path, sm->repo, sm->path) < 0) return -1; - if (git_path_isdir(path.ptr)) + if (git_fs_path_isdir(path.ptr)) sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED; - if (git_path_contains(&path, DOT_GIT)) + if (git_fs_path_contains(&path, DOT_GIT)) sm->flags |= GIT_SUBMODULE_STATUS_IN_WD; git_str_dispose(&path); @@ -2160,7 +2161,7 @@ static git_config_backend *open_gitmodules( if (git_repository_workdir_path(&path, repo, GIT_MODULES_FILE) != 0) return NULL; - if (okay_to_create || git_path_isfile(path.ptr)) { + if (okay_to_create || git_fs_path_isfile(path.ptr)) { /* git_config_backend_from_file should only fail if OOM */ if (git_config_backend_from_file(&mods, path.ptr) < 0) mods = NULL; diff --git a/src/submodule.h b/src/submodule.h index b01ff68a2..7fa982486 100644 --- a/src/submodule.h +++ b/src/submodule.h @@ -110,7 +110,7 @@ enum { GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE = (1u << 24), GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE = (1u << 25), GIT_SUBMODULE_STATUS__WD_NOT_SUBMODULE = (1u << 26), - GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27), + GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27) }; #define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \ diff --git a/src/sysdir.c b/src/sysdir.c index 457d7f8a8..84d212e01 100644 --- a/src/sysdir.c +++ b/src/sysdir.c @@ -9,7 +9,7 @@ #include "runtime.h" #include "str.h" -#include "path.h" +#include "fs_path.h" #include <ctype.h> #if GIT_WIN32 #include "win32/findfile.h" @@ -291,7 +291,7 @@ static int git_sysdir_find_in_dirlist( if (name) GIT_ERROR_CHECK_ERROR(git_str_joinpath(path, path->ptr, name)); - if (git_path_exists(path->ptr)) + if (git_fs_path_exists(path->ptr)) return 0; } diff --git a/src/sysdir.h b/src/sysdir.h index d12bac9d9..43392a022 100644 --- a/src/sysdir.h +++ b/src/sysdir.h @@ -74,7 +74,7 @@ typedef enum { GIT_SYSDIR_XDG = 2, GIT_SYSDIR_PROGRAMDATA = 3, GIT_SYSDIR_TEMPLATE = 4, - GIT_SYSDIR__MAX = 5, + GIT_SYSDIR__MAX = 5 } git_sysdir_t; /** @@ -539,6 +539,8 @@ int git_tag_name_is_valid(int *valid, const char *name) GIT_ASSERT(valid); + *valid = 0; + /* * Discourage tag name starting with dash, * https://github.com/git/git/commit/4f0accd638b8d2 diff --git a/src/thread.h b/src/thread.h index 4b091c0a2..4bbac9fd8 100644 --- a/src/thread.h +++ b/src/thread.h @@ -12,7 +12,7 @@ #if defined(__clang__) # if (__clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 1)) -# error Atomic primitives do not exist on this version of clang; configure libgit2 with -DTHREADSAFE=OFF +# error Atomic primitives do not exist on this version of clang; configure libgit2 with -DUSE_THREADS=OFF # else # define GIT_BUILTIN_ATOMIC # endif @@ -20,7 +20,7 @@ #elif defined(__GNUC__) # if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1)) -# error Atomic primitives do not exist on this version of gcc; configure libgit2 with -DTHREADSAFE=OFF +# error Atomic primitives do not exist on this version of gcc; configure libgit2 with -DUSE_THREADS=OFF # elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) # define GIT_BUILTIN_ATOMIC # else @@ -180,7 +180,7 @@ GIT_INLINE(volatile void *) git_atomic__swap( #if defined(GIT_WIN32) return InterlockedExchangePointer(ptr, newval); #elif defined(GIT_BUILTIN_ATOMIC) - void * volatile foundval = NULL; + void * foundval = NULL; __atomic_exchange(ptr, &newval, &foundval, __ATOMIC_SEQ_CST); return foundval; #elif defined(GIT_BUILTIN_SYNC) diff --git a/src/threadstate.c b/src/threadstate.c index f67cf082b..9e3ef5818 100644 --- a/src/threadstate.c +++ b/src/threadstate.c @@ -36,8 +36,8 @@ static void threadstate_dispose(git_threadstate *threadstate) if (!threadstate) return; - if (threadstate->error_t.message != git_str__initstr) - git__free(threadstate->error_t.message); + if (threadstate->error_t.message != git_str__initstr) + git__free(threadstate->error_t.message); threadstate->error_t.message = NULL; } diff --git a/src/trailer.c b/src/trailer.c index 61cdd1ba1..4761c9922 100644 --- a/src/trailer.c +++ b/src/trailer.c @@ -286,7 +286,7 @@ enum trailer_state { S_VALUE = 4, S_VALUE_NL = 5, S_VALUE_END = 6, - S_IGNORE = 7, + S_IGNORE = 7 }; #define NEXT(st) { state = (st); ptr++; continue; } diff --git a/src/transaction.c b/src/transaction.c index 98fa1ba90..ccffa9984 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -23,7 +23,7 @@ typedef enum { TRANSACTION_NONE, TRANSACTION_REFS, - TRANSACTION_CONFIG, + TRANSACTION_CONFIG } transaction_t; typedef struct { diff --git a/src/transport.c b/src/transport.c index fa1d35fce..640ccacae 100644 --- a/src/transport.c +++ b/src/transport.c @@ -12,7 +12,7 @@ #include "git2/net.h" #include "git2/transport.h" #include "git2/sys/transport.h" -#include "path.h" +#include "fs_path.h" typedef struct transport_definition { char *prefix; @@ -82,7 +82,7 @@ static int transport_find_fn( * to a directory and if so assume local path, else assume SSH */ /* Check to see if the path points to a file on the local file system */ - if (!definition && git_path_exists(url) && git_path_isdir(url)) + if (!definition && git_fs_path_exists(url) && git_fs_path_isdir(url)) definition = &local_transport_definition; #endif @@ -98,7 +98,7 @@ static int transport_find_fn( #ifndef GIT_WIN32 /* Check to see if the path points to a file on the local file system */ - if (!definition && git_path_exists(url) && git_path_isdir(url)) + if (!definition && git_fs_path_exists(url) && git_fs_path_isdir(url)) definition = &local_transport_definition; #endif diff --git a/src/transports/auth.h b/src/transports/auth.h index 824d7198c..64680cc53 100644 --- a/src/transports/auth.h +++ b/src/transports/auth.h @@ -15,7 +15,7 @@ typedef enum { GIT_HTTP_AUTH_BASIC = 1, GIT_HTTP_AUTH_NEGOTIATE = 2, - GIT_HTTP_AUTH_NTLM = 4, + GIT_HTTP_AUTH_NTLM = 4 } git_http_auth_t; typedef struct git_http_auth_context git_http_auth_context; diff --git a/src/transports/local.c b/src/transports/local.c index 0656ea592..0983914b1 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -10,7 +10,7 @@ #include "pack-objects.h" #include "refs.h" #include "posix.h" -#include "path.h" +#include "fs_path.h" #include "repository.h" #include "odb.h" #include "push.h" @@ -226,7 +226,7 @@ static int local_connect( t->flags = flags; /* 'url' may be a url or path; convert to a path */ - if ((error = git_path_from_url_or_path(&buf, url)) < 0) { + if ((error = git_fs_path_from_url_or_path(&buf, url)) < 0) { git_str_dispose(&buf); return error; } @@ -352,7 +352,7 @@ static int local_push( GIT_UNUSED(cbs); /* 'push->remote->url' may be a url or path; convert to a path */ - if ((error = git_path_from_url_or_path(&buf, push->remote->url)) < 0) { + if ((error = git_fs_path_from_url_or_path(&buf, push->remote->url)) < 0) { git_str_dispose(&buf); return error; } diff --git a/src/transports/smart.h b/src/transports/smart.h index 225e64996..a9cab31ac 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -46,7 +46,7 @@ typedef enum { GIT_PKT_PROGRESS, GIT_PKT_OK, GIT_PKT_NG, - GIT_PKT_UNPACK, + GIT_PKT_UNPACK } git_pkt_type; /* Used for multi_ack and multi_ack_detailed */ diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index efbaf0298..60f536fdb 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -99,7 +99,7 @@ typedef enum { GIT_WINHTTP_AUTH_BASIC = 1, GIT_WINHTTP_AUTH_NTLM = 2, GIT_WINHTTP_AUTH_NEGOTIATE = 4, - GIT_WINHTTP_AUTH_DIGEST = 8, + GIT_WINHTTP_AUTH_DIGEST = 8 } winhttp_authmechanism_t; typedef struct { diff --git a/src/tree.c b/src/tree.c index c1e39158d..b8e82d485 100644 --- a/src/tree.c +++ b/src/tree.c @@ -13,6 +13,7 @@ #include "futils.h" #include "tree-cache.h" #include "index.h" +#include "path.h" #define DEFAULT_TREE_SIZE 16 #define MAX_FILEMODE_BYTES 6 @@ -54,8 +55,8 @@ GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode) static int valid_entry_name(git_repository *repo, const char *filename) { return *filename != '\0' && - git_path_validate(repo, filename, 0, - GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT | GIT_PATH_REJECT_SLASH); + git_path_is_valid(repo, filename, 0, + GIT_FS_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT | GIT_FS_PATH_REJECT_SLASH); } static int entry_sort_cmp(const void *a, const void *b) @@ -63,7 +64,7 @@ static int entry_sort_cmp(const void *a, const void *b) const git_tree_entry *e1 = (const git_tree_entry *)a; const git_tree_entry *e2 = (const git_tree_entry *)b; - return git_path_cmp( + return git_fs_path_cmp( e1->filename, e1->filename_len, git_tree_entry__is_tree(e1), e2->filename, e2->filename_len, git_tree_entry__is_tree(e2), git__strncmp); @@ -1171,7 +1172,7 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli /* Figure out how much we need to change from the previous tree */ if (last_update) - common_prefix = git_path_common_dirlen(last_update->path, update->path); + common_prefix = git_fs_path_common_dirlen(last_update->path, update->path); /* * The entries are sorted, so when we find we're no @@ -1233,7 +1234,7 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli { /* Make sure we're replacing something of the same type */ tree_stack_entry *last = git_array_last(stack); - char *basename = git_path_basename(update->path); + char *basename = git_fs_path_basename(update->path); const git_tree_entry *e = git_treebuilder_get(last->bld, basename); if (e && git_tree_entry_type(e) != git_object__type_from_filemode(update->filemode)) { git__free(basename); @@ -1252,7 +1253,7 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli case GIT_TREE_UPDATE_REMOVE: { tree_stack_entry *last = git_array_last(stack); - char *basename = git_path_basename(update->path); + char *basename = git_fs_path_basename(update->path); error = git_treebuilder_remove(last->bld, basename); git__free(basename); break; diff --git a/src/util.c b/src/util.c index 2b1dadfe8..e06d4ca09 100644 --- a/src/util.c +++ b/src/util.c @@ -18,7 +18,7 @@ # endif # include <windows.h> -# ifdef HAVE_QSORT_S +# ifdef GIT_QSORT_S # include <search.h> # endif #endif @@ -673,7 +673,7 @@ size_t git__unescape(char *str) return (pos - str); } -#if defined(HAVE_QSORT_S) || defined(HAVE_QSORT_R_BSD) +#if defined(GIT_QSORT_S) || defined(GIT_QSORT_R_BSD) typedef struct { git__sort_r_cmp cmp; void *payload; @@ -688,9 +688,9 @@ static int GIT_LIBGIT2_CALL git__qsort_r_glue_cmp( #endif -#if !defined(HAVE_QSORT_R_BSD) && \ - !defined(HAVE_QSORT_R_GNU) && \ - !defined(HAVE_QSORT_S) +#if !defined(GIT_QSORT_R_BSD) && \ + !defined(GIT_QSORT_R_GNU) && \ + !defined(GIT_QSORT_S) static void swap(uint8_t *a, uint8_t *b, size_t elsize) { char tmp[256]; @@ -721,12 +721,12 @@ static void insertsort( void git__qsort_r( void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) { -#if defined(HAVE_QSORT_R_BSD) +#if defined(GIT_QSORT_R_BSD) git__qsort_r_glue glue = { cmp, payload }; qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp); -#elif defined(HAVE_QSORT_R_GNU) +#elif defined(GIT_QSORT_R_GNU) qsort_r(els, nel, elsize, cmp, payload); -#elif defined(HAVE_QSORT_S) +#elif defined(GIT_QSORT_S) git__qsort_r_glue glue = { cmp, payload }; qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue); #else diff --git a/src/util.h b/src/util.h index 30cdd0ddf..141779ade 100644 --- a/src/util.h +++ b/src/util.h @@ -37,8 +37,6 @@ # define GIT_CONTAINER_OF(ptr, type, member) (type *)(ptr) #endif -#define GIT_DATE_RFC2822_SZ 32 - /** * Return the length of a constant string. * We are aware that `strlen` performs the same task and is usually @@ -295,26 +293,6 @@ GIT_INLINE(bool) git__isxdigit(int c) extern int git__parse_bool(int *out, const char *value); /* - * Parse a string into a value as a git_time_t. - * - * Sample valid input: - * - "yesterday" - * - "July 17, 2003" - * - "2003-7-17 08:23" - */ -extern int git__date_parse(git_time_t *out, const char *date); - -/* - * Format a git_time as a RFC2822 string - * - * @param out buffer to store formatted date; a '\\0' terminator will automatically be added. - * @param len size of the buffer; should be atleast `GIT_DATE_RFC2822_SZ` in size; - * @param date the date to be formatted - * @return 0 if successful; -1 on error - */ -extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date); - -/* * Unescapes a string in-place. * * Edge cases behavior: diff --git a/src/vector.h b/src/vector.h index 3dcec3d13..ae3c79a4c 100644 --- a/src/vector.h +++ b/src/vector.h @@ -13,7 +13,7 @@ typedef int (*git_vector_cmp)(const void *, const void *); enum { GIT_VECTOR_SORTED = (1u << 0), - GIT_VECTOR_FLAG_MAX = (1u << 1), + GIT_VECTOR_FLAG_MAX = (1u << 1) }; typedef struct git_vector { diff --git a/src/win32/findfile.c b/src/win32/findfile.c index caa79398a..d4afc4acc 100644 --- a/src/win32/findfile.c +++ b/src/win32/findfile.c @@ -9,7 +9,7 @@ #include "path_w32.h" #include "utf-conv.h" -#include "path.h" +#include "fs_path.h" #define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" @@ -44,7 +44,7 @@ static int win32_path_to_8(git_str *dest, const wchar_t *src) } /* Convert backslashes to forward slashes */ - git_path_mkposix(utf8_path); + git_fs_path_mkposix(utf8_path); return git_str_sets(dest, utf8_path); } diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c index e6640c85e..5d7ad11f6 100644 --- a/src/win32/path_w32.c +++ b/src/win32/path_w32.c @@ -7,7 +7,7 @@ #include "path_w32.h" -#include "path.h" +#include "fs_path.h" #include "utf-conv.h" #include "posix.h" #include "reparse.h" @@ -57,7 +57,7 @@ static wchar_t *path__skip_server(wchar_t *path) wchar_t *c; for (c = path; *c; c++) { - if (git_path_is_dirsep(*c)) + if (git_fs_path_is_dirsep(*c)) return c + 1; } @@ -71,9 +71,9 @@ static wchar_t *path__skip_prefix(wchar_t *path) if (wcsncmp(path, L"UNC\\", 4) == 0) path = path__skip_server(path + 4); - else if (git_path_is_absolute(path)) + else if (git_fs_path_is_absolute(path)) path += PATH__ABSOLUTE_LEN; - } else if (git_path_is_absolute(path)) { + } else if (git_fs_path_is_absolute(path)) { path += PATH__ABSOLUTE_LEN; } else if (path__is_unc(path)) { path = path__skip_server(path + 2); @@ -204,7 +204,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src) dest += PATH__NT_NAMESPACE_LEN; /* See if this is an absolute path (beginning with a drive letter) */ - if (git_path_is_absolute(src)) { + if (git_fs_path_is_absolute(src)) { if (git__utf8_to_16(dest, GIT_WIN_PATH_MAX, src) < 0) goto on_error; } @@ -228,7 +228,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src) if (path__cwd(dest, GIT_WIN_PATH_MAX) < 0) goto on_error; - if (!git_path_is_absolute(dest)) { + if (!git_fs_path_is_absolute(dest)) { errno = ENOENT; goto on_error; } @@ -266,7 +266,7 @@ int git_win32_path_relative_from_utf8(git_win32_path out, const char *src) int len; /* Handle absolute paths */ - if (git_path_is_absolute(src) || + if (git_fs_path_is_absolute(src) || path__is_nt_namespace(src) || path__is_unc(src) || path__startswith_slash(src)) { @@ -305,7 +305,7 @@ int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src) if ((len = git__utf16_to_8(out, GIT_WIN_PATH_UTF8, src)) < 0) return len; - git_path_mkposix(dest); + git_fs_path_mkposix(dest); return len; } diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index f26983d97..ba46b5ea9 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -9,10 +9,9 @@ #include "../posix.h" #include "../futils.h" -#include "path.h" +#include "fs_path.h" #include "path_w32.h" #include "utf-conv.h" -#include "repository.h" #include "reparse.h" #include <errno.h> #include <io.h> @@ -418,10 +417,10 @@ static bool target_is_dir(const char *target, const char *path) git_win32_path resolved_w; bool isdir = true; - if (git_path_is_absolute(target)) + if (git_fs_path_is_absolute(target)) git_win32_path_from_utf8(resolved_w, target); - else if (git_path_dirname_r(&resolved, path) < 0 || - git_path_apply_relative(&resolved, target) < 0 || + else if (git_fs_path_dirname_r(&resolved, path) < 0 || + git_fs_path_apply_relative(&resolved, target) < 0 || git_win32_path_from_utf8(resolved_w, resolved.ptr) < 0) goto out; @@ -661,7 +660,7 @@ int p_getcwd(char *buffer_out, size_t size) return -1; } - git_path_mkposix(buffer_out); + git_fs_path_mkposix(buffer_out); return 0; } @@ -821,7 +820,7 @@ char *p_realpath(const char *orig_path, char *buffer) if (git_win32_path_to_utf8(buffer, buffer_w) < 0) return NULL; - git_path_mkposix(buffer); + git_fs_path_mkposix(buffer); return buffer; } diff --git a/src/worktree.c b/src/worktree.c index 92a0900b0..e08d6d401 100644 --- a/src/worktree.c +++ b/src/worktree.c @@ -9,6 +9,7 @@ #include "buf.h" #include "repository.h" +#include "path.h" #include "git2/branch.h" #include "git2/commit.h" @@ -22,9 +23,9 @@ static bool is_worktree_dir(const char *dir) if (git_str_sets(&buf, dir) < 0) return -1; - error = git_path_contains_file(&buf, "commondir") - && git_path_contains_file(&buf, "gitdir") - && git_path_contains_file(&buf, "HEAD"); + error = git_fs_path_contains_file(&buf, "commondir") + && git_fs_path_contains_file(&buf, "gitdir") + && git_fs_path_contains_file(&buf, "HEAD"); git_str_dispose(&buf); return error; @@ -46,9 +47,9 @@ int git_worktree_list(git_strarray *wts, git_repository *repo) if ((error = git_str_joinpath(&path, repo->commondir, "worktrees/")) < 0) goto exit; - if (!git_path_exists(path.ptr) || git_path_is_empty_dir(path.ptr)) + if (!git_fs_path_exists(path.ptr) || git_fs_path_is_empty_dir(path.ptr)) goto exit; - if ((error = git_path_dirload(&worktrees, path.ptr, path.size, 0x0)) < 0) + if ((error = git_fs_path_dirload(&worktrees, path.ptr, path.size, 0x0)) < 0) goto exit; len = path.size; @@ -86,12 +87,12 @@ char *git_worktree__read_link(const char *base, const char *file) git_str_rtrim(&buf); - if (!git_path_is_relative(buf.ptr)) + if (!git_fs_path_is_relative(buf.ptr)) return git_str_detach(&buf); if (git_str_sets(&path, base) < 0) goto err; - if (git_path_apply_relative(&path, buf.ptr) < 0) + if (git_fs_path_apply_relative(&path, buf.ptr) < 0) goto err; git_str_dispose(&buf); @@ -136,7 +137,7 @@ static int open_worktree_dir(git_worktree **out, const char *parent, const char goto out; } - if ((error = git_path_validate_workdir(NULL, dir)) < 0) + if ((error = git_path_validate_length(NULL, dir)) < 0) goto out; if ((wt = git__calloc(1, sizeof(*wt))) == NULL) { @@ -148,12 +149,12 @@ static int open_worktree_dir(git_worktree **out, const char *parent, const char (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL || (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL || (parent && (wt->parent_path = git__strdup(parent)) == NULL) || - (wt->worktree_path = git_path_dirname(wt->gitlink_path)) == NULL) { + (wt->worktree_path = git_fs_path_dirname(wt->gitlink_path)) == NULL) { error = -1; goto out; } - if ((error = git_path_prettify_dir(&gitdir, dir, NULL)) < 0) + if ((error = git_fs_path_prettify_dir(&gitdir, dir, NULL)) < 0) goto out; wt->gitdir_path = git_str_detach(&gitdir); @@ -214,11 +215,11 @@ int git_worktree_open_from_repository(git_worktree **out, git_repository *repo) gitdir = git_repository_path(repo); commondir = git_repository_commondir(repo); - if ((error = git_path_prettify_dir(&parent, "..", commondir)) < 0) + if ((error = git_fs_path_prettify_dir(&parent, "..", commondir)) < 0) goto out; /* The name is defined by the last component in '.git/worktree/%s' */ - name = git_path_basename(gitdir); + name = git_fs_path_basename(gitdir); if ((error = open_worktree_dir(out, parent.ptr, gitdir, name)) < 0) goto out; @@ -255,21 +256,21 @@ int git_worktree_validate(const git_worktree *wt) return GIT_ERROR; } - if (wt->parent_path && !git_path_exists(wt->parent_path)) { + if (wt->parent_path && !git_fs_path_exists(wt->parent_path)) { git_error_set(GIT_ERROR_WORKTREE, "worktree parent directory ('%s') does not exist ", wt->parent_path); return GIT_ERROR; } - if (!git_path_exists(wt->commondir_path)) { + if (!git_fs_path_exists(wt->commondir_path)) { git_error_set(GIT_ERROR_WORKTREE, "worktree common directory ('%s') does not exist ", wt->commondir_path); return GIT_ERROR; } - if (!git_path_exists(wt->worktree_path)) { + if (!git_fs_path_exists(wt->worktree_path)) { git_error_set(GIT_ERROR_WORKTREE, "worktree directory '%s' does not exist", wt->worktree_path); @@ -337,20 +338,20 @@ int git_worktree_add(git_worktree **out, git_repository *repo, /* Create gitdir directory ".git/worktrees/<name>" */ if ((err = git_str_joinpath(&gitdir, repo->commondir, "worktrees")) < 0) goto out; - if (!git_path_exists(gitdir.ptr)) + if (!git_fs_path_exists(gitdir.ptr)) if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0) goto out; if ((err = git_str_joinpath(&gitdir, gitdir.ptr, name)) < 0) goto out; if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0) goto out; - if ((err = git_path_prettify_dir(&gitdir, gitdir.ptr, NULL)) < 0) + if ((err = git_fs_path_prettify_dir(&gitdir, gitdir.ptr, NULL)) < 0) goto out; /* Create worktree work dir */ if ((err = git_futils_mkdir(worktree, 0755, GIT_MKDIR_EXCL)) < 0) goto out; - if ((err = git_path_prettify_dir(&wddir, worktree, NULL)) < 0) + if ((err = git_fs_path_prettify_dir(&wddir, worktree, NULL)) < 0) goto out; if (wtopts.lock) { @@ -375,7 +376,7 @@ int git_worktree_add(git_worktree **out, git_repository *repo, goto out; /* Create gitdir files */ - if ((err = git_path_prettify_dir(&buf, repo->commondir, NULL) < 0) + if ((err = git_fs_path_prettify_dir(&buf, repo->commondir, NULL) < 0) || (err = git_str_putc(&buf, '\n')) < 0 || (err = write_wtfile(gitdir.ptr, "commondir", &buf)) < 0) goto out; @@ -494,7 +495,7 @@ static int git_worktree__is_locked(git_str *reason, const git_worktree *wt) if ((error = git_str_joinpath(&path, wt->gitdir_path, "locked")) < 0) goto out; - locked = git_path_exists(path.ptr); + locked = git_fs_path_exists(path.ptr); if (locked && reason && (error = git_futils_readbuffer(reason, path.ptr)) < 0) goto out; @@ -614,7 +615,7 @@ int git_worktree_prune(git_worktree *wt, /* Delete gitdir in parent repository */ if ((err = git_str_join3(&path, '/', wt->commondir_path, "worktrees", wt->name)) < 0) goto out; - if (!git_path_exists(path.ptr)) + if (!git_fs_path_exists(path.ptr)) { git_error_set(GIT_ERROR_WORKTREE, "worktree gitdir '%s' does not exist", path.ptr); err = -1; @@ -626,15 +627,15 @@ int git_worktree_prune(git_worktree *wt, /* Skip deletion of the actual working tree if it does * not exist or deletion was not requested */ if ((popts.flags & GIT_WORKTREE_PRUNE_WORKING_TREE) == 0 || - !git_path_exists(wt->gitlink_path)) + !git_fs_path_exists(wt->gitlink_path)) { goto out; } - if ((wtpath = git_path_dirname(wt->gitlink_path)) == NULL) + if ((wtpath = git_fs_path_dirname(wt->gitlink_path)) == NULL) goto out; git_str_attach(&path, wtpath, 0); - if (!git_path_exists(path.ptr)) + if (!git_fs_path_exists(path.ptr)) { git_error_set(GIT_ERROR_WORKTREE, "working tree '%s' does not exist", path.ptr); err = -1; diff --git a/src/zstream.h b/src/zstream.h index 3a59d9a36..3f8b1c72f 100644 --- a/src/zstream.h +++ b/src/zstream.h @@ -15,7 +15,7 @@ typedef enum { GIT_ZSTREAM_INFLATE, - GIT_ZSTREAM_DEFLATE, + GIT_ZSTREAM_DEFLATE } git_zstream_t; typedef struct { |
