How to check if two device files are equal in C? - c

I want to check if two device files are equal in C, without accessing the underlying devices.
Can this be done using stat and lstat?

To determine if two device files are the same, call stat on each of them, and check that they're both the same type of device (block or character) and that their .st_rdev members are equal.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(int arc, char **argv)
{
struct stat s1, s2;
char *err;
if (stat(argv[1], &s1) < 0) {
err = strerror(errno);
fprintf(stderr, "Can't stat %s: %s\n", argv[1], err);
exit(1);
}
if (stat(argv[2], &s2) < 0) {
err = strerror(errno);
fprintf(stderr, "Can't stat %s: %s\n", argv[2], err);
exit(1);
}
if (S_ISCHR(s1.st_mode) && S_ISCHR(s2.st_mode) && s1.st_rdev == s2.st_rdev) {
printf("Same char device\n");
exit(0);
}
if (S_ISBLK(s1.st_mode) && S_ISBLK(s2.st_mode) && s1.st_rdev == s2.st_rdev) {
printf("Same block device\n");
exit(0);
}
printf("devices do not match\n");
exit(1);
}

Have you looked at stat structure? You can find the size of a file, and many many things. .st_dev should works in your case.

Related

How to get multiple strings from pointer that stores the output of a shell script in C?

I am trying to write a code in C with named pipes (fifo), where the client is asking for information about a directory.
The server checks for the existence of the directory, and sends back the size of the directory, and the number of files and subdirectories.
The request can also specify to get the name of the files and subdirectories.
The client gets the name of the directory as an argument, also the specification by -d option.
The server executes a shell script in order to solve the problem.
I already asked a question about this topic and got some improvements in the code, but still can't get it running correctly.
Here is the link to the question: How to pass multiple arguments to client (fifo)?
My problem is now that the server prints out only one file name instead of all filenames and subdirectories inside the directory that was given as an argument to the client.
Here is the modified server code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "header.h"
int f;
Message msg;
int main() {
if (mkfifo(FIFONAME, S_IFIFO | 0666) < 0) { /*Creating server fifo*/
perror("Failed creating own fifo");
printf("Server: Failed creating fifo_%d file\n", getpid());
unlink(FIFONAME);
exit(1);
}
if ((f = open(FIFONAME, O_RDONLY)) < 0) {
perror("Failed opening fifo");
unlink(FIFONAME);
exit(1);
}
printf("Server is working\n");
while (1) { /*Infinite loop, waiting for client requests*/
if ((read(f, &msg, sizeof(msg)))) {
if (strcmp(msg.dir, "exit") == 0) {
close(f);
unlink(FIFONAME);
exit(1);
}
switch (fork()) {
case -1: {
perror("Fork error\n");
exit(1);
}
case 0: {
char command[MAXLEN];
sprintf(command,"./shell.sh %s %s", msg.dir, msg.spec);
FILE *g;
if ((g = popen(command, "r")) == NULL) {
perror("Popen error");
exit(1);
}
fgets(msg.dir, MAXLEN, g);
fgets(msg.spec, MAXLEN, g);
char result[MAXLEN];
sprintf(result, "fifo_%d", msg.pid);
msg.pid = getpid();
int op;
op = open(result, O_WRONLY);
write(op, &msg, sizeof(msg));
close(op);
exit(0);
}
}
}
}
return 0;
}
And the client code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "header.h"
int f, fc;
Message msg;
char fifoname[MAXLEN];
int main(int argc, char *argv[]) {
if (argc == 1) {
printf("Usage: %s directory name\n",argv[0]);
exit(1);
}
sprintf(fifoname, "fifo_%d", getpid());
if (strcmp(argv[1], "0"))
if (mkfifo(fifoname, S_IFIFO | 0666) < 0) { /*Creating own FIFO file for result*/
perror("Failed creating own clientfifo");
printf("Client error: Failed creating fifo_%d file\n", getpid());
exit(2);
}
if ((f = open(FIFONAME, O_WRONLY)) < 0) { /*Opening serverfifo for writing*/
perror("Failed connecting to server");
exit(3);
}
strcpy(msg.dir, argv[1]);
strcpy(msg.spec, argv[2]);
msg.pid = getpid();
write(f, &msg, sizeof(msg));
if (strcmp(argv[1], "exit")) { /* The client is not expecting any result
because the server stopped*/
if ((fc = open(fifoname, O_RDONLY)) < 0) { /*Opening own fifo for reading*/
perror("Failed opening own fifo");
printf("Client error: Failed opening own %s file\n", fifoname);
exit(4);
}
read(fc, &msg, sizeof(msg));
printf("Client %d, received: %s%s\n", getpid(), msg.dir, msg.spec);
close(fc);
}
unlink(fifoname);
close(f);
exit(0);
}
The common header file:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#define FIFONAME "server_fifo"
#define MAXLEN 1000
typedef struct {
int pid; /*folyamat azonositoja*/
char dir[MAXLEN];
char spec[MAXLEN];
} Message;
And the output I get is:
-bash-4.1$ ./client dir -d
Client 42723, received: 16K,2 directories, 2 files
a
While it should look like this :
-bash-4.1$ ./client dir -d
Client 42723, received: 16K,2 directories, 2 files
a
b
dir1
dir2
What needs to be modified in order to get the full output?
The problem is at line 52 inside server.c.
You are using fgets() to copy the output to msg.spec.
But fgets() stops taking input at newline charater ('\n').
Hence you only see one result.
To overcome this, you can do something like:
char str[100]; // arbitrary length
while(fgets(str, MAXLEN, g))
{
strcat(msg.spec, str);
}
This keeps taking input every iteration and concatenates each line to previous output.

Get information about a file with c in linux

When I run:
ls -l /proc/123/fd/80
I can see for example:
89-> socket:[38676]
How can I get this information in C? How can I know that this fd point to a socket/pipe and get the number inside the brackets (the 38676 between [ ])
By the way, is the number between brackets the size of buffer's socket?
man fstat will give you all the documentation you need.
The man page gives an example you can easily adapt:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
struct stat sb;
if (argc != 2) {
fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (stat(argv[1], &sb) == -1) {
perror("stat");
exit(EXIT_FAILURE);
}
if (S_IFSOCK != (sb.st_mode & S_IFMT)) {
printf("Not a socket.\n");
exit(EXIT_FAILURE);
}
printf("socket: %ld\n", (long) sb.st_ino);
exit(EXIT_SUCCESS);
}

How to bypass the check of setuid

I am now taking a security class. And the professor asked us to exploit a program to gain higher privilege.
This is the code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#define BUFSIZE 512
int main(int argc, char *argv[]) {
struct stat buf;
char cmd[BUFSIZE];
FILE *f = NULL;
if (argv[1] == NULL) {
fprintf(stderr, "Please provide an argument\n");
exit(1);
}
if (stat(argv[1], &buf)) {
fprintf(stderr, "Can't stat the file\n");
exit(1);
}
if (buf.st_gid != getegid()) {
fprintf(stderr, "The file must be owned by group %d\n", getegid());
exit(1);
}
sleep(1);
if ((f = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, "Cannot open command file\n");
return 1;
}
while (fgets(cmd, BUFSIZE, f)) {
if ((cmd[0] == '\n') || (cmd[0] == 0x7f)) {
fprintf(stderr, "Found empty line, quitting!\n");
return 2;
}
system(cmd);
}
printf("Done!\n");
return 0;
}
The particular situation is:
There is a binary code for this program for us to run. And the SUID of the binary code is set to higher level(here I assume level2). However, buf.st_gid != getegid() will always check if the file we specified is owned by root. Most important thing is, I only have level1 privilege. There is only one file owned by level2 I can use which is provided by our professor. And I certainly tried to pass in that filename. But nothing happens. I don't know if there is anyway I can exploit this program.(To execute some commands during runtime using the privilege of level2)

Confused with the st_ino?

#include "stdio.h"
#include <sys/stat.h>
int
main(int argc, char *argv[]) {
struct stat buf;
//int fd = open("./fstatat.c", "r");
//int fd2 = fstatat(fd, "a.txt", &buf, 0);
//printf("%d\n", buf.st_ino);
stat("./fstatat.c", &buf);
printf("%d\n", buf.st_ino);
return 0;
}
if i use the function stat to get a struct stat, the st_ino is the same as the i-node number with the ls -i.
1305609
[inmove#localhost chapter-four]$ ls -i
1305607 a.txt 1305606 fstatat.bin 1305609 fstatat.c 1305605 tmp.txt
buf if i use the function fstat, the st_ino is always the 4195126.
anyone can tell me why this happen?
The problem is that you are not using open correctly and don't check the return values for errors. So you are then calling fstat on the invalid file descriptor value -1 returned by open on error, which will also fail and not touch buf at all, so the uninitialized garbage in the struct is still there (4195126, hex 0x400336 smells a lot like a return address of a previous function call still being on the stack or something like this.)
As davmac already pointed out, the second parameter to open must be a list of flags, which are numeric. Check the docs.
So, the correct code would be:
#include "stdio.h"
#include <sys/stat.h>
#include <sys/fcntl.h> // for the O_RDONLY constant
#include <errno.h> // for error output
int main(int argc, char *argv[]) {
struct stat buf;
int fd = open("./fstatat.c", O_RDONLY);
if(fd == -1) {
printf("Error calling open: %s\n", strerror(errno));
} else {
if(fstat(fd, &buf) == -1) {
printf("Error calling fstat: %s\n", strerror(errno));
} else {
printf("%d\n", buf.st_ino);
if(close(fd) == -1) {
printf("Error calling close: %s\n", strerror(errno));
}
}
}
return 0;
}

unlinkat() a Directory in C

I need to delete a directory in a given path using unlinkat(), the program runs fine but it doesn't delete (unlink) the directory (is empty by the way). Using the man7 documentation for unlinkat() I have the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h>
#include <fcntl.h>
void main(int argc,char *argv[]){
DIR *dirp;
if ((dirp = opendir(argv[1]))== NULL){
fprintf(stderr, "Couldn't open the directory %s: %s \n",argv[1],strerror(errno) );
exit(1);
}
unlinkat(dirp,argv[1],AT_REMOVEDIR);
exit(0);
}
The path given in the arguments is relative from the father of the folder I want to erase
Your dirp is a pointer to a directory handle (DIR *), but the first argument to unlinkat() must be a directory descriptor, or AT_FDCWD (for "current working directory"). This is clearly stated in the unlinkat() man page.
You should always enable compiler warnings (-Wall for gcc). It would have pointed you to the problem in this case, too.
To get hold of the underlying descriptor for dirp, use dirfd(dirp).
Here is an actual example. It removes the (empty) directories specified in the second and further parameters, relative to the directory/path specified as the first parameter. The directory specified in the first parameter is not removed.
#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
DIR *dirp;
int arg, retval;
if (argc < 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s BASE-DIRECTORY ITEM-TO-REMOVE ...\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
dirp = opendir(argv[1]);
if (!dirp) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
retval = EXIT_SUCCESS;
for (arg = 2; arg < argc; arg++) {
if (unlinkat(dirfd(dirp), argv[arg], AT_REMOVEDIR)) {
fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
retval = EXIT_FAILURE;
}
}
closedir(dirp);
return retval;
}

Resources