0

I have a full path stored in char full_path[1000] and I'd like to know whether anything exists at that location.

(The next thing my code would do after this check is create something at that location, but I want it to count as an error if there is already something there instead of clearing the spot with the equivalent of rm -rf)

The spot might be occupied by a file or a directory or even a link to some no-longer-existing-target e.g.:

lrwxrwxrwx 1 user grp 4 Jun 16 20:02 a-link -> non-existent-thing

With an invalid link, access(full_path, F_OK) is going to tell me I can't access full_path and that could be because (1) nothing exists there or because (2) the link is invalid.

Given that, what's the best way to determine if anything exists at full_path?

Mateusz Piotrowski
  • 8,029
  • 10
  • 53
  • 79
user1011471
  • 1,090
  • 2
  • 14
  • 34
  • does char full_path[1000] store a file path or only a directoyr? – mustangDC Jun 16 '15 at 20:16
  • 6
    `open(path, O_CREAT|O_EXCL, mode)` will return failure (-1) if the file already exists, and create the file otherwise. This is atomic and safe, unlike trying to `stat`. – EOF Jun 16 '15 at 20:19
  • If you can't access the full path - you can't access it. Thus there's no way to check. And checking if something exists before creating it creates a race condition that can be exploited. – Andrew Henle Jun 16 '15 at 20:52
  • @EOF are there any drawbacks to your suggestion? Is it cross-platform? Are there limitations with the result or can I make it a directory or a symlink or whatever I want? If the answers are favorable, perhaps your suggestion should be the accepted answer. Seems most useful for future readers. – user1011471 Jun 16 '15 at 22:49
  • @user1011471: Well, I did not see you wanted to create a symlink until you posted your own answer. If you want to create a symlink, your answer is all you need. If you want to create a regular file, `open(O_CREAT|O_EXCL)` is the way to go, and guaranteed to work atomically on POSIX (with the possible exception of NFS, but oh well). – EOF Jun 16 '15 at 22:53

2 Answers2

1

You simply cannot do that in a cross-platform obvious way. stat() and fopen() would not work. You can, though, use the OS API, for example, on windows you could use WinAPI with the example code:

int doesFileExist(TCHAR* path)
{
   WIN32_FIND_DATA FindFileData;
   HANDLE handle = FindFirstFile(path, &FindFileData) ;
   int found = (handle != INVALID_HANDLE_VALUE);
   if(found) 
   {
       FindClose(handle);
   }
   return found;
}

If you just want to check if anything exists, finding any file (again, using Windows API) would also work, you could just go directory by directory to check if it exists, if one doesn't - return an error. Keep going until you got to the directory then check for the certain file in the way mentioned above.

Say you have C:/Dir1/Dir2/Dir3/file.txt then you'd go to C: first, then check if Dir1 exists, if it does - go to it, if it doesn't return an error. Same for Dir2 and so on up until you get to the last directory and check for the file OR if you don't check for a certain file and for any item - just try using the functions mentioned in MSDN for finding first file or first directory.

Zach P
  • 1,731
  • 11
  • 16
1

Since the next thing we plan to do is create something at that location, and since we want to treat it as an error if something already exists there, then let's not bother checking. Just attempt the create and exit with an error if it fails. The create step uses symlink so if it fails we can use strerror(errno) for the explanation of any failure.

To create a file in general (vs just a symlink), EOF points out in comments that open(path, O_CREAT|O_EXCL, mode) will return failure (-1) if the file already exists, and create the file otherwise. This is atomic and safe (unlike trying to stat) and guaranteed to work atomically on POSIX (with the possible exception of NFS).

user1011471
  • 1,090
  • 2
  • 14
  • 34