C, trouble with stat() - c

I have never used stat() before and am not sure what is going wrong.
I have a server program that takes a GET request and parses out the file path. I also have a client program in the same directory that sends the GET request. The server program is taking the GET request and parsing out the file path correctly. The path to the directory where both programs are is: ~/asimes2/hw2/
If I have the client program send: GET /Makefile HTTP/1.0\r\n\r\n
Then the server program receives the same thing. I have two printf()s to confirm I am parsing the file path correctly and to see the full path. It outputs:
File path = '/Makefile'
Full path = '~/asimes2/hw2/Makefile'
NOT FOUND!
Makefile does exist in ~/asimes/hw2. Here is the code:
// Alex: Parse the PATH from the GET request using httpGet
char* filePath, * pathStart = strchr(httpGet, '/');
if (pathStart != NULL) {
// Alex: Increment to the first '/'
httpGet += (int)(pathStart-httpGet);
// Alex: Assuming " HTTP" is not a part of the PATH, this finds the end of the PATH
char* pathEnd = strstr(httpGet, " HTTP");
if (pathEnd != NULL) {
int endLoc = (int)(pathEnd-httpGet);
filePath = (char*)malloc((endLoc+1)*sizeof(char));
strncpy(filePath, httpGet, endLoc);
filePath[endLoc] = '\0';
}
else errorMessageExit("The GET request was not formatted as expected");
}
else errorMessageExit("The GET request was not formatted as expected");
printf("File path = '%s'\n", filePath);
char* fullPath = (char*)malloc((14+strlen(filePath))*sizeof(char));
strcpy(fullPath, "~/asimes2/hw2");
strcat(fullPath, filePath);
printf("Full path = '%s'\n", fullPath);
struct stat fileStat;
if (stat(fullPath, &fileStat) == -1) printf("NOT FOUND!\n");
else printf("HOORAY\n");

My answer only addresses your issue with the file name.
The shell interprets this: ~/asimes2/hw2/Makefile
It's not a valid filename to pass to stat() with the ~
You should be able replace the leading ~ with something link /home/ or wherever the actual home directory is.
Try this:
char* fullPath = malloc((80+strlen(filePath))*sizeof(char));
strcpy(fullPath, "/home/ubuntu/asimes2/hw2");
strcat(fullPath, filePath);
printf("Full path = '%s'\n", fullPath);

You need to glob pathnames, see glob(7). You could perhaps use wordexp(3) to expand the ~, $ etc...
HTTP servers usually have some configurable document root, perhaps /var/www. Then the URL pathname /Makefile is transformed to /var/www/Makefile
You should perhaps use some HTTP server library like libonion
And you should use errno at least for debugging purposes on syscall failure, so code
if (stat(fullPath, &fileStat) == -1)
printf("%s NOT FOUND! %s\n", fullPath, strerror(errno));
Perhaps chroot(2) might interest you. And read Advanced Linux Programming!

Related

Distinguishing between entered commands in C

I am working on a project in C. It is a primitive file system shell and one of commands can either take in a string after it or not. Here is how I am reading in commands from stdin. (This way is specified by my instructor):
fsys* f = newFileSystem();
char cmd[21];
char path[1001];
while(scanf("%s", cmd) > 0){
if(strcmp(cmd, "ls") == 0){
// this is probably the wrong way to go about it but...
if(fgets(path, 1001, stdin)){ // 1001 is max path length
strtok(path, "\n"); // doesn't remove if path is only \n
// fgets reads in \n character and it affects output
// later, so getting rid of it now
strtok(path, " "); // doesn't remove space between ls and path name
fs_ls(f, path);
}
}
else if(strcmp(cmd, "pwd") == 0){
// prints path to cwd beginning at root
fs_pwd(f);
// some other commands in here
else{
printf("Command not recognized.");
}
}
So ls can either take in no path (defaults to current working directory) or can take in a path and list files and directories at that location.
What can I write within the if statement "if(strcmp(cmd, "ls") == 0)" for the system to check if there is a path after ls or not and execute ls correctly?

open() file does not exists when file actually exists

I'm writing a simple HTTP server and I'm getting a file does not exists return value when the file does exist
printf("%s\n", html_path);
if ((fd = open(html_path, "r")) >= 0){ //file found
stat(file_name, &st);
file_size = st.st_size;
printf("%d\n", file_size);
while (read(fd, response, RCVBUFSIZE) > 0){
}
}
else { //file not found
strcpy(response, "404 File not found\n");
send(clntSocket, response, 32, 0);
}
the print statement is to verify the path and it looks like this:
/mounts/u-zon-d2/ugrad/kmwe236/HTML/index.html
note that this path is on a server that we use at our university. it's the path that shows when I command pwd
I have confirmed that the file exists. is there something wrong with my path?
There was an error opening the file, but you don't know that it was because the file was not found because you're didn't check the value of errno.
In the else section, add the following:
else { //file not found
// copy the value of errno since other function calls may change its value
int err = errno;
if (err == ENOENT) {
strcpy(response, "404 File not found\n");
send(clntSocket, response, 32, 0);
} else {
printf("file open failed: error code %d: %s\n", err, strerror(err));
}
}
If the file does not in fact exist you'll handle the error properly. If not, you'll print an error message that tells you what did happen.
You're also calling open incorrectly. The second parameter is an int containing flags. To open a file for reading, use O_RDONLY.
open does not have the 2nd parameter as a string. You using open with the parameters of fopen.
For a webserver fopen, fprintf, fclose is a better choise then more lowlevel open, read, ...
Cheers,
Chris
You need to check where you program is executing as it will try to open the path relative from that location. To check use:
char cwd[1024];
getcwd(cwd, sizeof(cwd));
puts(cwd);
Then you can concatenate your path using:
strncat(cwd, html_path, 100);
You may find that you have to go up one directory or something to then find the file you're looking for.
Also note that if you're debugging your program via gdb it may execute from a different location from your regular build location which may make it harder to spot bugs.

Open function C "file not found" on Xcode but terminal works

I work on Xcode and I have a simple function that opens a file using open in C.
void mfs_workwith() {
char *token, *temp_token;
char *search = ".";
temp_token = (char*)malloc(sizeof(char)*strlen(cc[1]));
strcpy(temp_token, cc[1]);
if ((token = strtok(temp_token, search)) == NULL) {
printf("mfs_workwith command is only used with mfs type files e.g. example.mfs \n");
} else if ((token = strtok(NULL, " \n\0")) == NULL) {
printf("mfs_workwith command is only used with mfs type files e.g. example.mfs \n");
} else if (strcmp(token, "mfs") == 0) {
filename = malloc(sizeof(char)*strlen(cc[1]));
strcpy(filename, cc[1]);
if ((file_mfs = open(filename, O_RDWR)) == -1) {
perror("open error");
} else {
printf("open successful \n");
}
}
}
The name of the file is stored in a global array and then copied into local buffers in order to tokenize and check if it has the right format (.mfs).
Then if everything is ok I make a fresh copy of the name of the file and call open with it.
My problem is that when I run my program in terminal it runs fine, prints open successful and then continues. But when I try to run it in Xcode it fails with this error:
No such file or directory
I am giving the input file.mfs which is the name of a file in the same directory.
Am I missing something obvious?
I found the problem thanks to iharob's comment. It seems xcode has a hard time opening relative paths since it uses a different file while running the program. There is a relative discussion here:
Open method opens files with full path only C++
thanks again everyone.
This:
filename = malloc(sizeof(char)*strlen(cc[1]));
strcpy(filename, cc[1]);
is broken, it fails to allocate room for the string's terminator, so it causes buffer overflow and undefined behavior.
Also, you never need to scale by sizeof (char), that's always 1. It should be:
filename = malloc(strlen(cc[1]) + 1);
strpcy(filename, cc[1]);
or, if you have it, just:
filename = strdup(cc[1]);

rename() only works for directory that program is running in?

I am trying to rename a bunch of files in a user specified directory, but it only seems to be working when the user specifies the directory that the program is running from. For example, when running from the command line:
./a.out . "NewName.txt" will work, while
./a.out .. "NewName.txt" will not work. Is there a reason for this? It's on Linux, by the way.
int main(int argc, char** argv){
char* dirpath = argv[1];
char* newName = argv[2];
DIR *d;
struct dirent *dir;
d = opendir(dirpath);
if (d){
while ((dir = readdir(d)) != NULL){
char* filename = dir->d_name;
if (rename(filename,newName) == 0){
printf("Renaming %s -> %s\n",filename,newName);
} else {
printf("Could not rename %s\n",filename);
}
}
}
closedir(d);
}
I have also tried (while running the program from outside of Desktop):
if (rename("~/Desktop/test.txt","~/Desktop/test2.txt") == 0){
printf("Renaming %s -> %s\n",filename,newName);
} else {
printf("Could not rename %s\n",filename);
}
and it still fails.
While readdir() is reading file names from the other directory, your program's current directory is still in a different location. Unless you prefix the source file name with the path to the directory (and the destination file name too) you're trying to rename non-existent files in the current directory, in general.
In pseudo-code:
dir = opendir(remote_directory)
foreach name from dir
rename "remote_directory/name" to "remote_directory/othername"
end for
Note that the pseudo-code works if 'remote_directory' happens to be ., the current directory; you don't need to special-case that code.
I believe that your main problem is that the result from readdir is just the filename. It doesn't include the directory. You need to paste the directory name and the filename from dir->d_name together in your program.
From the documentation:
The old argument points to the pathname of the file to be renamed.
The new argument points to the new pathname of the file.
If the new argument does not resolve to an existing directory entry for a
file of type directory and the new argument contains at least one non-<slash>
character and ends with one or more trailing <slash> characters after all symbolic
links have been processed, rename() shall fail
Looks like you're not referring to an existing element when you use any path other than '.', which is likely why it's failing.
Check the specific errno value to see why.

Issue on file existence in C

Here is my code which checks if the file exists :
#include<stdio.h>
#include<zlib.h>
#include<unistd.h>
#include<string.h>
int main(int argc, char *argv[])
{
char *path=NULL;
FILE *file = NULL;
char *fileSeparator = "/";
size_t size=100;
int index ;
printf("\nArgument count is = %d", argc);
if (argc <= 1)
{
printf("\nUsage: ./output filename1 filename2 ...");
printf("\n The program will display human readable information about the PNG file provided");
}
else if (argc > 1)
{
for (index = 1; index < argc;index++)
{
path = getcwd(path, size);
strcat(path, fileSeparator);
printf("\n File name entered is = %s", argv[index]);
strcat(path,argv[index]);
printf("\n The complete path of the file name is = %s", path);
if (access(path, F_OK) != -1)
{
printf("File does exist");
}
else
{
printf("File does not exist");
}
path=NULL;
}
}
return 0;
}
On running the command ./output test.txt test2.txt
The output is:
$ ./output test.txt test2.txt
Argument count is = 3
File name entered is = test.txt
The complete path of the file name is = /home/welcomeuser/test.txt
File does not exist
File name entered is = test2.txt
The complete path of the file name is = /home/welcomeuser/test2.txt
File does not exist
Now test.txt does exist on the system:
$ ls
assignment.c output.exe output.exe.stackdump test.txt
and yet test.txt is shown as a file not existing.
Please help me understand the issue here. Also, please feel free to post any suggestions to improve the code/avoid a bug.
Regards,
darkie
Just because the call to access() fails does not mean that the file does not exist. The call could fail for other reasons.
Use printf("error:%s\n", strerror(errno)); to print out the text of the error message.
Also you are still incorrectly appending to "path" received from getcwd as you were in your previous question. Even though it is not crashing, it is still not correct and could cause you problems... possibly even the problem you have now.
getcwd() allocates a buffer for your path, but that buffer is only sized to fit the path. you are appending to that buffer, going past the end. That's bad, you can't do that. It will cause problems, and occasionally crashes. you need to pause and understand how this getcwd function works and how to properly use it.
I strongly suggest allocating enough room to store the path via malloc() and fpathconf() (hint, PATH_MAX).
A non-standard way of allocating and assembling it would be asprintf().
Just be sure to free the resulting path when its no longer needed, and check every call that could possibly fail due to user typos for failure.
If using malloc(), always check for failure (the result being NULL).
Good luck with your assignment :)

Resources