int git_path_find_dir(git_buf *dir, const char *path, const char *base) { int error = GIT_SUCCESS; if (base != NULL && git_path_root(path) < 0) error = git_buf_joinpath(dir, base, path); else error = git_buf_sets(dir, path); if (error == GIT_SUCCESS) { char buf[GIT_PATH_MAX]; if (p_realpath(dir->ptr, buf) != NULL) error = git_buf_sets(dir, buf); } /* call dirname if this is not a directory */ if (error == GIT_SUCCESS && git_path_isdir(dir->ptr) != GIT_SUCCESS) if (git_path_dirname_r(dir, dir->ptr) < GIT_SUCCESS) error = git_buf_lasterror(dir); if (error == GIT_SUCCESS) error = git_path_to_dir(dir); return error; }
/* * Read the contents of `file_path` and set `path_out` to the repo dir that * it points to. Before calling, set `path_out` to the base directory that * should be used if the contents of `file_path` are a relative path. */ static int read_gitfile(git_buf *path_out, const char *file_path) { int error = 0; git_buf file = GIT_BUF_INIT; size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX); assert(path_out && file_path); if (git_futils_readbuffer(&file, file_path) < 0) return -1; git_buf_rtrim(&file); /* apparently on Windows, some people use backslashes in paths */ git_path_mkposix(file.ptr); if (git_buf_len(&file) <= prefix_len || memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) { giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path); error = -1; } else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { const char *gitlink = git_buf_cstr(&file) + prefix_len; while (*gitlink && git__isspace(*gitlink)) gitlink++; error = git_path_prettify_dir( path_out, gitlink, git_buf_cstr(path_out)); } git_buf_free(&file); return error; }
static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path) { int error; const git_config_entry *ce; git_buf worktree = GIT_BUF_INIT; if (repo->is_bare) return 0; if ((error = git_config__lookup_entry( &ce, config, "core.worktree", false)) < 0) return error; if (ce && ce->value) { if ((error = git_path_prettify_dir( &worktree, ce->value, repo->path_repository)) < 0) return error; repo->workdir = git_buf_detach(&worktree); } else if (parent_path && git_path_isdir(parent_path->ptr)) repo->workdir = git_buf_detach(parent_path); else { if (git_path_dirname_r(&worktree, repo->path_repository) < 0 || git_path_to_dir(&worktree) < 0) return -1; repo->workdir = git_buf_detach(&worktree); } GITERR_CHECK_ALLOC(repo->workdir); return 0; }
void test_submodule_init__relative_url(void) { git_submodule *sm; git_config *cfg; git_buf absolute_url = GIT_BUF_INIT; const char *config_url; g_repo = setup_fixture_submodule_simple(); cl_assert(git_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0); cl_git_pass(git_buf_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git")); cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); /* verify that the .gitmodules is set with an absolute path*/ cl_assert_equal_s("../testrepo.git", git_submodule_url(sm)); /* init and verify that absolute path is written to .git/config */ cl_git_pass(git_submodule_init(sm, false)); cl_git_pass(git_repository_config_snapshot(&cfg, g_repo)); cl_git_pass(git_config_get_string(&config_url, cfg, "submodule.testrepo.url")); cl_assert_equal_s(absolute_url.ptr, config_url); git_buf_free(&absolute_url); git_config_free(cfg); git_submodule_free(sm); }
/* * Read the contents of `file_path` and set `path_out` to the repo dir that * it points to. Before calling, set `path_out` to the base directory that * should be used if the contents of `file_path` are a relative path. */ static int read_gitfile(git_buf *path_out, const char *file_path) { int error = 0; git_buf file = GIT_BUF_INIT; size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX); assert(path_out && file_path); if (git_futils_readbuffer(&file, file_path) < 0) return -1; git_buf_rtrim(&file); if (file.size <= prefix_len || memcmp(file.ptr, GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) { giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path); error = -1; } else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { const char *gitlink = ((const char *)file.ptr) + prefix_len; while (*gitlink && git__isspace(*gitlink)) gitlink++; error = git_path_prettify_dir(path_out, gitlink, path_out->ptr); } git_buf_free(&file); return error; }
static int load_workdir(git_repository *repo, git_buf *parent_path) { int error; git_config *config; const char *worktree; git_buf worktree_buf = GIT_BUF_INIT; if (repo->is_bare) return 0; if (git_repository_config__weakptr(&config, repo) < 0) return -1; error = git_config_get_string(&worktree, config, "core.worktree"); if (!error && worktree != NULL) repo->workdir = git__strdup(worktree); else if (error != GIT_ENOTFOUND) return error; else { giterr_clear(); if (parent_path && git_path_isdir(parent_path->ptr)) repo->workdir = git_buf_detach(parent_path); else { git_path_dirname_r(&worktree_buf, repo->path_repository); git_path_to_dir(&worktree_buf); repo->workdir = git_buf_detach(&worktree_buf); } } GITERR_CHECK_ALLOC(repo->workdir); return 0; }
char *git_path_dirname(const char *path) { git_buf buf = GIT_BUF_INIT; char *dirname; git_path_dirname_r(&buf, path); dirname = git_buf_detach(&buf); git_buf_free(&buf); /* avoid memleak if error occurs */ return dirname; }
static void check_dirname(const char *A, const char *B) { git_buf dir = GIT_BUF_INIT; char *dir2; cl_assert(git_path_dirname_r(&dir, A) >= 0); cl_assert_equal_s(B, dir.ptr); git_buf_dispose(&dir); cl_assert((dir2 = git_path_dirname(A)) != NULL); cl_assert_equal_s(B, dir2); git__free(dir2); }
GIT_INLINE(int) mkdir_canonicalize( git_buf *path, uint32_t flags) { ssize_t root_len; if (path->size == 0) { giterr_set(GITERR_OS, "attempt to create empty path"); return -1; } /* Trim trailing slashes (except the root) */ if ((root_len = git_path_root(path->ptr)) < 0) root_len = 0; else root_len++; while (path->size > (size_t)root_len && path->ptr[path->size - 1] == '/') path->ptr[--path->size] = '\0'; /* 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); flags |= GIT_MKDIR_SKIP_LAST; } if ((flags & GIT_MKDIR_SKIP_LAST) != 0) { git_path_dirname_r(path, path->ptr); } /* We were either given the root path (or trimmed it to * the root), we don't have anything to do. */ if (path->size <= (size_t)root_len) git_buf_clear(path); return 0; }
int git_futils_mkpath2file(const char *file_path, const mode_t mode) { int result = 0; git_buf target_folder = GIT_BUF_INIT; if (git_path_dirname_r(&target_folder, file_path) < 0) return -1; /* Does the containing folder exist? */ if (git_path_isdir(target_folder.ptr) == false) /* Let's create the tree structure */ result = git_futils_mkdir_r(target_folder.ptr, NULL, mode); git_buf_free(&target_folder); return result; }
int git_path_find_dir(git_buf *dir, const char *path, const char *base) { int error = git_path_join_unrooted(dir, path, base, NULL); if (!error) { char buf[GIT_PATH_MAX]; if (p_realpath(dir->ptr, buf) != NULL) error = git_buf_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_futils_mkpath2file(const char *file_path) { const int mode = 0755; /* or 0777 ? */ int error = GIT_SUCCESS; char target_folder_path[GIT_PATH_MAX]; error = git_path_dirname_r(target_folder_path, sizeof(target_folder_path), file_path); if (error < GIT_SUCCESS) return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path); /* Does the containing folder exist? */ if (git_futils_isdir(target_folder_path)) { git_path_join(target_folder_path, target_folder_path, ""); /* Ensure there's a trailing slash */ /* Let's create the tree structure */ error = git_futils_mkdir_r(target_folder_path, mode); if (error < GIT_SUCCESS) return error; /* The callee already takes care of setting the correct error message. */ } return GIT_SUCCESS; }
void test_submodule_init__relative_url_detached_head(void) { git_submodule *sm; git_config *cfg; git_buf absolute_url = GIT_BUF_INIT; const char *config_url; git_reference *head_ref = NULL; git_object *head_commit = NULL; g_repo = setup_fixture_submodule_simple(); /* Put the parent repository into a detached head state. */ cl_git_pass(git_repository_head(&head_ref, g_repo)); cl_git_pass(git_reference_peel(&head_commit, head_ref, GIT_OBJ_COMMIT)); cl_git_pass(git_repository_set_head_detached(g_repo, git_commit_id((git_commit *)head_commit))); cl_assert(git_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0); cl_git_pass(git_buf_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git")); cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); /* verify that the .gitmodules is set with an absolute path*/ cl_assert_equal_s("../testrepo.git", git_submodule_url(sm)); /* init and verify that absolute path is written to .git/config */ cl_git_pass(git_submodule_init(sm, false)); cl_git_pass(git_repository_config_snapshot(&cfg, g_repo)); cl_git_pass(git_config_get_string(&config_url, cfg, "submodule.testrepo.url")); cl_assert_equal_s(absolute_url.ptr, config_url); git_buf_free(&absolute_url); git_config_free(cfg); git_object_free(head_commit); git_reference_free(head_ref); git_submodule_free(sm); }
int git_futils_mkdir( const char *path, mode_t mode, uint32_t flags) { git_buf make_path = GIT_BUF_INIT, parent_path = GIT_BUF_INIT; const char *relative; struct git_futils_mkdir_options opts = { 0 }; struct stat st; size_t depth = 0; int len = 0, root_len, error; if ((error = git_buf_puts(&make_path, path)) < 0 || (error = mkdir_canonicalize(&make_path, flags)) < 0 || (error = git_buf_puts(&parent_path, make_path.ptr)) < 0 || make_path.size == 0) goto done; root_len = git_path_root(make_path.ptr); /* find the first parent directory that exists. this will be used * as the base to dirname_relative. */ for (relative = make_path.ptr; parent_path.size; ) { error = p_lstat(parent_path.ptr, &st); if (error == 0) { break; } else if (errno != ENOENT) { giterr_set(GITERR_OS, "failed to stat '%s'", parent_path.ptr); goto done; } depth++; /* examine the parent of the current path */ if ((len = git_path_dirname_r(&parent_path, parent_path.ptr)) < 0) { error = len; goto done; } assert(len); /* we've walked all the given path's parents and it's either relative * or rooted. either way, give up and make the entire path. */ if ((len == 1 && parent_path.ptr[0] == '.') || len == root_len+1) { relative = make_path.ptr; break; } relative = make_path.ptr + len + 1; /* not recursive? just make this directory relative to its parent. */ if ((flags & GIT_MKDIR_PATH) == 0) break; } /* we found an item at the location we're trying to create, * validate it. */ if (depth == 0) { error = mkdir_validate_dir(make_path.ptr, &st, mode, flags, &opts); if (!error) error = mkdir_validate_mode( make_path.ptr, &st, true, mode, flags, &opts); goto done; } /* we already took `SKIP_LAST` and `SKIP_LAST2` into account when * canonicalizing `make_path`. */ flags &= ~(GIT_MKDIR_SKIP_LAST2 | GIT_MKDIR_SKIP_LAST); error = git_futils_mkdir_relative(relative, parent_path.size ? parent_path.ptr : NULL, mode, flags, &opts); done: git_buf_free(&make_path); git_buf_free(&parent_path); return error; }
int git_futils_mkdir_ext( const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts) { int error = -1; git_buf make_path = GIT_BUF_INIT; ssize_t root = 0, min_root_len, root_len; char lastch = '/', *tail; struct stat st; /* build path and find "root" where we should start calling mkdir */ if (git_path_join_unrooted(&make_path, path, base, &root) < 0) return -1; if (make_path.size == 0) { giterr_set(GITERR_OS, "Attempt to create empty path"); goto done; } /* Trim trailing slashes (except the root) */ if ((root_len = git_path_root(make_path.ptr)) < 0) root_len = 0; else root_len++; while (make_path.size > (size_t)root_len && make_path.ptr[make_path.size - 1] == '/') make_path.ptr[--make_path.size] = '\0'; /* if we are not supposed to made the last element, truncate it */ if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) { git_path_dirname_r(&make_path, make_path.ptr); flags |= GIT_MKDIR_SKIP_LAST; } if ((flags & GIT_MKDIR_SKIP_LAST) != 0) { git_path_dirname_r(&make_path, make_path.ptr); } /* We were either given the root path (or trimmed it to * the root), we don't have anything to do. */ if (make_path.size <= (size_t)root_len) { error = 0; goto done; } /* if we are not supposed to make the whole path, reset root */ if ((flags & GIT_MKDIR_PATH) == 0) root = git_buf_rfind(&make_path, '/'); /* advance root past drive name or network mount prefix */ min_root_len = git_path_root(make_path.ptr); if (root < min_root_len) root = min_root_len; while (root >= 0 && make_path.ptr[root] == '/') ++root; /* clip root to make_path length */ if (root > (ssize_t)make_path.size) root = (ssize_t)make_path.size; /* i.e. NUL byte of string */ if (root < 0) root = 0; /* walk down tail of path making each directory */ for (tail = &make_path.ptr[root]; *tail; *tail = lastch) { /* advance tail to include next path component */ while (*tail == '/') tail++; while (*tail && *tail != '/') tail++; /* truncate path at next component */ lastch = *tail; *tail = '\0'; st.st_mode = 0; if (opts->dir_map && git_strmap_exists(opts->dir_map, make_path.ptr)) continue; /* See what's going on with this path component */ opts->perfdata.stat_calls++; if (p_lstat(make_path.ptr, &st) < 0) { opts->perfdata.mkdir_calls++; if (errno != ENOENT || p_mkdir(make_path.ptr, mode) < 0) { giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); error = GIT_EEXISTS; goto done; } giterr_clear(); } else { /* with exclusive create, existing dir is an error */ if ((flags & GIT_MKDIR_EXCL) != 0) { giterr_set(GITERR_FILESYSTEM, "Failed to make directory '%s': directory exists", make_path.ptr); error = GIT_EEXISTS; goto done; } if ((error = validate_existing( make_path.ptr, &st, mode, flags, &opts->perfdata)) < 0) goto done; } /* chmod if requested and necessary */ if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 || (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) && st.st_mode != mode) { opts->perfdata.chmod_calls++; if ((error = p_chmod(make_path.ptr, mode)) < 0 && lastch == '\0') { giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr); goto done; } } if (opts->dir_map && opts->pool) { char *cache_path; size_t alloc_size; GITERR_CHECK_ALLOC_ADD(&alloc_size, make_path.size, 1); if (!git__is_uint32(alloc_size)) return -1; cache_path = git_pool_malloc(opts->pool, (uint32_t)alloc_size); GITERR_CHECK_ALLOC(cache_path); memcpy(cache_path, make_path.ptr, make_path.size + 1); git_strmap_insert(opts->dir_map, cache_path, cache_path, error); if (error < 0) goto done; } } error = 0; /* check that full path really is a directory if requested & needed */ if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 && lastch != '\0') { opts->perfdata.stat_calls++; if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { giterr_set(GITERR_OS, "Path is not a directory '%s'", make_path.ptr); error = GIT_ENOTFOUND; } } done: git_buf_free(&make_path); return error; }
static int find_repo( git_buf *repo_path, git_buf *parent_path, const char *start_path, uint32_t flags, const char *ceiling_dirs) { int error; git_buf path = GIT_BUF_INIT; struct stat st; dev_t initial_device = 0; bool try_with_dot_git = ((flags & GIT_REPOSITORY_OPEN_BARE) != 0); int ceiling_offset; git_buf_free(repo_path); if ((error = git_path_prettify(&path, start_path, NULL)) < 0) return error; ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs); if (!try_with_dot_git && (error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0) return error; while (!error && !git_buf_len(repo_path)) { if (p_stat(path.ptr, &st) == 0) { /* check that we have not crossed device boundaries */ if (initial_device == 0) initial_device = st.st_dev; else if (st.st_dev != initial_device && (flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0) break; if (S_ISDIR(st.st_mode)) { if (valid_repository_path(&path)) { git_path_to_dir(&path); git_buf_set(repo_path, path.ptr, path.size); break; } } else if (S_ISREG(st.st_mode)) { git_buf repo_link = GIT_BUF_INIT; if (!(error = read_gitfile(&repo_link, path.ptr))) { if (valid_repository_path(&repo_link)) git_buf_swap(repo_path, &repo_link); git_buf_free(&repo_link); break; } git_buf_free(&repo_link); } } /* move up one directory level */ if (git_path_dirname_r(&path, path.ptr) < 0) { error = -1; break; } if (try_with_dot_git) { /* if we tried original dir with and without .git AND either hit * directory ceiling or NO_SEARCH was requested, then be done. */ if (path.ptr[ceiling_offset] == '\0' || (flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0) break; /* otherwise look first for .git item */ error = git_buf_joinpath(&path, path.ptr, DOT_GIT); } try_with_dot_git = !try_with_dot_git; } if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { if (!git_buf_len(repo_path)) git_buf_clear(parent_path); else { git_path_dirname_r(parent_path, path.ptr); git_path_to_dir(parent_path); } if (git_buf_oom(parent_path)) return -1; } git_buf_free(&path); if (!git_buf_len(repo_path) && !error) { giterr_set(GITERR_REPOSITORY, "Could not find repository from '%s'", start_path); error = GIT_ENOTFOUND; } return error; }