This function fails to open the file. Are my parameters wrong or what could be causing this problem?
int CreateFile(const char *filename){
char filepath[strlen(filename) + 3];
sprintf(filepath, "./%s", filename);
int fd = open(filepath, O_CREAT, O_APPEND, S_IWGRP);
if(fd == -1) printf("file read failed\n");
return fd;
}
Xcode prints only "file read failed" to the console. I tried to run this via Terminal aswell but that didn't help either.
I fixed an issue pointed by NetMage:
int CreateFile(const char *filename){
char filepath[strlen(filename) + 3];
sprintf(filepath, "./%s", filename);
int fd = open(filepath, O_CREAT|O_APPEND, S_IWGRP);
if(fd == -1) printf("file read failed\n");
return fd;
}
Unfortunately that didn't fix the issue
Step 1 - Verify that filepath is being set correctly, either by printing it to the terminal or examining it in a debugger.
Step 2 - Verify that the file exists in that path, and that its permissions are set so that you can open it. If filepath is "./foo", then a file named foo had better exist in the current working directory (the directory from which you ran the program), and it needs to have at least read permission.
Step 3 - If the file does not exist, verify that you have permission to create new files in the current working directory.
Step 4 - If after doing all of that you still get an error, check errno. It will give you some additional information beyond "it didn't work."
#include <errno.h>
...
if(fd == -1)
{
switch( errno )
{
case EACCESS: // permission issues
handle_permission_issue();
break;
case EEXIST: // file already exists and you used O_CREAT and O_EXCL
handle_already_exists_issue();
break;
case EFAULT: // bad path
handle_bad_path_issue()
break;
...
}
printf("file read failed\n");
}
NetMage has pointed out one problem - your flags need to be bitwise-OR'd together, rather than listed as separate arguments. Surprised the compiler didn't yell at you over that.
The open function takes only one parameter for oflags, which must be bit-ored together:
#include <errno.h>
#include <string.h>
int fd = open(filepath, O_CREAT|O_APPEND, S_IWGRP);
if (fd == -1) printf("file read failed: %s\n", strerror(errno));
Per the POSIX documentation for open() (somewhat reformatted, and note the bolded text):
SYNOPSIS
#include <sys/stat.h> #include <fcntl.h>
int open(const char *path, int oflag, ...);
...
Values for oflag are constructed by a bitwise-inclusive OR of flags
from the following list, defined in . Applications shall
specify exactly one of the first five values (file access modes)
below in the value of oflag:
O_EXEC
Open for execute only (non-directory files). The result is unspecified if this flag is applied to a directory.
O_RDONLY
Open for reading only.
O_RDWR
Open for reading and writing. The result is undefined if this flag is applied to a FIFO.
O_SEARCH
Open directory for search only. The result is unspecified if this flag is applied to a non-directory file.
O_WRONLY
Open for writing only.
...
You need to include at least one of those five flags, perhaps like:
int fd = open(filepath, O_WRONLY|O_CREAT|O_APPEND, S_IWGRP);
Note that other failures may still occur. As noted in the comments, you're prepending "./" to the file name, which may cause problems if, for example, you get passed "/tmp/filename" and the tmp directory doesn't exist in your current working directory, as open() will not create missing directories in any path.
Related
I can't solve a problem with standard output laugh, I'm on Unix operating system, so the file-descriptor of STD_IN = 0, STD_OUT = 1, STD_ERR = 2, basically I tried to close the descriptor associated with the standard output, and then overwrite it with that of the file I want to write to, but when I open the file it is empty.
CODE:
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
/*special file's descriptor --use it or constants in unistd.h-- */
#define STD_IN 0 /*standard input stream*/
#define STD_OUT 1 /*standard output stream*/
#define STD_ERR 2 /*standard output-error stream*/
int main(unsigned int num_of_args, char** args)
{
if(num_of_args != 3)
{
write(STD_ERR, "Few argouments.\nThe use = ./executable <message> <file>\n", 69);
return -1;
}
int file_des= open(args[2], O_CREAT, 0640);
if(file_des < 0)
{
write(STD_ERR, "Error, we couldn't open file.\n", 31);
return -1;
}
//I close the descriptor associated with STD_OUT
close(STD_OUT);
//I copy the descriptor associated with fil_des on the first available descriptor(so STD_OUT)
dup(file_des);
write(STD_OUT, args[1], sizeof(args[1]));
close(file_des);
return 0;
}
It should be fine, I don't see any errors, but when I open the file I don't find the message written.
Because? help me
This code is incorrect:
int file_des= open(args[2], O_CREAT, 0640);
Per the POSIX documentation for open() (note the bolded portion - my bolding):
SYNOPSIS
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path, int oflag, ...);
int openat(int fd, const char *path, int oflag, ...);
DESCRIPTION ...
Values for oflag are constructed by a bitwise-inclusive OR of flags
from the following list, defined in <fcntl.h>. Applications shall
specify exactly one of the first five values (file access modes) below
in the value of oflag:
O_EXEC
Open for execute only (non-directory files). The result is unspecified if this flag is applied to a directory.
O_RDONLY
Open for reading only.
O_RDWR
Open for reading and writing. The result is undefined if this flag is applied to a FIFO.
O_SEARCH
Open directory for search only. The result is unspecified if this flag is applied to a non-directory file.
O_WRONLY
Open for writing only.
Your open() call needs to include either O_WRONLY or O_RDWR as you are writing to the file:
int file_des= open(args[2], O_CREAT | O_WRONLY, 0640);
The Problem
The problem here is that the file descriptor which is being assigned to file_des isn't really 1.
int test = dup(file_des);
fprintf(stderr, "%d", test);
3
Now, what could the reason be?
The problem is with how you are opening your file. O_CREAT creates a file if it does not exist.
There is no specification whether you are opening the file for read, write, or read/write.
Since you are associating your file to the file descriptor of stdout, you must use the O_WRONLY flag as well.
int file_des = open(args[2], O_CREAT | O_WRONLY, 0640);
As it seems, it is possible to use openat() to reopen an already opened directory. For instance on my Linux system I can do the following:
#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
int fd1 = open(".", O_PATH);
if (fd1 == -1) {
perror("open");
return 1;
}
int fd2 = openat(fd1, ".", O_RDONLY);
if (fd2 == -1) {
perror("openat");
close(fd1);
return 1;
}
close(fd1);
// do fancy things with fd2, now opened
// with access mode read-only
return 0;
}
I could not find this documented anywhere and it feels a bit like an edge case. Also I didn't find other code doing this. Is this well-defined behavior?
EDIT: changed the title: file -> directory
This is just the same as calling open twice on the same file, which you are allowed to do:
int fd1 = open("filename", flags1);
int fd2 = open("filename", flags2);
where filename refers to an existing file (of any type) and flags1 and flags2 are any set of O_ flags that can be validly applied to that type of file and won't destroy its contents. (In particular, we assume that they do not include O_CREAT, O_TRUNC, or O_EXCL.)
fd1 and fd2 will refer to separate "open file descriptions", so for instance lseek on one will not affect the other, flock on one will block flock on the other, etc.
With openat(), the first argument, fd, should be the file descriptor for a directory — such as the one you obtained from opening "." — or the special value AT_FDCWD (which means open relative paths relative the current directory). Note that the O_PATH option you use is a Linux-only extension to openat().
So, because you're using a valid file descriptor for a directory, the call to openat() should succeed. You now have two file descriptors both pointing (independently — with separate open file descriptions) to the current directory. In general, it is possible to open the same file multiple times in a single process (or in multiple processes — ensuring access by a single process is actually very hard on Unix-like (POSIX) systems).
There isn't a lot else you can do with those descriptors other than use them in *at() system calls. Either of the file descriptors would have been sufficient; opening both was overkill.
I have an open file descriptor which I want to duplicate in order to perform reading and seeking through both of them independently. I looked at the
int dup(int old_fd)
syscall. The problem is it does not really fit here. Man page states the following
http://man7.org/linux/man-pages/man2/dup.2.html :
After a successful return, the old and new file descriptors
may be used interchangeably. They refer to the same
open file description (see open(2)) and thus share file
offset and file status flags; for example, if the file offset is
modified by using lseek(2) on one of the file descriptors,
the offset is also changed for the other.
Is there a way to duplicate a file descriptor so they are completely independent?
In Linux, opening /proc/<pid>/fd/<n> opens the file that's currently open at fd N, but this is a new copy, not a linked duplicate like the one you get with dup() and friends.
This should create a file that contains bar, a bunch of zero bytes, then foo. Contrast with the version using dup().
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(void)
{
int fd1, fd2;
char buffer[50];
fd1 = open("testfile", O_CREAT | O_TRUNC | O_RDWR, 0600);
sprintf(buffer, "/proc/self/fd/%d", fd1);
#ifndef USE_DUP
fd2 = open(buffer, O_RDWR);
if (fd2 == -1) {
perror("open");
}
#else
fd2 = dup(fd1);
#endif
if (lseek(fd1, 16, SEEK_SET) == -1) {
perror("lseek");
}
if (write(fd1, "foo", 3) == -1) {
perror("write(fd1)");
}
if (write(fd2, "bar", 3) == -1) {
perror("write(fd2)");
}
}
No — at least, not in POSIX-defined mechanisms.
If you want complete independence of the file descriptors, you need to avoid the shared open file description, which means an independent open() or equivalent.
There's a chance that there's a Linux-specific mechanism that does the job that I've not heard of. However, looking through the system calls for Linux at http://man7.org/linux/man-pages/man2/ didn't provide enlightenment.
I just discovered that a FILE* can not only refer to a regular file, but also to a directory. If the latter is the case, fread will fail with errno set to 21 (Is a directory).
Minimal repro can be tested here
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
int main() {
char const* sz = ".";
int fd = open(sz, O_RDONLY | O_NOFOLLOW); // all cleanup omitted for brevity
FILE* file = fdopen(fd, "rb");
// I would like to test in this line if it is a directory
char buffer[21];
int const n = fread(buffer, 1, 20, file);
if (0 < n) {
buffer[n] = 0;
printf(buffer);
} else {
printf("Error %d", errno); // 21 = Is a directory
}
}
What is the proper way to detect early that my FILE* is referring to directory without trying to read from it?
EDIT to repel the duplicate flags:
I want to test on the FILE*, not the filename. Testing on filename only and then opening it later is a race condition.
Assuming a POSIX-like environment, if you have just the file stream (FILE *fp), then you are probably reduced to using fileno() and fstat():
#include <sys/stat.h>
struct stat sb;
if (fstat(fileno(fp), &sb) != 0)
…oops…
if (S_ISDIR(sb.st_mode))
…it is a directory…
else
…it is not a directory…
Assuming you are on a POSIX-based system, use stat() (if you wish to use the filename in sz before the call to open()) or fstat() (if you wish to use the descriptor fd after calling open()) to get a file status structure from the OS. The member of the structure named st_mode can be used with the POSIX API S_ISDIR(st_mode) to see if the file is a directory.
For more information, see: http://man7.org/linux/man-pages/man2/stat.2.html
Checking The fcntl.h man page:
header shall define the following symbolic constants as
file creation flags for use in the oflag value to open() and
openat(). The values shall be bitwise-distinct and shall be suitable
for use in #if preprocessing directives.
And the flag :
O_DIRECTORY Fail if not a directory.
My following program, which copies a file, won't allow me to copy files because of "permission denied". However, I gave it permissions.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int fdinput, fdoutput; //file pointers
char arrbuf[5000]; //size of what can be read in file
ssize_t bytesR, bytesW;//number of what input returns
mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH |S_IXOTH ;
fdinput = open(argv[1], O_RDONLY); //pointing to read file
fdoutput = open(argv[2], O_WRONLY);//pointing to write file
if(fdinput == -1){
perror("the source file cant be opened");
return 1;
}
if(fdoutput == -1){
perror("the written file cant be opened");
return 2;
}
while((bytesR = read(fdinput, arrbuf, sizeof arrbuf)) > 0){
bytesW = write(fdoutput, arrbuf, (ssize_t) bytesR);
}
close(fdinput);
close(fdoutput);
return 0;
}
The problem is in the call to the system call open() for the destination file (i.e.: the file to be created as a result of the copy):
fdoutput = open(argv[2], O_WRONLY);
Making possible the creation of the destination file
First, the call above to open() opens the file with the given name by argv[2], only if it already exits. Otherwise, the system call fails (errno is set to ENOENT) and perror() produces:
the written file cant be opened: No such file or directory
In order to create the file if it does not exist yet, the O_CREAT flag has to ORed together with O_WRONLY.
Truncating an already existing destination file
If the destination file already exist you surely want to truncate the length of that already existing file to zero at the moment of open()ing. That can by achieved by ORing the O_TRUNC flag together with the other flags.
Providing the permissions for the file to be created
Let's look at the open() system call's prototype:
int open(const char *path, int oflag, ...);
The ... at the end is to specify a kind of optional argument. That argument is used by open() only when a new file is being created. It provides the mode bits to be applied for the file to be created. This is not exactly the permissions for the file to be created, but it is strongly related to them (for more info see: file mode creation mask).
You created mode of type mode_t but just forgot to pass it to open().
With all exposed above in mind, your call should look like:
fdoutput = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, mode);