commit - 369fd7e5fa99b95f7d7aa812b5260584b86a3778
commit + b7422a2f5c775615ff397e14c04aa9c9a41504c1
blob - 2a2d317c015385526ce0f22c09ed3240dafad6a5
blob + cec70da8d017f316d73c553fe1f13cb015f3d6e8
--- lib/worktree.c
+++ lib/worktree.c
{
const struct got_error *err = NULL;
char target_path[PATH_MAX];
+ char canonpath[PATH_MAX];
size_t len, target_len = 0;
- char *resolved_path = NULL, *abspath = NULL;
char *path_got = NULL;
const uint8_t *buf = got_object_blob_get_read_buf(blob);
size_t hdrlen = got_object_blob_get_hdrlen(blob);
target_path[target_len] = '\0';
/*
+ * We do not use realpath(3) to resolve the symlink's target path
+ * because we don't want to resolve symlinks recursively. Instead
+ * we make the path absolute and then canonicalize it.
* Relative symlink target lookup should begin at the directory
* in which the blob object is being installed.
*/
if (!got_path_is_absolute(target_path)) {
+ char *abspath;
char *parent = dirname(ondisk_path);
if (parent == NULL) {
err = got_error_from_errno2("dirname", ondisk_path);
err = got_error_from_errno("asprintf");
goto done;
}
- }
-
- /*
- * unveil(2) restricts our view of paths in the filesystem.
- * ENOENT will occur if a link target path does not exist or
- * if it points outside our unveiled path space.
- */
- resolved_path = realpath(abspath ? abspath : target_path, NULL);
- if (resolved_path == NULL) {
- if (errno != ENOENT) {
- err = got_error_from_errno2("realpath", target_path);
+ err = got_canonpath(abspath, canonpath, sizeof(canonpath));
+ free(abspath);
+ if (err)
goto done;
- }
+ } else {
+ err = got_canonpath(target_path, canonpath, sizeof(canonpath));
+ if (err)
+ goto done;
}
/* Only allow symlinks pointing at paths within the work tree. */
- if (!got_path_is_child(resolved_path ? resolved_path : (abspath ?
- abspath : target_path), worktree->root_path,
+ if (!got_path_is_child(canonpath, worktree->root_path,
strlen(worktree->root_path))) {
/* install as a regular file */
*is_bad_symlink = 1;
err = got_error_from_errno("asprintf");
goto done;
}
- if (got_path_is_child(resolved_path ? resolved_path : (abspath ?
- abspath : target_path), path_got, strlen(path_got))) {
+ if (got_path_is_child(canonpath, path_got, strlen(path_got))) {
/* install as a regular file */
*is_bad_symlink = 1;
got_object_blob_rewind(blob);
err = (*progress_cb)(progress_arg, reverting_versioned_file ?
GOT_STATUS_REVERT : GOT_STATUS_ADD, path);
done:
- free(resolved_path);
- free(abspath);
free(path_got);
return err;
}
blob - 4ed5d4635a5d84136d563642565378542947f5fc
blob + b0bd8dbc81e9a03e974842f00623c4174ba9cef8
--- regress/cmdline/checkout.sh
+++ regress/cmdline/checkout.sh
(cd $testroot/repo && ln -s alpha alpha.link)
(cd $testroot/repo && ln -s epsilon epsilon.link)
(cd $testroot/repo && ln -s /etc/passwd passwd.link)
+ (cd $testroot/repo && ln -s passwd.link passwd2.link)
(cd $testroot/repo && ln -s ../beta epsilon/beta.link)
(cd $testroot/repo && ln -s nonexistent nonexistent.link)
(cd $testroot/repo && ln -s .got/foo dotgotfoo.link)
(cd $testroot/repo && git add .)
git_commit $testroot/repo -m "add symlinks"
- got checkout $testroot/repo $testroot/wt > /dev/null
+ got checkout $testroot/repo $testroot/wt > $testroot/stdout
ret="$?"
if [ "$ret" != "0" ]; then
+ echo "got checkout failed unexpectedly" >&2
test_done "$testroot" "$ret"
return 1
fi
+
+ echo "A $testroot/wt/alpha" > $testroot/stdout.expected
+ echo "A $testroot/wt/alpha.link" >> $testroot/stdout.expected
+ echo "A $testroot/wt/beta" >> $testroot/stdout.expected
+ echo "A $testroot/wt/dotgotfoo.link" >> $testroot/stdout.expected
+ echo "A $testroot/wt/epsilon/beta.link" >> $testroot/stdout.expected
+ echo "A $testroot/wt/epsilon/zeta" >> $testroot/stdout.expected
+ echo "A $testroot/wt/epsilon.link" >> $testroot/stdout.expected
+ echo "A $testroot/wt/gamma/delta" >> $testroot/stdout.expected
+ echo "A $testroot/wt/nonexistent.link" >> $testroot/stdout.expected
+ echo "A $testroot/wt/passwd.link" >> $testroot/stdout.expected
+ echo "A $testroot/wt/passwd2.link" >> $testroot/stdout.expected
+ echo "Now shut up and hack" >> $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
if ! [ -h $testroot/wt/alpha.link ]; then
echo "alpha.link is not a symlink"
test_done "$testroot" "1"
return 1
fi
+ if ! [ -h $testroot/wt/passwd2.link ]; then
+ echo "passwd2.link is not a symlink"
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ readlink $testroot/wt/passwd2.link > $testroot/stdout
+ echo "passwd.link" > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
readlink $testroot/wt/epsilon/beta.link > $testroot/stdout
echo "../beta" > $testroot/stdout.expected
cmp -s $testroot/stdout.expected $testroot/stdout