xref: /openbsd/usr.bin/cvs/init.c (revision 7b36286a)
1 /*	$OpenBSD: init.c,v 1.35 2008/06/23 20:51:08 ragge 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 
40 void	cvs_init_local(void);
41 
42 static void init_mkdir(const char *, mode_t);
43 static void init_mkfile(char *, const char **);
44 
45 struct cvsroot_file {
46 	char			*cf_path;
47 	const char		**cf_content;
48 };
49 
50 static 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 
66 static 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 
73 struct 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 
83 int
84 cvs_init(int argc, char **argv)
85 {
86 	if (argc > 1)
87 		fatal("init does not take any extra arguments");
88 
89 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
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 	return (0);
97 }
98 
99 void
100 cvs_init_local(void)
101 {
102 	u_int i;
103 	char path[MAXPATHLEN];
104 
105 	cvs_log(LP_TRACE, "cvs_init_local()");
106 
107 	/* Create repository root directory if it does not already exist */
108 	init_mkdir(current_cvsroot->cr_dir, 0777);
109 
110 	for (i = 0; i < INIT_NDIRS; i++) {
111 		(void)xsnprintf(path, MAXPATHLEN, "%s/%s",
112 		    current_cvsroot->cr_dir, cvsroot_dirs[i]);
113 
114 		init_mkdir(path, 0777);
115 	}
116 
117 	for (i = 0; i < INIT_NFILES; i++) {
118 		(void)xsnprintf(path, MAXPATHLEN, "%s/%s",
119 		    current_cvsroot->cr_dir, cvsroot_files[i].cf_path);
120 
121 		init_mkfile(path, cvsroot_files[i].cf_content);
122 	}
123 }
124 
125 static void
126 init_mkdir(const char *path, mode_t mode)
127 {
128 	struct stat st;
129 
130 	if (mkdir(path, mode) == -1) {
131 		if (!(errno == EEXIST ||
132 		    (errno == EACCES && (stat(path, &st) == 0) &&
133 		    S_ISDIR(st.st_mode)))) {
134 			fatal("init_mkdir: mkdir: `%s': %s",
135 			    path, strerror(errno));
136 		}
137 	}
138 }
139 
140 static void
141 init_mkfile(char *path, const char **content)
142 {
143 	BUF *b;
144 	size_t len;
145 	int fd, openflags, rcsflags;
146 	char rpath[MAXPATHLEN];
147 	const char **p;
148 	RCSFILE *file;
149 
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 			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, MAXPATHLEN, "%s%s", path, RCS_FILE_EXT);
176 
177 	if ((file = rcs_open(rpath, fd, rcsflags, 0444)) == NULL)
178 		fatal("failed to create RCS file for `%s'", path);
179 
180 	b = cvs_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 }
193