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