diff options
author | Bruno Haible <bruno@clisp.org> | 2020-07-05 12:27:24 +0200 |
---|---|---|
committer | Bruno Haible <bruno@clisp.org> | 2020-07-05 12:27:24 +0200 |
commit | 6dd23517fef58efe208ebcffbe55058c5c0091fe (patch) | |
tree | 132fdac55de69a12074a028e1de9461844c929dd /lib/supersede.h | |
parent | 6e042f77995d1cd06c68d90e01c5e29a67c3e168 (diff) | |
download | gnulib-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.h | 157 |
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 */ |