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