Concatenating files using read and write system calls - c

I wrote this simple code to concatenate two files. Why doesn't this work? Please help.
int main(int arg, char* argv[]){
int fd1, fd2;
char c;
fd1 = open(argv[1], O_APPEND);
fd2 = open(argv[2], O_RDONLY);
while(read(fd2, &c, sizeof(c))) {
write(fd1, &c, sizeof(c));
}
close(fd1);
close(fd2);
return 0;
}

As I noted in a comment:
You need to test the results of open() and write(). Your call with O_APPEND needs O_WRONLY too. And if you want it to create the file too, you need various other options too — O_CREAT; maybe O_TRUNC but probably not; maybe O_EXCL; maybe some others too — and you need a mode argument for the third argument (e.g. 0644 — owner can read or write, group and others can only read; or something more restrictive).
You also need to check that you have 2 file name arguments.
Fixing those issues leads to code similar to this:
int main(int arg, char* argv[]){
if (argc < 3)
{
fprintf(stderr, "Usage: %s out-file in-file\n", argv[0]);
return 1;
}
int fd1 = open(argv[1], O_APPEND|O_WRONLY|O_CREAT, 0644);
if (fd1 < 0)
{
fprintf(stderr, "%s: failed to open/create file %s for writing\n", argv[0], argv[1]);
return 1;
}
int fd2 = open(argv[2], O_RDONLY);
if (fd2 < 0)
{
fprintf(stderr, "%s: failed to open file %s for reading\n", argv[0], argv[2]);
return 1;
}
char c;
while (read(fd2, &c, sizeof(c)) == sizeof(c)) {
if (write(fd1, &c, sizeof(c)) != sizeof(c))
break;
}
close(fd1);
close(fd2);
return 0;
}
I note that you might get warnings about comparing signed and unsigned values with the read() and write() tests — the functions return a ssize_t and sizeof produces a size_t (2 vs 1 letters s). You can cast around that, but it's ugly. The code is also woefully inefficient. Single character reads and writes work, but you get dramatically better performance in general with buffer sizes in the 1 KiB to 4 KiB range, and even bigger. Of course, then you have to trap and use the value returned by read() so that the correct amount of data is written.
Note that the original code would continue the loop if the read() failed (returned -1). It would only stop if the read() was successful at returning 0 when it reached EOF.

Related

My program for copying one file to another fails

I was trying to copy what is written in a file to a different file (with system calls) but my code seems not to work. I have first tried just printing with printf() the buffer but it also does not work. My guess is that I'm reading the file incorrectly.
#define BUF_SIZE 200
int main(int argc, char *argv[]){
int entrada,salida,leidos;
char buffer[BUF_SIZE];
entrada = open(argv[1],O_RDONLY);
salida = creat(argv[2], 0644);
while( (leidos = read(entrada,buffer,BUF_SIZE)) > 0 ){
write(salida,buffer,leidos);
}
close(salida);
close(entrada);
return 0;
}
What's wrong with my implementation?
I think you're missing the appropriate open flags on the output . Try:
salida = creat(argv[2], O_WRONLY | O_CREAT, 0644);
However, as comments suggest, you're probably getting errors indicated by the return values and/or the errno variable, which you are ignoring.
Also, I would avoid Castellano-specific variable names. Writing C/C++ requires knowing English anyway, so better stick to that for naming; otherwise - people who don't speak Castellano will have trouble understanding your code.
Finally - why are you doing it this way? There are much nicer C++-friendly, or even C-friendly, ways to copy a file - which would also be portable (your code isn't). See:
Copy a file in a sane, safe and efficient way
As #einpoklum stated, the main problem must be probably searched in the way you are opening your output file (flags and permissions). Overall, your code is far from implementing the minimum debugging verbosity and I think that a few code flow controls would help you a lot in detecting the real problem in your code:
#include <stdio.h>
#include <stdlib.h>
#ifndef BUF_SIZE
#define BUF_SIZE 200
#endif
int main(int argc, char *argv[])
{
int entrada = open(argv[1], O_RDONLY);
if (entrada == -1)
{
fprintf(stderr, "Error opening: %s\n", argv[1]);
exit(-1);
}
int openFlags = O_CREAT | O_WRONLY | O_TRUNC;
mode_t filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
int salida = open(argv[2], openFlags, filePerms);
if (salida == -1)
{
fprintf(stderr, "Error opening: %s\n", argv[2]);
exit(-1);
}
ssize_t numRead;
char buf[BUF_SIZE];
while ((numRead = read(entrada, buf, BUF_SIZE)) > 0)
{
if (write(salida, buf, numRead) != numRead)
{
fprintf(stderr, "Writing error!\n");
exit(-1);
}
}
if (numRead == -1)
{
fprintf(stderr, "Reading error!\n");
exit(-1);
}
if (close(entrada) == -1)
{
fprintf(stderr, "Input closing error!\n");
exit(-1);
}
if (close(salida) == -1)
{
fprintf(stderr, "Output closing error!\n");
exit(-1);
}
exit(0);
}
Many of the functions you use return values that can provide you an insight on that's going on. Use them.

Segmentation Fault (Core Dumped) using read() and write()

I've been out of programming in C for almost 2 years and have recently gotten an assignment in school on using write() and read().
Somewhere in the code I'm receiving the Segmentation Fault error, possibly on the filecopy function is where I'd put my money on. I was trying GDB but I haven't used that since that last time I programmed in C so I turn to here.
The code.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
void filecopy(int infd, int outfd);
int fd = -1;
char *prog = argv[0];
if(argc == 1)
filecopy(STDIN_FILENO, STDOUT_FILENO);
else
while(--argc > 0) {
if((fd = open(*++argv, O_RDONLY, "rb")) == -1) {
// we don't have fprintf... but we have sprintf =]
char tmp[30];
sprintf(tmp, "%s: can't open %s\0", prog, *argv);
write(STDOUT_FILENO, &tmp, sizeof(tmp));
exit(-1);
} else {
filecopy(fd, STDOUT_FILENO);
close(fd);
}
}
exit(0);
}
void filecopy(int infd, int outfd) {
// char *buf[1]; <-- causes unreadable characters outputted by write
char *buf;
while(read(infd, buf, 1) != -1)
write(outfd, buf, sizeof(buf));
}
The input/output
Thanks!
char *buf; is an uninitialized pointer, writing data through that pointer is
undefined behaviour.
char buf[1024];
ssize_t len;
while((len = read(infd, buf, sizeof buf)) != -1)
write(outfd, buf, len);
would be correct.
Note that char *buf[1]; is a array (of dimension 1) of pointers, that's
different to an array of chars. Using that you would need to do
read(infd, buf[0], somelength), but here again buf[0] would be an
uninitialized pointer and you would have the same problem. That's why declaring
an char array of say 1024 (you can choose another size) is the correct thing
to do.
Also in main use strlen(tmp) and not sizeof(tmp)
char tmp[30];
sprintf(tmp, "%s: can't open %s\0", prog, *argv);
write(STDOUT_FILENO, &tmp, strlen(tmp));
strlen returns you the length of the string which might be smaller than 29 and
if you use sizeof(tmp) you might be writing garbage past the end of the
string. Note also that 0 may be too small for the whole string, I'd use a
larger number or construct the string using snprintf:
snprintf(tmp, sizeof tmp, "%s: can't open %s\0", prog, *argv);
would be more safe.
Last thing:
while(--argc > 0)
if((fd = open(*++argv, O_RDONLY, "rb")) == -1) {
...
While this is correct, I feel that this code is awkward and hard to read. It
would be so much simpler to read if you did:
for(int i = 1; i < argc; ++i)
if((fd = open(argv[i], O_RDONLY, "rb")) == -1) {
...
I've never seen open being called with "rb" as the mode. My man page says:
man 2 open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
[...]
The mode argument specifies the file mode bits be applied when a new file is created. This argument must be supplied when
O_CREAT or O_TMPFILE is specified in flags; if neither O_CREAT nor O_TMPFILE is specified, then mode is ignored.
The effective mode is modified by the process's umask in the usual way: in the absence of a default ACL, the mode of the created file is
(mode & ~umask). Note that this mode applies only to future accesses of the newly created file; the open() call that creates a
read-only file may well return a read/write file descriptor.
The following symbolic constants are provided for mode:
S_IRWXU 00700 user (file owner) has read, write, and execute permission
S_IRUSR 00400 user has read permission
S_IWUSR 00200 user has write permission
S_IXUSR 00100 user has execute permission
[...]
As you are neither using O_CREAT nor O_TMPFILE, this parameter will be
ignore and you are passing a char* as a mode_t which is integer in nature.
Hence your call should be:
if((fd = open(argv[i], O_RDONLY, 0)) == -1) {
...
Two adjustments are needed for you filecopy function:
You need to allocate space for your buffer. Right now you are using an uninitialized pointer and passing it to read which is undefined behavior.
You need to save the return value of read and pass the value to write
The end result should look something like this.
void filecopy(int infd, int outfd) {
char buf[1024];
size_t bytes_read;
while((bytes_read = read(infd, buf, sizeof buf)) != -1)
write(outfd, buf, bytes_read);
}
Running this through a static analysis tool gives 2 warnings:
1) The uninitialized variable that #Pablo points to
2) a buffer overrun when you sprintf *argv into tmp as *argv can very large (as #Pablo also suggested in his comment re: snprintf)

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.

lseek cause segmentation file in O_APPEND mod

int main(int argc,char* argv[]){
int fd;
off_t foffset;
char* fpath;
char* rbuf;
if(argc!=2){
printf("insert filename as argument!\n");
exit(1);
}
strcpy(fpath,argv[1]);
if( (fd = open(fpath,O_RDWR | O_APPEND)) <0 )
perror("error on open()");
//try to use lseek in file opened in O_APPEND mode
char buf[] = "I'm appending some text!\n";
if( write(fd, buf , sizeof(buf)) <0 )
perror("error on write()");
printf("the write() operation was succesful, let's try to seek and read the file..\n");
foffset = lseek(fd,0L,SEEK_CUR);
if(foffset<0)
perror("error on lseek() :");
close(fd);
return 0;
}
Why does it generate a segmentation fault when I execute this code??
The segFault occurs only if the lseek operation is added, otherwise is fine.
fpath is a wild pointer, i.e. you haven't allocated any storage for it before you call strcpy. However since you only need a const char * for the file name you can just make the following change.
Change:
strcpy(fpath,argv[1]);
to:
fpath = argv[1];
If you want to use fpath separately, change your definition:
char fpath[30];
Now your strcpy will work as expected (though you should check the length of the string is under 30). You can just pass argv[1] directly, however, to open, since you're not doing anything else with it.

Resources