Postfix3.3.1
cleanup_region.c
[詳解]
1 /*++
2 /* NAME
3 /* cleanup_region 3
4 /* SUMMARY
5 /* queue file region manager
6 /* SYNOPSIS
7 /* #include "cleanup.h"
8 /*
9 /* void cleanup_region_init(state)
10 /* CLEANUP_STATE *state;
11 /*
12 /* CLEANUP_REGION *cleanup_region_open(state, space_needed)
13 /* CLEANUP_STATE *state;
14 /* ssize_t space_needed;
15 /*
16 /* int cleanup_region_close(state, rp)
17 /* CLEANUP_STATE *state;
18 /* CLEANUP_REGION *rp;
19 /*
20 /* CLEANUP_REGION *cleanup_region_return(state, rp)
21 /* CLEANUP_STATE *state;
22 /* CLEANUP_REGION *rp;
23 /*
24 /* void cleanup_region_done(state)
25 /* CLEANUP_STATE *state;
26 /* DESCRIPTION
27 /* This module maintains queue file regions. Regions are created
28 /* on-the-fly and can be reused multiple times. Each region
29 /* structure consists of a file offset, a length (0 for an
30 /* open-ended region at the end of the file), a write offset
31 /* (maintained by the caller), and list linkage. Region
32 /* boundaries are not enforced by this module. It is up to the
33 /* caller to ensure that they stay within bounds.
34 /*
35 /* cleanup_region_init() performs mandatory initialization and
36 /* overlays an initial region structure over an already existing
37 /* queue file. This function must not be called before the
38 /* queue file is complete.
39 /*
40 /* cleanup_region_open() opens an existing region or creates
41 /* a new region that can accommodate at least the specified
42 /* amount of space. A new region is an open-ended region at
43 /* the end of the file; it must be closed (see next) before
44 /* unrelated data can be appended to the same file.
45 /*
46 /* cleanup_region_close() indicates that a region will not be
47 /* updated further. With an open-ended region, the region's
48 /* end is frozen just before the caller-maintained write offset.
49 /* With a close-ended region, unused space (beginning at the
50 /* caller-maintained write offset) may be returned to the free
51 /* pool.
52 /*
53 /* cleanup_region_return() returns a list of regions to the
54 /* free pool, and returns a null pointer. To avoid fragmentation,
55 /* adjacent free regions may be coalesced together.
56 /*
57 /* cleanup_region_done() destroys all in-memory information
58 /* that was allocated for administering queue file regions.
59 /*
60 /* Arguments:
61 /* .IP state
62 /* Queue file and message processing state. This state is
63 /* updated as records are processed and as errors happen.
64 /* .IP space_needed
65 /* The minimum region size needed.
66 /* LICENSE
67 /* .ad
68 /* .fi
69 /* The Secure Mailer license must be distributed with this software.
70 /* AUTHOR(S)
71 /* Wietse Venema
72 /* IBM T.J. Watson Research
73 /* P.O. Box 704
74 /* Yorktown Heights, NY 10598, USA
75 /*--*/
76 
77 /* System library. */
78 
79 #include <sys_defs.h>
80 #include <sys/stat.h>
81 
82 /* Utility library. */
83 
84 #include <msg.h>
85 #include <mymalloc.h>
86 #include <warn_stat.h>
87 
88 /* Application-specific. */
89 
90 #include <cleanup.h>
91 
92 /* cleanup_region_alloc - create queue file region */
93 
94 static CLEANUP_REGION *cleanup_region_alloc(off_t start, off_t len)
95 {
96  CLEANUP_REGION *rp;
97 
98  rp = (CLEANUP_REGION *) mymalloc(sizeof(*rp));
99  rp->write_offs = rp->start = start;
100  rp->len = len;
101  rp->next = 0;
102 
103  return (rp);
104 }
105 
106 /* cleanup_region_free - destroy region list */
107 
108 static CLEANUP_REGION *cleanup_region_free(CLEANUP_REGION *regions)
109 {
110  CLEANUP_REGION *rp;
111  CLEANUP_REGION *next;
112 
113  for (rp = regions; rp != 0; rp = next) {
114  next = rp->next;
115  myfree((void *) rp);
116  }
117  return (0);
118 }
119 
120 /* cleanup_region_init - create initial region overlay */
121 
123 {
124  const char *myname = "cleanup_region_init";
125 
126  /*
127  * Sanity check.
128  */
129  if (state->free_regions != 0 || state->body_regions != 0)
130  msg_panic("%s: repeated call", myname);
131 
132  /*
133  * Craft the first regions on the fly, from circumstantial evidence.
134  */
135  state->body_regions =
136  cleanup_region_alloc(state->append_hdr_pt_target,
137  state->xtra_offset - state->append_hdr_pt_target);
138  if (msg_verbose)
139  msg_info("%s: body start %ld len %ld",
140  myname, (long) state->body_regions->start, (long) state->body_regions->len);
141 }
142 
143 /* cleanup_region_open - open existing region or create new region */
144 
146 {
147  const char *myname = "cleanup_region_open";
148  CLEANUP_REGION **rpp;
149  CLEANUP_REGION *rp;
150  struct stat st;
151 
152  /*
153  * Find the first region that is large enough, or create a new region.
154  */
155  for (rpp = &state->free_regions; /* see below */ ; rpp = &(rp->next)) {
156 
157  /*
158  * Create an open-ended region at the end of the queue file. We
159  * freeze the region size after we stop writing to it. XXX Assume
160  * that fstat() returns a file size that is never less than the file
161  * append offset. It is not a problem if fstat() returns a larger
162  * result; we would just waste some space.
163  */
164  if ((rp = *rpp) == 0) {
165  if (fstat(vstream_fileno(state->dst), &st) < 0)
166  msg_fatal("%s: fstat file %s: %m", myname, cleanup_path);
167  rp = cleanup_region_alloc(st.st_size, 0);
168  break;
169  }
170 
171  /*
172  * Reuse an existing region.
173  */
174  if (rp->len >= len) {
175  (*rpp) = rp->next;
176  rp->next = 0;
177  rp->write_offs = rp->start;
178  break;
179  }
180 
181  /*
182  * Skip a too small region.
183  */
184  if (msg_verbose)
185  msg_info("%s: skip start %ld len %ld < %ld",
186  myname, (long) rp->start, (long) rp->len, (long) len);
187  }
188  if (msg_verbose)
189  msg_info("%s: done start %ld len %ld",
190  myname, (long) rp->start, (long) rp->len);
191  return (rp);
192 }
193 
194 /* cleanup_region_close - freeze queue file region size */
195 
197 {
198  const char *myname = "cleanup_region_close";
199 
200  /*
201  * If this region is still open ended, freeze the size. If this region is
202  * closed, some future version of this routine may shrink the size and
203  * return the unused portion to the free pool.
204  */
205  if (rp->len == 0)
206  rp->len = rp->write_offs - rp->start;
207  if (msg_verbose)
208  msg_info("%s: freeze start %ld len %ld",
209  myname, (long) rp->start, (long) rp->len);
210 }
211 
212 /* cleanup_region_return - return region list to free pool */
213 
215 {
216  CLEANUP_REGION **rpp;
217 
218  for (rpp = &state->free_regions; (*rpp) != 0; rpp = &(*rpp)->next)
219  /* void */ ;
220  *rpp = rp;
221  return (0);
222 }
223 
224 /* cleanup_region_done - destroy region metadata */
225 
227 {
228  if (state->free_regions != 0)
229  state->free_regions = cleanup_region_free(state->free_regions);
230  if (state->body_regions != 0)
231  state->body_regions = cleanup_region_free(state->body_regions);
232 }
int msg_verbose
Definition: msg.c:177
void myfree(void *ptr)
Definition: mymalloc.c:207
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
off_t start
Definition: cleanup.h:335
#define stat(p, s)
Definition: warn_stat.h:18
char * cleanup_path
Definition: cleanup_init.c:111
off_t xtra_offset
Definition: cleanup.h:78
void cleanup_region_init(CLEANUP_STATE *state)
void cleanup_region_close(CLEANUP_STATE *unused_state, CLEANUP_REGION *rp)
struct CLEANUP_REGION * body_regions
Definition: cleanup.h:128
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
void cleanup_region_done(CLEANUP_STATE *state)
VSTREAM * dst
Definition: cleanup.h:53
CLEANUP_REGION * cleanup_region_open(CLEANUP_STATE *state, ssize_t len)
#define vstream_fileno(vp)
Definition: vstream.h:115
struct CLEANUP_REGION * free_regions
Definition: cleanup.h:127
off_t append_hdr_pt_target
Definition: cleanup.h:85
CLEANUP_REGION * cleanup_region_return(CLEANUP_STATE *state, CLEANUP_REGION *rp)
struct CLEANUP_REGION * next
Definition: cleanup.h:338
off_t write_offs
Definition: cleanup.h:337
#define fstat(f, s)
Definition: warn_stat.h:20
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
void msg_info(const char *fmt,...)
Definition: msg.c:199