I was reading the GNU C PROGRAMMING TUTORIAL online, and get some confusion on the code example for low level read & write.
The code is as below:
#include <stdio.h>
#include <fcntl.h>
int main()
{
char my_write_str[] = "1234567890";
char my_read_str[100];
char my_filename[] = "snazzyjazz.txt";
int my_file_descriptor, close_err;
/* Open the file. Clobber it if it exists. */
my_file_descriptor = open (my_filename, O_RDWR | O_CREAT | O_TRUNC);
/* Write 10 bytes of data and make sure it's written */
write (my_file_descriptor, (void *) my_write_str, 10);
fsync (my_file_descriptor);
/* Seek the beginning of the file */
lseek (my_file_descriptor, 0, SEEK_SET);
/* Read 10 bytes of data */
read (my_file_descriptor, (void *) my_read_str, 10);
/* Terminate the data we've read with a null character */
my_read_str[10] = '\0';
printf ("String read = %s.\n", my_read_str);
close (my_file_descriptor);
return 0;
}
I compiled the code with gcc without issue. And run the first time, it is also ok. Output as below:
$ ./lowLevelWrite
String read = 1234567890.
The problem comes when i run the program second time:
$ ./lowLevelWrite
String read = .
Seems the code fails to write the string "1234567890" to the file second time. As we know from the GNU C manual, O_RDWR | O_CREAT | O_TRUNC these flag should allow us to truncate the file to 0 every time and then write to the file. I am not sure why it fails from second time execution.
Can anybody help me out of this confusion?
When you're creating a file with open() you need to pass a third argument, the permission modes:
my_file_descriptor = open (my_filename, O_RDWR | O_CREAT | O_TRUNC, 0664);
0664 is the permissions rw-rw-r--: readable and writable by the owner and group, readable by everyone else. These permissions will be further masked by your umask.
Since you didn't pass this argument, open() used random stack garbage, and this probably didn't include write permission. So you couldn't open the file for writing when it already exists.
Related
Aside from using popen() (as was discussed in this question) is this a valid way of doing it ?
Say we had a program who's name is hexdump_dup and wanted the program to output the exact output of the hexdump command.
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
fd = open("hexdump_dup", O_CREAT | O_TRUNC | O_WRONLY, 0755); // (line 8)
write(fd, "/usr/bin/hexdump $#;", 20); // (line 9)
close(fd);
return (0);
}
Also could someone briefly explain what line 8 and 9 do, and how afterwards the command gets executed ? Like when, where does it say to execute the command or what makes the command execute ?
After this
fd = open("hexdump_dup", O_CREAT | O_TRUNC | O_WRONLY, 0755); // (line 8)
write(fd, "/usr/bin/hexdump $#;", 20);
you need to execute hexdump_dup executable, for that you need to use either system() or exec() family function. For e.g
system("./hexdump_dup 1 2 3"); /* after creating binary file(hexdump_dup) & writing command into it, you need to run it, for that use system() or exec() */
This
fd = open("hexdump_dup", O_CREAT | O_TRUNC | O_WRONLY, 0755);
will create the hexdump_dup binary if it doesn't exist before & if exists before it will truncate its content to 0. You can refer the man page of open() , it says
int open(const char *pathname, int flags, mode_t mode);
The argument flags must include one of the following access
modes: O_RDONLY, O_WRONLY, or O_RDWR. These request opening
the file read-only, write-only, or read/write, respectively.
O_CREAT
If the file does not exist it will be created. The
owner (user ID) of the file is set to the effective
user ID of the process.
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.
Lastly this
write(fd, "/usr/bin/hexdump $#;", 20);
writes 20 bytes containing array of characters /usr/bin/hexdump $#; in this case into a file where fd points i.e it will put this into hexdump_dup file.
Here $# means when you execute hexdump_dup like
./hexdump_dup 1 2 3
it will take all the parameters to be passed.
I want to read and write a text or .dat file in verifone to store data on it.
How can I make it ?
here is my code
int main()
{
char buf [255];
FILE *tst;
int dsply = open(DEV_CONSOLE , 0);
tst = fopen("test.txt","r+");
fputs("this text should write in file.",tst);
fgets(buf,30,tst);
write(dsply,buf,strlen(buf));
return 0;
}
Chapter 3 of the "Programmers Manual for Vx Solutions" ("23230_Verix_V_Operating_System_Programmers_Manual.pdf") is all about file management and contains all the functions I typically use when dealing with data files on the terminal. Go read through that and I think you'll find everything you need.
To get you started, you'll want to use open() together with the flags you want
O_RDONLY (read only)
O_WRONLY (write only)
O_RDWR (read and write)
O_APPEND (Opens with the file position pointer at the end of the file)
O_CREAT (create the file if it doesn't already exist),
O_TRUNC (truncate/delete previous contents if the file already exists),
O_EXCL (Returns error value if the file already exists)
On success, open will return a positive integer that is a handle which can be used for subsequent access to the file. On failure, it returns -1;
When the file is open, you can use read() and write() to manipulate the contents.
Be sure to call close() and pass in the return value from open when you are done with the file.
Your example above would look something like this:
int main()
{
char buf [255];
int tst;
int dsply = open(DEV_CONSOLE , 0);
//next we will open the file. We will want to read and write, so we use
// O_RDWR. If the files does not already exist, we want to create it, so
// we use O_CREAT. If the file *DOES* already exist, we want to truncate
// and start fresh, so we delete all previous contents with O_TRUNC
tst = open("test.txt", O_RDWR | O_CREAT | O_TRUNC);
// always check the return value.
if(tst < 0)
{
write(dsply, "ERROR!", 6);
return 0;
}
strcpy(buf, "this text should write in file.")
write(tst, buf, strlen(buf));
memset(buf, 0, sizeof(buf));
read(tst, buf, 30);
//be sure to close when you are done
close(tst);
write(dsply,buf,strlen(buf));
//you'll want to close your devices, as well
close(dsply);
return 0;
}
Your comments also ask about searching. For that, you'll also need to use lseek with one of the following which specifies where you are starting from:
SEEK_SET — Beginning of file
SEEK_CUR — Current seek pointer location
SEEK_END — End of file
example
SomeDataStruct myData;
...
//assume "curPosition" is set to the beginning of the next data structure I want to read
lseek(file, curPosition, SEEK_SET);
result = read(file, (char*)&myData, sizeof(SomeDataStruct));
curPosition += sizeof(SomeDataStruct);
//now "curPosition" is ready to pull out the next data structure.
NOTE that the internal file pointer is already AT "curPosition", but doing it this way allows me to move forward and backward at will as I manipulate what is there. So, for example, if I wanted to move back to the previous data structure, I would simply set "curPosition" as follows:
curPosition -= 2 * sizeof(SomeDataStruct);
If I didn't want to keep track of "curPosition", I could also do the following which would also move the internal file pointer to the correct place:
lseek(file, - (2 * sizeof(SomeDataStruct)), SEEK_CUR);
You get to pick whichever method works best for you.
So I am trying to do some very simple read/writes on a file. Since it's for an assignment I can't use more sophisticated functions using File*.
I can easily create a file and write to it but if I try to read back my content (it's the same content but my problem boils down to this) I don't get what I expect and I can't yet see why.
Here the code snippet that causes me problems:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char** argv){
int fdisk = open("testfile.txt", O_RDWR | O_CREAT | O_APPEND);
char buff [20] = "Just a short text!!!";
write(fdisk, buff, sizeof(buff));
char buff2[20];
read((fdisk), buff2, sizeof(buff2));
printf("Context of deleted file: %c\n",buff[1]);
printf("Context of deleted file: %c\n",buff2[1]);
return 0;
}
When you create a file you need to specify the file access mode:
int fdisk = open("testfile.txt", O_RDWR | O_CREAT | O_APPEND, 0666);
Otherwise the access mode is some indeterminate value.
And before reading it back you need to rewind it:
lseek(fdisk, 0, SEEK_SET); // rewind
The problem is that the write call leaves the file descriptor pointing to just after the data written (so more writes will go after that rather than overwriting the same data), so the following read call tries to read data after that which was written, and probably gets nothing.
I think it is a combination of issues:
You are not rewinding or re-opening the file, so when you read you are always reading from the end of the file.
You are using append mode, so it will add data to the end of the file. This means that after the first run you will be writing data at the end of the file but always reading from the beginning (assuming you address the first problem).
You are not setting the permissions, so you get random file permissions and the file may not be readable after creating it.
Your print statement is only printing the second character from each buffer, rather than a full string.
Here is a minimal working example. This compiles and runs with the expected results on my machine.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char** argv){
int fdisk = open("testfile.txt", O_RDWR | O_CREAT, 0666);
char buff[] = "Just a short text!!!";
write(fdisk, buff, sizeof(buff));
lseek(fdisk, 0, SEEK_SET);
char buff2[sizeof(buff)];
read((fdisk), buff2, sizeof(buff2));
printf("Context of deleted file: %s\n",buff);
printf("Context of deleted file: %s\n",buff2);
return 0;
}
As a final note, the functions you are using (read/write) all return values indicating whether the operation was successful. You should check them. They would have indicated that the read operation in your problem was not actually reading any data (because it was at the end of the file).
For homework I have to read from the standard input, save it to a file and then read the file in another process. However, I'm confused as to why this code does not work:
while((n = read(0,buf,sizeof(buf))) > 0) {
int tempfile = open("testfile", O_TRUNC | O_CREAT, 0666);
write ( tempfile , buf , sizeof(buf) );
close(tempfile);
process("testfile");
}
I'm not supposed to use any stdio stuff.
When I look at the file I've created, it has 0 bytes and yet the buffer itself has the correct information....can someone help em see where I've gone wrong?
I can use process on file names and it correctly reads them.
You've specified O_TRUNC | O_CREAT for the open flags, but you've failed to specify O_RDWR or O_WRONLY.
You also probably want to write n bytes, not sizeof(buf), as the remaining sizeof(buf) - n bytes are uninitialized.
Given the following code (it's supposed to write "helloworld" in a "helloworld" file, and then read the text):
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FNAME "helloworld"
int main(){
int filedes, nbytes;
char buf[128];
/* Creates a file */
if((filedes=open(FNAME, O_CREAT | O_EXCL | O_WRONLY | O_APPEND,
S_IRUSR | S_IWUSR)) == -1){
write(2, "Error1\n", 7);
}
/* Writes hello world to file */
if(write(filedes, FNAME, 10) != 10)
write(2, "Error2\n", 7);
/* Close file */
close(filedes);
if((filedes = open(FNAME, O_RDONLY))==-1)
write(2, "Error3\n", 7);
/* Prints file contents on screen */
if((nbytes=read(filedes, buf, 128)) == -1)
write(2, "Error4\n", 7);
if(write(1, buf, nbytes) != nbytes)
write(2, "Error5\n", 7);
/* Close file after read */
close(filedes);
return (0);
}
The first time I run the program, the output is:
helloworld
After that every time I to run the program, the output is:
Error1
Error2
helloworld
I don't understand why the text isn't appended, as I've specified the O_APPEND file.
Is it because I've included O_CREAT?
It the file is already created, shouldn't O_CREAT be ignored?
O_EXCL forces the file to be created. If the file already exists, the call fails.
It is used to ensure that the file has to be created, with the given permissions passed in the third parameter. In short, you have these options:
O_CREAT: Create the file with the given permissions if the file doesn't already exist. If the file exists, it is opened and permissions are ignored.
O_CREAT | O_EXCL: Create the file with the given permissions if the file doesn't already exist. If the file exists, it fails. This is useful in order to create lockfiles and guarantee exclusive access to the file (as long as all programs which use that file follow the same protocol).
O_CREAT | O_TRUNC: Create the file with the given permissions if the file doesn't already exist. Otherwise, truncate the file to zero bytes. This has more of the effect we expect when we think "create a new blank file". Still, it keeps the permissions already present in the existing file.
More information from the manual page:
O_EXCL
When used with O_CREAT, if the file
already exists it is an error and
the open() will fail. In this context,
a symbolic link exists, regardless of
where it points to. O_EXCL is broken
on NFS file systems; programs which
rely on it for performing locking
tasks will contain a race condition.
The solution for performing atomic
file locking using a lockfile is to
create a unique file on the same file
system (e.g., incorporating hostname
and pid), use link(2) to make a link
to the lockfile. If link() returns 0,
the lock is successful. Otherwise, use
stat(2) on the unique file to check if
its link count has increased to 2, in
which case the lock is also
successful.