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