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