I found the answer to another question here to be very helpful.
There seems to be a limitation of the sys/stat.h library as when I tried to look in other directories everything was seen as a directory.
I was wondering if anyone knew of another system function or why it sees anything outside the current working directory as only a directory.
I appreciate any help anyone has to offer as this is perplexing me and various searches have turned up no help.
The code I made to test this is:
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
int main(void) {
int status;
struct stat st_buf;
struct dirent *dirInfo;
DIR *selDir;
selDir = opendir("../");
// ^ or wherever you want to look
while ((dirInfo = readdir(selDir))) {
status = stat (dirInfo->d_name, &st_buf);
if (S_ISREG (st_buf.st_mode)) {
printf ("%s is a regular file.\n", dirInfo->d_name);
}
if (S_ISDIR (st_buf.st_mode)) {
printf ("%s is a directory.\n", dirInfo->d_name);
}
}
return 0;
}
You need to check the status of the stat call; it is failing.
The trouble is that you're looking for a file the_file in the current directory when it is actually only found in ../the_file. The readdir() function gives you the name relative to the other directory, but stat() works w.r.t the current directory.
To make it work, you'd have to do the equivalent of:
char fullname[1024];
snprintf(fullname, sizeof(fullname), "%s/%s", "..", dirInfo->d_name);
if (stat(fullname, &st_buf) == 0)
...report on success...
else
...report on failure...
If you printed out stat, you'll notice there's an error (File not found).
This is because stat takes the path to the file, but you're just providing the file name.
You then call IS_REG on garbage values.
So, suppose you have a file ../test.txt
You call stat on test.txt...That isn't in directory ./test.txt, but you still print out the results from IS_REG.
Related
I am doing a project in C, and I'm stuck on one thing, i need to check if the directoy "/var/log/PROJECT " exist, if not, my program must create it, the aplication will always run on super user, this is what im doing:
struct stat st = {0};
if (stat("/var/log/project/PROJECT", &st) == -1) {
printf("im creating the folder\n");
mode_t process_mask = umask(0);
int result_code = mkdir("/var/log/project/PROJECT", 0777);
umask(process_mask);
}else{
printf("exist\n");
}
Sorry for asking to "do my homework" but im really stuck...
Well, I'm going to run with my suspicion. If the problem is that the parent directory of the directory you're trying to create does not exist, the solution is to create the parent directory before it. This is not terribly difficult to do with recursion, thankfully. Try this:
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int create_directory_with_parents(char const *pathname, mode_t modus) {
struct stat st;
if(stat(pathname, &st) == -1) {
// If the directory does not yet exist:
//
// Make sure the parent directory is there. dirname() gives us the name of
// the parent directory, then we call this very function recursively because
// we are, after all, in a function that makes sure directories exist.
char *path_cpy = strdup(pathname);
char *dir = dirname(path_cpy);
int err = create_directory_with_parents(dir, modus);
free(path_cpy);
// If we were able to make sure the parent directory exists,
// make the directory we really want.
if(err == 0) {
mode_t process_mask = umask(0);
int err = mkdir(pathname, modus);
umask(process_mask);
}
// err is 0 if everything went well.
return err;
} else if(!S_ISDIR(st.st_mode)) {
// If the "directory" exists but is not a directory, complain.
errno = ENOTDIR;
return -1;
}
// If the directory exists and is a directory, there's nothing to do and
// nothing to complain about.
return 0;
}
int main(void) {
if(0 != create_directory_with_parents("/var/log/project/PROJECT", 0777)) {
perror("Could not create directory or parent of directory: ");
}
}
The recursion ends when the first parent directory is found that exists; that is with / at the latest.
One limitation of this implementation is that all parent directories will have the same access rights as the leaf directory, which may or may not be what you want. If this is not what you want, you'll have to change the modus parameter in the recursive call to create_directory_with_parents. How to pass several modus parameters for several layers of parent directories that may have to be created is something of a design question that depends on what exactly your needs are, so I cannot give a general answer to it.
Why not just execute the mkdir command anyway, and simply ignore the error it will produce if the directory already exists? This will save you playing around with stat.
Is there a reason you need the file permissions to be 777? If not, you can drop the umask bit too.
I'm not sure if C can do this, but I'm hoping that I can make a program that will look into a directory, and print out all of the contents of the directory along with the file size of each file. As in I wanted it to look like this (possibly):
filename.txt -- 300 bytes
filename2.txt -- 400 bytes
filename3.txt -- 500 bytes
And so on.
So far, I created a program that can open a file, and it will print the bytes, but it does not read the entire directory, and I have to be specific with which file I want to read.. (which is not what I want).
Here is what I have so far:
#include <stdio.h>
int main(){
FILE *fp; // file pointer
long fileSize;
int size;
// opens specified file and reads
fp = fopen( "importantcommands.txt", "rw" );
if( fp == NULL ){
printf( "Opening file error\n" );
return 0;
}
// uses fileLength function and prints here
size = fileLength(fp);
printf( "\n Size of file: %d bytes", size );
fclose(fp);
return 0;
}
int fileLength( FILE *f ){
int pos;
int end;
// seeks the beginning of the file to the end and counts
// it and returns into variable end
pos = ftell(f);
fseek (f, 0, SEEK_END);
end = ftell(f);
fseek (f, pos, SEEK_SET);
return end;
}
Please help.
C can certainly do it - the ls(1) command can, for example, and it's written in C.
To iterate over a directory, you can use the opendir(3) and readdir(3) functions. It's probably easier to just let the shell do it for you, though.
As far as getting the filename, you can just take it as a command line parameter by defining main as:
int main(int argc, char **argv)
Command line parameters will begin at argv[1].
See opendir() / fdopendir() and readdir() if you are using linux in dirent.h
man page
Simple example from a : SO Post
DIR *dir;
struct dirent *ent;
if ((dir = opendir ("c:\\src\\")) != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
printf ("%s\n", ent->d_name);
}
closedir (dir);
}
else {
/* could not open directory */
perror ("Could not open directory");
return EXIT_FAILURE;
}
Also You can use the fstat() system call which can fill in the struct stat for any file you want. From that stat you can access that file's size.
Please use the man pages to help you out. (Almost) Everything related to Linux is insanely well documented.
To read a list of files in a directory look at opendir, readdir, closedir for Linux
use stat to get the length of the file.
These are of Linux
For winodws see http://msdn.microsoft.com/en-gb/library/windows/desktop/aa365200%28v=vs.85%29.asp and the link http://blog.kowalczyk.info/article/8f/Get-file-size-under-windows.html will show you how to do this.
To get the list of files in a directory look for "libc opendir". To get the size of a file without opening it you can use fstat.
This seems strangely similar to another question I saw recently. Anyway, here's my strangely similar answer (for Linux, not sure how it'll fare on Windows 7):
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
struct stat file_stats;
DIR *dirp;
struct dirent* dent;
dirp=opendir("."); // specify directory here: "." is the "current directory"
do {
dent = readdir(dirp);
if (dent)
{
printf("%s -- ", dent->d_name);
if (!stat(dent->d_name, &file_stats))
{
printf("%u bytes\n", (unsigned int)file_stats.st_size);
}
else
{
printf("(stat() failed for this file)\n");
}
}
} while (dent);
closedir(dirp);
}
There are little things need to be taken care for the given examples (under Linux or other UNIX).
You properly only want to print out the file name and size of a regular file only. Use S_ISREG() to test the st_mode field
If you want to recursively print out all files under sub directories also, you then need to use S_ISDIR() to test for direcotry and be carefull of special directory '.' and '..'.
This question already has answers here:
Checking if a directory exists in Unix (system call)
(5 answers)
Closed 7 years ago.
I'm writing a program to check if something is a file or is a directory. Is there a better way to do it than this?
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
int isFile(const char* name)
{
DIR* directory = opendir(name);
if(directory != NULL)
{
closedir(directory);
return 0;
}
if(errno == ENOTDIR)
{
return 1;
}
return -1;
}
int main(void)
{
const char* file = "./testFile";
const char* directory = "./";
printf("Is %s a file? %s.\n", file,
((isFile(file) == 1) ? "Yes" : "No"));
printf("Is %s a directory? %s.\n", directory,
((isFile(directory) == 0) ? "Yes" : "No"));
return 0;
}
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.
Use the S_ISDIRmacro:
int isDirectory(const char *path) {
struct stat statbuf;
if (stat(path, &statbuf) != 0)
return 0;
return S_ISDIR(statbuf.st_mode);
}
Yes, there is better. Check the stat or the fstat function
Normally you want to perform this check atomically with using the result, so stat() is useless. Instead, open() the file read-only first and use fstat(). If it's a directory, you can then use fdopendir()
to read it. Or you can try opening it for writing to begin with, and the open will fail if it's a directory. Some systems (POSIX 2008, Linux) also have an O_DIRECTORY extension to open which makes the call fail if the name is not a directory.
Your method with opendir() is also good if you want a directory, but you should not close it afterwards; you should go ahead and use it.
I have a very simple program here, but it seems to be returning
a "true" value to the query S_ISDIR() even when the directory
entry is not a directory. Can any one pleeas help me. I am using QNX Neurtion RTOS
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, char *argv[]) {
DIR *dir;
struct dirent *entry;
struct stat eStat;
char *root;
int i;
root = argv[1];
while((entry = readdir(dir)) != NULL) {
lstat(entry->d_name, &eStat);
if(S_ISDIR(eStat.st_mode))
printf("found directory %s\n", entry->d_name);
else
printf("not a dir\n");
}
return 0;
}
sample output:
found directory .
found directory ..
found directory NCURSES-Programming-HOWTO-html.tar.gz
found directory ncurses_programs
found directory ncurses.html
Following information may be helpful.
lstat for file is failing with errno set to 2. I am not sure why, can any one know this.
Just a guess; since you're not checking for an error after your lstat call, the eStat buffer could be containing the result of the last successful call. Try checking if lstat returns -1.
readdir() on Linux is fundamentally different, so I can't fully test on my system. See the sample programs at link text and link text. Modifying the lstat sample code, this seems to work for me:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
int main( int argc, char **argv )
{
int ecode = 0;
int n;
struct stat sbuf;
for( n = 1; n < argc; ++n ) {
if( lstat( argv[n], &sbuf ) == -1 ) {
perror( argv[n] );
ecode++;
} else if( S_ISDIR( sbuf.st_mode ) ) {
printf( "%s is a dir\n", argv[n] );
} else {
printf( "%s is not a dir\n", argv[n] );
}
}
}
I don't know if that helps any. Note that the readdir() sample code uses opendir() as schot suggested. But I can't explain why your readdir() seems to work regardless.
My compiler says: "warning: 'dir' is used uninitialized in this function" You may want to add dir = opendir(root); after you initialize root. And don't forget to add some error checking.
I doubt this causes your problem, jcomeau_ictx is probably right. If lstat returns -1 it sets errno to a value that signifies the type of error. Look at its man page and the man page for strerror
Even though this question was asked long time ago, and I found it because this quesion. but the answers here didn't really solve the problem, so I decided to post the answer which I wrote on another post, such that if anyone had the same problem and used google to find this page, there is a clear answer.
The real reason of S_ISDIR not working as expected is dp->d_name contains only the name of the file, you need to pass the full path of the file to lstat().
My code is something like this:
DIR* pDir = opendir("/path/to/my/dir");
struct dirent pFile = NULL;
while ((pFile = readdir())) {
// Check if it is a .zip file
if (subrstr(pFile->d_name,".zip") {
// It is a .zip file, delete it, and the matching log file
char zipname[200];
snprintf(zipname, sizeof(zipname), "/path/to/my/dir/%s", pFile->d_name);
unlink(zipname);
char* logname = subsstr(zipname, 0, strlen(pFile->d_name)-4); // Strip of .zip
logname = appendstring(&logname, ".log"); // Append .log
unlink(logname);
}
closedir(pDir);
(this code is untested and purely an example)
The point is: Is it allowed to delete a file in a directory while looping through the directory with readdir()?
Or will readdir() still find the deleted .log file?
Quote from POSIX readdir:
If a file is removed from or added to
the directory after the most recent
call to opendir() or rewinddir(),
whether a subsequent call to readdir()
returns an entry for that file is
unspecified.
So, my guess is ... it depends.
It depends on the OS, on the time of day, on the relative order of the files added/deleted, ...
And, as a further point, between the time the readdir() function returns and you try to unlink() the file, some other process could have deleted that file and your unlink() fails.
Edit
I tested with this program:
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
struct dirent *de;
DIR *dd;
/* create files `one.zip` and `one.log` before entering the readdir() loop */
printf("creating `one.log` and `one.zip`\n");
system("touch one.log"); /* assume it worked */
system("touch one.zip"); /* assume it worked */
dd = opendir("."); /* assume it worked */
while ((de = readdir(dd)) != NULL) {
printf("found %s\n", de->d_name);
if (strstr(de->d_name, ".zip")) {
char logname[1200];
size_t i;
if (*de->d_name == 'o') {
/* create `two.zip` and `two.log` when the program finds `one.zip` */
printf("creating `two.zip` and `two.log`\n");
system("touch two.zip"); /* assume it worked */
system("touch two.log"); /* assume it worked */
}
printf("unlinking %s\n", de->d_name);
if (unlink(de->d_name)) perror("unlink");
strcpy(logname, de->d_name);
i = strlen(logname);
logname[i-3] = 'l';
logname[i-2] = 'o';
logname[i-1] = 'g';
printf("unlinking %s\n", logname);
if (unlink(logname)) perror("unlink");
}
}
closedir(dd); /* assume it worked */
return 0;
}
On my computer, readdir() finds deleted files and does not find files created between opendir() and readdir(). But it may be different on another computer; it may be different on my computer if I compile with different options; it may be different if I upgrade the kernel; ...
I'm testing my new Linux reference book. The Linux Programming Interface by Michael Kerrisk and it says the following:
SUSv3 explicitly notes that it is unspecified whether readdir() will return a filename that has been added to or removed from since the last since the last call to opendir() or rewinddir(). All filenames that have been neither added nor removed since the last such call are guaranteed to be returned.
I think that what is unspecified is what happens to dirents not yet scanned. Once an entry has been returned, it is 100% guaranteed that it will not be returned anymore whether or not you unlink the current dirent.
Also note the guarantee provided by the second sentence. Since you are leaving alone the other files and only unlinking the current entry for the zip file, SUSv3 guarantees that all the other files will be returned. What happens to the log file is undefined. it may or may not be returned by readdir() but in your case, it shouldn't be harmful.
The reason why I have explored the question it is to find an efficient way to close file descriptors in a child process before exec().
The suggested way in APUE from Stevens is to do the following:
int max_open = sysconf(_SC_OPEN_MAX);
for (int i = 0; i < max_open; ++i)
close(i);
but I am thinking using code similar to what is found in the OP to scan /dev/fd/ directory to know exactly which fds I need to close. (Special note to myself, skip over dirfd contained in the DIR handle.)
I found the following page describe the solution of this problem.
https://support.apple.com/kb/TA21420