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

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.

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
}

not able to read iio file from userspace

I am trying to read following file from C code.
file: /sys/bus/iio/devices/iio\:device0/in_voltage7_raw
but file pointer I am getting is -1.
Using cat command it is able to read the file.
But I am trying to read the same from my code as follows:
nos_int32 nos_adc_read_port (ADC_PORT_DB *p_port, nos_int32 *data)
{
char file_name[VALUE_MAX];
int value;
char buffer[BUFFER_LENGTH];
char intBuffer[INT_BUFFER_LENGTH];
int fd;
sprintf(file_name, "/sys/bus/iio/devices/iio\\:device0/in_voltage7_raw");
fd = open(file_name, O_RDONLY);
if (fd == -1) {
return(-1);
}
if (read(fd, buffer, BUFFER_LENGTH) == -1) {
return(-1);
}
close(fd);
memcpy(intBuffer, buffer, BUFFER_LENGTH);
intBuffer[INT_BUFFER_LENGTH-1] = '\0';
value = atoi(intBuffer);
*data = value;
return(0);
}
After the line:
fd = open(file_name, O_RDONLY);
value of fd is -1. How can it be solved?
Most command line shells use some characters for special actions and if you're trying to use them as their actual character, you need to prefix them with a backslash to escape them. In this case, your shell needs you to escape the colon when accessing that filename.
In C you don't have this issue so you can put in your code the filename as it truly is, such as:
"/sys/bus/iio/devices/iio:device0/in_voltage7_raw"

Program gets stuck while trying to read a file using read() system call

Here is my code snippet:
int fd;
bufsize = 30;
char buf[bufsize];
char cmd[100] = "file.txt";
int newfd = 1;
if (fd = open(cmd,O_RDONLY) >=0){
puts("wanna read");
while (read(fd,&bin_buf,bufsize)==1){
puts("reading");
write(newfd,&bin_buf,bufsize);
}
close(fd);
}
So here the program prints "wanna read" but never prints "reading". I have also tried opening using nonblock flag, but no use. Can anybody help me? I must use open() and read() system calls only. Thanks.
Edit: I have made some clarifications in the code. Actually the newfd that I'm writing to is a socket descriptor, but I don't think that is important for this problem because it sticks on the read which is before the write.
The first problem is your if statement. You forgot to use enough parentheses, so if the open() works, the read tries to read from file descriptor 1, aka standard output. If that's your terminal (it probably is) on a Unix box, then that works — surprising though that may be; the program is waiting for you to type something.
Fix: use parentheses!
if ((fd = open(cmd, O_RDONLY)) >= 0)
The assignment is done before, not after, the comparison.
I observe in passing that you don't show how you set cmd, but if you see the 'wanna read' message, it must be OK. You don't show how newfd is initialized; maybe that's 1 too.
You also have the issue with 'what the read() call returns'. You probably need:
int fd;
char buf[bufsize];
int newfd = 1;
if ((fd = open(cmd, O_RDONLY)) >= 0)
{
puts("wanna read");
int nbytes; // ssize_t if you prefer
while ((nbytes = read(fd, buf, sizeof(buf))) > 0)
{
puts("reading");
write(newfd, buf, nbytes);
}
close(fd);
}
You can demonstrate my primary observation by typing something ('Surprise', or 'Terminal file descriptors are often readable and writable' or something) with your original if but my loop body and then writing that somewhere.
Your read() call attempts to read bufsize bytes and returns the number of bytes actually read. Unless bufsize ==, it is quite unlikely read() will return 1, so the block is almost always skipped and nothing get written.
Also note that if (fd = open(cmd, O_RDONLY) >= 0) is incorrect and would set fd to 1, the handle for standard output, if the file exists, causing the read to fail as standard input is most likely not opened for reading.
Note that reading with the read system call is tricky on some environments, because a return value of -1 may be restartable.
Here is an improved version:
int catenate_file(const char *cmd, int newfd, size_t bufsize) {
int fd;
char buf[bufsize];
if ((fd = open(cmd, O_RDONLY)) >= 0) {
puts("wanna read");
ssize_t nc;
while ((nc = read(fd, buf, bufsize)) != 0) {
if (nc < 0) {
if (errno == EINTR)
continue;
else
break;
}
printf("read %zd bytes\n", nc);
write(newfd, buf, nc);
}
close(fd);
return 0;
}
return -1;
}
read returns the number of bytes read from file that can be bufsize or less if the remainder of the file that has to be read is shorter than bufsize.
In your case most probably bufsize is bigger than 1 and the file is bigger than 1 byte so the condition of the while loop is evaluated false, the code is skipped to the point where file is closed.
You should check if there if there are more bytes to be read:
while( read(fd,&bin_buf,bufsize) > 0 ) {

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.

Some better Loop exiter than feof()

I really have a problem in here. It seems that i dont really find the best way to exit a loop when reading characters from a file. I know that every tutorial suggests that i shouldn't use while( !feof() ) but they dont really suggest anything else than putting fgets() in the while and that is not really apropriate because i want to read the whole FILE content in my variable.
while (!feof(newFile))
{
newString[i++] = fgetc(newFile);
}
newString[i] = '\0';
i = 0;
//this is the resoult seen with the debugger
newFile content = ABC
newString[0] = 65 (A)
newString[1] = 66 (B)
newString[2] = 67 (C)
newString[3] = 10 (\n)
newString[4] = -1
newString[5] = 0 (\0)
I am looking for a solution and some advices about how to improve my algorithm.
int c;
while ((c = fgetc(newFile)) != EOF) newString[i++] = c;
newString[i] = '\0';
For reading whole test files into memory, I suggest using mmap. This has the benefit, that all buffering and reading can be handled by your operating system, and you can focus your code on the task at hand. (also, it's usually faster than buffering stuff yourself.)
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
int
main (void)
{
int fd = open("filename", O_RDONLY);
if (fd == -1)
return 0; // file open failed
struct stat sb;
int res = fstat(fd, &sb);
if (res == -1)
return 0; // stat failed
size_t length = sb.st_size;
char *data = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (!data)
return 0; // mmap failed
// iterate over characters
size_t i;
for (i = 0; i < length; ++i)
printf("'%c'\n", data[i]);
munmap(data, length);
return 0;
}
they dont really suggest anything else than putting fgets() in the while and that is not really apropriate
That is absolutely, entirely appropriate. fgets() reads the file line by line, and you can append each line onto then end of a dybamically expanding buffer.
However, if you don't want to use fgets(), and you just want to read the file at once: use fread().
FILE *f = fopen("foo.txt", "rb");
if (!f)
abort(); // "handle" error
fseek(f, 0, SEEK_END);
size_t len = ftell(f);
fseek(f, 0, SEEK_SET);
char *buf = malloc(len + 1);
if (!buf)
abort();
if (fread(buf, len, 1, f) != 1) {
// handle reading error
}
buf[len] = 0;
fclose(f);

Resources