Unwanted characters when copying file using scatter/gather I/O (readv/writev) - c

I'm trying to build a program to copy existing content from an existing file to the new file using readv() and writev().
Here is my code:
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fs, fd;
ssize_t bytes_read, bytes_written;
char buf[3][50];
int iovcnt;
struct iovec iov[3];
int i;
fs = open(argv[1], O_RDONLY);
if (fs == -1) {
perror("open");
return -1;
}
fd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
if (fd == -1) {
perror("open");
return 1;
}
for(i = 0; i < 3; i++) {
iov[i].iov_base = buf[i];
iov[i].iov_len = sizeof(buf[i]);
}
iovcnt = sizeof(iov) / sizeof(struct iovec);
if ((bytes_read=readv(fs, iov, iovcnt)) != -1)
if ((bytes_written=writev(fd, iov, iovcnt)) == -1)
perror("error writev");
printf("read: %ld bytes, write: %ld bytes\n", bytes_read, bytes_written);
if (close (fs)) {
perror("close fs");
return 1;
}
if (close (fd)) {
perror("close fd");
return 1;
}
return 0;
}
Problem: Let's say I ran the program with argv[1] corresponding to the file called file1.txt and copied it to argv[2], let's say it's called as hello.txt.
This is the content of file1.txt:
Ini adalah line pertamaS
Ini adalah line kedua
Ini adalah line ketiga
When I ran the program, the new created file specified in argv[2] were filled by unwanted characters such as \00.
Output after running the program:
Ini adalah line pertamaS
Ini adalah line kedua
Ini adalah line ketiga
\00\00\FF\B5\F0\00\00\00\00\00\C2\00\00\00\00\00\00\00W\D4\CF\FF\00\00V\D4\CF\FF\00\00\8D\C4|\8C\F8U\00\00\C8o\A6U\E5\00\00#\C4|\8C\F8U\00\00\00\00\00\00\00\00\00\00 \C1|\8C\F8U\00\00`\D5\CF\FF
I suspect the main cause of the problem is unfitted size of buf array. I've already look up internet for the solutions and there are nothing to be found. Can anyone give me some enlightment to fix this problem? I tried to make the buf or iov_len to be variable-length but I couldn't find the right way to do it. Thanks everyone!

readv() works with byte counts driven by each .iov_len and no special treatment for any content (like a line-feed). The readv() in the original posting is passed an array of (3) struct iovec, each with .iov_len set to 50. After a successful readv(), the content of the local buf[3][50] would be:
buf[0] : first 50 bytes from the input file
buf[1] : next 20 bytes from the input file, then 30 bytes of uninitialized/leftover stack data
buf[2] : another 50 bytes of uninitialized/leftover stack data
The writev() reuses the same struct iovec array with all (3) .iov_len unchanged from 50, and writes 150 bytes as expected. The content of the output file has the first 70 bytes copied from the input file and 80 bytes of leftover stack data. If the local buf was cleared before calling readv(), the output file would contain trailing NULLs.

Related

Implementing the cp command using read/write system calls [duplicate]

This question already has an answer here:
Using open(), read() and write() system calls to copy a file
(1 answer)
Closed last year.
I am trying to implement the cp command only using read/write system calls.
Here is my code:
/**
* cp file1 file 2
*/
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int errsv;
char contents[1024];
int fd_read, fd_write;
fd_read = open(argv[1], O_RDONLY);
if (fd_read == -1)
{
errsv = errno;
printf("Error occured: %d\n", errsv);
}
read(fd_read, contents, sizeof(contents));
fd_write = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0744);
if (fd_write == -1)
{
errsv = errno;
printf("Error occured: %d\n", errsv);
}
write(fd_write, contents, sizeof(contents));
close(fd_read);
close(fd_write);
return 0;
}
I tested the code using the commands:
cc test.c
./a.out file1 file2
Here is my file1:
dummy text
dummy text
After running the code, although file2 contains the text from file1, it also has some gibberish characters. [not keeping this here.]
Why is this so?
You need to call read() and write() in a loop to copy the entire file. read() returns 0 when you reach EOF, or a negative result if there's an error, then you can end the loop.
read() returns the number of bytes that were read, which may be less than the size of the buffer. You need to use that number when calling write(), otherwise you'll write extra characters to the output file. These will be unitialized characters on the first iteration, and on other iterations they'll be left over characters from previous iterations.
int main(int argc, char *argv[])
{
char contents[1024];
int fd_read, fd_write;
fd_read = open(argv[1], O_RDONLY);
if (fd_read == -1)
{
perror("open input file");
exit(1);
}
fd_write = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0744);
if (fd_write == -1)
{
perror("open output file");
exit(1)
}
int n_read;
while ((n_read = read(fd_read, contents, sizeof(contents))) > 0) {
write(fd_write, contents, n_read);
}
close(fd_read);
close(fd_write);
return 0;
}
write(fd_write, contents, strlen(contents));
Strlen returns the filled entries number but sizeof returns the buffer size which is 1024

write() and read() functions not working properly

I am trying to write a struct to a .dat file and when I open it it shows me this:
"1^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#041720881^#^#^#^#^#^#^#^#^#^#^#Denko^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#Brenko^#^#^#^#^#^#^#^#^#^#^#^#^#^#13.07.2000^#^#^#^#^#^#^#^#^#^#
"
It adds random symbols between the actual values. And now when I at least try to read and print some values, it just doesn't work. It's like the buffer is empty. But I followed the instructions and guides I read.
Using fwrite or similar is not an option since I have to work with these specific functions write() and read().
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
struct info{
char id[20];
char telefon[20];
char ime[20];
char priimek[20];
char datum[20];
};
int main(int argc, char* argv[]) {
struct info dude =
{
"01",
"041720881",
"Denko",
"Brenko",
"13.07.2000"
};
struct info dude2 =
{
"02",
"041581734",
"Denko",
"Badenko",
"13.07.1990"
};
if(strcmp(argv[1], "-c") == 0){
int fd = open("xpo.dat", O_CREAT| O_APPEND | S_IRWXG, 0666);
if(fd == -1){
perror("Error while creating file");
exit(EXIT_FAILURE);
}
}
else if(strcmp(argv[1], "-o") == 0){
struct stat sizefile;
int fd = open("xpo.dat", O_RDWR);
if(fd == -1){
perror("Error while opening file");
exit(EXIT_FAILURE);
}
fstat(fd,&sizefile);
int wr = write(fd, &dude,sizeof(struct info));
char buf[101];
int sz = read(fd, buf, 100);
buf[sz] = '\0';
if(sz == -1) {
perror("Error while creating file");
exit(EXIT_FAILURE);
}
printf("%s", buf);
int cl = close(fd);
}
return 0;
}
The struct contains 100 chars of data. But you are setting only some of them. When you set ime as Denko, the first six chars are set as 'D', 'e', 'n', 'k', 'o','\0'. The remaining 14 are not initialized (or rather initialized implicitly, see #dave_thompson_085's comment below).
If you want to omit those chars, you cannot write the struct as one block. Either write each field separately, or concatenate the fields into a string and write it instead.
As stated in the comments and in the accepted answer, you have some issues, the why and the what is already talked about and explained.
I would like to add some more information:
And now when I at least try to read and print some values, it just doesn't work. It's like the buffer is empty.
What happens is that you are reading from the end of the file, if you want to read after you write without closing and reopening the file, you can, but you'll need to reposition the offset of the opened file to the beginning using lseek.
Another thing to note is that if you want to write the data as a structure you then need to read it as a structure as well, not as a string.
Taking that in consideration your code could look more or less like this (skipping return value validations, but you should do it, as you know):
//...
else if(strcmp(argv[1], "-o") == 0){
int fd = open("xpo.dat", O_RDWR);
int wr = write(fd, &dude, sizeof dude);
lseek(fd, 0, SEEK_SET); // set the reading position to the beginning of the file
struct info buf;
wr = read(fd, &buf, sizeof buf);
int cl = close(fd);
printf("%s %s %s %s %s", buf.id, buf.telefon, buf.ime, buf.priimek, buf.datum);
}
//...
If you prefer it as a string you can easily concatenate it using something like snprintf or similar.

Read a file a number of bytes per time in c

I am trying to write a program on how to read a file 10 bytes per time using read, however, I do not know how to go about it. How should I modify this code to read 10bytes per time. Thanks!!!!
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
int main (int argc, char *argv[])
{
printf("I am here1\n");
int fd, readd = 0;
char* buf[1024];
printf("I am here2\n");
fd =open("text.txt", O_RDWR);
if (fd == -1)
{
perror("open failed");
exit(1);
}
else
{
printf("I am here3\n");
if(("text.txt",buf, 1024)<0)
printf("read error\n");
else
{
printf("I am here3\n");
/*******************************
* I suspect this should be the place I make the modification
*******************************/
if(read("text.txt",buf, 1024)<0)
printf("read error\n");
else
{
printf("I am here4\n");
printf("\nN: %c",buf);
if(write(fd,buf,readd) != readd)
printf("write error\n");
}
}
return 0;
}
The final parameter of read() is the maximum size of the data you wish to read so, to try and read ten bytes at a time, you would need:
read (fd, buf, 10)
You'll notice I've also changed the first parameter to the file descriptor rather than the file name string.
Now, you'll probably want that in a loop since you'll want to do something with the data, and you also need to check the return value since it can give you less than what you asked for.
A good example for doing this would be:
int copyTenAtATime (char *infile, char *outfile) {
// Buffer details (size and data).
int sz;
char buff[10];
// Try open input and output.
int ifd = open (infile, O_RDWR);
int ofd = open (outfile, O_WRONLY|O_CREAT);
// Do nothing unless both opened okay.
if ((ifd >= 0) && (ofd >= 0)) {
// Read chunk, stopping on error or end of file.
while ((sz = read (ifd, buff, sizeof (buff))) > 0) {
// Write chunk, flagging error if not all written.
if (write (ofd, buff, sz) != sz) {
sz = -1;
break;
}
}
}
// Finished or errored here, close files that were opened.
if (ifd >= 0) close (ifd);
if (ofd >= 0) close (ofd);
// Return zero if all okay, otherwise error indicator.
return (sz == 0) ? 0 : -1;
}
change the value in read,
read(fd,buf,10);
From man of read
ssize_t read(int fd, void *buf, size_t count);
read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.
if(read("text.txt",buf, 1024)<0)// this will give you the error.
First argument must be an file descriptor.

Cannot copy an entire file to another one using write system call

I have to copy the content of file1 into a buffer (of size 23 bytes), then, I have to copy data from the buffer to file2.
I have trouble to make sure that file1 is entirely copied into the buffer. When the buffer is copied to file2, file2 contents only part of the content of file1, and the output says that only 4 bytes of data have been copied into file2.
I tried to figure out what I did wrong, but I have no luck so far. Your help will be very appreciated.
I am using Oracle VM VirtualBox, where I have Ubuntu installed.
I am also using make (MakeFile) to update all files at once on the command prompt.
My code is below in C/POSIX.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#define My_Full_Name "AAA!"
int PrintSentence()
{
size_t buffersize = (size_t) (4 * 5.75); //or (4 bytes * 5.75) = 23 bytes
char buffer[buffersize];
char source_file[200];
char destination_file[200];
ssize_t bytes_read;
int fdSource, fdDestination;
mode_t mode = S_IRUSR | S_IWUSR;
printf("Welcome to File Copy by %s\n", My_Full_Name);
printf("Enter the name of the source file: ");
scanf("%s", source_file);
printf("Enter the name of the destination file: ");
scanf("%s", destination_file);
fdSource = open(source_file, O_RDONLY);
if (fdSource < 0)
{
perror("Open failed!!");
return 1;
}
else
{
bytes_read = read(fdSource, buffer, sizeof(buffer));
fdDestination = open(destination_file, O_CREAT | O_WRONLY | mode);
if (fdDestination < 0)
{
perror("Oups!! cannot create file again!!");
return 1;
}
else
{
write(fdDestination, buffer, sizeof(buffer));
printf("current content of buffer: %s\n", buffer); //just to check
printf("current value of buffer size = %zd \n", buffersize); //just to check
printf("File copy was successful, with %d byte copied\n", fdDestination); //the output says only 4 bytes are copied
}
}
return;
}
Here:
printf("File copy was successful, with %d byte copied\n", fdDestination );
fdDestination is a file descriptor, it's not the number of bytes written. 0, 1 and 2 are your three standard streams, 3 will be your input file which is opened first, so 4 will be your output file, that's why it's always outputting 4.
You want to save the return value from write(), and use the value of that instead (after checking that return value for errors, of course, which you should be doing for read() as well).
EDIT: Slightly modifying your code:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#define My_Full_Name "AAA!"
int main(void) {
size_t buffersize = (size_t) (4 * 5.75);
char buffer[buffersize];
char source_file[200];
char destination_file[200];
ssize_t bytes_read, bytes_written;
int fdSource, fdDestination;
mode_t mode = S_IRUSR | S_IWUSR;
printf("Welcome to File Copy by %s\n", My_Full_Name);
printf("Enter the name of the source file: ");
scanf("%s", source_file);
printf("Enter the name of the destination file: ");
scanf("%s", destination_file);
fdSource = open(source_file, O_RDONLY);
if (fdSource < 0) {
perror("Open failed!!");
return 1;
} else {
bytes_read = read(fdSource, buffer, sizeof(buffer));
fdDestination = open(destination_file, O_CREAT | O_WRONLY | mode);
if (fdDestination < 0) {
perror("Oups!! cannot create file again!!");
return 1;
} else {
bytes_written = write(fdDestination, buffer, sizeof(buffer));
printf("current content of buffer: %s\n", buffer);
printf("current value of buffer size = %zd \n", buffersize);
printf("File copy was successful, with %d byte copied\n",
bytes_written);
}
}
return 0;
}
gives me this:
paul#local:~/src/c/fpc$ cat infile
12345678901234567890123
paul#local:~/src/c/fpc$ ./fpc
Welcome to File Copy by AAA!
Enter the name of the source file: infile
Enter the name of the destination file: outfile
current content of buffer: 12345678901234567890123
current value of buffer size = 23
File copy was successful, with 23 byte copied
paul#local:~/src/c/fpc$ cat outfile; echo ""
12345678901234567890123
paul#local:~/src/c/fpc$
How can you expect that a file will be entirely written into the buffer when your buffer is but 23 bytes long? With call to read you are reading only 23 bytes and leave the rest of the contents of the file1 untouched. Or, do is it your expected behaviour that your program should copy only 23 bytes of its contents to the target file?

How to read mp3 file tags in C (unix)?

I would like to read mp3 tags from mp3 file :D and save it to txt file. But my code doesnt work :( I mean I have some problems with setting the proper position in my mp3 file, take a look: (why doesnt it want to work?). I have to do it by myself, with no extra libs.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int getFileSize(const char *filename)
{
struct stat st;
if (stat(filename, &st) == 0)
return st.st_size;
return -1;
}
int main(int argc, char **argv)
{
char *infile = "in.mp3", *outfile = "out.txt";
int infd, bytes_read = 0, buffsize = 255;
char buffer[255];
infd = open(infile, O_RDONLY);
if (infd == -1)
return -1;
int outfd = open(outfile, O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
if (outfd == -1)
return -1;
if(lseek(infd, -128, SEEK_END) < 0)
return -1;
for(;;)
{
bytes_read = read(infd, buffer, buffsize);
if (bytes_read > 0)
{
write(outfd, buffer, bytes_read);
}
else
{
if (bytes_read == 0)
{
if (close(infd) < 0)
return -1;
break;
}
else if (bytes_read == -1)
{
break;
return -1;
}
}
}
return 0;
}
One approach to solve this problem:
You'll need to scan through the file depending on the Version of ID3 you're using (question has no specific version specified as noted by Steven), find either the whole tag or the tag header and decode from there.
For ID3v2 the header sequence is 10 Bytes and as follows (from the ID3v2 spec):
ID3v2/file identifier "ID3"
ID3v2 version $04 00
ID3v2 flags %abcd0000
ID3v2 size 4 * %0xxxxxxx
My suggestion is, take a look at the spec of ID3v2 here. Check Chapter 3.1 since part of the work is doing the background research.
For ID3v1 check that overview spec here. Decoding that information is quite easy and works exactly as noted in the comments to your question. Looking at your code this is probably what you want to do (jumping to 128 bytes at the end of the file and starting to read from there).
Make sure you have a properly tagged file and are sure about the tag version you're using before throwing your decoder at it.

Resources