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 49struct pattlist { 50 char **patterns; 51 size_t size; 52 size_t in; 53}; 54 55struct backoff_timer { 56 time_t min; 57 time_t max; 58 time_t interval; 59 float backoff; 60 float jitter; 61}; 62 63static void bt_update(struct backoff_timer *); 64static void bt_addjitter(struct backoff_timer *); 65 66int 67asciitoint(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 84int 85lprintf(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 */ 111int 112MD5_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 */ 136void 137MD5_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 152int 153pathcmp(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 169size_t 170commonpathlength(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 204const char * 205pathlast(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 215int 216rcsdatetotm(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 236time_t 237rcsdatetotime(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 */ 253int 254isrcs(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 */ 281char * 282checkoutpath(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 */ 298char * 299cvspath(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 */ 319char * 320atticpath(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 332int 333mkdirhier(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 390static pthread_mutex_t tempname_mtx = PTHREAD_MUTEX_INITIALIZER; 391static pid_t tempname_pid = -1; 392static int tempname_count; 393 394char * 395tempname(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 419void * 420xmalloc(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 430void * 431xrealloc(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 440char * 441xstrdup(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 451int 452xasprintf(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 465struct pattlist * 466pattlist_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 477void 478pattlist_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 489char * 490pattlist_get(struct pattlist *p, size_t i) 491{ 492 493 assert(i < p->in); 494 return (p->patterns[i]); 495} 496 497size_t 498pattlist_size(struct pattlist *p) 499{ 500 501 return (p->in); 502} 503 504void 505pattlist_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. */ 516struct backoff_timer * 517bt_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. */ 533static void 534bt_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. */ 542static void 543bt_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. */ 553time_t 554bt_get(struct backoff_timer *bt) 555{ 556 557 return (bt->interval); 558} 559 560/* Times out for bt->interval seconds. */ 561void 562bt_pause(struct backoff_timer *bt) 563{ 564 565 sleep(bt->interval); 566 bt_update(bt); 567} 568 569void 570bt_free(struct backoff_timer *bt) 571{ 572 573 free(bt); 574} 575 576/* Compare two revisions. */ 577int 578rcsnum_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. */ 618int 619rcsrev_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. */ 631char * 632rcsrev_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