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