This question already has answers here:
What's the best way to check if a file exists in C?
(8 answers)
Closed 5 years ago.
The way I'm using just involves trying to fopen() the file to be checked,
/* --- does file exist??? --- */
char fname[999] = "whatever"; /* constructed during execution */
FILE *fp = NULL; /* try to fopen(fname,"r") */
int isfilefound = 0; /* set true if fopen() succeeds */
if ( (fp = fopen(fname,"r")) /* try to fopen() for read */
!= NULL ) { /* succeeded */
isfilefound = 1; /* set file found flag */
fclose(fp); } /* and just close the file */
Is there a quicker, less resource-intensive, way?... A specific way for unix/linux? A Windows way? And preferably, a portable posix-compliant way (as above presumably is)? It's being done lots (1000's) of times, so I'd prefer not to be unnecessarily opening and closing files for no good reason.
-----------------------------------------------------------------
Edit Okay, based on answers below, I put together the following little function intended to check whether or not file (already:) exists in a posix,windows,other portable way...
/* ==========================================================================
* Function: isfilexists ( path )
* Purpose: check whether file at path exists
* --------------------------------------------------------------------------
* Arguments: path (I) pointer to null-terminated char string
* containing "path/filename.ext" of
* file whose existence is to be determined
* (path is relative to pwd unless explicitly
* absolute by initial '/' or other syntax)
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if file at path exists, or 0 if not
* --------------------------------------------------------------------------
* Notes: o conditional compiles for various systems,
* depending on whether POSIX or WINDOWS is #define'ed...
* o ...method used:
* 1: use access() on Posix systems,
* 2: PathFileExists() on Windows systems,
* 3: fopen() on any other systems.
* ======================================================================= */
/* --- entry point --- */
int isfilexists ( char *path )
{
/* ---
* allocations and declarations
* ------------------------------- */
int isexists = 0; /* set true if file at path exists */
FILE *fp = NULL; /* fopen() for non-posix,windows */
#define POSIX /* just for testing */
/* ---
* determine whether file at path already exists
* ------------------------------------------------ */
#if defined(POSIX) /* posix-compliant system... */
#include <unistd.h>
if ( access(path,F_OK) == 0 ) /* file at path exists */
isexists = 1; /* so set file exists flag */
#else
#if defined(WINDOWS) /* Windows system... */
isexists = PathFileExists(path); /* set flag if file at path exists */
#else
/* --- fopen() for any other non-posix, non-windows system --- */
if ( (fp = fopen(path,"r")) /* try to fopen() for read */
!= NULL ) { /* succeeded */
isexists = 1; /* set file exists flag */
fclose(fp); } /* and just close the file */
#endif
#endif
return ( isexists ); /* back to caller with 1 if file at path exists */
} /* --- end-of-function isfilexists() --- */
The access() and fopen() methods tested and work okay. Unable to test PathFileExists() for windows. And I still want to figure out what #define'ed symbols to automatically and unambiguously check for conditional compiles.
You are thinking about the problem the wrong way. You shouldn't ever "check whether a file already exists", because that has an inherent TOCTOU race — in between the time you check whether the file exists, and the time you act on that information, another process may come along and change whether the file exists, rendering the check invalid.
What you do instead depends on why you want to know. One very common case is that you only want to create the file if it doesn't already exist, in which case you use the lower-level open function in O_EXCL mode:
int fd = open("whatever", O_WRONLY|O_CREAT|O_EXCL, 0666);
if (fd == -1 && errno == EEXIST) {
/* the file already exists */
} else if (fd == -1) {
/* report that some other error happened */
} else {
FILE *fp = fdopen(fd, "w");
/* write data to fp here */
}
Another very common case is that you want to create the file if it doesn't exist, or append new data to the file if it does; this can be done with the "a" mode to fopen or O_APPEND flag to open.
On Windows, there is PathFileExists().
On a POSIX system, you have stat() or access().
That said, if you check for existence of the file because your code needs the file, this is the wrong approach -- file systems are out of your program's control, so this would be a race condition, the only correct way would be to properly handle errors when opening the file.
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
I am trying to create a program that using the fopen(); function.
My problem is that fopen(); cannot find the file.
when i am using perror("Error"); the output is Error: No such file or directory.
I have read this articles and they did`not solved my problem:
fopen() returning a NULL pointer, but the file definitely exists
Unable to open a file with fopen()
It means i have tried to use fopen(); with the exec path to the file, and i have checked that the filename is the specific filename that i am looking for.
I have also used exec("pwd"); to see that i am the correct directory.
My OS is ubuntu mate 20.04.
Here is my Code:
/* File Name: firstTransaction.c
* File Mission: scan the input files and make the first transaction.
*/
#include "defines.h"
/*
* first transaction file:
* Allocating new memory for the assembly source file.
* Scanning the assembly source file.
* #param filename - the filename of the file that needs to be converted by the program.
* #return output and files -
* output the assembly source file in a machine code.
* return extern, entry and object files.
*/
/* function prototype */
extern char * readText(FILE * ptr, long ic, long dc); /* the function defined on assistanceFunctions.c file */
int firstTransaction(int * cf, char **av)
{
FILE * fp; /* a pointer for fopen function */
char * filteredFile = NULL;
long ic = 100, dc = 0; /* declaration and initialization of the instruction counter and current data counter */
int i = 0, j = 0; /* indexes */
int cfh = 0; /* compatible file holder */
int fileNameLength = (int)strlen(av[i]); /* computing the first filename length to allocate memory */
char * fileName = (char*) calloc(fileNameLength,sizeof (char)); /* allocating memory for the first filename */
system("pwd");
while(cf[i] != 0) /* while there is more compatible files to open */
{
cfh = cf[i]; /* cfh - compatible file holder, cf - compatible file array, set the next compatible file to cfh */
strcpy(fileName,av[cfh]); /* copy the file name from the argv array */
fp = fopen("fileName","r"); /* open the first compatible file for read */
if(fp == NULL) /* if file does not exists */
{
perror("Error"); /* print the error - why the file not opened */
i++; /* increment i by one and try to open the next filename */
}else /* if the file was opened successfully, start scanning the file */
{
/*filteredFile = */ readText(fp, ic, dc); /* calling readText function with pointer to the start of the file that was opend by fopen function */
/* while(filteredFile[j] != EOF)
{
putchar(filteredFile[j]);
j++;
}
/* DONT FORGET TO CLOSE WITH FCLOSE(); */
}
}
return 0;
}
Here is some screenshots with the tested solutions that failed from the other articles:
Edited after NeonFire answers:
My first code used fp = fopen(fileName,"r"); but my error message was made manually this way: printf("error, %s file does not exists\n", fileName); /* print error message to the user */
this cause that i did`not find my real error.
then i changed to fp = fopen("fileName","r"); and used perror("Error"); instead of the first and right way.
after i read NeonFire answers i got a new error Error: Too many open files.
i removed the backups file from the directory and i still have the same error.
Edited after Ted Lyngmo comment:
i did had to close the file to solve the - "to many file to open error".
your fp = fopen("fileName","r"); is incorrect. Remove the quotes from fileName so it refers to your variable char * fileName , not a string "fileName" :)
I am wanting to pull from a plain-text file on the internet, and have it read line by line. Similar to how you read a file line by line with fgets(). I don't want to download the file. I know that if you use the read() function, you can specify how many bytes to receive and manually read the file line by line. I was just wondering if there was any way to do this automatically. Thanks for any help!
I have to do that a lot, and wrote the following little function to help. Just call FILE *wwwpopen(), *fp=wwwpopen("http://wherever.com/whatever.html","r"); and then use fp as usual to read whatever.com any way you like. When done, just pclose(fp) yourself. Here's the little function (note: if wget not on your default path, then replace wget[256] initialization below by the full path to wget on your system),
/* ==========================================================================
* Function: wwwpopen ( char *url, char *mode )
* Purpose: popen("wget -O - url",mode)
* --------------------------------------------------------------------------
* Arguments: url (I) char * containing null-terminated string
* with url to be opened
* mode (I) char * containing null-terminated string
* that should always be "r"
* (this arg ignored, always "r"; just there for
* consistency with fopen/popen calling sequence)
* Returns: ( FILE * ) file pointer to popen()'ed url file
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o pclose(fileptr) yourself when finished reading file
* ======================================================================= */
/* --- entry point --- */
FILE *wwwpopen ( char *url, char *mode ) {
/* --- Allocations and Declarations --- */
FILE *fileptr = NULL; /* file ptr returned to caller */
char defaultmode[16] = "r", /* default mode, always used */
wgetargs[16] = "-O -", /* command-line args for wget */
wget[256] = "wget", /* replace by path to wget, if any */
command[512]; /* constructed wget command */
/* --- Check input --- */
mode = defaultmode; /* force popen() mode, must be "r" */
if ( url != NULL ) { /* url supplied */
/* --- popen() file --- */
sprintf(command,"%s %s %s", /* construct wget args url */
wget, /* path to wget program */
wgetargs, /* command-line args for wget */
url); /* and url to be wgotten */
fileptr = popen(command,mode); } /* popen() url (mode better be "r")*/
return ( fileptr ); /* back to caller with file ptr */
} /* --- end-of-function wwwpopen() --- */
I have a school project. I have to write a basic virtual machine in C that is able to host a CoreWar game. I am supposed to read from a file written in binary, but I am not allowed to use fopen, fread or fseek.
I have to use read, write and lseek.
I really dont understand how I am supposed to do this, everything I found on internet says I have to use fopen with the "rb" mode.
Here's a complete example of reading the file using the low-level functions you are required to use.
Replace the comment /* Process the data */ with your own code that does something useful with the data you read.
int rfd; /* File descriptor. */
char buffer[BUFFER_SIZE]; /* Buffer to put file content into */
int bufferChars; /* number of characters returned by the read function */
/* Open the file */
if ((rfd = open(argv[1], O_RDONLY, 0)) < 0)
perror("Open failed.");
/* Read and process the file */
while (1)
{
/* Normal case --- some number of bytes read. */
if ((bufferChars = read(rfd, buffer, BUFFER_SIZE)) > 0)
{
/* Process the data */
}
else if (bufferChars == 0) /* EOF reached. */
break;
else /* bufferChars < 0 --- read failure. */
perror("Read failed.");
}
close(rfd);
you might consider using mmap() for reading the file data. Check this answer here: When should I use mmap for file access?
I am wondering if anyone could assist me with this, I am try to figure out how to handle a time of check, time of use problem and drop privileges when they are not needed, in case the for example it is a symbolic link to the file which could be changed to say the shadow file. Assuming the function below is invoked while the calling process is running with elevated privileges.
int
updatefile(char *file)
{
int fd;
if (access(file, R_OK|W_OK)) {
perror("access()");
return (-1);
}
fd = open(file, O_RDWR);
/*
* file is written to here.
*/
printf("Updated %s on...\n", file);
system("date");
/*
* elevated privileges are required here...
*/
return (0);
}
Assuming that your access function checks the file type and determines if the user has the appropriate privileges to manipulate the file, you are concerned about a potential TOCTTOU error between the call to access and the call to open.
The typical way to avoid this would be:
int updatefile(char *file)
{
int fd = -1;
if(-1 != (fd = open(file, R_OK | W_OK)))
{
struct stat buf;
if(0 == fstat(fd, &buf))
{
/* perform any necessary check on the here */
/* do what ever else you need to do */
/* write to the file here */
/* gain elevated permissions here */
/* do privileged task */
/* drop back to normal permissions here */
close(fd);
}
else
{
/* handle error stating the file */
}
}
else
{
/* handle error for not opening file */
}
}
The reason that this works, is that we postpone doing any checks on the file till after we get a "handle" to the file. We can tell if the user doesn't have the permissions to open the file by the value of errno in the outer else block;
If we are able to get a "handle" to the file, we then can do what ever checks we want. Because we maintain the "handle" from the time that we open the file, through when we perform our checks and finally when we use the file; a malicious would not be able to modify the file system between check and use.
Hope this helps
T.
I am trying to open a file in c using open() and I need to check that the file is a regular file (it can't be a directory or a block file). Every time I run open() my returned file discriptor is 3 - even when I don't enter a valid filename!
Here's what I have
/*
* Checks to see if the given filename is
* a valid file
*/
int isValidFile(char *filename) {
// We assume argv[1] is a filename to open
int fd;
fd = open(filename,O_RDWR|O_CREAT,0644);
printf("fd = %d\n", fd);
/* fopen returns 0, the NULL pointer, on failure */
}
Can anyone tell me how to validate input files?
Thanks!
Try this:
int file_isreg(const char *path) {
struct stat st;
if (stat(path, &st) < 0)
return -1;
return S_ISREG(st.st_mode);
}
This code will return 1 if regular, 0 if not, -1 on error (with errno set).
If you want to check the file via its file descriptor returned by open(2), then try:
int fd_isreg(int fd) {
struct stat st;
if (fstat(fd, &st) < 0)
return -1;
return S_ISREG(st.st_mode);
}
You can find more examples here, (specifically in the path.c file).
You should also include the following headers in your code (as stated on stat(2) manual page):
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
For future reference, here is an excerpt of the stat(2) manpage regarding the POSIX macros available for st_mode field validations:
S_ISREG(m) is it a regular file?
S_ISDIR(m) directory?
S_ISCHR(m) character device?
S_ISBLK(m) block device?
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
int isValidFile(char *filename) {
// We assume argv[1] is a filename to open
int fd;
fd = open(filename,O_RDWR|***O_CREAT***,0644);
printf("fd = %d\n", fd);
/* fopen returns 0, the NULL pointer, on failure */
}
you are using 0_CREAT which prompts the function to create if the file doesn't exist.this in the table its number is 3 (0,1,2 being std input std output and std error)
Wrong: check if the file is OK, then if it is, go open it and use it.
Right: go open it. If you can't, report the problem and bail out. Otherwise, use it (checking and reporting errors after each opetation).
Why: you have just checked that a file is OK. That's fine, but you cannot assume it will be OK in 0.000000017 seconds from now. Perhaps the disk wil overheat and break down. Perhaps some other process will mass-delete your entire file collection. Perhaps your cat will trip over the network cable. So let's just check if it's OK again, and then go open it. Wow, what a great idea! No wait...