1 /* Open a file, without destroying an old file with the same name.
2 
3    Copyright (C) 2020 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 */
158