I want to create a dir tree in C on linux. I wrote that code:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
static int dirExists(const char *path)
{
struct stat info;
if(stat( path, &info ) != 0)
return 0;
else if(info.st_mode & S_IFDIR)
return 1;
else
return 0;
}
int main(int argc, char **argv)
{
const char *path = "./mydir/firstdir/";
if(!dirExists(path))
{
mode_t mask = umask(0);
if(mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) == -1)
exit(-1);
umask(mask);
}
printf("%d\n", dirExists(path));
return 0;
}
Its ok when a path is a single dir, lets say, path = "./mydir" but when I want to create a dir tree, for example: path = "./mydir/a/b/c/d/" dirs are not created. Why?
You have no code to create a directory tree, so your code doesn't create a directory tree. If you want to create a directory tree, write code to do that.
What you do is create a folder who 's path is path
You need to define code that is able to create a directory tree. In other word Mkdir doesn't create directory recursively.
EDIT:
In the link you've posted the parent directory already exists.
Related
I'm trying to show what folder contains as an output. When I run this program in my harddisk after 1-2 minutes it crashes, beside of crashing part it works just fine. I dont know how I can prevent this. Can anyone help me ?
#include <string.h>
#include <stdio.h>
#include <dirent.h>
void showingFiles(DIR *, char *);
int main(void) {
DIR *folder;
char path[350];
sprintf(path, ".");
folder = opendir(path);
showingFiles(folder, path);
closedir(folder);
printf("\n\nEnter a key to close this program ");
getch();
return 0;
}
void showingFiles(DIR *currentFolder, char *path){
struct dirent *nextFile;
DIR *subFolder;
char copyPath[350];
strcpy(copyPath, path);
while ((nextFile = readdir(currentFolder)) != NULL) {
sprintf(copyPath, "%s//%s", path, nextFile->d_name);
printf("%s\n", (*nextFile).d_name);
if ((strcmp(nextFile->d_name, "..")) &&
strcmp(nextFile->d_name ,".") &&
(subFolder = opendir(copyPath)) != NULL) {
deletingFiles(subFolder, copyPath);
}
}
closedir(currentFolder);
}
There are at least 3 problems in your code that can explain the crash:
the buffers used to store the complete pathnames pay be too short and you use an unsafe sprintf to construct them, potentially causing buffer overflows.
you never close the subFolder directory handles that you open in the recursive function showingFiles, potentially running out of system handles.
you do close the directory handle currentFolder in the function showingFiles(), but is is also closed in the main() function. This causes undefined behavior. As a rule of thumb, always close the handle in the function that opened it and only there.
Less important but issues:
To name showingFiles a function that performs a recursive removal of a complete directory tree is a bit misleading.
separating directory and pathnames with double slashes // is useless and not portable. You may have been thinking of \\ and converted this Windows specific directory separator into // for Unix portability, but be aware that single forward slashes are supported by the Windows file system handlers, to you should always use / as a directory separator for programs aimed for both Unix and Windows.
Here is a modified version:
#include <dirent.h>
#include <stdio.h>
#include <string.h>
void deleteTree(DIR *, const char *);
int main(void) {
char path[350] = ".";
DIR *folder = opendir(path);
if (folder != NULL) {
deleteTree(folder, path);
closedir(folder);
}
printf("\n\nEnter a key to close this program ");
getch();
return 0;
}
void deleteTree(DIR *currentFolder, const char *path) {
char copyPath[1024];
struct dirent *nextFile;
DIR *subFolder;
while ((nextFile = readdir(currentFolder)) != NULL) {
snprintf(copyPath, sizeof(copyPath), "%s/%s", path, nextFile->d_name);
printf("%s\n", nextFile->d_name);
if (strcmp(nextFile->d_name,"..")
&& strcmp(nextFile->d_name,".")
&& (subFolder = opendir(copyPath)) != NULL) {
deletingFiles(subFolder, copyPath);
closedir(subFolder);
}
}
}
So im trying to write a program in C that works like the ls command from Linux,
for the moment i've been capable of listing the files and directories inside my Current working directory, but i don't seem to be capable of doing the same for directories that are not my CWD, do i need to change it before i start listing it? or does the function opendir() work for any directory?
It has to work like ls -li from Linux but i got the printing stuff handled.
In general my program looks like this (obvsly it has more things):
void function(char *directory_to_list){
DIR *d;
struct dirent *dirp;
struct stat filestat;
if ( (d = opendir(directory_to_list)) == NULL){
//print error
}
while ( ( dirp = readdir(d) ) != NULL){
//here i call the stat() function for every entry to get various information
if (stat(dirp->d_name, &filestat) == -1){
continue;
}
//various prints
}
closedir(d);
}
EDIT: The command would be -> ls -li [-dir] so if you dont get any dir you just list your CWD.
EDIT2: No error is being returned it just does nothing, it opens the directory just fine but doenst list anything so i guess the stat call is not being done well, also added the line for how i call stat().
Here is a working version. Pay special attention that you do not overflow the buffer. you'll have to do some significant error checking to make this secure:
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
int function(char *);
int main(void)
{
function("/tmp");
return 0;
}
int function(char *path)
{
DIR *dir;
struct dirent *dentry;
struct stat filestat;
char *giantbuffer = malloc(sizeof(char) * ((PATH_MAX * 2 ) + 1) );
if ( ( dir = opendir(path) ) )
{
dentry = readdir(dir);
while ( dentry )
{
sprintf(giantbuffer, "%s/%s", path, dentry->d_name);
printf("%s ", giantbuffer);
if (stat(giantbuffer, &filestat) == 0)
printf("%zu\n", filestat.st_size);
dentry = readdir(dir);
}
closedir(dir);
}
else
return -1;
return 0;
}
I am completing cs50x (the edX (free) version of the Harvard cs50) course and am trying to be a bit tricky/lazy/test myself.
I am trying to use a C program to create all the directories I will need for my psets.
I have looked online and found that <sys/stat.h> includes the mkdir() function and therefore tried creating some nested loops to create all the necessary folders by doing something similar to mkdir {pset1,pset1/{standard,hacker},pset2,pset2{standard... to give me a directory structure like this:
pset1/Standard
pset1/Hacker
pset2/Standard
etc...
I came up with this:
#include <cs50.h>
#include <stdio.h>
#include <sys/stat.h>
int main(int argc, string argv[])
{
for(int i = 1; i <=8; i++)
{
string dir = argv[1];
sprintf(dir,"%s%i", argv[1], i);
mkdir(dir, 0777);
for(int j = 0; j<2; j++)
{
string subDir[] = {"Standard","Hacker"};
sprintf(dir,"%s%i/%s", argv[1], i, subDir[j]);
mkdir(dir, 0777);
}
}
}
However, the program only creates pset1 and completes, there are no subfolders, no pset2 etc.
Yes, you're being lazy since you seem to have very little knowledge of C, yet try to program in it. :)
C is not Python, there is no string interpolation/formatting operator. You have to call a function, specificially snprintf(). Read that manual page.
Also, you can't create a bunch of nested directories with a single call to mkdir(). Read the manual page.
To create nested directories, you're either going to have to build each's absolute path (i.e. each successive time you call mkdir() the path will be longer than the previous time), or actually enter each directory as you create it, and go from there.
To create a full path you can call mkdir() recursivly like this:
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
int mkdirr(const char * path, const mode_t mode, const int fail_on_exist)
{
int result = 0;
char * dir = NULL;
do
{
if (NULL == path)
{
errno = EINVAL;
result = -1;
break;
}
if ((dir = strrchr(path, '/')))
{
*dir = '\0';
result = mkdirr(path, mode, fail_on_exist);
*dir = '/';
if (result)
{
break;
}
}
if (strlen(path))
{
if ((result = mkdir(path, mode)))
{
char s[PATH_MAX];
sprintf(s, "mkdir() failed for '%s'", path);
perror(s);
if ((EEXIST == result) && (0 == fail_on_exist))
{
result = 0;
}
else
{
break;
}
}
}
} while (0);
return result;
}
And then call mkdirr() like this;
int main(void)
{
char p[] = "test/1/2/3";
if (-1 == mkdirr(p, 0777, 0))
{
perror("mkdirr() failed()");
}
return 0;
}
user: ls -lt
lrwxrwxrwx 1 user sw-team 9 Jun 18 19:01 new_link -> test/file
I have a soft link like mentioned above. I want to check whether new_link(not the linked file) exists or not. I tried all the below but all are checking only if the final destination file (test/file) exists or not.
access(filename,F_OK)
stat()
open()
fopen()
I want to find it in C language not in shell script.Please tell me how to find if new_link exists before checking the linked file?
Use lstat - get symbolic link status:
The lstat() function shall be equivalent to stat(), except when path refers to a symbolic link. In that case lstat() shall return information about the link, while stat() shall return information about the file the link references.
(Emphasis mine.)
lstat will return non-zero, and errno will be set to ENOENT if the link (or any other part of the path) does not exist.
Example:
#include <stdio.h>
#include <stdbool.h>
#include <sys/stat.h>
bool symlink_exists(const char* path)
{
struct stat buf;
int result;
result = lstat(path, &buf);
return (result == 0);
}
void test(const char* path)
{
bool exists = symlink_exists(path);
printf("%s does%s exist.\n", path, exists ? "" : " not");
}
int main(int argc, char** argv)
{
test("/bin/sh");
test("/etc/no_such_thing");
return 0;
}
Output:
/bin/sh does exist.
/etc/no_such_thing does not exist.
You need lstat to get link status and readlink to read the value of symlink.
I have modified Jonthon's code. Check this:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
bool symlink_exists(const char* path)
{
struct stat buf;
int ret = 0;
char *linkname;
if (lstat(path, &buf) == 0) {
// TODO: Add error handling
linkname = malloc(buf.st_size + 1);
readlink(path, linkname, buf.st_size + 1);
linkname[buf.st_size] = '\0';
printf("---> '%s' points to '%s'\n", path, linkname);
if (stat(linkname, &buf) == 0)
ret = 1;
}
return ret;
}
void test(const char* path)
{
bool exists = symlink_exists(path);
printf("%s does%s exist.\n", path, exists ? "" : " *not*");
}
int main(int argc, char** argv)
{
test("/bin/sh"); //Normal link using relative path - NOT WORKING
test("/etc/no_such_thing"); //Broken file
test("tmpp"); //Normal link using absolute path - WORKING
test("tmppp"); //Broken link
return 0;
}
Use absolute path to create your links. Otherwise you have to convert it to relative paths.
Short answer:
#include <sys/stat.h>
#include <string>
bool symlinkExists(const string &path)
{
struct stat info;
return lstat(path.c_str(), &info) == 0;
}
I am working on a POSIX C learning exercise that involves recursively listing files/folders in a specified directory. The program takes in as arguments of one or more directories. I can list the contents of the initial directory fine but having a problem with the recursion. Is something wrong with the way I am passing in the argument for the recursive function call?
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
void listdir(char *argv[])
{
DIR *mydirhandle;
struct dirent *mydirent;
struct stat statinfo;
int n = 1;
while(argv[n] != NULL)
{
if((mydirhandle = opendir(argv[n])) == NULL)
{
perror("opendir");
exit(1);
}
printf("%s/\n", argv[n]);
while((mydirent = readdir(mydirhandle)) != NULL)
{
if((strcmp(mydirent->d_name, ".") == 0) || (strcmp(mydirent->d_name, "..") == 0))
{
continue;
}
else
{
printf("\t%s\n", mydirent->d_name);
//check if next entry is a directory
if(mydirent->d_type == DT_DIR)
{
//is current directory being passed correctly here?
listdir(mydirent->d_name);
}
}
}
n++;
closedir(mydirhandle);
}
}
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("usage: %s <directory>\n", argv[0]);
return 0;
}
listdir(argv);
return 0;
}
The d_name member of struct dirent is the basename of the item in question. So, if you're going through a directory like this:
.
..
where-is/
pancakes/
.
..
house
Once you're in where-is, you'll try to listdir("pancakes") but that won't work because you need to listdir("where-is/pancakes").
You need to combine that with the name of the directory that you're looking before you have something that you can pass to the next listdir call.
You'll want to replace things like this:
listdir(mydirent->d_name);
with things like this:
char *next_dir = malloc(strlen(argv[n]) + strlen(mydirent->d_name) + 1 + 1);
sprintf(next_dir, "%s/%s", argv[n], mydirent->d_name);
listdir(next_dir);
free(next_dir);
Alternatively, you could chdir into the directories as you enter them and then chdir back up when you're done.
Turning on warnings will show that you're passing the wrong type when making the recursive function call. I would simply make listdir take a char * argument rather than char **, and then use a for loop in main to loop over multiple arguments if you need to.
You should use ftw for this, it calls a given callback on every item of the subtree. This way, you avoid using explicit recursion yourself, and your code will get much shorter.