C check if file exists - c

In a project I have to do in C89 standard I have to check if a file exists.
How do I do this?
I thought of using
FILE *file;
if ((file = fopen(fname, "r")) == NULL)
{
printf("file doesn't exists");
}
return 0;
but I think there can be more cases then file doesn't exists that will do fopen == NULL.
How do I do this? I prefer not using includes rather then .

If you can't use stat() in your environment (which is definitely the better approach), just evaluate errno. Don't forget to include errno.h.
FILE *file;
if ((file = fopen(fname, "r")) == NULL) {
if (errno == ENOENT) {
printf("File doesn't exist");
} else {
// Check for other errors too, like EACCES and EISDIR
printf("Some other error occured");
}
} else {
fclose(file);
}
return 0;
Edit: forgot to wrap fclose into a else

It's impossible to check existence for certain in pure ISO standard C.
There's no really good portable way to determine whether a named file
exists; you'll probably have to resort to system-specific methods.

This isn't a portable thing, so I'll give you OS-specific calls.
In Windows you use GetFileAttributes and check for a -1 return (INVALID_HANDLE or something like that).
In Linux, you have fstat to do this.
Most of the time however, I just do the file opening trick to test, or just go ahead and use the file and check for exceptions (C++/C#).

I guess this has more to do with system environment (such as POSIX or BSD) than with which version of C language you're using.
In POSIX there is a stat() syscall that will give you information about a file, even if you cannot read it. However, if the file is in path that you have no access permissions to it's always going to fail regardless of whether the file exists.
If you have no access to the path then it should never be possible to look on files contained.

Do you really want to access the file? A check is usually better with the
access(filename,F_OK)==0 from <unistd.h> and is a pretty wide standard, I think.

Related

Is there a reason why C doesn't let me write to this file?

I am new to C, and I am trying to get the hang of file handling. I have tried writing to this file but its not working, and I am not sure why it doesn't work, could someone help me?
#include <stdio.h>
#include <stdlib.h>
int main(){
FILE *f;
f = fopen("out.txt","w+");
if (f == NULL) {
printf("ERROR");
exit(-1);
}
char test[] = "HELLO";
fprintf (f, "%s", test);
fclose(f);
return 0;
}
I keep getting the "ERROR" message when I check if the file has been opened :/
Replace rather useless
printf("ERROR");
with
perror("fopen");
C doesn't require fopen to set errno (which is used by perror), but POSIX does, and the usual compilers for Linux and Windows set it.
The error is almost surely going to be EPERM ("Permission denied"). That error means one of the following:
you don't have permission to access that directory,
you don't have permission to write to that directory (if the file doesn't exist),
you don't have permission to read that directory (if the file exists),
you don't have permission to modify the file (if the file exists), or
the file is locked by another program (if the file exists).
Well, other causes are possible, but so unlikely I don't know what they would be.
Your program attempts to create or modify a file in the current directory. Note that the current directory is not necessarily the directory in which the executable is located. You're probably trying to write to the wrong directory accidentally.

How to replace the use of tmpnam() in the following code snippet

I fully understand that tmpnam has been deprecated and would like to remove it from a function in an existing file that prevents me from building the project. However, since I am not familiar with it and am unable to experiment with it, I am not sure how best to replicate this functionality.
if ((myfileName = tmpnam(NULL)) == NULL) { return APP_ERROR }
I read the information on tmpnam here but the best I can come up with is to use something like:
if (tmpnam_r == NULL) { return APP_ERROR }
However, since I cannot compile with tmpnam and am unfamiliar with the code in question, I am not confident in properly capturing the original intent.
As best as I can tell, this appears to be testing if the file exists, and if not, simply returns an error, as the next step consists of copying content into myfileName, which should presumably exist following the above check.
The problem with tmpnam() is that it generates a name that is unique and does not exist when it returns, but that name is not guaranteed to be unique by the time you use it in a call to fopen() (or open()).
The key feature of the mkstemp() function is that it creates and opens a file with the new name, so that there isn't a TOCTOU (time of check, time of use) vulnerability. This cuts down the avenues for security risks.
Code designed to use tmpnam() usually needs a file name, so using tmpfile() is usually not an option; it doesn't provide a way to find the file name. If you don't need the file name then using tmpfile() works well and is Standard C, so it is widely available.
The specific case of tmpnam() and tmpnam_s() is interesting. Although tmpnam_s() avoids some string-related problems, it does not change the behaviour of tmpnam() in the way that causes the security problems addressed by mkstemp(). So, independent of the portability issues that arise from attempting to use tmpnam_s() (or any of the other *_s() functions from Annex K of the C11 or C18 standards), it doesn't fix the problem that causes tmpnam() to be deprecated.
You can arrange to use mkstemp() instead of tmpnam() and close the file descriptor before continuing with the other code:
tmpnam(name); // Replace this
int fd = mkstemp(name); // With this…
if (fd >= 0)
close(fd);
It's not great, but it does ensure the file is created, which reduces the security vulnerabilities a bit, but not as much as using the file descriptor directly. You could (should) wrap that into a function.
Note that the mkstemp() returns a file descriptor; if you want a file stream, you can use fdopen() to create a file stream from the file descriptor. And if that fails, you probably want to remove the file (with remove() or unlink()).
So, that gives you a need for fmkstemp():
#include <stdio.h>
#include <stdlib.h> /* mkstemp() */
#include <unistd.h> /* close() */
extern FILE *fmkstemp(char *name); /* Add to a convenient header file */
FILE *fmkstemp(char *name)
{
int fd = mkstemp(name);
FILE *fp = 0;
if (fd >= 0)
{
fp = fdopen(fd, "w+");
if (fp == 0)
{
close(fd);
unlink(name);
}
}
return(fp);
}
Note that after you've used fmkstemp(), you use fclose() to close the file stream (and, behind the scenes, that closes the file descriptor).
Don't forget to remove the temporary file before exit. That's where a function registered with atexit() or one of its variants can be useful.

getcwd error when $HOME

I wrote my own find() function. When I do:
./myown $HOME/Documents test.txt
I get:
/Users/CJ/Documents/test/test.txt
/Users/CJ/Documents/test/test1/test.txt
/Users/CJ/Documents/test/test2/test.txt
However when I do:
./myown $HOME test.txt
I get:
getcwd error
My code for getcwd is here and it is in a helper function:
findit(*directory, *pattern){
...
if(getcwd(cwd, 2048+1) == NULL){
fprintf(stderr, "getcwd error\n");
exit(1);
}
...
}
How can I solve this issue?
EDIT: closedir() solve this issue but there is another issue now. Result is the same when I do : ./myown $HOME/Documents test.txt but when I do the other way I get : stat error
`
struct stat mode;
if(stat(entry->d_name, &mode) == -1){
fprintf(stderr, "stat error\n");
continue;
}`
I didn't use stat error anywhere else in the code.
This can be helpful too, this is how I used open
DIR *dir
dir = opendir(".");
The error is in readdir().
One suggested step in debugging was:
Since getcwd() sets errno when it fails, you should probably report errno, maybe with perror("getcwd"). Although I'm not keen on perror(), it is probably simplest here.
It turns out that the error set was EMFILE Too many open files.
So, now you know what the trouble is. The getcwd() is failing because you have opened a lot of files and not closed enough of them, and it needs some available file descriptors but you've not left it any that it can use.
And, when requested, I elaborated on that with:
You've opened files and/or directories (opening a directory with opendir() usually uses a file descriptor), and you've not closed them. Consequently, the system won't allow you to open any more files — and the getcwd() fails. It probably isn't immediate; your program has probably done some processing before that failure.
The OP observed:
I just saw that I haven't used fclose; give me a second and I will check it if that's it.
Making sure you've used fclose() and closedir() — and plain close() if you've used any file descriptors by calling open() directly — should help. If, however, the call to getcwd() is the very first thing your code is doing, it won't be because you've opened many files (you haven't).
If there are still problems after files have been closed, then you need to take a step back and review the larger context.
For example:
Why is stat() failing after readdir()?
stat() error 'No such file or directory' when file name is returned by readdir()

POSIX ways for generation of a temporary file with a nice file name

I want to generate a temporary file with a "nice" name like
my-app-Mar27-120357-Qf3K0a.html
while following the best practices for security.
POSIX offers me mkstemp(3) which takes a filename template (typically something like /tmp/my-app-XXXXXX) but it has two problems:
I need to choose the output directory myself. When I see glibc tempnam(3) (which is deprecated for a security reason) considers many factors, I wish to let the library function choose it.
There's no extension in the file name
The second item can be addressed by mkstemps(3) which takes a number of characters to keep as a user-defined extension. In my case, I can pass my-app-Mar27-120357-XXXXXX.html and 5
but it has its own problems:
I still need to choose the output directory
It isn't perfectly portable. NetBSD seems to lack it.
So I'm considering to use the deprecated tempnam(3) to generate a filename with the output directory path, overwrite the filename part with X and feed it to mkstemp(3), and then rename the file to my preferred format. So the problem lies in the last step, renaming without overwrite; is it possible in POSIX?
Or could there be any better alternatives?
Let mkstemp make the file it wants to make, in the POSIX-compliant way that it wants to. Use symlink to make a symbolic link from a source file and path of your choice to a destination that matches whatever comes from using mkstemp. Remove the symbolic link when you're done.
Another approach is to simply munge the template and add your path. We describe such a function in the BEDOPS toolkit here, used by the sort-bed application to allow the end user to specify where temporary intermediate files are stored: https://github.com/bedops/bedops/blob/6da835468565dfc30a3fcb65807e91fcf133ea2b/applications/bed/sort-bed/src/SortDetails.cpp#L115
FILE *
createTmpFile(char const* path, char** fileName)
{
FILE* fp;
int fd;
char* tmpl;
if (path == NULL)
{
fileName = NULL;
return tmpfile();
}
tmpl = static_cast<char*>( malloc(1 + strlen(path) + L_tmpnam) );
strcpy(tmpl, path);
strcpy(tmpl+strlen(path), "/sb.XXXXXX");
fd = mkstemp(tmpl);
if(fd == -1)
{
fprintf(stderr, "unable to create temp file!\n");
return NULL;
}
fp = fdopen(fd, "wb+");
*fileName = static_cast<char*>( malloc(strlen(tmpl) + 1) );
strcpy(*fileName, tmpl);
free(tmpl);
return fp;
}
This uses the L_tmpnam macro, part of the stdio library, to set the number of characters that the variable tmpl (the filename, ultimately) can store.
This compiles and works under Linux and OS X (BSD) hosts and also uses POSIX routines.
It is more complex than my other solution but it might work better for your use case.

Save file with C fopen

I did a program in C but it does not allow to save on c:\SomeDirectory\afile.txt
I'm using this:
FILE* m_hFile = fopen("c:\\SomeDirectory\\afile.txt", "a+t");
fprintf(m_hFile, "testing");
fclose(m_hFile);
Why that? Is there a defined folder I can save in?
SomeDirectory is previously created.
I'm using Windows 7 OS.
If fopen encounters an error, it sets the errno variable indicating what error occurred. You can test this, or even simpler, use perror to print out an error message that will tell you what went wrong:
FILE* m_hFile = fopen("c:\\SomeDirectory\\afile.txt", "a+t");
if (m_hFile == NULL) {
perror("fopen");
}
It sounds like perhaps "SomeDirectory" doesn't exist. You can create folders with C++ but you'll want to check if one's already there. Just calling the open command doesn't automagically create the folder. :)

Resources