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