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