Write to file without write permission in C? - c

I want to create a file and the file has no write permission like this.
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
main()
{
int fd;
fd=open("xxx",O_CREAT|O_WRONLY|O_APPEND,S_IRUSR);
if(fd==-1)
return 0;
else
if(write(fd,"created",7)==-1)
return 0;
if(close(fd)==-1)
return 0;
printf("OK");
}
I ran executable file and notice that the file xxx could be written to. Of cource, when I ran executable after first time, it worked like I expect when it could't be written to.
But how can I make the file xxx even can't be written to in first time (when it is created).

You should remove:
O_WRONLY
And it should look like this:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void){
int fd;
fd=open("test.sh",O_CREAT|O_APPEND,S_IRUSR);
if(fd==-1)
return 0;
else
if(write(fd,"created",7)==-1)
return 0;
if(close(fd)==-1)
return 0;
printf("OK");
return 0;
}
And if you try to write that file, you will get:
[ Error writing test.sh: Permission denied ]
Anyway, on my Linux machine if I wanna check that file, I will do it like this:
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
void fileCheck(const char *fileName){
if(!access(fileName, F_OK )){
printf("The File %s\t Found\n",fileName);
}else{
printf("The File %s\t not Found\n",fileName);
exit(1);
}
if(!access(fileName, R_OK )){
printf("The File %s\t can be read\n",fileName);
}else{
printf("The File %s\t cannot be read\n",fileName);
}
if(!access( fileName, W_OK )){
printf("The File %s\t it can be Edited\n",fileName);
}else{
printf("The File %s\t it cannot be Edited\n",fileName);
}
if(!access( fileName, X_OK )){
printf("The File %s\t is an Executable\n",fileName);
}else{
printf("The File %s\t is not an Executable\n",fileName);
}
}
int main (void){
char *fileName = "test.sh";
fileCheck(fileName);
return 0;
}
And the output says:
The File test.sh Found
The File test.sh can be read
The File test.sh it cannot be Edited
The File test.sh is not an Executable

I can understand author's intent. He want to test the usage of S_IRUSR. I will explain why it doesn't work at first time.
At first let us resolve your question. you can just remove O_WRONLY and O_APPEND flag, and add O_RDONLY flag or not. Codes are following:
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
int main() {
int fd;
fd = open("xxx", O_CREAT|O_RDONLY, S_IRUSR);
// or
// fd = open("xxx", O_CREAT, S_IRUSR);
if (fd == -1) {
printf("open failed\n");
return 0;
} else {
if (write(fd, "created", 7) == -1) {
printf("write failed\n");
return 0;
}
}
if (close(fd) == -1) {
printf("close failed\n");
return 0;
}
printf("OK\n");
}
then execute the program whenever, it will always says "write failed", and "xxx" is empty.
$ ls
test.c
$ make test
cc test.c -o test
$ ls
test test.c
$ ./test
write failed
$ ls
test test.c xxx
$ cat xxx
$ ./test
write failed
you said:
Of cource, when I ran executable after first time, it worked like I expect when it could't be written to.
in fact, if use your code, ran after first time, it is not couldn't be written to, it is not be open.
$ ls
test test.c
$ ./test
OK
$ ls
test test.c xxx
$ cat xxx
created
$ ./test
open failed
Then let us face the more important question, why S_ISUSR doens't work at first time. let us run 'man 2 open', it says:
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.
It means S_ISUSR only works after first time. Even S_ISUSR is read only, but in fact still return read/write permission, the final permission depends on the flags, if the flag is O_WRONLY, it can only write when create, if the flag is O_RDONLY, it can only read when create.
let's do a test:
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
int main() {
int fd;
char buf[10];
fd = open("xxx", O_CREAT|O_WRONLY, S_IRUSR);
if (fd == -1) {
printf("open failed\n");
return 0;
} else {
if (write(fd, "created", 7) == -1) {
printf("write failed\n");
return 0;
}
}
if (read(fd, buf, 7) == -1) {
printf("read failed\n");
return 0;
} else {
printf(buf);
}
if (close(fd) == -1) {
printf("close failed\n");
return 0;
}
printf("OK\n");
}
and result is expected, at first time, when file create, if has write permission and has no read permission, after first time, it open failed:
$ ls
test.c
$ make test
cc test.c -o test
$ ./test
read failed
$ ./test
open failed
$ cat xxx
created
And I can understand why they design the mode parameter only work after first time. You think, if S_IRUSR works when file create, then the file is always empty and read only, it has no sense.

Related

Missing message in FIFO

I have built a IPC prgram with FIFO(named piped).
A very interesting problem is that my written message may be lost.
the following is the code snippet.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main()
{
char buffer[2000] = {0};
strcpy(buffer, "abc");
char *write_path = "test-123";
mkfifo(write_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int dummy = open(write_path, O_RDONLY | O_NONBLOCK);
int fd = open(write_path, O_WRONLY | O_NONBLOCK);
int bytes = write(fd, buffer, 2000);
printf("write_path:%d %s %d %d\n", bytes, write_path, dummy, fd);
close(fd);
close(dummy);
}
how to reproduce?
ubuntu 1804
gcc main.c -o main
./main
cat < test-123
it will be pending. I think, it should output abc.
You open the FIFO in nonblocking mode, which in means I/O functions can fail with (-1 and errno set to) EAGAIN or EWOULDBLOCK, instead of blocking (waiting) for the function to complete. In Linux, open descriptors to a FIFO have the same semantics as pipes.
If you bothered to check for errors (printing strerror(errno) whenever open() or write() returns -1 indicating an error), you'd already know the reason why this fails. As is, the code shown does not even reproduce the problem. The following code,
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
const char *msg = "The Example Message.\n";
const size_t msg_len = strlen(msg);
int rfd, wfd;
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *mypath = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", mypath);
fprintf(stderr, " %s FIFO\n", mypath);
fprintf(stderr, "\n");
fprintf(stderr, "This program tests opening and writing a short message to the FIFO.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
rfd = open(argv[1], O_RDONLY | O_NONBLOCK);
if (rfd == -1) {
fprintf(stderr, "%s: Cannot open FIFO for reading: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
wfd = open(argv[1], O_WRONLY | O_NONBLOCK);
if (wfd == -1) {
fprintf(stderr, "%s: Cannot open FIFO for writing: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
ssize_t n = write(wfd, msg, msg_len);
if (n == -1) {
fprintf(stderr, "%s: Cannot write to FIFO: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
} else
if (n != (ssize_t)msg_len) {
fprintf(stderr, "%s: Wrote only %zd of %zu bytes to the FIFO.\n", argv[1], n, msg_len);
return EXIT_FAILURE;
} else {
printf("Success!\n");
}
close(wfd);
close(rfd);
return EXIT_SUCCESS;
}
compiles (gcc -Wall -Wextra -O2 source.c -o binary) cleanly, and implements the shown code (but with error checking, and using a command line parameter for the name of the FIFO to be opened). It does not verify the named file is a FIFO, though. If run on a non-existent FIFO, it complains "FIFO: Cannot open FIFO for reading: No such file or directory."
If you create the FIFO (mkfifo test-fifo) and run the test program, there are no errors; the only output is "Success!".
If you create the FIFO, and repeatedly write data to it but never read from it, at some point the kernel buffer for the FIFO becomes full, and running the test program will report "test-fifo: Cannot write to FIFO: Resource temporarily unavailable." (which means that write() returned -1 with errno==EWOULDBLOCK or errno==EAGAIN).
You can simulate this by running e.g. bash -c 'exec 4<>test-fifo ; dd if=/dev/zero of=test-fifo bs=1 oflag=nonblock status=progress', which opens the test-fifo FIFO read-write (which in Linux always succeeds) to descriptor 4, then runs dd to fill it with zeroes, using Bash (sub-)shell. On my system, a FIFO can hold 65536 bytes (64k). After filling the FIFO like this, running the test program (./binary test-fifo) will fail as described.
Hopefully, you'll see the light and the importance and usefulness of error checking. It is NOT something you should consider as "I'll add them in later when/if I have time"; they are also an important development tool.

open()'s "mode" argument won't set the correct permissions for the file

My intention was to open two files, where the second one would be brand new, with the same permissions as the first file. So to test my code I changed the first file permissions to "777". Then I proceeded to run my program. And to my surprise, the permission of the newborn file2 were wrong! They where set to 755. Even weirder is when I set the first file to "111" and tried again, the result now was "1204".
Can someone explain to me this weird behavior?
Here's my code
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *args[]) {
struct stat stats;
int fd1, fd2;
fd1 = open("testfile.txt", O_RDONLY);
/* Error check*/
if (fd1 == -1) {
/* Error handling */
perror("Opening");
printf("Unable to open file: %s\n", "testfile.txt");
printf("ERROR: %s\n", strerror(errno));
return 1;
}
if(fstat(fd1, &stats) == -1)
{
printf("Error while getting stats: %s\n", strerror(errno));
exit(-1);
}
//Receives the output file as a main argument . . .
if (argc > 1)
{
//(stats.st_mode = Gets the mask of the first file)
fd2 = open(args[1], O_WRONLY|O_CREAT, stats.st_mode);
/* Error check*/
if (fd2 == -1) {
/* Error handling */
perror("Opening");
printf("Unable to open file: %s\n",args[1]);
printf("ERROR: %s\n", strerror(errno));
return 1;
}
}
//. . . if it doesn't it creates a standard one warning you about it
else
{
fd2 = open("Nope.txt", O_WRONLY|O_CREAT, stats.st_mode);
/* Error check*/
if (fd2 == -1) {
/* Error handling */
perror("Opening");
printf("Unable to open file: %s\n",args[1]);
printf("ERROR: %s\n", strerror(errno));
return 1;
}
printf("Standard file created\n");
}
close(fd1);
close(fd2);
return 0;
}
I tried to make it as tidy as I could :)
From open(2) man page on the part about O_CREATE:
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).
If you type umask in bash you can see what value is used and which bits get cleared from the mode you provide.

Ubuntu C code how to execute command in another folder

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
char *buffer = malloc(1024);
FILE* f = popen("ls", "r");
int byteReads = fread(buffer, 1, 1024, f);
fclose(f);
printf("%s\n", buffer);
return 0;
}
Ther are 2 folders ./parent/folder1 and ./parent/folder2 . ./main is in folder1, and I want it to execute ls in folder2 and get the result.
Not something that is specific to "ls", but that works for any commands in general.
How can I do it using popen()? Or do I have to use another command?
You could use popen as you use it in your question. You could pass the cmd you want to execute as argument to the program call, e.g. if your program is named myexecute the command line call would look like this:
myexecute folder2 'ls -l'
or
myexecute 'another folder' 'ls'
Please note the single quotes to get an argument if spaces are included in the argument.
With chdir you can change the current working directory. The output of the command can be read in a loop and output to the stdout.
Finally, with pclose, you would wait for the associated process to finish and it would even return the exit status of the executed command, just in case you are interested.
You code slightly modified could look like this:
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if(argc != 3) {
fprintf(stderr, "usage: %s '<dir>' '<cmd>'\n", basename(argv[0]));
return EXIT_FAILURE;
}
if(chdir(argv[1]) != 0) {
perror(argv[1]);
return EXIT_FAILURE;
}
FILE *f = popen(argv[2], "r");
if (!f) {
perror("popen failed");
return EXIT_FAILURE;
}
char buf[1024];
while (fgets(buf, sizeof(buf), f)) {
printf("%s", buf);
}
if (pclose(f) != 0) {
perror("pclose failed");
}
return EXIT_SUCCESS;
}

C copying files using only system calls, infinite runtime

So I am working on a simple program in C but have been stuck on the copying portion. The program takes two filenames on the command line as arguments and copies the first to the second by using system calls. If the second file exists it asks the user if they want to overwrite, if not it creates it. However, my program when the user choices overwrite goes on infinitely.
Here is my code:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd1, fd2;
char buffer[1024];
long int n;
char c;
int num;
if (argc != 3) {
printf("%d\n",argc);
printf("Error, you need to give 2 arguments. Such that [File to copy] [File to create].\n");
exit(1);
}
if (access(argv[1], F_OK) < 0) {
printf("File %s either does not exist or cannot be accessed.\n", argv[1]);
exit(1);
} else {
printf("file %s exists\n", argv[1]);
}
if (access(argv[2], F_OK) < 0) {
printf("File %s does not exist, but one will be created.\n", argv[1]);
fd2=open(argv[2],O_CREAT|O_WRONLY|O_TRUNC, 0700);
} else {
printf("file %s exists\n", argv[2]);
printf("Would you like to overwrite %s? (Type 1 for yes or 0 for no)\n", argv[2]);
scanf("%d%c", &num, &c); // use c to capture \n
if (num == 1) {
fd2=open(argv[2],O_CREAT|O_WRONLY|O_TRUNC, 0700);
} else {
if (num == 0) {
printf("Ok, the file will not be copied and the program will now exit.\n");
exit(1);
} else {
printf("I do not recognize this response, program will now be terminated.\n");
}
}
}
printf("step\n");
while ((n1 = read(fd1, buffer, 1024)) > 0) {
printf("step\n");
if(write(fd2, buffer, n1) != n1){
printf("step\n");
perror("Error writing file.");
printf("step\n");
exit(3);
}
printf("stepss\n");
}
close(fd1);
close(fd2);
}
The printf("step") is for debugging, but it only prints one. Meaning the program freezes up by the while loop. I can use stat(), open(), read(), write(), close(), and access(). Any ideas on what is wrong or how it can be done better would be appreciated!
Any ideas on what is wrong
Your fd1 never been assigned, so read(fd1, ...) returns an error.
Check return value of read and printf("%m\n") will print the details.
$ ./a.out a b
file a exists
file b exists
Would you like to overwrite b? (Type 1 for yes or 0 for no)
1
step
Bad file descriptor

dup2() is preventing output

my code is pasted below.
I'm trying to use dup2 to redirect my output to file.
if I use it to redirect it works fine (if I remove the comments), output in file and not on stdout. ex: ls > test , results in ls outputting to test.
the problem is that ls, without the > doesn't output anything. If I leave the comments ls outputs just as it should, albeit with no ability to redirect.
redirect[0] is either < or > or nothing
redirect[1] is the path for the file to redirect to
command is is an array of cstrings with the pices of the command commands is as well
example output
with code commented
xxxxx#myshell:/home/majors/kingacev/ubuntumap/cop4610/proj1> ls
a.out myshell.c myshell.c~
xxxxx#myshell:/home/majors/kingacev/ubuntumap/cop4610/proj1>
with code uncommented
xxxxx#myshell:/home/majors/kingacev/ubuntumap/cop4610/proj1> ls
xxxxx#myshell:/home/majors/kingacev/ubuntumap/cop4610/proj1>
/*
if (!strcmp(redirect[0],">")){
if ((fd = open(redirect[1], O_RDWR | O_CREAT)) != -1)
dup2(fd, STDOUT_FILENO);
close(fd);
}
*/
if (command[0][0] == '/'){
int c = execv(command[0], commands);
if (c != 0){
printf("ERROR: command does not exist at path specified\n");
exit(0);
}
}
else if (!execv(path, commands)){
exit(0);
}
This code works, redirecting to file.out:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int fd;
char *redirect[] = { ">", "file.out" };
char *command[] = { "/bin/ls", "-l", 0 };
if (!strcmp(redirect[0], ">"))
{
if ((fd = open(redirect[1], O_WRONLY | O_CREAT, 0644)) != -1)
{
fprintf(stderr, "Dupping stdout to %s\n", redirect[1]);
dup2(fd, STDOUT_FILENO);
close(fd);
}
}
if (command[0][0] == '/')
{
execv(command[0], command);
fprintf(stderr, "ERROR: command %s does not exist at path specified\n", command[0]);
return(1);
}
else
{
fprintf(stderr, "ERROR: not handling relative names like %s\n", command[0]);
return(1);
}
return 0;
}
This code works too, not redirecting to file:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int fd;
char *redirect[] = { "<", "file.in" };
char *command[] = { "/bin/ls", "-l", 0 };
if (!strcmp(redirect[0], ">"))
{
if ((fd = open(redirect[1], O_WRONLY | O_CREAT, 0644)) != -1)
{
fprintf(stderr, "Dupping stdout to %s\n", redirect[1]);
dup2(fd, STDOUT_FILENO);
close(fd);
}
}
if (command[0][0] == '/')
{
execv(command[0], command);
fprintf(stderr, "ERROR: command %s does not exist at path specified\n", command[0]);
return(1);
}
else
{
fprintf(stderr, "ERROR: not handling relative names like %s\n", command[0]);
return(1);
}
return 0;
}
Note that it sets up the command array and uses execv(command[0], command); — this is the recommended way of doing business. Your code appears to have a variable commands with presumably the arguments to the program; you also appear to have a variable path with presumably the path name of the program. Since we can't see what's in those, it is hard to know what they contain and where there might be problems. Note the explicit null pointer (0) at the end of the command array. That is crucial. Note too that the error messages identify what was failing. There are few things more frustrating than a program that says "it went wrong" without identifying what 'it' is.

Resources