/*ident "@(#)Path:ksh/test.c 3.1" */ /* * Code for test * Stolen from ksh * Originally written by David Korn * Modified by me */ /* The values of the enums in ../Path.h * must match the values used in this file. */ #include "defs.h" static MSG e_devfdNN = "/dev/fd/+([0-9])"; #define tio(a,f,id) (_sh_access_Path_ATTLC(a,f,id)==0) static time_t ftime_compare(); static int test_stat(); static int test_inode(); static int test_type(); static int io_access(); static struct stat statb; unop_test_Path_ATTLC(op,arg,id) register int op; register char *arg; register int id; { switch(op) { case 'a': return(tio(arg, F_OK, id)); case 'r': return(tio(arg, R_OK, id)); case 'w': return(tio(arg, W_OK, id)); case 'x': return(tio(arg, X_OK, id)); case 'd': return(test_type(arg,S_IFMT,S_IFDIR)); case 'c': return(test_type(arg,S_IFMT,S_IFCHR)); case 'b': return(test_type(arg,S_IFMT,S_IFBLK)); case 'f': return(test_type(arg,S_IFMT,S_IFREG)); case 'u': return(test_type(arg,S_ISUID,S_ISUID)); case 'g': return(test_type(arg,S_ISGID,S_ISGID)); case 'k': #ifdef S_ISVTX return(test_type(arg,S_ISVTX,S_ISVTX)); #else return(0); #endif /* S_ISVTX */ case 'V': #ifdef FS_3D { struct stat statb; if(lstat(arg,&statb)<0) return(0); return((statb.st_mode&(S_IFMT|S_ISVTX|S_ISUID))==(S_IFDIR|S_ISVTX|S_ISUID)); } #else return(0); #endif /* FS_3D */ case 'L': #ifdef LSTAT { struct stat statb; if(lstat(arg,&statb)<0) return(0); return((statb.st_mode&S_IFMT)==S_IFLNK); } #else return(0); #endif /* S_IFLNK */ case 'S': #ifdef S_IFSOCK return(test_type(arg,S_IFMT,S_IFSOCK)); #else return(0); #endif /* S_IFSOCK */ case 'p': #ifdef S_IFIFO return(test_type(arg,S_IFMT,S_IFIFO)); #else return(0); #endif /* S_IFIFO */ case 's': case 'O': case 'G': { struct stat statb; if(test_stat(arg,&statb)<0) return(0); if(op=='s') return(statb.st_size>0); else if(op=='O') return(statb.st_uid==geteuid()); return(statb.st_gid==getegid()); } case 't': if(isdigit(*arg) && arg[1]==0) return(isatty(*arg-'0')); return(0); } } binop_test_Path_ATTLC(op,left,right,id) char *left, *right; register int op; register int id; { switch(op) { /* op must be one of the following values */ case 0: /* ef */ return(test_inode(left,right)); case 1: /* nt */ return(ftime_compare(left,right)>0); case 2: /* ot */ return(ftime_compare(left,right)<0); } /* NOTREACHED */ } /* * returns the modification time of f1 - modification time of f2 */ static time_t ftime_compare(file1,file2) char *file1,*file2; { struct stat statb1,statb2; if(test_stat(file1,&statb1)<0) statb1.st_mtime = 0; if(test_stat(file2,&statb2)<0) statb2.st_mtime = 0; return(statb1.st_mtime-statb2.st_mtime); } /* * return true if inode of two files are the same */ static test_inode(file1,file2) char *file1,*file2; { struct stat stat1,stat2; if(test_stat(file1,&stat1)>=0 && test_stat(file2,&stat2)>=0) if(stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino) return(1); return(0); } /* /* These are the mode values supplied as argument to access(): /* #define R_OK 4/* test for read permission */ /* #define W_OK 2/* test for write permission */ /* #define X_OK 1/* test for execute (search) permission */ /* #define F_OK 0/* test for presence of file */ /* /* And the status information word st_mode (in the info returned by stat()) /* has the following bits: /* #define S_IFMT 0170000/* type of file */ /* #define S_IFIFO 0010000/* fifo special */ /* #define S_IFCHR 0020000/* character special */ /* #define S_IFDIR 0040000/* directory */ /* #define S_IFBLK 0060000/* block special */ /* #define S_IFREG 0100000/* regular file */ /* #define S_IFLNK 0120000/* symbolic link */ /* #define S_IFSOCK 0140000/* socket */ /* #define S_ISUID 0004000/* set user id on execution */ /* #define S_ISGID 0002000/* set group id on execution */ /* #define S_ISVTX 0001000/* save swapped text even after use */ /* #define S_IREAD 0000400/* read permission, owner */ /* #define S_IWRITE 0000200/* write permission, owner */ /* #define S_IEXEC 0000100/* execute/search permission, owner */ /* /* The mode bits 0000070 and 0000007 encode group and others /* permissions (see chmod(2)). */ /* * This version of access checks against the desired uid/gid. * This version is guaranteed to match access(2) only in those * cases when we're checking against real id other than root. * The static buffer statb is shared with test_type. */ /* The setting of shouldn't make a difference in case * = F_OK, unless access(2) is written screwy. When * = F_OK, _sh_access should return 1 just if the file can be * statted. */ int _sh_access_Path_ATTLC(name, mode, realid) register char *name; register int mode; register int realid; { int uid, gid; if (realid) { uid = getuid(); gid = getgid(); } else { uid = geteuid(); gid = getegid(); } if(strmatch_Path_ATTLC(name,(char*)e_devfdNN)) return(io_access(atoi(name+8),mode)); if(uid != 0 && realid) return(access(name,mode)); if(stat(name, &statb) == 0) { if(mode == F_OK) return(0); else if(uid == 0) { if((statb.st_mode&S_IFMT)!=S_IFREG || mode!=X_OK) return(0); /* root needs execute permission for someone */ mode = (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)); } else if(uid == statb.st_uid) mode <<= 6; else if(gid == statb.st_gid) mode <<= 3; #if defined(NGROUPS) && NGROUPS>0 else { /* in both the real and effective cases, permission is checked against the groups in the group access list. (if you don't believe me, read the intro(2) and access(2) man pages.) */ int groups[NGROUPS]; register int n; n = getgroups(NGROUPS,groups); while(--n >= 0) { if(groups[n] == statb.st_gid) { mode <<= 3; break; } } } #endif /* NGROUPS */ if(statb.st_mode & mode) return(0); } return(-1); } /* * Return true if the mode bits of file corresponding to have * the value equal to . If is null, then the previous stat * buffer is used. */ static test_type(f,mask,field) char *f; int field; { if(f && test_stat(f,&statb)<0) return(0); return((statb.st_mode&mask)==field); } /* * do an fstat() for /dev/fd/n, otherwise stat() */ static int test_stat(f,buff) char *f; struct stat *buff; { if(strmatch_Path_ATTLC(f,(char*)e_devfdNN)) return(fstat(atoi(f+8),buff)); else return(stat(f,buff)); } /* * returns access information on open file * returns -1 for failure, 0 for success * is the same as for access() */ static io_access(fd,mode) register int mode; { register int flags; register struct fileblk *fp; #ifndef F_GETFL struct stat statb; #endif /* F_GETFL */ if(mode==X_OK) return(-1); #ifdef F_GETFL flags = fcntl(fd,F_GETFL,0); #else flags = fstat(fd,&statb); #endif /* F_GETFL */ if(flags < 0) return(-1); #ifdef F_GETFL if(mode==R_OK && (flags&1)) return(-1); if(mode==W_OK && !(flags&3)) return(-1); #endif /* F_GETFL */ return(0); }