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} 193