1 /*- 2 * Copyright (c) 1992 Keith Muller. 3 * Copyright (c) 1992 The Regents of the University of California. 4 * 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 1.2 (Berkeley) 01/12/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}. We only allow a flag suffix(s) 357 * in write and copy (as none of the formats stores inode change time; 358 * currently inode change time cannot be set to a specific value by 359 * any system call). 360 */ 361 if ((flgpt = rindex(str, '/')) != NULL) { 362 *flgpt++ = '\0'; 363 if ((act == LIST) || (act == EXTRACT)) { 364 warn(1,"Time suffix only valid in write or copy modes"); 365 return(-1); 366 } 367 } 368 369 for (stpt = str; *stpt != '\0'; ++stpt) { 370 if ((*stpt >= '0') && (*stpt <= '9')) 371 continue; 372 if ((*stpt == ',') && (up_pt == NULL)) { 373 *stpt = '\0'; 374 up_pt = stpt + 1; 375 dot = 0; 376 continue; 377 } 378 379 /* 380 * allow only one dot per range (secs) 381 */ 382 if ((*stpt == '.') && (!dot)) { 383 ++dot; 384 continue; 385 } 386 warn(1, "Improperly specified time range: %s", str); 387 goto out; 388 } 389 390 /* 391 * allocate space for the time range and store the limits 392 */ 393 if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) { 394 warn(1, "Unable to allocate memory for time range"); 395 return(-1); 396 } 397 398 /* 399 * by default we only will check file mtime, but usee can specify 400 * mtime, ctime (inode change time) or both. 401 */ 402 if ((flgpt == NULL) || (*flgpt == '\0')) 403 pt->flgs = CMPMTME; 404 else { 405 pt->flgs = 0; 406 while (*flgpt != '\0') { 407 switch(*flgpt) { 408 case 'M': 409 case 'm': 410 pt->flgs |= CMPMTME; 411 break; 412 case 'C': 413 case 'c': 414 pt->flgs |= CMPCTME; 415 break; 416 default: 417 warn(1, "Bad option %c with time range %s", 418 *flgpt, str); 419 goto out; 420 } 421 ++flgpt; 422 } 423 } 424 425 /* 426 * start off with the current time 427 */ 428 pt->low_time = pt->high_time = time((time_t *)NULL); 429 if (*str != '\0') { 430 /* 431 * add lower limit 432 */ 433 if (str_sec(str, &(pt->low_time)) < 0) { 434 warn(1, "Illegal lower time range %s", str); 435 (void)free((char *)pt); 436 goto out; 437 } 438 pt->flgs |= HASLOW; 439 } 440 441 if ((up_pt != NULL) && (*up_pt != '\0')) { 442 /* 443 * add upper limit 444 */ 445 if (str_sec(up_pt, &(pt->high_time)) < 0) { 446 warn(1, "Illegal upper time range %s", up_pt); 447 (void)free((char *)pt); 448 goto out; 449 } 450 pt->flgs |= HASHIGH; 451 452 /* 453 * check that the upper and lower do not overlap 454 */ 455 if (pt->flgs & HASLOW) { 456 if (pt->low_time > pt->high_time) { 457 warn(1, "Upper %s and lower %s time overlap", 458 up_pt, str); 459 (void)free((char *)pt); 460 return(-1); 461 } 462 } 463 } 464 465 pt->fow = NULL; 466 if (trhead == NULL) { 467 trtail = trhead = pt; 468 return(0); 469 } 470 trtail->fow = pt; 471 trtail = pt; 472 return(0); 473 474 out: 475 warn(1, "Time range format is: [yy[mm[dd[hh]]]]mm[.ss][/[c][m]]"); 476 return(-1); 477 } 478 479 /* 480 * trng_match() 481 * check if this files mtime/ctime falls within any supplied time range. 482 * Return: 483 * 0 if this archive member should be processed, 1 if it should be skipped 484 */ 485 486 #if __STDC__ 487 static int 488 trng_match(register ARCHD *arcn) 489 #else 490 static int 491 trng_match(arcn) 492 register ARCHD *arcn; 493 #endif 494 { 495 register TIME_RNG *pt; 496 497 /* 498 * have to search down the list one at a time looking for a match. 499 * remember time range limits are inclusive. 500 */ 501 pt = trhead; 502 while (pt != NULL) { 503 switch(pt->flgs & CMPBOTH) { 504 case CMPBOTH: 505 /* 506 * user wants both mtime and ctime checked for this 507 * time range 508 */ 509 if (((pt->flgs & HASLOW) && 510 (arcn->sb.st_mtime < pt->low_time) && 511 (arcn->sb.st_ctime < pt->low_time)) || 512 ((pt->flgs & HASHIGH) && 513 (arcn->sb.st_mtime > pt->high_time) && 514 (arcn->sb.st_ctime > pt->high_time))) { 515 pt = pt->fow; 516 continue; 517 } 518 break; 519 case CMPCTME: 520 /* 521 * user wants only ctime checked for this time range 522 */ 523 if (((pt->flgs & HASLOW) && 524 (arcn->sb.st_ctime < pt->low_time)) || 525 ((pt->flgs & HASHIGH) && 526 (arcn->sb.st_ctime > pt->high_time))) { 527 pt = pt->fow; 528 continue; 529 } 530 break; 531 case CMPMTME: 532 default: 533 /* 534 * user wants only mtime checked for this time range 535 */ 536 if (((pt->flgs & HASLOW) && 537 (arcn->sb.st_mtime < pt->low_time)) || 538 ((pt->flgs & HASHIGH) && 539 (arcn->sb.st_mtime > pt->high_time))) { 540 pt = pt->fow; 541 continue; 542 } 543 break; 544 } 545 break; 546 } 547 548 if (pt == NULL) 549 return(1); 550 return(0); 551 } 552 553 /* 554 * str_sec() 555 * Convert a time string in the format of [yy[mm[dd[hh]]]]mm[.ss] to gmt 556 * seconds. Tval already has current time loaded into it at entry. 557 * Return: 558 * 0 if converted ok, -1 otherwise 559 */ 560 561 #if __STDC__ 562 static int 563 str_sec(register char *str, time_t *tval) 564 #else 565 static int 566 str_sec(str, tval) 567 register char *str; 568 time_t *tval; 569 #endif 570 { 571 register struct tm *lt; 572 register char *dot = NULL; 573 574 lt = localtime(tval); 575 if ((dot = index(str, '.')) != NULL) { 576 /* 577 * seconds (.ss) 578 */ 579 *dot++ = '\0'; 580 if (strlen(dot) != 2) 581 return(-1); 582 if ((lt->tm_sec = ATOI2(dot)) > 61) 583 return(-1); 584 } else 585 lt->tm_sec = 0; 586 587 switch (strlen(str)) { 588 case 10: 589 /* 590 * year (yy) 591 * watch out for year 2000 592 */ 593 if ((lt->tm_year = ATOI2(str)) < 69) 594 lt->tm_year += 100; 595 str += 2; 596 /* FALLTHROUGH */ 597 case 8: 598 /* 599 * month (mm) 600 * watch out months are from 0 - 11 internally 601 */ 602 if ((lt->tm_mon = ATOI2(str)) > 12) 603 return(-1); 604 --lt->tm_mon; 605 str += 2; 606 /* FALLTHROUGH */ 607 case 6: 608 /* 609 * day (dd) 610 */ 611 if ((lt->tm_mday = ATOI2(str)) > 31) 612 return(-1); 613 str += 2; 614 /* FALLTHROUGH */ 615 case 4: 616 /* 617 * hour (hh) 618 */ 619 if ((lt->tm_hour = ATOI2(str)) > 23) 620 return(-1); 621 str += 2; 622 /* FALLTHROUGH */ 623 case 2: 624 /* 625 * minute (mm) 626 */ 627 if ((lt->tm_min = ATOI2(str)) > 59) 628 return(-1); 629 break; 630 default: 631 return(-1); 632 } 633 /* 634 * convert broken-down time to GMT clock time seconds 635 */ 636 if ((*tval = mktime(lt)) == -1) 637 return(-1); 638 return(0); 639 } 640