7 /* simple cooperative file updating protocol
9 /* #include <edit_file.h>
13 /* char *tmp_path; /* temp. pathname */
14 /* VSTREAM *tmp_fp; /* temp. stream */
15 /* /* private members... */
19 /* EDIT_FILE *edit_file_open(original_path, output_flags, output_mode)
20 /* const char *original_path;
22 /* mode_t output_mode;
24 /* int edit_file_close(edit_file)
25 /* EDIT_FILE *edit_file;
27 /* void edit_file_cleanup(edit_file)
28 /* EDIT_FILE *edit_file;
30 /* This module implements a simple protocol for cooperative
31 /* processes to update one file. The idea is to 1) create a
32 /* new file under a deterministic temporary pathname, 2)
33 /* populate the new file with updated information, and 3)
34 /* rename the new file into the place of the original file.
35 /* This module provides 1) and 3), and leaves 2) to the
36 /* application. The temporary pathname is deterministic to
37 /* avoid accumulation of thrash after program crashes.
39 /* edit_file_open() implements the first phase of the protocol.
40 /* It creates or opens an output file with a deterministic
41 /* temporary pathname, obtained by appending the suffix defined
42 /* with EDIT_FILE_SUFFIX to the specified original file pathname.
43 /* The original file itself is not opened. edit_file_open()
44 /* then locks the output file for exclusive access, and verifies
45 /* that the file still exists under the temporary pathname.
46 /* At this point in the protocol, the current process controls
47 /* both the output file content and its temporary pathname.
49 /* In the second phase, the application opens the original
50 /* file if needed, and updates the output file via the
51 /* \fBtmp_fp\fR member of the EDIT_FILE data structure. This
52 /* phase is not implemented by the edit_file() module.
54 /* edit_file_close() implements the third and final phase of
55 /* the protocol. It flushes the output file to persistent
56 /* storage, and renames the output file from its temporary
57 /* pathname into the place of the original file. When any of
58 /* these operations fails, edit_file_close() behaves as if
59 /* edit_file_cleanup() was called. Regardless of whether these
60 /* operations suceed, edit_file_close() releases the exclusive
61 /* lock, closes the output file, and frees up memory that was
62 /* allocated by edit_file_open().
64 /* edit_file_cleanup() aborts the protocol. It discards the
65 /* output file, releases the exclusive lock, closes the output
66 /* file, and frees up memory that was allocated by edit_file_open().
70 /* The pathname of the original file that will be replaced by
71 /* the output file. The temporary pathname for the output file
72 /* is obtained by appending the suffix defined with EDIT_FILE_SUFFIX
73 /* to a copy of the specified original file pathname, and is
74 /* made available via the \fBtmp_path\fR member of the EDIT_FILE
77 /* Flags for opening the output file. These are as with open(2),
78 /* except that the O_TRUNC flag is ignored. edit_file_open()
79 /* always truncates the output file after it has obtained
80 /* exclusive control over the output file content and temporary
83 /* Permissions for the output file. These are as with open(2),
84 /* except that the output file is initially created with no
85 /* group or other access permissions. The specified output
86 /* file permissions are applied by edit_file_close().
88 /* Pointer to data structure that is returned upon successful
89 /* completion by edit_file_open(), and that must be passed to
90 /* edit_file_close() or edit_file_cleanup().
92 /* Fatal errors: memory allocation failure, fstat() failure,
93 /* unlink() failure, lock failure, ftruncate() failure.
95 /* edit_file_open() immediately returns a null pointer when
96 /* it cannot open the output file.
98 /* edit_file_close() returns zero on success, VSTREAM_EOF on
101 /* With both functions, the global errno variable indicates
102 /* the nature of the problem. All errors are relative to the
103 /* temporary output's pathname. With both functions, this
104 /* pathname is not available via the EDIT_FILE data structure,
105 /* because that structure was already destroyed, or not created.
107 /* In the non-error case, edit_file_open() will not return
108 /* until it obtains exclusive control over the output file
109 /* content and temporary pathname. Applications that are
110 /* concerned about deadlock should protect the edit_file_open()
111 /* call with a watchdog timer.
113 /* When interrupted, edit_file_close() may leave behind a
114 /* world-readable output file under the temporary pathname.
115 /* On some systems this can be used to inflict a shared-lock
116 /* DOS on the protocol. Applications that are concerned about
117 /* maximal safety should protect the edit_file_close() call
118 /* with sigdelay() and sigresume() calls, but this introduces
119 /* the risk that the program will get stuck forever.
123 /* The Secure Mailer license must be distributed with this software.
125 /* Based on code originally by:
129 /* Packaged into one module with minor improvements by:
131 /* IBM T.J. Watson Research
133 /* Yorktown Heights, NY 10598, USA
136 /* System library. */
138 #include <sys_defs.h>
139 #include <sys/stat.h>
140 #include <stdio.h> /* rename(2) */
144 * This mask selects all permission bits in the st_mode stat data. There is
145 * no portable definition (unlike S_IFMT, which is defined for the file type
146 * bits). For example, BSD / Linux have ALLPERMS, while Solaris has S_IAMB.
148 #define FILE_PERM_MASK \
149 (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
151 /* Utility Library. */
155 #include <mymalloc.h>
156 #include <stringops.h>
158 #include <edit_file.h>
161 * Do we reuse and truncate an output file that persists after a crash, or
162 * do we unlink it and create a new file?
164 #define EDIT_FILE_REUSE_AFTER_CRASH
167 * Protocol internals: the temporary file permissions.
169 #define EDIT_FILE_MODE (S_IRUSR | S_IWUSR) /* temp file mode */
172 * Make complex operations more readable. We could use functions, instead.
173 * The main thing is that we keep the _alloc and _free code together.
175 #define EDIT_FILE_ALLOC(ep, path, mode) do { \
176 (ep) = (EDIT_FILE *) mymalloc(sizeof(EDIT_FILE)); \
177 (ep)->final_path = mystrdup(path); \
178 (ep)->final_mode = (mode); \
179 (ep)->tmp_path = concatenate((path), EDIT_FILE_SUFFIX, (char *) 0); \
183 #define EDIT_FILE_FREE(ep) do { \
184 myfree((ep)->final_path); \
185 myfree((ep)->tmp_path); \
186 myfree((char *) (ep)); \
189 /* edit_file_open - open and lock file with deterministic temporary pathname */
191 EDIT_FILE
*edit_file_open(const char *path
, int flags
, mode_t mode
)
193 struct stat before_lock
;
194 struct stat after_lock
;
199 * Initialize. Do not bother to optimize for the error case.
201 EDIT_FILE_ALLOC(ep
, path
, mode
);
204 * As long as the output file can be opened under the temporary pathname,
205 * this code can loop or block forever.
207 * Applications that are concerned about deadlock should protect the
208 * edit_file_open() call with a watchdog timer.
210 for ( /* void */ ; /* void */ ; (void) vstream_fclose(ep
->tmp_fp
)) {
213 * Try to open the output file under the temporary pathname. This
214 * succeeds or fails immediately. To avoid creating a shared-lock DOS
215 * opportunity after we crash, we create the output file with no
216 * group or other permissions, and set the final permissions at the
217 * end (this is one reason why we try to get exclusive control over
218 * the output file instead of the original file). We postpone file
219 * truncation until we have obtained exclusive control over the file
220 * content and temporary pathname. If the open operation fails, we
221 * give up immediately. The caller can retry the call if desirable.
223 * XXX If we replace the vstream_fopen() call by safe_open(), then we
224 * should replace the stat() call below by lstat().
226 if ((ep
->tmp_fp
= vstream_fopen(ep
->tmp_path
, flags
& ~(O_TRUNC
),
227 EDIT_FILE_MODE
)) == 0) {
235 * At this point we may have opened an existing output file that was
236 * already locked. Try to lock the open file exclusively. This may
239 if (myflock(vstream_fileno(ep
->tmp_fp
), INTERNAL_LOCK
,
240 MYFLOCK_OP_EXCLUSIVE
) < 0)
241 msg_fatal("lock %s: %m", ep
->tmp_path
);
244 * At this point we have an exclusive lock, but some other process
245 * may have renamed or removed the output file while we were waiting
246 * for the lock. If that is the case, back out and try again.
248 if (fstat(vstream_fileno(ep
->tmp_fp
), &before_lock
) < 0)
249 msg_fatal("open %s: %m", ep
->tmp_path
);
250 if (stat(ep
->tmp_path
, &after_lock
) < 0
251 || before_lock
.st_dev
!= after_lock
.st_dev
252 || before_lock
.st_ino
!= after_lock
.st_ino
254 || before_lock
.st_gen
!= after_lock
.st_gen
256 /* No need to compare st_rdev or st_nlink here. */
262 * At this point we have exclusive control over the output file
263 * content and its temporary pathname (within the rules of the
264 * cooperative protocol). But wait, there is more.
266 * There are many opportunies for trouble when opening a pre-existing
267 * output file. Here are just a few.
269 * - Victor observes that a system crash in the middle of the
270 * final-phase rename() operation may result in the output file
271 * having both the temporary pathname and the final pathname. In that
272 * case we must not write to the output file.
274 * - Wietse observes that crashes may also leave the output file in
275 * other inconsistent states. To avoid permission-related trouble, we
276 * simply refuse to work with an output file that has the wrong
277 * temporary permissions. This won't stop the shared-lock DOS if we
278 * crash after changing the file permissions, though.
280 * To work around these crash-related problems, remove the temporary
281 * pathname, back out, and try again.
283 if (!S_ISREG(after_lock
.st_mode
)
284 #ifndef EDIT_FILE_REUSE_AFTER_CRASH
285 || after_lock
.st_size
> 0
287 || after_lock
.st_nlink
> 1
288 || (after_lock
.st_mode
& FILE_PERM_MASK
) != EDIT_FILE_MODE
) {
289 if (unlink(ep
->tmp_path
) < 0 && errno
!= ENOENT
)
290 msg_fatal("unlink %s: %m", ep
->tmp_path
);
295 * Settle the final details.
297 #ifdef EDIT_FILE_REUSE_AFTER_CRASH
298 if (ftruncate(vstream_fileno(ep
->tmp_fp
), 0) < 0)
299 msg_fatal("truncate %s: %m", ep
->tmp_path
);
305 /* edit_file_cleanup - clean up without completing the protocol */
307 void edit_file_cleanup(EDIT_FILE
*ep
)
311 * Don't touch the file after we lose the exclusive lock!
313 if (unlink(ep
->tmp_path
) < 0 && errno
!= ENOENT
)
314 msg_fatal("unlink %s: %m", ep
->tmp_path
);
315 (void) vstream_fclose(ep
->tmp_fp
);
319 /* edit_file_close - rename the file into place and and close the file */
321 int edit_file_close(EDIT_FILE
*ep
)
323 VSTREAM
*fp
= ep
->tmp_fp
;
324 int fd
= vstream_fileno(fp
);
328 * The rename/unlock portion of the protocol is relatively simple. The
329 * only things that really matter here are that we change permissions as
330 * late as possible, and that we rename the file to its final pathname
331 * before we lose the exclusive lock.
333 * Applications that are concerned about maximal safety should protect the
334 * edit_file_close() call with sigdelay() and sigresume() calls. It is
335 * not safe for us to call these functions directly, because the calls do
336 * not nest. It is also not nice to force every caller to run with
337 * interrupts turned off.
339 if (vstream_fflush(fp
) < 0
340 || fchmod(fd
, ep
->final_mode
) < 0
344 || rename(ep
->tmp_path
, ep
->final_path
) < 0) {
346 edit_file_cleanup(ep
);
348 return (VSTREAM_EOF
);
350 (void) vstream_fclose(ep
->tmp_fp
);