List hard links of a file C - c

I need to list all hard links of a given file in "pure" C, so without help of bash commands.
I googled for hours but couldn't find anything useful.
My current idea is to get inode number then loop through directory to find files with same inode.
In bash something like
sudo find / -inum 199053
Any better suggestion?
Thanks

To get the inode number of a single file, invoke the stat function and reference the st_ino value of the returned struct.
int result;
struct stat s;
result = stat("filename.txt", &s);
if ((result == 0) && (s.st_ino == 199053))
{
// match
}
You could build a solution with the stat function using opendir, readdir, and closedir to recursively scan a directory hierarchy to look for matching inode values.
You could also use scandir to scan an entire directory:
int filter(const struct dirent* entry)
{
return (entry->d_ino == 199053) ? 1 : 0;
}
int main()
{
struct dirent **namelist = NULL;
scandir("/home/selbie", &namelist, filter, NULL);
free(namelist);
return 0;
}

Related

Printing Hardlinks of an Inode in C

I'm writing a program in C to scan the files in a directory, get its inode number, get the hardlink count and print out the hardlinks. So in printing out the hardlinks, i search files from root and match the files with the same inode. However when i set the path to find the matching inode it does not show any files. On the other hand, if i set the path to the same directory i scanned initially it displays one file as a hardlink. I'm open to any other way to display the hardlinks of an inode.
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include<sys/dir.h>
#include<stdlib.h>
void listFilesRecursively(char *path);
void filter(char *basePath, long inode);
void get_hardLinks(long inode);
int main()
{
// Directory path to list files
char path[100]="../";
listFilesRecursively(path);
return 0;
}
void listFilesRecursively(char *basePath)
{
char path[1000];
struct dirent *dp;
struct stat sb;
DIR *dir = opendir(basePath);
struct dirent **namelist = NULL;
// Unable to open directory stream
if (!dir)
return;
while ((dp = readdir(dir)) != NULL)
{
stat(dp->d_name, &sb);
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
{
printf("Inode:%lu | %s |%ld \n",(unsigned long)dp-> d_ino, dp->d_name,(long) sb.st_nlink);
strcpy(path, basePath);
strcat(path, "/");
strcat(path, dp->d_name);
listFilesRecursively(path);
get_hardLinks((unsigned long)dp-> d_ino);
}
}
closedir(dir);
}
void filter(char *basePath, long inode){
char path[1000];
struct dirent *dp;
struct stat sb;
DIR *dir = opendir(basePath);
while ((dp = readdir(dir)) != NULL)
{
stat(dp->d_name, &sb);
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0 && (unsigned long)dp->d_ino==inode && ((long)sb.st_nlink<=10))
{
printf("----HardLink: Inode:%lu | %s \n",(unsigned long)dp-> d_ino, dp->d_name);
strcpy(path, basePath);
strcat(path, "/");
strcat(path, dp->d_name);
filter("/",inode);
}
}
closedir(dir);
}
void get_hardLinks(long inode){
filter("../",inode);
}
The ASK seems to be
List of allfiles (inode, name, #links), in DFS order
for each file show all hard links
It does not explicitly state how symlink should be handled. Answer assumed they can be ignored (including symlink to other directories, which may result in infinite loops).
The current solution has few problems. The first cause incorrect results when scanning over multiple directories, the other may cause serious performance issues when the number of files goes up.
Implementation issue:
Missing Path: The 'stat' calls in listFilesRecursively and filter just pass the file name. This will cause a lookup in the current directory (pwd). But the files is in directory basepath. Code need to form the full pathname by combining the two with a '/'.
Error checking on all system calls: Not testing on 'stat', 'opendir', etc, will have serious impact on the code on any error - either program crash, partial results, on infinite loops.
Performance:
The function listFilesRecursively(path) is being called for each entry. It should only be called for directory entries. (S_ISDIR on the mode).
The function get_hardLinks is getting called for every file ,but it should only get called for files with more than 1 hard link (st_nlink > 1)
The filter will perform recursive call on any entry. Should restrict recursive calls to directories, same as listFilesRecursively above.
Starting point: the question indicate search should start at root, program is starting all searches at the parent folder.
Also, if this is going to run on a large file system in production, consider the following performance tuning:
If the total number of files in the scanned tree is X, current performance will be O(XX), on IO. Even with above improvements, the performance will be O( KX ), where K is the number of files with hardlinks >1, that will cause a scan.
Alternative implementation will perform
Single scan (O(n), remember the i-nodes of all files with hardlinks>1 in dynamically resized array
Sort the array by inodes
Print the files with identical hardlinks from memory.
This will be O(n) for IO, and O(n log n) for sorting. Practically O(n) because IO will dominate the calls. Much faster the current logic.

How to properly use S_ISREG function

EDIT: After some help from the forum it was made clear that this issue was not in the use of S_ISREG() but in my use of lstat(). Sorry for the misleading question.
I'm looking in a directory and trying to tell the difference between regular files and sub-directories.
I've looked through other people's issues with this problem, and while some were similar, none were answered clearly enough to fix my code.
int find(char *argv)
{
DIR *pointerToDir;
struct dirent *pointerToDirent;
struct stat status;
int mode;
pointerToDir = opendir(argv);
if(pointerToDir == NULL)
{
printf("Can't open that directory (or it doesn't exist)\n");
closedir(pointerToDir);
return 0;
}
else
{
while((pointerToDirent = readdir(pointerToDir)) != NULL)
{
lstat(pointerToDirent->d_name, &status);
mode = S_ISREG(status.st_mode);
if(mode != 0)
printf("%s must be a file\n", pointerToDirent->d_name);
else
printf("%s must be a dir\n", pointerToDirent->d_name);
}
closedir(pointerToDir);
return 0;
}
}
I pass the program a test directory that has 2 sub-directories and 2 regular files. The layout would be something like this:
dir1
sub1
sub2
dir1.txt
test.c
Now when I run my program and pass "dir1" as the argument, I would expect it to return the following:
. must be a dir
.. must be a dir
sub1 must be a dir
dir1.txt must be a file
sub2 must be a dir
test.c must be a file
But instead, it returns that they are all "dirs". What am I missing?

readdir looping more times than number of files present in the directory [duplicate]

This question already has answers here:
List regular files only (without directory) problem
(2 answers)
Closed 10 years ago.
My goal is to count the number of files in a directory. After searching around, I found a piece of code which iterates over each file in a directory. But the issue is that it's looping extra times, 2 times extra to be more precise.
So for
int main(void)
{
DIR *d;
struct dirent *dir;
char *ary[10000];
char fullpath[256];
d = opendir("D:\\frames\\");
if (d)
{
int count = 1;
while ((dir = readdir(d)) != NULL)
{
snprintf(fullpath, sizeof(fullpath), "%s%d%s", "D:\\frames\\", count, ".jpg");
int fs = fsize(fullpath);
printf("%s\t%d\n", fullpath, fs); // using this line just for output purposes
count++;
}
closedir(d);
}
getchar();
return(0);
}
My folder contains 500 files, but the output is shown till 502
UPDATE
I modified the code to read as
struct stat buf;
if ( S_ISREG(buf.st_mode) ) // <-- I'm assuming this says "if it is a file"
{
snprintf(fullpath, sizeof(fullpath), "%s%d%s", "D:\\frames\\", count, ".jpg");
int fs = fsize(fullpath);
printf("%s\t%d\n", fullpath, fs);
}
But I'm getting storage size of "buf" isn't known. I also tried doing struct stat buf[100], but that didn't help either.
As pointed out in comments, you're also getting the two directories named . and .., which skews your count.
In Linux, you can use the d_type field of the struct dirent to filter them out, but the documentation says:
The only fields in the dirent structure that are mandated by POSIX.1 are: d_name[], of unspecified size, with at most NAME_MAX characters preceding the terminating null byte; and (as an XSI extension) d_ino. The other fields are unstandardized, and not present on all systems; see NOTES below for some further details.
So, assuming you're on Windows you probably don't have d_type. Then you can use some other call instead, for instance stat(). You can of course filter out based on name too, but if you want to skip directories anyway that is a more robust and general solution.
You need to call _stat()/stat() on the file name you want info for.
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WINDOWS
# define STAT _stat
#else
# define STAT stat
#endif
...
char * filename = ... /* let it point to some file's name */
struct STAT buffer = {0};
if (STAT(filename, &buffer)
... /* error */
else
{
if (S_ISREG(buffer.st_mode))
{
... /* getting here, means `filename` referrs to a ordinary file */
}
}

Traversing file system according to a given root place by using threads by using C for unix

I wanna traverse inside the file system by using threads and processes.My program has to assume the first parameter is either given as "-p" which offers a multi-process application or "-t" which runs in a multi-threaded way. The second parameter is the
pathname of a file or directory. If my program gets the path of a file, it should print out the size of the file in bytes. If my program gets the path of a directory, it should, in the same way, print out the directory name, then process all the entries in the
directory except the directory itself and the parent directory. If my program is given a directory, it must display the entire hierarchy rooted at the specified directory. I wrote something but I got stuck in.I can not improve my code.Please help me.
My code is as following:
include
include
include
include
include
include
include
int funcThread(DIR *D);
int main(int argc, char * argv[])
{
pthread_t thread[100];
DIR *dirPointer;
struct stat object_file;
struct dirent *object_dir;
int counter;
if(opendir(argv[1])==NULL)
{
printf("\n\nERROR !\n\n Please enter -p or -t \n\n");
return 0;
}
if((dirPointer=opendir(argv[1]))=="-t")
{
if ((object_dir = opendir(argv[2])) == NULL)
{
printf("\n\nERROR !\n\nPlease enter the third argument\n\n");
return 0;.
}
else
{
counter=0;
while ((object_dir = readdir(object_dir)) != NULL)
{
pthread_create(&thread[counter],NULL,funcThread,(void *) object_dir);
counter++;
}
}
}
return 0;
}
int funcThread(DIR *dPtr)
{
DIR *ptr;
struct stat oFile;
struct dirent *oDir;
int num;
if(ptr=readdir(dPtr)==NULL)
rewinddir(ptr);
if(S_ISDIR(oFile.st_mode))
{
ptr=readdir(dPtr);
printf("\t%s\n",ptr);
return funcThread(ptr);
}
else
{
while(ptr=readdir(dPtr)!=NULL)
{
printf("\n%s\n",oDir->d_name);
stat(oDir->d_name,&oFile);
printf("\n%f\n",oFile.st_size);
}
rewinddir(ptr);
}
}
This line:
if((dirPointer=opendir(argv[1]))=="-t")
dirPointer is a pointer DIR* so how can it be equal to a literal string pointer?
I spotted a few errors:
Why are you using opendir() to check your arguments? You should use something like strcmp for that.
You're passing struct dirent* to funcThread() but funcThread() takes a DIR*.
You're using oFile on funcThread() before you initialize it (by calling stat()).
What is the purpose of calling rewinddir()? I guess you're blindly trying to get readdir() to work with a struct dirent*.
You're using oDir but it's never initialized.
You're calling printf() from multiple threads with no means to synchronize the output so it would be completelly out of order or garbled.
I suggest you read and understand the documentation of all those functions before using them (google "posix function_name") and get familiar with the basics of C. And before bringing threads into the equation try to get it working on a single threaded program. Also you won't see an improvement in performance by using that many threads unless you have close to that many cores, it will actually decrease performance and increase resource usage.
if(ptr=readdir(dPtr)==NULL){}
The = operator has lower precedence than ==
[this error is repeated several times]

Need To Eliminate Directories From File Listing in C

I have a problem, in that I need to get a list of the files in a Directory. Using this previous StackOverflow question as a base, I've currently got this code:
void get_files(int maxfiles) {
int count = 0;
DIR *dir;
struct dirent *ent;
dir = opendir(DIRECTORY);
if (dir != NULL) {
/* get all the files and directories within directory */
while ((ent = readdir(dir)) != NULL) {
if (count++ > maxfiles) break;
printf("%s\n", ent->d_name);
}
closedir(dir);
} else {
/* could not open directory */
printf("ERROR: Could not open directory");
exit(EXIT_FAILURE);
}
}
Now it works almost exactly how I want it too, but the problem is that its also listing directories in with he files, and I only want file entries. Is there a easy modification I can make to do this?
You can filter directories using code similar to
this one
POSIX defines fstat which can be used for the purpose of checking whether a file is a directory. It also has a macro to simplify the check.
http://linux.die.net/man/2/fstat
Note that for Windows you may have to use windows API here.
If your struct dirent contains the nonstandard-but-widely-available d_type member, you can use this to filter out directories. Worth having an option to use it and only falling back to stat on systems that don't, since using d_type rather than stat will possibly make your directory listing tens or hundreds of times faster.

Resources