Merge branch 'mh/remove-subtree-long-pathname-fix' into maint

* mh/remove-subtree-long-pathname-fix:
  entry.c: fix possible buffer overflow in remove_subtree()
  checkout_entry(): use the strbuf throughout the function
This commit is contained in:
Junio C Hamano 2014-04-03 13:39:05 -07:00
commit a3236f4739

64
entry.c
View File

@ -44,33 +44,33 @@ static void create_directories(const char *path, int path_len,
free(buf); free(buf);
} }
static void remove_subtree(const char *path) static void remove_subtree(struct strbuf *path)
{ {
DIR *dir = opendir(path); DIR *dir = opendir(path->buf);
struct dirent *de; struct dirent *de;
char pathbuf[PATH_MAX]; int origlen = path->len;
char *name;
if (!dir) if (!dir)
die_errno("cannot opendir '%s'", path); die_errno("cannot opendir '%s'", path->buf);
strcpy(pathbuf, path);
name = pathbuf + strlen(path);
*name++ = '/';
while ((de = readdir(dir)) != NULL) { while ((de = readdir(dir)) != NULL) {
struct stat st; struct stat st;
if (is_dot_or_dotdot(de->d_name)) if (is_dot_or_dotdot(de->d_name))
continue; continue;
strcpy(name, de->d_name);
if (lstat(pathbuf, &st)) strbuf_addch(path, '/');
die_errno("cannot lstat '%s'", pathbuf); strbuf_addstr(path, de->d_name);
if (lstat(path->buf, &st))
die_errno("cannot lstat '%s'", path->buf);
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
remove_subtree(pathbuf); remove_subtree(path);
else if (unlink(pathbuf)) else if (unlink(path->buf))
die_errno("cannot unlink '%s'", pathbuf); die_errno("cannot unlink '%s'", path->buf);
strbuf_setlen(path, origlen);
} }
closedir(dir); closedir(dir);
if (rmdir(path)) if (rmdir(path->buf))
die_errno("cannot rmdir '%s'", path); die_errno("cannot rmdir '%s'", path->buf);
} }
static int create_file(const char *path, unsigned int mode) static int create_file(const char *path, unsigned int mode)
@ -245,27 +245,25 @@ static int check_path(const char *path, int len, struct stat *st, int skiplen)
int checkout_entry(struct cache_entry *ce, int checkout_entry(struct cache_entry *ce,
const struct checkout *state, char *topath) const struct checkout *state, char *topath)
{ {
static struct strbuf path_buf = STRBUF_INIT; static struct strbuf path = STRBUF_INIT;
char *path;
struct stat st; struct stat st;
int len;
if (topath) if (topath)
return write_entry(ce, topath, state, 1); return write_entry(ce, topath, state, 1);
strbuf_reset(&path_buf); strbuf_reset(&path);
strbuf_add(&path_buf, state->base_dir, state->base_dir_len); strbuf_add(&path, state->base_dir, state->base_dir_len);
strbuf_add(&path_buf, ce->name, ce_namelen(ce)); strbuf_add(&path, ce->name, ce_namelen(ce));
path = path_buf.buf;
len = path_buf.len;
if (!check_path(path, len, &st, state->base_dir_len)) { if (!check_path(path.buf, path.len, &st, state->base_dir_len)) {
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
if (!changed) if (!changed)
return 0; return 0;
if (!state->force) { if (!state->force) {
if (!state->quiet) if (!state->quiet)
fprintf(stderr, "%s already exists, no checkout\n", path); fprintf(stderr,
"%s already exists, no checkout\n",
path.buf);
return -1; return -1;
} }
@ -280,12 +278,14 @@ int checkout_entry(struct cache_entry *ce,
if (S_ISGITLINK(ce->ce_mode)) if (S_ISGITLINK(ce->ce_mode))
return 0; return 0;
if (!state->force) if (!state->force)
return error("%s is a directory", path); return error("%s is a directory", path.buf);
remove_subtree(path); remove_subtree(&path);
} else if (unlink(path)) } else if (unlink(path.buf))
return error("unable to unlink old '%s' (%s)", path, strerror(errno)); return error("unable to unlink old '%s' (%s)",
path.buf, strerror(errno));
} else if (state->not_new) } else if (state->not_new)
return 0; return 0;
create_directories(path, len, state);
return write_entry(ce, path, state, 0); create_directories(path.buf, path.len, state);
return write_entry(ce, path.buf, state, 0);
} }