Home | History | Annotate | Line # | Download | only in pax
sel_subs.c revision 1.8
      1 /*	$NetBSD: sel_subs.c,v 1.8 1998/01/21 00:11:16 mycroft Exp $	*/
      2 
      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. All advertising materials mentioning features or use of this software
     20  *    must display the following acknowledgement:
     21  *	This product includes software developed by the University of
     22  *	California, Berkeley and its contributors.
     23  * 4. Neither the name of the University nor the names of its contributors
     24  *    may be used to endorse or promote products derived from this software
     25  *    without specific prior written permission.
     26  *
     27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     37  * SUCH DAMAGE.
     38  */
     39 
     40 #include <sys/cdefs.h>
     41 #ifndef lint
     42 #if 0
     43 static char sccsid[] = "@(#)sel_subs.c	8.1 (Berkeley) 5/31/93";
     44 #else
     45 __RCSID("$NetBSD: sel_subs.c,v 1.8 1998/01/21 00:11:16 mycroft Exp $");
     46 #endif
     47 #endif /* not lint */
     48 
     49 #include <sys/types.h>
     50 #include <sys/time.h>
     51 #include <sys/stat.h>
     52 #include <sys/param.h>
     53 
     54 #include <pwd.h>
     55 #include <grp.h>
     56 #include <stdio.h>
     57 #include <ctype.h>
     58 #include <string.h>
     59 #include <strings.h>
     60 #include <unistd.h>
     61 #include <stdlib.h>
     62 #include <tzfile.h>
     63 
     64 #include "pax.h"
     65 #include "sel_subs.h"
     66 #include "extern.h"
     67 
     68 static int str_sec __P((char *, time_t *));
     69 static int usr_match __P((ARCHD *));
     70 static int grp_match __P((ARCHD *));
     71 static int trng_match __P((ARCHD *));
     72 
     73 static TIME_RNG *trhead = NULL;		/* time range list head */
     74 static TIME_RNG *trtail = NULL;		/* time range list tail */
     75 static USRT **usrtb = NULL;		/* user selection table */
     76 static GRPT **grptb = NULL;		/* group selection table */
     77 
     78 /*
     79  * Routines for selection of archive members
     80  */
     81 
     82 /*
     83  * sel_chk()
     84  *	check if this file matches a specfied uid, gid or time range
     85  * Return:
     86  *	0 if this archive member should be processed, 1 if it should be skipped
     87  */
     88 
     89 #if __STDC__
     90 int
     91 sel_chk(ARCHD *arcn)
     92 #else
     93 int
     94 sel_chk(arcn)
     95 	ARCHD *arcn;
     96 #endif
     97 {
     98 	if (((usrtb != NULL) && usr_match(arcn)) ||
     99 	    ((grptb != NULL) && grp_match(arcn)) ||
    100 	    ((trhead != NULL) && trng_match(arcn)))
    101 		return(1);
    102 	return(0);
    103 }
    104 
    105 /*
    106  * User/group selection routines
    107  *
    108  * Routines to handle user selection of files based on the file uid/gid. To
    109  * add an entry, the user supplies either then name or the uid/gid starting with
    110  * a # on the command line. A \# will eascape the #.
    111  */
    112 
    113 /*
    114  * usr_add()
    115  *	add a user match to the user match hash table
    116  * Return:
    117  *	0 if added ok, -1 otherwise;
    118  */
    119 
    120 #if __STDC__
    121 int
    122 usr_add(char *str)
    123 #else
    124 int
    125 usr_add(str)
    126 	char *str;
    127 #endif
    128 {
    129 	u_int indx;
    130 	USRT *pt;
    131 	struct passwd *pw;
    132 	uid_t uid;
    133 
    134 	/*
    135 	 * create the table if it doesn't exist
    136 	 */
    137 	if ((str == NULL) || (*str == '\0'))
    138 		return(-1);
    139 	if ((usrtb == NULL) &&
    140  	    ((usrtb = (USRT **)calloc(USR_TB_SZ, sizeof(USRT *))) == NULL)) {
    141                 tty_warn(1,
    142 		    "Unable to allocate memory for user selection table");
    143                 return(-1);
    144 	}
    145 
    146 	/*
    147 	 * figure out user spec
    148 	 */
    149 	if (str[0] != '#') {
    150 		/*
    151 		 * it is a user name, \# escapes # as first char in user name
    152 		 */
    153 		if ((str[0] == '\\') && (str[1] == '#'))
    154 			++str;
    155 		if ((pw = getpwnam(str)) == NULL) {
    156                 	tty_warn(1, "Unable to find uid for user: %s", str);
    157                 	return(-1);
    158 		}
    159 		uid = (uid_t)pw->pw_uid;
    160         } else
    161 #		ifdef NET2_STAT
    162 		uid = (uid_t)atoi(str+1);
    163 #		else
    164 		uid = (uid_t)strtoul(str+1, (char **)NULL, 10);
    165 #		endif
    166 	endpwent();
    167 
    168 	/*
    169 	 * hash it and go down the hash chain (if any) looking for it
    170 	 */
    171 	indx = ((unsigned)uid) % USR_TB_SZ;
    172 	if ((pt = usrtb[indx]) != NULL) {
    173                 while (pt != NULL) {
    174                         if (pt->uid == uid)
    175 				return(0);
    176                         pt = pt->fow;
    177                 }
    178 	}
    179 
    180 	/*
    181 	 * uid is not yet in the table, add it to the front of the chain
    182 	 */
    183 	if ((pt = (USRT *)malloc(sizeof(USRT))) != NULL) {
    184 		pt->uid = uid;
    185 		pt->fow = usrtb[indx];
    186 		usrtb[indx] = pt;
    187 		return(0);
    188 	}
    189         tty_warn(1, "User selection table out of memory");
    190         return(-1);
    191 }
    192 
    193 /*
    194  * usr_match()
    195  *	check if this files uid matches a selected uid.
    196  * Return:
    197  *	0 if this archive member should be processed, 1 if it should be skipped
    198  */
    199 
    200 #if __STDC__
    201 static int
    202 usr_match(ARCHD *arcn)
    203 #else
    204 static int
    205 usr_match(arcn)
    206 	ARCHD *arcn;
    207 #endif
    208 {
    209 	USRT *pt;
    210 
    211 	/*
    212 	 * hash and look for it in the table
    213 	 */
    214 	pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ];
    215 	while (pt != NULL) {
    216 		if (pt->uid == arcn->sb.st_uid)
    217 			return(0);
    218 		pt = pt->fow;
    219 	}
    220 
    221 	/*
    222 	 * not found
    223 	 */
    224 	return(1);
    225 }
    226 
    227 /*
    228  * grp_add()
    229  *	add a group match to the group match hash table
    230  * Return:
    231  *	0 if added ok, -1 otherwise;
    232  */
    233 
    234 #if __STDC__
    235 int
    236 grp_add(char *str)
    237 #else
    238 int
    239 grp_add(str)
    240 	char *str;
    241 #endif
    242 {
    243 	u_int indx;
    244 	GRPT *pt;
    245 	struct group *gr;
    246 	gid_t gid;
    247 
    248 	/*
    249 	 * create the table if it doesn't exist
    250 	 */
    251 	if ((str == NULL) || (*str == '\0'))
    252 		return(-1);
    253 	if ((grptb == NULL) &&
    254  	    ((grptb = (GRPT **)calloc(GRP_TB_SZ, sizeof(GRPT *))) == NULL)) {
    255                 tty_warn(1,
    256 		    "Unable to allocate memory fo group selection table");
    257                 return(-1);
    258 	}
    259 
    260 	/*
    261 	 * figure out user spec
    262 	 */
    263 	if (str[0] != '#') {
    264 		/*
    265 		 * it is a group name, \# escapes # as first char in group name
    266 		 */
    267 		if ((str[0] == '\\') && (str[1] == '#'))
    268 			++str;
    269 		if ((gr = getgrnam(str)) == NULL) {
    270                 	tty_warn(1,
    271 			    "Cannot determine gid for group name: %s", str);
    272                 	return(-1);
    273 		}
    274 		gid = (gid_t)gr->gr_gid;
    275         } else
    276 #		ifdef NET2_STAT
    277 		gid = (gid_t)atoi(str+1);
    278 #		else
    279 		gid = (gid_t)strtoul(str+1, (char **)NULL, 10);
    280 #		endif
    281 	endgrent();
    282 
    283 	/*
    284 	 * hash it and go down the hash chain (if any) looking for it
    285 	 */
    286 	indx = ((unsigned)gid) % GRP_TB_SZ;
    287 	if ((pt = grptb[indx]) != NULL) {
    288                 while (pt != NULL) {
    289                         if (pt->gid == gid)
    290 				return(0);
    291                         pt = pt->fow;
    292                 }
    293 	}
    294 
    295 	/*
    296 	 * gid not in the table, add it to the front of the chain
    297 	 */
    298 	if ((pt = (GRPT *)malloc(sizeof(GRPT))) != NULL) {
    299 		pt->gid = gid;
    300 		pt->fow = grptb[indx];
    301 		grptb[indx] = pt;
    302 		return(0);
    303 	}
    304         tty_warn(1, "Group selection table out of memory");
    305         return(-1);
    306 }
    307 
    308 /*
    309  * grp_match()
    310  *	check if this files gid matches a selected gid.
    311  * Return:
    312  *	0 if this archive member should be processed, 1 if it should be skipped
    313  */
    314 
    315 #if __STDC__
    316 static int
    317 grp_match(ARCHD *arcn)
    318 #else
    319 static int
    320 grp_match(arcn)
    321 	ARCHD *arcn;
    322 #endif
    323 {
    324 	GRPT *pt;
    325 
    326 	/*
    327 	 * hash and look for it in the table
    328 	 */
    329 	pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ];
    330 	while (pt != NULL) {
    331 		if (pt->gid == arcn->sb.st_gid)
    332 			return(0);
    333 		pt = pt->fow;
    334 	}
    335 
    336 	/*
    337 	 * not found
    338 	 */
    339 	return(1);
    340 }
    341 
    342 /*
    343  * Time range selection routines
    344  *
    345  * Routines to handle user selection of files based on the modification and/or
    346  * inode change time falling within a specified time range (the non-standard
    347  * -T flag). The user may specify any number of different file time ranges.
    348  * Time ranges are checked one at a time until a match is found (if at all).
    349  * If the file has a mtime (and/or ctime) which lies within one of the time
    350  * ranges, the file is selected. Time ranges may have a lower and/or a upper
    351  * value. These ranges are inclusive. When no time ranges are supplied to pax
    352  * with the -T option, all members in the archive will be selected by the time
    353  * range routines. When only a lower range is supplied, only files with a
    354  * mtime (and/or ctime) equal to or younger are selected. When only a upper
    355  * range is supplied, only files with a mtime (and/or ctime) equal to or older
    356  * are selected. When the lower time range is equal to the upper time range,
    357  * only files with a mtime (or ctime) of exactly that time are selected.
    358  */
    359 
    360 /*
    361  * trng_add()
    362  *	add a time range match to the time range list.
    363  *	This is a non-standard pax option. Lower and upper ranges are in the
    364  *	format: [yy[mm[dd[hh]]]]mm[.ss] and are comma separated.
    365  *	Time ranges are based on current time, so 1234 would specify a time of
    366  *	12:34 today.
    367  * Return:
    368  *	0 if the time range was added to the list, -1 otherwise
    369  */
    370 
    371 #if __STDC__
    372 int
    373 trng_add(char *str)
    374 #else
    375 int
    376 trng_add(str)
    377 	char *str;
    378 #endif
    379 {
    380 	TIME_RNG *pt;
    381 	char *up_pt = NULL;
    382 	char *stpt;
    383 	char *flgpt;
    384 	int dot = 0;
    385 
    386 	/*
    387 	 * throw out the badly formed time ranges
    388 	 */
    389 	if ((str == NULL) || (*str == '\0')) {
    390 		tty_warn(1, "Empty time range string");
    391 		return(-1);
    392 	}
    393 
    394 	/*
    395 	 * locate optional flags suffix /{cm}.
    396 	 */
    397 	if ((flgpt = strrchr(str, '/')) != NULL)
    398 		*flgpt++ = '\0';
    399 
    400 	for (stpt = str; *stpt != '\0'; ++stpt) {
    401 		if ((*stpt >= '0') && (*stpt <= '9'))
    402 			continue;
    403 		if ((*stpt == ',') && (up_pt == NULL)) {
    404 			*stpt = '\0';
    405 			up_pt = stpt + 1;
    406 			dot = 0;
    407 			continue;
    408 		}
    409 
    410 		/*
    411 		 * allow only one dot per range (secs)
    412 		 */
    413 		if ((*stpt == '.') && (!dot)) {
    414 			++dot;
    415 			continue;
    416 		}
    417 		tty_warn(1, "Improperly specified time range: %s", str);
    418 		goto out;
    419 	}
    420 
    421 	/*
    422 	 * allocate space for the time range and store the limits
    423 	 */
    424 	if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) {
    425 		tty_warn(1, "Unable to allocate memory for time range");
    426 		return(-1);
    427 	}
    428 
    429 	/*
    430 	 * by default we only will check file mtime, but usee can specify
    431 	 * mtime, ctime (inode change time) or both.
    432 	 */
    433 	if ((flgpt == NULL) || (*flgpt == '\0'))
    434 		pt->flgs = CMPMTME;
    435 	else {
    436 		pt->flgs = 0;
    437 		while (*flgpt != '\0') {
    438 			switch(*flgpt) {
    439 			case 'M':
    440 			case 'm':
    441 				pt->flgs |= CMPMTME;
    442 				break;
    443 			case 'C':
    444 			case 'c':
    445 				pt->flgs |= CMPCTME;
    446 				break;
    447 			default:
    448 				tty_warn(1, "Bad option %c with time range %s",
    449 				    *flgpt, str);
    450 				goto out;
    451 			}
    452 			++flgpt;
    453 		}
    454 	}
    455 
    456 	/*
    457 	 * start off with the current time
    458 	 */
    459 	pt->low_time = pt->high_time = time((time_t *)NULL);
    460 	if (*str != '\0') {
    461 		/*
    462 		 * add lower limit
    463 		 */
    464 		if (str_sec(str, &(pt->low_time)) < 0) {
    465 			tty_warn(1, "Illegal lower time range %s", str);
    466 			(void)free((char *)pt);
    467 			goto out;
    468 		}
    469 		pt->flgs |= HASLOW;
    470 	}
    471 
    472 	if ((up_pt != NULL) && (*up_pt != '\0')) {
    473 		/*
    474 		 * add upper limit
    475 		 */
    476 		if (str_sec(up_pt, &(pt->high_time)) < 0) {
    477 			tty_warn(1, "Illegal upper time range %s", up_pt);
    478 			(void)free((char *)pt);
    479 			goto out;
    480 		}
    481 		pt->flgs |= HASHIGH;
    482 
    483 		/*
    484 		 * check that the upper and lower do not overlap
    485 		 */
    486 		if (pt->flgs & HASLOW) {
    487 			if (pt->low_time > pt->high_time) {
    488 				tty_warn(1,
    489 				    "Upper %s and lower %s time overlap",
    490 				    up_pt, str);
    491 				(void)free((char *)pt);
    492 				return(-1);
    493 			}
    494 		}
    495 	}
    496 
    497 	pt->fow = NULL;
    498 	if (trhead == NULL) {
    499 		trtail = trhead = pt;
    500 		return(0);
    501 	}
    502 	trtail->fow = pt;
    503 	trtail = pt;
    504 	return(0);
    505 
    506     out:
    507 	tty_warn(1, "Time range format is: [yy[mm[dd[hh]]]]mm[.ss][/[c][m]]");
    508 	return(-1);
    509 }
    510 
    511 /*
    512  * trng_match()
    513  *	check if this files mtime/ctime falls within any supplied time range.
    514  * Return:
    515  *	0 if this archive member should be processed, 1 if it should be skipped
    516  */
    517 
    518 #if __STDC__
    519 static int
    520 trng_match(ARCHD *arcn)
    521 #else
    522 static int
    523 trng_match(arcn)
    524 	ARCHD *arcn;
    525 #endif
    526 {
    527 	TIME_RNG *pt;
    528 
    529 	/*
    530 	 * have to search down the list one at a time looking for a match.
    531 	 * remember time range limits are inclusive.
    532 	 */
    533 	pt = trhead;
    534 	while (pt != NULL) {
    535 		switch(pt->flgs & CMPBOTH) {
    536 		case CMPBOTH:
    537 			/*
    538 			 * user wants both mtime and ctime checked for this
    539 			 * time range
    540 			 */
    541 			if (((pt->flgs & HASLOW) &&
    542 			    (arcn->sb.st_mtime < pt->low_time) &&
    543 			    (arcn->sb.st_ctime < pt->low_time)) ||
    544 			    ((pt->flgs & HASHIGH) &&
    545 			    (arcn->sb.st_mtime > pt->high_time) &&
    546 			    (arcn->sb.st_ctime > pt->high_time))) {
    547 				pt = pt->fow;
    548 				continue;
    549 			}
    550 			break;
    551 		case CMPCTME:
    552 			/*
    553 			 * user wants only ctime checked for this time range
    554 			 */
    555 			if (((pt->flgs & HASLOW) &&
    556 			    (arcn->sb.st_ctime < pt->low_time)) ||
    557 			    ((pt->flgs & HASHIGH) &&
    558 			    (arcn->sb.st_ctime > pt->high_time))) {
    559 				pt = pt->fow;
    560 				continue;
    561 			}
    562 			break;
    563 		case CMPMTME:
    564 		default:
    565 			/*
    566 			 * user wants only mtime checked for this time range
    567 			 */
    568 			if (((pt->flgs & HASLOW) &&
    569 			    (arcn->sb.st_mtime < pt->low_time)) ||
    570 			    ((pt->flgs & HASHIGH) &&
    571 			    (arcn->sb.st_mtime > pt->high_time))) {
    572 				pt = pt->fow;
    573 				continue;
    574 			}
    575 			break;
    576 		}
    577 		break;
    578 	}
    579 
    580 	if (pt == NULL)
    581 		return(1);
    582 	return(0);
    583 }
    584 
    585 /*
    586  * str_sec()
    587  *	Convert a time string in the format of [yy[mm[dd[hh]]]]mm[.ss] to gmt
    588  *	seconds. Tval already has current time loaded into it at entry.
    589  * Return:
    590  *	0 if converted ok, -1 otherwise
    591  */
    592 
    593 #define ATOI2(s)	((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
    594 
    595 #if __STDC__
    596 static int
    597 str_sec(char *str, time_t *tval)
    598 #else
    599 static int
    600 str_sec(str, tval)
    601 	char *str;
    602 	time_t *tval;
    603 #endif
    604 {
    605 	struct tm *lt;
    606 	char *dot = NULL;
    607 	int yearset;
    608 
    609 	lt = localtime(tval);
    610 	if ((dot = strchr(str, '.')) != NULL) {
    611 		/*
    612 		 * seconds (.ss)
    613 		 */
    614 		*dot++ = '\0';
    615 		if (strlen(dot) != 2)
    616 			return(-1);
    617 		lt->tm_sec = ATOI2(dot);
    618 	} else
    619 		lt->tm_sec = 0;
    620 
    621 	yearset = 0;
    622 	switch (strlen(str)) {
    623 	case 12:
    624 		lt->tm_year = ATOI2(str) * 100 - TM_YEAR_BASE;
    625 		yearset = 1;
    626 		/* FALLTHROUGH */
    627 	case 10:
    628 		if (yearset) {
    629 			lt->tm_year += ATOI2(str);
    630 		} else {
    631 			yearset = ATOI2(str);
    632 			if (yearset < 69)
    633 				lt->tm_year = yearset + 2000 - TM_YEAR_BASE;
    634 			else
    635 				lt->tm_year = yearset + 1900 - TM_YEAR_BASE;
    636 		}
    637 		/* FALLTHROUGH */
    638 	case 8:
    639 		lt->tm_mon = ATOI2(str);
    640 		--lt->tm_mon;
    641 		/* FALLTHROUGH */
    642 	case 6:
    643 		lt->tm_mday = ATOI2(str);
    644 		/* FALLTHROUGH */
    645 	case 4:
    646 		lt->tm_hour = ATOI2(str);
    647 		/* FALLTHROUGH */
    648 	case 2:
    649 		lt->tm_min = ATOI2(str);
    650 		break;
    651 	default:
    652 		return(-1);
    653 	}
    654 
    655 	/*
    656 	 * convert broken-down time to GMT clock time seconds
    657 	 */
    658 	if ((*tval = mktime(lt)) == -1)
    659 		return(-1);
    660 	return(0);
    661 }
    662