void test_checkout_index__options_dir_modes(void) { #ifndef GIT_WIN32 git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; struct stat st; git_oid oid; git_commit *commit; cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); cl_git_pass(git_commit_lookup(&commit, g_repo, &oid)); reset_index_to_treeish((git_object *)commit); opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; opts.dir_mode = 0701; cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(p_stat("./testrepo/a", &st)); cl_assert_equal_i(st.st_mode & 0777, 0701); /* File-mode test, since we're on the 'dir' branch */ cl_git_pass(p_stat("./testrepo/a/b.txt", &st)); cl_assert_equal_i(st.st_mode & 0777, 0755); git_commit_free(commit); #endif }
void test_write_object_permission( mode_t dir_mode, mode_t file_mode, mode_t expected_dir_mode, mode_t expected_file_mode) { git_odb *odb; git_odb_backend *backend; git_oid oid; struct stat statbuf; mode_t mask, os_mask; /* Windows does not return group/user bits from stat, * files are never executable. */ #ifdef GIT_WIN32 os_mask = 0600; #else os_mask = 0777; #endif mask = p_umask(0); p_umask(mask); cl_git_pass(git_odb_new(&odb)); cl_git_pass(git_odb_backend_loose(&backend, "test-objects", -1, 0, dir_mode, file_mode)); cl_git_pass(git_odb_add_backend(odb, backend, 1)); cl_git_pass(git_odb_write(&oid, odb, "Test data\n", 10, GIT_OBJECT_BLOB)); cl_git_pass(p_stat("test-objects/67", &statbuf)); cl_assert_equal_i(statbuf.st_mode & os_mask, (expected_dir_mode & ~mask) & os_mask); cl_git_pass(p_stat("test-objects/67/b808feb36201507a77f85e6d898f0a2836e4a5", &statbuf)); cl_assert_equal_i(statbuf.st_mode & os_mask, (expected_file_mode & ~mask) & os_mask); git_odb_free(odb); }
void test_checkout_index__options_dir_modes(void) { git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct stat st; git_oid oid; git_commit *commit; mode_t um; if (!cl_is_chmod_supported()) return; cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); cl_git_pass(git_commit_lookup(&commit, g_repo, &oid)); reset_index_to_treeish((git_object *)commit); opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING; opts.dir_mode = 0701; cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); /* umask will influence actual directory creation mode */ (void)p_umask(um = p_umask(022)); cl_git_pass(p_stat("./testrepo/a", &st)); /* Haiku & Hurd use other mode bits, so we must mask them out */ cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), (GIT_FILEMODE_TREE | 0701) & ~um, "%07o"); /* File-mode test, since we're on the 'dir' branch */ cl_git_pass(p_stat("./testrepo/a/b.txt", &st)); cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), GIT_FILEMODE_BLOB_EXECUTABLE & ~um, "%07o"); git_commit_free(commit); }
void test_checkout_index__options_dir_modes(void) { #ifndef GIT_WIN32 git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; struct stat st; git_oid oid; git_commit *commit; mode_t um; cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); cl_git_pass(git_commit_lookup(&commit, g_repo, &oid)); reset_index_to_treeish((git_object *)commit); opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; opts.dir_mode = 0701; cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); /* umask will influence actual directory creation mode */ (void)p_umask(um = p_umask(022)); cl_git_pass(p_stat("./testrepo/a", &st)); cl_assert_equal_i_fmt(st.st_mode, (GIT_FILEMODE_TREE | 0701) & ~um, "%07o"); /* File-mode test, since we're on the 'dir' branch */ cl_git_pass(p_stat("./testrepo/a/b.txt", &st)); cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE, "%07o"); git_commit_free(commit); #endif }
bool cl_toggle_filemode(const char *filename) { struct stat st1, st2; cl_must_pass(p_stat(filename, &st1)); cl_must_pass(p_chmod(filename, st1.st_mode ^ 0100)); cl_must_pass(p_stat(filename, &st2)); return (st1.st_mode != st2.st_mode); }
static bool is_chmod_supported(const char *file_path) { struct stat st1, st2; if (p_stat(file_path, &st1) < 0) return false; if (p_chmod(file_path, st1.st_mode ^ S_IXUSR) < 0) return false; if (p_stat(file_path, &st2) < 0) return false; return (st1.st_mode != st2.st_mode); }
void test_core_posix__utimes(void) { struct timeval times[2]; struct stat st; time_t curtime; int fd; /* test p_utimes */ times[0].tv_sec = 1234567890; times[0].tv_usec = 0; times[1].tv_sec = 1234567890; times[1].tv_usec = 0; cl_git_mkfile("foo", "Dummy file."); cl_must_pass(p_utimes("foo", times)); p_stat("foo", &st); cl_assert_equal_i(1234567890, st.st_atime); cl_assert_equal_i(1234567890, st.st_mtime); /* test p_futimes */ times[0].tv_sec = 1414141414; times[0].tv_usec = 0; times[1].tv_sec = 1414141414; times[1].tv_usec = 0; cl_must_pass(fd = p_open("foo", O_RDWR)); cl_must_pass(p_futimes(fd, times)); p_close(fd); p_stat("foo", &st); cl_assert_equal_i(1414141414, st.st_atime); cl_assert_equal_i(1414141414, st.st_mtime); /* test p_utimes with current time, assume that * it takes < 5 seconds to get the time...! */ cl_must_pass(p_utimes("foo", NULL)); curtime = time(NULL); p_stat("foo", &st); cl_assert((st.st_atime - curtime) < 5); cl_assert((st.st_mtime - curtime) < 5); p_unlink("foo"); }
static int _isExecutable(const char* path) { struct s_stat st; return ((p_stat(path, &st) == 0) && (st.st_mode & _S_IEXEC)); }
/* make sure git_filebuf_commit takes umask into account */ void test_core_filebuf__umask(void) { git_filebuf file = GIT_FILEBUF_INIT; char test[] = "test"; struct stat statbuf; mode_t mask, os_mask; #ifdef GIT_WIN32 os_mask = 0600; #else os_mask = 0777; #endif p_umask(mask = p_umask(0)); cl_assert(file.buffer == NULL); cl_git_pass(git_filebuf_open(&file, test, 0, 0666)); cl_assert(file.buffer != NULL); cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); cl_assert(file.buffer != NULL); cl_git_pass(git_filebuf_commit(&file)); cl_assert(file.buffer == NULL); cl_must_pass(p_stat("test", &statbuf)); cl_assert_equal_i(statbuf.st_mode & os_mask, (0666 & ~mask) & os_mask); cl_must_pass(p_unlink(test)); }
static void hack_index(char *files[]) { char *filename; struct stat statbuf; git_buf path = GIT_BUF_INIT; git_index_entry *entry; size_t i; /* Update the index to suggest that checkout placed these files on * disk, keeping the object id but updating the cache, which will * emulate a Git implementation's different filter. */ for (i = 0, filename = files[i]; filename; filename = files[++i]) { git_buf_clear(&path); cl_assert(entry = (git_index_entry *) git_index_get_bypath(repo_index, filename, 0)); cl_git_pass(git_buf_printf(&path, "%s/%s", TEST_REPO_PATH, filename)); cl_git_pass(p_stat(path.ptr, &statbuf)); entry->ctime.seconds = (git_time_t)statbuf.st_ctime; entry->ctime.nanoseconds = 0; entry->mtime.seconds = (git_time_t)statbuf.st_mtime; entry->mtime.nanoseconds = 0; entry->dev = statbuf.st_dev; entry->ino = statbuf.st_ino; entry->uid = statbuf.st_uid; entry->gid = statbuf.st_gid; entry->file_size = statbuf.st_size; } git_buf_free(&path); }
int git_index_write(git_index *index) { git_filebuf file; struct stat indexst; int error; git_vector_sort(&index->entries); if ((error = git_filebuf_open(&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < GIT_SUCCESS) return git__rethrow(error, "Failed to write index"); if ((error = write_index(index, &file)) < GIT_SUCCESS) { git_filebuf_cleanup(&file); return git__rethrow(error, "Failed to write index"); } if ((error = git_filebuf_commit(&file)) < GIT_SUCCESS) return git__rethrow(error, "Failed to write index"); if (p_stat(index->index_file_path, &indexst) == 0) { index->last_modified = indexst.st_mtime; index->on_disk = 1; } return GIT_SUCCESS; }
static int packfile_refresh_all(struct pack_backend *backend) { int error; struct stat st; if (backend->pack_folder == NULL) return GIT_SUCCESS; if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode)) return git__throw(GIT_ENOTFOUND, "Failed to refresh packfiles. Backend not found"); if (st.st_mtime != backend->pack_folder_mtime) { char path[GIT_PATH_MAX]; strcpy(path, backend->pack_folder); /* reload all packs */ error = git_futils_direach(path, GIT_PATH_MAX, packfile_load__cb, (void *)backend); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to refresh packfiles"); git_vector_sort(&backend->packs); backend->pack_folder_mtime = st.st_mtime; } return GIT_SUCCESS; }
static void setup_race(void) { git_buf path = GIT_BUF_INIT; git_index *index; git_index_entry *entry; struct stat st; /* Make sure we do have a timestamp */ cl_git_pass(git_repository_index__weakptr(&index, g_repo)); cl_git_pass(git_index_write(index)); cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A")); cl_git_mkfile(path.ptr, "A"); cl_git_pass(git_index_add_bypath(index, "A")); cl_git_mkfile(path.ptr, "B"); cl_git_pass(git_index_write(index)); cl_git_mkfile(path.ptr, ""); cl_git_pass(p_stat(path.ptr, &st)); cl_assert(entry = (git_index_entry *)git_index_get_bypath(index, "A", 0)); /* force a race */ entry->mtime.seconds = (int32_t)st.st_mtime; entry->mtime.nanoseconds = (int32_t)st.st_mtime_nsec; git_buf_dispose(&path); }
void test_index_version__v4_uses_path_compression(void) { git_index_entry entry; git_index *index; char path[250], buf[1]; struct stat st; char i, j; memset(path, 'a', sizeof(path)); memset(buf, 'a', sizeof(buf)); memset(&entry, 0, sizeof(entry)); entry.path = path; entry.mode = GIT_FILEMODE_BLOB; g_repo = cl_git_sandbox_init("indexv4"); cl_git_pass(git_repository_index(&index, g_repo)); /* write 676 paths of 250 bytes length */ for (i = 'a'; i <= 'z'; i++) { for (j = 'a'; j < 'z'; j++) { path[ARRAY_SIZE(path) - 3] = i; path[ARRAY_SIZE(path) - 2] = j; path[ARRAY_SIZE(path) - 1] = '\0'; cl_git_pass(git_index_add_frombuffer(index, &entry, buf, sizeof(buf))); } } cl_git_pass(git_index_write(index)); cl_git_pass(p_stat(git_index_path(index), &st)); /* * Without path compression, the written paths would at * least take * * (entries * pathlen) = len * (676 * 250) = 169000 * * bytes. As index v4 uses suffix-compression and our * written paths only differ in the last two entries, * this number will be much smaller, e.g. * * (1 * pathlen) + (675 * 2) = len * 676 + 1350 = 2026 * * bytes. * * Note that the above calculations do not include * additional metadata of the index, e.g. OIDs or * index extensions. Including those we get an index * of approx. 200kB without compression and 40kB with * compression. As this is a lot smaller than without * compression, we can verify that path compression is * used. */ cl_assert_(st.st_size < 75000, "path compression not enabled"); git_index_free(index); }
int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated) { git_file fd; size_t len; struct stat st; unsigned char *buff; assert(obj && path && *path); if (updated != NULL) *updated = 0; if (p_stat(path, &st) < 0) return git__throw(GIT_ENOTFOUND, "Failed to stat file %s", path); if (S_ISDIR(st.st_mode)) return git__throw(GIT_ERROR, "Can't read a dir into a buffer"); /* * If we were given a time, we only want to read the file if it * has been modified. */ if (mtime != NULL && *mtime >= st.st_mtime) return GIT_SUCCESS; if (mtime != NULL) *mtime = st.st_mtime; if (!git__is_sizet(st.st_size+1)) return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path); len = (size_t) st.st_size; if ((fd = p_open(path, O_RDONLY)) < 0) return git__throw(GIT_EOSERR, "Failed to open %s for reading", path); if ((buff = git__malloc(len + 1)) == NULL) { p_close(fd); return GIT_ENOMEM; } if (p_read(fd, buff, len) < 0) { p_close(fd); free(buff); return git__throw(GIT_ERROR, "Failed to read file `%s`", path); } buff[len] = '\0'; p_close(fd); if (mtime != NULL) *mtime = st.st_mtime; if (updated != NULL) *updated = 1; obj->data = buff; obj->len = len; return GIT_SUCCESS; }
static bool try_create_file_with_nsec_timestamp(const char *path) { struct stat st; int try; /* retry a few times to avoid nanos *actually* equal 0 race condition */ for (try = 0; try < 3; try++) { cl_git_mkfile(path, "This is hopefully a file with nanoseconds!"); cl_must_pass(p_stat(path, &st)); if (st.st_ctime_nsec && st.st_mtime_nsec) return true; } return false; } /* try to determine if the underlying filesystem supports a resolution * higher than a single second. (i'm looking at you, hfs+) */ static bool should_expect_nsecs(void) { git_buf nsec_path = GIT_BUF_INIT; bool expect; git_buf_joinpath(&nsec_path, clar_sandbox_path(), "nsec_test"); expect = try_create_file_with_nsec_timestamp(nsec_path.ptr); p_unlink(nsec_path.ptr); git_buf_clear(&nsec_path); return expect; } static bool has_nsecs(void) { const git_index_entry *entry; size_t i; bool has_nsecs = false; for (i = 0; i < git_index_entrycount(repo_index); i++) { entry = git_index_get_byindex(repo_index, i); if (entry->ctime.nanoseconds || entry->mtime.nanoseconds) { has_nsecs = true; break; } } return has_nsecs; } void test_index_nsec__has_nanos(void) { cl_assert_equal_b(true, has_nsecs()); }
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; assert(path); if (p_stat(path, &st) < 0) return false; return S_ISREG(st.st_mode) != 0; }
static bool can_link(const char *src, const char *dst, int link) { #ifdef GIT_WIN32 return false; #else struct stat st_src, st_dst; if (!link) return false; if (p_stat(src, &st_src) < 0) return false; if (p_stat(dst, &st_dst) < 0) return false; return st_src.st_dev == st_dst.st_dev; #endif }
void test_repo_init__detect_ignorecase(void) { struct stat st; bool found_without_match; cl_git_write2file("testCAPS", "whatever\n", 0, O_CREAT | O_WRONLY, 0666); found_without_match = (p_stat("Testcaps", &st) == 0); cl_must_pass(p_unlink("testCAPS")); assert_config_entry_on_init( "core.ignorecase", found_without_match ? true : GIT_ENOTFOUND); }
void test_checkout_index__options_override_file_modes(void) { #ifndef GIT_WIN32 struct stat st; g_opts.file_mode = 0700; cl_git_pass(git_checkout_index(g_repo, &g_opts)); cl_git_pass(p_stat("./testrepo/new.txt", &st)); cl_assert_equal_i(st.st_mode & 0777, 0700); #endif }
void test_checkout_index__options_override_file_modes(void) { #ifndef GIT_WIN32 git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; struct stat st; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; opts.file_mode = 0700; cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(p_stat("./testrepo/new.txt", &st)); cl_assert_equal_i(st.st_mode & 0777, 0700); #endif }
static void ensure_workdir_mode(const char *path, int mode) { #ifndef GIT_WIN32 git_buf fullpath = GIT_BUF_INIT; struct stat st; cl_git_pass( git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path)); cl_git_pass(p_stat(git_buf_cstr(&fullpath), &st)); cl_assert_equal_i(mode, st.st_mode); git_buf_free(&fullpath); #endif }
GIT_INLINE(int) mkdir_validate_dir( const char *path, struct stat *st, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts) { /* 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", path); return GIT_EEXISTS; } if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) || (S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) { if (p_unlink(path) < 0) { giterr_set(GITERR_OS, "failed to remove %s '%s'", S_ISLNK(st->st_mode) ? "symlink" : "file", path); return GIT_EEXISTS; } opts->perfdata.mkdir_calls++; if (p_mkdir(path, mode) < 0) { giterr_set(GITERR_OS, "failed to make directory '%s'", path); return GIT_EEXISTS; } } else if (S_ISLNK(st->st_mode)) { /* Re-stat the target, make sure it's a directory */ opts->perfdata.stat_calls++; if (p_stat(path, st) < 0) { giterr_set(GITERR_OS, "failed to make directory '%s'", path); return GIT_EEXISTS; } } else if (!S_ISDIR(st->st_mode)) { giterr_set(GITERR_FILESYSTEM, "failed to make directory '%s': directory exists", path); return GIT_EEXISTS; } return 0; }
static void hack_index(char *files[]) { char *filename; struct stat statbuf; git_buf path = GIT_BUF_INIT; git_index_entry *entry; struct timeval times[2]; time_t now; size_t i; /* Update the index to suggest that checkout placed these files on * disk, keeping the object id but updating the cache, which will * emulate a Git implementation's different filter. * * We set the file's timestamp to before now to pretend that * it was an old checkout so we don't trigger the racy * protections would would check the content. */ now = time(NULL); times[0].tv_sec = now - 5; times[0].tv_usec = 0; times[1].tv_sec = now - 5; times[1].tv_usec = 0; for (i = 0, filename = files[i]; filename; filename = files[++i]) { git_buf_clear(&path); cl_assert(entry = (git_index_entry *) git_index_get_bypath(repo_index, filename, 0)); cl_git_pass(git_buf_printf(&path, "%s/%s", TEST_REPO_PATH, filename)); cl_git_pass(p_utimes(path.ptr, times)); cl_git_pass(p_stat(path.ptr, &statbuf)); entry->ctime.seconds = (git_time_t)statbuf.st_ctime; entry->ctime.nanoseconds = 0; entry->mtime.seconds = (git_time_t)statbuf.st_mtime; entry->mtime.nanoseconds = 0; entry->dev = statbuf.st_dev; entry->ino = statbuf.st_ino; entry->uid = statbuf.st_uid; entry->gid = statbuf.st_gid; entry->file_size = statbuf.st_size; } git_buf_free(&path); }
int git_path_isfile(const char *path) { struct stat st; int stat_error; assert(path); stat_error = p_stat(path, &st); if (stat_error < GIT_SUCCESS) return -1; if (!S_ISREG(st.st_mode)) return -1; return 0; }
void test_checkout_index__options_override_file_modes(void) { git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct stat st; if (!cl_is_chmod_supported()) return; opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING; opts.file_mode = 0700; cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(p_stat("./testrepo/new.txt", &st)); cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o"); }
void test_repo_init__detect_precompose_unicode_required(void) { #ifdef GIT_USE_ICONV char *composed = "ḱṷṓn", *decomposed = "ḱṷṓn"; struct stat st; bool found_with_nfd; cl_git_write2file(composed, "whatever\n", 0, O_CREAT | O_WRONLY, 0666); found_with_nfd = (p_stat(decomposed, &st) == 0); cl_must_pass(p_unlink(composed)); assert_config_entry_on_init("core.precomposeunicode", found_with_nfd); #else assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND); #endif }
mode_t read_filemode(const char *path) { git_buf fullpath = GIT_BUF_INIT; struct stat st; mode_t result; git_buf_joinpath(&fullpath, "testrepo", path); cl_must_pass(p_stat(fullpath.ptr, &st)); result = GIT_PERMS_IS_EXEC(st.st_mode) ? GIT_FILEMODE_BLOB_EXECUTABLE : GIT_FILEMODE_BLOB; git_buf_free(&fullpath); return result; }
int git_path_isdir(const char *path) { #ifdef GIT_WIN32 DWORD attr = GetFileAttributes(path); if (attr == INVALID_FILE_ATTRIBUTES) return GIT_ERROR; return (attr & FILE_ATTRIBUTE_DIRECTORY) ? GIT_SUCCESS : GIT_ERROR; #else struct stat st; if (p_stat(path, &st) < GIT_SUCCESS) return GIT_ERROR; return S_ISDIR(st.st_mode) ? GIT_SUCCESS : GIT_ERROR; #endif }