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