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