xref: /openbsd-src/bin/pax/sel_subs.c (revision 55449a4bb35a049d6de07a109bfa26688f55c824)
1*55449a4bSflorian /*	$OpenBSD: sel_subs.c,v 1.29 2024/04/28 16:43:15 florian Exp $	*/
2df930be7Sderaadt /*	$NetBSD: sel_subs.c,v 1.5 1995/03/21 09:07:42 cgd Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*-
5df930be7Sderaadt  * Copyright (c) 1992 Keith Muller.
6df930be7Sderaadt  * Copyright (c) 1992, 1993
7df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
8df930be7Sderaadt  *
9df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
10df930be7Sderaadt  * Keith Muller of the University of California, San Diego.
11df930be7Sderaadt  *
12df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
13df930be7Sderaadt  * modification, are permitted provided that the following conditions
14df930be7Sderaadt  * are met:
15df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
16df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
17df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
18df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
19df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
2029295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
21df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
22df930be7Sderaadt  *    without specific prior written permission.
23df930be7Sderaadt  *
24df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34df930be7Sderaadt  * SUCH DAMAGE.
35df930be7Sderaadt  */
36df930be7Sderaadt 
37df930be7Sderaadt #include <sys/types.h>
38df930be7Sderaadt #include <sys/stat.h>
39f04e0f38Smillert #include <ctype.h>
40df930be7Sderaadt #include <grp.h>
41f04e0f38Smillert #include <pwd.h>
42df930be7Sderaadt #include <stdio.h>
43df930be7Sderaadt #include <stdlib.h>
44f04e0f38Smillert #include <string.h>
453a135c23Sguenther #include <time.h>
463a135c23Sguenther 
47df930be7Sderaadt #include "pax.h"
48df930be7Sderaadt #include "extern.h"
49df930be7Sderaadt 
503a135c23Sguenther /*
513a135c23Sguenther  * data structure for storing uid/grp selects (-U, -G non standard options)
523a135c23Sguenther  */
533a135c23Sguenther 
543a135c23Sguenther #define USR_TB_SZ	317		/* user selection table size */
553a135c23Sguenther #define GRP_TB_SZ	317		/* user selection table size */
563a135c23Sguenther 
573a135c23Sguenther typedef struct usrt {
583a135c23Sguenther 	uid_t uid;
593a135c23Sguenther 	struct usrt *fow;		/* next uid */
603a135c23Sguenther } USRT;
613a135c23Sguenther 
623a135c23Sguenther typedef struct grpt {
633a135c23Sguenther 	gid_t gid;
643a135c23Sguenther 	struct grpt *fow;		/* next gid */
653a135c23Sguenther } GRPT;
663a135c23Sguenther 
673a135c23Sguenther /*
683a135c23Sguenther  * data structure for storing user supplied time ranges (-T option)
693a135c23Sguenther  */
703a135c23Sguenther 
713a135c23Sguenther #define ATOI2(ar)	((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
723a135c23Sguenther 
733a135c23Sguenther typedef struct time_rng {
743a135c23Sguenther 	time_t		low_time;	/* lower inclusive time limit */
753a135c23Sguenther 	time_t		high_time;	/* higher inclusive time limit */
763a135c23Sguenther 	int		flgs;		/* option flags */
773a135c23Sguenther #define	HASLOW		0x01		/* has lower time limit */
783a135c23Sguenther #define HASHIGH		0x02		/* has higher time limit */
793a135c23Sguenther #define CMPMTME		0x04		/* compare file modification time */
803a135c23Sguenther #define CMPCTME		0x08		/* compare inode change time */
813a135c23Sguenther #define CMPBOTH	(CMPMTME|CMPCTME)	/* compare inode and mod time */
823a135c23Sguenther 	struct time_rng	*fow;		/* next pattern */
833a135c23Sguenther } TIME_RNG;
843a135c23Sguenther 
857097cf92Smillert static int str_sec(const char *, time_t *);
86be87792eSmillert static int usr_match(ARCHD *);
87be87792eSmillert static int grp_match(ARCHD *);
88be87792eSmillert static int trng_match(ARCHD *);
89df930be7Sderaadt 
90df930be7Sderaadt static TIME_RNG *trhead = NULL;		/* time range list head */
91df930be7Sderaadt static TIME_RNG *trtail = NULL;		/* time range list tail */
92df930be7Sderaadt static USRT **usrtb = NULL;		/* user selection table */
93df930be7Sderaadt static GRPT **grptb = NULL;		/* group selection table */
94df930be7Sderaadt 
95df930be7Sderaadt /*
96df930be7Sderaadt  * Routines for selection of archive members
97df930be7Sderaadt  */
98df930be7Sderaadt 
99df930be7Sderaadt /*
100df930be7Sderaadt  * sel_chk()
10108cab283Sjmc  *	check if this file matches a specified uid, gid or time range
102df930be7Sderaadt  * Return:
103df930be7Sderaadt  *	0 if this archive member should be processed, 1 if it should be skipped
104df930be7Sderaadt  */
105df930be7Sderaadt 
106df930be7Sderaadt int
sel_chk(ARCHD * arcn)107be87792eSmillert sel_chk(ARCHD *arcn)
108df930be7Sderaadt {
109df930be7Sderaadt 	if (((usrtb != NULL) && usr_match(arcn)) ||
110df930be7Sderaadt 	    ((grptb != NULL) && grp_match(arcn)) ||
111df930be7Sderaadt 	    ((trhead != NULL) && trng_match(arcn)))
112df930be7Sderaadt 		return(1);
113df930be7Sderaadt 	return(0);
114df930be7Sderaadt }
115df930be7Sderaadt 
116df930be7Sderaadt /*
117df930be7Sderaadt  * User/group selection routines
118df930be7Sderaadt  *
119df930be7Sderaadt  * Routines to handle user selection of files based on the file uid/gid. To
1204eb0b000Smillert  * add an entry, the user supplies either the name or the uid/gid starting with
1214eb0b000Smillert  * a # on the command line. A \# will escape the #.
122df930be7Sderaadt  */
123df930be7Sderaadt 
124df930be7Sderaadt /*
125df930be7Sderaadt  * usr_add()
126df930be7Sderaadt  *	add a user match to the user match hash table
127df930be7Sderaadt  * Return:
128df930be7Sderaadt  *	0 if added ok, -1 otherwise;
129df930be7Sderaadt  */
130df930be7Sderaadt 
131df930be7Sderaadt int
usr_add(char * str)132be87792eSmillert usr_add(char *str)
133df930be7Sderaadt {
134be87792eSmillert 	u_int indx;
135be87792eSmillert 	USRT *pt;
136be87792eSmillert 	uid_t uid;
137df930be7Sderaadt 
138df930be7Sderaadt 	/*
139df930be7Sderaadt 	 * create the table if it doesn't exist
140df930be7Sderaadt 	 */
141df930be7Sderaadt 	if ((str == NULL) || (*str == '\0'))
142df930be7Sderaadt 		return(-1);
143df930be7Sderaadt 	if ((usrtb == NULL) &&
144f38a3474Sguenther 	    ((usrtb = calloc(USR_TB_SZ, sizeof(USRT *))) == NULL)) {
14542cf9836Stholo 		paxwarn(1, "Unable to allocate memory for user selection table");
146df930be7Sderaadt 		return(-1);
147df930be7Sderaadt 	}
148df930be7Sderaadt 
149df930be7Sderaadt 	/*
150df930be7Sderaadt 	 * figure out user spec
151df930be7Sderaadt 	 */
152df930be7Sderaadt 	if (str[0] != '#') {
153df930be7Sderaadt 		/*
154df930be7Sderaadt 		 * it is a user name, \# escapes # as first char in user name
155df930be7Sderaadt 		 */
156df930be7Sderaadt 		if ((str[0] == '\\') && (str[1] == '#'))
157df930be7Sderaadt 			++str;
1586ed32900Sderaadt 		if (uid_from_user(str, &uid) == -1) {
15942cf9836Stholo 			paxwarn(1, "Unable to find uid for user: %s", str);
160df930be7Sderaadt 			return(-1);
161df930be7Sderaadt 		}
162df930be7Sderaadt 	} else
1634bbb0cd1Skstailey 		uid = (uid_t)strtoul(str+1, NULL, 10);
164df930be7Sderaadt 	endpwent();
165df930be7Sderaadt 
166df930be7Sderaadt 	/*
167df930be7Sderaadt 	 * hash it and go down the hash chain (if any) looking for it
168df930be7Sderaadt 	 */
169df930be7Sderaadt 	indx = ((unsigned)uid) % USR_TB_SZ;
170df930be7Sderaadt 	if ((pt = usrtb[indx]) != NULL) {
171df930be7Sderaadt 		while (pt != NULL) {
172df930be7Sderaadt 			if (pt->uid == uid)
173df930be7Sderaadt 				return(0);
174df930be7Sderaadt 			pt = pt->fow;
175df930be7Sderaadt 		}
176df930be7Sderaadt 	}
177df930be7Sderaadt 
178df930be7Sderaadt 	/*
179df930be7Sderaadt 	 * uid is not yet in the table, add it to the front of the chain
180df930be7Sderaadt 	 */
181f38a3474Sguenther 	if ((pt = malloc(sizeof(USRT))) != NULL) {
182df930be7Sderaadt 		pt->uid = uid;
183df930be7Sderaadt 		pt->fow = usrtb[indx];
184df930be7Sderaadt 		usrtb[indx] = pt;
185df930be7Sderaadt 		return(0);
186df930be7Sderaadt 	}
18742cf9836Stholo 	paxwarn(1, "User selection table out of memory");
188df930be7Sderaadt 	return(-1);
189df930be7Sderaadt }
190df930be7Sderaadt 
191df930be7Sderaadt /*
192df930be7Sderaadt  * usr_match()
193df930be7Sderaadt  *	check if this files uid matches a selected uid.
194df930be7Sderaadt  * Return:
195df930be7Sderaadt  *	0 if this archive member should be processed, 1 if it should be skipped
196df930be7Sderaadt  */
197df930be7Sderaadt 
198df930be7Sderaadt static int
usr_match(ARCHD * arcn)199be87792eSmillert usr_match(ARCHD *arcn)
200df930be7Sderaadt {
201be87792eSmillert 	USRT *pt;
202df930be7Sderaadt 
203df930be7Sderaadt 	/*
204df930be7Sderaadt 	 * hash and look for it in the table
205df930be7Sderaadt 	 */
206df930be7Sderaadt 	pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ];
207df930be7Sderaadt 	while (pt != NULL) {
208df930be7Sderaadt 		if (pt->uid == arcn->sb.st_uid)
209df930be7Sderaadt 			return(0);
210df930be7Sderaadt 		pt = pt->fow;
211df930be7Sderaadt 	}
212df930be7Sderaadt 
213df930be7Sderaadt 	/*
214df930be7Sderaadt 	 * not found
215df930be7Sderaadt 	 */
216df930be7Sderaadt 	return(1);
217df930be7Sderaadt }
218df930be7Sderaadt 
219df930be7Sderaadt /*
220df930be7Sderaadt  * grp_add()
221df930be7Sderaadt  *	add a group match to the group match hash table
222df930be7Sderaadt  * Return:
223df930be7Sderaadt  *	0 if added ok, -1 otherwise;
224df930be7Sderaadt  */
225df930be7Sderaadt 
226df930be7Sderaadt int
grp_add(char * str)227be87792eSmillert grp_add(char *str)
228df930be7Sderaadt {
229be87792eSmillert 	u_int indx;
230be87792eSmillert 	GRPT *pt;
231be87792eSmillert 	gid_t gid;
232df930be7Sderaadt 
233df930be7Sderaadt 	/*
234df930be7Sderaadt 	 * create the table if it doesn't exist
235df930be7Sderaadt 	 */
236df930be7Sderaadt 	if ((str == NULL) || (*str == '\0'))
237df930be7Sderaadt 		return(-1);
238df930be7Sderaadt 	if ((grptb == NULL) &&
239f38a3474Sguenther 	    ((grptb = calloc(GRP_TB_SZ, sizeof(GRPT *))) == NULL)) {
24042cf9836Stholo 		paxwarn(1, "Unable to allocate memory fo group selection table");
241df930be7Sderaadt 		return(-1);
242df930be7Sderaadt 	}
243df930be7Sderaadt 
244df930be7Sderaadt 	/*
245f84583feSmillert 	 * figure out group spec
246df930be7Sderaadt 	 */
247df930be7Sderaadt 	if (str[0] != '#') {
248df930be7Sderaadt 		/*
249df930be7Sderaadt 		 * it is a group name, \# escapes # as first char in group name
250df930be7Sderaadt 		 */
251df930be7Sderaadt 		if ((str[0] == '\\') && (str[1] == '#'))
252df930be7Sderaadt 			++str;
2536ed32900Sderaadt 		if (gid_from_group(str, &gid) == -1) {
25442cf9836Stholo 			paxwarn(1,"Cannot determine gid for group name: %s", str);
255df930be7Sderaadt 			return(-1);
256df930be7Sderaadt 		}
257df930be7Sderaadt 	} else
2584bbb0cd1Skstailey 		gid = (gid_t)strtoul(str+1, NULL, 10);
259df930be7Sderaadt 	endgrent();
260df930be7Sderaadt 
261df930be7Sderaadt 	/*
262df930be7Sderaadt 	 * hash it and go down the hash chain (if any) looking for it
263df930be7Sderaadt 	 */
264df930be7Sderaadt 	indx = ((unsigned)gid) % GRP_TB_SZ;
265df930be7Sderaadt 	if ((pt = grptb[indx]) != NULL) {
266df930be7Sderaadt 		while (pt != NULL) {
267df930be7Sderaadt 			if (pt->gid == gid)
268df930be7Sderaadt 				return(0);
269df930be7Sderaadt 			pt = pt->fow;
270df930be7Sderaadt 		}
271df930be7Sderaadt 	}
272df930be7Sderaadt 
273df930be7Sderaadt 	/*
274df930be7Sderaadt 	 * gid not in the table, add it to the front of the chain
275df930be7Sderaadt 	 */
276f38a3474Sguenther 	if ((pt = malloc(sizeof(GRPT))) != NULL) {
277df930be7Sderaadt 		pt->gid = gid;
278df930be7Sderaadt 		pt->fow = grptb[indx];
279df930be7Sderaadt 		grptb[indx] = pt;
280df930be7Sderaadt 		return(0);
281df930be7Sderaadt 	}
28242cf9836Stholo 	paxwarn(1, "Group selection table out of memory");
283df930be7Sderaadt 	return(-1);
284df930be7Sderaadt }
285df930be7Sderaadt 
286df930be7Sderaadt /*
287df930be7Sderaadt  * grp_match()
288df930be7Sderaadt  *	check if this files gid matches a selected gid.
289df930be7Sderaadt  * Return:
290df930be7Sderaadt  *	0 if this archive member should be processed, 1 if it should be skipped
291df930be7Sderaadt  */
292df930be7Sderaadt 
293df930be7Sderaadt static int
grp_match(ARCHD * arcn)294be87792eSmillert grp_match(ARCHD *arcn)
295df930be7Sderaadt {
296be87792eSmillert 	GRPT *pt;
297df930be7Sderaadt 
298df930be7Sderaadt 	/*
299df930be7Sderaadt 	 * hash and look for it in the table
300df930be7Sderaadt 	 */
301df930be7Sderaadt 	pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ];
302df930be7Sderaadt 	while (pt != NULL) {
303df930be7Sderaadt 		if (pt->gid == arcn->sb.st_gid)
304df930be7Sderaadt 			return(0);
305df930be7Sderaadt 		pt = pt->fow;
306df930be7Sderaadt 	}
307df930be7Sderaadt 
308df930be7Sderaadt 	/*
309df930be7Sderaadt 	 * not found
310df930be7Sderaadt 	 */
311df930be7Sderaadt 	return(1);
312df930be7Sderaadt }
313df930be7Sderaadt 
314df930be7Sderaadt /*
315df930be7Sderaadt  * Time range selection routines
316df930be7Sderaadt  *
317df930be7Sderaadt  * Routines to handle user selection of files based on the modification and/or
318df930be7Sderaadt  * inode change time falling within a specified time range (the non-standard
319df930be7Sderaadt  * -T flag). The user may specify any number of different file time ranges.
320df930be7Sderaadt  * Time ranges are checked one at a time until a match is found (if at all).
321df930be7Sderaadt  * If the file has a mtime (and/or ctime) which lies within one of the time
322df930be7Sderaadt  * ranges, the file is selected. Time ranges may have a lower and/or a upper
323df930be7Sderaadt  * value. These ranges are inclusive. When no time ranges are supplied to pax
324df930be7Sderaadt  * with the -T option, all members in the archive will be selected by the time
325df930be7Sderaadt  * range routines. When only a lower range is supplied, only files with a
326df930be7Sderaadt  * mtime (and/or ctime) equal to or younger are selected. When only a upper
327df930be7Sderaadt  * range is supplied, only files with a mtime (and/or ctime) equal to or older
328df930be7Sderaadt  * are selected. When the lower time range is equal to the upper time range,
329df930be7Sderaadt  * only files with a mtime (or ctime) of exactly that time are selected.
330df930be7Sderaadt  */
331df930be7Sderaadt 
332df930be7Sderaadt /*
333df930be7Sderaadt  * trng_add()
334df930be7Sderaadt  *	add a time range match to the time range list.
335df930be7Sderaadt  *	This is a non-standard pax option. Lower and upper ranges are in the
336f04e0f38Smillert  *	format: [[[[[cc]yy]mm]dd]HH]MM[.SS] and are comma separated.
337df930be7Sderaadt  *	Time ranges are based on current time, so 1234 would specify a time of
338df930be7Sderaadt  *	12:34 today.
339df930be7Sderaadt  * Return:
340df930be7Sderaadt  *	0 if the time range was added to the list, -1 otherwise
341df930be7Sderaadt  */
342df930be7Sderaadt 
343df930be7Sderaadt int
trng_add(char * str)344be87792eSmillert trng_add(char *str)
345df930be7Sderaadt {
346be87792eSmillert 	TIME_RNG *pt;
347be87792eSmillert 	char *up_pt = NULL;
348be87792eSmillert 	char *stpt;
349be87792eSmillert 	char *flgpt;
350be87792eSmillert 	int dot = 0;
351df930be7Sderaadt 
352df930be7Sderaadt 	/*
353df930be7Sderaadt 	 * throw out the badly formed time ranges
354df930be7Sderaadt 	 */
355df930be7Sderaadt 	if ((str == NULL) || (*str == '\0')) {
35642cf9836Stholo 		paxwarn(1, "Empty time range string");
357df930be7Sderaadt 		return(-1);
358df930be7Sderaadt 	}
359df930be7Sderaadt 
360df930be7Sderaadt 	/*
361df930be7Sderaadt 	 * locate optional flags suffix /{cm}.
362df930be7Sderaadt 	 */
363df930be7Sderaadt 	if ((flgpt = strrchr(str, '/')) != NULL)
364df930be7Sderaadt 		*flgpt++ = '\0';
365df930be7Sderaadt 
366df930be7Sderaadt 	for (stpt = str; *stpt != '\0'; ++stpt) {
367df930be7Sderaadt 		if ((*stpt >= '0') && (*stpt <= '9'))
368df930be7Sderaadt 			continue;
369df930be7Sderaadt 		if ((*stpt == ',') && (up_pt == NULL)) {
370df930be7Sderaadt 			*stpt = '\0';
371df930be7Sderaadt 			up_pt = stpt + 1;
372df930be7Sderaadt 			dot = 0;
373df930be7Sderaadt 			continue;
374df930be7Sderaadt 		}
375df930be7Sderaadt 
376df930be7Sderaadt 		/*
377df930be7Sderaadt 		 * allow only one dot per range (secs)
378df930be7Sderaadt 		 */
379df930be7Sderaadt 		if ((*stpt == '.') && (!dot)) {
380df930be7Sderaadt 			++dot;
381df930be7Sderaadt 			continue;
382df930be7Sderaadt 		}
38342cf9836Stholo 		paxwarn(1, "Improperly specified time range: %s", str);
384df930be7Sderaadt 		goto out;
385df930be7Sderaadt 	}
386df930be7Sderaadt 
387df930be7Sderaadt 	/*
388df930be7Sderaadt 	 * allocate space for the time range and store the limits
389df930be7Sderaadt 	 */
390f38a3474Sguenther 	if ((pt = malloc(sizeof(TIME_RNG))) == NULL) {
39142cf9836Stholo 		paxwarn(1, "Unable to allocate memory for time range");
392df930be7Sderaadt 		return(-1);
393df930be7Sderaadt 	}
394df930be7Sderaadt 
395df930be7Sderaadt 	/*
396ec95929aSmillert 	 * by default we only will check file mtime, but user can specify
397df930be7Sderaadt 	 * mtime, ctime (inode change time) or both.
398df930be7Sderaadt 	 */
399df930be7Sderaadt 	if ((flgpt == NULL) || (*flgpt == '\0'))
400df930be7Sderaadt 		pt->flgs = CMPMTME;
401df930be7Sderaadt 	else {
402df930be7Sderaadt 		pt->flgs = 0;
403df930be7Sderaadt 		while (*flgpt != '\0') {
404df930be7Sderaadt 			switch (*flgpt) {
405df930be7Sderaadt 			case 'M':
406df930be7Sderaadt 			case 'm':
407df930be7Sderaadt 				pt->flgs |= CMPMTME;
408df930be7Sderaadt 				break;
409df930be7Sderaadt 			case 'C':
410df930be7Sderaadt 			case 'c':
411df930be7Sderaadt 				pt->flgs |= CMPCTME;
412df930be7Sderaadt 				break;
413df930be7Sderaadt 			default:
41442cf9836Stholo 				paxwarn(1, "Bad option %c with time range %s",
415df930be7Sderaadt 				    *flgpt, str);
416f38a3474Sguenther 				free(pt);
417df930be7Sderaadt 				goto out;
418df930be7Sderaadt 			}
419df930be7Sderaadt 			++flgpt;
420df930be7Sderaadt 		}
421df930be7Sderaadt 	}
422df930be7Sderaadt 
423df930be7Sderaadt 	/*
424df930be7Sderaadt 	 * start off with the current time
425df930be7Sderaadt 	 */
4264bbb0cd1Skstailey 	pt->low_time = pt->high_time = time(NULL);
427df930be7Sderaadt 	if (*str != '\0') {
428df930be7Sderaadt 		/*
429df930be7Sderaadt 		 * add lower limit
430df930be7Sderaadt 		 */
431df930be7Sderaadt 		if (str_sec(str, &(pt->low_time)) < 0) {
43242cf9836Stholo 			paxwarn(1, "Illegal lower time range %s", str);
433f38a3474Sguenther 			free(pt);
434df930be7Sderaadt 			goto out;
435df930be7Sderaadt 		}
436df930be7Sderaadt 		pt->flgs |= HASLOW;
437df930be7Sderaadt 	}
438df930be7Sderaadt 
439df930be7Sderaadt 	if ((up_pt != NULL) && (*up_pt != '\0')) {
440df930be7Sderaadt 		/*
441df930be7Sderaadt 		 * add upper limit
442df930be7Sderaadt 		 */
443df930be7Sderaadt 		if (str_sec(up_pt, &(pt->high_time)) < 0) {
44442cf9836Stholo 			paxwarn(1, "Illegal upper time range %s", up_pt);
445f38a3474Sguenther 			free(pt);
446df930be7Sderaadt 			goto out;
447df930be7Sderaadt 		}
448df930be7Sderaadt 		pt->flgs |= HASHIGH;
449df930be7Sderaadt 
450df930be7Sderaadt 		/*
451df930be7Sderaadt 		 * check that the upper and lower do not overlap
452df930be7Sderaadt 		 */
453df930be7Sderaadt 		if (pt->flgs & HASLOW) {
454df930be7Sderaadt 			if (pt->low_time > pt->high_time) {
45542cf9836Stholo 				paxwarn(1, "Upper %s and lower %s time overlap",
456df930be7Sderaadt 					up_pt, str);
457f38a3474Sguenther 				free(pt);
458df930be7Sderaadt 				return(-1);
459df930be7Sderaadt 			}
460df930be7Sderaadt 		}
461df930be7Sderaadt 	}
462df930be7Sderaadt 
463df930be7Sderaadt 	pt->fow = NULL;
464df930be7Sderaadt 	if (trhead == NULL) {
465df930be7Sderaadt 		trtail = trhead = pt;
466df930be7Sderaadt 		return(0);
467df930be7Sderaadt 	}
468df930be7Sderaadt 	trtail->fow = pt;
469df930be7Sderaadt 	trtail = pt;
470df930be7Sderaadt 	return(0);
471df930be7Sderaadt 
472df930be7Sderaadt     out:
473f04e0f38Smillert 	paxwarn(1, "Time range format is: [[[[[cc]yy]mm]dd]HH]MM[.SS][/[c][m]]");
474df930be7Sderaadt 	return(-1);
475df930be7Sderaadt }
476df930be7Sderaadt 
477df930be7Sderaadt /*
478df930be7Sderaadt  * trng_match()
479df930be7Sderaadt  *	check if this files mtime/ctime falls within any supplied time range.
480df930be7Sderaadt  * Return:
481df930be7Sderaadt  *	0 if this archive member should be processed, 1 if it should be skipped
482df930be7Sderaadt  */
483df930be7Sderaadt 
484df930be7Sderaadt static int
trng_match(ARCHD * arcn)485be87792eSmillert trng_match(ARCHD *arcn)
486df930be7Sderaadt {
487be87792eSmillert 	TIME_RNG *pt;
488df930be7Sderaadt 
489df930be7Sderaadt 	/*
490df930be7Sderaadt 	 * have to search down the list one at a time looking for a match.
491df930be7Sderaadt 	 * remember time range limits are inclusive.
492df930be7Sderaadt 	 */
493df930be7Sderaadt 	pt = trhead;
494df930be7Sderaadt 	while (pt != NULL) {
495df930be7Sderaadt 		switch (pt->flgs & CMPBOTH) {
496df930be7Sderaadt 		case CMPBOTH:
497df930be7Sderaadt 			/*
498df930be7Sderaadt 			 * user wants both mtime and ctime checked for this
499df930be7Sderaadt 			 * time range
500df930be7Sderaadt 			 */
501df930be7Sderaadt 			if (((pt->flgs & HASLOW) &&
502df930be7Sderaadt 			    (arcn->sb.st_mtime < pt->low_time) &&
503df930be7Sderaadt 			    (arcn->sb.st_ctime < pt->low_time)) ||
504df930be7Sderaadt 			    ((pt->flgs & HASHIGH) &&
505df930be7Sderaadt 			    (arcn->sb.st_mtime > pt->high_time) &&
506df930be7Sderaadt 			    (arcn->sb.st_ctime > pt->high_time))) {
507df930be7Sderaadt 				pt = pt->fow;
508df930be7Sderaadt 				continue;
509df930be7Sderaadt 			}
510df930be7Sderaadt 			break;
511df930be7Sderaadt 		case CMPCTME:
512df930be7Sderaadt 			/*
513df930be7Sderaadt 			 * user wants only ctime checked for this time range
514df930be7Sderaadt 			 */
515df930be7Sderaadt 			if (((pt->flgs & HASLOW) &&
516df930be7Sderaadt 			    (arcn->sb.st_ctime < pt->low_time)) ||
517df930be7Sderaadt 			    ((pt->flgs & HASHIGH) &&
518df930be7Sderaadt 			    (arcn->sb.st_ctime > pt->high_time))) {
519df930be7Sderaadt 				pt = pt->fow;
520df930be7Sderaadt 				continue;
521df930be7Sderaadt 			}
522df930be7Sderaadt 			break;
523df930be7Sderaadt 		case CMPMTME:
524df930be7Sderaadt 		default:
525df930be7Sderaadt 			/*
526df930be7Sderaadt 			 * user wants only mtime checked for this time range
527df930be7Sderaadt 			 */
528df930be7Sderaadt 			if (((pt->flgs & HASLOW) &&
529df930be7Sderaadt 			    (arcn->sb.st_mtime < pt->low_time)) ||
530df930be7Sderaadt 			    ((pt->flgs & HASHIGH) &&
531df930be7Sderaadt 			    (arcn->sb.st_mtime > pt->high_time))) {
532df930be7Sderaadt 				pt = pt->fow;
533df930be7Sderaadt 				continue;
534df930be7Sderaadt 			}
535df930be7Sderaadt 			break;
536df930be7Sderaadt 		}
537df930be7Sderaadt 		break;
538df930be7Sderaadt 	}
539df930be7Sderaadt 
540df930be7Sderaadt 	if (pt == NULL)
541df930be7Sderaadt 		return(1);
542df930be7Sderaadt 	return(0);
543df930be7Sderaadt }
544df930be7Sderaadt 
545df930be7Sderaadt /*
546df930be7Sderaadt  * str_sec()
547f04e0f38Smillert  *	Convert a time string in the format of [[[[[cc]yy]mm]dd]HH]MM[.SS] to
5484eb0b000Smillert  *	seconds UTC. Tval already has current time loaded into it at entry.
549df930be7Sderaadt  * Return:
550df930be7Sderaadt  *	0 if converted ok, -1 otherwise
551df930be7Sderaadt  */
552df930be7Sderaadt 
553df930be7Sderaadt static int
str_sec(const char * p,time_t * tval)5547097cf92Smillert str_sec(const char *p, time_t *tval)
555df930be7Sderaadt {
556be87792eSmillert 	struct tm *lt;
5577097cf92Smillert 	const char *dot, *t;
5587097cf92Smillert 	size_t len;
559f04e0f38Smillert 	int bigyear;
5607097cf92Smillert 	int yearset;
5617097cf92Smillert 
5627097cf92Smillert 	yearset = 0;
5637097cf92Smillert 	len = strlen(p);
564f04e0f38Smillert 
565f04e0f38Smillert 	for (t = p, dot = NULL; *t; ++t) {
566cd3e3e8cSderaadt 		if (isdigit((unsigned char)*t))
567f04e0f38Smillert 			continue;
568f04e0f38Smillert 		if (*t == '.' && dot == NULL) {
569f04e0f38Smillert 			dot = t;
570f04e0f38Smillert 			continue;
571f04e0f38Smillert 		}
572f04e0f38Smillert 		return(-1);
573f04e0f38Smillert 	}
574df930be7Sderaadt 
575*55449a4bSflorian 	if ((lt = localtime(tval)) == NULL)
576*55449a4bSflorian 		return (-1);
577f04e0f38Smillert 
578f04e0f38Smillert 	if (dot != NULL) {			/* .SS */
5797097cf92Smillert 		if (strlen(++dot) != 2)
580df930be7Sderaadt 			return(-1);
581f04e0f38Smillert 		lt->tm_sec = ATOI2(dot);
582f04e0f38Smillert 		if (lt->tm_sec > 61)
583df930be7Sderaadt 			return(-1);
5847097cf92Smillert 		len -= 3;
585df930be7Sderaadt 	} else
586df930be7Sderaadt 		lt->tm_sec = 0;
587df930be7Sderaadt 
5887097cf92Smillert 	switch (len) {
589f04e0f38Smillert 	case 12:				/* cc */
590f04e0f38Smillert 		bigyear = ATOI2(p);
5910240030dSguenther 		lt->tm_year = (bigyear * 100) - 1900;
592f04e0f38Smillert 		yearset = 1;
593df930be7Sderaadt 		/* FALLTHROUGH */
594f04e0f38Smillert 	case 10:				/* yy */
595f04e0f38Smillert 		if (yearset) {
596f04e0f38Smillert 			lt->tm_year += ATOI2(p);
597f04e0f38Smillert 		} else {
598f04e0f38Smillert 			lt->tm_year = ATOI2(p);
599f04e0f38Smillert 			if (lt->tm_year < 69)		/* hack for 2000 ;-} */
6000240030dSguenther 				lt->tm_year += (2000 - 1900);
601f04e0f38Smillert 		}
602f04e0f38Smillert 		/* FALLTHROUGH */
603f04e0f38Smillert 	case 8:					/* mm */
604f04e0f38Smillert 		lt->tm_mon = ATOI2(p);
605f04e0f38Smillert 		if ((lt->tm_mon > 12) || !lt->tm_mon)
606df930be7Sderaadt 			return(-1);
607f04e0f38Smillert 		--lt->tm_mon;			/* time struct is 0 - 11 */
608df930be7Sderaadt 		/* FALLTHROUGH */
609f04e0f38Smillert 	case 6:					/* dd */
610f04e0f38Smillert 		lt->tm_mday = ATOI2(p);
611f04e0f38Smillert 		if ((lt->tm_mday > 31) || !lt->tm_mday)
612df930be7Sderaadt 			return(-1);
613df930be7Sderaadt 		/* FALLTHROUGH */
614f04e0f38Smillert 	case 4:					/* HH */
615f04e0f38Smillert 		lt->tm_hour = ATOI2(p);
616f04e0f38Smillert 		if (lt->tm_hour > 23)
617df930be7Sderaadt 			return(-1);
618df930be7Sderaadt 		/* FALLTHROUGH */
619f04e0f38Smillert 	case 2:					/* MM */
620f04e0f38Smillert 		lt->tm_min = ATOI2(p);
621f04e0f38Smillert 		if (lt->tm_min > 59)
622df930be7Sderaadt 			return(-1);
623df930be7Sderaadt 		break;
624df930be7Sderaadt 	default:
625df930be7Sderaadt 		return(-1);
626df930be7Sderaadt 	}
627f04e0f38Smillert 
628f04e0f38Smillert 	/* convert broken-down time to UTC clock time seconds */
629df930be7Sderaadt 	if ((*tval = mktime(lt)) == -1)
630df930be7Sderaadt 		return(-1);
631df930be7Sderaadt 	return(0);
632df930be7Sderaadt }
633