write() and read() functions not working properly - c

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.

Related

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

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.

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)

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.

memory mapped files

I wrote a code for writing the content to the mapped buffer which mapped by using the mmap() system call.
After I did some the changes in the mapped buffer,then I called the msync().It should update to the file on disk.
But,It doesn't made any changes to the file on disk.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<sys/mman.h>
#include<fcntl.h>
#define FILEMODE S_IRWXU | S_IRGRP | S_IROTH
#define MAX 150
main(int argc,char *argv[])
{
int fd,ret,len;
long int len_file;
struct stat st;
char *addr;
char buf[MAX];
if(argc > 1)
{
if((fd = open(argv[1],O_RDWR | O_APPEND | O_CREAT ,FILEMODE)) < 0)
perror("Error in file opening");
if((ret=fstat(fd,&st)) < 0)
perror("Error in fstat");
len_file = st.st_size;
/*len_file having the total length of the file(fd).*/
if((addr=mmap(NULL,len_file,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0)) == MAP_FAILED)
perror("Error in mmap");
len = len_file;
while((fgets(buf,MAX,stdin)) != NULL)
{
strcat(addr+len,buf);
printf( "Val:%s\n",addr ) ; //Checking purpose
len = len + (strlen(buf));
}
if((msync(addr,len,MS_SYNC)) < 0)
perror("Error in msync");
if( munmap(addr,len) == -1)
printf("Error:\n");
printf("addr %p\n",addr);
}
else
{
printf("Usage a.out <filename>\n");
}
}
If you want your changes to be reflected in the on-disk file, you must map the file as MAP_SHARED, not MAP_PRIVATE.
Additionally, you cannot extend the file simply by writing beyond the end of the mapping. You must use ftruncate() to extend the file to the new size, then change the mapping to include the new portion of the file. The portable way to change the mapping is to unmap the mapping then recreate it with the new size; on Linux you can instead use mremap().
Your len and len_file variables should be of type size_t, and you should use memcpy() rather than strcat(), since you know exactly the length of the string, exactly where you want to copy it, and you don't want to copy the null-terminator.
The following modification of your code works on Linux (using mremap()) :
#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<sys/mman.h>
#include<fcntl.h>
#define FILEMODE S_IRWXU | S_IRGRP | S_IROTH
#define MAX 150
int main(int argc,char *argv[])
{
int fd, ret;
size_t len_file, len;
struct stat st;
char *addr;
char buf[MAX];
if (argc < 2)
{
printf("Usage a.out <filename>\n");
return EXIT_FAILURE;
}
if ((fd = open(argv[1],O_RDWR | O_CREAT, FILEMODE)) < 0)
{
perror("Error in file opening");
return EXIT_FAILURE;
}
if ((ret = fstat(fd,&st)) < 0)
{
perror("Error in fstat");
return EXIT_FAILURE;
}
len_file = st.st_size;
/*len_file having the total length of the file(fd).*/
if ((addr = mmap(NULL,len_file,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED)
{
perror("Error in mmap");
return EXIT_FAILURE;
}
while ((fgets(buf,MAX,stdin)) != NULL)
{
len = len_file;
len_file += strlen(buf);
if (ftruncate(fd, len_file) != 0)
{
perror("Error extending file");
return EXIT_FAILURE;
}
if ((addr = mremap(addr, len, len_file, MREMAP_MAYMOVE)) == MAP_FAILED)
{
perror("Error extending mapping");
return EXIT_FAILURE;
}
memcpy(addr+len, buf, len_file - len);
printf( "Val:%s\n",addr ) ; //Checking purpose
}
if((msync(addr,len,MS_SYNC)) < 0)
perror("Error in msync");
if (munmap(addr,len) == -1)
perror("Error in munmap");
if (close(fd))
perror("Error in close");
return 0;
}
Note that you've provided a mapping for the file that is exactly the size of the file. If you create the file in your call to open(2), it will have a length of 0, and I wouldn't be surprised if the kernel doesn't bother setting up any kind of memory mapping from a 0 length mapping. (Maybe it does? I've never tried...)
I would suggest using ftruncate(2) to extend the length of your file before performing the mapping. (Note that extending files using ftruncate(2) isn't very portable; not all platforms provide extending functionality and not all filesystem drivers support the extending functionality. See your system's manpage for details.)
You must use the MAP_SHARED mapping for your file modifications to be saved to disk.
Your use of perror(3) isn't quite correct; perror(3) will not terminate your program, so it will continue executing with incorrect assumptions:
if((ret=fstat(fd,&st)) < 0)
perror("Error in fstat");
Should read:
if((ret=fstat(fd,&st)) < 0) {
perror("Error in fstat");
exit(1);
}
(Or exit(EXIT_FAILURE) if you want to be more portable -- I find that a little harder on the eyes but I live in Linux-land.)
strcat(3) expects to find an ASCII NUL character (byte value 0x00, C representation '\0') -- the usual C end-of-string marker -- at the end of the dest string. Your file will not contain an ASCII NUL if you create it in this program -- its length is zero, after all -- and I don't know the consequences of trying to read a zero-byte file via mmap(2). If the file already exists and has data in it, it probably doesn't have an ASCII NUL encoded in the file. strcat(3) is almost certainly the wrong tool to write into your file. (No one wants ASCII NULs in their files anyway.) Try memcpy(3) instead.

lseek function problem in a copy file program!

Got to use lseek function in this program below... Program is simply copying file (that already exist). I wanned to copy the existing file with the chars from the end of file
for example: Sorce_File.txt contains:"1 2 3" after copy Target_File.txt contains:"3 2 1"
I'm pretty sure it's simple problem but couldn't find out since 2 days how to do it
#include <fcntl.h>
#include <stdio.h>
#define MAX 512
int main(int argc, char* argv[]){
char buf[MAX];
int desc_sorc, desc_targ;
int lbajt;
if (argc<3){
argv[0];
exit(1);
}
desc_sorc = open(argv[1], O_RDONLY);
if (desc_sorc == -1){
}
desc_targ = creat(argv[2], 0640);
if (desc_targ == -1){
exit(1);
}
while((lbajt = read(desc_sorc, buf, MAX)) > 0){
if (lbajt == -1) {
perror("position error");
exit(1);}
if (write(desc_targ, buf, lbajt) == -1)
{
exit(1);
}
}
if (lbajt == -1){
exit(1);
}
if (close(desc_sorc) == -1 || close(desc_targ) == -1){
exit(1);
}
exit(0);
}
int desc_sorc, desc_targ;
You don't actually initialize these to anything. Anywhere.
EDIT: Now that you've fixed that, have you actually tested it again?
You are missing the equivalent of strrev(...) in there to reverse the string you write out as well starting from the end of the source file and reading backwards or writing from the end of the target file back to the beginning.
The actual implementation is left as an exercise to the reader.

Resources