18

For homework I have to write a C program and one of the things it has to do is check to see a file exists and if it is executable by the owner.

Using (stat(path[j], &sb) >= 0 I'm able to see if the file indicated by path[j] exists.

I've looked through man pages, a lot of questions and answers on stackoverflow, and several websites but I'm not able to wrap my head around exactly how to check if a file is executable using stat. I thought it would be as simple as ((stat(path[j], &sb) >= 0) && (sb.st_mode > 0) && (S_IEXEC) but as far as I can tell by testing it, it seems to ignore the fact that these files aren't executable.

I think that perhaps stat doesn't work the way I think it does. Assuming I use stat, how can I go about fixing this?

Chris
  • 1,416
  • 18
  • 29
Joseph Kahn
  • 396
  • 1
  • 3
  • 16
  • 2
    `&& (S_IEXEC)` doesn't depend on `sb` at all, what's that supposed to do in your test? – Mat Oct 27 '12 at 08:54
  • 2
    Lookup the bitwise AND operator. You need to use it and S_IXUSR against sb.st_mode – goji Oct 27 '12 at 08:54
  • 1
    Funny that if this was a Python question, everybody would instantly [shout out about how terribly dangerous this is](http://stackoverflow.com/questions/82831/how-do-i-check-if-a-file-exists-using-python) because of a possible race condition if you plan to rely on that result later. If you do (and enjoy keeping it safe), you might want to obtain a lock on the file before calling `stat`. – Kos Oct 27 '12 at 09:13
  • @Mat Ahhhhh that is what I didn't really understand last night. I originally tried to use sb.S_IEXEC and it gave me an error. I didn't realize that was what I was supposed to compare to. – Joseph Kahn Oct 27 '12 at 16:07

3 Answers3

22

You can indeed use stat to do this. You just have to use S_IXUSR (S_IEXEC is an old synonym of S_IXUSR) to check if you have execute permission. Bitwise AND operator (&) checks whether the bits of S_IXUSR are set or not.

if (stat(file, &sb) == 0 && sb.st_mode & S_IXUSR) 
    /* executable */
else  
    /* non-executable */

Example:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc > 1) {
        struct stat sb;
        printf("%s is%s executable.\n", argv[1], stat(argv[1], &sb) == 0 &&
                                                 sb.st_mode & S_IXUSR ? 
                                                 "" : " not");
    }
    return 0;
}   
md5
  • 23,373
  • 3
  • 44
  • 93
  • (sb.st_mode & S_IXUSR) just making sure I got it. So S_IXUSR stores whichever digit of sb.st_mode indicates it's executable surrounded by 0's and the bitwise and will only give a 1 if that particular bit is set to 1? – Joseph Kahn Oct 27 '12 at 16:02
  • 2
    You are right. :) Just note it is possible that *several* bits of `S_IXUSR` are set to 1. – md5 Oct 27 '12 at 16:10
  • Does this also check if the file isn't a directory ? Nevermind, reading docs I found the S_ISDIR macro. – Jerska Dec 29 '13 at 22:39
  • 1
    Correct me if I'm wrong, but this just checks if the file is executable by the file owner right? If the user running "this program" is not the owner of the file, then the check might incorrectly indicate that it is executable. In such a case, the program should also inspect S_IXGRP (if the calling user group is the same as the program group) and S_IXOTH (otherwise). – cheshirekow May 06 '20 at 18:00
4

Try:

((stat(path[j], &sb) >= 0) && (sb.st_mode > 0) && (S_IEXEC & sb.st_mode)

alk
  • 69,737
  • 10
  • 105
  • 255
1

We can leverage the libmagic.so library which comes along with the file(1) utility. It can detect all executable like ELF, bash/python/perl scripts etc

Here's my code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "magic.h"

int
main(int argc, char **argv)
{
   struct magic_set *ms;
   const char *result;
   char *desired;
   size_t desired_len;
   int i;
   FILE *fp;

   ms = magic_open(MAGIC_RAW);
   if (ms == NULL) {
      (void)fprintf(stderr, "ERROR opening MAGIC_NONE: out of memory\n");
      return -1;
   }
   if (magic_load(ms, NULL) == -1) {
      (void)fprintf(stderr, "ERROR loading with NULL file: %s\n", magic_error(ms));
      return 11;
   }

   if (argc > 1) {
      if (argc != 2) {
         (void)fprintf(stderr, "Usage:  ./a.out </path/to/file>\n");
      } else {
         if ((result = magic_file(ms, argv[1])) == NULL) {
            (void)fprintf(stderr, "ERROR loading file %s: %s\n", argv[1], magic_error(ms));
            return -1;
         } else {
             if (strstr(result, (const char *)"executable")) {
                printf("%s: is executable\n", argv[1], result);
             }
         }
      }
   }
   magic_close(ms);
   return 0;
}

$ gcc test.c -I/path/to/magic.h /usr/lib/libmagic.so.1

./a.out /bin/ls

./a.out a.out

./a.out test.c