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