jcs's openbsd hax
openbsd
1/* $OpenBSD: init.c,v 1.42 2021/10/25 10:07:12 deraadt Exp $ */
2/*
3 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4 * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
19 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/stat.h>
29
30#include <errno.h>
31#include <fcntl.h>
32#include <string.h>
33#include <unistd.h>
34
35#include "atomicio.h"
36#include "cvs.h"
37#include "init.h"
38#include "remote.h"
39
40void cvs_init_local(void);
41
42static void init_mkdir(const char *, mode_t);
43static void init_mkfile(char *, char **);
44
45struct cvsroot_file {
46 char *cf_path;
47 char **cf_content;
48};
49
50static const struct cvsroot_file cvsroot_files[] = {
51 { CVS_PATH_CHECKOUTLIST, NULL },
52 { CVS_PATH_COMMITINFO, NULL },
53 { CVS_PATH_CONFIG, config_contents },
54 { CVS_PATH_CVSWRAPPERS, NULL },
55 { CVS_PATH_EDITINFO, NULL },
56 { CVS_PATH_HISTORY, NULL },
57 { CVS_PATH_LOGINFO, NULL },
58 { CVS_PATH_MODULES, NULL },
59 { CVS_PATH_NOTIFY_R, NULL },
60 { CVS_PATH_RCSINFO, NULL },
61 { CVS_PATH_TAGINFO, NULL },
62 { CVS_PATH_VALTAGS, NULL },
63 { CVS_PATH_VERIFYMSG, NULL }
64};
65
66static const char *cvsroot_dirs[2] = {
67 CVS_PATH_ROOT, CVS_PATH_EMPTYDIR
68};
69
70#define INIT_NFILES (sizeof(cvsroot_files)/sizeof(cvsroot_files[0]))
71#define INIT_NDIRS (sizeof(cvsroot_dirs)/sizeof(cvsroot_dirs[0]))
72
73struct cvs_cmd cvs_cmd_init = {
74 CVS_OP_INIT, 0, "init",
75 { { 0 }, { 0 } },
76 "Create a CVS repository if it doesn't exist",
77 "",
78 "",
79 NULL,
80 cvs_init
81};
82
83int
84cvs_init(int argc, char **argv)
85{
86 if (argc > 1)
87 fatal("init does not take any extra arguments");
88
89 if (cvsroot_is_remote()) {
90 cvs_client_connect_to_server();
91 cvs_client_send_request("init %s", current_cvsroot->cr_dir);
92 cvs_client_get_responses();
93 } else {
94 cvs_init_local();
95 }
96
97 return (0);
98}
99
100void
101cvs_init_local(void)
102{
103 u_int i;
104 char path[PATH_MAX];
105
106 cvs_log(LP_TRACE, "cvs_init_local()");
107
108 /* Create repository root directory if it does not already exist */
109 init_mkdir(current_cvsroot->cr_dir, 0777);
110
111 for (i = 0; i < INIT_NDIRS; i++) {
112 (void)xsnprintf(path, PATH_MAX, "%s/%s",
113 current_cvsroot->cr_dir, cvsroot_dirs[i]);
114
115 init_mkdir(path, 0777);
116 }
117
118 for (i = 0; i < INIT_NFILES; i++) {
119 (void)xsnprintf(path, PATH_MAX, "%s/%s",
120 current_cvsroot->cr_dir, cvsroot_files[i].cf_path);
121
122 init_mkfile(path, cvsroot_files[i].cf_content);
123 }
124}
125
126static void
127init_mkdir(const char *path, mode_t mode)
128{
129 struct stat st;
130
131 if (mkdir(path, mode) == -1) {
132 if (!(errno == EEXIST ||
133 (errno == EACCES && (stat(path, &st) == 0) &&
134 S_ISDIR(st.st_mode)))) {
135 fatal("init_mkdir: mkdir: `%s': %s",
136 path, strerror(errno));
137 }
138 }
139}
140
141static void
142init_mkfile(char *path, char **content)
143{
144 BUF *b;
145 size_t len;
146 int fd, rcsflags;
147 char rpath[PATH_MAX];
148 char **p;
149 RCSFILE *file;
150
151 rcsflags = RCS_WRITE | RCS_CREATE;
152
153 if ((fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0444)) == -1)
154 fatal("init_mkfile: open: `%s': %s", path, strerror(errno));
155
156 if (content != NULL) {
157 for (p = content; *p != NULL; ++p) {
158 len = strlen(*p);
159 if (atomicio(vwrite, fd, *p, len) != len)
160 fatal("init_mkfile: atomicio failed");
161 }
162 }
163
164 /*
165 * Make sure history and val-tags files are world-writable.
166 * Every user should be able to write to them.
167 */
168 if (strcmp(strrchr(CVS_PATH_HISTORY, '/'), strrchr(path, '/')) == 0 ||
169 strcmp(strrchr(CVS_PATH_VALTAGS, '/'), strrchr(path, '/')) == 0) {
170 (void)fchmod(fd, 0666);
171 (void)close(fd);
172 return;
173 }
174
175 (void)xsnprintf(rpath, PATH_MAX, "%s%s", path, RCS_FILE_EXT);
176
177 if ((file = rcs_open(rpath, -1, rcsflags, 0444)) == NULL)
178 fatal("failed to create RCS file for `%s'", path);
179
180 b = buf_load(path);
181
182 if (rcs_rev_add(file, RCS_HEAD_REV, "initial checkin", -1, NULL) == -1)
183 fatal("init_mkfile: failed to add new revision");
184
185 /* b buffer is free'd in rcs_deltatext_set */
186 if (rcs_deltatext_set(file, file->rf_head, b) == -1)
187 fatal("init_mkfile: failed to set delta");
188
189 file->rf_flags &= ~RCS_SYNCED;
190 rcs_close(file);
191 (void)close(fd);
192}