summaryrefslogtreecommitdiff
path: root/lib/supersede.h
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2020-07-05 12:27:24 +0200
committerBruno Haible <bruno@clisp.org>2020-07-05 12:27:24 +0200
commit6dd23517fef58efe208ebcffbe55058c5c0091fe (patch)
tree132fdac55de69a12074a028e1de9461844c929dd /lib/supersede.h
parent6e042f77995d1cd06c68d90e01c5e29a67c3e168 (diff)
downloadgnulib-6dd23517fef58efe208ebcffbe55058c5c0091fe.tar.gz
supersede: New module.
* lib/supersede.h: New file. * lib/supersede.c: New file. * m4/supersede.m4: New file. * modules/supersede: New file.
Diffstat (limited to 'lib/supersede.h')
-rw-r--r--lib/supersede.h157
1 files changed, 157 insertions, 0 deletions
diff --git a/lib/supersede.h b/lib/supersede.h
new file mode 100644
index 0000000000..111d15b864
--- /dev/null
+++ b/lib/supersede.h
@@ -0,0 +1,157 @@
+/* Open a file, without destroying an old file with the same name.
+
+ Copyright (C) 2020 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 <stdbool.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 */