void test_odb_freshen__loose_object(void) { git_oid expected_id, id; struct stat before, after; struct p_timeval old_times[2]; cl_git_pass(git_oid_fromstr(&expected_id, LOOSE_ID)); old_times[0].tv_sec = 1234567890; old_times[0].tv_usec = 0; old_times[1].tv_sec = 1234567890; old_times[1].tv_usec = 0; /* set time to way back */ cl_must_pass(p_utimes("testrepo.git/objects/" LOOSE_FN, old_times)); cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_FN, &before)); cl_git_pass(git_odb_write(&id, odb, LOOSE_STR, CONST_STRLEN(LOOSE_STR), GIT_OBJ_BLOB)); cl_assert_equal_oid(&expected_id, &id); cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_FN, &after)); cl_assert(before.st_atime < after.st_atime); cl_assert(before.st_mtime < after.st_mtime); }
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 void check_stat_data(git_index *index, const char *path, bool match) { const git_index_entry *entry; struct stat st; cl_must_pass(p_lstat(path, &st)); /* skip repo base dir name */ while (*path != '/') ++path; ++path; entry = git_index_get_bypath(index, path, 0); cl_assert(entry); if (match) { cl_assert(st.st_ctime == entry->ctime.seconds); cl_assert(st.st_mtime == entry->mtime.seconds); cl_assert(st.st_size == entry->file_size); cl_assert(st.st_uid == entry->uid); cl_assert(st.st_gid == entry->gid); cl_assert_equal_i_fmt( GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o"); if (cl_is_chmod_supported()) cl_assert_equal_b( GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode)); } else { /* most things will still match */ cl_assert(st.st_size != entry->file_size); /* would check mtime, but with second resolution it won't work :( */ } }
bool git_path_islink(const char *path) { struct stat st; assert(path); if (p_lstat(path, &st) < 0) return false; return S_ISLNK(st.st_mode) != 0; }
int git_path_lstat(const char *path, struct stat *st) { int err = 0; if (p_lstat(path, st) < 0) { err = (errno == ENOENT) ? GIT_ENOTFOUND : -1; giterr_set(GITERR_OS, "Failed to stat file '%s'", path); } return err; }
void test_odb_freshen__packed_object(void) { git_oid expected_id, id; struct stat before, after; struct p_timeval old_times[2]; cl_git_pass(git_oid_fromstr(&expected_id, PACKED_ID)); old_times[0].tv_sec = 1234567890; old_times[0].tv_usec = 0; old_times[1].tv_sec = 1234567890; old_times[1].tv_usec = 0; /* set time to way back */ cl_must_pass(p_utimes("testrepo.git/objects/pack/" PACKED_FN, old_times)); cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &before)); /* ensure that packfile is freshened */ cl_git_pass(git_odb_write(&id, odb, PACKED_STR, CONST_STRLEN(PACKED_STR), GIT_OBJ_BLOB)); cl_assert_equal_oid(&expected_id, &id); cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &after)); cl_assert(before.st_atime < after.st_atime); cl_assert(before.st_mtime < after.st_mtime); memcpy(&before, &after, sizeof(struct stat)); /* ensure that the pack file is not freshened again immediately */ cl_git_pass(git_odb_write(&id, odb, PACKED_STR, CONST_STRLEN(PACKED_STR), GIT_OBJ_BLOB)); cl_assert_equal_oid(&expected_id, &id); cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &after)); cl_assert(before.st_atime == after.st_atime); cl_assert(before.st_atime_nsec == after.st_atime_nsec); cl_assert(before.st_mtime == after.st_mtime); cl_assert(before.st_mtime_nsec == after.st_mtime_nsec); }
static bool are_symlinks_supported(const char *wd_path) { git_buf path = GIT_BUF_INIT; int fd; struct stat st; int symlinks_supported = -1; if ((fd = git_futils_mktmp(&path, wd_path, 0666)) < 0 || p_close(fd) < 0 || p_unlink(path.ptr) < 0 || p_symlink("testing", path.ptr) < 0 || p_lstat(path.ptr, &st) < 0) symlinks_supported = false; else symlinks_supported = (S_ISLNK(st.st_mode) != 0); (void)p_unlink(path.ptr); git_buf_free(&path); return symlinks_supported; }
static int index_init_entry(git_index_entry *entry, git_index *index, const char *rel_path, int stage) { char full_path[GIT_PATH_MAX]; struct stat st; int error; if (index->repository == NULL) return git__throw(GIT_EBAREINDEX, "Failed to initialize entry. Repository is bare"); git_path_join(full_path, index->repository->path_workdir, rel_path); if (p_lstat(full_path, &st) < 0) return git__throw(GIT_ENOTFOUND, "Failed to initialize entry. '%s' cannot be opened", full_path); if (stage < 0 || stage > 3) return git__throw(GIT_ERROR, "Failed to initialize entry. Invalid stage %i", stage); memset(entry, 0x0, sizeof(git_index_entry)); entry->ctime.seconds = (git_time_t)st.st_ctime; entry->mtime.seconds = (git_time_t)st.st_mtime; /* entry.mtime.nanoseconds = st.st_mtimensec; */ /* entry.ctime.nanoseconds = st.st_ctimensec; */ entry->dev= st.st_rdev; entry->ino = st.st_ino; entry->mode = index_create_mode(st.st_mode); entry->uid = st.st_uid; entry->gid = st.st_gid; entry->file_size = st.st_size; /* write the blob to disk and get the oid */ if ((error = git_blob_create_fromfile(&entry->oid, index->repository, rel_path)) < GIT_SUCCESS) return git__rethrow(error, "Failed to initialize index entry"); entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT); entry->path = rel_path; /* do not duplicate; index_insert already does this */ return GIT_SUCCESS; }
static void ensure_workdir_link(const char *path, const char *target) { #ifdef GIT_WIN32 ensure_workdir_contents(path, target); #else git_buf fullpath = GIT_BUF_INIT; char actual[1024]; struct stat st; int len; cl_git_pass( git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path)); cl_git_pass(p_lstat(git_buf_cstr(&fullpath), &st)); cl_assert(S_ISLNK(st.st_mode)); cl_assert((len = p_readlink(git_buf_cstr(&fullpath), actual, 1024)) > 0); actual[len] = '\0'; cl_assert(strcmp(actual, target) == 0); git_buf_free(&fullpath); #endif }
static int workdir_reader_read( git_buf *out, git_oid *out_id, git_filemode_t *out_filemode, git_reader *_reader, const char *filename) { workdir_reader *reader = (workdir_reader *)_reader; git_buf path = GIT_BUF_INIT; struct stat st; git_filemode_t filemode; git_filter_list *filters = NULL; const git_index_entry *idx_entry; git_oid id; int error; if ((error = git_buf_joinpath(&path, git_repository_workdir(reader->repo), filename)) < 0) goto done; if ((error = p_lstat(path.ptr, &st)) < 0) { if (error == -1 && errno == ENOENT) error = GIT_ENOTFOUND; giterr_set(GITERR_OS, "could not stat '%s'", path.ptr); goto done; } filemode = git_futils_canonical_mode(st.st_mode); /* * Patch application - for example - uses the filtered version of * the working directory data to match git. So we will run the * workdir -> ODB filter on the contents in this workdir reader. */ if ((error = git_filter_list_load(&filters, reader->repo, NULL, filename, GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT)) < 0) goto done; if ((error = git_filter_list_apply_to_file(out, filters, reader->repo, path.ptr)) < 0) goto done; if (out_id || reader->index) { if ((error = git_odb_hash(&id, out->ptr, out->size, GIT_OBJ_BLOB)) < 0) goto done; } if (reader->index) { if (!(idx_entry = git_index_get_bypath(reader->index, filename, 0)) || filemode != idx_entry->mode || !git_oid_equal(&id, &idx_entry->id)) { error = GIT_READER_MISMATCH; goto done; } } if (out_id) git_oid_cpy(out_id, &id); if (out_filemode) *out_filemode = filemode; done: git_filter_list_free(filters); git_buf_dispose(&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; }
void test_core_stat__0(void) { struct stat st; int err; cl_assert_equal_i(0, p_lstat("root", &st)); cl_assert(S_ISDIR(st.st_mode)); cl_assert_error(0); cl_assert_equal_i(0, p_lstat("root/", &st)); cl_assert(S_ISDIR(st.st_mode)); cl_assert_error(0); cl_assert_equal_i(0, p_lstat("root/file", &st)); cl_assert(S_ISREG(st.st_mode)); cl_assert_error(0); cl_assert_equal_i(0, p_lstat("root/d1", &st)); cl_assert(S_ISDIR(st.st_mode)); cl_assert_error(0); cl_assert_equal_i(0, p_lstat("root/d1/", &st)); cl_assert(S_ISDIR(st.st_mode)); cl_assert_error(0); cl_assert_equal_i(0, p_lstat("root/d1/file", &st)); cl_assert(S_ISREG(st.st_mode)); cl_assert_error(0); cl_assert(p_lstat("root/missing", &st) < 0); cl_assert_error(ENOENT); cl_assert(p_lstat("root/missing/but/could/be/created", &st) < 0); cl_assert_error(ENOENT); cl_assert(p_lstat_posixly("root/missing/but/could/be/created", &st) < 0); cl_assert_error(ENOENT); cl_assert(p_lstat("root/d1/missing", &st) < 0); cl_assert_error(ENOENT); cl_assert(p_lstat("root/d1/missing/deeper/path", &st) < 0); cl_assert_error(ENOENT); cl_assert(p_lstat_posixly("root/d1/missing/deeper/path", &st) < 0); cl_assert_error(ENOENT); cl_assert(p_lstat_posixly("root/d1/file/deeper/path", &st) < 0); cl_assert_error(ENOTDIR); cl_assert(p_lstat("root/file/invalid", &st) < 0); #ifdef GIT_WIN32 cl_assert_error(ENOENT); #else cl_assert_error(ENOTDIR); #endif cl_assert(p_lstat_posixly("root/file/invalid", &st) < 0); cl_assert_error(ENOTDIR); cl_assert(p_lstat("root/file/invalid/deeper_path", &st) < 0); #ifdef GIT_WIN32 cl_assert_error(ENOENT); #else cl_assert_error(ENOTDIR); #endif cl_assert(p_lstat_posixly("root/file/invalid/deeper_path", &st) < 0); cl_assert_error(ENOTDIR); cl_assert(p_lstat_posixly("root/d1/file/extra", &st) < 0); cl_assert_error(ENOTDIR); cl_assert(p_lstat_posixly("root/d1/file/further/invalid/items", &st) < 0); cl_assert_error(ENOTDIR); }
void test_object_commit_commitstagedfile__generate_predictable_object_ids(void) { git_index *index; const git_index_entry *entry; git_oid expected_blob_oid, tree_oid, expected_tree_oid, commit_oid, expected_commit_oid; git_signature *signature; git_tree *tree; char buffer[128]; /* * The test below replicates the following git scenario * * $ echo "test" > test.txt * $ git hash-object test.txt * 9daeafb9864cf43055ae93beb0afd6c7d144bfa4 * * $ git add . * $ git commit -m "Initial commit" * * $ git log * commit 1fe3126578fc4eca68c193e4a3a0a14a0704624d * Author: nulltoken <*****@*****.**> * Date: Wed Dec 14 08:29:03 2011 +0100 * * Initial commit * * $ git show 1fe3 --format=raw * commit 1fe3126578fc4eca68c193e4a3a0a14a0704624d * tree 2b297e643c551e76cfa1f93810c50811382f9117 * author nulltoken <*****@*****.**> 1323847743 +0100 * committer nulltoken <*****@*****.**> 1323847743 +0100 * * Initial commit * * diff --git a/test.txt b/test.txt * new file mode 100644 * index 0000000..9daeafb * --- /dev/null * +++ b/test.txt * @@ -0,0 +1 @@ * +test * * $ git ls-tree 2b297 * 100644 blob 9daeafb9864cf43055ae93beb0afd6c7d144bfa4 test.txt */ cl_git_pass(git_oid_fromstr(&expected_commit_oid, "1fe3126578fc4eca68c193e4a3a0a14a0704624d")); cl_git_pass(git_oid_fromstr(&expected_tree_oid, "2b297e643c551e76cfa1f93810c50811382f9117")); cl_git_pass(git_oid_fromstr(&expected_blob_oid, "9daeafb9864cf43055ae93beb0afd6c7d144bfa4")); /* * Add a new file to the index */ cl_git_mkfile("treebuilder/test.txt", "test\n"); cl_git_pass(git_repository_index(&index, repo)); cl_git_pass(git_index_add_bypath(index, "test.txt")); entry = git_index_get_byindex(index, 0); cl_assert(git_oid_cmp(&expected_blob_oid, &entry->oid) == 0); /* * Information about index entry should match test file */ { struct stat st; cl_must_pass(p_lstat("treebuilder/test.txt", &st)); cl_assert(entry->file_size == st.st_size); #ifndef _WIN32 /* * Windows doesn't populate these fields, and the signage is * wrong in the Windows version of the struct, so lets avoid * the "comparing signed and unsigned" compilation warning in * that case. */ cl_assert(entry->uid == st.st_uid); cl_assert(entry->gid == st.st_gid); #endif } /* * Build the tree from the index */ cl_git_pass(git_index_write_tree(&tree_oid, index)); cl_assert(git_oid_cmp(&expected_tree_oid, &tree_oid) == 0); /* * Commit the staged file */ cl_git_pass(git_signature_new(&signature, "nulltoken", "*****@*****.**", 1323847743, 60)); cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid)); cl_assert_equal_i(16, git_message_prettify(buffer, 128, "Initial commit", 0)); cl_git_pass(git_commit_create_v( &commit_oid, repo, "HEAD", signature, signature, NULL, buffer, tree, 0)); cl_assert(git_oid_cmp(&expected_commit_oid, &commit_oid) == 0); git_signature_free(signature); git_tree_free(tree); git_index_free(index); }