Given one txt file Im trying to make a C program that copy the content of that into other which name is passed by arguments.The program must read blocks of 512 bytes from the source file and write the bytes read in the destination file.
My attempt:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include<fcntl.h>
int main(int argc, char* argv[]){
if(argc < 2){
printf("ERROR: Missing arguments\n");
exit(1);
}
int fdo, fdd;
if((fdo = open(argv[1], O_RDONLY)) == -1){
printf("ERROR: Origin file %s can not be opened\n", argv[1]);
exit(1);
}
if(fdd = open(argv[2], O_CREAT | O_TRUNC, 0666) == -1){
printf("ERROR: Dest. file %s can not be opened\n", argv[2]);
exit(1);
}
char buff[512];
size_t n_bytes;
while((n_bytes = read(fdo,buff,512)) > 0){
if(write(fdd,buff,n_bytes) < 0){
printf("Can not write buffer content in %s \n", argv[2]);
exit(1);
}
}
if(n_bytes < 0){
printf("Can not read %s file \n", argv[1]);
exit(1);
}
close(fdo);
close(fdd);
return 0;
}
The content of the file test.txt is:
abcdef
1890
And their permissions are:
usuarioso#usuarioso-virtualbox:~/Documentos/SO 2022/pr2/API de ficheros y directorios$ ls -l
total 32
-rwxrwxr-x 1 usuarioso usuarioso 17048 nov 23 16:15 copyrf
-rw-rw-r-- 1 usuarioso usuarioso 774 nov 23 16:39 copyrf.c
-rw-rw-r-- 1 usuarioso usuarioso 12 nov 23 16:52 test.txt
However when I execute it I get the following:
usuarioso#usuarioso-virtualbox:~/Documentos/SO 2022/pr2/API de ficheros y directorios$ gcc -o copyrf copyrf.c
usuarioso#usuarioso-virtualbox:~/Documentos/SO 2022/pr2/API de ficheros y directorios$ ./copyrf test.txt test1.txt
abcdef
1890
usuarioso#usuarioso-virtualbox:~/Documentos/SO 2022/pr2/API de ficheros y directorios$ ls -l
total 28
-rwxrwxr-x 1 usuarioso usuarioso 17008 nov 23 17:00 copyrf
-rw-rw-r-- 1 usuarioso usuarioso 771 nov 23 16:59 copyrf.c
-rw-rw-rw- 1 usuarioso usuarioso 0 nov 23 17:00 test1.txt
-rw-rw-r-- 1 usuarioso usuarioso 12 nov 23 16:52 test.txt
usuarioso#usuarioso-virtualbox:~/Documentos/SO 2022/pr2/API de ficheros y directorios$ cat test1.txt
usuarioso#usuarioso-virtualbox:~/Documentos/SO 2022/pr2/API de ficheros y directorios$
i.e the file test1.txt is created but is empty and the content of the file test.txt is printed in console.
What am I missing?
Clang's gcc points out the problem pretty well:
$ gcc temp.c
temp.c:19:12: warning: using the result of an assignment as a condition without parentheses [-Wparentheses]
if(fdd = open(argv[2], O_CREAT | O_TRUNC, 0666) == -1){
~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
temp.c:19:12: note: place parentheses around the assignment to silence this warning
if(fdd = open(argv[2], O_CREAT | O_TRUNC, 0666) == -1){
^
( )
temp.c:19:12: note: use '==' to turn this assignment into an equality comparison
if(fdd = open(argv[2], O_CREAT | O_TRUNC, 0666) == -1){
^
==
... in particular, fdd is being set to the result of (open(...) == -1), i.e. to 1 if the open() call returned -1, or to 0 if the open() call returned any other value. Since neither 0 nor 1 is the value of the file-descriptor returned by open(), your subsequent calls to write(fdd, ...) are using an invalid file-descriptor value and therefore failing.
The fix would be to add parentheses as you did for your first/open-for-read open() call, making it like this instead:
if((fdd = open(argv[2], O_CREAT | O_TRUNC, 0666)) == -1){
In addition to that, you need to add O_WRONLY to the oflag argument of your second call to open(), otherwise fdd will not be allowed to write to the file.
You forgot some parentheses so fdd is getting set to 0 or 1 instead of the return value of open. Compare it to the fdo code. It's best to avoid assignment inside an expression like that unless you really know what you're doing. It would also be one of the first things to remove while simplifying your code to make a minimal complete verifiable example for Stack Overflow.
In the line:
if((fdo = open(argv[1], O_RDONLY)) == -1)
you put the assignment into brackets. This first assigns the opened file descriptor to fdo and then checks if that value is equal to -1 afterwards.
On the other hand, in second case:
if(fdd = open(argv[2], O_CREAT | O_TRUNC, 0666) == -1)
you did not put the assignment in the brackets. Because of this, the == operator is evaluated first, which returns a value of 0 (false) or 1 (true) instead of a valid file descriptor. This value is then assigned to fdd, causing you to write to a wrong file descriptor (standard output).
Related
EDIT:
lsof shows that it's opened by ptmx. Thanks to #zwol
My code below prints the file descriptor returned from open. I noticed that 20 is missing. There is no similar question to the best of my knowledge.
background:
Filesystem: ext4
Ubuntu 20.04 on WSL2
code:
int main()
{
char name[2] = "a";
for (int i = 0; i < 52; i++) {
int fd = open(name, O_RDWR | O_CREAT, 0644);
printf("fd is %d\n", fd);
}
return 0;
}
output:
$ ./a.out
fd is 3
fd is 4
fd is 5
...
fd is 18
fd is 19
fd is 21 <-- here
fd is 22
...
lsof
...
a.out 1815 ryan 19u REG 8,16 0 42321 /tmp/tmp/a
a.out 1815 ryan 20u CHR 5,2 0t0 15832 /dev/ptmx
a.out 1815 ryan 21u REG 8,16 0 42321 /tmp/tmp/a
...
I have 2 questions:
What's the reason behind it? (What triggers ptmx)
Will there be more indices missing if I keep opening files? (More programs like ptmx?)
POSIX says that every system call that allocates file descriptors must use the lowest number(s) that are not already in use. Therefore, descriptor number 20 must have already been open. Your sample program doesn't open anything before the loop, so it must have been inherited from your shell, or opened by the C library prior to main.
You can find out more by having your program print its PID and then sleep for a long time, after the loop, and then running lsof on it while it's sleeping.
File is there and having json data inside it. I want to know length of file.but when i try below code it but size remains 0.
int file_contentl_Len = 0;
int fd_0 ;
fd_0 = open(FILE_PATH_CONFIG_0, O_WRONLY | O_TRUNC | O_CREAT, 0644);
if(fd_0 < 0)
{
printf("\r\nError opening Config file %s: %s\n",FILE_PATH_CONFIG_0, strerror(errno));
return -1;
}
struct stat buf;
fstat(fd_0, &buf);
file_contentl_Len = buf.st_size;
printf("\r\nConfig file %s content length: %d\r\n", FILE_PATH_CONFIG_0, file_contentl_Len);
You opened the file for writing with truncation, creating it if necessary — O_WRONLY | O_TRUNC | O_CREAT.
The size of zero tells you the truncation worked, or the file was created empty.
If you wanted to read what was in the file, use O_RDONLY instead. Or use O_RDWR and think carefully about whether to allow the file to be created.
I want to redirect the output to a specific file when the program encounters a ">" and for it to get the input from a file if it finds a "<".
This works for the input, but when I try the output to file it doesn't write anything in the actual file nor does it show it in the terminal.
I can't seem to figure out why, can anyone help?
switch(fork()){
case 0:
for(i=0; i<num; i++){
if(strcmp(argv[i], ">") == 0){
if(fd1 = open(argv[i+1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR) < 0){
perror("cant open file");
exit(0);
}
dup2(fd1, 1);
close(fd1);
argv[i] = NULL;
}
if(strcmp(argv[i], "<") == 0){
if(fd0 = open(argv[i+1], O_RDONLY) < 0){
perror("cant open file");
exit(0);
}
dup2(fd0, 0);
close(fd0);
}
}
execvp(argv[0], argv);
The primary problem is that the = operator has lower precedence than the < operator. Thus, in your expression
fd1 = open(argv[i+1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR) < 0
, the return value of open() is compared to 0, and then the result of that comparison -- either 1 or 0 -- is assigned to fd1. The newly-opened file descriptor is lost, and goes unused. The actual result of the comparison is most likely 0 (the file was successfully opened), so your dup2() call a few lines later attempts to dupe file descriptor 0 onto file descriptor 1. In all likelihood that works, but any attempt by the program you exec to write to that file descriptor very likely fails.
You ought to write it as
(fd1 = open(argv[i+1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0
Alternatively, be a little less clever and separate that into two statements, so that the precedence question does not arise in the first place.
You have the same problem when you redirect input, so I'm doubtful of your claim that "This works for the input", but because in that case you'll end up just duping file descriptor 0 onto itself, the problem will manifest simply as the redirection not working as expected -- input will still come from the original program's standard input, not the standard input you designate.
Even that might go unnoticed, however, if you allow the shell to perform the input redirection instead of quoting the redirection operator so as to get it through to the program as an argument.
I have the following simple program that catenates infile to outfile
char *execArgs[] = { "cat", NULL};
int outfile = open("outfile", O_WRONLY | O_CREAT, 0644);
int infile = open("infile", O_RDONLY, 0);
dup2(outfile, STDOUT_FILENO);
dup2(infile, STDIN_FILENO);
close(outfile);
close(infile);
execvp(execArgs[0], execArgs);
Now, suppose the content of infile is
this is infile
and outfile is
this is outfile
After running the program, the content of outfile has an extra "e" at the end as such
this is infilee
Also, if the outfile is instead
this is outfile
this is outfile
It becomes
this is infilee
this is outfile
What is wrong?
What you're experiencing is the expected behavior. cat just writes the number of bytes it reads, so since the original outfile is longer, the remaining bytes contain what they contained before.
If you're asking why you get different behavior from using the shell to perform:
cat < infile > outfile
the reason is that the shell opens outfile (or any target of >) with O_TRUNC, which truncates the file to zero length as part of opening it. You can get the same behavior by adding | O_TRUNC to your open call's flags argument.
Try using O_TRUNC this will truncate the file it it exists.
int outfile = open("outfile", O_WRONLY | O_CREAT | O_TRUNC, 0644);
It doesn't have an extra e, it has the same e it always had at that position.
Look at the strings you're writing:
this is infilee
this is outfile
^
+-- lines up
The problem is that the output is not truncated, and thus it retains all its old content. You're just overwriting it from the start.
I am trying to read some text from a file and write it to another using open(), read() and write().
This is my open() for the file-to-write-to (I want to create a new file and write into it):
fOut = open ("test-1", O_RDWR | O_CREAT | O_SYNC);
This is setting file-permissions to something I don't understand at all. This is the output of ls -l:
---------T 1 chaitanya chaitanya 0 2010-02-11 09:38 test-1
Even the read permission is locked. I tried searching for this, but could not find ANYTHING.
Strangely, write() still successfully writes data to the file.
Also, if I do a 'chmod 777 test-1', things start working properly again.
Could someone please let me know where I am going wrong in my open call?
Thanks!
For your reference, I have pasted the complete program below:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main () {
char buffer[512], ch;
int fIn, fOut, i;
ssize_t bytes;
FILE *fp = NULL;
//open a file
fIn = open ("test", O_RDONLY);
if (fIn == -1) {
printf("\nfailed to open file.");
return 1;
}
//read from file
bytes = read (fIn, buffer, sizeof(buffer));
//and close it
close (fIn);
printf("\nSuccessfully read %d bytes.\n", bytes);
//Create a new file
fOut = open ("test-1", O_RDWR | O_CREAT | O_SYNC);
printf("\nThese are the permissions for test-1\n");
fflush(stdout);
system("ls -l test-1");
//write to it and close it.
write (fOut, buffer, bytes);
close (fOut);
//write is somehow locking even the read permission to the file. Change it.
system("chmod 777 test-1");
fp = fopen ("test-1", "r");
if (fp == NULL) {
printf("\nCan't open test-1");
return 1;
}
while (1)
{
ch = fgetc(fp);
if (ch == EOF)
break;
printf("\n%c", ch);
}
fclose (fp);
return 0;
}
open() takes a third argument which is the set of permissions, i.e.
open(filename, O_RDWR|O_CREAT, 0666)
0666 is an octal number, i.e. every one of the 6's corresponds to three permission bits
6 = rw
7 = rwx
first three bits for owner permission, next three bits for group permission and next is for the world
the first digit - represents that is file or directory. (0 - file, d - directory)
here we used 0 means file
It's a typical pitfall. The compiler allows you to leave the permission argument away because when you open an existing file the permission bits don't make sense. But when you forget the argument when you create a file, you get a random set of permissions, e.g. 0000 in your case (---).
Reading http://linux.die.net/man/2/open it seems you missed the mode parameter for open:
mode must be specified when O_CREAT is in the flags, and is ignored otherwise.
The argument mode specifies the permissions to use in case a new file is created.
This question recently helped me out, so I wanted to do my part to add a bit more depth as to what's going on. Like it was stated before, you were missing the third argument to open(). However, the permissions you see aren't random; they're coming from the stack. Look at the following code snippet:
asm("push $0");
asm("push $0");
asm("push $0");
fd = open("base", O_RDWR|O_CREAT);
Note the following result:
----------. 1 user user 4 Feb 26 08:21 base
Let's change the first push to 1, i.e. execute permission:
asm("push $1;push $0;push $0");
fd = open("base", O_RDWR|O_CREAT);
and we get:
---------x. 1 user user 4 Feb 26 08:25 base
Change the push to 4, i.e. read permission, and mess with the other two values:
asm("push $4;push $5;push $6");
fd = open("base", O_RDWR|O_CREAT);
and we get:
-------r--. 1 user user 4 Feb 26 08:27 base
Thus we can see the third value popped off the stack (first pushed) is what really matters. Finally for fun we can try 5 and then 50, which respectively result in:
-------r-x. 1 user user 4 Feb 26 08:27 base
----rw----. 1 user user 4 Feb 26 08:28 base
Hope this adds some clarity!
Actually umask() only filters permissions and does not set them. The typical umask() value is 0002 ("don't give away write permission to the world") and if your mode value in the open( "file", O_CREAT, 0777) gave all permissions, the resulting file would have 775 as its permssions.
Not strictly relevant to the question, but the accepted answer could use this clarifying point:
There is a relationship between rwx and its numerical representation that can be seen by treating the presence of a letter as a binary 1, and its absence as a binary 0.
e.g.
rwx <--> 111 (binary) <--> 7 (octal)
r-- <--> 100 (binary) <--> 4 (octal)
-wx <--> 011 (binary) <--> 3 (octal)
As a further addendum, you may now consider the chmod command:
chmod 777 filename.extension --> rwxrwxrwx permissions
777 <--> 111 111 111 <--> rwx rwx rwx
or: chmod 654 filename.extension --> rw-r-x-r--
654 <--> 110 101 100 <--> rw- r-x r--
Hope this is informative!
you can call umask(0); system call before using open(); system call to set your choices permissions to file correctly.
This is kind of an old thread, but I think people should be aware of the "sys/stat.h" library. This includes a bunch of symbolic constants for setting permission bits.
For example: To open a file with Read/Write permissions enabled for the user
#include <fcntl.h>
#include <sys/stat.h>
open("Your/File/Path", O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);
where:
S_IWUSR // Sets the Users Write bit
S_IRUSR // Sets the Users Read bit
This library includes a bunch of others, I won't list them all here but you can read up on it all here.
Of course you can put in the octal values to set these bits, however some may argue that it is poor coding practice.