int git_futils_mkdir_r(const char *path, int mode) { int error, root_path_offset; char *pp, *sp; char *path_copy = git__strdup(path); if (path_copy == NULL) return GIT_ENOMEM; error = GIT_SUCCESS; pp = path_copy; root_path_offset = git_path_root(pp); if (root_path_offset > 0) pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */ while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) { if (sp != pp && git_futils_isdir(path_copy) < GIT_SUCCESS) { *sp = 0; error = p_mkdir(path_copy, mode); /* Do not choke while trying to recreate an existing directory */ if (errno == EEXIST) error = GIT_SUCCESS; *sp = '/'; } pp = sp + 1; } if (*pp != '\0' && error == GIT_SUCCESS) { error = p_mkdir(path, mode); if (errno == EEXIST) error = GIT_SUCCESS; } free(path_copy); if (error < GIT_SUCCESS) return git__throw(error, "Failed to recursively create `%s` tree structure", path); return GIT_SUCCESS; }
int git_path_prettify(git_buf *path_out, const char *path, const char *base) { int error = GIT_SUCCESS; char buf[GIT_PATH_MAX]; git_buf_clear(path_out); /* construct path if needed */ if (base != NULL && git_path_root(path) < 0) { if ((error = git_buf_joinpath(path_out, base, path)) < GIT_SUCCESS) return error; path = path_out->ptr; } if (path == NULL || p_realpath(path, buf) == NULL) error = GIT_EOSERR; else error = git_buf_sets(path_out, buf); return error; }
static int create_and_configure_origin( git_remote **out, git_repository *repo, const char *url, const git_clone_options *options) { int error; git_remote *origin = NULL; char buf[GIT_PATH_MAX]; git_remote_create_cb remote_create = options->remote_cb; 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 (p_realpath(url, buf) == NULL) return -1; url = buf; } if (!remote_create) { remote_create = default_remote_create; payload = (void *)&options->remote_callbacks; } if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0) goto on_error; if ((error = git_remote_save(origin)) < 0) goto on_error; *out = origin; return 0; on_error: git_remote_free(origin); return error; }
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_path_join_unrooted( git_buf *path_out, const char *path, const char *base, ssize_t *root_at) { int error, root; assert(path && path_out); root = git_path_root(path); if (base != NULL && root < 0) { error = git_buf_joinpath(path_out, base, path); if (root_at) *root_at = (ssize_t)strlen(base); } else { error = git_buf_sets(path_out, path); if (root_at) *root_at = (root < 0) ? 0 : (ssize_t)root; } return error; }
int git_path_find_dir(git_buf *dir, const char *path, const char *base) { int error; if (base != NULL && git_path_root(path) < 0) error = git_buf_joinpath(dir, base, path); else error = git_buf_sets(dir, path); 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); if (!error) error = git_path_to_dir(dir); return error; }
int git_path_resolve_relative(git_buf *path, size_t ceiling) { char *base, *to, *from, *next; size_t len; if (!path || git_buf_oom(path)) return -1; 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) { giterr_set(GITERR_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_futils_mkdir( const char *path, const char *base, mode_t mode, uint32_t flags) { int error = -1; git_buf make_path = GIT_BUF_INIT; ssize_t root = 0, min_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; } /* remove trailing slashes on path */ while (make_path.ptr[make_path.size - 1] == '/') { make_path.size--; 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_buf_rtruncate_at_char(&make_path, '/'); flags |= GIT_MKDIR_SKIP_LAST; } if ((flags & GIT_MKDIR_SKIP_LAST) != 0) git_buf_rtruncate_at_char(&make_path, '/'); /* if nothing left after truncation, then we're done! */ if (!make_path.size) { 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; /* make directory */ if (p_mkdir(make_path.ptr, mode) < 0) { int tmp_errno = giterr_system_last(); /* ignore error if not at end or if directory already exists */ if (lastch == '\0' && (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) { giterr_system_set(tmp_errno); giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); goto done; } /* with exclusive create, existing dir is an error */ if ((flags & GIT_MKDIR_EXCL) != 0) { giterr_set(GITERR_OS, "Directory already exists '%s'", make_path.ptr); error = GIT_EEXISTS; 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 && (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; } } error = 0; /* check that full path really is a directory if requested & needed */ if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 && lastch != '\0' && (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; }
int git_futils_mkdir_relative( const char *relative_path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts) { git_buf make_path = GIT_BUF_INIT; ssize_t root = 0, min_root_len; char lastch = '/', *tail; struct stat st; struct git_futils_mkdir_options empty_opts = {0}; int error; if (!opts) 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) return -1; if ((error = mkdir_canonicalize(&make_path, flags)) < 0 || make_path.size == 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) { bool mkdir_attempted = false; /* 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++; retry_lstat: if (p_lstat(make_path.ptr, &st) < 0) { if (mkdir_attempted || errno != ENOENT) { giterr_set(GITERR_OS, "cannot access component in path '%s'", make_path.ptr); error = -1; goto done; } giterr_clear(); opts->perfdata.mkdir_calls++; mkdir_attempted = true; if (p_mkdir(make_path.ptr, mode) < 0) { if (errno == EEXIST) goto retry_lstat; giterr_set(GITERR_OS, "failed to make directory '%s'", make_path.ptr); error = -1; goto done; } } else { if ((error = mkdir_validate_dir( make_path.ptr, &st, mode, flags, opts)) < 0) goto done; } /* chmod if requested and necessary */ if ((error = mkdir_validate_mode( make_path.ptr, &st, (lastch == '\0'), mode, flags, opts)) < 0) 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; }
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; }
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode) { git_buf make_path = GIT_BUF_INIT; size_t start = 0; char *pp, *sp; bool failed = false; if (base != NULL) { /* * when a base is being provided, it is supposed to already exist. * Therefore, no attempt is being made to recursively create this leading path * segment. It's just skipped. */ start = strlen(base); if (git_buf_joinpath(&make_path, base, path) < 0) return -1; } else { int root_path_offset; if (git_buf_puts(&make_path, path) < 0) return -1; root_path_offset = git_path_root(make_path.ptr); if (root_path_offset > 0) { /* * On Windows, will skip the drive name (eg. C: or D:) * or the leading part of a network path (eg. //computer_name ) */ start = root_path_offset; } } pp = make_path.ptr + start; while (!failed && (sp = strchr(pp, '/')) != NULL) { if (sp != pp && git_path_isdir(make_path.ptr) == false) { *sp = 0; /* Do not choke while trying to recreate an existing directory */ if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST) failed = true; *sp = '/'; } pp = sp + 1; } if (*pp != '\0' && !failed) { if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST) failed = true; } git_buf_free(&make_path); if (failed) { giterr_set(GITERR_OS, "Failed to create directory structure at '%s'", path); return -1; } return 0; }