How to know which file failed during rename? - c

I have a simple example:
#include <stdio.h>
#include <errno.h>
int main() {
int result = rename("filea", "doesntexist/fileb");
if (result != 0) {
printf("NOOOO %d\n", errno);
}
return 0;
}
and I want to distinguish between 2 of the possible failures:
filea doesn't exist
directory for fileb doesn't exist
but it always returns errno = 2 when either doesn't exist... uhm
Any ideas how can I approach this?
Thanks
EDIT: If possible without manually checking if the files exist.
EDIT2: Not checking if the file exists is a stupid constraint ;) so, I've accepted one of the answers already. Thanks!

I don't know how you're going to check if a file exists without checking if a file exists, but hopefully this function will help you out:
#include <sys/stat.h>
if (!fileExists("foo")) { /* foo does not exist */ }
int fileExists (const char *fn)
{
struct stat buf;
int i = stat(fn, &buf);
if (i == 0)
return 1; /* file found */
return 0;
}
If your goal is to keep the code clean, then just use functions:
int main()
{
if (! renameFiles("fileA", "fileB")) {
fprintf(stderr, "rename failed...\n");
exit EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int renameFiles(const char *source, const char *destination)
{
int result = -1;
if ( (fileExists(source)) && (!fileExists(destination)) )
result = rename(source, destination);
if (result == 0)
return 1; /* rename succeeded */
/*
Either `source` does not exist, or `destination`
already exists, or there is some other error (take
a look at `errno` and handle appropriately)
*/
return 0;
}
You could return custom error codes from renameFiles() and conditionally handle errors based on which file does or does not exist, or if there is some other problem with the rename() call.

Call access() (unistd.h) first. Or stat(). And you are probably getting an ENOENT error when filea does not exist. Some ways you can get an error on fileB:
path cannot be found
no permissions on the path
fileB exists and you do not have permissions
you have a too long or malformed name
There are others but they are not very common.
There is no case where you should get an error when fileB is not there. You execute a mv filea fileb (what rename does) and all of the errors for mv apply here. Missing destination file is not one of them.
You should also have
#include <errno.h>
since you reference errno.

The ISO C standard does not even require the library function rename to set errno in case of error. All that is guaranteed is a non-zero return value on error (7.19.4.2, ยง3).
So whether this is possible or not depends on your platform (and it is not portable).
E.g. in Linux there is no way to distinguish which of them is missing by just looking at errno after rename (according to this man page).

If the errno is always 2 ENOENT "No such file or directory" on your system you are going to HAVE to check for the existence of something. On my system I get errno of 2 if old does not existent or if the directory path of new does not exist.
However there is much more then 2 possible errors. The link http://man.chinaunix.net/unix/susv3/functions/rename.html has 20 distinct errno values specified.
I would suggest that if the rename fails and the errno is 2 then check for the existence of old. If found then the problem is that the directory specified in the new doesn't exist.

Related

Will errno == ENOENT be a sufficient check to check if file exists in C?

Using PellesC on Windows 8.1.
I know this topic has been addressed many times with many solutions. I have read solutions stating the usage of CreateFile, PathFileExists, GetFileAttributes, _access which I somewhat understand.
I have also read an important point about race conditions in the answer to the questions Quickest way to check whether or not file exists
and What's the best way to check if a file exists in C? (cross platform).
So if I open a file using fopen() in C and when it fails (for any reason) and passes NULL back; then can I further check for errno == ENOENT and be content with it and report correctly that the file does not exists.
#include <stdio.h>
#include <string.h>
#include <errno.h>
int file_exists(char filename[]) {
int err = 0; //copy of errno at specific instance
int r = 0; //1 for exists and 0 for not exists
FILE *f = NULL;
//validate
if (filename == NULL) {
puts("Error: bad filename.");
return 0;
}
printf("Checking if file %s exists...\n", filename);
//check
errno = 0;
f = fopen(filename, "r");
err = errno;
if (f == NULL) {
switch (errno) {
case ENOENT:
r = 0;
break;
default:
r = 1;
}
printf("errno = %d\n%s\n", err, strerror(err));
} else {
fclose(f);
r = 1;
}
if (r == 0) {
puts("It does not.");
} else {
puts("It does.");
}
return r;
}
fopen needs to do lots of stuff and checking before the file is opened. ENOENT implies that the file does not exist, but file not existing does not imply ENOENT.
It is possible for a file to not exist and you to get another error, such as EACCES for not being able to read the parent directory for example.
On the other hand, ENOENT from fopen does not mean that some other process couldn't have created the file even before fopen returns or before you're inspecting the errno and so on; this is the reason why C11 added the x flag for opening a file for writing in exclusive mode - failing if the file already exists.
To sum it up: if you get ENOENT, the file did not exist when you tried opening it. If you get some other error, then each other error code would belong to one of 3 these classes - that it is certain that either
the file existed, or
it cannot have existed
or it might have existed at the time
at the time of opening. It is up to you and your required logic how you'd handle these other errors. A simple way would be to refuse to continue processing, reporting the error to the user.

error handling mkdir and chdir in C?

I do not understand why after creating directory mkdir return different values. In this example i'm trying to make a directory if not so error handle otherwise go to this directory if not so error handle.
But it's not working as I want!
It shows me 'failed to create' but actually creates the directory.
If I read reference right it returns 0 when successes.
Code:
char* dirName = "Output";
FILE *output;
if(!mkdir(dirName))
if(chdir(dirName))
printf("Permission to open a new directory is denied");
else
printf("Permission to create a new directory is denied");
It looks like you need curly braces around the if statement bodies so that the else is bound to the correct if statement.
According to the documentation of mkdir:
Upon successful completion, mkdir() shall return 0. Otherwise, -1 shall be returned, no directory shall be created, and errno shall be set to indicate the error.
Now, remember, in C 0 evaluates to false, non-zero evaluates to true. So, when you check if( mkdir( ... ) ) you are essentially asking if mkdir returned non-zero. And -1 is non-zero, so you are asking whether mkdir() failed, not whether mkdir() succeeded.
What you want to do instead is if( mkdir ( ... ) == 0 ).
Also, in the event of failure, you have no reason to suppose that the reason of the failure was an "access denied" error. Check errno and report the correct error accordingly.
As correctly diagnosed by John Carpenter, the else relates to the second if. Use braces to avoid this bug known as the dangling else issue.
There is another issue in your code: under Linux or BSD, the prototype for mkdir is:
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *path, mode_t mode);
Do you include the system header files?
Not passing proper permissions, such as 0777 invokes undefined behavior, the syscall wrapper will pass indeterminate permissions, creating the directory with inconsistent permissions.
Here is a corrected version:
const char *dirName = "Output";
if (!mkdir(dirName, 0777)) {
if (chdir(dirName)) {
printf("Permission to open a new directory is denied");
}
} else {
printf("Permission to create a new directory is denied");
}

What is the method to check if the target of a symlink exists or not?

Using a c program I need to find and delete all symbolic links in a directory, with missing target.
What is the most efficient way to check whether the target of a symbolic link exist or not. Any method other than opening the symlink and check the return value. I'm working with linux and gcc.
stat or access, or indeed open. That's about all you can do.
From the man 3 stat man page
If the named file is a symbolic link, the stat() function shall continue pathname resolution using the
contents of the symbolic link, and shall return information pertaining to the resulting file if the
file exists.
So the following works nice:
#include <sys/stat.h>
#include <stdio.h>
int main() {
struct stat ctx;
int status = stat("test.txt", &ctx);
if(status != 0) {
perror("[stat]");
return 1;
}
else {
puts("works nice");
}
return 0;
}
The access () function with F_OK mode set will follow a symlink path.
The following code will print "Yes!" if both the symlink and the target file exist...
#include <stdio.h>
#include <unistd.h>
int
main (void)
{
if (access ("test.txt", F_OK) != -1) {
puts ("Yes!");
return 0;
}
puts ("No.");
return 1;
}

Why does fopen("any_path_name",'r') not give NULL as return?

While debugging some code I got something like below:
#include<stdio.h>
int main()
{
FILE *fb = fopen("/home/jeegar/","r");
if(NULL == fb)
printf("it is null");
else
printf("working");
}
Here in fopen I gave a somewhat valid path name but not a filename. Shouldn't fopen return NULL then? But it does not return null!
Edit:
If I give path of valid directory in fopen then it will print working:
If I give path of invalid directory in fopen then it will print it is null
Edit:
spec says
Upon successful completion, fopen() shall return a pointer to the object
controlling the stream. Otherwise, a null pointer shall be returned.
so here whether error code set or not, it MUST return NULL
And error code setting is an extansion to ISO C standard standard.
ERROR IS ALSO NOT GOING TO SET HERE
#include<stdio.h>
#include <errno.h>
int main()
{
errno = 0;
FILE *fb = fopen("/home/jeegar/","r");
if(fb==NULL)
printf("its null");
else
printf("working");
printf("Error %d \n", errno);
}
OUTPUT IS
workingError 0
I think that in Unix everything (directories included) is considered to be file so fopen should work on them.
The posix man page man 3p fopen says, in the section ERRORS:
The fopen() function shall fail if:
[...]
EISDIR The named file is a directory and mode requires write access.
(Emphasis mine). Since you are not requesting write access, and chances are that the path you use is a directory, the function does not fail.
About what can you use with a FILE* that refers to a directory, I have no idea.
As you might be very well aware that pretty much everything on Linux system is a file, if not a file then its a process (corrections & remarks welcome :) ) Directory is treated like a file which lists other files (Reference from TLDP); so opening to read a directory as a file is a valid operation and thus you do not get any error. Although trying to write to it is not allowed, so if you open directory in write or append mode, the fopen operation will fail (this has been very well mentioned is other responses & link to fopen documentation). Most of the file operation like read & write operations on this file stream will fail with the error stating that its a directory. Only use which could be found was finding the size of the file (directory in this case) using fseek to SEEK_END & ftell (which will most likely give a result of 4096).
Regarding using errno to get meaningful messages, you can use perror which is in stdio.h & pass message which will be added before the error message or strerror which is in string.h & pass errno which is in errno.h
Hope this helps!
How to check that errno?
You can check errno for example:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
errno = 0;
fp = fopen("file.txt", "r");
if ( errno != 0 )
{
// Here you can check your error types:
perror("Error %d \n", errno);
exit(1);
}
}
Error types you can find at http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html Error section.

detecting loops in symbolic links (c programming)

I'm looking to detect loops in symbolic links in a C program:
$ ln -s self self
$ ln -s a b
$ ln -s b a
Here's what I've got so far:
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
int
main(int argc, char *argv[])
{
struct stat buffer;
int status;
if (argc != 2) {
fprintf(stderr, "error: file name required\n");
return 0;
}
errno = 0;
status = lstat(argv[1], &buffer);
if (errno == ELOOP) {
fprintf(stderr, "loop found");
}
return 1;
}
I'm running my program like this:
$ findloops self
$ findloops a
Any idea what I'm doing wrong?
This is NOT homework.
This is where I got the idea from.
The trouble is that 'lstat()' looks at the symlink and its properties, and the symlinks actually exist.
If you replace the call with 'stat()', then you will get the ELOOP error. This tries to get the information at the far end of the symlink, and that cannot be found because of the ELOOP condition.
You should only test errno after you have verified that status indicates a failure. With a genuine system call, it is unlikely that errno would be set when the call succeeds, but with library functions, you can find errno is set even though the call succeeds. For example, with some standard I/O library implementations, you can have errno == ENOTTY even after a successful function call; the code checks whether the file descriptor represents a terminal and errno is set to indicate that it isn't, but since the function succeeded, it is not legitimate to check errno.
I would take a look at the buffer returned. According to the documentation of lstat the buffer contains two items that would be relevant:
st_ino - The inode for the file (note that this number is unique to each distinct file and all directories on a Linux file system, but the same inode number can appear in different file systems).
st_dev - The device that the file currently resides on.
If you create a list containing these two items per element+the directory where the link is located as the previously visited elements, you could detect loops. Also don't forget to pop them off when you leave the directory that they were created in.
I'm not convinced that ELOOP is the value that you think it is. According to this, it identifies the maximum links tolerated in the class path, but it won't tell you which link looped first.
The documentation on the page claimed this: "ELOOP: Too many symbolic links were encountered in translating the pathname. "
ELOOP doesn't have to mean that there is a loop. It can also mean that there are too many symbolic links from source to target, as in
a -> b -> c -> d -> e ... -> z
do this enough times and the OS kernel (particularily for some cases on linux) will give up trying to follow the links, even if they are all valid and non-cyclic.
You may also be interested in man 2 readlink.
After some playing with code, it looks like you've found either a feature or a bug with lstat(2). According to the man page on lstat, which is also stat and fstat, the difference between stat and lstat is:
stat() stats the file pointed to by
path and fills in buf.
lstat() is identical to stat(), except
that if path is a symbolic link, then
the link itself is stat-ed, not the
file that it refers to
I took your program and played with it a little. I used lstat, stat, and fopen to check the link. Code is below. The bottom line is that both stat and fopen detected the link properly, while lstat failed. I have no explanation for this.
The program below, executed on file bar created as 'ln -s bar bar', gave the following output:
./foo ./bar
Errno as returned from lstat = 0
Errno as returned from stat = 92
loop found
Errno as returned from fopen = 92
loop found
Code:
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
int
main(int argc, char *argv[])
{
struct stat buffer;
int status;
int savedErrno1;
int savedErrno2;
int savedErrno3;
FILE *theFile;
if (argc != 2) {
printf("error: file name required\n");
return 0;
}
errno = 0;
status = lstat(argv[1], &buffer);
savedErrno1 = errno;
printf("Errno as returned from lstat = %d\n", savedErrno1);
if (savedErrno1 == ELOOP) {
printf("loop found\n");
}
errno = 0;
status = stat(argv[1], &buffer);
savedErrno2 = errno;
printf("Errno as returned from stat = %d\n", savedErrno2);
if (savedErrno2 == ELOOP) {
printf("loop found\n");
}
errno = 0;
theFile = fopen(argv[1], "w");
savedErrno3 = errno;
printf("Errno as returned from fopen = %d\n", savedErrno3);
if (savedErrno3 == ELOOP) {
printf("loop found\n");
}
return 1;
}

Resources