C File reading doesn't stop - c

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.

Related

Why does the system call "read()" block my program?

I am contacting you because I have a problem with my program. For my studies, I am currently programming in C a function to read a file, I am only allowed to use the following functions/system calls: malloc, free, exit, (f)open, (f)close, (f)read, (f)write, getline, ioctl, usleep, sigaction, signal, stat, lstat, fstat
The problem is that when calling my function my_filetostr(), it blocks the program at the read() system call and nothing happens.
here's my code:
char *my_filetostr(const char *filepath)
{
int fd = 0;
struct stat s;
char *buff = NULL;
if (fd = open(filepath, O_RDONLY) > 0) {
stat(filepath, &s);
buff = malloc(sizeof(char) * s.st_size + 1);
my_printf("fd : %d, size : %d\n", fd, s.st_size);
if (read(fd, buff, s.st_size) == s.st_size) {
buff[s.st_size] = '\0';
}
close(fd);
}
return buff;
}
I specify that my file exists (my error handling works).
I tried to put some my_printf (we had to recode printf) in order to see where the program stops and to display some useful information:
So I know that the problem comes from read()
st_size returns the right size and filedescriptor has a correct value.
The program is just supposed to move on.
You are assigning to fd inside the if condition.
This usually causes bugs and should be avoided
Your issue here is specifically because > has higher precedence compared to =, so fd becomes 1 which is stdout.
I have modified your code to highlight the issue
https://godbolt.org/z/M4PbcPfev
fd : 1, size : 140728905723182
Thanks you #stark , #user3840170 , #tejas
My code now works :
char *my_filetostr(const char *filepath)
{
int fd = -1;
struct stat s;
char *buff = NULL;
if ((fd = open(filepath, O_RDONLY)) != -1) {
fstat(fd, &s);
buff = malloc(sizeof(char) * s.st_size + 1);
if (read(fd, buff, s.st_size) == s.st_size) {
buff[s.st_size] = '\0';
}
close(fd);
}
return buff;
}
I have performed the following actions:
Replace stat with fstat (but that didn't solve the problem)
Replace int fd = 0 by int fd = -1
Replace fd = open(filepath, O_RDONLY) > 0 par (fd = open(filepath, O_RDONLY)) != -1
Thank you for your answers.

write() undefined behavior

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.

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
}

Copy data from file X to file Y program in C

I tried to write basic program in C which copy data from file to another with given source path, destination path and buffer size as input.
my problem is the destination file filled with junk or something because its way larger than the source (get bigger depending on buffer size) and can't be open.
How do i read and write just the bytes in the source?
i'm working in linux, and this is the actually copying part:
char buffer[buffer_size];
int readable=1;
int writeable;
while(readable != 0){
readable = read(sourcef, buffer, buffer_size);
if(readable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not read.");
}
writeable = write(destf, buffer, buffer_size);
if(writeable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
writeable = write(destf, buffer, buffer_size);
must be
writeable = write(destf, buffer, readable);
Currently you do not write the number of characters you read but all the buffer, so the output file is too large
You also manage wrongly the end of the input file
The return value of read is :
On success, the number of bytes read is returned (zero indicates end of file)
On error, -1 is returned
A proposal :
/* you already check input and output file was open with success */
char buffer[buffer_size];
for(;;){
ssize_t readable = read(sourcef, buffer, buffer_size);
if(readable <= 0){
close(sourcef);
close(destf);
if (readable != 0)
/* not EOF */
exit_with_usage("Could not read.");
/* EOF */
break;
}
if (write(destf, buffer, n) != n) {
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
I suppose exit_with_usage calls exit() so does not return
Note in theory write may write less than the expected number of characters without being an error, and the write has to be done in a loop, but in that case it is useless to manage that
read function returns how many bytes were read to buffer(which has buffer_size). Its not always the case actual bytes read has same value as buffer size(consider scenario if there are not enough bytes left in source file to fully fill your buffer). So you should write to destination file not buffer_size(third argument of the write function), but how many bytes have you read - that is readable variable in your code
You should exit when readable returns an error.So
while(readable != 0){
should be
while(readable != -1){
So that loop could be terminataed when an readfile is exhausted.
You see currently after the whole readfile has been read, calling read fails but write is being called repeatedly since execution has no exit path for failure on read. Also write should only write the number of bytes read. So the code would look like this:
char buffer[buffer_size];
int readable=1;
int writeable;
while(readable != -1){
readable = read(sourcef, buffer, buffer_size);
if(readable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not read.");
}
writeable = write(destf, buffer, readable);
if(writeable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
Simple code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // For system calls write, read e close
#include <fcntl.h>
#define BUFFER_SIZE 4096
int main(int argc, char* argv[]) {
if (argc != 3) {
printf("Usage %s Src_file Dest_file\n", argv[0]);
exit(1);
}
unsigned char buffer[BUFFER_SIZE] = {0};
ssize_t ReadByte = 0;
int src_fd, dst_fd;
// open file in read mode
if ((src_fd = open(argv[1], O_RDONLY)) == -1) {
printf("Failed to open input file %s\n", argv[1]);
exit(1);
}
// open file in write mode and already exists to overwrite
if ((dst_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 644)) == -1) {
printf("Failed to create output file %s\n", argv[2]);
exit(1);
}
// loop
while (1) {
// read buffer
ReadByte = read(src_fd, buffer, sizeof(buffer));
// error with reading
if (ReadByte == -1) {
printf("Encountered an error\n");
break;
} else if (ReadByte == 0) {
// file end exit loop
printf("File copying successful.\n");
break;
}
// error with writing
if (write(dst_fd, buffer, ReadByte) == -1) {
printf("Failed to copying file\n");
break;
}
}
// Close file
close(src_fd);
close(dst_fd);
exit(0);
}
Run
./program src_file dest_file

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