Overwrite a file instead of appending to it? - c

I'm a newbie linux programmer that ordered to be a sitin for my colleague.
I opened up a file with this line:
err = open("path/foo.txt", O_RDWR | O_CREAT,0777);
And now I write to it, and hexdump the file, the output showed that I appended
new content instead of overwriting the original.
How do I overwrite?
Also if you ask me "wth is "open"?" I'd refer to the newbie defense and say I don't know. The closest thing I know is fopen but I don't know what library/framework my colleague is using. Posix perhaps?

If you open the manual for open(2) via man 2 open, you will find a list of accepted flags.
You should be reading man pages in any case and it's not hard, the only things you need to remember is that you can search for keywords by pressing /, n/N for jumping between things that were found, and q to exit when you're done, everything else is superfluous and may only speed things up a little.
The flag you're looking for is
O_TRUNC
If the file already exists and is a regular file and the access 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.
Otherwise, the effect of O_TRUNC is unspecified.
So all you have to do is add this flag whenever you want to overwrite the file completely.
But if you can, you should be using fopen(3), in which case the mode you're looking for is w+, and it is equivalent to O_RDWR | O_CREAT | O_TRUNC in open(2).
open(2) is a low level system call and reading from a raw file descriptor efficiently and correctly is not that simple, FILE* you get from fread(3) on the other hand is an implementation that handles most of details like buffering for you, and unless you can do better, you shouldn't be avoiding it.
By the way, 0777 file permission is rarely needed and doesn't apply to regular files, I'd recommend you open normal text files with 0666, which does everything you need except does not enable it to be executed as a program.

Related

How to implement append method in our own shell using open system call in c?

I am trying to implement my own shell in which i have to make a shell feature by which i will able to append text to file by using >>.here is my code....
int filedesc = open(inputargs[limit-1],O_WRONLY | O_APPEND);
//printf("%s\n",inputargs[limit-1]);
if(filedesc < 0) {
printf("Error opening file\n");
}
else{
dup2(filedesc,1);
}
filedesc always returning -1.
Transferring an edited form of my relevant comments into an answer.
Using O_APPEND is correct — and you do need O_WRONLY too — and maybe you should add O_CREAT in case it doesn't exist yet (but you definitely don't want O_TRUNC). I suppose O_RDWR would also work (in place of O_WRONLY), but it is not a good idea.
Does the file exist to be appended to? If not, add O_CREAT and a file mode argument. Tradition says "use 0666" (aka S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH — there's a reason people prefer typing the octal number!). I'd be sorely tempted to not grant write to group or others (using 0644), even though you can (and I do) use umask 022 to suppress those permissions.
What do you get from perror(inputargs[limit-1]); (instead of or as well as the printf() message? That will tell you why the system thinks it can't open the file. And don't forget that the system is right — it can't open the file. Your code has a problem — it is not yet clear what that is. It might be EPERM (no permission); it might be ENOENT (no such file or directory); it might be something else, but those two are the most likely.
Getting "permission denied" from perror(inputargs[limit-1]);
So, you don't have permission to write on the file — what are you going to do about that? Change the file name to one you have permission to write on, or set the permissions on the file you have so that you can write (append) to it? Or something else? If you, somewhere along the line, create the file using O_CREAT but don't specify the third (optional) argument to open(), you get quasi-random weird permissions on the file that you create. What does ls -l file-to-append-to tell you? (Note that the third argument is mandatory when O_CREAT is one of the options used in the call to open() — it is optional and ignored otherwise.)
Note that you should always write error messages in a shell to standard error and not to standard output. Even if there's I/O redirection on standard error, the shell still writes the error messages to standard error, and the redirection points it where the user wanted it to go.

What is the difference between open with O_EXCL and using flock

Imagine I have two processes trying to open the same file in read only mode. I want only one process to open the file and the other one to fail (essentialy a file lock). What is the difference between doing
if (open("name", O_RDONLY | O_EXCL | O_NONBLOCK, 0666) == -1 && EWOULDBLOCK == errno) {
...
}
and
int fd = open("name", O_RDONLY | O_EXCL, 0666);
if (flock(fd, LOCK_EX | LOCK_NB, 0666) && EWOULDBLOCK == errno) {
...
}
Is it the same? Does the above work at all as I expect? If not then what does O_EXCL do? Is there a way to make the above one work as I want?
As the question What is the written-out word for O_EXCL cross-referenced by Erdal Küçük in a comment implies (if not states), the O_EXCL flag is only of relevance if you are creating a file. Your other options to open() do not create a file, so the use of O_EXCL is irrelevant (harmless but not useful). Even if you were creating the file, it would not stop another program from opening the file for reading after the file is created but while other processes have the file open. Note that if the other program tries to create the file with O_EXCL, then that other program will fail to create the file — that's what O_EXCL is for.
You will need to use one of the advisory locking mechanisms — flock(2) (found on Linux and BSD systems (and probably others) but not standardized by POSIX), or POSIX functions lockf() or
fcntl().
All the programs accessing the file will need to agree on using the advisory locking. If someone runs cat name, the cat command will not pay any attention to the advisory locks on the file name.
Some systems support mandatory file locking. When supported, this is activated by setting the SGID bit on the file while the group-execute bit is cleared. If the system supports mandatory file locking, it is likely the ls -l would list an l in the group-execute bit column; otherwise, it will likely report S. So the permissions might show as either of these:
-rw-r-lr--
-rw-r-Sr--
The file locking functions should not be confused with flockfile() which is for locking a single file stream in a multi-threaded program, not for locking files.

Trying to implement append in my own shell Linux

I'm trying to implement append command in my own shell.
I succeeded to append to existing file but whenever I'm trying to append to file doesn't exist it makes a file without any permission (not read and not write)
if (append) {
fd = open(outfile,'a');
lseek(fd,0,SEEK_END);
close (STDOUT_FILENO) ;
dup(fd);
close(fd);
/* stdout is now appended */
}
What should I do to make a file with permissions
?
The open() system call doesn't use a character constant to indicate 'append'. Read the POSIX specification for open() — and look at O_APPEND etc. You need more flags than just O_APPEND, and you need three arguments to open() if you want to create the file if it doesn't exist (O_CREAT), etc.
if (append)
{
fd = open(outfile, O_CREAT|O_APPEND|O_WRONLY, 0644);
if (fd < 0)
…deal with error…
}
You can write 0644 as S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH but the octal is shorter and (after 30+ years practice) a lot easier to read. You can add write permissions for group (S_IWGRP) and others (S_IWOTH) if you like (0666 in octal), but unless you know you want group members and others to modify the files, it is safer to omit those — for all it goes against historical precedent. Users can and should set the shell umask value to 022 to prevent group and others from being able to write to files by default, but there's no harm (IMO) in being secure without that.

How does mode affect the permisson on newly created files in Linux?

I'm new to Linux, still struggling to understand how permisson control work in Linux. The open function prototype is sth like :
int open(char *filename, int flags, mode_t mode);
Let's I have the following code:
fd = open("foo.txt", O_CREAT|O_RDWR, S_IRUSR)
and let's say the file "foo.txt" didn't exist before, so the above statment will create a file called "foo.txt", and current process who executes this open statment can read and write this file. But after this process terminates, If another process starts and tries to open this file. Below is my question:
Q1-Since the file was created with S_IRUSR(owner can read this file) in the first open call, does it mean that even I as owner of the file, if I start a new process to open this file again, I can only read this file and I cannot write this file, is my understanding correct?
If my understanding is correct, is it sensible/practicable that owners create sth that they cannot have full access to it later?
Q2-If my above understanding is correct, then in the second call to open by a new process. I can only call like:
fd = open("foo.txt", O_RDONLY) // uses flags like O_WRONLY or O_RDWR will throw an error?
since the first open specified the mode as S_IRUSR, which maps to O_RDONLY in the subsequent calls, is my understanding correct?
Correct, if you create the file with permissions S_IRUSR (often written in octal as 0400), then you will not be able to open the file for writing. Attempting to do so will fail and set errno to EACCES.
This is quite practical as it gives you a way to protect files you do not want to accidentally overwrite, as long as the permissions stay as they are. However, as the owner, you have the power to change the permissions later, using the chmod() system call. So it's not as though you have permanently lost the ability to write that file; you can give yourself back that ability whenever you want.

How to reserve a file descriptor?

I'm writing a curses-based program. In order to make it simpler for me to find errors in this program, I would like to produce debug output. Due to the program already displaying a user interface on the terminal, I cannot put debugging output there.
Instead, I plan to write debugging output to file descriptor 3 unconditionally. You can invoke the program as program 3>/dev/ttyX with /dev/ttyX being a different teletype to see the debugging output. When file descriptor 3 is not opened, write calls fail with EBADF, which I ignore like all errors when writing debugging output.
A problem occurs when I open another file and no debugging output has been requested (i.e. file descriptor 3 has not been opened). In this case, the newly opened file might receive file descriptor 3, causing debugging output to randomly corrupt a file I just opened. This is a bad thing. How can I avoid this? Is there a portable way to mark a file descriptor as “reserved” or such?
Here are a couple of ideas I had and their problems:
I could open /dev/null or a temporary file to file descriptor 3 (e.g. by means of dup2()) before opening any other file. This works but I'm not sure if I can assume this to always succeed as opening /dev/null may not succeed.
I could test if file descriptor 3 is open and not write debugging output if it isn't. This is problematic when I'm attempting to restart the program by calling exec as a different file descriptor might have been opened (and not closed) prior to the exec call. I could intentionally close file descriptor 3 before calling exec when it has not been opened for debugging, but this feels really uggly.
Why use fd 3? Why not use fd 2 (stderr)? It already has a well-defined "I am logging of some sorts" meaning, is always (not true, but sufficiently true...) and you can redirect it before starting your binary, to get the logs where you want.
Another option would be to log messages to syslog, using the LOG_DEBUG level. This entails calling syslog() instead of a normal write function, but that's simply making the logging more explicit.
A simple way of checking if stderr has been redirected or is still pointing at the terminal is by using the isatty function (example code below):
#include <stdio.h>
#include <unistd.h>
int main(void) {
if (isatty(2)) {
printf("stderr is not redirected.\n");
} else {
printf("stderr seems to be redirected.\n");
}
}
In the very beginning of your program, open /dev/null and then assign it to file descriptor 3:
int fd = open ("/dev/null", O_WRONLY);
dup2(fd, 3);
This way, file descriptor 3 won't be taken.
Then, if needed, reuse dup2() to assign file descriptor 3 to your debugging output.
You claim you can't guarantee you can open /dev/null successfully, which is a little strange, but let's run with it. You should be able to use socketpair() to get a pair of FDs. You can then set the write end of the pair non-blocking, and dup2 it. You claim you are already ignoring errors on writes to this FD, so the data going in the bit-bucket won't bother you. You can of course close the other end of the socketpair.
Don't focus on a specific file descriptor value - you can't control it in a portable manner anyway. If you can control it at all. But you can use an environment variable to control debug output to a file:
int debugFD = getDebugFD();
...
int getDebugFD()
{
const char *debugFile = getenv( "DEBUG_FILE" );
if ( NULL == debugFile )
{
return( -1 );
}
int fd = open( debugFile, O_CREAT | O_APPEND | O_WRONLY, 0644 );
// error checking can be here
return( fd );
}
Now you can write your debug output to debugFD. I assume you know enough to make sure debugFD is visible where you need it, and also how to make sure it's initialized before trying to use it.
If you don't pass a DEBUG_FILE envval, you get an invalid file descriptor and your debug calls fail - presumably silently.

Resources