xref: /openbsd-src/usr.bin/cvs/init.c (revision cf2525843d483a385de106a1361b2b9c18d96583)
1 /*	$OpenBSD: init.c,v 1.25 2006/06/16 14:07:42 joris 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 "includes.h"
29 
30 #include "cvs.h"
31 #include "init.h"
32 #include "log.h"
33 
34 int	cvs_init(int, char **);
35 void	cvs_init_local(void);
36 
37 static void init_mkdir(const char *, mode_t);
38 static void init_mkfile(char *, const char *const *);
39 
40 struct cvsroot_file {
41 	char			*cf_path;
42 	const char *const	*cf_content;
43 };
44 
45 static const struct cvsroot_file cvsroot_files[] = {
46 	{ CVS_PATH_CHECKOUTLIST,	NULL			},
47 	{ CVS_PATH_COMMITINFO,		NULL			},
48 	{ CVS_PATH_CONFIG,		config_contents		},
49 	{ CVS_PATH_CVSWRAPPERS,		NULL			},
50 	{ CVS_PATH_EDITINFO,		NULL			},
51 	{ CVS_PATH_HISTORY,		NULL			},
52 	{ CVS_PATH_LOGINFO,		NULL			},
53 	{ CVS_PATH_MODULES,		NULL			},
54 	{ CVS_PATH_NOTIFY_R,		NULL			},
55 	{ CVS_PATH_RCSINFO,		NULL			},
56 	{ CVS_PATH_TAGINFO,		NULL			},
57 	{ CVS_PATH_VALTAGS,		NULL			},
58 	{ CVS_PATH_VERIFYMSG,		NULL			}
59 };
60 
61 static const char *cvsroot_dirs[2] = {
62 	CVS_PATH_ROOT, CVS_PATH_EMPTYDIR
63 };
64 
65 #define INIT_NFILES	(sizeof(cvsroot_files)/sizeof(cvsroot_files[0]))
66 #define INIT_NDIRS	(sizeof(cvsroot_dirs)/sizeof(cvsroot_dirs[0]))
67 
68 struct cvs_cmd cvs_cmd_init = {
69 	CVS_OP_INIT, 0, "init",
70 	{ },
71 	"Create a CVS repository if it doesn't exist",
72 	"",
73 	"",
74 	NULL,
75 	cvs_init
76 };
77 
78 int
79 cvs_init(int argc, char **argv)
80 {
81 	if (argc > 1)
82 		fatal("init does not take any extra arguments");
83 
84 	if (current_cvsroot->cr_method == CVS_METHOD_LOCAL)
85 		cvs_init_local();
86 
87 	return (0);
88 }
89 
90 void
91 cvs_init_local(void)
92 {
93 	u_int i;
94 	char *path;
95 
96 	cvs_log(LP_TRACE, "cvs_init_local()");
97 
98 	/* Create repository root directory if it does not already exist */
99 	init_mkdir(current_cvsroot->cr_dir, 0777);
100 
101 	path = xmalloc(MAXPATHLEN);
102 
103 	for (i = 0; i < INIT_NDIRS; i++) {
104 		if (cvs_path_cat(current_cvsroot->cr_dir,
105 		    cvsroot_dirs[i], path, MAXPATHLEN) >= MAXPATHLEN)
106 			fatal("cvs_init_local: truncation");
107 
108 		init_mkdir(path, 0777);
109 	}
110 
111 	for (i = 0; i < INIT_NFILES; i++) {
112 		if (cvs_path_cat(current_cvsroot->cr_dir,
113 		    cvsroot_files[i].cf_path, path, MAXPATHLEN) >= MAXPATHLEN)
114 			fatal("cvs_init_local: truncation");
115 
116 		init_mkfile(path, cvsroot_files[i].cf_content);
117 	}
118 
119 	xfree(path);
120 }
121 
122 static void
123 init_mkdir(const char *path, mode_t mode)
124 {
125 	struct stat st;
126 
127 	if (mkdir(path, mode) == -1) {
128 		if (!(errno == EEXIST ||
129 		    (errno == EACCES && (stat(path, &st) == 0) &&
130 		    S_ISDIR(st.st_mode)))) {
131 			fatal("init_mkdir: mkdir: `%s': %s",
132 			    path, strerror(errno));
133 		}
134 	}
135 }
136 
137 static void
138 init_mkfile(char *path, const char *const *content)
139 {
140 	BUF *b;
141 	size_t len;
142 	int fd, openflags, rcsflags;
143 	char *d, *rpath;
144 	const char *const *p;
145 	RCSFILE *file;
146 
147 	len = 0;
148 	fd = -1;
149 	d = NULL;
150 	openflags = O_WRONLY|O_CREAT|O_EXCL;
151 	rcsflags = RCS_RDWR|RCS_CREATE;
152 
153 	if ((fd = open(path, openflags, 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 			b = cvs_buf_alloc(len, BUF_AUTOEXT);
160 
161 			if (cvs_buf_append(b, *p, strlen(*p)) < 0)
162 				fatal("init_mkfile: cvs_buf_append");
163 
164 			if (cvs_buf_write_fd(b, fd) < 0)
165 				fatal("init_mkfile: cvs_buf_write_fd");
166 
167 			cvs_buf_free(b);
168 		}
169 	}
170 
171 	/*
172 	 * Make sure history and val-tags files are world-writable.
173 	 * Every user should be able to write to them.
174 	 */
175 	if (strcmp(strrchr(CVS_PATH_HISTORY, '/'), strrchr(path, '/')) == 0 ||
176 	    strcmp(strrchr(CVS_PATH_VALTAGS, '/'), strrchr(path, '/')) == 0) {
177 		(void)fchmod(fd, 0666);
178 		goto out;
179 	}
180 
181 	rpath = xstrdup(path);
182 	if (strlcat(rpath, RCS_FILE_EXT, MAXPATHLEN) >= MAXPATHLEN)
183 		fatal("init_mkfile: truncation");
184 
185 	if ((file = rcs_open(rpath, fd, rcsflags, 0444)) == NULL)
186 		fatal("failed to create RCS file for `%s'", path);
187 
188 	if ((b = cvs_buf_load(path, BUF_AUTOEXT)) == NULL)
189 		fatal("init_mkfile: failed to load %s", path);
190 
191 	cvs_buf_putc(b, '\0');
192 	d = cvs_buf_release(b);
193 
194 	if (rcs_rev_add(file, RCS_HEAD_REV, "initial checkin", -1, NULL) == -1)
195 		fatal("init_mkfile: failed to add new revision");
196 
197 	if (rcs_deltatext_set(file, file->rf_head, d) == -1)
198 		fatal("init_mkfile: failed to set delta");
199 
200 	file->rf_flags &= ~RCS_SYNCED;
201 	rcs_close(file);
202 	xfree(rpath);
203 out:
204 	(void)close(fd);
205 
206 	if (d != NULL)
207 		xfree(d);
208 }
209