1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1992 Keith Muller. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Keith Muller of the University of California, San Diego. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)sel_subs.c 8.1 (Berkeley) 5/31/93"; 39 #endif 40 #endif /* not lint */ 41 #include <sys/cdefs.h> 42 __FBSDID("$FreeBSD$"); 43 44 #include <sys/types.h> 45 #include <sys/time.h> 46 #include <sys/stat.h> 47 48 #include <ctype.h> 49 #include <grp.h> 50 #include <pwd.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <strings.h> 55 56 #include "pax.h" 57 #include "sel_subs.h" 58 #include "extern.h" 59 60 static int str_sec(const 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 the 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 uid = (uid_t)strtoul(str+1, NULL, 10); 141 endpwent(); 142 143 /* 144 * hash it and go down the hash chain (if any) looking for it 145 */ 146 indx = ((unsigned)uid) % USR_TB_SZ; 147 if ((pt = usrtb[indx]) != NULL) { 148 while (pt != NULL) { 149 if (pt->uid == uid) 150 return(0); 151 pt = pt->fow; 152 } 153 } 154 155 /* 156 * uid is not yet in the table, add it to the front of the chain 157 */ 158 if ((pt = (USRT *)malloc(sizeof(USRT))) != NULL) { 159 pt->uid = uid; 160 pt->fow = usrtb[indx]; 161 usrtb[indx] = pt; 162 return(0); 163 } 164 paxwarn(1, "User selection table out of memory"); 165 return(-1); 166 } 167 168 /* 169 * usr_match() 170 * check if this files uid matches a selected uid. 171 * Return: 172 * 0 if this archive member should be processed, 1 if it should be skipped 173 */ 174 175 static int 176 usr_match(ARCHD *arcn) 177 { 178 USRT *pt; 179 180 /* 181 * hash and look for it in the table 182 */ 183 pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ]; 184 while (pt != NULL) { 185 if (pt->uid == arcn->sb.st_uid) 186 return(0); 187 pt = pt->fow; 188 } 189 190 /* 191 * not found 192 */ 193 return(1); 194 } 195 196 /* 197 * grp_add() 198 * add a group match to the group match hash table 199 * Return: 200 * 0 if added ok, -1 otherwise; 201 */ 202 203 int 204 grp_add(char *str) 205 { 206 u_int indx; 207 GRPT *pt; 208 struct group *gr; 209 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 paxwarn(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 paxwarn(1,"Cannot determine gid for group name: %s", str); 233 return(-1); 234 } 235 gid = gr->gr_gid; 236 } else 237 gid = (gid_t)strtoul(str+1, NULL, 10); 238 endgrent(); 239 240 /* 241 * hash it and go down the hash chain (if any) looking for it 242 */ 243 indx = ((unsigned)gid) % GRP_TB_SZ; 244 if ((pt = grptb[indx]) != NULL) { 245 while (pt != NULL) { 246 if (pt->gid == gid) 247 return(0); 248 pt = pt->fow; 249 } 250 } 251 252 /* 253 * gid not in the table, add it to the front of the chain 254 */ 255 if ((pt = (GRPT *)malloc(sizeof(GRPT))) != NULL) { 256 pt->gid = gid; 257 pt->fow = grptb[indx]; 258 grptb[indx] = pt; 259 return(0); 260 } 261 paxwarn(1, "Group selection table out of memory"); 262 return(-1); 263 } 264 265 /* 266 * grp_match() 267 * check if this files gid matches a selected gid. 268 * Return: 269 * 0 if this archive member should be processed, 1 if it should be skipped 270 */ 271 272 static int 273 grp_match(ARCHD *arcn) 274 { 275 GRPT *pt; 276 277 /* 278 * hash and look for it in the table 279 */ 280 pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ]; 281 while (pt != NULL) { 282 if (pt->gid == arcn->sb.st_gid) 283 return(0); 284 pt = pt->fow; 285 } 286 287 /* 288 * not found 289 */ 290 return(1); 291 } 292 293 /* 294 * Time range selection routines 295 * 296 * Routines to handle user selection of files based on the modification and/or 297 * inode change time falling within a specified time range (the non-standard 298 * -T flag). The user may specify any number of different file time ranges. 299 * Time ranges are checked one at a time until a match is found (if at all). 300 * If the file has a mtime (and/or ctime) which lies within one of the time 301 * ranges, the file is selected. Time ranges may have a lower and/or an upper 302 * value. These ranges are inclusive. When no time ranges are supplied to pax 303 * with the -T option, all members in the archive will be selected by the time 304 * range routines. When only a lower range is supplied, only files with a 305 * mtime (and/or ctime) equal to or younger are selected. When only an upper 306 * range is supplied, only files with a mtime (and/or ctime) equal to or older 307 * are selected. When the lower time range is equal to the upper time range, 308 * only files with a mtime (or ctime) of exactly that time are selected. 309 */ 310 311 /* 312 * trng_add() 313 * add a time range match to the time range list. 314 * This is a non-standard pax option. Lower and upper ranges are in the 315 * format: [[[[[cc]yy]mm]dd]HH]MM[.SS] and are comma separated. 316 * Time ranges are based on current time, so 1234 would specify a time of 317 * 12:34 today. 318 * Return: 319 * 0 if the time range was added to the list, -1 otherwise 320 */ 321 322 int 323 trng_add(char *str) 324 { 325 TIME_RNG *pt; 326 char *up_pt = NULL; 327 char *stpt; 328 char *flgpt; 329 int dot = 0; 330 331 /* 332 * throw out the badly formed time ranges 333 */ 334 if ((str == NULL) || (*str == '\0')) { 335 paxwarn(1, "Empty time range string"); 336 return(-1); 337 } 338 339 /* 340 * locate optional flags suffix /{cm}. 341 */ 342 if ((flgpt = strrchr(str, '/')) != NULL) 343 *flgpt++ = '\0'; 344 345 for (stpt = str; *stpt != '\0'; ++stpt) { 346 if ((*stpt >= '0') && (*stpt <= '9')) 347 continue; 348 if ((*stpt == ',') && (up_pt == NULL)) { 349 *stpt = '\0'; 350 up_pt = stpt + 1; 351 dot = 0; 352 continue; 353 } 354 355 /* 356 * allow only one dot per range (secs) 357 */ 358 if ((*stpt == '.') && (!dot)) { 359 ++dot; 360 continue; 361 } 362 paxwarn(1, "Improperly specified time range: %s", str); 363 goto out; 364 } 365 366 /* 367 * allocate space for the time range and store the limits 368 */ 369 if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) { 370 paxwarn(1, "Unable to allocate memory for time range"); 371 return(-1); 372 } 373 374 /* 375 * by default we only will check file mtime, but the user can specify 376 * mtime, ctime (inode change time) or both. 377 */ 378 if ((flgpt == NULL) || (*flgpt == '\0')) 379 pt->flgs = CMPMTME; 380 else { 381 pt->flgs = 0; 382 while (*flgpt != '\0') { 383 switch(*flgpt) { 384 case 'M': 385 case 'm': 386 pt->flgs |= CMPMTME; 387 break; 388 case 'C': 389 case 'c': 390 pt->flgs |= CMPCTME; 391 break; 392 default: 393 paxwarn(1, "Bad option %c with time range %s", 394 *flgpt, str); 395 free(pt); 396 goto out; 397 } 398 ++flgpt; 399 } 400 } 401 402 /* 403 * start off with the current time 404 */ 405 pt->low_time = pt->high_time = time(NULL); 406 if (*str != '\0') { 407 /* 408 * add lower limit 409 */ 410 if (str_sec(str, &(pt->low_time)) < 0) { 411 paxwarn(1, "Illegal lower time range %s", str); 412 free(pt); 413 goto out; 414 } 415 pt->flgs |= HASLOW; 416 } 417 418 if ((up_pt != NULL) && (*up_pt != '\0')) { 419 /* 420 * add upper limit 421 */ 422 if (str_sec(up_pt, &(pt->high_time)) < 0) { 423 paxwarn(1, "Illegal upper time range %s", up_pt); 424 free(pt); 425 goto out; 426 } 427 pt->flgs |= HASHIGH; 428 429 /* 430 * check that the upper and lower do not overlap 431 */ 432 if (pt->flgs & HASLOW) { 433 if (pt->low_time > pt->high_time) { 434 paxwarn(1, "Upper %s and lower %s time overlap", 435 up_pt, str); 436 free(pt); 437 return(-1); 438 } 439 } 440 } 441 442 pt->fow = NULL; 443 if (trhead == NULL) { 444 trtail = trhead = pt; 445 return(0); 446 } 447 trtail->fow = pt; 448 trtail = pt; 449 return(0); 450 451 out: 452 paxwarn(1, "Time range format is: [[[[[cc]yy]mm]dd]HH]MM[.SS][/[c][m]]"); 453 return(-1); 454 } 455 456 /* 457 * trng_match() 458 * check if this files mtime/ctime falls within any supplied time range. 459 * Return: 460 * 0 if this archive member should be processed, 1 if it should be skipped 461 */ 462 463 static int 464 trng_match(ARCHD *arcn) 465 { 466 TIME_RNG *pt; 467 468 /* 469 * have to search down the list one at a time looking for a match. 470 * remember time range limits are inclusive. 471 */ 472 pt = trhead; 473 while (pt != NULL) { 474 switch(pt->flgs & CMPBOTH) { 475 case CMPBOTH: 476 /* 477 * user wants both mtime and ctime checked for this 478 * time range 479 */ 480 if (((pt->flgs & HASLOW) && 481 (arcn->sb.st_mtime < pt->low_time) && 482 (arcn->sb.st_ctime < pt->low_time)) || 483 ((pt->flgs & HASHIGH) && 484 (arcn->sb.st_mtime > pt->high_time) && 485 (arcn->sb.st_ctime > pt->high_time))) { 486 pt = pt->fow; 487 continue; 488 } 489 break; 490 case CMPCTME: 491 /* 492 * user wants only ctime checked for this time range 493 */ 494 if (((pt->flgs & HASLOW) && 495 (arcn->sb.st_ctime < pt->low_time)) || 496 ((pt->flgs & HASHIGH) && 497 (arcn->sb.st_ctime > pt->high_time))) { 498 pt = pt->fow; 499 continue; 500 } 501 break; 502 case CMPMTME: 503 default: 504 /* 505 * user wants only mtime checked for this time range 506 */ 507 if (((pt->flgs & HASLOW) && 508 (arcn->sb.st_mtime < pt->low_time)) || 509 ((pt->flgs & HASHIGH) && 510 (arcn->sb.st_mtime > pt->high_time))) { 511 pt = pt->fow; 512 continue; 513 } 514 break; 515 } 516 break; 517 } 518 519 if (pt == NULL) 520 return(1); 521 return(0); 522 } 523 524 /* 525 * str_sec() 526 * Convert a time string in the format of [[[[[cc]yy]mm]dd]HH]MM[.SS] to 527 * seconds UTC. Tval already has current time loaded into it at entry. 528 * Return: 529 * 0 if converted ok, -1 otherwise 530 */ 531 532 static int 533 str_sec(const char *p, time_t *tval) 534 { 535 struct tm *lt; 536 const char *dot, *t; 537 size_t len; 538 int bigyear; 539 int yearset; 540 541 yearset = 0; 542 len = strlen(p); 543 544 for (t = p, dot = NULL; *t; ++t) { 545 if (isdigit((unsigned char)*t)) 546 continue; 547 if (*t == '.' && dot == NULL) { 548 dot = t; 549 continue; 550 } 551 return(-1); 552 } 553 554 lt = localtime(tval); 555 556 if (dot != NULL) { /* .SS */ 557 if (strlen(++dot) != 2) 558 return(-1); 559 lt->tm_sec = ATOI2(dot); 560 if (lt->tm_sec > 61) 561 return(-1); 562 len -= 3; 563 } else 564 lt->tm_sec = 0; 565 566 switch (len) { 567 case 12: /* cc */ 568 bigyear = ATOI2(p); 569 lt->tm_year = (bigyear * 100) - 1900; 570 yearset = 1; 571 /* FALLTHROUGH */ 572 case 10: /* yy */ 573 if (yearset) { 574 lt->tm_year += ATOI2(p); 575 } else { 576 lt->tm_year = ATOI2(p); 577 if (lt->tm_year < 69) /* hack for 2000 ;-} */ 578 lt->tm_year += (2000 - 1900); 579 } 580 /* FALLTHROUGH */ 581 case 8: /* mm */ 582 lt->tm_mon = ATOI2(p); 583 if ((lt->tm_mon > 12) || !lt->tm_mon) 584 return(-1); 585 --lt->tm_mon; /* time struct is 0 - 11 */ 586 /* FALLTHROUGH */ 587 case 6: /* dd */ 588 lt->tm_mday = ATOI2(p); 589 if ((lt->tm_mday > 31) || !lt->tm_mday) 590 return(-1); 591 /* FALLTHROUGH */ 592 case 4: /* HH */ 593 lt->tm_hour = ATOI2(p); 594 if (lt->tm_hour > 23) 595 return(-1); 596 /* FALLTHROUGH */ 597 case 2: /* MM */ 598 lt->tm_min = ATOI2(p); 599 if (lt->tm_min > 59) 600 return(-1); 601 break; 602 default: 603 return(-1); 604 } 605 606 /* convert broken-down time to UTC clock time seconds */ 607 if ((*tval = mktime(lt)) == -1) 608 return(-1); 609 return(0); 610 } 611