1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
/* Open a file, without destroying an old file with the same name.
Copyright (C) 2020-2024 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/* Written by Bruno Haible, 2020. */
#ifndef _GL_SUPERSEDE_H
#define _GL_SUPERSEDE_H
#include <stdio.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/* When writing a file, for some usages it is important that at any moment,
a process that opens the file will see consistent data in the file. This
can be important in two situations:
* If supersede_if_exists == true, then when the file already existed,
it is important that a process that opens the file while the new file's
contents is being written sees consistent data - namely the old file's
data.
* If supersede_if_does_not_exist == true, then when the file did not exist,
it is important that a process that opens the file while the new file's
contents is being written sees no file (as opposed to a file with
truncated contents).
In both situations, the effect is implemented by creating a temporary file,
writing into that temporary file, and renaming the temporary file when the
temporary file's contents is complete.
Note that opening a file with superseding may fail when it would succeed
without superseding (for example, for a writable file in an unwritable
directory). And also the other way around: Opening a file with superseding
may succeed although it would fail without superseding (for example, for
an unwritable file in a writable directory). */
/* This type holds everything that needs to needs to be remembered in order to
execute the final rename action. */
struct supersede_final_action
{
char *final_rename_temp;
char *final_rename_dest;
};
/* =================== open() and close() with supersede =================== */
/* The typical code idiom is like this:
struct supersede_final_action action;
int fd = open_supersede (filename, O_RDWR, mode,
supersede_if_exists, supersede_if_does_not_exist,
&action);
if (fd >= 0)
{
... write the file's contents ...
if (successful)
{
if (close_supersede (fd, &action) < 0)
error (...);
}
else
{
// Abort the operation.
close (fd);
close_supersede (-1, &action);
}
}
*/
/* Opens a file (typically for writing) in superseding mode, depending on
supersede_if_exists and supersede_if_does_not_exist.
FLAGS should not contain O_CREAT nor O_EXCL.
MODE is used when the file does not yet exist. The umask of the process
is considered, like in open(), i.e. the effective mode is
(MODE & ~ getumask ()).
Upon success, it fills in ACTION and returns a file descriptor.
Upon failure, it returns -1 and sets errno. */
extern int open_supersede (const char *filename, int flags, mode_t mode,
bool supersede_if_exists,
bool supersede_if_does_not_exist,
struct supersede_final_action *action);
/* Closes a file and executes the final rename action.
FD must have been returned by open_supersede(), or -1 if you want to abort
the operation. */
extern int close_supersede (int fd,
const struct supersede_final_action *action);
/* ================== fopen() and fclose() with supersede ================== */
/* The typical code idiom is like this:
struct supersede_final_action action;
FILE *stream =
fopen_supersede (filename, O_RDWR, mode,
supersede_if_exists, supersede_if_does_not_exist,
&action);
if (stream != NULL)
{
... write the file's contents ...
if (successful)
{
if (fclose_supersede (stream, &action) < 0)
error (...);
}
else
{
// Abort the operation.
fclose (stream);
fclose_supersede (NULL, &action);
}
}
*/
/* Opens a file (typically for writing) in superseding mode, depending on
supersede_if_exists and supersede_if_does_not_exist.
Upon success, it fills in ACTION and returns a file stream.
Upon failure, it returns NULL and sets errno. */
extern FILE *fopen_supersede (const char *filename, const char *mode,
bool supersede_if_exists,
bool supersede_if_does_not_exist,
struct supersede_final_action *action);
/* Closes a file stream and executes the final rename action.
STREAM must have been returned by fopen_supersede(), or NULL if you want to
abort the operation. */
extern int fclose_supersede (FILE *stream,
const struct supersede_final_action *action);
/* Closes a file stream, like with fwriteerror, and executes the final rename
action.
STREAM must have been returned by fopen_supersede(), or NULL if you want to
abort the operation. */
extern int fwriteerror_supersede (FILE *stream,
const struct supersede_final_action *action);
#ifdef __cplusplus
}
#endif
#endif /* _GL_SUPERSEDE_H */
|