write() undefined behavior - c

I've this code in which I receive a server response, check if it's a successful one, and if it is - create the file under the requested path and then write the response body to the file:
I'm testing it with the following URL:
http://neverssl.com/index.html
So it means that I need to create a folder named neverssl.com with an index.html file inside.
I'm creating the file using the following method:
//full_path = "http://neverssl.com/index.html"
int create_file_under_path(char *full_path) {
int fd, path_len = (int) strlen(full_path);
char *curr_path = calloc(path_len + 1, 1);
char *token = strtok(full_path, "/");
strncpy(curr_path, token, path_len);
while (token) {
if (path_len == strlen(curr_path))
break;
if (access(curr_path, F_OK) != 0) {//The folder doesn't exist
if (mkdir(curr_path, S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
perror("mkdir failed\n");
return -1;
}
}
token = strtok(NULL, "/");
strcat(curr_path, "/");
strncat(curr_path, token, strlen(token));
}
if ((fd = open(curr_path, O_CREAT | O_WRONLY, 0700)) == -1) {
perror("failed to open file\n");
return -1;
}
free(curr_path);
return fd;
}
And then I write to it using write system call as follows:
if ((fd = create_file_under_path(url->full_path)) == -1) {
free_URL(url);
free(buf);
exit(EXIT_FAILURE);
}
if (write(fd, buf + header_size, tot_read - header_size) != (buf_size - header_size)) {
perror("write:\n");
exit(EXIT_FAILURE);
}
9 out of 10 times everything works fine, but sometimes even though that the folder and the file are created, write fails with the following message: No such file or directory.
I printed the FD when this happens and it's 4 as it should be.
I've no idea how to procced with debugging it any further and will apricate the help!
Here's an example where you can see that the file exists, the fd is indeed 4 and even so it enters the if statement:

write(fd, buf + header_size, tot_read - header_size) != (buf_size - header_size)
From man 3p write:
Upon successful completion, these functions shall return the number of bytes actually written to the file associated with
fildes. This number shall never be greater than nbyte. Otherwise, -1 shall be returned and errno set to indicate the error.
Only when write() == -1 then errno is set, otherwise it is irrelevant - leftover from some other operation. The number returned by write() can be lower than the requested number of bytes.
You have to write a small loop where you loop over the bytes to be written. When by it, consider handling errno == EAGAIN.

Related

How can I detect that I open()ed a directory in C, without using stat()?

I've written a simplified "cat" function in C. It is working fine, except when one of my argument is the name of a directory.
As it is an assignement, I'm only allowed to use "open", "read" and "close" functions in my code.
When "-1" is returned by function open(file, O_RDONLY), I call function ft_display_error to display error messages such as "No such file or directory".
Yet it doesn't work when "file" is a directory: in this case open will not return "-1". It will go on some kind of infinite loop.
void ft_display_file(char *file)
{
int fd;
char buf[BUF_SIZE + 1];
int ret;
fd = open(file, O_RDONLY);
if (fd == -1)
ft_display_error(file);
else
{
ret = read(fd, buf, BUF_SIZE);
while(ret)
{
buf[ret] = 0;
write(1, buf, ret);
ret = read(fd, buf, BUF_SIZE);
}
}
close(fd);
}
int main(int ac, char **av)
{
int i;
i = 1;
while (i < ac)
{
ft_display_file(av[i]);
i++;
}
}
Instead, I would like my program to identify that my argument is a directory, and then display the following message "cat: file: Is a directory.
Opening a directory for reading with open is the low level way of accessing its contents. Not very useful for you, but it doesn't allow to test for a directory.
If you cannot use stat (which is the best option) there seems to be another trick:
According to the documentation of open
The open() function shall fail if:
...
EISDIR
The named file is a directory and oflag includes O_WRONLY or O_RDWR.
So first try to open your file with O_RDWR (read-write) and if it fails, check if errno is equal to EISDIR
Code (untested)
fd = open(file, O_RDWR);
if ((fd == -1) && (errno == EISDIR))
{
// this is a directory
}

C File reading doesn't stop

I am writing a simple server that allows sending files using HTTP protocol. I have a function that puts everything from the file into buffer.
Everything goes well before read. The file size is printed correctly. But on read program just waits.
char *get_file(char *dir) {
fprintf(stderr, "GET FILE\n");
char *buff;
int fd;
if (fd = open(dir, O_RDONLY) == -1) {
fprintf(stderr, "No such file: %s\n", dir);
exit(6);
}
size_t size = fsize(dir);
fprintf(stderr, "OPENED FILE, SIZE: %ld\n", size);
buff = malloc(size);
read(fd, buff, size);
fprintf(stderr, "to be downloaded: %s\n", buff);
char *response = make_file_response(buff);
return response;
}
You have an issue with this statement
if (fd = open(dir, O_RDONLY) == -1)
according to operator precendence == is evaluated first and thus, fd is being assigned the value of the comparison and not the opened file descriptor.
With compiler warnings enabled parentheses would be suggested, and the correted expression would be
if ((fd = open(dir, O_RDONLY)) == -1)
/* ^~~~~~~~~~~~~~~~~~~~~~~~~^ */
would first assign the return value of open() to fd and then the comparison is performed.
If you print the value of fd you will see that it's 0 if open() succeeded i.e. returned a value not -1 and 1 otherwise.

when I try to read from txt file it returns : Cannot read input file

I am trying to write a program that compares 2 files and return if their equal or not.
I can only use the functions: fork, dup, dup2, open, write, exec, read.
When I compile the program on linux gcc, it returns
Cannot read input file
shay#shay-Latitude-E6410 ~/workspace/targ1OS $ ./comp.out input.txt input.txt Cannot read input file
the code:
/*
* This function checks if the files are similar or similar by case sensitive
* it gets 2 files, and returns: 3 if identical, 2 if identical but only if not
* case sensitive or 1 else.
*/
int CheckSimilar(char *path1, char *path2){
//open the files
int fd1 = open(path1, O_RDONLY), fd2 = open(path2, O_RDONLY);
int flag = 1;//this flag is to check for case sensitive
char *firstFile = NULL, *secondFile = NULL;
int readBytes, read2ndFile;
if (fd1 == -1 || fd2 == -1){
write(2, "Cannot open input file\n", 24);
return -1;//checks if there is a problem opening the file
}
while (1){
readBytes = read(fd1, firstFile, 1);
read2ndFile = read(fd2, secondFile, 1);
if (readBytes < 0 || read2ndFile < 0){
write(2, "Cannot read input file\n", 24);
return -1;
}//checks if there is a problem reading chars from the file
if (!readBytes || !read2ndFile)
break;
if (*firstFile == *secondFile)
continue;//the chars are equal
//checks if it's an abc char
else if ((*firstFile > 64 && *firstFile < 91) ||
(*firstFile > 96 && *firstFile < 123)){
// checks for not case sensitive
if ((*firstFile - *secondFile) == 22 ||
(*firstFile - *secondFile) == -22)
flag = 0;
}
else
return 1;
}
close(fd1);
close(fd2);
if (readBytes != read2ndFile)
return 1;
if (flag)
return 2;
return 3;
}
Make yourself a better world and ask the system about errno, and read the manual about the system calls read(2), open(2),... and errno(3)
(read(2) for example is a manual page address, saying: Manual page "read" in section 2, read man man about sections).
#include <stdio.h>
#include <string.h>
#include <errno.h>
[...]
char* err = strerror(errno);
char* errlen = err ? strlen(err): 0;
char* form = "Cannot read input file since \"%s\".\n"
if (errlen == 0) {
form = "Cannot read input file failed with unknown error %d.\n";
fprintf(stderr, form, errno);
}
else {
fprintf(stderr, form, err);
}
As you cannot use fprintf, I leave it for you to write the forms. At least you should print out the errno after the failed read.
The problem is here:
You declare:
char *firstFile = NULL, *secondFile = NULL;
and then you use
read(fd1, firstFile, 1);
when firstFile is NULL, and therefore read fails.
Declare firstFile and secondFile like this:
char firstFile[1];
char secondFile[1];
Check whether the file exists in the given path or not

pclose() on file descriptor opened with popen() returns errno 10 (No child processes)

I'm running linux and I try to do the following:
Run ls on current directory (using popen)
Output the result to buffer (using fread from pipe descriptor)
close pipe (using pclose).
Everything works fine (the buffer is filled correctly with the ls result) but when I check
pclose() result it returns -1 and errno is set to 10 (No child processes). Have no idea why
this is happening but I can't ignore it (unless there is a reasonable explanation to why this is happening).
My code:
FILE * lsoutput = NULL;
lsoutput = popen("ls -ltr", "r");
if (readFromPipeOrFile(lsOutput, pSendBuf, pSendActualSize) == -1)
{
printf("readFromPipeOrFile failed.");
pclose(lsOutput);
safeFree(pSendBuf);
return -1;
}
if (pclose(lsOutput) == -1) // No idea why it returns -1 but it does...
{
printf("pclose failed");
printf("errno: %d\n", errno);
printf("strerror: '%s'", strerror(errno));
return -1;
}
The code for readFromPipeOrFile (the function writing to the buffer):
int readFromPipeOrFile(FILE * pipeOrFile, char ** pSendBuf, size_t * pSendActualSize)
{
int multiplication = 1;
char * pSendBufCurrentLocation = NULL;
ERR_RETURN(pipeOrFile == NULL || pSendBuf == NULL || pSendActualSize == NULL,
"No values should be NULL.");
ERR_RETURN(*pSendBuf != NULL, "*pSendBuf should be NULL");
*pSendBuf = (char *) calloc (MAX_READ_FROM_STREAM * multiplication, sizeof(char));
ERR_RETURN(*pSendBuf == NULL, "Failed allocating sendBuf");
pSendBufCurrentLocation = *pSendBuf;
while (fread(pSendBufCurrentLocation, MAX_READ_FROM_STREAM, 1, pipeOrFile) == 1)
{
++multiplication;
*pSendBuf = realloc(*pSendBuf, MAX_READ_FROM_STREAM * multiplication);
ERR_RETURN(*pSendBuf == NULL, "Failed re-allocating sendBuf");
pSendBufCurrentLocation = *pSendBuf + (MAX_READ_FROM_STREAM * (multiplication - 1));
memset(pSendBufCurrentLocation, '\0', MAX_READ_FROM_STREAM);
}
ERR_RETURN(!feof(pipeOrFile), "Hasn't reached eof but fread stopped");
ERR_RETURN(ferror(pipeOrFile), "Error in fread");
*pSendActualSize = MAX_READ_FROM_STREAM * multiplication;
return 0;
}
Thanks in advance!
EDIT: ERR_RETURN is just a macro that checks if the condition on the first paramter is true and if so, print the string in the second parameter and return -1.
signal(SIGCHLD, SIG_IGN) will cause pclose() result it returns -1 and errno is set to 10 (No child processes).
According to the documentation for pclose(), it returns -1 if wait4 returns an error. However, it says nothing of setting errno (other than setting ECHILD if it was unable to get a child status) so I'm not so sure you can count on the strerror() string being accurate.
The implication is that ls exited with an error code which would imply that ls had trouble accessing a subdirectory (permission error?). So, you have results because ls returned what it could read, but it is an incomplete read because it could not access a subdirectory in the recursion.

How to rewrite full content of a file in C

I have text file which uses for ajax source. Every 1 sec browser sends ajax request to read actual data from this file.
Also I have deamon written on C which writes actual data to that file. Look at the following code:
static void writeToFile_withLock(const char * file_path, const char * str)
{
struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 };
int fd;
const char * begin = str;
const char * const end = begin + strlen(str);
fl.l_pid = getpid();
if ((fd = open(file_path, O_CREAT | O_WRONLY)) == -1) {
perror("open");
exit(1);
}
printf("Trying to get lock...\n");
if (fcntl(fd, F_SETLKW, &fl) == -1) {
perror("fcntl");
exit(1);
}
printf("got lock\n");
printf("Try to write %s\n", str);
while (begin < end)
{
size_t remaining = end - begin;
ssize_t res = write(fd, begin, remaining);
if (res >= 0)
{
begin += res;
continue; // Let's send the remaining part of this message
}
if (EINTR == errno)
{
continue; // It's just a signal, try again
}
// It's a real error
perror("Write to file");
break;
}
fl.l_type = F_UNLCK; /* set to unlock same region */
if (fcntl(fd, F_SETLK, &fl) == -1) {
perror("fcntl");
exit(1);
}
printf("Unlocked.\n");
close(fd);
}
The problem: If former data was > the new data then old several symbols keeped at the end of the file.
How I can rewrite full file content?
Thanks in advance.
Add O_TRUNC to the open() call...
O_TRUNC
If the file already exists and is a regular file and the open mode
allows writing (i.e., is O_RDWR or O_WRONLY) it will be truncated to
length 0. If the file is a FIFO or terminal device file, the O_TRUNC
flag is ignored. Otherwise the effect of O_TRUNC is unspecified.
You basically have two options. Either set the O_TRUNC bit of the 2nd parameter of open to discard all content when you open the file, or call ftruncate when you are finished to discard the content of the file that you do not want. (Or use truncate, but since you already have an open file descriptor, there's no advantage to doing that.)

Resources