1 |
|
|
/* $OpenBSD: util.c,v 1.161 2017/08/28 19:33:20 otto Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> |
4 |
|
|
* Copyright (c) 2005, 2006 Joris Vink <joris@openbsd.org> |
5 |
|
|
* Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org> |
6 |
|
|
* All rights reserved. |
7 |
|
|
* |
8 |
|
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
|
* modification, are permitted provided that the following conditions |
10 |
|
|
* are met: |
11 |
|
|
* |
12 |
|
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
|
* 2. The name of the author may not be used to endorse or promote products |
15 |
|
|
* derived from this software without specific prior written permission. |
16 |
|
|
* |
17 |
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
18 |
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
19 |
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
20 |
|
|
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
21 |
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
22 |
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
23 |
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
24 |
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
25 |
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
26 |
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 |
|
|
*/ |
28 |
|
|
|
29 |
|
|
#include <sys/stat.h> |
30 |
|
|
#include <sys/types.h> |
31 |
|
|
#include <sys/wait.h> |
32 |
|
|
|
33 |
|
|
#include <atomicio.h> |
34 |
|
|
#include <errno.h> |
35 |
|
|
#include <fcntl.h> |
36 |
|
|
#include <stdlib.h> |
37 |
|
|
#include <string.h> |
38 |
|
|
#include <paths.h> |
39 |
|
|
#include <unistd.h> |
40 |
|
|
|
41 |
|
|
#include "cvs.h" |
42 |
|
|
#include "remote.h" |
43 |
|
|
#include "hash.h" |
44 |
|
|
|
45 |
|
|
extern int print_stdout; |
46 |
|
|
extern int build_dirs; |
47 |
|
|
extern int disable_fast_checkout; |
48 |
|
|
|
49 |
|
|
/* letter -> mode type map */ |
50 |
|
|
static const int cvs_modetypes[26] = { |
51 |
|
|
-1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, |
52 |
|
|
-1, 2, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, |
53 |
|
|
}; |
54 |
|
|
|
55 |
|
|
/* letter -> mode map */ |
56 |
|
|
static const mode_t cvs_modes[3][26] = { |
57 |
|
|
{ |
58 |
|
|
0, 0, 0, 0, 0, 0, 0, /* a - g */ |
59 |
|
|
0, 0, 0, 0, 0, 0, 0, /* h - m */ |
60 |
|
|
0, 0, 0, S_IRUSR, 0, 0, 0, /* n - u */ |
61 |
|
|
0, S_IWUSR, S_IXUSR, 0, 0 /* v - z */ |
62 |
|
|
}, |
63 |
|
|
{ |
64 |
|
|
0, 0, 0, 0, 0, 0, 0, /* a - g */ |
65 |
|
|
0, 0, 0, 0, 0, 0, 0, /* h - m */ |
66 |
|
|
0, 0, 0, S_IRGRP, 0, 0, 0, /* n - u */ |
67 |
|
|
0, S_IWGRP, S_IXGRP, 0, 0 /* v - z */ |
68 |
|
|
}, |
69 |
|
|
{ |
70 |
|
|
0, 0, 0, 0, 0, 0, 0, /* a - g */ |
71 |
|
|
0, 0, 0, 0, 0, 0, 0, /* h - m */ |
72 |
|
|
0, 0, 0, S_IROTH, 0, 0, 0, /* n - u */ |
73 |
|
|
0, S_IWOTH, S_IXOTH, 0, 0 /* v - z */ |
74 |
|
|
} |
75 |
|
|
}; |
76 |
|
|
|
77 |
|
|
|
78 |
|
|
/* octal -> string */ |
79 |
|
|
static const char *cvs_modestr[8] = { |
80 |
|
|
"", "x", "w", "wx", "r", "rx", "rw", "rwx" |
81 |
|
|
}; |
82 |
|
|
|
83 |
|
|
/* |
84 |
|
|
* cvs_strtomode() |
85 |
|
|
* |
86 |
|
|
* Read the contents of the string <str> and generate a permission mode from |
87 |
|
|
* the contents of <str>, which is assumed to have the mode format of CVS. |
88 |
|
|
* The CVS protocol specification states that any modes or mode types that are |
89 |
|
|
* not recognized should be silently ignored. This function does not return |
90 |
|
|
* an error in such cases, but will issue warnings. |
91 |
|
|
*/ |
92 |
|
|
void |
93 |
|
|
cvs_strtomode(const char *str, mode_t *mode) |
94 |
|
|
{ |
95 |
|
|
char type; |
96 |
|
|
size_t l; |
97 |
|
|
mode_t m; |
98 |
|
|
char buf[32], ms[4], *sp, *ep; |
99 |
|
|
|
100 |
|
|
m = 0; |
101 |
|
|
l = strlcpy(buf, str, sizeof(buf)); |
102 |
|
|
if (l >= sizeof(buf)) |
103 |
|
|
fatal("cvs_strtomode: string truncation"); |
104 |
|
|
|
105 |
|
|
sp = buf; |
106 |
|
|
ep = sp; |
107 |
|
|
|
108 |
|
|
for (sp = buf; ep != NULL; sp = ep + 1) { |
109 |
|
|
ep = strchr(sp, ','); |
110 |
|
|
if (ep != NULL) |
111 |
|
|
*ep = '\0'; |
112 |
|
|
|
113 |
|
|
memset(ms, 0, sizeof ms); |
114 |
|
|
if (sscanf(sp, "%c=%3s", &type, ms) != 2 && |
115 |
|
|
sscanf(sp, "%c=", &type) != 1) { |
116 |
|
|
fatal("failed to scan mode string `%s'", sp); |
117 |
|
|
} |
118 |
|
|
|
119 |
|
|
if (type <= 'a' || type >= 'z' || |
120 |
|
|
cvs_modetypes[type - 'a'] == -1) { |
121 |
|
|
cvs_log(LP_ERR, |
122 |
|
|
"invalid mode type `%c'" |
123 |
|
|
" (`u', `g' or `o' expected), ignoring", type); |
124 |
|
|
continue; |
125 |
|
|
} |
126 |
|
|
|
127 |
|
|
/* make type contain the actual mode index */ |
128 |
|
|
type = cvs_modetypes[type - 'a']; |
129 |
|
|
|
130 |
|
|
for (sp = ms; *sp != '\0'; sp++) { |
131 |
|
|
if (*sp <= 'a' || *sp >= 'z' || |
132 |
|
|
cvs_modes[(int)type][*sp - 'a'] == 0) { |
133 |
|
|
fatal("invalid permission bit `%c'", *sp); |
134 |
|
|
} else |
135 |
|
|
m |= cvs_modes[(int)type][*sp - 'a']; |
136 |
|
|
} |
137 |
|
|
} |
138 |
|
|
|
139 |
|
|
*mode = m; |
140 |
|
|
} |
141 |
|
|
|
142 |
|
|
/* |
143 |
|
|
* cvs_modetostr() |
144 |
|
|
* |
145 |
|
|
* Generate a CVS-format string to represent the permissions mask on a file |
146 |
|
|
* from the mode <mode> and store the result in <buf>, which can accept up to |
147 |
|
|
* <len> bytes (including the terminating NUL byte). The result is guaranteed |
148 |
|
|
* to be NUL-terminated. |
149 |
|
|
*/ |
150 |
|
|
void |
151 |
|
|
cvs_modetostr(mode_t mode, char *buf, size_t len) |
152 |
|
|
{ |
153 |
|
|
char tmp[16], *bp; |
154 |
|
|
mode_t um, gm, om; |
155 |
|
|
|
156 |
|
|
um = (mode & S_IRWXU) >> 6; |
157 |
|
|
gm = (mode & S_IRWXG) >> 3; |
158 |
|
|
om = mode & S_IRWXO; |
159 |
|
|
|
160 |
|
|
bp = buf; |
161 |
|
|
*bp = '\0'; |
162 |
|
|
|
163 |
|
|
if (um) { |
164 |
|
|
if (strlcpy(tmp, "u=", sizeof(tmp)) >= sizeof(tmp) || |
165 |
|
|
strlcat(tmp, cvs_modestr[um], sizeof(tmp)) >= sizeof(tmp)) |
166 |
|
|
fatal("cvs_modetostr: overflow for user mode"); |
167 |
|
|
|
168 |
|
|
if (strlcat(buf, tmp, len) >= len) |
169 |
|
|
fatal("cvs_modetostr: string truncation"); |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
if (gm) { |
173 |
|
|
if (um) { |
174 |
|
|
if (strlcat(buf, ",", len) >= len) |
175 |
|
|
fatal("cvs_modetostr: string truncation"); |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
if (strlcpy(tmp, "g=", sizeof(tmp)) >= sizeof(tmp) || |
179 |
|
|
strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp)) |
180 |
|
|
fatal("cvs_modetostr: overflow for group mode"); |
181 |
|
|
|
182 |
|
|
if (strlcat(buf, tmp, len) >= len) |
183 |
|
|
fatal("cvs_modetostr: string truncation"); |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
if (om) { |
187 |
|
|
if (um || gm) { |
188 |
|
|
if (strlcat(buf, ",", len) >= len) |
189 |
|
|
fatal("cvs_modetostr: string truncation"); |
190 |
|
|
} |
191 |
|
|
|
192 |
|
|
if (strlcpy(tmp, "o=", sizeof(tmp)) >= sizeof(tmp) || |
193 |
|
|
strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp)) |
194 |
|
|
fatal("cvs_modetostr: overflow for others mode"); |
195 |
|
|
|
196 |
|
|
if (strlcat(buf, tmp, len) >= len) |
197 |
|
|
fatal("cvs_modetostr: string truncation"); |
198 |
|
|
} |
199 |
|
|
} |
200 |
|
|
|
201 |
|
|
/* |
202 |
|
|
* cvs_getargv() |
203 |
|
|
* |
204 |
|
|
* Parse a line contained in <line> and generate an argument vector by |
205 |
|
|
* splitting the line on spaces and tabs. The resulting vector is stored in |
206 |
|
|
* <argv>, which can accept up to <argvlen> entries. |
207 |
|
|
* Returns the number of arguments in the vector, or -1 if an error occurred. |
208 |
|
|
*/ |
209 |
|
|
int |
210 |
|
|
cvs_getargv(const char *line, char **argv, int argvlen) |
211 |
|
|
{ |
212 |
|
|
u_int i; |
213 |
|
|
int argc, error; |
214 |
|
|
char *linebuf, *lp, *cp; |
215 |
|
|
|
216 |
|
|
linebuf = xstrdup(line); |
217 |
|
|
|
218 |
|
|
memset(argv, 0, argvlen * sizeof(char *)); |
219 |
|
|
argc = 0; |
220 |
|
|
|
221 |
|
|
/* build the argument vector */ |
222 |
|
|
error = 0; |
223 |
|
|
for (lp = linebuf; lp != NULL;) { |
224 |
|
|
cp = strsep(&lp, " \t"); |
225 |
|
|
if (cp == NULL) |
226 |
|
|
break; |
227 |
|
|
else if (*cp == '\0') |
228 |
|
|
continue; |
229 |
|
|
|
230 |
|
|
if (argc == argvlen) { |
231 |
|
|
error++; |
232 |
|
|
break; |
233 |
|
|
} |
234 |
|
|
|
235 |
|
|
argv[argc] = xstrdup(cp); |
236 |
|
|
argc++; |
237 |
|
|
} |
238 |
|
|
|
239 |
|
|
if (error != 0) { |
240 |
|
|
/* ditch the argument vector */ |
241 |
|
|
for (i = 0; i < (u_int)argc; i++) |
242 |
|
|
free(argv[i]); |
243 |
|
|
argc = -1; |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
free(linebuf); |
247 |
|
|
return (argc); |
248 |
|
|
} |
249 |
|
|
|
250 |
|
|
/* |
251 |
|
|
* cvs_makeargv() |
252 |
|
|
* |
253 |
|
|
* Allocate an argument vector large enough to accommodate for all the |
254 |
|
|
* arguments found in <line> and return it. |
255 |
|
|
*/ |
256 |
|
|
char ** |
257 |
|
|
cvs_makeargv(const char *line, int *argc) |
258 |
|
|
{ |
259 |
|
|
int i, ret; |
260 |
|
|
char *argv[1024], **copy; |
261 |
|
|
|
262 |
|
|
ret = cvs_getargv(line, argv, 1024); |
263 |
|
|
if (ret == -1) |
264 |
|
|
return (NULL); |
265 |
|
|
|
266 |
|
|
copy = xcalloc(ret + 1, sizeof(char *)); |
267 |
|
|
|
268 |
|
|
for (i = 0; i < ret; i++) |
269 |
|
|
copy[i] = argv[i]; |
270 |
|
|
copy[ret] = NULL; |
271 |
|
|
|
272 |
|
|
*argc = ret; |
273 |
|
|
return (copy); |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
/* |
277 |
|
|
* cvs_freeargv() |
278 |
|
|
* |
279 |
|
|
* Free an argument vector previously generated by cvs_getargv(). |
280 |
|
|
*/ |
281 |
|
|
void |
282 |
|
|
cvs_freeargv(char **argv, int argc) |
283 |
|
|
{ |
284 |
|
|
int i; |
285 |
|
|
|
286 |
|
|
for (i = 0; i < argc; i++) |
287 |
|
|
free(argv[i]); |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
/* |
291 |
|
|
* cvs_chdir() |
292 |
|
|
* |
293 |
|
|
* Change to directory <path>. |
294 |
|
|
* If <rm> is equal to `1', <path> is removed if chdir() fails so we |
295 |
|
|
* do not have temporary directories leftovers. |
296 |
|
|
* Returns 0 on success. |
297 |
|
|
*/ |
298 |
|
|
int |
299 |
|
|
cvs_chdir(const char *path, int rm) |
300 |
|
|
{ |
301 |
|
|
if (chdir(path) == -1) { |
302 |
|
|
if (rm == 1) |
303 |
|
|
cvs_unlink(path); |
304 |
|
|
fatal("cvs_chdir: `%s': %s", path, strerror(errno)); |
305 |
|
|
} |
306 |
|
|
|
307 |
|
|
return (0); |
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
/* |
311 |
|
|
* cvs_rename() |
312 |
|
|
* Change the name of a file. |
313 |
|
|
* rename() wrapper with an error message. |
314 |
|
|
* Returns 0 on success. |
315 |
|
|
*/ |
316 |
|
|
int |
317 |
|
|
cvs_rename(const char *from, const char *to) |
318 |
|
|
{ |
319 |
|
|
if (cvs_server_active == 0) |
320 |
|
|
cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to); |
321 |
|
|
|
322 |
|
|
if (cvs_noexec == 1) |
323 |
|
|
return (0); |
324 |
|
|
|
325 |
|
|
if (rename(from, to) == -1) |
326 |
|
|
fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno)); |
327 |
|
|
|
328 |
|
|
return (0); |
329 |
|
|
} |
330 |
|
|
|
331 |
|
|
/* |
332 |
|
|
* cvs_unlink() |
333 |
|
|
* |
334 |
|
|
* Removes the link named by <path>. |
335 |
|
|
* unlink() wrapper with an error message. |
336 |
|
|
* Returns 0 on success, or -1 on failure. |
337 |
|
|
*/ |
338 |
|
|
int |
339 |
|
|
cvs_unlink(const char *path) |
340 |
|
|
{ |
341 |
|
|
if (cvs_server_active == 0) |
342 |
|
|
cvs_log(LP_TRACE, "cvs_unlink(%s)", path); |
343 |
|
|
|
344 |
|
|
if (cvs_noexec == 1 && disable_fast_checkout != 0) |
345 |
|
|
return (0); |
346 |
|
|
|
347 |
|
|
if (unlink(path) == -1 && errno != ENOENT) { |
348 |
|
|
cvs_log(LP_ERRNO, "%s", path); |
349 |
|
|
return (-1); |
350 |
|
|
} |
351 |
|
|
|
352 |
|
|
return (0); |
353 |
|
|
} |
354 |
|
|
|
355 |
|
|
/* |
356 |
|
|
* cvs_rmdir() |
357 |
|
|
* |
358 |
|
|
* Remove a directory tree from disk. |
359 |
|
|
* Returns 0 on success, or -1 on failure. |
360 |
|
|
*/ |
361 |
|
|
int |
362 |
|
|
cvs_rmdir(const char *path) |
363 |
|
|
{ |
364 |
|
|
int type, ret = -1; |
365 |
|
|
DIR *dirp; |
366 |
|
|
struct dirent *ent; |
367 |
|
|
struct stat st; |
368 |
|
|
char fpath[PATH_MAX]; |
369 |
|
|
|
370 |
|
|
if (cvs_server_active == 0) |
371 |
|
|
cvs_log(LP_TRACE, "cvs_rmdir(%s)", path); |
372 |
|
|
|
373 |
|
|
if (cvs_noexec == 1 && disable_fast_checkout != 0) |
374 |
|
|
return (0); |
375 |
|
|
|
376 |
|
|
if ((dirp = opendir(path)) == NULL) { |
377 |
|
|
cvs_log(LP_ERR, "failed to open '%s'", path); |
378 |
|
|
return (-1); |
379 |
|
|
} |
380 |
|
|
|
381 |
|
|
while ((ent = readdir(dirp)) != NULL) { |
382 |
|
|
if (!strcmp(ent->d_name, ".") || |
383 |
|
|
!strcmp(ent->d_name, "..")) |
384 |
|
|
continue; |
385 |
|
|
|
386 |
|
|
(void)xsnprintf(fpath, sizeof(fpath), "%s/%s", |
387 |
|
|
path, ent->d_name); |
388 |
|
|
|
389 |
|
|
if (ent->d_type == DT_UNKNOWN) { |
390 |
|
|
if (lstat(fpath, &st) == -1) |
391 |
|
|
fatal("'%s': %s", fpath, strerror(errno)); |
392 |
|
|
|
393 |
|
|
switch (st.st_mode & S_IFMT) { |
394 |
|
|
case S_IFDIR: |
395 |
|
|
type = CVS_DIR; |
396 |
|
|
break; |
397 |
|
|
case S_IFREG: |
398 |
|
|
type = CVS_FILE; |
399 |
|
|
break; |
400 |
|
|
default: |
401 |
|
|
fatal("'%s': Unknown file type in copy", |
402 |
|
|
fpath); |
403 |
|
|
} |
404 |
|
|
} else { |
405 |
|
|
switch (ent->d_type) { |
406 |
|
|
case DT_DIR: |
407 |
|
|
type = CVS_DIR; |
408 |
|
|
break; |
409 |
|
|
case DT_REG: |
410 |
|
|
type = CVS_FILE; |
411 |
|
|
break; |
412 |
|
|
default: |
413 |
|
|
fatal("'%s': Unknown file type in copy", |
414 |
|
|
fpath); |
415 |
|
|
} |
416 |
|
|
} |
417 |
|
|
switch (type) { |
418 |
|
|
case CVS_DIR: |
419 |
|
|
if (cvs_rmdir(fpath) == -1) |
420 |
|
|
goto done; |
421 |
|
|
break; |
422 |
|
|
case CVS_FILE: |
423 |
|
|
if (cvs_unlink(fpath) == -1 && errno != ENOENT) |
424 |
|
|
goto done; |
425 |
|
|
break; |
426 |
|
|
default: |
427 |
|
|
fatal("type %d unknown, shouldn't happen", type); |
428 |
|
|
} |
429 |
|
|
} |
430 |
|
|
|
431 |
|
|
|
432 |
|
|
if (rmdir(path) == -1 && errno != ENOENT) { |
433 |
|
|
cvs_log(LP_ERRNO, "%s", path); |
434 |
|
|
goto done; |
435 |
|
|
} |
436 |
|
|
|
437 |
|
|
ret = 0; |
438 |
|
|
done: |
439 |
|
|
closedir(dirp); |
440 |
|
|
return (ret); |
441 |
|
|
} |
442 |
|
|
|
443 |
|
|
void |
444 |
|
|
cvs_get_repository_path(const char *dir, char *dst, size_t len) |
445 |
|
|
{ |
446 |
|
|
char buf[PATH_MAX]; |
447 |
|
|
|
448 |
|
|
cvs_get_repository_name(dir, buf, sizeof(buf)); |
449 |
|
|
(void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf); |
450 |
|
|
cvs_validate_directory(dst); |
451 |
|
|
} |
452 |
|
|
|
453 |
|
|
void |
454 |
|
|
cvs_get_repository_name(const char *dir, char *dst, size_t len) |
455 |
|
|
{ |
456 |
|
|
FILE *fp; |
457 |
|
|
char fpath[PATH_MAX]; |
458 |
|
|
|
459 |
|
|
dst[0] = '\0'; |
460 |
|
|
|
461 |
|
|
if (!(cmdp->cmd_flags & CVS_USE_WDIR)) { |
462 |
|
|
if (strlcpy(dst, dir, len) >= len) |
463 |
|
|
fatal("cvs_get_repository_name: truncation"); |
464 |
|
|
return; |
465 |
|
|
} |
466 |
|
|
|
467 |
|
|
switch (cvs_cmdop) { |
468 |
|
|
case CVS_OP_EXPORT: |
469 |
|
|
if (strcmp(dir, ".")) |
470 |
|
|
if (strlcpy(dst, dir, len) >= len) |
471 |
|
|
fatal("cvs_get_repository_name: truncation"); |
472 |
|
|
break; |
473 |
|
|
case CVS_OP_IMPORT: |
474 |
|
|
if (strlcpy(dst, import_repository, len) >= len) |
475 |
|
|
fatal("cvs_get_repository_name: truncation"); |
476 |
|
|
if (strlcat(dst, "/", len) >= len) |
477 |
|
|
fatal("cvs_get_repository_name: truncation"); |
478 |
|
|
|
479 |
|
|
if (strcmp(dir, ".")) |
480 |
|
|
if (strlcat(dst, dir, len) >= len) |
481 |
|
|
fatal("cvs_get_repository_name: truncation"); |
482 |
|
|
break; |
483 |
|
|
default: |
484 |
|
|
(void)xsnprintf(fpath, sizeof(fpath), "%s/%s", |
485 |
|
|
dir, CVS_PATH_REPOSITORY); |
486 |
|
|
if ((fp = fopen(fpath, "r")) != NULL) { |
487 |
|
|
if ((fgets(dst, len, fp)) == NULL) |
488 |
|
|
fatal("%s: bad repository file", fpath); |
489 |
|
|
dst[strcspn(dst, "\n")] = '\0'; |
490 |
|
|
(void)fclose(fp); |
491 |
|
|
} else if (cvs_cmdop != CVS_OP_CHECKOUT) |
492 |
|
|
fatal("%s is missing", fpath); |
493 |
|
|
break; |
494 |
|
|
} |
495 |
|
|
} |
496 |
|
|
|
497 |
|
|
void |
498 |
|
|
cvs_mkadmin(const char *path, const char *root, const char *repo, |
499 |
|
|
char *tag, char *date) |
500 |
|
|
{ |
501 |
|
|
FILE *fp; |
502 |
|
|
int fd; |
503 |
|
|
char buf[PATH_MAX]; |
504 |
|
|
struct hash_data *hdata, hd; |
505 |
|
|
|
506 |
|
|
hdata = hash_table_find(&created_cvs_directories, path, strlen(path)); |
507 |
|
|
if (hdata != NULL) |
508 |
|
|
return; |
509 |
|
|
|
510 |
|
|
hd.h_key = xstrdup(path); |
511 |
|
|
hd.h_data = NULL; |
512 |
|
|
hash_table_enter(&created_cvs_directories, &hd); |
513 |
|
|
|
514 |
|
|
if (cvs_server_active == 0) |
515 |
|
|
cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s)", |
516 |
|
|
path, root, repo, (tag != NULL) ? tag : "", |
517 |
|
|
(date != NULL) ? date : ""); |
518 |
|
|
|
519 |
|
|
(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR); |
520 |
|
|
|
521 |
|
|
if (mkdir(buf, 0755) == -1 && errno != EEXIST) |
522 |
|
|
fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); |
523 |
|
|
|
524 |
|
|
if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_ADD || |
525 |
|
|
(cvs_cmdop == CVS_OP_UPDATE && build_dirs == 1)) { |
526 |
|
|
(void)xsnprintf(buf, sizeof(buf), "%s/%s", |
527 |
|
|
path, CVS_PATH_ROOTSPEC); |
528 |
|
|
|
529 |
|
|
if ((fp = fopen(buf, "w")) == NULL) |
530 |
|
|
fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); |
531 |
|
|
|
532 |
|
|
fprintf(fp, "%s\n", root); |
533 |
|
|
(void)fclose(fp); |
534 |
|
|
} |
535 |
|
|
|
536 |
|
|
(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY); |
537 |
|
|
|
538 |
|
|
if ((fp = fopen(buf, "w")) == NULL) |
539 |
|
|
fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); |
540 |
|
|
|
541 |
|
|
fprintf(fp, "%s\n", repo); |
542 |
|
|
(void)fclose(fp); |
543 |
|
|
|
544 |
|
|
cvs_write_tagfile(path, tag, date); |
545 |
|
|
|
546 |
|
|
(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ENTRIES); |
547 |
|
|
|
548 |
|
|
if ((fd = open(buf, O_WRONLY|O_CREAT|O_EXCL, 0666 & ~cvs_umask)) |
549 |
|
|
== -1) { |
550 |
|
|
if (errno == EEXIST) |
551 |
|
|
return; |
552 |
|
|
fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); |
553 |
|
|
} |
554 |
|
|
|
555 |
|
|
if (atomicio(vwrite, fd, "D\n", 2) != 2) |
556 |
|
|
fatal("cvs_mkadmin: %s", strerror(errno)); |
557 |
|
|
close(fd); |
558 |
|
|
} |
559 |
|
|
|
560 |
|
|
void |
561 |
|
|
cvs_mkpath(const char *path, char *tag) |
562 |
|
|
{ |
563 |
|
|
CVSENTRIES *ent; |
564 |
|
|
FILE *fp; |
565 |
|
|
size_t len; |
566 |
|
|
struct hash_data *hdata, hd; |
567 |
|
|
char *entry, *sp, *dp, *dir, *p, rpath[PATH_MAX], repo[PATH_MAX]; |
568 |
|
|
|
569 |
|
|
hdata = hash_table_find(&created_directories, path, strlen(path)); |
570 |
|
|
if (hdata != NULL) |
571 |
|
|
return; |
572 |
|
|
|
573 |
|
|
hd.h_key = xstrdup(path); |
574 |
|
|
hd.h_data = NULL; |
575 |
|
|
hash_table_enter(&created_directories, &hd); |
576 |
|
|
|
577 |
|
|
if (cvsroot_is_remote() || cvs_server_active == 1) |
578 |
|
|
cvs_validate_directory(path); |
579 |
|
|
|
580 |
|
|
dir = xstrdup(path); |
581 |
|
|
|
582 |
|
|
STRIP_SLASH(dir); |
583 |
|
|
|
584 |
|
|
if (cvs_server_active == 0) |
585 |
|
|
cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir); |
586 |
|
|
|
587 |
|
|
repo[0] = '\0'; |
588 |
|
|
rpath[0] = '\0'; |
589 |
|
|
|
590 |
|
|
if ((cvs_cmdop != CVS_OP_CHECKOUT) && (cvs_cmdop != CVS_OP_EXPORT)) { |
591 |
|
|
if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) { |
592 |
|
|
if ((fgets(repo, sizeof(repo), fp)) == NULL) |
593 |
|
|
fatal("cvs_mkpath: bad repository file"); |
594 |
|
|
repo[strcspn(repo, "\n")] = '\0'; |
595 |
|
|
(void)fclose(fp); |
596 |
|
|
} |
597 |
|
|
} |
598 |
|
|
|
599 |
|
|
for (sp = dir; sp != NULL; sp = dp) { |
600 |
|
|
dp = strchr(sp, '/'); |
601 |
|
|
if (dp != NULL) |
602 |
|
|
*(dp++) = '\0'; |
603 |
|
|
|
604 |
|
|
if (sp == dir && module_repo_root != NULL) { |
605 |
|
|
len = strlcpy(repo, module_repo_root, sizeof(repo)); |
606 |
|
|
if (len >= (int)sizeof(repo)) |
607 |
|
|
fatal("cvs_mkpath: overflow"); |
608 |
|
|
} else if (strcmp(sp, ".")) { |
609 |
|
|
if (repo[0] != '\0') { |
610 |
|
|
len = strlcat(repo, "/", sizeof(repo)); |
611 |
|
|
if (len >= (int)sizeof(repo)) |
612 |
|
|
fatal("cvs_mkpath: overflow"); |
613 |
|
|
} |
614 |
|
|
|
615 |
|
|
len = strlcat(repo, sp, sizeof(repo)); |
616 |
|
|
if (len >= (int)sizeof(repo)) |
617 |
|
|
fatal("cvs_mkpath: overflow"); |
618 |
|
|
} |
619 |
|
|
|
620 |
|
|
if (rpath[0] != '\0') { |
621 |
|
|
len = strlcat(rpath, "/", sizeof(rpath)); |
622 |
|
|
if (len >= (int)sizeof(rpath)) |
623 |
|
|
fatal("cvs_mkpath: overflow"); |
624 |
|
|
} |
625 |
|
|
|
626 |
|
|
len = strlcat(rpath, sp, sizeof(rpath)); |
627 |
|
|
if (len >= (int)sizeof(rpath)) |
628 |
|
|
fatal("cvs_mkpath: overflow"); |
629 |
|
|
|
630 |
|
|
if (mkdir(rpath, 0755) == -1 && errno != EEXIST) |
631 |
|
|
fatal("cvs_mkpath: %s: %s", rpath, strerror(errno)); |
632 |
|
|
|
633 |
|
|
if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active) |
634 |
|
|
continue; |
635 |
|
|
|
636 |
|
|
cvs_mkadmin(rpath, current_cvsroot->cr_str, repo, |
637 |
|
|
tag, NULL); |
638 |
|
|
|
639 |
|
|
if (dp != NULL) { |
640 |
|
|
if ((p = strchr(dp, '/')) != NULL) |
641 |
|
|
*p = '\0'; |
642 |
|
|
|
643 |
|
|
entry = xmalloc(CVS_ENT_MAXLINELEN); |
644 |
|
|
cvs_ent_line_str(dp, NULL, NULL, NULL, NULL, 1, 0, |
645 |
|
|
entry, CVS_ENT_MAXLINELEN); |
646 |
|
|
|
647 |
|
|
ent = cvs_ent_open(rpath); |
648 |
|
|
cvs_ent_add(ent, entry); |
649 |
|
|
free(entry); |
650 |
|
|
|
651 |
|
|
if (p != NULL) |
652 |
|
|
*p = '/'; |
653 |
|
|
} |
654 |
|
|
} |
655 |
|
|
|
656 |
|
|
free(dir); |
657 |
|
|
} |
658 |
|
|
|
659 |
|
|
void |
660 |
|
|
cvs_mkdir(const char *path, mode_t mode) |
661 |
|
|
{ |
662 |
|
|
size_t len; |
663 |
|
|
char *sp, *dp, *dir, rpath[PATH_MAX]; |
664 |
|
|
|
665 |
|
|
if (cvsroot_is_remote() || cvs_server_active == 1) |
666 |
|
|
cvs_validate_directory(path); |
667 |
|
|
|
668 |
|
|
dir = xstrdup(path); |
669 |
|
|
|
670 |
|
|
STRIP_SLASH(dir); |
671 |
|
|
|
672 |
|
|
if (cvs_server_active == 0) |
673 |
|
|
cvs_log(LP_TRACE, "cvs_mkdir(%s)", dir); |
674 |
|
|
|
675 |
|
|
rpath[0] = '\0'; |
676 |
|
|
|
677 |
|
|
for (sp = dir; sp != NULL; sp = dp) { |
678 |
|
|
dp = strchr(sp, '/'); |
679 |
|
|
if (dp != NULL) |
680 |
|
|
*(dp++) = '\0'; |
681 |
|
|
|
682 |
|
|
len = strlcat(rpath, "/", sizeof(rpath)); |
683 |
|
|
if (len >= (int)sizeof(rpath)) |
684 |
|
|
fatal("cvs_mkdir: overflow"); |
685 |
|
|
|
686 |
|
|
len = strlcat(rpath, sp, sizeof(rpath)); |
687 |
|
|
if (len >= (int)sizeof(rpath)) |
688 |
|
|
fatal("cvs_mkdir: overflow"); |
689 |
|
|
if (1 == len) |
690 |
|
|
continue; |
691 |
|
|
|
692 |
|
|
if (mkdir(rpath, mode) == -1 && errno != EEXIST) |
693 |
|
|
fatal("cvs_mkdir: %s: %s", rpath, strerror(errno)); |
694 |
|
|
} |
695 |
|
|
|
696 |
|
|
free(dir); |
697 |
|
|
} |
698 |
|
|
|
699 |
|
|
/* |
700 |
|
|
* Split the contents of a file into a list of lines. |
701 |
|
|
*/ |
702 |
|
|
struct rcs_lines * |
703 |
|
|
cvs_splitlines(u_char *data, size_t len) |
704 |
|
|
{ |
705 |
|
|
u_char *p, *c; |
706 |
|
|
size_t i, tlen; |
707 |
|
|
struct rcs_lines *lines; |
708 |
|
|
struct rcs_line *lp; |
709 |
|
|
|
710 |
|
|
lines = xcalloc(1, sizeof(*lines)); |
711 |
|
|
TAILQ_INIT(&(lines->l_lines)); |
712 |
|
|
|
713 |
|
|
lp = xcalloc(1, sizeof(*lp)); |
714 |
|
|
TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list); |
715 |
|
|
|
716 |
|
|
p = c = data; |
717 |
|
|
for (i = 0; i < len; i++) { |
718 |
|
|
if (*p == '\n' || (i == len - 1)) { |
719 |
|
|
tlen = p - c + 1; |
720 |
|
|
lp = xcalloc(1, sizeof(*lp)); |
721 |
|
|
lp->l_line = c; |
722 |
|
|
lp->l_len = tlen; |
723 |
|
|
lp->l_lineno = ++(lines->l_nblines); |
724 |
|
|
TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list); |
725 |
|
|
c = p + 1; |
726 |
|
|
} |
727 |
|
|
p++; |
728 |
|
|
} |
729 |
|
|
|
730 |
|
|
return (lines); |
731 |
|
|
} |
732 |
|
|
|
733 |
|
|
void |
734 |
|
|
cvs_freelines(struct rcs_lines *lines) |
735 |
|
|
{ |
736 |
|
|
struct rcs_line *lp; |
737 |
|
|
|
738 |
|
|
while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) { |
739 |
|
|
TAILQ_REMOVE(&(lines->l_lines), lp, l_list); |
740 |
|
|
if (lp->l_needsfree == 1) |
741 |
|
|
free(lp->l_line); |
742 |
|
|
free(lp); |
743 |
|
|
} |
744 |
|
|
|
745 |
|
|
free(lines); |
746 |
|
|
} |
747 |
|
|
|
748 |
|
|
/* |
749 |
|
|
* cvs_strsplit() |
750 |
|
|
* |
751 |
|
|
* Split a string <str> of <sep>-separated values and allocate |
752 |
|
|
* an argument vector for the values found. |
753 |
|
|
*/ |
754 |
|
|
struct cvs_argvector * |
755 |
|
|
cvs_strsplit(char *str, const char *sep) |
756 |
|
|
{ |
757 |
|
|
struct cvs_argvector *av; |
758 |
|
|
size_t i = 0; |
759 |
|
|
char *cp, *p; |
760 |
|
|
|
761 |
|
|
cp = xstrdup(str); |
762 |
|
|
av = xmalloc(sizeof(*av)); |
763 |
|
|
av->str = cp; |
764 |
|
|
av->argv = xmalloc(sizeof(*(av->argv))); |
765 |
|
|
|
766 |
|
|
while ((p = strsep(&cp, sep)) != NULL) { |
767 |
|
|
av->argv[i++] = p; |
768 |
|
|
av->argv = xreallocarray(av->argv, |
769 |
|
|
i + 1, sizeof(*(av->argv))); |
770 |
|
|
} |
771 |
|
|
av->argv[i] = NULL; |
772 |
|
|
|
773 |
|
|
return (av); |
774 |
|
|
} |
775 |
|
|
|
776 |
|
|
/* |
777 |
|
|
* cvs_argv_destroy() |
778 |
|
|
* |
779 |
|
|
* Free an argument vector previously allocated by cvs_strsplit(). |
780 |
|
|
*/ |
781 |
|
|
void |
782 |
|
|
cvs_argv_destroy(struct cvs_argvector *av) |
783 |
|
|
{ |
784 |
|
|
free(av->str); |
785 |
|
|
free(av->argv); |
786 |
|
|
free(av); |
787 |
|
|
} |
788 |
|
|
|
789 |
|
|
u_int |
790 |
|
|
cvs_revision_select(RCSFILE *file, char *range) |
791 |
|
|
{ |
792 |
|
|
int i; |
793 |
|
|
u_int nrev; |
794 |
|
|
char *lstr, *rstr; |
795 |
|
|
struct rcs_delta *rdp; |
796 |
|
|
struct cvs_argvector *revargv, *revrange; |
797 |
|
|
RCSNUM *lnum, *rnum; |
798 |
|
|
|
799 |
|
|
nrev = 0; |
800 |
|
|
lnum = rnum = NULL; |
801 |
|
|
|
802 |
|
|
revargv = cvs_strsplit(range, ","); |
803 |
|
|
for (i = 0; revargv->argv[i] != NULL; i++) { |
804 |
|
|
revrange = cvs_strsplit(revargv->argv[i], ":"); |
805 |
|
|
if (revrange->argv[0] == NULL) |
806 |
|
|
fatal("invalid revision range: %s", revargv->argv[i]); |
807 |
|
|
else if (revrange->argv[1] == NULL) |
808 |
|
|
lstr = rstr = revrange->argv[0]; |
809 |
|
|
else { |
810 |
|
|
if (revrange->argv[2] != NULL) |
811 |
|
|
fatal("invalid revision range: %s", |
812 |
|
|
revargv->argv[i]); |
813 |
|
|
|
814 |
|
|
lstr = revrange->argv[0]; |
815 |
|
|
rstr = revrange->argv[1]; |
816 |
|
|
|
817 |
|
|
if (strcmp(lstr, "") == 0) |
818 |
|
|
lstr = NULL; |
819 |
|
|
if (strcmp(rstr, "") == 0) |
820 |
|
|
rstr = NULL; |
821 |
|
|
} |
822 |
|
|
|
823 |
|
|
if (lstr == NULL) |
824 |
|
|
lstr = RCS_HEAD_INIT; |
825 |
|
|
|
826 |
|
|
if ((lnum = rcs_translate_tag(lstr, file)) == NULL) |
827 |
|
|
fatal("cvs_revision_select: could not translate tag `%s'", lstr); |
828 |
|
|
|
829 |
|
|
if (rstr != NULL) { |
830 |
|
|
if ((rnum = rcs_translate_tag(rstr, file)) == NULL) |
831 |
|
|
fatal("cvs_revision_select: could not translate tag `%s'", rstr); |
832 |
|
|
} else { |
833 |
|
|
rnum = rcsnum_alloc(); |
834 |
|
|
rcsnum_cpy(file->rf_head, rnum, 0); |
835 |
|
|
} |
836 |
|
|
|
837 |
|
|
cvs_argv_destroy(revrange); |
838 |
|
|
|
839 |
|
|
TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) { |
840 |
|
|
if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 && |
841 |
|
|
rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 && |
842 |
|
|
!(rdp->rd_flags & RCS_RD_SELECT)) { |
843 |
|
|
rdp->rd_flags |= RCS_RD_SELECT; |
844 |
|
|
nrev++; |
845 |
|
|
} |
846 |
|
|
} |
847 |
|
|
|
848 |
|
|
free(lnum); |
849 |
|
|
free(rnum); |
850 |
|
|
} |
851 |
|
|
|
852 |
|
|
cvs_argv_destroy(revargv); |
853 |
|
|
|
854 |
|
|
return (nrev); |
855 |
|
|
} |
856 |
|
|
|
857 |
|
|
int |
858 |
|
|
cvs_yesno(void) |
859 |
|
|
{ |
860 |
|
|
int c, ret; |
861 |
|
|
|
862 |
|
|
ret = 0; |
863 |
|
|
|
864 |
|
|
fflush(stderr); |
865 |
|
|
fflush(stdout); |
866 |
|
|
|
867 |
|
|
if ((c = getchar()) != 'y' && c != 'Y') |
868 |
|
|
ret = -1; |
869 |
|
|
else |
870 |
|
|
while (c != EOF && c != '\n') |
871 |
|
|
c = getchar(); |
872 |
|
|
|
873 |
|
|
return (ret); |
874 |
|
|
} |
875 |
|
|
|
876 |
|
|
/* |
877 |
|
|
* cvs_exec() |
878 |
|
|
* |
879 |
|
|
* Execute <prog> and send <in> to the STDIN if not NULL. |
880 |
|
|
* If <needwait> == 1, return the result of <prog>, |
881 |
|
|
* else, 0 or -1 if an error occur. |
882 |
|
|
*/ |
883 |
|
|
int |
884 |
|
|
cvs_exec(char *prog, char *in, int needwait) |
885 |
|
|
{ |
886 |
|
|
pid_t pid; |
887 |
|
|
size_t size; |
888 |
|
|
int fds[2], st; |
889 |
|
|
char *argp[4] = { "sh", "-c", prog, NULL }; |
890 |
|
|
|
891 |
|
|
if (in != NULL && pipe(fds) < 0) { |
892 |
|
|
cvs_log(LP_ERR, "cvs_exec: pipe failed"); |
893 |
|
|
return (-1); |
894 |
|
|
} |
895 |
|
|
|
896 |
|
|
if ((pid = fork()) == -1) { |
897 |
|
|
cvs_log(LP_ERR, "cvs_exec: fork failed"); |
898 |
|
|
return (-1); |
899 |
|
|
} else if (pid == 0) { |
900 |
|
|
if (in != NULL) { |
901 |
|
|
close(fds[1]); |
902 |
|
|
dup2(fds[0], STDIN_FILENO); |
903 |
|
|
} |
904 |
|
|
|
905 |
|
|
setenv("CVSROOT", current_cvsroot->cr_dir, 1); |
906 |
|
|
execv(_PATH_BSHELL, argp); |
907 |
|
|
cvs_log(LP_ERR, "cvs_exec: failed to run '%s'", prog); |
908 |
|
|
_exit(127); |
909 |
|
|
} |
910 |
|
|
|
911 |
|
|
if (in != NULL) { |
912 |
|
|
close(fds[0]); |
913 |
|
|
size = strlen(in); |
914 |
|
|
if (atomicio(vwrite, fds[1], in, size) != size) |
915 |
|
|
cvs_log(LP_ERR, "cvs_exec: failed to write on STDIN"); |
916 |
|
|
close(fds[1]); |
917 |
|
|
} |
918 |
|
|
|
919 |
|
|
if (needwait == 1) { |
920 |
|
|
while (waitpid(pid, &st, 0) == -1) |
921 |
|
|
; |
922 |
|
|
if (!WIFEXITED(st)) { |
923 |
|
|
errno = EINTR; |
924 |
|
|
return (-1); |
925 |
|
|
} |
926 |
|
|
return (WEXITSTATUS(st)); |
927 |
|
|
} |
928 |
|
|
|
929 |
|
|
return (0); |
930 |
|
|
} |