Skip to content

Commit

Permalink
Merge branch 'jc/setup'
Browse files Browse the repository at this point in the history
* jc/setup:
  builtin-mv: minimum fix to avoid losing files
  git-add: adjust to the get_pathspec() changes.
  Make blame accept absolute paths
  setup: sanitize absolute and funny paths in get_pathspec()
  • Loading branch information
Junio C Hamano committed Feb 21, 2008
2 parents 23f1291 + 744dacd commit 9e7bd01
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 49 deletions.
12 changes: 12 additions & 0 deletions builtin-add.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,18 @@ int cmd_add(int argc, const char **argv, const char *prefix)
goto finish;
}

if (*argv) {
/* Was there an invalid path? */
if (pathspec) {
int num;
for (num = 0; pathspec[num]; num++)
; /* just counting */
if (argc != num)
exit(1); /* error message already given */
} else
exit(1); /* error message already given */
}

fill_directory(&dir, pathspec, ignored_too);

if (show_only) {
Expand Down
4 changes: 1 addition & 3 deletions builtin-blame.c
Original file line number Diff line number Diff line change
Expand Up @@ -1894,9 +1894,7 @@ static unsigned parse_score(const char *arg)

static const char *add_prefix(const char *prefix, const char *path)
{
if (!prefix || !prefix[0])
return path;
return prefix_path(prefix, strlen(prefix), path);
return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
}

/*
Expand Down
11 changes: 10 additions & 1 deletion builtin-ls-files.c
Original file line number Diff line number Diff line change
Expand Up @@ -574,8 +574,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
pathspec = get_pathspec(prefix, argv + i);

/* Verify that the pathspec matches the prefix */
if (pathspec)
if (pathspec) {
if (argc != i) {
int cnt;
for (cnt = 0; pathspec[cnt]; cnt++)
;
if (cnt != (argc - i))
exit(1); /* error message already given */
}
prefix = verify_pathspec(prefix);
} else if (argc != i)
exit(1); /* error message already given */

/* Treat unmatching pathspec elements as errors */
if (pathspec && error_unmatch) {
Expand Down
10 changes: 7 additions & 3 deletions builtin-mv.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
int count, int base_name)
{
int i;
int len = prefix ? strlen(prefix) : 0;
const char **result = xmalloc((count + 1) * sizeof(const char *));
memcpy(result, pathspec, count * sizeof(const char *));
result[count] = NULL;
Expand All @@ -32,8 +33,11 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
if (last_slash)
result[i] = last_slash + 1;
}
result[i] = prefix_path(prefix, len, result[i]);
if (!result[i])
exit(1); /* error already given */
}
return get_pathspec(prefix, result);
return result;
}

static void show_list(const char *label, struct path_list *list)
Expand Down Expand Up @@ -164,15 +168,15 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
}

dst = add_slash(dst);
dst_len = strlen(dst) - 1;
dst_len = strlen(dst);

for (j = 0; j < last - first; j++) {
const char *path =
active_cache[first + j]->name;
source[argc + j] = path;
destination[argc + j] =
prefix_path(dst, dst_len,
path + length);
path + length + 1);
modes[argc + j] = INDEX;
}
argc += last - first;
Expand Down
158 changes: 116 additions & 42 deletions setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,118 @@
static int inside_git_dir = -1;
static int inside_work_tree = -1;

const char *prefix_path(const char *prefix, int len, const char *path)
static int sanitary_path_copy(char *dst, const char *src)
{
const char *orig = path;
char *dst0 = dst;

if (*src == '/') {
*dst++ = '/';
while (*src == '/')
src++;
}

for (;;) {
char c;
if (*path != '.')
break;
c = path[1];
/* "." */
if (!c) {
path++;
break;
char c = *src;

/*
* A path component that begins with . could be
* special:
* (1) "." and ends -- ignore and terminate.
* (2) "./" -- ignore them, eat slash and continue.
* (3) ".." and ends -- strip one and terminate.
* (4) "../" -- strip one, eat slash and continue.
*/
if (c == '.') {
switch (src[1]) {
case '\0':
/* (1) */
src++;
break;
case '/':
/* (2) */
src += 2;
while (*src == '/')
src++;
continue;
case '.':
switch (src[2]) {
case '\0':
/* (3) */
src += 2;
goto up_one;
case '/':
/* (4) */
src += 3;
while (*src == '/')
src++;
goto up_one;
}
}
}
/* "./" */

/* copy up to the next '/', and eat all '/' */
while ((c = *src++) != '\0' && c != '/')
*dst++ = c;
if (c == '/') {
path += 2;
continue;
}
if (c != '.')
*dst++ = c;
while (c == '/')
c = *src++;
src--;
} else if (!c)
break;
c = path[2];
if (!c)
path += 2;
else if (c == '/')
path += 3;
else
break;
/* ".." and "../" */
/* Remove last component of the prefix */
do {
if (!len)
die("'%s' is outside repository", orig);
len--;
} while (len && prefix[len-1] != '/');
continue;

up_one:
/*
* dst0..dst is prefix portion, and dst[-1] is '/';
* go up one level.
*/
dst -= 2; /* go past trailing '/' if any */
if (dst < dst0)
return -1;
while (1) {
if (dst <= dst0)
break;
c = *dst--;
if (c == '/') {
dst += 2;
break;
}
}
}
if (len) {
int speclen = strlen(path);
char *n = xmalloc(speclen + len + 1);
*dst = '\0';
return 0;
}

memcpy(n, prefix, len);
memcpy(n + len, path, speclen+1);
path = n;
const char *prefix_path(const char *prefix, int len, const char *path)
{
const char *orig = path;
char *sanitized = xmalloc(len + strlen(path) + 1);
if (*orig == '/')
strcpy(sanitized, path);
else {
if (len)
memcpy(sanitized, prefix, len);
strcpy(sanitized + len, path);
}
return path;
if (sanitary_path_copy(sanitized, sanitized))
goto error_out;
if (*orig == '/') {
const char *work_tree = get_git_work_tree();
size_t len = strlen(work_tree);
size_t total = strlen(sanitized) + 1;
if (strncmp(sanitized, work_tree, len) ||
(sanitized[len] != '\0' && sanitized[len] != '/')) {
error_out:
error("'%s' is outside repository", orig);
free(sanitized);
return NULL;
}
if (sanitized[len] == '/')
len++;
memmove(sanitized, sanitized + len, total - len);
}
return sanitized;
}

/*
Expand Down Expand Up @@ -114,7 +181,7 @@ void verify_non_filename(const char *prefix, const char *arg)
const char **get_pathspec(const char *prefix, const char **pathspec)
{
const char *entry = *pathspec;
const char **p;
const char **src, **dst;
int prefixlen;

if (!prefix && !entry)
Expand All @@ -128,12 +195,19 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
}

/* Otherwise we have to re-write the entries.. */
p = pathspec;
src = pathspec;
dst = pathspec;
prefixlen = prefix ? strlen(prefix) : 0;
do {
*p = prefix_path(prefix, prefixlen, entry);
} while ((entry = *++p) != NULL);
return (const char **) pathspec;
while (*src) {
const char *p = prefix_path(prefix, prefixlen, *src);
if (p)
*(dst++) = p;
src++;
}
*dst = NULL;
if (!*pathspec)
return NULL;
return pathspec;
}

/*
Expand Down
38 changes: 38 additions & 0 deletions t/t7001-mv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,42 @@ test_expect_success "Sergey Vlasov's test case" '
git mv ab a
'

test_expect_success 'absolute pathname' '(
rm -fr mine &&
mkdir mine &&
cd mine &&
test_create_repo one &&
cd one &&
mkdir sub &&
>sub/file &&
git add sub/file &&
git mv sub "$(pwd)/in" &&
! test -d sub &&
test -d in &&
git ls-files --error-unmatch in/file
)'

test_expect_success 'absolute pathname outside should fail' '(
rm -fr mine &&
mkdir mine &&
cd mine &&
out=$(pwd) &&
test_create_repo one &&
cd one &&
mkdir sub &&
>sub/file &&
git add sub/file &&
! git mv sub "$out/out" &&
test -d sub &&
! test -d ../in &&
git ls-files --error-unmatch sub/file
)'

test_done
Loading

0 comments on commit 9e7bd01

Please sign in to comment.