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