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