]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[dhcp] Allow use of custom reallocation functions for DHCP option blocks
authorMichael Brown <mcb30@ipxe.org>
Mon, 10 Jan 2011 23:58:11 +0000 (23:58 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 11 Jan 2011 21:24:40 +0000 (21:24 +0000)
Allow functions other than realloc() to be used to reallocate DHCP
option block data, and specify the reallocation function at the time
of calling dhcpopt_init().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/core/nvo.c
src/include/ipxe/dhcpopts.h
src/net/dhcpopts.c
src/net/dhcppkt.c

index 1a886c0f865f5f4bf34c965b1208a8bb324d4d76..9b325db759d8c553352604a5d69d07265d4dc1e3 100644 (file)
@@ -133,7 +133,8 @@ static void nvo_init_dhcpopts ( struct nvo_block *nvo ) {
                memset ( nvo->data, 0, nvo->total_len );
        }
 
-       dhcpopt_init ( &nvo->dhcpopts, options_data, options_len );
+       dhcpopt_init ( &nvo->dhcpopts, options_data, options_len,
+                      dhcpopt_no_realloc );
 }
 
 /**
index d88036e3aca17c3b469c5be973159af2f4727166..fe07d903d3cd836ad4bbcc86294049008d9d5483 100644 (file)
@@ -19,16 +19,23 @@ struct dhcp_options {
        size_t used_len;
        /** Option block allocated length */
        size_t alloc_len;
+       /** Reallocate option block raw data
+        *
+        * @v options           DHCP option block
+        * @v len               New length
+        * @ret rc              Return status code
+        */
+       int ( * realloc ) ( struct dhcp_options *options, size_t len );
 };
 
 extern int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
                           const void *data, size_t len );
-extern int dhcpopt_extensible_store ( struct dhcp_options *options,
-                                     unsigned int tag,
-                                     const void *data, size_t len );
 extern int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
                           void *data, size_t len );
 extern void dhcpopt_init ( struct dhcp_options *options,
-                          void *data, size_t alloc_len );
+                          void *data, size_t alloc_len,
+                          int ( * realloc ) ( struct dhcp_options *options,
+                                              size_t len ) );
+extern int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len );
 
 #endif /* _IPXE_DHCPOPTS_H */
index 214a8244ce6a6a359b1961835702d2702bd431d4..d1330eae50d319901317a4c35cfe8e784c02eaeb 100644 (file)
@@ -169,6 +169,17 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options,
        return -ENOENT;
 }
 
+/**
+ * Refuse to reallocate DHCP option block
+ *
+ * @v options          DHCP option block
+ * @v len              New length
+ * @ret rc             Return status code
+ */
+int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len ) {
+       return ( ( len <= options->alloc_len ) ? 0 : -ENOSPC );
+}
+
 /**
  * Resize a DHCP option
  *
@@ -177,46 +188,44 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options,
  * @v encap_offset     Offset of encapsulating offset (or -ve for none)
  * @v old_len          Old length (including header)
  * @v new_len          New length (including header)
- * @v can_realloc      Can reallocate options data if necessary
  * @ret rc             Return status code
  */
 static int resize_dhcp_option ( struct dhcp_options *options,
                                int offset, int encap_offset,
-                               size_t old_len, size_t new_len,
-                               int can_realloc ) {
+                               size_t old_len, size_t new_len ) {
        struct dhcp_option *encapsulator;
        struct dhcp_option *option;
        ssize_t delta = ( new_len - old_len );
-       size_t new_options_len;
+       size_t old_alloc_len;
+       size_t new_used_len;
        size_t new_encapsulator_len;
-       void *new_data;
        void *source;
        void *dest;
        void *end;
+       int rc;
 
-       /* Check for sufficient space, and update length fields */
+       /* Check for sufficient space */
        if ( new_len > DHCP_MAX_LEN ) {
                DBGC ( options, "DHCPOPT %p overlength option\n", options );
                return -ENOSPC;
        }
-       new_options_len = ( options->used_len + delta );
-       if ( new_options_len > options->alloc_len ) {
-               /* Reallocate options block if allowed to do so. */
-               if ( can_realloc ) {
-                       new_data = realloc ( options->data, new_options_len );
-                       if ( ! new_data ) {
-                               DBGC ( options, "DHCPOPT %p could not "
-                                      "reallocate to %zd bytes\n", options,
-                                      new_options_len );
-                               return -ENOMEM;
-                       }
-                       options->data = new_data;
-                       options->alloc_len = new_options_len;
-               } else {
-                       DBGC ( options, "DHCPOPT %p out of space\n", options );
-                       return -ENOMEM;
+       new_used_len = ( options->used_len + delta );
+
+       /* Expand options block, if necessary */
+       if ( new_used_len > options->alloc_len ) {
+               /* Reallocate options block */
+               old_alloc_len = options->alloc_len;
+               if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){
+                       DBGC ( options, "DHCPOPT %p could not reallocate to "
+                              "%zd bytes\n", options, new_used_len );
+                       return rc;
                }
+               /* Clear newly allocated space */
+               memset ( ( options->data + old_alloc_len ), 0,
+                        ( options->alloc_len - old_alloc_len ) );
        }
+
+       /* Update encapsulator, if applicable */
        if ( encap_offset >= 0 ) {
                encapsulator = dhcp_option ( options, encap_offset );
                new_encapsulator_len = ( encapsulator->len + delta );
@@ -227,7 +236,9 @@ static int resize_dhcp_option ( struct dhcp_options *options,
                }
                encapsulator->len = new_encapsulator_len;
        }
-       options->used_len = new_options_len;
+
+       /* Update used length */
+       options->used_len = new_used_len;
 
        /* Move remainder of option data */
        option = dhcp_option ( options, offset );
@@ -236,6 +247,15 @@ static int resize_dhcp_option ( struct dhcp_options *options,
        end = ( options->data + options->alloc_len );
        memmove ( dest, source, ( end - dest ) );
 
+       /* Shrink options block, if applicable */
+       if ( new_used_len < options->alloc_len ) {
+               if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){
+                       DBGC ( options, "DHCPOPT %p could not reallocate to "
+                              "%zd bytes\n", options, new_used_len );
+                       return rc;
+               }
+       }
+
        return 0;
 }
 
@@ -246,7 +266,6 @@ static int resize_dhcp_option ( struct dhcp_options *options,
  * @v tag              DHCP option tag
  * @v data             New value for DHCP option
  * @v len              Length of value, in bytes
- * @v can_realloc      Can reallocate options data if necessary
  * @ret offset         Offset of DHCP option, or negative error
  *
  * Sets the value of a DHCP option within the options block.  The
@@ -258,9 +277,8 @@ static int resize_dhcp_option ( struct dhcp_options *options,
  * be left with its original value.
  */
 static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
-                            const void *data, size_t len,
-                            int can_realloc ) {
-       static const uint8_t empty_encapsulator[] = { DHCP_END };
+                            const void *data, size_t len ) {
+       static const uint8_t empty_encap[] = { DHCP_END };
        int offset;
        int encap_offset = -1;
        int creation_offset;
@@ -291,10 +309,12 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
 
        /* Ensure that encapsulator exists, if required */
        if ( encap_tag ) {
-               if ( encap_offset < 0 )
-                       encap_offset = set_dhcp_option ( options, encap_tag,
-                                                        empty_encapsulator, 1,
-                                                        can_realloc );
+               if ( encap_offset < 0 ) {
+                       encap_offset =
+                               set_dhcp_option ( options, encap_tag,
+                                                 empty_encap,
+                                                 sizeof ( empty_encap ) );
+               }
                if ( encap_offset < 0 )
                        return encap_offset;
                creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN );
@@ -306,8 +326,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
 
        /* Resize option to fit new data */
        if ( ( rc = resize_dhcp_option ( options, offset, encap_offset,
-                                        old_len, new_len,
-                                        can_realloc ) ) != 0 )
+                                        old_len, new_len ) ) != 0 )
                return rc;
 
        /* Copy new data into option, if applicable */
@@ -322,7 +341,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
        if ( encap_offset >= 0 ) {
                option = dhcp_option ( options, encap_offset );
                if ( option->len <= 1 )
-                       set_dhcp_option ( options, encap_tag, NULL, 0, 0 );
+                       set_dhcp_option ( options, encap_tag, NULL, 0 );
        }
 
        return offset;
@@ -341,26 +360,7 @@ int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
                    const void *data, size_t len ) {
        int offset;
 
-       offset = set_dhcp_option ( options, tag, data, len, 0 );
-       if ( offset < 0 )
-               return offset;
-       return 0;
-}
-
-/**
- * Store value of DHCP option setting, extending options block if necessary
- *
- * @v options          DHCP option block
- * @v tag              Setting tag number
- * @v data             Setting data, or NULL to clear setting
- * @v len              Length of setting data
- * @ret rc             Return status code
- */
-int dhcpopt_extensible_store ( struct dhcp_options *options, unsigned int tag,
-                              const void *data, size_t len ) {
-       int offset;
-
-       offset = set_dhcp_option ( options, tag, data, len, 1 );
+       offset = set_dhcp_option ( options, tag, data, len );
        if ( offset < 0 )
                return offset;
        return 0;
@@ -428,16 +428,19 @@ static void dhcpopt_update_used_len ( struct dhcp_options *options ) {
  * @v options          Uninitialised DHCP option block
  * @v data             Memory for DHCP option data
  * @v alloc_len                Length of memory for DHCP option data
+ * @v realloc          DHCP option block reallocator
  *
  * The memory content must already be filled with valid DHCP options.
  * A zeroed block counts as a block of valid DHCP options.
  */
-void dhcpopt_init ( struct dhcp_options *options, void *data,
-                   size_t alloc_len ) {
+void dhcpopt_init ( struct dhcp_options *options, void *data, size_t alloc_len,
+                   int ( * realloc ) ( struct dhcp_options *options,
+                                       size_t len ) ) {
 
        /* Fill in fields */
        options->data = data;
        options->alloc_len = alloc_len;
+       options->realloc = realloc;
 
        /* Update length */
        dhcpopt_update_used_len ( options );
index e043bb5d4a601417ff62cda3e52a22d06b5d5206..237c3e2cf609b1da3e5ebcae29257d16e85d9133 100644 (file)
@@ -267,7 +267,8 @@ void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data,
        ref_init ( &dhcppkt->refcnt, NULL );
        dhcppkt->dhcphdr = data;
        dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
-                      ( len - offsetof ( struct dhcphdr, options ) ) );
+                      ( len - offsetof ( struct dhcphdr, options ) ),
+                      dhcpopt_no_realloc );
        settings_init ( &dhcppkt->settings,
                        &dhcppkt_settings_operations, &dhcppkt->refcnt, 0 );
 }