Annotation of src/usr.bin/cvs/util.c, Revision 1.15
1.15 ! deraadt 1: /* $OpenBSD: util.c,v 1.14 2004/12/03 20:24:37 weingart Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.9 jfb 4: * All rights reserved.
1.1 jfb 5: *
1.9 jfb 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.9 jfb 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.9 jfb 13: * derived from this software without specific prior written permission.
1.1 jfb 14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.9 jfb 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
27: #include <sys/types.h>
28: #include <sys/stat.h>
1.12 krapht 29: #include <sys/wait.h>
1.1 jfb 30:
31: #include <md5.h>
32: #include <errno.h>
1.2 jfb 33: #include <fcntl.h>
1.1 jfb 34: #include <stdio.h>
35: #include <stdlib.h>
36: #include <unistd.h>
37: #include <string.h>
38:
39: #include "cvs.h"
40: #include "log.h"
1.5 jfb 41: #include "file.h"
1.1 jfb 42:
1.9 jfb 43: static const char *cvs_months[] = {
44: "Jan", "Feb", "Mar", "Apr", "May", "Jun",
45: "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
46: };
47:
1.1 jfb 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:
1.11 krapht 85: pid_t cvs_exec_pid;
86:
1.1 jfb 87:
88: /*
89: * cvs_readrepo()
90: *
91: * Read the path stored in the `Repository' CVS file for a given directory
92: * <dir>, and store that path into the buffer pointed to by <dst>, whose size
93: * is <len>.
94: */
95:
96: int
97: cvs_readrepo(const char *dir, char *dst, size_t len)
98: {
99: size_t dlen;
100: FILE *fp;
101: char repo_path[MAXPATHLEN];
102:
103: snprintf(repo_path, sizeof(repo_path), "%s/CVS/Repository", dir);
104: fp = fopen(repo_path, "r");
105: if (fp == NULL) {
106: return (-1);
107: }
108:
109: if (fgets(dst, (int)len, fp) == NULL) {
110: if (ferror(fp)) {
111: cvs_log(LP_ERRNO, "failed to read from `%s'",
112: repo_path);
113: }
114: (void)fclose(fp);
115: return (-1);
116: }
117: dlen = strlen(dst);
118: if ((dlen > 0) && (dst[dlen - 1] == '\n'))
119: dst[--dlen] = '\0';
120:
121: (void)fclose(fp);
122: return (0);
123: }
124:
125:
126: /*
1.9 jfb 127: * cvs_datesec()
128: *
1.10 jfb 129: * Take a date string and transform it into the number of seconds since the
130: * Epoch. The <type> argument specifies whether the timestamp is in ctime(3)
131: * format or RFC 822 format (as CVS uses in its protocol). If the <adj>
132: * parameter is not 0, the returned time will be adjusted according to the
133: * machine's local timezone.
1.9 jfb 134: */
135:
136: time_t
1.10 jfb 137: cvs_datesec(const char *date, int type, int adj)
1.9 jfb 138: {
139: int i;
140: long off;
141: char sign, mon[8], gmt[8], hr[4], min[4], *ep;
142: struct tm cvs_tm;
143:
144: memset(&cvs_tm, 0, sizeof(cvs_tm));
145: cvs_tm.tm_isdst = -1;
146:
1.10 jfb 147: if (type == CVS_DATE_RFC822) {
148: if (sscanf(date, "%d %3s %d %2d:%2d:%2d %5s", &cvs_tm.tm_mday,
149: mon, &cvs_tm.tm_year, &cvs_tm.tm_hour, &cvs_tm.tm_min,
150: &cvs_tm.tm_sec, gmt) < 7)
151: return (-1);
152: cvs_tm.tm_year -= 1900;
1.9 jfb 153:
1.10 jfb 154: if (*gmt == '-') {
155: sscanf(gmt, "%c%2s%2s", &sign, hr, min);
156: cvs_tm.tm_gmtoff = strtol(hr, &ep, 10);
1.9 jfb 157: if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
158: (cvs_tm.tm_gmtoff == LONG_MAX) ||
159: (*ep != '\0')) {
160: cvs_log(LP_ERR,
1.10 jfb 161: "parse error in GMT hours specification `%s'", hr);
162: cvs_tm.tm_gmtoff = 0;
1.15 ! deraadt 163: } else {
1.10 jfb 164: /* get seconds */
165: cvs_tm.tm_gmtoff *= 3600;
166:
167: /* add the minutes */
168: off = strtol(min, &ep, 10);
169: if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
170: (cvs_tm.tm_gmtoff == LONG_MAX) ||
171: (*ep != '\0')) {
172: cvs_log(LP_ERR,
173: "parse error in GMT minutes "
174: "specification `%s'", min);
1.15 ! deraadt 175: } else
1.10 jfb 176: cvs_tm.tm_gmtoff += off * 60;
1.9 jfb 177: }
178: }
1.10 jfb 179: if (sign == '-')
180: cvs_tm.tm_gmtoff = -cvs_tm.tm_gmtoff;
1.15 ! deraadt 181: } else if (type == CVS_DATE_CTIME) {
1.10 jfb 182: /* gmt is used for the weekday */
183: sscanf(date, "%3s %3s %d %2d:%2d:%2d %d", gmt, mon,
184: &cvs_tm.tm_mday, &cvs_tm.tm_hour, &cvs_tm.tm_min,
185: &cvs_tm.tm_sec, &cvs_tm.tm_year);
186: cvs_tm.tm_year -= 1900;
187: cvs_tm.tm_gmtoff = 0;
1.9 jfb 188: }
189:
190: for (i = 0; i < (int)(sizeof(cvs_months)/sizeof(cvs_months[0])); i++) {
191: if (strcmp(cvs_months[i], mon) == 0) {
192: cvs_tm.tm_mon = i;
193: break;
194: }
195: }
196:
197: return mktime(&cvs_tm);
198: }
199:
200:
201: /*
1.1 jfb 202: * cvs_strtomode()
203: *
204: * Read the contents of the string <str> and generate a permission mode from
205: * the contents of <str>, which is assumed to have the mode format of CVS.
206: * The CVS protocol specification states that any modes or mode types that are
207: * not recognized should be silently ignored. This function does not return
208: * an error in such cases, but will issue warnings.
209: * Returns 0 on success, or -1 on failure.
210: */
211:
212: int
213: cvs_strtomode(const char *str, mode_t *mode)
214: {
1.2 jfb 215: char type;
1.1 jfb 216: mode_t m;
217: char buf[32], ms[4], *sp, *ep;
218:
219: m = 0;
220: strlcpy(buf, str, sizeof(buf));
221: sp = buf;
222: ep = sp;
223:
224: for (sp = buf; ep != NULL; sp = ep + 1) {
225: ep = strchr(sp, ',');
226: if (ep != NULL)
1.2 jfb 227: *ep = '\0';
1.1 jfb 228:
1.14 weingart 229: memset(ms, 0, sizeof ms);
230: if (sscanf(sp, "%c=%3s", &type, ms) != 2 &&
231: sscanf(sp, "%c=", &type) != 1) {
1.1 jfb 232: cvs_log(LP_WARN, "failed to scan mode string `%s'", sp);
233: continue;
234: }
235:
236: if ((type <= 'a') || (type >= 'z') ||
237: (cvs_modetypes[type - 'a'] == -1)) {
238: cvs_log(LP_WARN,
239: "invalid mode type `%c'"
240: " (`u', `g' or `o' expected), ignoring", type);
241: continue;
242: }
243:
244: /* make type contain the actual mode index */
245: type = cvs_modetypes[type - 'a'];
246:
247: for (sp = ms; *sp != '\0'; sp++) {
248: if ((*sp <= 'a') || (*sp >= 'z') ||
1.5 jfb 249: (cvs_modes[(int)type][*sp - 'a'] == 0)) {
1.1 jfb 250: cvs_log(LP_WARN,
251: "invalid permission bit `%c'", *sp);
1.15 ! deraadt 252: } else
1.5 jfb 253: m |= cvs_modes[(int)type][*sp - 'a'];
1.1 jfb 254: }
255: }
256:
257: *mode = m;
258:
259: return (0);
260: }
261:
262:
263: /*
264: * cvs_modetostr()
265: *
266: * Returns 0 on success, or -1 on failure.
267: */
268:
269: int
270: cvs_modetostr(mode_t mode, char *buf, size_t len)
271: {
272: size_t l;
273: char tmp[16], *bp;
274: mode_t um, gm, om;
275:
276: um = (mode & S_IRWXU) >> 6;
277: gm = (mode & S_IRWXG) >> 3;
278: om = mode & S_IRWXO;
279:
280: bp = buf;
281: *bp = '\0';
282: l = 0;
283:
284: if (um) {
285: snprintf(tmp, sizeof(tmp), "u=%s", cvs_modestr[um]);
1.9 jfb 286: l = strlcat(buf, tmp, len);
1.1 jfb 287: }
288: if (gm) {
289: if (um)
290: strlcat(buf, ",", len);
291: snprintf(tmp, sizeof(tmp), "g=%s", cvs_modestr[gm]);
1.9 jfb 292: strlcat(buf, tmp, len);
1.1 jfb 293: }
294: if (om) {
295: if (um || gm)
296: strlcat(buf, ",", len);
297: snprintf(tmp, sizeof(tmp), "o=%s", cvs_modestr[gm]);
1.9 jfb 298: strlcat(buf, tmp, len);
1.1 jfb 299: }
300:
301: return (0);
302: }
303:
304:
305: /*
306: * cvs_cksum()
307: *
308: * Calculate the MD5 checksum of the file whose path is <file> and generate
309: * a CVS-format 32 hex-digit string, which is stored in <dst>, whose size is
310: * given in <len> and must be at least 33.
311: * Returns 0 on success, or -1 on failure.
312: */
313:
314: int
315: cvs_cksum(const char *file, char *dst, size_t len)
316: {
317: if (len < CVS_CKSUM_LEN) {
318: cvs_log(LP_WARN, "buffer too small for checksum");
319: return (-1);
320: }
321: if (MD5File(file, dst) == NULL) {
322: cvs_log(LP_ERRNO, "failed to generate file checksum");
323: return (-1);
324: }
325:
326: return (0);
327: }
328:
329:
330: /*
331: * cvs_splitpath()
332: *
1.7 jfb 333: * Split a path <path> into the base portion and the filename portion.
334: * The path is copied in <base> and the last delimiter is replaced by a NUL
335: * byte. The <file> pointer is set to point to the first character after
336: * that delimiter.
1.1 jfb 337: * Returns 0 on success, or -1 on failure.
338: */
339:
340: int
1.7 jfb 341: cvs_splitpath(const char *path, char *base, size_t blen, char **file)
1.1 jfb 342: {
343: size_t rlen;
1.7 jfb 344: char *sp;
1.1 jfb 345:
1.7 jfb 346: if ((rlen = strlcpy(base, path, blen)) >= blen)
347: return (-1);
1.6 jfb 348:
1.7 jfb 349: while ((rlen > 0) && (base[rlen - 1] == '/'))
350: base[--rlen] = '\0';
351:
352: sp = strrchr(base, '/');
1.1 jfb 353: if (sp == NULL) {
1.7 jfb 354: strlcpy(base, "./", blen);
355: strlcat(base, path, blen);
356: sp = base + 1;
1.1 jfb 357: }
358:
1.7 jfb 359: *sp = '\0';
360: if (file != NULL)
361: *file = sp + 1;
1.1 jfb 362:
363: return (0);
364: }
365:
366:
367: /*
368: * cvs_getargv()
369: *
370: * Parse a line contained in <line> and generate an argument vector by
371: * splitting the line on spaces and tabs. The resulting vector is stored in
372: * <argv>, which can accept up to <argvlen> entries.
373: * Returns the number of arguments in the vector, or -1 if an error occured.
374: */
375:
376: int
377: cvs_getargv(const char *line, char **argv, int argvlen)
378: {
379: u_int i;
380: int argc, err;
381: char linebuf[256], qbuf[128], *lp, *cp, *arg;
382:
383: strlcpy(linebuf, line, sizeof(linebuf));
384: memset(argv, 0, sizeof(argv));
385: argc = 0;
386:
387: /* build the argument vector */
388: err = 0;
389: for (lp = linebuf; lp != NULL;) {
390: if (*lp == '"') {
391: /* double-quoted string */
392: lp++;
393: i = 0;
394: memset(qbuf, 0, sizeof(qbuf));
395: while (*lp != '"') {
396: if (*lp == '\0') {
397: cvs_log(LP_ERR, "no terminating quote");
398: err++;
399: break;
1.15 ! deraadt 400: } else if (*lp == '\\')
1.1 jfb 401: lp++;
402:
1.9 jfb 403: qbuf[i++] = *lp++;
1.1 jfb 404: if (i == sizeof(qbuf)) {
405: err++;
406: break;
407: }
408: }
409:
410: arg = qbuf;
1.15 ! deraadt 411: } else {
1.1 jfb 412: cp = strsep(&lp, " \t");
413: if (cp == NULL)
414: break;
415: else if (*cp == '\0')
416: continue;
417:
418: arg = cp;
419: }
420:
421: argv[argc] = strdup(arg);
422: if (argv[argc] == NULL) {
423: cvs_log(LP_ERRNO, "failed to copy argument");
424: err++;
425: break;
426: }
427: argc++;
428: }
429:
430: if (err) {
431: /* ditch the argument vector */
432: for (i = 0; i < (u_int)argc; i++)
433: free(argv[i]);
434: argc = -1;
435: }
436:
437: return (argc);
438: }
439:
440:
441: /*
442: * cvs_freeargv()
443: *
444: * Free an argument vector previously generated by cvs_getargv().
445: */
446:
447: void
448: cvs_freeargv(char **argv, int argc)
449: {
450: int i;
451:
452: for (i = 0; i < argc; i++)
453: free(argv[i]);
1.2 jfb 454: }
455:
456:
457: /*
458: * cvs_mkadmin()
459: *
1.5 jfb 460: * Create the CVS administrative files within the directory <cdir>. If the
461: * files already exist, they are kept as is.
1.2 jfb 462: * Returns 0 on success, or -1 on failure.
463: */
464:
465: int
1.13 jfb 466: cvs_mkadmin(CVSFILE *cdir, mode_t mode)
1.2 jfb 467: {
1.13 jfb 468: char dpath[MAXPATHLEN], path[MAXPATHLEN];
1.2 jfb 469: FILE *fp;
470: CVSENTRIES *ef;
1.5 jfb 471: struct stat st;
1.2 jfb 472: struct cvsroot *root;
473:
1.13 jfb 474: cvs_file_getpath(cdir, dpath, sizeof(dpath));
475:
476: snprintf(path, sizeof(path), "%s/" CVS_PATH_CVSDIR, dpath);
1.5 jfb 477: if ((mkdir(path, mode) == -1) && (errno != EEXIST)) {
1.2 jfb 478: cvs_log(LP_ERRNO, "failed to create directory %s", path);
479: return (-1);
480: }
481:
1.5 jfb 482: /* just create an empty Entries file */
1.13 jfb 483: ef = cvs_ent_open(dpath, O_WRONLY);
1.2 jfb 484: (void)cvs_ent_close(ef);
485:
1.5 jfb 486: root = cdir->cf_ddat->cd_root;
1.13 jfb 487: snprintf(path, sizeof(path), "%s/" CVS_PATH_ROOTSPEC, dpath);
1.8 jfb 488: if ((root != NULL) && (stat(path, &st) != 0) && (errno == ENOENT)) {
1.5 jfb 489: fp = fopen(path, "w");
490: if (fp == NULL) {
491: cvs_log(LP_ERRNO, "failed to open %s", path);
492: return (-1);
493: }
494: if (root->cr_user != NULL) {
495: fprintf(fp, "%s", root->cr_user);
496: if (root->cr_pass != NULL)
497: fprintf(fp, ":%s", root->cr_pass);
498: if (root->cr_host != NULL)
499: putc('@', fp);
500: }
501:
502: if (root->cr_host != NULL) {
503: fprintf(fp, "%s", root->cr_host);
504: if (root->cr_dir != NULL)
505: putc(':', fp);
506: }
507: if (root->cr_dir)
508: fprintf(fp, "%s", root->cr_dir);
509: putc('\n', fp);
510: (void)fclose(fp);
1.2 jfb 511: }
512:
1.13 jfb 513: snprintf(path, sizeof(path), "%s/" CVS_PATH_REPOSITORY, dpath);
1.5 jfb 514: if ((stat(path, &st) != 0) && (errno == ENOENT) &&
515: (cdir->cf_ddat->cd_repo != NULL)) {
1.2 jfb 516: fp = fopen(path, "w");
517: if (fp == NULL) {
518: cvs_log(LP_ERRNO, "failed to open %s", path);
519: return (-1);
520: }
1.3 jfb 521: fprintf(fp, "%s\n", cdir->cf_ddat->cd_repo);
1.2 jfb 522: (void)fclose(fp);
523: }
524:
525: return (0);
1.11 krapht 526: }
527:
528:
529: /*
530: * cvs_exec()
531: */
532:
533: int
534: cvs_exec(int argc, char **argv, int fds[3])
535: {
536: int ret;
537: pid_t pid;
538:
539: if ((pid = fork()) == -1) {
540: cvs_log(LP_ERRNO, "failed to fork");
541: return (-1);
542: } else if (pid == 0) {
543: execvp(argv[0], argv);
1.13 jfb 544: cvs_log(LP_ERRNO, "failed to exec %s", argv[0]);
545: exit(1);
1.11 krapht 546: }
547:
548: if (waitpid(pid, &ret, 0) == -1)
1.13 jfb 549: cvs_log(LP_ERRNO, "failed to waitpid");
1.11 krapht 550:
551: return (ret);
1.1 jfb 552: }