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