1/* $NetBSD: cleanup_region.c,v 1.3 2020/03/18 19:05:15 christos 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 accommodate 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 96static 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 110static 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((void *) rp); 118 } 119 return (0); 120} 121 122/* cleanup_region_init - create initial region overlay */ 123 124void 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 147CLEANUP_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 198void 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 216CLEANUP_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 228void 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