Blob


1 /*
2 * Copyright (c) 2018, 2019 Stefan Sperling <stsp@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <sys/param.h>
18 #include <sys/queue.h>
19 #include <sys/limits.h>
20 #include <sys/stat.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <util.h>
29 #include <err.h>
30 #include <unistd.h>
31 #include <uuid.h>
33 #include "got_error.h"
34 #include "got_object.h"
35 #include "got_reference.h"
36 #include "got_repository.h"
37 #include "got_worktree.h"
38 #include "got_opentemp.h"
39 #include "got_privsep.h"
41 #include "got_lib_worktree.h"
42 #include "got_lib_path.h"
44 #define GOT_REPO_PATH "../../../"
46 static int verbose;
48 void
49 test_printf(char *fmt, ...)
50 {
51 va_list ap;
53 if (!verbose)
54 return;
56 va_start(ap, fmt);
57 vprintf(fmt, ap);
58 va_end(ap);
59 }
61 static int
62 remove_got_dir(const char *worktree_path)
63 {
64 char *path;
66 if (asprintf(&path, "%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR) == -1)
67 return 0;
68 rmdir(path);
69 free(path);
70 return 1;
71 }
73 static int
74 remove_meta_file(const char *worktree_path, const char *name)
75 {
76 char *path;
78 if (asprintf(&path, "%s/%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR,
79 name) == -1)
80 return 0;
81 unlink(path);
82 free(path);
83 return 1;
84 }
86 static int
87 remove_worktree(const char *worktree_path)
88 {
89 if (!remove_meta_file(worktree_path, GOT_WORKTREE_HEAD_REF))
90 return 0;
91 if (!remove_meta_file(worktree_path, GOT_WORKTREE_BASE_COMMIT))
92 return 0;
93 if (!remove_meta_file(worktree_path, GOT_WORKTREE_FILE_INDEX))
94 return 0;
95 if (!remove_meta_file(worktree_path, GOT_WORKTREE_REPOSITORY))
96 return 0;
97 if (!remove_meta_file(worktree_path, GOT_WORKTREE_PATH_PREFIX))
98 return 0;
99 if (!remove_meta_file(worktree_path, GOT_WORKTREE_LOCK))
100 return 0;
101 if (!remove_meta_file(worktree_path, GOT_WORKTREE_FORMAT))
102 return 0;
103 if (!remove_meta_file(worktree_path, GOT_WORKTREE_UUID))
104 return 0;
105 if (!remove_got_dir(worktree_path))
106 return 0;
107 if (rmdir(worktree_path) == -1)
108 return 0;
109 return 1;
112 static int
113 read_meta_file(char **content, const char *path)
115 FILE *f;
116 size_t len;
117 const char delim[3] = {'\0', '\0', '\0'};
118 int ret = 0;
120 f = fopen(path, "r");
121 if (f == NULL)
122 return errno;
124 *content = fparseln(f, &len, NULL, delim, 0);
125 if (*content == NULL)
126 ret = errno;
127 if (fclose(f) != 0 && ret == 0)
128 ret = errno;
129 return ret;
132 static int
133 check_meta_file_exists(const char *worktree_path, const char *name)
135 struct stat sb;
136 char *path;
137 int ret = 0;
139 if (asprintf(&path, "%s/%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR,
140 name) == -1)
141 return 0;
142 if (stat(path, &sb) == 0)
143 ret = 1;
144 if (verbose) {
145 char *content;
146 if (read_meta_file(&content, path) == 0) {
147 test_printf("%s:\t%s\n", name, content);
148 free(content);
151 free(path);
152 return ret;
155 static int
156 worktree_init(const char *repo_path)
158 const struct got_error *err;
159 struct got_repository *repo = NULL;
160 struct got_reference *head_ref = NULL;
161 char worktree_path[PATH_MAX];
162 int ok = 0;
164 err = got_repo_open(&repo, repo_path);
165 if (err != NULL || repo == NULL)
166 goto done;
167 err = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
168 if (err != NULL || head_ref == NULL)
169 goto done;
171 strlcpy(worktree_path, "worktree-XXXXXX", sizeof(worktree_path));
172 if (mkdtemp(worktree_path) == NULL)
173 goto done;
175 err = got_worktree_init(worktree_path, head_ref, "/", repo);
176 if (err != NULL)
177 goto done;
179 /* Ensure required files were created. */
180 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_HEAD_REF))
181 goto done;
182 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_BASE_COMMIT))
183 goto done;
184 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_LOCK))
185 goto done;
186 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_FILE_INDEX))
187 goto done;
188 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_REPOSITORY))
189 goto done;
190 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_PATH_PREFIX))
191 goto done;
192 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_FORMAT))
193 goto done;
194 if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_UUID))
195 goto done;
197 if (!remove_worktree(worktree_path))
198 goto done;
199 ok = 1;
200 done:
201 if (head_ref)
202 got_ref_close(head_ref);
203 if (repo)
204 got_repo_close(repo);
205 return ok;
208 static int
209 obstruct_meta_file(char **path, const char *worktree_path, const char *name)
211 FILE *f;
212 char *s = "This file should not be here\n";
213 int ret = 1;
215 if (asprintf(path, "%s/%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR,
216 name) == -1)
217 return 0;
218 f = fopen(*path, "w+");
219 if (f == NULL) {
220 free(*path);
221 return 0;
223 if (fwrite(s, 1, strlen(s), f) != strlen(s)) {
224 free(*path);
225 ret = 0;
227 if (fclose(f) != 0)
228 ret = 0;
229 return ret;
232 static int
233 obstruct_meta_file_and_init(int *ok, struct got_repository *repo,
234 const char *worktree_path, char *name)
236 const struct got_error *err;
237 char *path;
238 int ret = 0;
239 struct got_reference *head_ref = NULL;
241 if (!obstruct_meta_file(&path, worktree_path, GOT_WORKTREE_FILE_INDEX))
242 return 0;
244 err = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
245 if (err != NULL || head_ref == NULL)
246 return 0;
248 err = got_worktree_init(worktree_path, head_ref, "/", repo);
249 if (err != NULL && err->code == GOT_ERR_ERRNO && errno == EEXIST) {
250 (*ok)++;
251 ret = 1;
253 unlink(path);
254 free(path);
255 got_ref_close(head_ref);
256 return ret;
259 static int
260 worktree_init_exists(const char *repo_path)
262 const struct got_error *err;
263 struct got_repository *repo = NULL;
264 char worktree_path[PATH_MAX];
265 char *gotpath = NULL;
266 int ok = 0;
268 err = got_repo_open(&repo, repo_path);
269 if (err != NULL || repo == NULL)
270 goto done;
271 strlcpy(worktree_path, "worktree-XXXXXX", sizeof(worktree_path));
272 if (mkdtemp(worktree_path) == NULL)
273 goto done;
274 if (mkdir(worktree_path, GOT_DEFAULT_DIR_MODE) == -1 && errno != EEXIST)
275 goto done;
277 if (asprintf(&gotpath, "%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR)
278 == -1)
279 goto done;
280 if (mkdir(gotpath, GOT_DEFAULT_DIR_MODE) == -1 && errno != EEXIST)
281 goto done;
283 /* Create files which got_worktree_init() will try to create as well. */
284 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
285 GOT_WORKTREE_HEAD_REF))
286 goto done;
287 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
288 GOT_WORKTREE_BASE_COMMIT))
289 goto done;
290 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
291 GOT_WORKTREE_LOCK))
292 goto done;
293 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
294 GOT_WORKTREE_FILE_INDEX))
295 goto done;
296 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
297 GOT_WORKTREE_REPOSITORY))
298 goto done;
299 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
300 GOT_WORKTREE_PATH_PREFIX))
301 goto done;
302 if (!obstruct_meta_file_and_init(&ok, repo, worktree_path,
303 GOT_WORKTREE_FORMAT))
304 goto done;
306 done:
307 if (repo)
308 got_repo_close(repo);
309 free(gotpath);
310 if (ok == 7)
311 remove_worktree(worktree_path);
312 return (ok == 7);
315 static void
316 progress_cb(void *arg, unsigned char status, const char *path)
320 static int
321 worktree_checkout(const char *repo_path)
323 const struct got_error *err;
324 struct got_repository *repo = NULL;
325 struct got_reference *head_ref = NULL;
326 struct got_worktree *worktree = NULL;
327 char *makefile_path = NULL, *cfile_path = NULL;
328 char worktree_path[PATH_MAX];
329 int ok = 0;
330 struct stat sb;
332 err = got_repo_open(&repo, repo_path);
333 if (err != NULL || repo == NULL)
334 goto done;
335 err = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
336 if (err != NULL || head_ref == NULL)
337 goto done;
339 strlcpy(worktree_path, "worktree-XXXXXX", sizeof(worktree_path));
340 if (mkdtemp(worktree_path) == NULL)
341 goto done;
343 err = got_worktree_init(worktree_path, head_ref, "/regress/worktree",
344 repo);
345 if (err != NULL)
346 goto done;
348 err = got_worktree_open(&worktree, worktree_path);
349 if (err != NULL)
350 goto done;
352 err = got_worktree_checkout_files(worktree, repo, progress_cb, NULL,
353 NULL, NULL);
354 if (err != NULL)
355 goto done;
357 test_printf("checked out %s\n", worktree_path);
359 /* The work tree should contain a Makefile and worktree_test.c. */
360 if (asprintf(&makefile_path, "%s/Makefile", worktree_path) == -1)
361 goto done;
362 if (stat(makefile_path, &sb) != 0)
363 goto done;
364 else
365 unlink(makefile_path);
366 if (asprintf(&cfile_path, "%s/worktree_test.c", worktree_path) == -1)
367 goto done;
368 if (stat(cfile_path, &sb) != 0)
369 goto done;
370 else
371 unlink(cfile_path);
373 if (!remove_worktree(worktree_path))
374 goto done;
376 ok = 1;
377 done:
378 if (worktree)
379 got_worktree_close(worktree);
380 if (head_ref)
381 got_ref_close(head_ref);
382 if (repo)
383 got_repo_close(repo);
384 free(makefile_path);
385 free(cfile_path);
386 return ok;
389 #define RUN_TEST(expr, name) \
390 { test_ok = (expr); \
391 printf("test_%s %s\n", (name), test_ok ? "ok" : "failed"); \
392 failure = (failure || !test_ok); }
395 void
396 usage(void)
398 fprintf(stderr, "usage: worktree_test [-v] [REPO_PATH]\n");
401 int
402 main(int argc, char *argv[])
404 int test_ok = 0, failure = 0;
405 const char *repo_path;
406 char *cwd = NULL;
407 int ch;
409 #ifndef PROFILE
410 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
411 "unveil", NULL) == -1)
412 err(1, "pledge");
413 #endif
415 while ((ch = getopt(argc, argv, "v")) != -1) {
416 switch (ch) {
417 case 'v':
418 verbose = 1;
419 break;
420 default:
421 usage();
422 return 1;
425 argc -= optind;
426 argv += optind;
428 if (argc == 0)
429 repo_path = GOT_REPO_PATH;
430 else if (argc == 1)
431 repo_path = argv[0];
432 else {
433 usage();
434 return 1;
437 cwd = getcwd(NULL, 0);
438 if (cwd == NULL)
439 err(1, "getcwd");
440 if (unveil(cwd, "rwc") != 0)
441 err(1, "unvail");
442 free(cwd);
444 if (unveil("/tmp", "rwc") != 0)
445 err(1, "unveil");
447 if (unveil(repo_path, "r") != 0)
448 err(1, "unveil");
450 if (got_privsep_unveil_exec_helpers() != NULL)
451 return 1;
453 if (unveil(NULL, NULL) != 0)
454 err(1, "unveil");
456 RUN_TEST(worktree_init(repo_path), "init");
457 RUN_TEST(worktree_init_exists(repo_path), "init exists");
458 RUN_TEST(worktree_checkout(repo_path), "checkout");
460 return failure ? 1 : 0;