Skip to content

Commit 9c28390

Browse files
peffgitster
authored andcommitted
init: use strbufs to store paths
The init code predates strbufs, and uses PATH_MAX-sized buffers along with many manual checks on intermediate sizes (some of which make magic assumptions, such as that init will not create a path inside .git longer than 50 characters). We can simplify this greatly by using strbufs, which drops some hard-to-verify strcpy calls in favor of git_path_buf. While we're in the area, let's also convert existing calls to git_path to the safer git_path_buf (our existing calls were passed to pretty tame functions, and so were not a problem, but it's easy to be consistent and safe here). Note that we had an explicit test that "git init" rejects long template directories. This comes from 32d1776 (init: Do not segfault on big GIT_TEMPLATE_DIR environment variable, 2009-04-18). We can drop the test_must_fail here, as we now accept this and need only confirm that we don't segfault, which was the original point of the test. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent fdf7296 commit 9c28390

File tree

2 files changed

+76
-100
lines changed

2 files changed

+76
-100
lines changed

builtin/init-db.c

Lines changed: 74 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ static void safe_create_dir(const char *dir, int share)
3636
die(_("Could not make %s writable by group"), dir);
3737
}
3838

39-
static void copy_templates_1(char *path, int baselen,
40-
char *template, int template_baselen,
39+
static void copy_templates_1(struct strbuf *path, struct strbuf *template,
4140
DIR *dir)
4241
{
42+
size_t path_baselen = path->len;
43+
size_t template_baselen = template->len;
4344
struct dirent *de;
4445

4546
/* Note: if ".git/hooks" file exists in the repository being
@@ -49,77 +50,64 @@ static void copy_templates_1(char *path, int baselen,
4950
* with the way the namespace under .git/ is organized, should
5051
* be really carefully chosen.
5152
*/
52-
safe_create_dir(path, 1);
53+
safe_create_dir(path->buf, 1);
5354
while ((de = readdir(dir)) != NULL) {
5455
struct stat st_git, st_template;
55-
int namelen;
5656
int exists = 0;
5757

58+
strbuf_setlen(path, path_baselen);
59+
strbuf_setlen(template, template_baselen);
60+
5861
if (de->d_name[0] == '.')
5962
continue;
60-
namelen = strlen(de->d_name);
61-
if ((PATH_MAX <= baselen + namelen) ||
62-
(PATH_MAX <= template_baselen + namelen))
63-
die(_("insanely long template name %s"), de->d_name);
64-
memcpy(path + baselen, de->d_name, namelen+1);
65-
memcpy(template + template_baselen, de->d_name, namelen+1);
66-
if (lstat(path, &st_git)) {
63+
strbuf_addstr(path, de->d_name);
64+
strbuf_addstr(template, de->d_name);
65+
if (lstat(path->buf, &st_git)) {
6766
if (errno != ENOENT)
68-
die_errno(_("cannot stat '%s'"), path);
67+
die_errno(_("cannot stat '%s'"), path->buf);
6968
}
7069
else
7170
exists = 1;
7271

73-
if (lstat(template, &st_template))
74-
die_errno(_("cannot stat template '%s'"), template);
72+
if (lstat(template->buf, &st_template))
73+
die_errno(_("cannot stat template '%s'"), template->buf);
7574

7675
if (S_ISDIR(st_template.st_mode)) {
77-
DIR *subdir = opendir(template);
78-
int baselen_sub = baselen + namelen;
79-
int template_baselen_sub = template_baselen + namelen;
76+
DIR *subdir = opendir(template->buf);
8077
if (!subdir)
81-
die_errno(_("cannot opendir '%s'"), template);
82-
path[baselen_sub++] =
83-
template[template_baselen_sub++] = '/';
84-
path[baselen_sub] =
85-
template[template_baselen_sub] = 0;
86-
copy_templates_1(path, baselen_sub,
87-
template, template_baselen_sub,
88-
subdir);
78+
die_errno(_("cannot opendir '%s'"), template->buf);
79+
strbuf_addch(path, '/');
80+
strbuf_addch(template, '/');
81+
copy_templates_1(path, template, subdir);
8982
closedir(subdir);
9083
}
9184
else if (exists)
9285
continue;
9386
else if (S_ISLNK(st_template.st_mode)) {
94-
char lnk[256];
95-
int len;
96-
len = readlink(template, lnk, sizeof(lnk));
97-
if (len < 0)
98-
die_errno(_("cannot readlink '%s'"), template);
99-
if (sizeof(lnk) <= len)
100-
die(_("insanely long symlink %s"), template);
101-
lnk[len] = 0;
102-
if (symlink(lnk, path))
103-
die_errno(_("cannot symlink '%s' '%s'"), lnk, path);
87+
struct strbuf lnk = STRBUF_INIT;
88+
if (strbuf_readlink(&lnk, template->buf, 0) < 0)
89+
die_errno(_("cannot readlink '%s'"), template->buf);
90+
if (symlink(lnk.buf, path->buf))
91+
die_errno(_("cannot symlink '%s' '%s'"),
92+
lnk.buf, path->buf);
93+
strbuf_release(&lnk);
10494
}
10595
else if (S_ISREG(st_template.st_mode)) {
106-
if (copy_file(path, template, st_template.st_mode))
107-
die_errno(_("cannot copy '%s' to '%s'"), template,
108-
path);
96+
if (copy_file(path->buf, template->buf, st_template.st_mode))
97+
die_errno(_("cannot copy '%s' to '%s'"),
98+
template->buf, path->buf);
10999
}
110100
else
111-
error(_("ignoring template %s"), template);
101+
error(_("ignoring template %s"), template->buf);
112102
}
113103
}
114104

115105
static void copy_templates(const char *template_dir)
116106
{
117-
char path[PATH_MAX];
118-
char template_path[PATH_MAX];
119-
int template_len;
107+
struct strbuf path = STRBUF_INIT;
108+
struct strbuf template_path = STRBUF_INIT;
109+
size_t template_len;
120110
DIR *dir;
121-
const char *git_dir = get_git_dir();
122-
int len = strlen(git_dir);
123111
char *to_free = NULL;
124112

125113
if (!template_dir)
@@ -132,26 +120,23 @@ static void copy_templates(const char *template_dir)
132120
free(to_free);
133121
return;
134122
}
135-
template_len = strlen(template_dir);
136-
if (PATH_MAX <= (template_len+strlen("/config")))
137-
die(_("insanely long template path %s"), template_dir);
138-
strcpy(template_path, template_dir);
139-
if (template_path[template_len-1] != '/') {
140-
template_path[template_len++] = '/';
141-
template_path[template_len] = 0;
142-
}
143-
dir = opendir(template_path);
123+
124+
strbuf_addstr(&template_path, template_dir);
125+
strbuf_complete(&template_path, '/');
126+
template_len = template_path.len;
127+
128+
dir = opendir(template_path.buf);
144129
if (!dir) {
145130
warning(_("templates not found %s"), template_dir);
146131
goto free_return;
147132
}
148133

149134
/* Make sure that template is from the correct vintage */
150-
strcpy(template_path + template_len, "config");
135+
strbuf_addstr(&template_path, "config");
151136
repository_format_version = 0;
152137
git_config_from_file(check_repository_format_version,
153-
template_path, NULL);
154-
template_path[template_len] = 0;
138+
template_path.buf, NULL);
139+
strbuf_setlen(&template_path, template_len);
155140

156141
if (repository_format_version &&
157142
repository_format_version != GIT_REPO_VERSION) {
@@ -162,17 +147,15 @@ static void copy_templates(const char *template_dir)
162147
goto close_free_return;
163148
}
164149

165-
memcpy(path, git_dir, len);
166-
if (len && path[len - 1] != '/')
167-
path[len++] = '/';
168-
path[len] = 0;
169-
copy_templates_1(path, len,
170-
template_path, template_len,
171-
dir);
150+
strbuf_addstr(&path, get_git_dir());
151+
strbuf_complete(&path, '/');
152+
copy_templates_1(&path, &template_path, dir);
172153
close_free_return:
173154
closedir(dir);
174155
free_return:
175156
free(to_free);
157+
strbuf_release(&path);
158+
strbuf_release(&template_path);
176159
}
177160

178161
static int git_init_db_config(const char *k, const char *v, void *cb)
@@ -199,28 +182,20 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
199182

200183
static int create_default_files(const char *template_path)
201184
{
202-
const char *git_dir = get_git_dir();
203-
unsigned len = strlen(git_dir);
204-
static char path[PATH_MAX];
205185
struct stat st1;
186+
struct strbuf buf = STRBUF_INIT;
187+
char *path;
206188
char repo_version_string[10];
207189
char junk[2];
208190
int reinit;
209191
int filemode;
210192

211-
if (len > sizeof(path)-50)
212-
die(_("insane git directory %s"), git_dir);
213-
memcpy(path, git_dir, len);
214-
215-
if (len && path[len-1] != '/')
216-
path[len++] = '/';
217-
218193
/*
219194
* Create .git/refs/{heads,tags}
220195
*/
221-
safe_create_dir(git_path("refs"), 1);
222-
safe_create_dir(git_path("refs/heads"), 1);
223-
safe_create_dir(git_path("refs/tags"), 1);
196+
safe_create_dir(git_path_buf(&buf, "refs"), 1);
197+
safe_create_dir(git_path_buf(&buf, "refs/heads"), 1);
198+
safe_create_dir(git_path_buf(&buf, "refs/tags"), 1);
224199

225200
/* Just look for `init.templatedir` */
226201
git_config(git_init_db_config, NULL);
@@ -244,16 +219,16 @@ static int create_default_files(const char *template_path)
244219
*/
245220
if (shared_repository) {
246221
adjust_shared_perm(get_git_dir());
247-
adjust_shared_perm(git_path("refs"));
248-
adjust_shared_perm(git_path("refs/heads"));
249-
adjust_shared_perm(git_path("refs/tags"));
222+
adjust_shared_perm(git_path_buf(&buf, "refs"));
223+
adjust_shared_perm(git_path_buf(&buf, "refs/heads"));
224+
adjust_shared_perm(git_path_buf(&buf, "refs/tags"));
250225
}
251226

252227
/*
253228
* Create the default symlink from ".git/HEAD" to the "master"
254229
* branch, if it does not exist yet.
255230
*/
256-
strcpy(path + len, "HEAD");
231+
path = git_path_buf(&buf, "HEAD");
257232
reinit = (!access(path, R_OK)
258233
|| readlink(path, junk, sizeof(junk)-1) != -1);
259234
if (!reinit) {
@@ -266,10 +241,8 @@ static int create_default_files(const char *template_path)
266241
"%d", GIT_REPO_VERSION);
267242
git_config_set("core.repositoryformatversion", repo_version_string);
268243

269-
path[len] = 0;
270-
strcpy(path + len, "config");
271-
272244
/* Check filemode trustability */
245+
path = git_path_buf(&buf, "config");
273246
filemode = TEST_FILEMODE;
274247
if (TEST_FILEMODE && !lstat(path, &st1)) {
275248
struct stat st2;
@@ -290,14 +263,13 @@ static int create_default_files(const char *template_path)
290263
/* allow template config file to override the default */
291264
if (log_all_ref_updates == -1)
292265
git_config_set("core.logallrefupdates", "true");
293-
if (needs_work_tree_config(git_dir, work_tree))
266+
if (needs_work_tree_config(get_git_dir(), work_tree))
294267
git_config_set("core.worktree", work_tree);
295268
}
296269

297270
if (!reinit) {
298271
/* Check if symlink is supported in the work tree */
299-
path[len] = 0;
300-
strcpy(path + len, "tXXXXXX");
272+
path = git_path_buf(&buf, "tXXXXXX");
301273
if (!close(xmkstemp(path)) &&
302274
!unlink(path) &&
303275
!symlink("testing", path) &&
@@ -308,31 +280,35 @@ static int create_default_files(const char *template_path)
308280
git_config_set("core.symlinks", "false");
309281

310282
/* Check if the filesystem is case-insensitive */
311-
path[len] = 0;
312-
strcpy(path + len, "CoNfIg");
283+
path = git_path_buf(&buf, "CoNfIg");
313284
if (!access(path, F_OK))
314285
git_config_set("core.ignorecase", "true");
315286
probe_utf8_pathname_composition();
316287
}
317288

289+
strbuf_release(&buf);
318290
return reinit;
319291
}
320292

321293
static void create_object_directory(void)
322294
{
323-
const char *object_directory = get_object_directory();
324-
int len = strlen(object_directory);
325-
char *path = xmalloc(len + 40);
295+
struct strbuf path = STRBUF_INIT;
296+
size_t baselen;
297+
298+
strbuf_addstr(&path, get_object_directory());
299+
baselen = path.len;
300+
301+
safe_create_dir(path.buf, 1);
326302

327-
memcpy(path, object_directory, len);
303+
strbuf_setlen(&path, baselen);
304+
strbuf_addstr(&path, "/pack");
305+
safe_create_dir(path.buf, 1);
328306

329-
safe_create_dir(object_directory, 1);
330-
strcpy(path+len, "/pack");
331-
safe_create_dir(path, 1);
332-
strcpy(path+len, "/info");
333-
safe_create_dir(path, 1);
307+
strbuf_setlen(&path, baselen);
308+
strbuf_addstr(&path, "/info");
309+
safe_create_dir(path.buf, 1);
334310

335-
free(path);
311+
strbuf_release(&path);
336312
}
337313

338314
int set_git_dir_init(const char *git_dir, const char *real_git_dir,

t/t0001-init.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ test_expect_success 'init honors global core.sharedRepository' '
202202
x$(git config -f shared-honor-global/.git/config core.sharedRepository)
203203
'
204204

205-
test_expect_success 'init rejects insanely long --template' '
206-
test_must_fail git init --template=$(printf "x%09999dx" 1) test
205+
test_expect_success 'init allows insanely long --template' '
206+
git init --template=$(printf "x%09999dx" 1) test
207207
'
208208

209209
test_expect_success 'init creates a new directory' '

0 commit comments

Comments
 (0)