Annotation of src/usr.bin/cvs/util.c, Revision 1.5
1.1 jfb 1: /* $OpenBSD$ */
2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: *
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. The name of the author may not be used to endorse or promote products
13: * derived from this software without specific prior written permission.
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
24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27: #include <sys/types.h>
28: #include <sys/stat.h>
29:
30: #include <md5.h>
31: #include <errno.h>
1.2 jfb 32: #include <fcntl.h>
1.1 jfb 33: #include <stdio.h>
34: #include <stdlib.h>
35: #include <unistd.h>
36: #include <string.h>
37:
38: #include "cvs.h"
39: #include "log.h"
1.5 ! jfb 40: #include "file.h"
1.1 jfb 41:
42:
43: /* letter -> mode type map */
44: static const int cvs_modetypes[26] = {
45: -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
46: -1, 2, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
47: };
48:
49: /* letter -> mode map */
50: static const mode_t cvs_modes[3][26] = {
51: {
52: 0, 0, 0, 0, 0, 0, 0, /* a - g */
53: 0, 0, 0, 0, 0, 0, 0, /* h - m */
54: 0, 0, 0, S_IRUSR, 0, 0, 0, /* n - u */
55: 0, S_IWUSR, S_IXUSR, 0, 0 /* v - z */
56: },
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_IRGRP, 0, 0, 0, /* n - u */
61: 0, S_IWGRP, S_IXGRP, 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_IROTH, 0, 0, 0, /* n - u */
67: 0, S_IWOTH, S_IXOTH, 0, 0 /* v - z */
68: }
69: };
70:
71:
72: /* octal -> string */
73: static const char *cvs_modestr[8] = {
74: "", "x", "w", "wx", "r", "rx", "rw", "rwx"
75: };
76:
77:
78:
79:
80: /*
81: * cvs_readrepo()
82: *
83: * Read the path stored in the `Repository' CVS file for a given directory
84: * <dir>, and store that path into the buffer pointed to by <dst>, whose size
85: * is <len>.
86: */
87:
88: int
89: cvs_readrepo(const char *dir, char *dst, size_t len)
90: {
91: size_t dlen;
92: FILE *fp;
93: char repo_path[MAXPATHLEN];
94:
95: snprintf(repo_path, sizeof(repo_path), "%s/CVS/Repository", dir);
96: fp = fopen(repo_path, "r");
97: if (fp == NULL) {
98: return (-1);
99: }
100:
101: if (fgets(dst, (int)len, fp) == NULL) {
102: if (ferror(fp)) {
103: cvs_log(LP_ERRNO, "failed to read from `%s'",
104: repo_path);
105: }
106: (void)fclose(fp);
107: return (-1);
108: }
109: dlen = strlen(dst);
110: if ((dlen > 0) && (dst[dlen - 1] == '\n'))
111: dst[--dlen] = '\0';
112:
113: (void)fclose(fp);
114: return (0);
115: }
116:
117:
118: /*
119: * cvs_strtomode()
120: *
121: * Read the contents of the string <str> and generate a permission mode from
122: * the contents of <str>, which is assumed to have the mode format of CVS.
123: * The CVS protocol specification states that any modes or mode types that are
124: * not recognized should be silently ignored. This function does not return
125: * an error in such cases, but will issue warnings.
126: * Returns 0 on success, or -1 on failure.
127: */
128:
129: int
130: cvs_strtomode(const char *str, mode_t *mode)
131: {
1.2 jfb 132: char type;
1.1 jfb 133: mode_t m;
134: char buf[32], ms[4], *sp, *ep;
135:
136: m = 0;
137: strlcpy(buf, str, sizeof(buf));
138: sp = buf;
139: ep = sp;
140:
141: for (sp = buf; ep != NULL; sp = ep + 1) {
142: ep = strchr(sp, ',');
143: if (ep != NULL)
1.2 jfb 144: *ep = '\0';
1.1 jfb 145:
1.2 jfb 146: if (sscanf(sp, "%c=%3s", &type, ms) != 2) {
1.1 jfb 147: cvs_log(LP_WARN, "failed to scan mode string `%s'", sp);
148: continue;
149: }
150:
151: if ((type <= 'a') || (type >= 'z') ||
152: (cvs_modetypes[type - 'a'] == -1)) {
153: cvs_log(LP_WARN,
154: "invalid mode type `%c'"
155: " (`u', `g' or `o' expected), ignoring", type);
156: continue;
157: }
158:
159: /* make type contain the actual mode index */
160: type = cvs_modetypes[type - 'a'];
161:
162: for (sp = ms; *sp != '\0'; sp++) {
163: if ((*sp <= 'a') || (*sp >= 'z') ||
1.5 ! jfb 164: (cvs_modes[(int)type][*sp - 'a'] == 0)) {
1.1 jfb 165: cvs_log(LP_WARN,
166: "invalid permission bit `%c'", *sp);
167: }
168: else
1.5 ! jfb 169: m |= cvs_modes[(int)type][*sp - 'a'];
1.1 jfb 170: }
171: }
172:
173: *mode = m;
174:
175: return (0);
176: }
177:
178:
179: /*
180: * cvs_modetostr()
181: *
182: * Returns 0 on success, or -1 on failure.
183: */
184:
185: int
186: cvs_modetostr(mode_t mode, char *buf, size_t len)
187: {
188: size_t l;
189: char tmp[16], *bp;
190: mode_t um, gm, om;
191:
192: um = (mode & S_IRWXU) >> 6;
193: gm = (mode & S_IRWXG) >> 3;
194: om = mode & S_IRWXO;
195:
196: bp = buf;
197: *bp = '\0';
198: l = 0;
199:
200: if (um) {
201: snprintf(tmp, sizeof(tmp), "u=%s", cvs_modestr[um]);
202: l = strlcat(buf, tmp, len);
203: }
204: if (gm) {
205: if (um)
206: strlcat(buf, ",", len);
207: snprintf(tmp, sizeof(tmp), "g=%s", cvs_modestr[gm]);
208: strlcat(buf, tmp, len);
209: }
210: if (om) {
211: if (um || gm)
212: strlcat(buf, ",", len);
213: snprintf(tmp, sizeof(tmp), "o=%s", cvs_modestr[gm]);
214: strlcat(buf, tmp, len);
215: }
216:
217: return (0);
218: }
219:
220:
221: /*
222: * cvs_cksum()
223: *
224: * Calculate the MD5 checksum of the file whose path is <file> and generate
225: * a CVS-format 32 hex-digit string, which is stored in <dst>, whose size is
226: * given in <len> and must be at least 33.
227: * Returns 0 on success, or -1 on failure.
228: */
229:
230: int
231: cvs_cksum(const char *file, char *dst, size_t len)
232: {
233: if (len < CVS_CKSUM_LEN) {
234: cvs_log(LP_WARN, "buffer too small for checksum");
235: return (-1);
236: }
237: if (MD5File(file, dst) == NULL) {
238: cvs_log(LP_ERRNO, "failed to generate file checksum");
239: return (-1);
240: }
241:
242: return (0);
243: }
244:
245:
246: /*
247: * cvs_splitpath()
248: *
249: * Split a path <path> into the directory portion and the filename portion
250: * and copy them in <dir> and <file>, whose lengths are <dlen> and <flen>,
251: * unless they are NULL.
252: * Returns 0 on success, or -1 on failure.
253: */
254:
255: int
256: cvs_splitpath(const char *path, char *dir, size_t dlen, char *file, size_t flen)
257: {
258: size_t rlen;
259: const char *sp;
260: struct stat st;
261:
262: sp = strrchr(path, '/');
263: if (sp == NULL) {
264: if (stat(path, &st) == -1)
265: return (-1);
266:
267: if (S_ISDIR(st.st_mode)) {
268: if (dir != NULL)
269: strlcpy(dir, path, dlen);
270: if (file != NULL)
271: file[0] = '\0';
272: }
273: else {
274: if (file != NULL)
275: strlcpy(file, path, flen);
276: if (dir != NULL)
277: strlcpy(dir, ".", dlen);
278: }
279: }
280: else {
1.4 jfb 281: rlen = MIN(dlen - 1, (size_t)(sp - path));
1.1 jfb 282: if (dir != NULL) {
283: strncpy(dir, path, rlen);
284: dir[rlen] = '\0';
285: }
286:
287: sp++;
288: if (file != NULL)
289: strlcpy(file, sp, flen);
290: }
291:
292: return (0);
293: }
294:
295:
296: /*
297: * cvs_getargv()
298: *
299: * Parse a line contained in <line> and generate an argument vector by
300: * splitting the line on spaces and tabs. The resulting vector is stored in
301: * <argv>, which can accept up to <argvlen> entries.
302: * Returns the number of arguments in the vector, or -1 if an error occured.
303: */
304:
305: int
306: cvs_getargv(const char *line, char **argv, int argvlen)
307: {
308: u_int i;
309: int argc, err;
310: char linebuf[256], qbuf[128], *lp, *cp, *arg;
311:
312: strlcpy(linebuf, line, sizeof(linebuf));
313: memset(argv, 0, sizeof(argv));
314: argc = 0;
315:
316: /* build the argument vector */
317: err = 0;
318: for (lp = linebuf; lp != NULL;) {
319: if (*lp == '"') {
320: /* double-quoted string */
321: lp++;
322: i = 0;
323: memset(qbuf, 0, sizeof(qbuf));
324: while (*lp != '"') {
325: if (*lp == '\0') {
326: cvs_log(LP_ERR, "no terminating quote");
327: err++;
328: break;
329: }
330: else if (*lp == '\\')
331: lp++;
332:
333: qbuf[i++] = *lp++;
334: if (i == sizeof(qbuf)) {
335: err++;
336: break;
337: }
338: }
339:
340: arg = qbuf;
341: }
342: else {
343: cp = strsep(&lp, " \t");
344: if (cp == NULL)
345: break;
346: else if (*cp == '\0')
347: continue;
348:
349: arg = cp;
350: }
351:
352: argv[argc] = strdup(arg);
353: if (argv[argc] == NULL) {
354: cvs_log(LP_ERRNO, "failed to copy argument");
355: err++;
356: break;
357: }
358: argc++;
359: }
360:
361: if (err) {
362: /* ditch the argument vector */
363: for (i = 0; i < (u_int)argc; i++)
364: free(argv[i]);
365: argc = -1;
366: }
367:
368: return (argc);
369: }
370:
371:
372: /*
373: * cvs_freeargv()
374: *
375: * Free an argument vector previously generated by cvs_getargv().
376: */
377:
378: void
379: cvs_freeargv(char **argv, int argc)
380: {
381: int i;
382:
383: for (i = 0; i < argc; i++)
384: free(argv[i]);
1.2 jfb 385: }
386:
387:
388: /*
389: * cvs_mkadmin()
390: *
1.5 ! jfb 391: * Create the CVS administrative files within the directory <cdir>. If the
! 392: * files already exist, they are kept as is.
1.2 jfb 393: * Returns 0 on success, or -1 on failure.
394: */
395:
396: int
397: cvs_mkadmin(struct cvs_file *cdir, mode_t mode)
398: {
399: char path[MAXPATHLEN];
400: FILE *fp;
401: CVSENTRIES *ef;
1.5 ! jfb 402: struct stat st;
1.2 jfb 403: struct cvsroot *root;
404:
405: snprintf(path, sizeof(path), "%s/" CVS_PATH_CVSDIR, cdir->cf_path);
1.5 ! jfb 406: if ((mkdir(path, mode) == -1) && (errno != EEXIST)) {
1.2 jfb 407: cvs_log(LP_ERRNO, "failed to create directory %s", path);
408: return (-1);
409: }
410:
1.5 ! jfb 411: /* just create an empty Entries file */
1.2 jfb 412: ef = cvs_ent_open(cdir->cf_path, O_WRONLY);
413: (void)cvs_ent_close(ef);
414:
1.5 ! jfb 415: root = cdir->cf_ddat->cd_root;
1.2 jfb 416: snprintf(path, sizeof(path), "%s/" CVS_PATH_ROOTSPEC, cdir->cf_path);
1.5 ! jfb 417: if ((stat(path, &st) != 0) && (errno == ENOENT) && (root != NULL)) {
! 418: fp = fopen(path, "w");
! 419: if (fp == NULL) {
! 420: cvs_log(LP_ERRNO, "failed to open %s", path);
! 421: return (-1);
! 422: }
! 423: if (root->cr_user != NULL) {
! 424: fprintf(fp, "%s", root->cr_user);
! 425: if (root->cr_pass != NULL)
! 426: fprintf(fp, ":%s", root->cr_pass);
! 427: if (root->cr_host != NULL)
! 428: putc('@', fp);
! 429: }
! 430:
! 431: if (root->cr_host != NULL) {
! 432: fprintf(fp, "%s", root->cr_host);
! 433: if (root->cr_dir != NULL)
! 434: putc(':', fp);
! 435: }
! 436: if (root->cr_dir)
! 437: fprintf(fp, "%s", root->cr_dir);
! 438: putc('\n', fp);
! 439: (void)fclose(fp);
1.2 jfb 440: }
441:
1.5 ! jfb 442: snprintf(path, sizeof(path), "%s/" CVS_PATH_REPOSITORY, cdir->cf_path);
! 443: if ((stat(path, &st) != 0) && (errno == ENOENT) &&
! 444: (cdir->cf_ddat->cd_repo != NULL)) {
1.2 jfb 445: fp = fopen(path, "w");
446: if (fp == NULL) {
447: cvs_log(LP_ERRNO, "failed to open %s", path);
448: return (-1);
449: }
1.3 jfb 450: fprintf(fp, "%s\n", cdir->cf_ddat->cd_repo);
1.2 jfb 451: (void)fclose(fp);
452: }
453:
454: return (0);
1.1 jfb 455: }