Is There Any Posix Way Through Fstat() to Check Whether a File Is a Symbolic Link or Not

How to figure out if a file is a link?

From the stat (2) man page:

stat() stats the file pointed to by path and fills in buf.

lstat() is identical to stat(), except that if path is a symbolic link, then the link itself is stat-ed, not the file that it refers to.

In other words, the stat call will follow the symbolic link to the target file and retrieve the information for that. Try using lstat instead, it will give you the information for the link.


If you do the following:

touch junkfile
ln -s junkfile junklink

then compile and run the following program:

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

int main (void) {
struct stat buf;
int x;

x = stat ("junklink", &buf);
if (S_ISLNK(buf.st_mode)) printf (" stat says link\n");
if (S_ISREG(buf.st_mode)) printf (" stat says file\n");

x = lstat ("junklink", &buf);
if (S_ISLNK(buf.st_mode)) printf ("lstat says link\n");
if (S_ISREG(buf.st_mode)) printf ("lstat says file\n");

return 0;
}

you will get:

 stat says file
lstat says link

as expected.

Checking if a file is a directory or just a file

You can call the stat() function and use the S_ISREG() macro on the st_mode field of the stat structure in order to determine if your path points to a regular file:

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

int is_regular_file(const char *path)
{
struct stat path_stat;
stat(path, &path_stat);
return S_ISREG(path_stat.st_mode);
}

Note that there are other file types besides regular and directory, like devices, pipes, symbolic links, sockets, etc. You might want to take those into account.

How to tell if FILE* is referring to a directory?

Assuming a POSIX-like environment, if you have just the file stream (FILE *fp), then you are probably reduced to using fileno() and fstat():

#include <sys/stat.h>

struct stat sb;
if (fstat(fileno(fp), &sb) != 0)
…oops…
if (S_ISDIR(sb.st_mode))
…it is a directory…
else
…it is not a directory…

following symbolic links in C

Make sure that you have an environment which supports POSIX functions, include unistd.h and then use the readlink function.

how can i go about checking if a file exists

Look at the manual page for stat() also covering lstat() and fstat(). If you get -1 as the return value, the file you tried to get summary information on does not exist, or may not have rights to read it (see last example below which shows the difference from your fopen() test as a file may exist but not let you open it).

edd@max:/tmp$ cat stat.c

#include <stdio.h>

int main(int argc, char *argv[]) {
if (argc > 1)
printf("Returned %d for %s\n", stat(argv[1]), argv[1]);
}
edd@max:/tmp$ gcc -o stat stat.c
edd@max:/tmp$ ./stat stat.c
Returned 0 for stat.c
edd@max:/tmp$ ./stat does-not-exist
Returned -1 for does-not-exist
edd@max:/tmp$ ./stat /root/.ssh/id_dsa ## exists, but I can't read it
Returned -1 for /root/.ssh/id_dsa
edd@max:/tmp$

how to determine directory or file in linux in c

What you are looking for is stat or one of its variants. Specifically look at the st_mode field of struct stat. The macro you are interested in is S_ISDIR(x).

Find below your modified code that demonstrates what you want:

void list_file(char* directory) {
DIR *d;
struct dirent *dir;
int dir_len = strlen(directory);
char* path = malloc(dir_len + NAME_MAX + 2); // +2, 1 for '/' and 1 for '\0'
if(path == NULL) {
fprintf(stderr, "malloc failed\n");
return;
}
strcpy(path, directory);
if(path[dir_len-1] != '/') {
path[dir_len] = '/';
dir_len++;
}
d = opendir(directory);
if (d) {
while ((dir = readdir(d)) != NULL)
{
struct stat buf;
strcpy(&path[dir_len], dir->d_name);
if(stat(path, &buf) < 0) {
fprintf(stderr, "error\n");
}
else {if(!S_ISDIR(buf.st_mode)) {
printf("%s\n", dir->d_name);
}
}
}

closedir(d);
}
free(path);
}

I have removed your first print as it was printing the null terminating character of the string.

Update:

As pointed out in the comments since we are dealing with Linux you can use the d_type field in struct dirent (which is not part of POSIX but is part of Linux).

With that said the code would be the following.

void list_file(char* directory){
DIR *d;
struct dirent *dir;

d = opendir(directory);
if (d) {
while ((dir = readdir(d)) != NULL)
{
struct stat buf;
if(dir->d_type == DT_DIR) {
printf("%s\n", dir->d_name);
}
}

closedir(d);
}
}

It is a lot cleaner, no need for malloc.

Can I check if two FILE* or file descriptor numbers refer to the same file?

Yes - compare the file device ID and inode. Per the <sys/stat.h> specification:

The st_ino and st_dev fields taken together uniquely identify the file within the system.

Use

int same_file(int fd1, int fd2) {
struct stat stat1, stat2;
if(fstat(fd1, &stat1) < 0) return -1;
if(fstat(fd2, &stat2) < 0) return -1;
return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino);
}

Open every file but not links to other directories when using scandir()

Look at this: How to check whether two file names point to the same physical file

You need to store a list of inodes that you have visited to make sure that you don't get any duplicates. If you have two hard links to the same file, there is no "one" canonical name. One possibility is to first store all the files and then recurse through all the filenames. You can store the path structure separately from the inodes and file contents.

How do you check to see if two different file reference strings refer to the same file?

On a POSIX system you can use stat() to check if they have the same inode number on the same filesystem.

You should also check the generation number, to handle a race condition due to the file being deleted and a new file getting its inode number between the two calls.

#include <sys/stat.h>

static bool is_same_file (const char *f1, const char *f2) {
struct stat s1, s2;
if (stat(f1, &s1) < 0)) {
perror("stat f1");
return false;
}
if (stat(f2, &s2) < 0)) {
perror("stat f2");
return false;
}
return s1.st_dev == s2.st_dev && s1.st_ino == st.st_ino && s1.st_gen == s2.st_gen;
}


Related Topics



Leave a reply



Submit