1

I have separate locations in my C program where I call fopen, fwrite and fclose.

When some conditions are met, I want to delete the file I worked on instead of calling fclose on it. However, by the time I reach fclose, it isn't trivial to recreate the file name I used when calling fopen so using remove isn't practical.

Is there something I can do instead or before calling fclose so that in effect the file I opened with fopen and used fwrite on will not show up on disk in the end?

E.g.

f = fopen(filename,"wt");
...
fwrite(f,...)
...
// How can I undo here the effect of fopen and fwrite without knowledge of filename?
Lolo
  • 3,935
  • 5
  • 40
  • 50
  • 2
    If you're using a Unix-like system, just delete it — you're allowed to delete a file that's open. Windows is a different story, though. – Steve Summit Jan 04 '23 at 15:10
  • Do you mean, you actually simply just want to clear the `FILE*` buffer in memory to avoid writing the data in buffer to the file? Because `fopen` already created the file. – hyde Jan 04 '23 at 15:15
  • No, there is no standard C way to perform such an action. Even with posix functions it is not possible to delete a file by handle. The main reason behind being, that the file itself and its entries in one or more directories are treated separately. – Ctx Jan 04 '23 at 15:15
  • I'd try closing the _file descriptor_. After that you should be able to delete the file, and also `fclose` should fail, so it shouldn't matter in which order you do these operations. https://man7.org/linux/man-pages/man3/fileno.3.html – hyde Jan 04 '23 at 15:17
  • 2
    Well you know the filename, because you've called `fopen` with a well known filename. Just remember that filename somehow and call `remove(filename)` once you've fclosed the file. – Jabberwocky Jan 04 '23 at 15:18
  • 1
    @Jabberwocky You don't even need to wait - on a Unix system, just call `remove(filename);` right after `fopen()`. That makes cleanup of temporary files easy - you don't have to. As long as you don't need to access the file by name, you can still use the `FILE *` handle to read/write from/to the file. – Andrew Henle Jan 04 '23 at 15:46
  • @AndrewHenle true, but by doing it after the `fclose` you're on the safe side. – Jabberwocky Jan 04 '23 at 15:47
  • @Jabberwocky `readlink( "/proc/self/fd/N", ... );` just prior to `fclose()` then, as long as the file doesn't get renamed in the meantime. – Andrew Henle Jan 04 '23 at 16:02
  • Lolo, what do you want to do with the open `FILE *` after the file is deleted? – chux - Reinstate Monica Jan 04 '23 at 17:22
  • depending on the constraints maybe an option: after fopen directly store the filename in a hashtable e.g. with the file descriptor as key, then create a function which returns the filename based on descriptor and with that you can delete the file if necessary - finally you need a function which removes the entry from the hashtable after a fclose / delete operation? – Stephan Schlecht Jan 04 '23 at 18:59
  • I don't have any need for `f` once the file is deleted. Yes, I could add some logic in my code to know at the time of `fclose` what the filename is so that I can call `remove` if need be. Or alternatively I can retrieve the filename from the file descriptor itself. My question was whether I could avoid having to retrieve the file name and call `remove` if I haven't already done an `fclose`. Sounds like no, and I should `fclose`, retrieve the file name, and then `remove`. – Lolo Jan 05 '23 at 08:37
  • @Lolo You'd need to retrieve the name from the descriptor *before* you call `fclose()`. – Andrew Henle Jan 05 '23 at 10:31
  • @AndrewHenle Yes indeed. – Lolo Jan 05 '23 at 11:08

3 Answers3

0

This is untested code, but this should maybe do what you want:

// store both file pointer and file name to be able to delete the file
struct file_info {
    const char *filename;
    FILE *fp;
};


struct file_info file; // uninitialized, remember to initialize all fields
file.filename = "whatever"; // consider ownership of this string!
file.fp = fopen(file.filename, "wt");
if (!file.fp) {
    perror("fopen");
    exit(EXIT_FAILURE); // following code would crash with file.fp=NULL
}
// possibly make the buffer larger with setvbuf to prevent flushing

// do file operations

// abort

// stop any IO on this file descriptor
if (close(fileno(file.fp)) == -1) {
    perror("warning: close fail");
}

// clean up the FILE* structure, the file descriptor should be invalid,
// on condition that you must not open new files between close and fclose!
if (fclose(file.fp) == 0) {
    fputs("warning: fclose success", stderr);
}

// delete after closing the file, otherwise will not work on Windows
if (remove(file.filename) == -1) {
    perror("warning: remove fail");
}
hyde
  • 60,639
  • 21
  • 115
  • 176
  • `close(fileno(file.fp))` and then `fclose(file.fp)` is dangerous. Closing the file descriptor out from under a `FILE *` risks that file descriptor being reused and having its data corrupted and/or closed unexpectedly. – Andrew Henle Jan 04 '23 at 15:32
  • @AndrewHenle Yes, if this is multithreaded code, which may open files from other threads, this needs locking. But otherwise, there's nothing which can open a file and re-use the descriptor. – hyde Jan 04 '23 at 15:33
  • `open()`, `pipe()`, and `socket()` and `socketpair()` are all async-signal-safe... – Andrew Henle Jan 04 '23 at 15:35
  • @AndrewHenle The locking is not needed because they wouldn't be. Locking is needed in the multi-threaded scenario, so the file descriptor does not get re-used. – hyde Jan 04 '23 at 15:36
  • Locking has nothing to do with it. Your "there's nothing which can open a file and re-use the descriptor" is wrong. Signal handlers can safely call `open()` *et al* and reuse the file descriptor value. – Andrew Henle Jan 04 '23 at 15:43
  • @AndrewHenle In this case they can't do it (create new open file descriptors) safely, it will create a race condition. The code in signal handlers is under the control of the programmer. – hyde Jan 04 '23 at 23:12
  • *The code in signal handlers is under the control of the programmer.* Really?!?! What libraries are you linking in? How much of the code of a huge project do you have control over? You're aiming to create Heisenbugs. – Andrew Henle Jan 05 '23 at 01:57
  • @AndrewHenle If a library messes with signal handlers without application programmer knowing about it, that's already a problem (programmer using same signal) waiting to happen. – hyde Jan 05 '23 at 07:03
0

If I summarize all comments I got, the answer to my question is as follows.

No, for safe code running on all platforms, once fopen has been invoked already, one needs to call fclose and then remove.

Lolo
  • 3,935
  • 5
  • 40
  • 50
-1

Sounds like Retrieve filename from file descriptor in C. Maybe you can find a solution to get the filename there.