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