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