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