I am trying to write a small C program that emulates the unix command
ls -l
. To do so, I am using the stat(2)
syscall and have ran into a small hiccup writing the permissions. I have a mode_t
variable which holds the file permissions from st_mode
, and it wouldn't be hard to parse that value into s string representation, but I was just wondering if there is a better way to be doing it than that.
example from google
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
if(argc != 2)
return 1;
struct stat fileStat;
if(stat(argv[1], &fileStat) < 0)
return 1;
printf("Information for %s\n", argv[1]);
printf("---------------------------\n");
printf("File Size: \t\t%d bytes\n", fileStat.st_size);
printf("Number of Links: \t%d\n", fileStat.st_nlink);
printf("File inode: \t\t%d\n", fileStat.st_ino);
printf("File Permissions: \t");
printf( (S_ISDIR(fileStat.st_mode)) ? "d" : "-");
printf( (fileStat.st_mode & S_IRUSR) ? "r" : "-");
printf( (fileStat.st_mode & S_IWUSR) ? "w" : "-");
printf( (fileStat.st_mode & S_IXUSR) ? "x" : "-");
printf( (fileStat.st_mode & S_IRGRP) ? "r" : "-");
printf( (fileStat.st_mode & S_IWGRP) ? "w" : "-");
printf( (fileStat.st_mode & S_IXGRP) ? "x" : "-");
printf( (fileStat.st_mode & S_IROTH) ? "r" : "-");
printf( (fileStat.st_mode & S_IWOTH) ? "w" : "-");
printf( (fileStat.st_mode & S_IXOTH) ? "x" : "-");
printf("\n\n");
printf("The file %s a symbolic link\n", (S_ISLNK(fileStat.st_mode)) ? "is" : "is not");
return 0;
}
result:
Information for 2.c --------------------------- File Size: 1223 bytes Number of Links: 1 File inode: 39977236 File Permissions: -rw-r--r-- The file is not a symbolic link
stat()
instead of lstat()
, the only time it will report 'symlink' is when the symlink is broken. Otherwise, it will report on the file at the end of the symlink. –
Irredentist The basics are simple enough; the tricky bits are the SUID and SGID bits and the sticky bit, which modify the 'x' bits. Consider splitting the permissions into 3 octal digits for user, group, owner, and using those to index into an array of 3-character strings such as rwx
and ---
. Then adjust the appropriate x
bits based on the other mode bits. The file type will have to be dealt with separately, but you could use a 12-bit shift right (possibly with masking) and a 16 entry table to deal with the 16 possible values (not all of which are valid on any given system). Or you can deal with known types as shown in the code below.
+----+---+---+---+---+
|type|SSS|USR|GRP|OTH|
+----+---+---+---+---+
The 4 types bits, the three S-bits (setuid, setgid, sticky) and the user, group and other bits.
This is code I use for converting mode_t
into a string. It was written for a nicely threadless program, so it uses static data; it would be trivial to modify it to take the output string as an input parameter:
/* Convert a mode field into "ls -l" type perms field. */
static char *lsperms(int mode)
{
static const char *rwx[] = {"---", "--x", "-w-", "-wx",
"r--", "r-x", "rw-", "rwx"};
static char bits[11];
bits[0] = filetypeletter(mode);
strcpy(&bits[1], rwx[(mode >> 6)& 7]);
strcpy(&bits[4], rwx[(mode >> 3)& 7]);
strcpy(&bits[7], rwx[(mode & 7)]);
if (mode & S_ISUID)
bits[3] = (mode & S_IXUSR) ? 's' : 'S';
if (mode & S_ISGID)
bits[6] = (mode & S_IXGRP) ? 's' : 'l';
if (mode & S_ISVTX)
bits[9] = (mode & S_IXOTH) ? 't' : 'T';
bits[10] = '\0';
return(bits);
}
static int filetypeletter(int mode)
{
char c;
if (S_ISREG(mode))
c = '-';
else if (S_ISDIR(mode))
c = 'd';
else if (S_ISBLK(mode))
c = 'b';
else if (S_ISCHR(mode))
c = 'c';
#ifdef S_ISFIFO
else if (S_ISFIFO(mode))
c = 'p';
#endif /* S_ISFIFO */
#ifdef S_ISLNK
else if (S_ISLNK(mode))
c = 'l';
#endif /* S_ISLNK */
#ifdef S_ISSOCK
else if (S_ISSOCK(mode))
c = 's';
#endif /* S_ISSOCK */
#ifdef S_ISDOOR
/* Solaris 2.6, etc. */
else if (S_ISDOOR(mode))
c = 'D';
#endif /* S_ISDOOR */
else
{
/* Unknown type -- possibly a regular file? */
c = '?';
}
return(c);
}
clang -Weverything
and it can be a bit painful at times. I've not actually tried clang -Weverything
directly; it might be less onerous than the options I'm using (about 18 -W*
flags; -Wconversion
is the biggest cause of trouble for my code). –
Irredentist drwx-wx--t
, where ls
shows drwx-wx--T
. For example in case of /var/spool/cron/crontabs/
. –
Tilt I don't like the if/ else if
syntax.
I prefer use the switch
statement. After struggling a bit I found the way we can do it using different macros, for example:
S_ISCHR (mode)
Is equivalent to:
((mode & S_IFMT) == S_IFCHR)
This allows us to build a switch statement like this:
char f_type(mode_t mode)
{
char c;
switch (mode & S_IFMT)
{
case S_IFBLK:
c = 'b';
break;
case S_IFCHR:
c = 'c';
break;
case S_IFDIR:
c = 'd';
break;
case S_IFIFO:
c = 'p';
break;
case S_IFLNK:
c = 'l';
break;
case S_IFREG:
c = '-';
break;
case S_IFSOCK:
c = 's';
break;
default:
c = '?';
break;
}
return (c);
}
Which in my opinion it's a bit more elegant than the if/else if
approach.
gcc -S -masm=intel -O2 -o filemode.asm filemode.c
) –
Incorrect © 2022 - 2024 — McMap. All rights reserved.