author | Olivier Brunel
<jjk@jjacky.com> 2015-02-21 13:47:50 UTC |
committer | Olivier Brunel
<jjk@jjacky.com> 2015-04-04 12:47:34 UTC |
parent | 294bb6aa2451a7b4078ceed32068f859fd182113 |
doc/aa-test.pod | +67 | -0 |
package/modes | +1 | -0 |
package/targets.mak | +2 | -0 |
src/utils/aa-test.c | +174 | -0 |
src/utils/deps-exe/aa-test | +2 | -0 |
diff --git a/doc/aa-test.pod b/doc/aa-test.pod new file mode 100644 index 0000000..a1558ad --- /dev/null +++ b/doc/aa-test.pod @@ -0,0 +1,67 @@ +=head1 NAME + +aa-test - Test file types + +=head1 SYNOPSIS + +B<aa-test> [B<-b> | B<-d> | B<-e> | B<-f> | B<-L> | B<-p> | B<-S> | B<-r> | +B<-w> | B<-x>] I<FILE> + +=head1 OPTIONS + +=over + +=item B<-b, --block> + +Test whether I<FILE> exists and is a block special + +=item B<-d, --directory> + +Test whether I<FILE> exists and is a directory + +=item B<-e, --exists> + +Test whether I<FILE> exists + +=item B<-f, --file> + +Test whether I<FILE> exists and is a regular file + +=item B<-h, --help> + +Show help screen and exit. + +=item B<-L, --symlink> + +Test whether I<FILE> exists and is a symlink + +=item B<-p, --pipe> + +Test whether I<FILE> exists and is a named pipe (FIFO) + +=item B<-r, --read> + +Test whether I<FILE> exists and read permission is granted + +=item B<-S, --socket> + +Test whether I<FILE> exists and is a socket + +=item B<-V, --version> + +Show version information and exit. + +=item B<-w, --write> + +Test whether I<FILE> exists and write permission is granted + +=item B<-x, --execute> + +Test whether I<FILE> exists and execute (search) permission is granted + +=back + +=head1 DESCRIPTION + +B<aa-test>(1) is a simple tool to check file types/permissions as specified, and +return 0 when true, else 1. diff --git a/package/modes b/package/modes index a126d1b..a5a2f3c 100644 --- a/package/modes +++ b/package/modes @@ -6,4 +6,5 @@ aa-mount 0755 aa-pivot 0755 aa-start 0755 aa-stop 0755 +aa-test 0755 aa-umount 0755 diff --git a/package/targets.mak b/package/targets.mak index 06f758e..dfb1ecc 100644 --- a/package/targets.mak +++ b/package/targets.mak @@ -9,6 +9,7 @@ aa-echo \ aa-kill \ aa-mount \ aa-pivot \ +aa-test \ aa-umount DOC_TARGETS := \ @@ -21,6 +22,7 @@ aa-mount.1 \ aa-pivot.1 \ aa-start.1 \ aa-stop.1 \ +aa-test.1 \ aa-umount.1 ifdef DO_ALLSTATIC diff --git a/src/utils/aa-test.c b/src/utils/aa-test.c new file mode 100644 index 0000000..e7b820d --- /dev/null +++ b/src/utils/aa-test.c @@ -0,0 +1,174 @@ + +#include <getopt.h> +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <skalibs/strerr2.h> +#include <anopa/common.h> + +static int +is_group_member (gid_t gid) +{ + int nb = getgroups (0, NULL); + gid_t groups[nb]; + int i; + + if (gid == getgid () || gid == getegid()) + return 1; + + if (getgroups (nb, groups) < 0) + return 0; + + for (i = 0; i < nb; ++i) + if (gid == groups[i]) + return 1; + + return 0; +} + +static void +dieusage (int rc) +{ + aa_die_usage (rc, "OPTION FILE", + " -b, --block Test whether FILE is a block special\n" + " -d, --directory Test whether FILE is a directory\n" + " -e, --exists Test whether FILE exists\n" + " -f, --file Test whether FILE is a regular file\n" + " -L, --symlink Test whether FILE is a symbolic link\n" + " -p, --pipe Test whether FILE is a named pipe\n" + " -S, --socket Test whether FILE is a socket\n" + " -r, --read Test for read permission on FILE\n" + " -w, --write Test for write permission on FILE\n" + " -x, --execute Test for execute permission on FILE\n" + "\n" + " -h, --help Show this help screen and exit\n" + " -V, --version Show version information and exit\n" + ); +} + +int +main (int argc, char * const argv[]) +{ + PROG = "aa-test"; + struct stat st; + uid_t euid; + int mode; + char test; + + for (;;) + { + struct option longopts[] = { + { "block", no_argument, NULL, 'b' }, + { "directory", no_argument, NULL, 'd' }, + { "exists", no_argument, NULL, 'e' }, + { "file", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "symlink", no_argument, NULL, 'L' }, + { "pipe", no_argument, NULL, 'p' }, + { "read", no_argument, NULL, 'r' }, + { "socket", no_argument, NULL, 'S' }, + { "version", no_argument, NULL, 'V' }, + { "write", no_argument, NULL, 'w' }, + { "execute", no_argument, NULL, 'x' }, + { NULL, 0, 0, 0 } + }; + int c; + + c = getopt_long (argc, argv, "bdefhLprSVwx", longopts, NULL); + if (c == -1) + break; + switch (c) + { + case 'b': + case 'd': + case 'e': + case 'f': + case 'L': + case 'p': + case 'r': + case 'S': + case 'w': + case 'x': + test = c; + break; + + case 'h': + dieusage (0); + + case 'V': + aa_die_version (); + + default: + dieusage (1); + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + dieusage (1); + + if (lstat (argv[0], &st) < 0) + { + if (errno != ENOENT) + strerr_diefu2sys (1, "stat ", argv[0]); + else + return 1; + } + + switch (test) + { + case 'b': + return (S_ISBLK (st.st_mode)) ? 0 : 1; + + case 'd': + return (S_ISDIR (st.st_mode)) ? 0 : 1; + + case 'e': + return 0; + + case 'f': + return (S_ISREG (st.st_mode)) ? 0 : 1; + + case 'L': + return (S_ISLNK (st.st_mode)) ? 0 : 1; + + case 'p': + return (S_ISFIFO (st.st_mode)) ? 0 : 1; + + case 'r': + mode = R_OK; + break; + + case 'S': + return (S_ISSOCK (st.st_mode)) ? 0 : 1; + + case 'w': + mode = W_OK; + break; + + case 'x': + mode = X_OK; + break; + } + + euid = geteuid (); + if (euid == 0) + { + /* root can read/write any file */ + if (mode != X_OK) + return 0; + /* and execute anything any execute permission set */ + else if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + return 0; + else + return 1; + } + + if (st.st_uid == euid) + mode <<= 6; + else if (is_group_member (st.st_gid)) + mode <<= 3; + + return (st.st_mode & mode) ? 0 : 1; +} diff --git a/src/utils/deps-exe/aa-test b/src/utils/deps-exe/aa-test new file mode 100644 index 0000000..30987b4 --- /dev/null +++ b/src/utils/deps-exe/aa-test @@ -0,0 +1,2 @@ +${LIBANOPA} +-lskarnet