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