Create Directory and Store File (C programming) - c

I want to create a directory that has the name ends with the process ID (to make it unique) and then store the new files that I just wrote inside that directory.
What I want:
1) Create a new directory named : mydirectory.1923 (example of the process id number)
2) Store a file that I just created using
FILE * fPointer = fopen("new.txt",w+)
into mydirectory.1923
What I have so far is this:
int bufSize = 20;
int pid = getpid();
char *fileName = malloc(bufSize);
char *prefix = "that.rooms.";
snprintf(fileName, bufSize,"%s%d", prefix, pid);
printf("%s\n",fileName);
struct stat st = {0};
if (stat(fileName, &st) == -1) {
mkdir(fileName, 0755);
}
DIR *dir = opendir (fileName);
if (dir != NULL) {
FILE *fLib = fopen("library.txt" , "w+");
fclose(fLib);
}
closedir(fileName);
return 0;
My Question:
This code doesn't work, apparently it says error on the DIR part.
Is this the right thing to do if I want to create directory, create file and store that file directly to the new directory?
Is there any suggestion or advice to do it better than this? Thank you.

Some comments:
As commented above, you should really allocate more space for your dir's name, a 5 digit pid will break your code right away. There are the macros PATH_MAX and FILE_MAX in limits.h
You don't need to open the directory for anything. This is usually used to iterate over items in directories. You only want to create a file in it.
Even if you don't really need it, the closedir function receives a DIR * argument, which I suppose would be dir in your code.
To create the file inside the new directory you should include your dir in the path on creation, or at least chdir to it before the fopen call. I don't recommend the latter as it affects the process wide working directory.
Below is a quick and dirty patched version of your code that creates the directory and the new file inside it taking into account the above:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <limits.h>
int main(int argc, char **argv){
int pid = getpid();
char dirName[NAME_MAX+1];
char *prefix = "that.rooms.";
snprintf(dirName, NAME_MAX + 1,"%s%d", prefix, pid);
printf("%s\n",dirName);
struct stat st = {0};
if (stat(dirName, &st) == -1) {
if(mkdir(dirName, 0755) != -1){
char libPath[PATH_MAX+1];
snprintf(libPath, PATH_MAX + 1, "%s/library.txt", dirName);
FILE *fLib = fopen(libPath , "w+");
fclose(fLib);
}else{
perror("mkdir: ");
}
}
return 0;
}
Changed the variable names a bit so it's clearer:
dirName is used to hold the directory name. It uses NAME_MAX as this is the system limit for the length of a file name
libPath is used to hold the path to the library.txt file you are creating. If your pid was 3123, libPath would read that.rooms.3123/library.txt after the snprintf call. It uses PATH_MAX as this is the system limit for a the length of a file's path.

Related

How to properly use realpath() for reading multiple files within a directory?

This is supposed to be a program that reads multiple files within a directory and gets the frequency of a given word across those files, I know that part is incomplete for now.
fopen() is returning null to files that exist and have permission 777.
here is permissions from the terminal:
ra#ra-VirtualBox:~/Desktop/lab2/folder$ ls -l
total 12
-rwxrwxrwx 1 ra ra 21 mar 14 23:20 file1.txt
-rwxrwxrwx 1 ra ra 21 mar 14 23:20 file2.txt
-rwxrwxrwx 1 ra ra 21 mar 14 23:20 file3.txt
and here is the output obtained after using errno as suggested by this answer
with buf printed
READING FILE: /home/ra/Desktop/lab2/file3.txt
fopen: No such file or directory
READING FILE: /home/ra/Desktop/lab2/file1.txt
fopen: No such file or directory
READING FILE: /home/ra/Desktop/lab2/file2.txt
fopen: No such file or directory
Edit: from output I noticed that realpath() somehow erased an entire subdirectory before going to the file. /folder/ is not there and /home/ra/Desktop/lab2/file*.txt does not exist!
And the code as follows:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <pthread.h>
#include <limits.h> //For PATH_MAX
typedef struct {
FILE * fptr;
char * word;
}
inputFile;
void * readFrequenciesFromFile(void * path) {
static int count = 0;
}
int main(int argc, char * argv) {
int i;
char buf[PATH_MAX + 1];
char * wordToSearch = "test";
DIR * dir;
FILE * entry_file;
struct dirent * in_file;
// notice that the full directory is used here
// but buf somehow does magic and tells me the files
// are in /lab2/ directory
dir = opendir("/home/ra/Desktop/lab2/folder");
if (dir == NULL) {
printf("Error! Unable to read directory");
exit(1);
}
while ((in_file = readdir(dir)) != NULL) {
if (!strcmp(in_file -> d_name, "."))
continue;
if (!strcmp(in_file -> d_name, ".."))
continue;
realpath(in_file -> d_name, buf);
entry_file = fopen(buf, "r");
// printf("READING FILE: %s\n", buf);
if (!entry_file) perror("fopen");
if (entry_file != NULL) {
pthread_t tid;
inputFile * args = malloc(sizeof * args);
args -> fptr = malloc(sizeof entry_file);
args -> word = wordToSearch;
if (pthread_create( & tid, NULL, readFrequenciesFromFile, args) == 0) {
printf("Creating thread for file [%s] with ID %ld\n", in_file -> d_name, tid);
} else {
free(args);
}
fclose(entry_file);
}
}
closedir(dir);
return 0;
}
I will mention that I'm using Ubuntu 18.04 on VirtualBox Version 6.1.10 r138449 (Qt5.6.2)
Where do I begin to solve this problem? Is this even plausible or have I missed something?
You would have a better idea of what is going wrong if you consistently checked the return values of your function calls to recognize when they fail. In this case, you might have been clued in if you had checked the return value of the realpath() calls.
I will suppose that your home directory is /home/ra as your code suggests. Observe that the files you want to open are in /home/ra/Desktop/lab2/folder. You are successfully opening that directory and reading its entries, and those entries give the base names of the files within -- that is, the names without any path components.
Whether you try to resolve those file names by opening the files or by computing their realpath()s, you are doing so relative to the working directory. But the way you are launching the program, its working directory is /home/ra/Desktop/lab2, not /home/ra/Desktop/lab2/folder, so indeed the files the program is asking for don't exist.
Among your options are
run the program from the folder containing the files
form correct paths to the files with which to open them -- either correct absolute paths or correct paths relative to the actual working directory.
I see ra#ra-VirtualBox:~/Desktop/lab2/folder$ has folder but READING FILE: /home/ra/Desktop/lab2/file3.txt does not. The problem is that the default directory is ~/Desktop/lab2/, and that is what realpath() uses; perhaps adding chdir() will help:
chdir("/home/ra/Desktop/lab2/folder");
dir = opendir("/home/ra/Desktop/lab2/folder");

Create md5sum in C

Im trying to use the md5sum command in a C program, right now im using dirent.h to get all the files in a folder, now, I want to get all the md5 of all those files, I am doing this:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <dirent.h>
int main(void){
char *word = ".gz";
int i=0;
char *word2 = ".";
char *word3 = "..";
unsigned int md5;
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d) {
while ((dir = readdir(d)) != NULL)
{
if((strstr(dir->d_name, word) == NULL) && (strcmp(dir->d_name, word2) != 0) && (strcmp(dir->d_name, word3)!= 0)) {
md5 = system("md5sum dir->d_name");
printf("The md5 of %s is %d\n", dir->d_name, md5);
}
}
}
return(0);
}
but when I run it, it says, for example:
md5sum: dir-: No such file or directory
The md5 of ej1_signal.c is 256
md5sum: dir-: No such file or directory
The md5 of pipeL.c is 256
Could you please explain me why is this happening? Thanks !
The system function doesn't returns you what you think. system is used to launch a command and when that command finished, it (generally) exits with an exit code. This is the value you catched.
What you need is the output of the command not its return value. So what you need is popen which lets you launch some external command and read/write to it through a pipe. See http://pubs.opengroup.org/onlinepubs/009695399/functions/popen.html for example.
system does not return the output of a command. To get the output of a command, you need to create a process and tie the standard output stream to a file descriptor you can read data off in the other process. For an example on how to do that, you can refer to the pipe man page (section 2).
Another option is to use a library that provides an MD5 implementation (eg. OpenSSL). The man page of EVP_DigestInit (section 3) provides an example for that.
Another problem is that your code tries to calculate the digest of d->d_name, not the file which name is in d->d_name. You could use sprintf or strncat with a suitably sized buffer (ie. the length of the static string part md5sum plus the maximum size of the file name (usually 256 bytes, may vary between library implementations and file systems) plus another byte for safely terminating the string (as some implementations may report an unterminated string in d->d_name)). Please note that this does not apply if you use a library for digest calculation, as the library uses either the file name or you need to pass the file contents to a library function (eg. EVP_DigestUpdate).
The first problem is that you launch a new shell process executing "md5sum dir->d_name", meaning it does a md5 on the "file" named dir->d_name, instead of using the value you get from readdir.
So you could add a temp variable, and prepare the command in it prior to running system.
limits.h is for Linux, adjust it if necessary to get the max length of a path
...
#include <linux/limits.h>
char temp[PATH_MAX];
then instead of
md5 = system("md5sum dir->d_name");
add
strcpy(temp, "md5sum ");
strcat(temp, dir->d_name);
system(temp);
as for the other problem (system will not return the md5 string), this will display the md5 of the file in the directory. And you can just remove the printf ...
There is no command in C to return the output of an external command, but there exists popen you can just open a command as a FILE * and read the output from it. This is how you can do it, and it's all explained within the code
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
int main(void)
{
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d == NULL)
return -1;
while ((dir = readdir(d)) != NULL)
{
char command[sizeof dir->d_name + 10];
struct stat st;
FILE *pipe;
if (stat(dir->d_name, &st) == -1)
continue;
/* check if the entry is a directory, md5sum does not work with them */
if (S_ISDIR(st.st_mode) != 0)
continue;
/*
* md5sum dir->d_name will pass `dir->d_name` as the argument to the md5sum command,
* we need to build the command string, I like snprintf in this case
*/
snprintf(command, sizeof command, "md5sum \"%s\"", dir->d_name);
/*
* Open the pipe, it will execute the new command in a new process (fork)
* and create a pipe for communication with the current porcess
*/
pipe = popen(command, "r");
if (pipe != NULL)
{
char md5[33];
/* read the md5 digest string from the command output */
fread(md5, 1, sizeof md5 - 1, pipe);
/* append a null terminator */
md5[sizeof md5 - 1] = '\0';
printf("The md5 of %s is %s\n", dir->d_name, md5);
}
/* close the pipe */
pclose(pipe);
}
/* you should always call closedir() if opendir() succeded */
closedir(d);
return 0;
}

Print out file names and its' sizes in C

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 '..'.

C Test For File Existence Before Calling execvp

I'm writing a UNIX minishell on ubuntu, and am trying to add built-in commands at this point. When it's not a built-in command I fork and then the child executes it, however for built-in commands I'll just execute it in the current process.
So, I need a way to see if the files exist(if they do it's not a built-in command), however execvp uses the environment PATH variable to automatically look for them, so I have no idea how I would manually check beforehand.
So, do you guys know how I could test an argument to see if it's a built-in command simply by supplying the name?
Thanks guys.
I have tested the answer by Tom
It contained a number of problems. I have fixed them here and provided a test program.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
int is_file(const char* path) {
struct stat buf;
stat(path, &buf);
return S_ISREG(buf.st_mode);
}
/*
* returns non-zero if the file is a file in the system path, and executable
*/
int is_executable_in_path(char *name)
{
char *path = getenv("PATH");
char *item = NULL;
int found = 0;
if (!path)
return 0;
path = strdup(path);
char real_path[4096]; // or PATH_MAX or something smarter
for (item = strtok(path, ":"); (!found) && item; item = strtok(NULL, ":"))
{
sprintf(real_path, "%s/%s", item, name);
// printf("Testing %s\n", real_path);
if ( is_file(real_path) && !(
access(real_path, F_OK)
|| access(real_path, X_OK))) // check if the file exists and is executable
{
found = 1;
}
}
free(path);
return found;
}
int main()
{
if (is_executable_in_path("."))
puts(". is executable");
if (is_executable_in_path("echo"))
puts("echo is executable");
}
Notes
the test for access return value was reversed
the second strtok call had the wrong delimiter
strtok changed the path argument. My sample uses a copy
there was nothing to guarantee a proper path separator char in the concatenated real_path
there was no check whether the matched file was actually a file (directories can be 'executable' too). This leads to strange things like . being recognized as an external binary
What you can do is you can change the path to the particular directory and then use #include<dirent.h> header file and its readdir and scandir functions to walk through the directory or stat structure to see if the file exists in the directory or not.
You can iterate yourself through the PATH directories, and for each entry in PATH (You will have to split PATH with :, probably using strtok) concatenate at the end of each path the name of the command called. When you have create this path, check if the file exists and if it is executable using access.
int is_built_in(char *path, char *name)
{
char *item = strtok(path, ":");
do {
char real_path[4096] = strcat(item, name); // you would normally alloc exactly the size needed but lets stick to it for the sake of the example
if (!access(real_path, F_OK) && !access(real_path, X_OK)) // check if the file exists and is executable
return 0;
} while ((item = strtok(NULL, ":")) != NULL);
return 1;
}
Why do you want to test before calling execvp? That's the wrong approach. Just call execvp and it will tell you if the program does not exist.

Trying to read in the files of a directory and write it to a list

I'm trying to create 5000 junk files, write them to a file and delete them. But this code only is writing a portion of the files to the file. ls -l | grep ^- | wc -l says I have 1598 files remaining in the directory that is supposed to be emptied with unlink();. If I remove close(fd) I get a seg fault if I do any more than 1000 files. Any suggestions?
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
main (int argv, char *args[]){
if(argv<3){
printf("Please run with proper command line arguements.\n");
return;
}
int numFiles = atoi(args[1]);
char *fileName = args[2];
char *fileList[numFiles];
int x, ret,fd;
char buff[50];
for(x=0;x<numFiles;x++){
ret = sprintf(buff,"./stuff/%s-%d.junk",fileName, x);
fd = creat(buff);
close(fd);
}
DIR *odir = opendir("./stuff");
struct dirent *rdir = NULL;
FILE *fp;
fp = fopen("./files.list", "w");
x=0;
while(rdir = readdir(odir)){
char* name = rdir->d_name;
ret = sprintf(buff,"./stuff/%s-%d.junk",fileName, x);
if(strcmp(name,"..")!=0){
if(strcmp(name,".")!=0){
fprintf(fp,"%s %d\n",name,x);
x++;
}
}
unlink(buff);
}
close(fp);
closedir(odir);
}
Thanks!
Note: Use of creat(), opendir(), readdir() and unlink() were required for the assignment. And as for error checking, your right of course but I'm under time constraints and the TA really, really doesn't care... But thank you all!
Here you're using fopen:
FILE *fp;
fp = fopen("./files.list", "w");
But then you're using close instead of fclose to close it:
close(fp);
I'm not at all sure this is what's causing the problem you're seeing, but it's definitely wrong anyway. You probably just want unlink(rdir->d_name) instead of unlink(buff). You embedded the number into the file name when you created it -- you don't need to do it again when you're reading in the name of the file you created.
You're removing things from the directory while calling readdir; I think that's supposed to work OK, but you might want to consider avoiding it.
More to the point: as you iterate over the directory with readdir you're potentially removing different files from the ones readdir is listing. (Because what you pass to unlink is buff which you've filled in from the steadily-incrementing x rather than from anything returned by readdir.) So, here's a toy example to show why that's problematic. Suppose the directory contains files 1,2,3,4 and readdir lists them in the order 4,3,2,1.
readdir tells you about file 4. You delete file 1.
readdir tells you about file 3. You delete file 2.
readdir would have told you about file 2, but it's gone so it doesn't.
readdir would have told you about file 1, but it's gone so it doesn't.
You end up with files 3 and 4 still in the directory.

Resources