mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-06 17:20:22 +00:00
Merge pull request #3413 from libgit2/cmn/follow-symlink
filebuf: follow symlinks when creating a lock file
This commit is contained in:
commit
e1d27bcaaf
@ -7,6 +7,9 @@ v0.23 + 1
|
|||||||
example `filter=*`. Consumers should examine the attributes parameter
|
example `filter=*`. Consumers should examine the attributes parameter
|
||||||
of the `check` function for details.
|
of the `check` function for details.
|
||||||
|
|
||||||
|
* Symlinks are now followed when locking a file, which can be
|
||||||
|
necessary when multiple worktrees share a base repository.
|
||||||
|
|
||||||
### API additions
|
### API additions
|
||||||
|
|
||||||
* `git_config_lock()` has been added, which allow for
|
* `git_config_lock()` has been added, which allow for
|
||||||
|
@ -191,6 +191,81 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MAX_SYMLINK_DEPTH 5
|
||||||
|
|
||||||
|
static int resolve_symlink(git_buf *out, const char *path)
|
||||||
|
{
|
||||||
|
int i, error, root;
|
||||||
|
ssize_t ret;
|
||||||
|
struct stat st;
|
||||||
|
git_buf curpath = GIT_BUF_INIT, target = GIT_BUF_INIT;
|
||||||
|
|
||||||
|
if ((error = git_buf_grow(&target, GIT_PATH_MAX + 1)) < 0 ||
|
||||||
|
(error = git_buf_puts(&curpath, path)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_SYMLINK_DEPTH; i++) {
|
||||||
|
error = p_lstat(curpath.ptr, &st);
|
||||||
|
if (error < 0 && errno == ENOENT) {
|
||||||
|
error = git_buf_puts(out, curpath.ptr);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error < 0) {
|
||||||
|
giterr_set(GITERR_OS, "failed to stat '%s'", curpath.ptr);
|
||||||
|
error = -1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISLNK(st.st_mode)) {
|
||||||
|
error = git_buf_puts(out, curpath.ptr);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX);
|
||||||
|
if (ret < 0) {
|
||||||
|
giterr_set(GITERR_OS, "failed to read symlink '%s'", curpath.ptr);
|
||||||
|
error = -1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == GIT_PATH_MAX) {
|
||||||
|
giterr_set(GITERR_INVALID, "symlink target too long");
|
||||||
|
error = -1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* readlink(2) won't NUL-terminate for us */
|
||||||
|
target.ptr[ret] = '\0';
|
||||||
|
target.size = ret;
|
||||||
|
|
||||||
|
root = git_path_root(target.ptr);
|
||||||
|
if (root >= 0) {
|
||||||
|
if ((error = git_buf_puts(&curpath, target.ptr)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
} else {
|
||||||
|
git_buf dir = GIT_BUF_INIT;
|
||||||
|
|
||||||
|
if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
git_buf_swap(&curpath, &dir);
|
||||||
|
git_buf_free(&dir);
|
||||||
|
|
||||||
|
if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
giterr_set(GITERR_INVALID, "maximum symlink depth reached");
|
||||||
|
error = -1;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
git_buf_free(&curpath);
|
||||||
|
git_buf_free(&target);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
|
int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
|
||||||
{
|
{
|
||||||
int compression, error = -1;
|
int compression, error = -1;
|
||||||
@ -265,11 +340,14 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
|
|||||||
file->path_lock = git_buf_detach(&tmp_path);
|
file->path_lock = git_buf_detach(&tmp_path);
|
||||||
GITERR_CHECK_ALLOC(file->path_lock);
|
GITERR_CHECK_ALLOC(file->path_lock);
|
||||||
} else {
|
} else {
|
||||||
path_len = strlen(path);
|
git_buf resolved_path = GIT_BUF_INIT;
|
||||||
|
|
||||||
|
if ((error = resolve_symlink(&resolved_path, path)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
/* Save the original path of the file */
|
/* Save the original path of the file */
|
||||||
file->path_original = git__strdup(path);
|
path_len = resolved_path.size;
|
||||||
GITERR_CHECK_ALLOC(file->path_original);
|
file->path_original = git_buf_detach(&resolved_path);
|
||||||
|
|
||||||
/* create the locking path by appending ".lock" to the original */
|
/* create the locking path by appending ".lock" to the original */
|
||||||
GITERR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH);
|
GITERR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH);
|
||||||
|
@ -151,3 +151,56 @@ void test_core_filebuf__rename_error(void)
|
|||||||
|
|
||||||
cl_assert_equal_i(false, git_path_exists(test_lock));
|
cl_assert_equal_i(false, git_path_exists(test_lock));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_core_filebuf__symlink_follow(void)
|
||||||
|
{
|
||||||
|
git_filebuf file = GIT_FILEBUF_INIT;
|
||||||
|
const char *dir = "linkdir", *source = "linkdir/link";
|
||||||
|
|
||||||
|
#ifdef GIT_WIN32
|
||||||
|
cl_skip();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
cl_git_pass(p_mkdir(dir, 0777));
|
||||||
|
cl_git_pass(p_symlink("target", source));
|
||||||
|
|
||||||
|
cl_git_pass(git_filebuf_open(&file, source, 0, 0666));
|
||||||
|
cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
|
||||||
|
|
||||||
|
cl_assert_equal_i(true, git_path_exists("linkdir/target.lock"));
|
||||||
|
|
||||||
|
cl_git_pass(git_filebuf_commit(&file));
|
||||||
|
cl_assert_equal_i(true, git_path_exists("linkdir/target"));
|
||||||
|
|
||||||
|
git_filebuf_cleanup(&file);
|
||||||
|
|
||||||
|
/* The second time around, the target file does exist */
|
||||||
|
cl_git_pass(git_filebuf_open(&file, source, 0, 0666));
|
||||||
|
cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
|
||||||
|
|
||||||
|
cl_assert_equal_i(true, git_path_exists("linkdir/target.lock"));
|
||||||
|
|
||||||
|
cl_git_pass(git_filebuf_commit(&file));
|
||||||
|
cl_assert_equal_i(true, git_path_exists("linkdir/target"));
|
||||||
|
|
||||||
|
git_filebuf_cleanup(&file);
|
||||||
|
cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_core_filebuf__symlink_depth(void)
|
||||||
|
{
|
||||||
|
git_filebuf file = GIT_FILEBUF_INIT;
|
||||||
|
const char *dir = "linkdir", *source = "linkdir/link";
|
||||||
|
|
||||||
|
#ifdef GIT_WIN32
|
||||||
|
cl_skip();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
cl_git_pass(p_mkdir(dir, 0777));
|
||||||
|
/* Endless loop */
|
||||||
|
cl_git_pass(p_symlink("link", source));
|
||||||
|
|
||||||
|
cl_git_fail(git_filebuf_open(&file, source, 0, 0666));
|
||||||
|
|
||||||
|
cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user