Files
QNX/lib/util/patmodule.c
2025-08-20 19:02:58 +08:00

865 lines
18 KiB
C

/*
* $QNXLicenseC:
* Copyright 2007, QNX Software Systems. All Rights Reserved.
*
* You must obtain a written license from and pay applicable license fees to QNX
* Software Systems before you may reproduce, modify or distribute this software,
* or any work that includes all or part of this software. Free development
* licenses are available for evaluation and non-commercial purposes. For more
* information visit http://licensing.qnx.com or email licensing@qnx.com.
*
* This file may contain contributions from others. Please review this entire
* file for other proprietary rights or license notices, as well as the QNX
* Development Suite License Guide at http://licensing.qnx.com/license-guide/
* for other information.
* $
*/
/*
------------------------------------------------------------- includes -----
*/
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <malloc.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#ifdef __MINGW32__
#include <lib/compat.h>
#else
#include <pwd.h>
#include <grp.h>
#include <fnmatch.h>
#endif
#include <util/defns.h>
#include <patmodule.h>
#include <util/stdutil.h>
/*
------------------------------------------------------------- text strings --
*/
#define T_INVALID_USER "Unknown user (%s)\n"
#define T_INVALID_GROUP "Unknown group (%s)\n"
#define T_GROUP_TOO_HIGH "Group greater than 32767"
#define T_TOO_MANY_GROUPS "Too many groups specified"
#define T_TOO_MANY_OWNERS "Too many users specified"
#define T_TOO_MANY_PATTERNS "Too many patterns specified"
#define T_TOO_MANY_MODES "Too many modes specified"
#define T_UID_RANGE "Userid not in valid range"
#define T_NUM_PARSE_ERROR "Error parsing number near '%s'\n"
#define T_OUT_OF_RANGE "Number must be < 65536 (%s)\n"
#define T_ILLEGAL_MODE_SPEC "Illegal mode spec '%s'\n"
#define T_NUM_OUT_OF_RANGE "Octal number out of range (%s)\n"
#define TXT(s) s
#define isoctal(c) (c>='0' && c<='7')
#define NOT_CHARACTER ('!')
#define HEAVY TRUE /* means 'if no time specified, assume 23:59:59' */
#define LIGHT FALSE /* means 'if no time specified, assume 00:00:00' */
#define OWNER_FLAG 1 /* in parse_nums, can be owner # or owner name */
#define GROUP_FLAG 2 /* in parse_nums, can be group # or group name */
/*
----------------------------------------------------- structs & typedefs ---
*/
struct modemode
{
unsigned fmode;
bool negative;
};
typedef struct modemode mode;
/*
------------------------------------------------------------- externs ------
*/
//extern char *malloc();
/*
-------------------------------------------------------- globble globbles --
*/
static int all_not_patterns;
static uint16_t numpatterns, numgroups, numowners, nummodes;
static int Maxgroups, Maxpatterns, Maxowners, Maxmodes;
time_t _PM_afterdate, _PM_beforedate; /* NOT STATIC */
static uint16_t *group_table;
static uint16_t *owner_table;
static char **pattern_table;
static mode *mode_table;
/*
----------------------------------------------------------- prototypes -----
*/
#ifdef __STDC__
static int16_t atoo ( char *string );
uint8_t octalorliteral ( char *string );
static uint16_t parse_nums ( uint16_t *table, char *string,
uint16_t *num,char *errstring,
uint16_t max, uint16_t flags);
static int16_t parse_patterns ( char *table[], char *string,
uint16_t *num, bool update_all_not_patterns,
char *errstring, uint16_t max );
static int parse_modes ( mode *table, char *string,
uint16_t *num);
static int ctod ( char *s);
static int comp ( char *s1, char *s2 );
static time_t ascdatetotime ( char *string, int heavy );
#else
extern long ascdatetotime();
extern int16_t parse_patterns();
extern uint16_t parse_nums();
#endif
void
terminate(char *string)
{
fprintf(stderr,"%s",string);
exit(1);
}
/*
----------------------------------------------------- patmodule_init() -------
*/
int patmodule_init( int max_patterns,
int max_groups,
int max_owners,
int max_modes )
{
Maxpatterns = max_patterns;
Maxgroups = max_groups;
Maxowners = max_owners;
Maxmodes = max_modes;
#ifdef DIAG
fprintf(stderr,"Allocating memory for %d groups\n",Maxgroups);
#endif
group_table = malloc(Maxgroups*sizeof(*group_table));
#ifdef DIAG
fprintf(stderr,"Allocating memory for %d owners\n",Maxowners);
#endif
owner_table = malloc(Maxowners*sizeof(*owner_table));
#ifdef DIAG
fprintf(stderr,"Allocating memory for %d patterns\n",Maxpatterns+1);
#endif
pattern_table = malloc(Maxpatterns*sizeof(*pattern_table));
#ifdef DIAG
fprintf(stderr,"Allocating memory for %d modes\n",Maxmodes+1);
#endif
mode_table = malloc(Maxmodes*sizeof(*mode_table));
#ifdef DIAG
fprintf(stderr,"Resetting dates, counts & flags\n");
#endif
_PM_afterdate = _PM_beforedate = -1L;
numgroups = numowners = numpatterns = 0;
all_not_patterns = TRUE;
#ifdef DIAG
fprintf(stderr,"Finished patmodule_init()\n");
#endif
if ((group_table!=NULL) && (owner_table!=NULL) && (pattern_table!=NULL) && (mode_table!=NULL)) return(0);
else return(-1);
}
/*
----------------------------------------------------- patmodule_free() -------
*/
void patmodule_free()
{
free(group_table);
free(owner_table);
free(pattern_table);
free(mode_table);
}
/*
----------------------------------------------------- patmodule_enter() ------
*/
int patmodule_enter(int type, char *opt_arg)
{
/* fprintf(stderr,"type = %d, optarg = '%s'\n",type,optarg); */
switch(type)
{
case P_AFTERDATE: return(((_PM_afterdate=ascdatetotime(opt_arg,HEAVY))==-1)?-1:0);
case P_BEFOREDATE: return(((_PM_beforedate=ascdatetotime(opt_arg,LIGHT))==-1)?-1:0);
case P_OWNER: return(parse_nums(owner_table,opt_arg,&numowners,TXT(T_TOO_MANY_OWNERS),Maxowners,OWNER_FLAG));
case P_GROUP: return(parse_nums(group_table,opt_arg,&numgroups,TXT(T_TOO_MANY_GROUPS),Maxgroups,GROUP_FLAG));
case P_MODE: return(parse_modes(mode_table,opt_arg,&nummodes));
case P_PATTERN: return(parse_patterns(pattern_table,opt_arg,&numpatterns,TRUE,TXT(T_TOO_MANY_PATTERNS),Maxpatterns));
}
return(-1);
}
/*
----------------------------------------------------- patmodule_commit() -----
*/
void patmodule_commit()
{
static char all[2]="*";
if (numpatterns && all_not_patterns)
{
parse_patterns(pattern_table,all,&numpatterns,TRUE,TXT(T_TOO_MANY_PATTERNS),Maxpatterns+1);
}
#ifdef DIAG
{
int i;
for (i=0;i<numpatterns;i++)
{
fprintf(stderr,"pattern[%d] = '%s'\n",i,pattern_table[i]);
}
for (i=0;i<numgroups;i++)
{
fprintf(stderr,"group[%d] = %d\n",i,group_table[i]);
}
for (i=0;i<numowners;i++)
{
fprintf(stderr,"owner[%d] = %d\n",i,owner_table[i]);
}
for (i=0;i<nummodes;i++)
{
fprintf(stderr,"mode[%d] = %s%09o\n",i,mode_table[i].negative?"(not)":"",mode_table[i].fmode);
}
}
#endif
}
/*
----------------------------------------------------- patmodule_check() ------
*/
bool patmodule_check (char *fname, struct stat *statbufp)
{
register char *p;
char match_flag;
int i;
#ifdef DIAG
fprintf(stderr,"ok_to_exec('%s',---)\n",fname);
#endif
/* check file group */
if (numgroups)
{
for (i=0;i<numgroups;i++)
{
if (group_table[i] == statbufp->st_gid) break;
}
if (i==numgroups) return(FALSE);
}
/* check owner */
if (numowners)
{
for (i=0;i<numowners;i++)
{
if (owner_table[i] == statbufp->st_uid) break;
}
if (i==numowners) return(FALSE);
}
/* look at file modified time */
if (_PM_afterdate!=-1L && (statbufp->st_mtime < _PM_afterdate)) return(0);
if (_PM_beforedate!=-1L && (statbufp->st_mtime > _PM_beforedate)) return(0);
#ifdef MATCHDIAG
fprintf(stderr,"fname = '%s'\n",fname);
#endif
/* check patterns */
for(match_flag = FALSE, i = 0 ; i < numpatterns ; ++i)
{
if(*(p = pattern_table[i]) == NOT_CHARACTER) {
if(!fnmatch(p + 1,fname,0)) return(FALSE);
} else {
if(!(fnmatch(p,fname,0)))
{
match_flag = 1;
#ifdef MATCHDIAG
fprintf(stderr,"matched '%s'\n",p);
#endif
}
#ifdef MATCHDIAG
else {
fprintf(stderr,"nomatch '%s'\n",p);
}
#endif
}
}
if (numpatterns && !match_flag) return(0);
#ifdef DIAG
fprintf(stderr,"ok_to_exec: nummodes = %d\n",nummodes);
#endif
{
bool all_negative_modes = TRUE;
for(match_flag=FALSE, i=0; i<nummodes ; ++i)
{
if(mode_table[i].negative) {
#ifdef DIAG
fprintf(stderr,"ok_to_exec: (!) st_mode = 0%o, fmode[%d] = 0%o\n",statbufp->st_mode,i,mode_table[i].fmode);
#endif
if ((statbufp->st_mode & mode_table[i].fmode)==mode_table[i].fmode) return(FALSE);
} else {
#ifdef DIAG
fprintf(stderr,"ok_to_exec: st_mode = 0%o, fmode = 0%o\n",statbufp->st_mode,mode_table[i].fmode);
#endif
all_negative_modes = FALSE;
if ((statbufp->st_mode & mode_table[i].fmode)==mode_table[i].fmode)
{
#ifdef DIAG
fprintf(stderr,"ok_to_exec: MATCH!\n");
#endif
match_flag = TRUE;
}
}
}
return((nummodes == 0) || match_flag || all_negative_modes );
}
}
/*
---------------------------------------------------------- cmd line parsing --
*/
static int16_t atoo(char *string)
{
int16_t sum = 0;
if (!string) return(-1);
for (;isoctal(*string);string++)
{
sum *= 8;
sum += *string - '0';
}
if ((sum<0)||(*string)) return(-1);
else return(sum);
}
uint8_t octalorliteral (char *string)
{
uint8_t rc;
uint16_t numdigit;
/* read in octal sequence */
for ( rc = 0, numdigit=0;
isoctal(*string) && (numdigit<3);
numdigit++
)
{
rc *= (uint8_t) 8;
rc += (uint8_t) (*string++ - '0');
}
/* if it wasn't an octal sequence, take next char literal */
if (!numdigit)
{
rc = *++string;
}
#ifdef DIAG
fprintf(stderr,"Octal %d\n",(int) rc);
#endif
return(rc);
}
static uint16_t parse_nums (
uint16_t *table,
char *string,
uint16_t *num,
char *errstring,
uint16_t max,
uint16_t flags) /* OWNER_FLAG | GROUP_FLAG */
{
char *ptr;
char *item;
bool hex;
unsigned long ltemp;
char *endptr;
/* interpret 0x000 or nnnn decimal */
for(ptr=string;*ptr;)
{
if ((flags==OWNER_FLAG)||(flags==GROUP_FLAG)) {
char *comma;
long result;
comma = strchr(ptr,',');
if (comma!=NULL) *comma='\000';
if (flags==OWNER_FLAG) {
struct passwd *pwtemp;
pwtemp = getpwnam(ptr);
if (!pwtemp) {
if (strspn(ptr,"0123456789x")!=strlen(ptr)) {
fprintf(stderr,TXT(T_INVALID_USER),ptr);
exit(EXIT_FAILURE);
}
} else {
result = (long) pwtemp->pw_uid;
ptr = (comma==NULL) ? strchr(ptr,0) : (comma+1);
if (comma!=NULL) *comma=',';
table[(*num)++] = result;
continue;
}
} else if (flags==GROUP_FLAG) {
struct group *grtemp;
grtemp = getgrnam(ptr);
if (grtemp==NULL) {
if (strspn(ptr,"0123456789x")!=strlen(ptr)) {
fprintf(stderr,TXT(T_INVALID_GROUP),ptr);
exit(EXIT_FAILURE);
}
} else {
result = (long) grtemp->gr_gid;
ptr = (comma==NULL)?strchr(ptr,0):(comma+1);
if (comma!=NULL) *comma=',';
table[(*num)++] = result;
continue;
}
}
if (comma!=NULL) *comma=',';
}
if (hex = ((ptr[0]=='0') && (ptr[1] == 'x')))
ptr+=2;
item=ptr;
for (;hex?isdigit(*ptr):isxdigit(*ptr);ptr++);
if (*ptr && (*ptr!=','))
{
fprintf(stderr,TXT(T_NUM_PARSE_ERROR),item);
errno = EINVAL;
return(-1);
}
if ((*num+1)>max) {
fprintf(stderr,"%s (%d max)\n",errstring,max);
return(-1);
}
if (*ptr) {
*ptr = (char) 0x00;
ptr++; /* null term item, make ptr point past , */
}
if (hex) ltemp = strtoul(item,&endptr,16);
else ltemp = strtoul(item,&endptr,10);
if (errno || (ltemp>65535)) {
fprintf(stderr,TXT(T_OUT_OF_RANGE),item);
errno = EINVAL;
return(-1);
}
table[(*num)++] = ltemp;
}
return(0);
}
static void eliminate_escape(char *string)
{
char *read, *write;
for (read=string,write=string;*read;read++) {
if (*read!='\\'||(*(read+1)&&*(read+1)!=',')) {
*write=*read;
write++;
}
}
*write=0;
}
static int16_t
parse_patterns (char *table[],
char *string,
uint16_t *num,
bool update_all_not_patterns,
char *errstring,
uint16_t max)
{
char *ptr;
char *item;
bool escaped;
for(ptr=string;*ptr;)
{
item = ptr;
for (escaped=0;(*ptr)&&((*ptr!=',')||escaped);ptr++) escaped=(*ptr=='\\');
if ((*num+1)>max) {
fprintf(stderr,"%s (%d max)\n",errstring,max);
return(-1);
}
if (*ptr)
{
*ptr = (char) 0x00;
ptr++;
eliminate_escape(item);
table[(*num)++] = item;
} else {
eliminate_escape(item);
table[(*num)++] = item;
}
if ((update_all_not_patterns)&&(*item!=NOT_CHARACTER))
{
all_not_patterns = FALSE;
}
}
return(0);
}
static int parse_modes (mode *table, char *string, uint16_t *num)
{
char *ptr;
char *temptable[32];
uint16_t tempnum = 0;
int i,i2,factor;
/* call parse_patterns(). Clever, huh? */
if (parse_patterns(temptable,string,&tempnum,FALSE,TXT(T_TOO_MANY_MODES),32)==-1) return(-1);
/* tempnum now has # of items in temptable,
temptable has pointers to the individual mode specifiers */
if ((*num+tempnum)>Maxmodes) {
fprintf (stderr,"%s (%d max)\n",TXT(T_TOO_MANY_MODES),Maxmodes);
return(-1);
}
for (i=0;i<tempnum;i++)
{
ptr = temptable[i];
#ifdef DIAG
fprintf(stderr,"parse_modes: parsing '%s'\n",ptr);
#endif
if (*ptr == NOT_CHARACTER)
{
ptr++;
table[i+*num].negative = TRUE;
} else table[i+*num].negative = FALSE;
if (!*ptr)
{
fprintf(stderr,TXT(T_ILLEGAL_MODE_SPEC),temptable[i]);
exit(0xBAD);
}
/* is it a nonnegative octal number? */
if (isoctal(*ptr))
{
int j;
j = atoo(ptr);
if (j<0)
{
fprintf(stderr,TXT(T_NUM_OUT_OF_RANGE),temptable[i]);
exit(0xBAD);
} else {
#ifdef DIAG
fprintf(stderr,"parse_modes: got octal 0%o\n",j);
#endif
table[i+*num].fmode = j;
}
} else {
/* it is a symbolic type definition */
for (factor=0;*ptr!='=';ptr++)
{
switch(*ptr)
{
case 'u':
factor |= 0100;
break;
case 'g':
factor |= 010;
break;
case 'o':
factor |= 01;
break;
case 'a':
factor = 0111;
break;
default:
fprintf(stderr,TXT(T_ILLEGAL_MODE_SPEC),temptable[i]);
exit(0xBAD);
}
}
/* ptr now points to '=' */
for (++ptr,i2=0;*ptr;ptr++)
{
switch(*ptr)
{
case 'r':
i2 |= S_IROTH;
break;
case 'w':
i2 |= S_IWOTH;
break;
case 'x':
case 's':
i2 |= S_IXOTH;
break;
default:
fprintf(stderr,TXT(T_ILLEGAL_MODE_SPEC),temptable[i]);
exit(0xBAD);
}
}
table[i+*num].fmode = i2*factor;
#ifdef DIAG
fprintf(stderr,"parse_modes: table[%d].fmode = 0%o\n",i+*num,table[i+*num].fmode);
#endif
} /* else */
} /* for */
*num += tempnum;
return(0);
}
#define SKIP_WHITE(ptr) { for(;(*ptr) && iswhite(*ptr);ptr++);}
#define SKIP_TO_COLON(ptr) { for(;(*ptr) && (*ptr!=':');ptr++);}
#define SKIP_TO_WHITE(ptr) { for(;(*ptr) && !iswhite(*ptr);ptr++);}
#define SKIP_TO_SLASH(ptr) { for(;(*ptr) && (*ptr!='/');ptr++);}
#define BREAK_ON_END(ptr) { if(!*ptr) break; *(ptr++)=(char)0x00;}
static time_t ascdatetotime(char *string, bool heavy)
{
bool utc = FALSE;
static char *months[] = {"---", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
"Aug", "Sep", "Oct", "Nov", "Dec", "---", "---", "---"};
static unsigned msize[] ={ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
struct tm tm, *tm2;
time_t lsec;
char am_pm, flg24;
char *ptr = string;
char *day="", *month="", *year="";
char *hour="",*minute="",*second="";
/* check - does it start with 'UTC'? */
if ((!strncmp(ptr,"UTC",3))||(!strncmp(ptr,"utc",3))) {
utc = TRUE;
ptr+=3;
}
/* change default from 0:0:0 to 59:59:59 if heavy */
if (heavy) {
hour = "23";
minute = second = "59";
} else {
hour = minute = second = "0";
}
/* "dd/mm/yy hh:mm:ss" */
do
{
SKIP_WHITE(ptr);
day = ptr;
SKIP_TO_SLASH(ptr);
BREAK_ON_END(ptr);
month = ptr;
SKIP_TO_SLASH(ptr);
BREAK_ON_END(ptr);
year = ptr;
SKIP_TO_WHITE(ptr);
BREAK_ON_END(ptr);
SKIP_WHITE(ptr);
minute = second = NULL;
hour = ptr;
SKIP_TO_COLON(ptr);
BREAK_ON_END(ptr);
minute = ptr;
SKIP_TO_COLON(ptr);
BREAK_ON_END(ptr);
second = ptr;
SKIP_TO_WHITE(ptr);
BREAK_ON_END(ptr);
break;
} while (FALSE);
#ifdef TDIAG
fprintf(stderr,"day = '%s'\n",day);
fprintf(stderr,"month = '%s'\n",month);
fprintf(stderr,"year = '%s'\n",year);
fprintf(stderr,"hour = '%s'\n",hour);
fprintf(stderr,"minute = '%s'\n",minute);
fprintf(stderr,"second = '%s'\n",second);
#endif
if (!utc) {
/* learn whether we are in daylight savings time or not */
lsec = time(NULL);
tm2 = localtime(&lsec);
tm.tm_isdst = tm2->tm_isdst;
} else tm.tm_isdst = 0;
tm.tm_min = 0;
am_pm = 0;
flg24 = 1;
tm.tm_sec = ctod(second);
if(tm.tm_sec > 59)
terminate("Time - Invalid second\n");
tm.tm_min = ctod(minute);
if(tm.tm_min > 59)
terminate("Time - Invalid minute\n");
tm.tm_hour = ctod(hour);
if(tm.tm_hour > 23)
terminate("Time - Invalid hour\n");
if(am_pm && !flg24 && tm.tm_hour < 12)
tm.tm_hour += 12;
if(tm.tm_hour > 11)
am_pm = 1;
tm.tm_year = ctod(year);
if (tm.tm_year>=0 && tm.tm_year<=38) tm.tm_year+=2000; // 00-38 is 2000-2038
if(tm.tm_year >= 1970) tm.tm_year -= 1900;
if(tm.tm_year < 70 || tm.tm_year > 155)
terminate("Date - Invalid year\n");
if(month[0] >= '0' && month[0] <= '9')
{
tm.tm_mon = ctod(month) - 1;
}
else
{
for(tm.tm_mon = 0; tm.tm_mon < 12; ++tm.tm_mon)
if(comp(months[tm.tm_mon + 1], month)) break;
}
if(tm.tm_mon > 11)
terminate("Date - Invalid month\n");
if(tm.tm_mon == 1 && (tm.tm_year % 4) == 2)
msize[2] = 29; /* leap years allow feb 29 */
tm.tm_mday = ctod(day);
if(tm.tm_mday > msize[tm.tm_mon + 1] || tm.tm_mday == 0)
terminate("Date - Invalid day\n");
lsec = mktime(&tm);
if (utc)
#if defined(__CYGWIN32__) || defined(__MINGW32__)
lsec = lsec - _timezone;
#else
lsec = lsec - timezone;
#endif
#ifdef TDIAG
fprintf(stderr,"lsec = %ld\n",lsec);
#endif
return(lsec);
}
static int ctod(char *s)
{
register int i = 0;
register char *p = s;
if (!p) return(0);
while(*p >= '0' && *p <= '9')
i = i * 10 + *p++ - '0';
if(*p != '\0')
terminate("DATE: Numbers may only contain the digits 0-9.\n Separate each (hour, min ...) by a space\n");
return(i);
}
static int comp(char *s1, char *s2)
{
register char *p1 = s1;
register char *p2 = s2;
while(*p1)
if((*p1++ | ' ') != (*p2++ | ' '))
return(0);
return(1);
}