I try to write program where part of it is listing all directories (especially starting from /), but I have a problem with /proc/self which is infinitely recursive (I get /proc/self/task/4300/fd/3/proc/self/task/4300/fd/3/proc/self/task/4300/fd/3/proc/... and so on). What is nice way to deal with it?
EDIT: Program is written in C language and I use opendir(), readdir()
You can use the S_ISLNK macro to test the st_mode field returned by a call to lstat. If the file is a symbolic link, do not try to follow it.
[user#machine:~]:./list | grep link
/proc/mounts is a symbolic link
/proc/self is a symbolic link
Example code
#include <stdio.h> // For perror
#include <stdlib.h>
#include <sys/types.h> // For stat, opendir, readdir
#include <sys/stat.h> // For stat
#include <unistd.h> // For stat
#include <dirent.h> // For opendir, readdir
const char *prefix = "/proc";
int main(void)
{
DIR *dir;
struct dirent *entry;
int result;
struct stat status;
char path[PATH_MAX];
dir = opendir(prefix);
if (!dir)
{
perror("opendir");
exit(1);
}
entry = readdir(dir);
while (entry)
{
result = snprintf(path, sizeof(path), "%s", prefix);
snprintf(&path[result], sizeof(path) - result, "/%s", entry->d_name);
printf("%s", path);
result = lstat(path, &status);
if (-1 == result)
{
printf("\n");
perror("stat");
exit(2);
}
if (S_ISLNK(status.st_mode))
{
printf("%s", " is a symbolic link");
}
printf("\n");
entry = readdir(dir);
}
return(0);
}
From path_resolution(7):
Length limit
There is a maximum length for pathnames. If the pathname (or some intermediate pathname obtained while resolving symbolic links) is too long, an ENAMETOOLONG error
is returned ("File name too long").
I think you should employ similar behaviour: check for too long pathnames.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/param.h>
/* Short & sweet recursive directory scan, finds regular files only.
Good starting point, should work on Linux OS.
Pass the root path, and returns number of dirs and number of files
found.
*/
char *tree_scan( const char *path, int *ndirs, int *nfiles){
DIR *dir;
struct dirent *entry;
char spath[MAXPATHLEN] = "";
if( !(dir = opendir( path))){ perror("opendir"); exit(1);}
for( entry = readdir( dir); entry; entry = readdir( dir)){
sprintf( spath, "%s/%s", path, entry->d_name);
if( entry->d_type == DT_REG){ (*nfiles)++; printf( "%s\n", spath);}
if( entry->d_type == DT_DIR &&
(strcmp( ".", entry->d_name)) &&
(strcmp( "..", entry->d_name))){
(*ndirs)++; tree_scan( spath, ndirs, nfiles);
}
}
closedir( dir);
return(0);
}
/* Call it like so */
int i = 0, l = 0;
tree_scan( "/path", &i, &l);
printf("Scanned %d directories, %d files.\n", i, l);
I don't have a *nix terminal handy, but you could always take a look at the source for ls.c and see how it's done.
The source as part of the gnu core utils can be found here.
I created a ls clone a few years ago in school, and I think I got around it by watching the pathname size as ulidtko mentioned.
Related
I'm trying to rewrite ls function with some of its flags, currently I'm implementing [-l] flag but output about permissions from original ls and lstat are different
Here is my code
void mx_strmode(mode_t mode, char * buf) {
const char chars[] = "rwxrwxrwx";
for (size_t i = 0; i < 9; i++) {
buf[i] = (mode & (1 << (8-i))) ? chars[i] : '-';
}
buf[9] = '\0';
}
int main(int ac, char **av) {
t_flags flags = mx_get_flags(ac, av);
char *dir_name = get_dir_name(ac, av);
DIR *dir;
struct dirent *entry;
dir = opendir(dir_name);
if (!dir) {
perror("diropen");
exit(1);
};
struct stat s_stat;
while ((entry = readdir(dir)) != NULL) {
lstat(entry->d_name, &s_stat);
char buf_perm[10];
mx_strmode(s_stat.st_mode, buf_perm);
printf("%s %s\n", buf_perm , entry->d_name);
};
closedir(dir);
}
And here is what i get from ls and my program. I'm opening directory that doesn't contain my executable(may be it's a root of a problem)
>drwxr-xr-x 3 fstaryk 4242 102 Jan 3 17:27 .
>drwxr-xr-x 11 fstaryk 4242 374 Jan 18 17:40 ..
>-rw-r--r-- 1 fstaryk 4242 4365 Jan 18 17:40 main.c
>rwxr-xr-x .
>rwx------ ..
>rwx------ main.c
As you've discovered from adding the error checking as suggested in comments, you're getting issues with 'No such file or directory'. This is because lstat() resolves relative paths like the filenames in struct dirent objects starting from the current working directory, which is not the directory you're trying to list the files of.
Luckily, modern unix/linux systems have a function fstatat() that lets you specify a directory to use as the base of relative paths, and you can get the required directory descriptor from a DIR struct with dirfd().
Simplified example of using it:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
int main(void) {
DIR *d = opendir("test/");
if (!d) {
perror("opendir");
return EXIT_FAILURE;
}
int dfd = dirfd(d); // Get the directory file descriptor for use with fstatat()
if (dfd < 0) {
perror("dirfd");
closedir(d);
return EXIT_FAILURE;
}
struct dirent *entry;
while ((entry = readdir(d))) {
if (entry->d_name[0] == '.') {
// Skip dotfiles
continue;
}
struct stat s;
// Resolve filenames relative to the directory being scanned
// and don't follow symlinks to emulate lstat()'s behavior
if (fstatat(dfd, entry->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
perror("fstatat");
closedir(d);
return EXIT_FAILURE;
}
printf("%s: %ld\n", entry->d_name, (long)s.st_size);
}
closedir(d);
return 0;
}
On older OSes that lack the *at() functions, you'd have to resort to creating a string holding the directory name + filename (With snprintf() or whatever) and use that as an argument to lstat().
I would like to know the most efficient way to list the filenames on a Posix system. Doing either:
$ ls -R
Or:
$ find /
Or:
$ du /
Or 100 other variations (links abound on StackOverflow/ServerFault about various ways to do this). However, this is way too slow on the filesystem I am on, cifs -- for example, I have currently been running the ls -R for about two days (it takes about 50 hours to complete -- there are tons of files and directories on the system -- several petabytes worth).
So I am wondering if this can be done at a lower-level, hopefully in C. to list out the filenames from the inode database (example here). I don't need a recursive lookup of the entire path, but only the top-level name | filename -- and I would build out everything else manually. Is there a way to do this so that hopefully instead of taking ~50 hours to do an ls command with the billions of recursive lookups (yes, it does get cached after successive runs, but not most of it on the first run) can the inode database itself be dumped?
An an example, perhaps something like:
#filename,inode
myfile.mov,1234
myotherfile.csv,92033
But the main point here --and why I asked this question -- is speed not actually a command in order to do the above (such as $ ls -iR).
Here is a way to directly use getdents recursively. I will update timings of this shortly to compare it to ls and the other standard unix utils:
#define _GNU_SOURCE
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)
struct linux_dirent {
unsigned long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
};
void print_files(char* dir, FILE* out)
{
// open the file
int fd = open(dir, O_RDONLY | O_DIRECTORY);
if (fd == -1) handle_error("Error opening file.\n");
// grab a buffer to read the file data
#define BUF_SIZE (1024*1024*1)
char* buffer = malloc(sizeof *buffer * BUF_SIZE);
if (buffer == NULL) handle_error("Error malloc.\n");
// do the getdents syscall writing to buffer
int num_read = syscall(SYS_getdents, fd, buffer, BUF_SIZE);
if (num_read == -1) handle_error("Error getdents syscall.\n");
close(fd);
for (long buffer_position = 0; buffer_position < num_read;) {
struct linux_dirent *d = (struct linux_dirent *) (buffer + buffer_position);
char d_type = *(buffer + buffer_position + d->d_reclen - 1);
// skip on . and .. in the listing
if (d->d_name[0] == '.') {
buffer_position += d->d_reclen;
continue;
}
// path = dir + '/' + name
char path[400];
strcpy(path, dir);
strcat(path, "/");
strcat(path, d->d_name);
// recursive call, as necessary
if (d_type == DT_DIR)
print_files(path, out);
else if (d_type == DT_REG)
fprintf(out, "%s\n", path);
// advance buffer position
buffer_position += d->d_reclen;
}
free(buffer);
}
int main(int argc, char *argv[])
{
char dir[1024];
strcpy(dir, argc > 1 ? argv[1] : ".");
FILE *out = fopen("c-log.txt", "w");
fprintf(out, "-------------[ START ]---------------------\n");
print_files(dir, out);
}
I am creating a project on moving a directory files by creating sub-folders according to their particular type in c. I have made up to creating directories with the help of POSIX library dirent.h for the files having different extension present in the home directory but I don't know how to cut a file from home directory and paste in its particular sub-folder. So please guide me about how can I cut and paste a file from one directory to another in c.
Use rename(DestinationFilepath, SourceFilepath);
For more info check man page http://linux.die.net/man/2/rename
For two different system use cURL library:
http://en.wikipedia.org/wiki/CURL
Code in C:
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <time.h>
#define DESTINATION_FOLDER "/home/second/"
#define SOURCE_FOLDER "/home/first/"
void printdir()
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
struct tm *tm;
char src_folder[1024];
char dest_folder[1024];
if ((dp = opendir(SOURCE_FOLDER)) == NULL) {
fprintf(stderr,"cannot open directory: %s\n", SOURCE_FOLDER);
return;
}
chdir(SOURCE_FOLDER);
while ((entry = readdir(dp)) != NULL) {
lstat(entry->d_name, &statbuf);
if (!S_ISDIR(statbuf.st_mode)) {
sprintf(src_folder, "%s%s", SOURCE_FOLDER, entry->d_name);
sprintf(dest_folder, "%s%s", DESTINATION_FOLDER, entry->d_name);
printf("%s----------------%s\n", entry->d_name, dest_folder);
rename(src_folder, dest_folder);
}
}
chdir("..");
closedir(dp);
}
int main()
{
while (1) {
printdir();
}
rename("aaa.txt", "/home/second/bbb.txt");
printf("done.\n");
exit(0);
}
I'm trying to create a function in c which scans all my path C: \ temp (Windows) to search for a file that I pass (eg test.txt) and each time it finds one return the path to steps another function to write something in the bottom of this file.
I managed to do the function that writes to the file but can not figure out how to do that scans the folder and pass the address of the file found.
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
void printdir(char *dir, int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL) {
fprintf(stderr,"cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 ||
strcmp("..",entry->d_name) == 0)
continue;
printf("%*s%s/\n",depth,"",entry->d_name);
/* Recurse at a new indent level */
printdir(entry->d_name,depth+4);
}
else printf("%*s%s\n",depth,"",entry->d_name);
}
chdir("..");
closedir(dp);
}
int main()
{
printf("Directory scan of /home:\n");
printdir("/home",0);
printf("done.\n");
exit(0);
}
Use FindFirstFile function. Here's a good example of this function using.
Hi I reached inode 2 , the root directory. I know the direct block number of it, which is 265. How can I list the content of the root directory in C?
This should work. I suggest looking up the man pages for opendir() and readdir(). This is not based on inodes. Do you require to be able to look up directories based on inode?
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
int main() {
DIR *dir = opendir("/");
if(dir==NULL) {
perror("Couldn't open dir");
exit(1);
}
printf("opened\n");
struct dirent * entry;
while((entry = readdir(dir))) {
printf("%s\n", entry->d_name);
}
return 0;
}