sel_subs.c revision 1.12
1/* $NetBSD: sel_subs.c,v 1.12 2000/02/17 03:12:26 itohy Exp $ */ 2 3/*- 4 * Copyright (c) 1992 Keith Muller. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Keith Muller of the University of California, San Diego. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40#include <sys/cdefs.h> 41#ifndef lint 42#if 0 43static char sccsid[] = "@(#)sel_subs.c 8.1 (Berkeley) 5/31/93"; 44#else 45__RCSID("$NetBSD: sel_subs.c,v 1.12 2000/02/17 03:12:26 itohy Exp $"); 46#endif 47#endif /* not lint */ 48 49#include <sys/types.h> 50#include <sys/time.h> 51#include <sys/stat.h> 52#include <sys/param.h> 53 54#include <pwd.h> 55#include <grp.h> 56#include <stdio.h> 57#include <ctype.h> 58#include <string.h> 59#include <strings.h> 60#include <time.h> 61#include <unistd.h> 62#include <stdlib.h> 63#include <tzfile.h> 64 65#include "pax.h" 66#include "sel_subs.h" 67#include "extern.h" 68 69static int str_sec __P((const char *, time_t *)); 70static int usr_match __P((ARCHD *)); 71static int grp_match __P((ARCHD *)); 72static int trng_match __P((ARCHD *)); 73 74static TIME_RNG *trhead = NULL; /* time range list head */ 75static TIME_RNG *trtail = NULL; /* time range list tail */ 76static USRT **usrtb = NULL; /* user selection table */ 77static GRPT **grptb = NULL; /* group selection table */ 78 79/* 80 * Routines for selection of archive members 81 */ 82 83/* 84 * sel_chk() 85 * check if this file matches a specfied uid, gid or time range 86 * Return: 87 * 0 if this archive member should be processed, 1 if it should be skipped 88 */ 89 90#if __STDC__ 91int 92sel_chk(ARCHD *arcn) 93#else 94int 95sel_chk(arcn) 96 ARCHD *arcn; 97#endif 98{ 99 if (((usrtb != NULL) && usr_match(arcn)) || 100 ((grptb != NULL) && grp_match(arcn)) || 101 ((trhead != NULL) && trng_match(arcn))) 102 return(1); 103 return(0); 104} 105 106/* 107 * User/group selection routines 108 * 109 * Routines to handle user selection of files based on the file uid/gid. To 110 * add an entry, the user supplies either then name or the uid/gid starting with 111 * a # on the command line. A \# will eascape the #. 112 */ 113 114/* 115 * usr_add() 116 * add a user match to the user match hash table 117 * Return: 118 * 0 if added ok, -1 otherwise; 119 */ 120 121#if __STDC__ 122int 123usr_add(char *str) 124#else 125int 126usr_add(str) 127 char *str; 128#endif 129{ 130 u_int indx; 131 USRT *pt; 132 struct passwd *pw; 133 uid_t uid; 134 135 /* 136 * create the table if it doesn't exist 137 */ 138 if ((str == NULL) || (*str == '\0')) 139 return(-1); 140 if ((usrtb == NULL) && 141 ((usrtb = (USRT **)calloc(USR_TB_SZ, sizeof(USRT *))) == NULL)) { 142 tty_warn(1, 143 "Unable to allocate memory for user selection table"); 144 return(-1); 145 } 146 147 /* 148 * figure out user spec 149 */ 150 if (str[0] != '#') { 151 /* 152 * it is a user name, \# escapes # as first char in user name 153 */ 154 if ((str[0] == '\\') && (str[1] == '#')) 155 ++str; 156 if ((pw = getpwnam(str)) == NULL) { 157 tty_warn(1, "Unable to find uid for user: %s", str); 158 return(-1); 159 } 160 uid = (uid_t)pw->pw_uid; 161 } else 162# ifdef NET2_STAT 163 uid = (uid_t)atoi(str+1); 164# else 165 uid = (uid_t)strtoul(str+1, (char **)NULL, 10); 166# endif 167 endpwent(); 168 169 /* 170 * hash it and go down the hash chain (if any) looking for it 171 */ 172 indx = ((unsigned)uid) % USR_TB_SZ; 173 if ((pt = usrtb[indx]) != NULL) { 174 while (pt != NULL) { 175 if (pt->uid == uid) 176 return(0); 177 pt = pt->fow; 178 } 179 } 180 181 /* 182 * uid is not yet in the table, add it to the front of the chain 183 */ 184 if ((pt = (USRT *)malloc(sizeof(USRT))) != NULL) { 185 pt->uid = uid; 186 pt->fow = usrtb[indx]; 187 usrtb[indx] = pt; 188 return(0); 189 } 190 tty_warn(1, "User selection table out of memory"); 191 return(-1); 192} 193 194/* 195 * usr_match() 196 * check if this files uid matches a selected uid. 197 * Return: 198 * 0 if this archive member should be processed, 1 if it should be skipped 199 */ 200 201#if __STDC__ 202static int 203usr_match(ARCHD *arcn) 204#else 205static int 206usr_match(arcn) 207 ARCHD *arcn; 208#endif 209{ 210 USRT *pt; 211 212 /* 213 * hash and look for it in the table 214 */ 215 pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ]; 216 while (pt != NULL) { 217 if (pt->uid == arcn->sb.st_uid) 218 return(0); 219 pt = pt->fow; 220 } 221 222 /* 223 * not found 224 */ 225 return(1); 226} 227 228/* 229 * grp_add() 230 * add a group match to the group match hash table 231 * Return: 232 * 0 if added ok, -1 otherwise; 233 */ 234 235#if __STDC__ 236int 237grp_add(char *str) 238#else 239int 240grp_add(str) 241 char *str; 242#endif 243{ 244 u_int indx; 245 GRPT *pt; 246 struct group *gr; 247 gid_t gid; 248 249 /* 250 * create the table if it doesn't exist 251 */ 252 if ((str == NULL) || (*str == '\0')) 253 return(-1); 254 if ((grptb == NULL) && 255 ((grptb = (GRPT **)calloc(GRP_TB_SZ, sizeof(GRPT *))) == NULL)) { 256 tty_warn(1, 257 "Unable to allocate memory fo group selection table"); 258 return(-1); 259 } 260 261 /* 262 * figure out user spec 263 */ 264 if (str[0] != '#') { 265 /* 266 * it is a group name, \# escapes # as first char in group name 267 */ 268 if ((str[0] == '\\') && (str[1] == '#')) 269 ++str; 270 if ((gr = getgrnam(str)) == NULL) { 271 tty_warn(1, 272 "Cannot determine gid for group name: %s", str); 273 return(-1); 274 } 275 gid = (gid_t)gr->gr_gid; 276 } else 277# ifdef NET2_STAT 278 gid = (gid_t)atoi(str+1); 279# else 280 gid = (gid_t)strtoul(str+1, (char **)NULL, 10); 281# endif 282 endgrent(); 283 284 /* 285 * hash it and go down the hash chain (if any) looking for it 286 */ 287 indx = ((unsigned)gid) % GRP_TB_SZ; 288 if ((pt = grptb[indx]) != NULL) { 289 while (pt != NULL) { 290 if (pt->gid == gid) 291 return(0); 292 pt = pt->fow; 293 } 294 } 295 296 /* 297 * gid not in the table, add it to the front of the chain 298 */ 299 if ((pt = (GRPT *)malloc(sizeof(GRPT))) != NULL) { 300 pt->gid = gid; 301 pt->fow = grptb[indx]; 302 grptb[indx] = pt; 303 return(0); 304 } 305 tty_warn(1, "Group selection table out of memory"); 306 return(-1); 307} 308 309/* 310 * grp_match() 311 * check if this files gid matches a selected gid. 312 * Return: 313 * 0 if this archive member should be processed, 1 if it should be skipped 314 */ 315 316#if __STDC__ 317static int 318grp_match(ARCHD *arcn) 319#else 320static int 321grp_match(arcn) 322 ARCHD *arcn; 323#endif 324{ 325 GRPT *pt; 326 327 /* 328 * hash and look for it in the table 329 */ 330 pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ]; 331 while (pt != NULL) { 332 if (pt->gid == arcn->sb.st_gid) 333 return(0); 334 pt = pt->fow; 335 } 336 337 /* 338 * not found 339 */ 340 return(1); 341} 342 343/* 344 * Time range selection routines 345 * 346 * Routines to handle user selection of files based on the modification and/or 347 * inode change time falling within a specified time range (the non-standard 348 * -T flag). The user may specify any number of different file time ranges. 349 * Time ranges are checked one at a time until a match is found (if at all). 350 * If the file has a mtime (and/or ctime) which lies within one of the time 351 * ranges, the file is selected. Time ranges may have a lower and/or a upper 352 * value. These ranges are inclusive. When no time ranges are supplied to pax 353 * with the -T option, all members in the archive will be selected by the time 354 * range routines. When only a lower range is supplied, only files with a 355 * mtime (and/or ctime) equal to or younger are selected. When only a upper 356 * range is supplied, only files with a mtime (and/or ctime) equal to or older 357 * are selected. When the lower time range is equal to the upper time range, 358 * only files with a mtime (or ctime) of exactly that time are selected. 359 */ 360 361/* 362 * trng_add() 363 * add a time range match to the time range list. 364 * This is a non-standard pax option. Lower and upper ranges are in the 365 * format: [yy[mm[dd[hh]]]]mm[.ss] and are comma separated. 366 * Time ranges are based on current time, so 1234 would specify a time of 367 * 12:34 today. 368 * Return: 369 * 0 if the time range was added to the list, -1 otherwise 370 */ 371 372#if __STDC__ 373int 374trng_add(char *str) 375#else 376int 377trng_add(str) 378 char *str; 379#endif 380{ 381 TIME_RNG *pt; 382 char *up_pt = NULL; 383 char *stpt; 384 char *flgpt; 385 int dot = 0; 386 387 /* 388 * throw out the badly formed time ranges 389 */ 390 if ((str == NULL) || (*str == '\0')) { 391 tty_warn(1, "Empty time range string"); 392 return(-1); 393 } 394 395 /* 396 * locate optional flags suffix /{cm}. 397 */ 398 if ((flgpt = strrchr(str, '/')) != NULL) 399 *flgpt++ = '\0'; 400 401 for (stpt = str; *stpt != '\0'; ++stpt) { 402 if ((*stpt >= '0') && (*stpt <= '9')) 403 continue; 404 if ((*stpt == ',') && (up_pt == NULL)) { 405 *stpt = '\0'; 406 up_pt = stpt + 1; 407 dot = 0; 408 continue; 409 } 410 411 /* 412 * allow only one dot per range (secs) 413 */ 414 if ((*stpt == '.') && (!dot)) { 415 ++dot; 416 continue; 417 } 418 tty_warn(1, "Improperly specified time range: %s", str); 419 goto out; 420 } 421 422 /* 423 * allocate space for the time range and store the limits 424 */ 425 if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) { 426 tty_warn(1, "Unable to allocate memory for time range"); 427 return(-1); 428 } 429 430 /* 431 * by default we only will check file mtime, but usee can specify 432 * mtime, ctime (inode change time) or both. 433 */ 434 if ((flgpt == NULL) || (*flgpt == '\0')) 435 pt->flgs = CMPMTME; 436 else { 437 pt->flgs = 0; 438 while (*flgpt != '\0') { 439 switch(*flgpt) { 440 case 'M': 441 case 'm': 442 pt->flgs |= CMPMTME; 443 break; 444 case 'C': 445 case 'c': 446 pt->flgs |= CMPCTME; 447 break; 448 default: 449 tty_warn(1, "Bad option %c with time range %s", 450 *flgpt, str); 451 goto out; 452 } 453 ++flgpt; 454 } 455 } 456 457 /* 458 * start off with the current time 459 */ 460 pt->low_time = pt->high_time = time((time_t *)NULL); 461 if (*str != '\0') { 462 /* 463 * add lower limit 464 */ 465 if (str_sec(str, &(pt->low_time)) < 0) { 466 tty_warn(1, "Illegal lower time range %s", str); 467 (void)free((char *)pt); 468 goto out; 469 } 470 pt->flgs |= HASLOW; 471 } 472 473 if ((up_pt != NULL) && (*up_pt != '\0')) { 474 /* 475 * add upper limit 476 */ 477 if (str_sec(up_pt, &(pt->high_time)) < 0) { 478 tty_warn(1, "Illegal upper time range %s", up_pt); 479 (void)free((char *)pt); 480 goto out; 481 } 482 pt->flgs |= HASHIGH; 483 484 /* 485 * check that the upper and lower do not overlap 486 */ 487 if (pt->flgs & HASLOW) { 488 if (pt->low_time > pt->high_time) { 489 tty_warn(1, 490 "Upper %s and lower %s time overlap", 491 up_pt, str); 492 (void)free((char *)pt); 493 return(-1); 494 } 495 } 496 } 497 498 pt->fow = NULL; 499 if (trhead == NULL) { 500 trtail = trhead = pt; 501 return(0); 502 } 503 trtail->fow = pt; 504 trtail = pt; 505 return(0); 506 507 out: 508 tty_warn(1, "Time range format is: [yy[mm[dd[hh]]]]mm[.ss][/[c][m]]"); 509 return(-1); 510} 511 512/* 513 * trng_match() 514 * check if this files mtime/ctime falls within any supplied time range. 515 * Return: 516 * 0 if this archive member should be processed, 1 if it should be skipped 517 */ 518 519#if __STDC__ 520static int 521trng_match(ARCHD *arcn) 522#else 523static int 524trng_match(arcn) 525 ARCHD *arcn; 526#endif 527{ 528 TIME_RNG *pt; 529 530 /* 531 * have to search down the list one at a time looking for a match. 532 * remember time range limits are inclusive. 533 */ 534 pt = trhead; 535 while (pt != NULL) { 536 switch(pt->flgs & CMPBOTH) { 537 case CMPBOTH: 538 /* 539 * user wants both mtime and ctime checked for this 540 * time range 541 */ 542 if (((pt->flgs & HASLOW) && 543 (arcn->sb.st_mtime < pt->low_time) && 544 (arcn->sb.st_ctime < pt->low_time)) || 545 ((pt->flgs & HASHIGH) && 546 (arcn->sb.st_mtime > pt->high_time) && 547 (arcn->sb.st_ctime > pt->high_time))) { 548 pt = pt->fow; 549 continue; 550 } 551 break; 552 case CMPCTME: 553 /* 554 * user wants only ctime checked for this time range 555 */ 556 if (((pt->flgs & HASLOW) && 557 (arcn->sb.st_ctime < pt->low_time)) || 558 ((pt->flgs & HASHIGH) && 559 (arcn->sb.st_ctime > pt->high_time))) { 560 pt = pt->fow; 561 continue; 562 } 563 break; 564 case CMPMTME: 565 default: 566 /* 567 * user wants only mtime checked for this time range 568 */ 569 if (((pt->flgs & HASLOW) && 570 (arcn->sb.st_mtime < pt->low_time)) || 571 ((pt->flgs & HASHIGH) && 572 (arcn->sb.st_mtime > pt->high_time))) { 573 pt = pt->fow; 574 continue; 575 } 576 break; 577 } 578 break; 579 } 580 581 if (pt == NULL) 582 return(1); 583 return(0); 584} 585 586/* 587 * str_sec() 588 * Convert a time string in the format of [yy[mm[dd[hh]]]]mm[.ss] to gmt 589 * seconds. Tval already has current time loaded into it at entry. 590 * Return: 591 * 0 if converted ok, -1 otherwise 592 */ 593 594#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) 595 596#if __STDC__ 597static int 598str_sec(const char *p, time_t *tval) 599#else 600static int 601str_sec(p, tval) 602 const char *p; 603 time_t *tval; 604#endif 605{ 606 struct tm *lt; 607 const char *dot, *t; 608 int yearset, len; 609 610 for (t = p, dot = NULL; *t; ++t) { 611 if (isdigit((unsigned char)*t)) 612 continue; 613 if (*t == '.' && dot == NULL) { 614 dot = t; 615 continue; 616 } 617 return(-1); 618 } 619 620 lt = localtime(tval); 621 622 if (dot != NULL) { 623 len = strlen(dot); 624 if (len != 3) 625 return(-1); 626 ++dot; 627 lt->tm_sec = ATOI2(dot); 628 } else { 629 len = 0; 630 lt->tm_sec = 0; 631 } 632 633 yearset = 0; 634 switch (strlen(p) - len) { 635 case 12: 636 lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE; 637 yearset = 1; 638 /* FALLTHROUGH */ 639 case 10: 640 if (yearset) { 641 lt->tm_year += ATOI2(p); 642 } else { 643 yearset = ATOI2(p); 644 if (yearset < 69) 645 lt->tm_year = yearset + 2000 - TM_YEAR_BASE; 646 else 647 lt->tm_year = yearset + 1900 - TM_YEAR_BASE; 648 } 649 /* FALLTHROUGH */ 650 case 8: 651 lt->tm_mon = ATOI2(p); 652 --lt->tm_mon; 653 /* FALLTHROUGH */ 654 case 6: 655 lt->tm_mday = ATOI2(p); 656 /* FALLTHROUGH */ 657 case 4: 658 lt->tm_hour = ATOI2(p); 659 /* FALLTHROUGH */ 660 case 2: 661 lt->tm_min = ATOI2(p); 662 break; 663 default: 664 return(-1); 665 } 666 667 /* 668 * convert broken-down time to GMT clock time seconds 669 */ 670 if ((*tval = mktime(lt)) == -1) 671 return(-1); 672 return(0); 673} 674