xref: /trueos/usr.bin/csup/misc.c (revision bcd0e15cf642d6e5bf78ee585ad282b0e3061864)
1 /*-
2  * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 
32 #include <assert.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <limits.h>
37 #include <pthread.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 
45 #include "fattr.h"
46 #include "main.h"
47 #include "misc.h"
48 
49 struct pattlist {
50 	char **patterns;
51 	size_t size;
52 	size_t in;
53 };
54 
55 struct backoff_timer {
56 	time_t min;
57 	time_t max;
58 	time_t interval;
59 	float backoff;
60 	float jitter;
61 };
62 
63 static void	bt_update(struct backoff_timer *);
64 static void	bt_addjitter(struct backoff_timer *);
65 
66 int
asciitoint(const char * s,int * val,int base)67 asciitoint(const char *s, int *val, int base)
68 {
69 	char *end;
70 	long longval;
71 
72 	errno = 0;
73 	longval = strtol(s, &end, base);
74 	if (errno || *end != '\0')
75 		return (-1);
76 	if (longval > INT_MAX || longval < INT_MIN) {
77 		errno = ERANGE;
78 		return (-1);
79 	}
80 	*val = longval;
81 	return (0);
82 }
83 
84 int
lprintf(int level,const char * fmt,...)85 lprintf(int level, const char *fmt, ...)
86 {
87 	FILE *to;
88 	va_list ap;
89 	int ret;
90 
91 	if (level > verbose)
92 		return (0);
93 	if (level == -1)
94 		to = stderr;
95 	else
96 		to = stdout;
97 	va_start(ap, fmt);
98 	ret = vfprintf(to, fmt, ap);
99 	va_end(ap);
100 	fflush(to);
101 	return (ret);
102 }
103 
104 /*
105  * Compute the MD5 checksum of a file.  The md parameter must
106  * point to a buffer containing at least MD5_DIGEST_SIZE bytes.
107  *
108  * Do not confuse OpenSSL's MD5_DIGEST_LENGTH with our own
109  * MD5_DIGEST_SIZE macro.
110  */
111 int
MD5_File(char * path,char * md)112 MD5_File(char *path, char *md)
113 {
114 	char buf[1024];
115 	MD5_CTX ctx;
116 	ssize_t n;
117 	int fd;
118 
119 	fd = open(path, O_RDONLY);
120 	if (fd == -1)
121 		return (-1);
122 	MD5_Init(&ctx);
123 	while ((n = read(fd, buf, sizeof(buf))) > 0)
124 		MD5_Update(&ctx, buf, n);
125 	close(fd);
126 	if (n == -1)
127 		return (-1);
128 	MD5_End(md, &ctx);
129 	return (0);
130 }
131 
132 /*
133  * Wrapper around MD5_Final() that converts the 128 bits MD5 hash
134  * to an ASCII string representing this value in hexadecimal.
135  */
136 void
MD5_End(char * md,MD5_CTX * c)137 MD5_End(char *md, MD5_CTX *c)
138 {
139 	unsigned char md5[MD5_DIGEST_LENGTH];
140 	const char hex[] = "0123456789abcdef";
141 	int i, j;
142 
143 	MD5_Final(md5, c);
144 	j = 0;
145 	for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
146 		md[j++] = hex[md5[i] >> 4];
147 		md[j++] = hex[md5[i] & 0xf];
148 	}
149 	md[j] = '\0';
150 }
151 
152 int
pathcmp(const char * s1,const char * s2)153 pathcmp(const char *s1, const char *s2)
154 {
155 	char c1, c2;
156 
157 	do {
158 		c1 = *s1++;
159 		if (c1 == '/')
160 			c1 = 1;
161 		c2 = *s2++;
162 		if (c2 == '/')
163 			c2 = 1;
164 	} while (c1 == c2 && c1 != '\0');
165 
166 	return (c1 - c2);
167 }
168 
169 size_t
commonpathlength(const char * a,size_t alen,const char * b,size_t blen)170 commonpathlength(const char *a, size_t alen, const char *b, size_t blen)
171 {
172 	size_t i, minlen, lastslash;
173 
174 	minlen = min(alen, blen);
175 	lastslash = 0;
176 	for (i = 0; i < minlen; i++) {
177 		if (a[i] != b[i])
178 			return (lastslash);
179 		if (a[i] == '/') {
180 			if (i == 0)	/* Include the leading slash. */
181 				lastslash = 1;
182 			else
183 				lastslash = i;
184 		}
185 	}
186 
187 	/* One path is a prefix of the other/ */
188 	if (alen > minlen) {		/* Path "b" is a prefix of "a". */
189 		if (a[minlen] == '/')
190 			return (minlen);
191 		else
192 			return (lastslash);
193 	} else if (blen > minlen) {	/* Path "a" is a prefix of "b". */
194 		if (b[minlen] == '/')
195 			return (minlen);
196 		else
197 			return (lastslash);
198 	}
199 
200 	/* The paths are identical. */
201 	return (minlen);
202 }
203 
204 const char *
pathlast(const char * path)205 pathlast(const char *path)
206 {
207 	const char *s;
208 
209 	s = strrchr(path, '/');
210 	if (s == NULL)
211 		return (path);
212 	return (++s);
213 }
214 
215 int
rcsdatetotm(const char * revdate,struct tm * tm)216 rcsdatetotm(const char *revdate, struct tm *tm)
217 {
218 	char *cp;
219 	size_t len;
220 
221 	cp = strchr(revdate, '.');
222 	if (cp == NULL)
223 		return (-1);
224 	len = cp - revdate;
225 	if (len >= 4)
226 		cp = strptime(revdate, "%Y.%m.%d.%H.%M.%S", tm);
227 	else if (len == 2)
228 		cp = strptime(revdate, "%y.%m.%d.%H.%M.%S", tm);
229 	else
230 		return (-1);
231 	if (cp == NULL || *cp != '\0')
232 		return (-1);
233 	return (0);
234 }
235 
236 time_t
rcsdatetotime(const char * revdate)237 rcsdatetotime(const char *revdate)
238 {
239 	struct tm tm;
240 	time_t t;
241 	int error;
242 
243 	error = rcsdatetotm(revdate, &tm);
244 	if (error)
245 		return (error);
246 	t = timegm(&tm);
247 	return (t);
248 }
249 
250 /*
251  * Checks if a file is an RCS file.
252  */
253 int
isrcs(const char * file,size_t * len)254 isrcs(const char *file, size_t *len)
255 {
256 	const char *cp;
257 
258 	if (file[0] == '/')
259 		return (0);
260 	cp = file;
261 	while ((cp = strstr(cp, "..")) != NULL) {
262 		if (cp == file || cp[2] == '\0' ||
263 		    (cp[-1] == '/' && cp[2] == '/'))
264 			return (0);
265 		cp += 2;
266 	}
267 	*len = strlen(file);
268 	if (*len < 2 || file[*len - 1] != 'v' || file[*len - 2] != ',') {
269 		return (0);
270 	}
271 
272 	return (1);
273 }
274 
275 /*
276  * Returns a buffer allocated with malloc() containing the absolute
277  * pathname to the checkout file made from the prefix and the path
278  * of the corresponding RCS file relatively to the prefix.  If the
279  * filename is not an RCS filename, NULL will be returned.
280  */
281 char *
checkoutpath(const char * prefix,const char * file)282 checkoutpath(const char *prefix, const char *file)
283 {
284 	char *path;
285 	size_t len;
286 
287 	if (!isrcs(file, &len))
288 		return (NULL);
289 	xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file);
290 	return (path);
291 }
292 
293 /*
294  * Returns a cvs path allocated with malloc() containing absolute pathname to a
295  * file in cvs mode which can reside in the attic. XXX: filename has really no
296  * restrictions.
297  */
298 char *
cvspath(const char * prefix,const char * file,int attic)299 cvspath(const char *prefix, const char *file, int attic)
300 {
301 	const char *last;
302 	char *path;
303 
304 	last = pathlast(file);
305 	if (attic)
306 		xasprintf(&path, "%s/%.*sAttic/%s", prefix, (int)(last - file),
307 		    file, last);
308 	else
309 		xasprintf(&path, "%s/%s", prefix, file);
310 
311 	return (path);
312 }
313 
314 /*
315  * Regular or attic path if regular fails.
316  * XXX: This should perhaps also check if the Attic file exists too, and return
317  * NULL if not.
318  */
319 char *
atticpath(const char * prefix,const char * file)320 atticpath(const char *prefix, const char *file)
321 {
322 	char *path;
323 
324 	path = cvspath(prefix, file, 0);
325 	if (access(path, F_OK) != 0) {
326 		free(path);
327 		path = cvspath(prefix, file, 1);
328 	}
329 	return (path);
330 }
331 
332 int
mkdirhier(char * path,mode_t mask)333 mkdirhier(char *path, mode_t mask)
334 {
335 	struct fattr *fa;
336 	size_t i, last, len;
337 	int error, finish, rv;
338 
339 	finish = 0;
340 	last = 0;
341 	len = strlen(path);
342 	for (i = len - 1; i > 0; i--) {
343 		if (path[i] == '/') {
344 			path[i] = '\0';
345 			if (access(path, F_OK) == 0) {
346 				path[i] = '/';
347 				break;
348 			}
349 			if (errno != ENOENT) {
350 				path[i] = '/';
351 				if (last == 0)
352 					return (-1);
353 				finish = 1;
354 				break;
355 			}
356 			last = i;
357 		}
358 	}
359 	if (last == 0)
360 		return (0);
361 
362 	i = strlen(path);
363 	fa = fattr_new(FT_DIRECTORY, -1);
364 	fattr_mergedefault(fa);
365 	fattr_umask(fa, mask);
366 	while (i < len) {
367 		if (!finish) {
368 			rv = 0;
369 			error = fattr_makenode(fa, path);
370 			if (!error)
371 				rv = fattr_install(fa, path, NULL);
372 			if (error || rv == -1)
373 				finish = 1;
374 		}
375 		path[i] = '/';
376 		i += strlen(path + i);
377         }
378 	assert(i == len);
379 	if (finish)
380 		return (-1);
381         return (0);
382 }
383 
384 /*
385  * Compute temporary pathnames.
386  * This can look a bit like overkill but we mimic CVSup's behaviour.
387  */
388 #define	TEMPNAME_PREFIX		"#cvs.csup"
389 
390 static pthread_mutex_t tempname_mtx = PTHREAD_MUTEX_INITIALIZER;
391 static pid_t tempname_pid = -1;
392 static int tempname_count;
393 
394 char *
tempname(const char * path)395 tempname(const char *path)
396 {
397 	char *cp, *temp;
398 	int count, error;
399 
400 	error = pthread_mutex_lock(&tempname_mtx);
401 	assert(!error);
402 	if (tempname_pid == -1) {
403 		tempname_pid = getpid();
404 		tempname_count = 0;
405 	}
406 	count = tempname_count++;
407 	error = pthread_mutex_unlock(&tempname_mtx);
408 	assert(!error);
409 	cp = strrchr(path, '/');
410 	if (cp == NULL)
411 		xasprintf(&temp, "%s-%ld.%d", TEMPNAME_PREFIX,
412 		    (long)tempname_pid, count);
413 	else
414 		xasprintf(&temp, "%.*s%s-%ld.%d", (int)(cp - path + 1), path,
415 		    TEMPNAME_PREFIX, (long)tempname_pid, count);
416 	return (temp);
417 }
418 
419 void *
xmalloc(size_t size)420 xmalloc(size_t size)
421 {
422 	void *buf;
423 
424 	buf = malloc(size);
425 	if (buf == NULL)
426 		err(1, "malloc");
427 	return (buf);
428 }
429 
430 void *
xrealloc(void * buf,size_t size)431 xrealloc(void *buf, size_t size)
432 {
433 
434 	buf = realloc(buf, size);
435 	if (buf == NULL)
436 		err(1, "realloc");
437 	return (buf);
438 }
439 
440 char *
xstrdup(const char * str)441 xstrdup(const char *str)
442 {
443 	char *buf;
444 
445 	buf = strdup(str);
446 	if (buf == NULL)
447 		err(1, "strdup");
448 	return (buf);
449 }
450 
451 int
xasprintf(char ** ret,const char * format,...)452 xasprintf(char **ret, const char *format, ...)
453 {
454 	va_list ap;
455 	int rv;
456 
457 	va_start(ap, format);
458 	rv = vasprintf(ret, format, ap);
459 	va_end(ap);
460 	if (*ret == NULL)
461 		err(1, "asprintf");
462 	return (rv);
463 }
464 
465 struct pattlist *
pattlist_new(void)466 pattlist_new(void)
467 {
468 	struct pattlist *p;
469 
470 	p = xmalloc(sizeof(struct pattlist));
471 	p->size = 4;		/* Initial size. */
472 	p->patterns = xmalloc(p->size * sizeof(char *));
473 	p->in = 0;
474 	return (p);
475 }
476 
477 void
pattlist_add(struct pattlist * p,const char * pattern)478 pattlist_add(struct pattlist *p, const char *pattern)
479 {
480 
481 	if (p->in == p->size) {
482 		p->size *= 2;
483 		p->patterns = xrealloc(p->patterns, p->size * sizeof(char *));
484 	}
485 	assert(p->in < p->size);
486 	p->patterns[p->in++] = xstrdup(pattern);
487 }
488 
489 char *
pattlist_get(struct pattlist * p,size_t i)490 pattlist_get(struct pattlist *p, size_t i)
491 {
492 
493 	assert(i < p->in);
494 	return (p->patterns[i]);
495 }
496 
497 size_t
pattlist_size(struct pattlist * p)498 pattlist_size(struct pattlist *p)
499 {
500 
501 	return (p->in);
502 }
503 
504 void
pattlist_free(struct pattlist * p)505 pattlist_free(struct pattlist *p)
506 {
507 	size_t i;
508 
509 	for (i = 0; i < p->in; i++)
510 		free(p->patterns[i]);
511 	free(p->patterns);
512 	free(p);
513 }
514 
515 /* Creates a backoff timer. */
516 struct backoff_timer *
bt_new(time_t min,time_t max,float backoff,float jitter)517 bt_new(time_t min, time_t max, float backoff, float jitter)
518 {
519 	struct backoff_timer *bt;
520 
521 	bt = xmalloc(sizeof(struct backoff_timer));
522 	bt->min = min;
523 	bt->max = max;
524 	bt->backoff = backoff;
525 	bt->jitter = jitter;
526 	bt->interval = min;
527 	bt_addjitter(bt);
528 	srandom(time(0));
529 	return (bt);
530 }
531 
532 /* Updates the backoff timer. */
533 static void
bt_update(struct backoff_timer * bt)534 bt_update(struct backoff_timer *bt)
535 {
536 
537 	bt->interval = (time_t)min(bt->interval * bt->backoff, bt->max);
538 	bt_addjitter(bt);
539 }
540 
541 /* Adds some jitter. */
542 static void
bt_addjitter(struct backoff_timer * bt)543 bt_addjitter(struct backoff_timer *bt)
544 {
545 	long mag;
546 
547 	mag = (long)(bt->jitter * bt->interval);
548 	/* We want a random number between -mag and mag. */
549 	bt->interval += (time_t)(random() % (2 * mag) - mag);
550 }
551 
552 /* Returns the current timer value. */
553 time_t
bt_get(struct backoff_timer * bt)554 bt_get(struct backoff_timer *bt)
555 {
556 
557 	return (bt->interval);
558 }
559 
560 /* Times out for bt->interval seconds. */
561 void
bt_pause(struct backoff_timer * bt)562 bt_pause(struct backoff_timer *bt)
563 {
564 
565 	sleep(bt->interval);
566 	bt_update(bt);
567 }
568 
569 void
bt_free(struct backoff_timer * bt)570 bt_free(struct backoff_timer *bt)
571 {
572 
573 	free(bt);
574 }
575 
576 /* Compare two revisions. */
577 int
rcsnum_cmp(char * revision1,char * revision2)578 rcsnum_cmp(char *revision1, char *revision2)
579 {
580         char *ptr1, *ptr2, *dot1, *dot2;
581 	int num1len, num2len, ret;
582 
583         ptr1 = revision1;
584         ptr2 = revision2;
585         while (*ptr1 != '\0' && *ptr2 != '\0') {
586                 dot1 = strchr(ptr1, '.');
587                 dot2 = strchr(ptr2, '.');
588                 if (dot1 == NULL)
589                         dot1 = strchr(ptr1, '\0');
590                 if (dot2 == NULL)
591                         dot2 = strchr(ptr2, '\0');
592 
593 		num1len = dot1 - ptr1;
594 		num2len = dot2 - ptr2;
595                 /* Check the distance between each, showing how many digits */
596                 if (num1len > num2len)
597                         return (1);
598                 else if (num1len < num2len)
599                         return (-1);
600 
601                 /* Equal distance means we must check each character. */
602 		ret = strncmp(ptr1, ptr2, num1len);
603 		if (ret != 0)
604 			return (ret);
605 		ptr1 = (*dot1 == '.') ? (dot1 + 1) : dot1;
606 		ptr2 = (*dot2 == '.') ? (dot2 + 1) : dot2;
607         }
608 
609         if (*ptr1 != '\0' && *ptr2 == '\0')
610                 return (1);
611         if (*ptr1 == '\0' && *ptr2 != '\0')
612                 return (-1);
613         return (0);
614 
615 }
616 
617 /* Returns 0 if a rcsrev is not a trunk revision number. */
618 int
rcsrev_istrunk(char * revnum)619 rcsrev_istrunk(char *revnum)
620 {
621 	char *tmp;
622 
623 	tmp = strchr(revnum, '.');
624 	tmp++;
625 	if (strchr(tmp, '.') != NULL)
626 		return (0);
627 	return (1);
628 }
629 
630 /* Return prefix of rcsfile. */
631 char *
rcsrev_prefix(char * revnum)632 rcsrev_prefix(char *revnum)
633 {
634 	char *modrev, *pos;
635 
636 	modrev = xstrdup(revnum);
637 	pos = strrchr(modrev, '.');
638 	if (pos == NULL) {
639 		free(modrev);
640 		return (NULL);
641 	}
642 	*pos = '\0';
643 	return (modrev);
644 }
645