Home | History | Annotate | Line # | Download | only in cleanup
      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 
     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((void *) 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