Postfix3.3.1
edit_file.c
[詳解]
1 /*++
2 /* NAME
3 /* edit_file 3
4 /* SUMMARY
5 /* simple cooperative file updating protocol
6 /* SYNOPSIS
7 /* #include <edit_file.h>
8 /*
9 /* typedef struct {
10 /* .in +4
11 /* char *tmp_path; /* temp. pathname */
12 /* VSTREAM *tmp_fp; /* temp. stream */
13 /* /* private members... */
14 /* .in -4
15 /* } EDIT_FILE;
16 /*
17 /* EDIT_FILE *edit_file_open(original_path, output_flags, output_mode)
18 /* const char *original_path;
19 /* int output_flags;
20 /* mode_t output_mode;
21 /*
22 /* int edit_file_close(edit_file)
23 /* EDIT_FILE *edit_file;
24 /*
25 /* void edit_file_cleanup(edit_file)
26 /* EDIT_FILE *edit_file;
27 /* DESCRIPTION
28 /* This module implements a simple protocol for cooperative
29 /* processes to update one file. The idea is to 1) create a
30 /* new file under a deterministic temporary pathname, 2)
31 /* populate the new file with updated information, and 3)
32 /* rename the new file into the place of the original file.
33 /* This module provides 1) and 3), and leaves 2) to the
34 /* application. The temporary pathname is deterministic to
35 /* avoid accumulation of thrash after program crashes.
36 /*
37 /* edit_file_open() implements the first phase of the protocol.
38 /* It creates or opens an output file with a deterministic
39 /* temporary pathname, obtained by appending the suffix defined
40 /* with EDIT_FILE_SUFFIX to the specified original file pathname.
41 /* The original file itself is not opened. edit_file_open()
42 /* then locks the output file for exclusive access, and verifies
43 /* that the file still exists under the temporary pathname.
44 /* At this point in the protocol, the current process controls
45 /* both the output file content and its temporary pathname.
46 /*
47 /* In the second phase, the application opens the original
48 /* file if needed, and updates the output file via the
49 /* \fBtmp_fp\fR member of the EDIT_FILE data structure. This
50 /* phase is not implemented by the edit_file() module.
51 /*
52 /* edit_file_close() implements the third and final phase of
53 /* the protocol. It flushes the output file to persistent
54 /* storage, and renames the output file from its temporary
55 /* pathname into the place of the original file. When any of
56 /* these operations fails, edit_file_close() behaves as if
57 /* edit_file_cleanup() was called. Regardless of whether these
58 /* operations succeed, edit_file_close() releases the exclusive
59 /* lock, closes the output file, and frees up memory that was
60 /* allocated by edit_file_open().
61 /*
62 /* edit_file_cleanup() aborts the protocol. It discards the
63 /* output file, releases the exclusive lock, closes the output
64 /* file, and frees up memory that was allocated by edit_file_open().
65 /*
66 /* Arguments:
67 /* .IP original_path
68 /* The pathname of the original file that will be replaced by
69 /* the output file. The temporary pathname for the output file
70 /* is obtained by appending the suffix defined with EDIT_FILE_SUFFIX
71 /* to a copy of the specified original file pathname, and is
72 /* made available via the \fBtmp_path\fR member of the EDIT_FILE
73 /* data structure.
74 /* .IP output_flags
75 /* Flags for opening the output file. These are as with open(2),
76 /* except that the O_TRUNC flag is ignored. edit_file_open()
77 /* always truncates the output file after it has obtained
78 /* exclusive control over the output file content and temporary
79 /* pathname.
80 /* .IP output_mode
81 /* Permissions for the output file. These are as with open(2),
82 /* except that the output file is initially created with no
83 /* group or other access permissions. The specified output
84 /* file permissions are applied by edit_file_close().
85 /* .IP edit_file
86 /* Pointer to data structure that is returned upon successful
87 /* completion by edit_file_open(), and that must be passed to
88 /* edit_file_close() or edit_file_cleanup().
89 /* DIAGNOSTICS
90 /* Fatal errors: memory allocation failure, fstat() failure,
91 /* unlink() failure, lock failure, ftruncate() failure.
92 /*
93 /* edit_file_open() immediately returns a null pointer when
94 /* it cannot open the output file.
95 /*
96 /* edit_file_close() returns zero on success, VSTREAM_EOF on
97 /* failure.
98 /*
99 /* With both functions, the global errno variable indicates
100 /* the nature of the problem. All errors are relative to the
101 /* temporary output's pathname. With both functions, this
102 /* pathname is not available via the EDIT_FILE data structure,
103 /* because that structure was already destroyed, or not created.
104 /* BUGS
105 /* In the non-error case, edit_file_open() will not return
106 /* until it obtains exclusive control over the output file
107 /* content and temporary pathname. Applications that are
108 /* concerned about deadlock should protect the edit_file_open()
109 /* call with a watchdog timer.
110 /*
111 /* When interrupted, edit_file_close() may leave behind a
112 /* world-readable output file under the temporary pathname.
113 /* On some systems this can be used to inflict a shared-lock
114 /* DOS on the protocol. Applications that are concerned about
115 /* maximal safety should protect the edit_file_close() call
116 /* with sigdelay() and sigresume() calls, but this introduces
117 /* the risk that the program will get stuck forever.
118 /* LICENSE
119 /* .ad
120 /* .fi
121 /* The Secure Mailer license must be distributed with this software.
122 /* AUTHOR(S)
123 /* Based on code originally by:
124 /* Victor Duchovni
125 /* Morgan Stanley
126 /*
127 /* Packaged into one module with minor improvements by:
128 /* Wietse Venema
129 /* IBM T.J. Watson Research
130 /* P.O. Box 704
131 /* Yorktown Heights, NY 10598, USA
132 /*--*/
133 
134 /* System library. */
135 
136 #include <sys_defs.h>
137 #include <sys/stat.h>
138 #include <stdio.h> /* rename(2) */
139 #include <errno.h>
140 
141  /*
142  * This mask selects all permission bits in the st_mode stat data. There is
143  * no portable definition (unlike S_IFMT, which is defined for the file type
144  * bits). For example, BSD / Linux have ALLPERMS, while Solaris has S_IAMB.
145  */
146 #define FILE_PERM_MASK \
147  (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
148 
149 /* Utility Library. */
150 
151 #include <msg.h>
152 #include <vstream.h>
153 #include <mymalloc.h>
154 #include <stringops.h>
155 #include <myflock.h>
156 #include <edit_file.h>
157 #include <warn_stat.h>
158 
159  /*
160  * Do we reuse and truncate an output file that persists after a crash, or
161  * do we unlink it and create a new file?
162  */
163 #define EDIT_FILE_REUSE_AFTER_CRASH
164 
165  /*
166  * Protocol internals: the temporary file permissions.
167  */
168 #define EDIT_FILE_MODE (S_IRUSR | S_IWUSR) /* temp file mode */
169 
170  /*
171  * Make complex operations more readable. We could use functions, instead.
172  * The main thing is that we keep the _alloc and _free code together.
173  */
174 #define EDIT_FILE_ALLOC(ep, path, mode) do { \
175  (ep) = (EDIT_FILE *) mymalloc(sizeof(EDIT_FILE)); \
176  (ep)->final_path = mystrdup(path); \
177  (ep)->final_mode = (mode); \
178  (ep)->tmp_path = concatenate((path), EDIT_FILE_SUFFIX, (char *) 0); \
179  (ep)->tmp_fp = 0; \
180  } while (0)
181 
182 #define EDIT_FILE_FREE(ep) do { \
183  myfree((ep)->final_path); \
184  myfree((ep)->tmp_path); \
185  myfree((void *) (ep)); \
186  } while (0)
187 
188 /* edit_file_open - open and lock file with deterministic temporary pathname */
189 
190 EDIT_FILE *edit_file_open(const char *path, int flags, mode_t mode)
191 {
192  struct stat before_lock;
193  struct stat after_lock;
194  int saved_errno;
195  EDIT_FILE *ep;
196 
197  /*
198  * Initialize. Do not bother to optimize for the error case.
199  */
200  EDIT_FILE_ALLOC(ep, path, mode);
201 
202  /*
203  * As long as the output file can be opened under the temporary pathname,
204  * this code can loop or block forever.
205  *
206  * Applications that are concerned about deadlock should protect the
207  * edit_file_open() call with a watchdog timer.
208  */
209  for ( /* void */ ; /* void */ ; (void) vstream_fclose(ep->tmp_fp)) {
210 
211  /*
212  * Try to open the output file under the temporary pathname. This
213  * succeeds or fails immediately. To avoid creating a shared-lock DOS
214  * opportunity after we crash, we create the output file with no
215  * group or other permissions, and set the final permissions at the
216  * end (this is one reason why we try to get exclusive control over
217  * the output file instead of the original file). We postpone file
218  * truncation until we have obtained exclusive control over the file
219  * content and temporary pathname. If the open operation fails, we
220  * give up immediately. The caller can retry the call if desirable.
221  *
222  * XXX If we replace the vstream_fopen() call by safe_open(), then we
223  * should replace the stat() call below by lstat().
224  */
225  if ((ep->tmp_fp = vstream_fopen(ep->tmp_path, flags & ~(O_TRUNC),
226  EDIT_FILE_MODE)) == 0) {
227  saved_errno = errno;
228  EDIT_FILE_FREE(ep);
229  errno = saved_errno;
230  return (0);
231  }
232 
233  /*
234  * At this point we may have opened an existing output file that was
235  * already locked. Try to lock the open file exclusively. This may
236  * take some time.
237  */
238  if (myflock(vstream_fileno(ep->tmp_fp), INTERNAL_LOCK,
240  msg_fatal("lock %s: %m", ep->tmp_path);
241 
242  /*
243  * At this point we have an exclusive lock, but some other process
244  * may have renamed or removed the output file while we were waiting
245  * for the lock. If that is the case, back out and try again.
246  */
247  if (fstat(vstream_fileno(ep->tmp_fp), &before_lock) < 0)
248  msg_fatal("open %s: %m", ep->tmp_path);
249  if (stat(ep->tmp_path, &after_lock) < 0
250  || before_lock.st_dev != after_lock.st_dev
251  || before_lock.st_ino != after_lock.st_ino
252 #ifdef HAS_ST_GEN
253  || before_lock.st_gen != after_lock.st_gen
254 #endif
255  /* No need to compare st_rdev or st_nlink here. */
256  ) {
257  continue;
258  }
259 
260  /*
261  * At this point we have exclusive control over the output file
262  * content and its temporary pathname (within the rules of the
263  * cooperative protocol). But wait, there is more.
264  *
265  * There are many opportunies for trouble when opening a pre-existing
266  * output file. Here are just a few.
267  *
268  * - Victor observes that a system crash in the middle of the
269  * final-phase rename() operation may result in the output file
270  * having both the temporary pathname and the final pathname. In that
271  * case we must not write to the output file.
272  *
273  * - Wietse observes that crashes may also leave the output file in
274  * other inconsistent states. To avoid permission-related trouble, we
275  * simply refuse to work with an output file that has the wrong
276  * temporary permissions. This won't stop the shared-lock DOS if we
277  * crash after changing the file permissions, though.
278  *
279  * To work around these crash-related problems, remove the temporary
280  * pathname, back out, and try again.
281  */
282  if (!S_ISREG(after_lock.st_mode)
284  || after_lock.st_size > 0
285 #endif
286  || after_lock.st_nlink > 1
287  || (after_lock.st_mode & FILE_PERM_MASK) != EDIT_FILE_MODE) {
288  if (unlink(ep->tmp_path) < 0 && errno != ENOENT)
289  msg_fatal("unlink %s: %m", ep->tmp_path);
290  continue;
291  }
292 
293  /*
294  * Settle the final details.
295  */
296 #ifdef EDIT_FILE_REUSE_AFTER_CRASH
297  if (ftruncate(vstream_fileno(ep->tmp_fp), 0) < 0)
298  msg_fatal("truncate %s: %m", ep->tmp_path);
299 #endif
300  return (ep);
301  }
302 }
303 
304 /* edit_file_cleanup - clean up without completing the protocol */
305 
307 {
308 
309  /*
310  * Don't touch the file after we lose the exclusive lock!
311  */
312  if (unlink(ep->tmp_path) < 0 && errno != ENOENT)
313  msg_fatal("unlink %s: %m", ep->tmp_path);
314  (void) vstream_fclose(ep->tmp_fp);
315  EDIT_FILE_FREE(ep);
316 }
317 
318 /* edit_file_close - rename the file into place and and close the file */
319 
321 {
322  VSTREAM *fp = ep->tmp_fp;
323  int fd = vstream_fileno(fp);
324  int saved_errno;
325 
326  /*
327  * The rename/unlock portion of the protocol is relatively simple. The
328  * only things that really matter here are that we change permissions as
329  * late as possible, and that we rename the file to its final pathname
330  * before we lose the exclusive lock.
331  *
332  * Applications that are concerned about maximal safety should protect the
333  * edit_file_close() call with sigdelay() and sigresume() calls. It is
334  * not safe for us to call these functions directly, because the calls do
335  * not nest. It is also not nice to force every caller to run with
336  * interrupts turned off.
337  */
338  if (vstream_fflush(fp) < 0
339  || fchmod(fd, ep->final_mode) < 0
340 #ifdef HAS_FSYNC
341  || fsync(fd) < 0
342 #endif
343  || rename(ep->tmp_path, ep->final_path) < 0) {
344  saved_errno = errno;
345  edit_file_cleanup(ep);
346  errno = saved_errno;
347  return (VSTREAM_EOF);
348  } else {
349  (void) vstream_fclose(ep->tmp_fp);
350  EDIT_FILE_FREE(ep);
351  return (0);
352  }
353 }
#define VSTREAM_EOF
Definition: vstream.h:110
void edit_file_cleanup(EDIT_FILE *ep)
Definition: edit_file.c:306
#define stat(p, s)
Definition: warn_stat.h:18
#define MYFLOCK_OP_EXCLUSIVE
Definition: myflock.h:30
VSTREAM * vstream_fopen(const char *path, int flags, mode_t mode)
Definition: vstream.c:1241
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
char * final_path
Definition: edit_file.h:24
#define EDIT_FILE_FREE(ep)
Definition: edit_file.c:182
int myflock(int fd, int lock_style, int operation)
Definition: myflock.c:87
int edit_file_close(EDIT_FILE *ep)
Definition: edit_file.c:320
#define EDIT_FILE_MODE
Definition: edit_file.c:168
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
mode_t final_mode
Definition: edit_file.h:25
#define EDIT_FILE_REUSE_AFTER_CRASH
Definition: edit_file.c:163
#define EDIT_FILE_ALLOC(ep, path, mode)
Definition: edit_file.c:174
#define FILE_PERM_MASK
Definition: edit_file.c:146
#define vstream_fileno(vp)
Definition: vstream.h:115
char * tmp_path
Definition: edit_file.h:27
EDIT_FILE * edit_file_open(const char *path, int flags, mode_t mode)
Definition: edit_file.c:190
VSTREAM * tmp_fp
Definition: edit_file.h:28
#define fstat(f, s)
Definition: warn_stat.h:20