Commit | Line | Data |
---|---|---|
2f5a0e7a KM |
1 | /*- |
2 | * Copyright (c) 1992 Keith Muller. | |
3 | * Copyright (c) 1992 The Regents of the University of California. | |
4 | * All rights reserved. | |
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 | |
0d8c357e | 13 | static char sccsid[] = "@(#)sel_subs.c 1.2 (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 KM |
355 | /* |
356 | * locate optional flags suffix /{cm}. We only allow a flag suffix(s) | |
357 | * in write and copy (as none of the formats stores inode change time; | |
358 | * currently inode change time cannot be set to a specific value by | |
359 | * any system call). | |
360 | */ | |
361 | if ((flgpt = rindex(str, '/')) != NULL) { | |
362 | *flgpt++ = '\0'; | |
363 | if ((act == LIST) || (act == EXTRACT)) { | |
364 | warn(1,"Time suffix only valid in write or copy modes"); | |
365 | return(-1); | |
366 | } | |
367 | } | |
368 | ||
2f5a0e7a KM |
369 | for (stpt = str; *stpt != '\0'; ++stpt) { |
370 | if ((*stpt >= '0') && (*stpt <= '9')) | |
371 | continue; | |
372 | if ((*stpt == ',') && (up_pt == NULL)) { | |
373 | *stpt = '\0'; | |
374 | up_pt = stpt + 1; | |
375 | dot = 0; | |
376 | continue; | |
377 | } | |
378 | ||
379 | /* | |
380 | * allow only one dot per range (secs) | |
381 | */ | |
382 | if ((*stpt == '.') && (!dot)) { | |
383 | ++dot; | |
384 | continue; | |
385 | } | |
386 | warn(1, "Improperly specified time range: %s", str); | |
387 | goto out; | |
388 | } | |
389 | ||
390 | /* | |
391 | * allocate space for the time range and store the limits | |
392 | */ | |
393 | if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) { | |
394 | warn(1, "Unable to allocate memory for time range"); | |
395 | return(-1); | |
396 | } | |
0d8c357e KM |
397 | |
398 | /* | |
399 | * by default we only will check file mtime, but usee can specify | |
400 | * mtime, ctime (inode change time) or both. | |
401 | */ | |
402 | if ((flgpt == NULL) || (*flgpt == '\0')) | |
403 | pt->flgs = CMPMTME; | |
404 | else { | |
405 | pt->flgs = 0; | |
406 | while (*flgpt != '\0') { | |
407 | switch(*flgpt) { | |
408 | case 'M': | |
409 | case 'm': | |
410 | pt->flgs |= CMPMTME; | |
411 | break; | |
412 | case 'C': | |
413 | case 'c': | |
414 | pt->flgs |= CMPCTME; | |
415 | break; | |
416 | default: | |
417 | warn(1, "Bad option %c with time range %s", | |
418 | *flgpt, str); | |
419 | goto out; | |
420 | } | |
421 | ++flgpt; | |
422 | } | |
423 | } | |
2f5a0e7a KM |
424 | |
425 | /* | |
426 | * start off with the current time | |
427 | */ | |
428 | pt->low_time = pt->high_time = time((time_t *)NULL); | |
429 | if (*str != '\0') { | |
430 | /* | |
431 | * add lower limit | |
432 | */ | |
433 | if (str_sec(str, &(pt->low_time)) < 0) { | |
434 | warn(1, "Illegal lower time range %s", str); | |
435 | (void)free((char *)pt); | |
436 | goto out; | |
437 | } | |
0d8c357e | 438 | pt->flgs |= HASLOW; |
2f5a0e7a KM |
439 | } |
440 | ||
441 | if ((up_pt != NULL) && (*up_pt != '\0')) { | |
442 | /* | |
443 | * add upper limit | |
444 | */ | |
445 | if (str_sec(up_pt, &(pt->high_time)) < 0) { | |
446 | warn(1, "Illegal upper time range %s", up_pt); | |
447 | (void)free((char *)pt); | |
448 | goto out; | |
449 | } | |
0d8c357e | 450 | pt->flgs |= HASHIGH; |
2f5a0e7a KM |
451 | |
452 | /* | |
453 | * check that the upper and lower do not overlap | |
454 | */ | |
0d8c357e | 455 | if (pt->flgs & HASLOW) { |
2f5a0e7a KM |
456 | if (pt->low_time > pt->high_time) { |
457 | warn(1, "Upper %s and lower %s time overlap", | |
458 | up_pt, str); | |
459 | (void)free((char *)pt); | |
460 | return(-1); | |
461 | } | |
462 | } | |
463 | } | |
464 | ||
465 | pt->fow = NULL; | |
466 | if (trhead == NULL) { | |
467 | trtail = trhead = pt; | |
468 | return(0); | |
469 | } | |
470 | trtail->fow = pt; | |
471 | trtail = pt; | |
472 | return(0); | |
0d8c357e | 473 | |
2f5a0e7a | 474 | out: |
0d8c357e | 475 | warn(1, "Time range format is: [yy[mm[dd[hh]]]]mm[.ss][/[c][m]]"); |
2f5a0e7a KM |
476 | return(-1); |
477 | } | |
478 | ||
479 | /* | |
480 | * trng_match() | |
0d8c357e | 481 | * check if this files mtime/ctime falls within any supplied time range. |
2f5a0e7a KM |
482 | * Return: |
483 | * 0 if this archive member should be processed, 1 if it should be skipped | |
484 | */ | |
485 | ||
486 | #if __STDC__ | |
487 | static int | |
488 | trng_match(register ARCHD *arcn) | |
489 | #else | |
490 | static int | |
491 | trng_match(arcn) | |
492 | register ARCHD *arcn; | |
493 | #endif | |
494 | { | |
495 | register TIME_RNG *pt; | |
496 | ||
497 | /* | |
498 | * have to search down the list one at a time looking for a match. | |
499 | * remember time range limits are inclusive. | |
500 | */ | |
501 | pt = trhead; | |
502 | while (pt != NULL) { | |
0d8c357e KM |
503 | switch(pt->flgs & CMPBOTH) { |
504 | case CMPBOTH: | |
505 | /* | |
506 | * user wants both mtime and ctime checked for this | |
507 | * time range | |
508 | */ | |
509 | if (((pt->flgs & HASLOW) && | |
510 | (arcn->sb.st_mtime < pt->low_time) && | |
511 | (arcn->sb.st_ctime < pt->low_time)) || | |
512 | ((pt->flgs & HASHIGH) && | |
513 | (arcn->sb.st_mtime > pt->high_time) && | |
514 | (arcn->sb.st_ctime > pt->high_time))) { | |
2f5a0e7a KM |
515 | pt = pt->fow; |
516 | continue; | |
517 | } | |
0d8c357e KM |
518 | break; |
519 | case CMPCTME: | |
520 | /* | |
521 | * user wants only ctime checked for this time range | |
522 | */ | |
523 | if (((pt->flgs & HASLOW) && | |
524 | (arcn->sb.st_ctime < pt->low_time)) || | |
525 | ((pt->flgs & HASHIGH) && | |
526 | (arcn->sb.st_ctime > pt->high_time))) { | |
527 | pt = pt->fow; | |
528 | continue; | |
529 | } | |
530 | break; | |
531 | case CMPMTME: | |
532 | default: | |
533 | /* | |
534 | * user wants only mtime checked for this time range | |
535 | */ | |
536 | if (((pt->flgs & HASLOW) && | |
537 | (arcn->sb.st_mtime < pt->low_time)) || | |
538 | ((pt->flgs & HASHIGH) && | |
539 | (arcn->sb.st_mtime > pt->high_time))) { | |
2f5a0e7a KM |
540 | pt = pt->fow; |
541 | continue; | |
542 | } | |
0d8c357e | 543 | break; |
2f5a0e7a KM |
544 | } |
545 | break; | |
546 | } | |
547 | ||
548 | if (pt == NULL) | |
549 | return(1); | |
550 | return(0); | |
551 | } | |
552 | ||
553 | /* | |
554 | * str_sec() | |
555 | * Convert a time string in the format of [yy[mm[dd[hh]]]]mm[.ss] to gmt | |
556 | * seconds. Tval already has current time loaded into it at entry. | |
557 | * Return: | |
558 | * 0 if converted ok, -1 otherwise | |
559 | */ | |
560 | ||
561 | #if __STDC__ | |
562 | static int | |
563 | str_sec(register char *str, time_t *tval) | |
564 | #else | |
565 | static int | |
566 | str_sec(str, tval) | |
567 | register char *str; | |
568 | time_t *tval; | |
569 | #endif | |
570 | { | |
571 | register struct tm *lt; | |
572 | register char *dot = NULL; | |
573 | ||
574 | lt = localtime(tval); | |
575 | if ((dot = index(str, '.')) != NULL) { | |
576 | /* | |
577 | * seconds (.ss) | |
578 | */ | |
579 | *dot++ = '\0'; | |
580 | if (strlen(dot) != 2) | |
581 | return(-1); | |
582 | if ((lt->tm_sec = ATOI2(dot)) > 61) | |
583 | return(-1); | |
584 | } else | |
585 | lt->tm_sec = 0; | |
586 | ||
587 | switch (strlen(str)) { | |
588 | case 10: | |
589 | /* | |
590 | * year (yy) | |
591 | * watch out for year 2000 | |
592 | */ | |
593 | if ((lt->tm_year = ATOI2(str)) < 69) | |
594 | lt->tm_year += 100; | |
595 | str += 2; | |
596 | /* FALLTHROUGH */ | |
597 | case 8: | |
598 | /* | |
599 | * month (mm) | |
600 | * watch out months are from 0 - 11 internally | |
601 | */ | |
602 | if ((lt->tm_mon = ATOI2(str)) > 12) | |
603 | return(-1); | |
604 | --lt->tm_mon; | |
605 | str += 2; | |
606 | /* FALLTHROUGH */ | |
607 | case 6: | |
608 | /* | |
609 | * day (dd) | |
610 | */ | |
611 | if ((lt->tm_mday = ATOI2(str)) > 31) | |
612 | return(-1); | |
613 | str += 2; | |
614 | /* FALLTHROUGH */ | |
615 | case 4: | |
616 | /* | |
617 | * hour (hh) | |
618 | */ | |
619 | if ((lt->tm_hour = ATOI2(str)) > 23) | |
620 | return(-1); | |
621 | str += 2; | |
622 | /* FALLTHROUGH */ | |
623 | case 2: | |
624 | /* | |
625 | * minute (mm) | |
626 | */ | |
627 | if ((lt->tm_min = ATOI2(str)) > 59) | |
628 | return(-1); | |
629 | break; | |
630 | default: | |
631 | return(-1); | |
632 | } | |
633 | /* | |
634 | * convert broken-down time to GMT clock time seconds | |
635 | */ | |
636 | if ((*tval = mktime(lt)) == -1) | |
637 | return(-1); | |
638 | return(0); | |
639 | } |