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