m4-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: branch-1_4 allocation overflow


From: Eric Blake
Subject: Re: branch-1_4 allocation overflow
Date: Mon, 13 Nov 2006 23:22:18 +0000 (UTC)
User-agent: Loom/3.14 (http://gmane.org/)

Eric Blake <ebb9 <at> byu.net> writes:

> 
> $ build/src/m4
> include(examples/forloop.m4)dnl
> forloop(i,1,2200,`divert(i)divnum
> ')
> build/src/m4:stdin:2: cannot create temporary file for diversion: Too many 
open 
> files
> Aborted (core dumped)

For that matter, the arbitrary limit of EMFILE can be worked around on the 
branch:

2006-11-13  Eric Blake  <address@hidden>

        * src/output.c (cleanup_tmpfile): Avoid double error message when
        umask is prohibitive.
        (m4_tmpname, m4_tmpopen, m4_tmpclose, m4_tmpremove): New
        functions.
        (m4_tmpfile): Add parameter, move cloexec action here.
        (make_room_for): Adjust caller.  Don't keep too many files open.
        (insert_diversion_helper): Unlink emptied temp files.
        (make_diversion): Don't keep too many files open.
        * doc/m4.texinfo (Diversions): Tweak wording, now that open file
        descriptors are no longer a limiting factor.
        * NEWS: Document this change.

Index: NEWS
===================================================================
RCS file: /sources/m4/m4/NEWS,v
retrieving revision 1.1.1.1.2.81
diff -u -r1.1.1.1.2.81 NEWS
--- NEWS        13 Nov 2006 19:10:56 -0000      1.1.1.1.2.81
+++ NEWS        13 Nov 2006 23:19:20 -0000
@@ -8,7 +8,8 @@
   cause a core dump when handed extra large values.  Also, `divert' now
   uses memory proportional to the number of diversions in use, rather than
   to the maximum diversion number encountered, so that large diversion
-  numbers are less likely to exhaust system memory.
+  numbers are less likely to exhaust system memory; and is no longer
+  limited by the maximum number of file descriptors.
 * The `--help' and `--version' command line options now consistently
   override all earlier options.  For example, `m4 --debugfile=trace
   --help' now no longer accidentally creates an empty file `trace'.
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.1.1.1.2.101
diff -u -r1.1.1.1.2.101 m4.texinfo
--- doc/m4.texinfo      13 Nov 2006 19:10:56 -0000      1.1.1.1.2.101
+++ doc/m4.texinfo      13 Nov 2006 23:19:20 -0000
@@ -3584,8 +3584,8 @@
 diversion still in memory, freeing this memory for other diversions.
 When creating the temporary file, @code{m4} honors the value of the
 environment variable @env{TMPDIR}, and falls back to @file{/tmp}.
-So, it is theoretically possible that the number of diversions be
-limited by the number of available file descriptors.
+So, it is theoretically possible that the number and aggregate size of
+diversions is limited only by available disk space.
 
 @ignore
 @comment We need to test spilled diversions, but don't need to expose
Index: src/output.c
===================================================================
RCS file: /sources/m4/m4/src/Attic/output.c,v
retrieving revision 1.1.1.1.2.16
diff -u -r1.1.1.1.2.16 output.c
--- src/output.c        13 Nov 2006 19:10:57 -0000      1.1.1.1.2.16
+++ src/output.c        13 Nov 2006 23:19:20 -0000
@@ -21,6 +21,7 @@
 
 #include "m4.h"
 
+#include <limits.h>
 #include <sys/stat.h>
 
 #include "gl_avltree_oset.h"
@@ -47,8 +48,9 @@
 /* When part of diversion_table, each struct m4_diversion either
    represents an open file (zero size, non-NULL u.file), an in-memory
    buffer (non-zero size, non-NULL u.buffer), or an unused placeholder
-   diversion (zero size, u is NULL).  When not part of
-   diversion_table, u.next is a pointer to the free_list chain.  */
+   diversion (zero size, u is NULL, non-zero used indicates that a
+   file has been created).  When not part of diversion_table, u.next
+   is a pointer to the free_list chain.  */
 
 typedef struct m4_diversion m4_diversion;
 
@@ -180,16 +182,32 @@
     _exit (exit_failure);
 }
 
-/* Create a temporary file open for reading and writing in a secure
-   temp directory.  The file will be automatically closed and deleted
-   on a fatal signal.  When done with the file, close it with
-   close_stream_temp.  Exits on failure, so the return value is always
-   an open file.  */
+/* Convert DIVNUM into a temporary file name for use in m4_tmp*.  */
+static const char *
+m4_tmpname (int divnum)
+{
+  static char *buffer;
+  static char *tail;
+  if (buffer == NULL)
+    {
+      tail = xasprintf ("%s/m4-%d", output_temp_dir->dir_name, INT_MAX);
+      buffer = obstack_copy0 (&diversion_storage, tail, strlen (tail));
+      tail = strrchr (buffer, '-') + 1;
+    }
+  sprintf (tail, "%d", divnum);
+  return buffer;
+}
+
+/* Create a temporary file for diversion DIVNUM open for reading and
+   writing in a secure temp directory.  The file will be automatically
+   closed and deleted on a fatal signal.  The file can be closed and
+   reopened with m4_tmpclose and m4_tmpopen; when finally done with
+   the file, close it with m4_tmpremove.  Exits on failure, so the
+   return value is always an open file.  */
 static FILE *
-m4_tmpfile (void)
+m4_tmpfile (int divnum)
 {
-  static unsigned int count;
-  char *name;
+  const char *name;
   FILE *file;
 
   if (output_temp_dir == NULL)
@@ -201,17 +219,56 @@
                  "cannot create temporary file for diversion"));
       atexit (cleanup_tmpfile);
     }
-  name = xasprintf ("%s/m4-%d", output_temp_dir->dir_name, count++);
+  name = m4_tmpname (divnum);
   register_temp_file (output_temp_dir, name);
   errno = 0;
   file = fopen_temp (name, O_BINARY ? "wb+" : "w+");
   if (file == NULL)
+    {
+      unregister_temp_file (output_temp_dir, name);
+      M4ERROR ((EXIT_FAILURE, errno,
+               "cannot create temporary file for diversion"));
+    }
+  else if (set_cloexec_flag (fileno (file), true) != 0)
+    M4ERROR ((warning_status, errno,
+             "Warning: cannot protect diversion across forks"));
+  return file;
+}
+
+/* Reopen a temporary file for diversion DIVNUM for reading and
+   writing in a secure temp directory.  Exits on failure, so the
+   return value is always an open file.  */
+static FILE *
+m4_tmpopen (int divnum)
+{
+  const char *name = m4_tmpname (divnum);
+  FILE *file;
+
+  errno = 0;
+  file = fopen_temp (name, O_BINARY ? "ab+" : "a+");
+  if (file == NULL)
     M4ERROR ((EXIT_FAILURE, errno,
              "cannot create temporary file for diversion"));
-  free (name);
+  else if (set_cloexec_flag (fileno (file), true) != 0)
+    M4ERROR ((warning_status, errno,
+             "Warning: cannot protect diversion across forks"));
   return file;
 }
 
+/* Close, but don't delete, a temporary FILE.  */
+static int
+m4_tmpclose (FILE *file)
+{
+  return close_stream_temp (file);
+}
+
+/* Delete a closed temporary FILE for diversion DIVNUM.  */
+static int
+m4_tmpremove (int divnum)
+{
+  return cleanup_temp_file (output_temp_dir, m4_tmpname (divnum));
+}
+
 /*-----------------------------------------------------------------------.
 | Reorganize in-memory diversion buffers so the current diversion can   |
 | accomodate LENGTH more characters without further reorganization.  The |
@@ -280,11 +337,7 @@
       total_buffer_size -= selected_diversion->size;
       selected_diversion->size = 0;
       selected_diversion->u.file = NULL;
-      selected_diversion->u.file = m4_tmpfile ();
-
-      if (set_cloexec_flag (fileno (selected_diversion->u.file), true) != 0)
-       M4ERROR ((warning_status, errno,
-                 "Warning: cannot protect diversion across forks"));
+      selected_diversion->u.file = m4_tmpfile (selected_diversion->divnum);
 
       if (selected_diversion->used > 0)
        {
@@ -298,7 +351,7 @@
       /* Reclaim the buffer space for other diversions.  */
 
       free (selected_buffer);
-      selected_diversion->used = 0;
+      selected_diversion->used = 1;
     }
 
   /* Reload output_file, just in case the flushed diversion was current.  */
@@ -313,8 +366,16 @@
     }
   else
     {
-      /* The buffer may be safely reallocated.  */
+      /* Close any selected file since it is not the current diversion.  */
+      if (selected_diversion)
+       {
+         FILE *file = selected_diversion->u.file;
+         selected_diversion->u.file = 0;
+         if (m4_tmpclose (file) != 0)
+           M4ERROR ((0, errno, "cannot close temporary file for diversion"));
+       }
 
+      /* The current buffer may be safely reallocated.  */
       output_diversion->u.buffer
        = xrealloc (output_diversion->u.buffer, (size_t) wanted_size);
 
@@ -501,10 +562,18 @@
          if (!gl_oset_remove (diversion_table, output_diversion))
            error (EXIT_FAILURE, 0, "INTERNAL ERROR: make_diversion failed");
          output_diversion->u.next = free_list;
+         output_diversion->used = 0;
          free_list = output_diversion;
        }
-      else
+      else if (output_diversion->size)
        output_diversion->used = output_diversion->size - output_unused;
+      else if (output_diversion->used)
+       {
+         FILE *file = output_diversion->u.file;
+         output_diversion->u.file = NULL;
+         if (m4_tmpclose (file) != 0)
+           M4ERROR ((0, errno, "cannot close temporary file for diversion"));
+       }
       output_diversion = NULL;
       output_file = NULL;
       output_cursor = NULL;
@@ -556,7 +625,11 @@
       output_unused = output_diversion->size - output_diversion->used;
     }
   else
-    output_file = output_diversion->u.file;
+    {
+      if (!output_diversion->u.file && output_diversion->used)
+       output_diversion->u.file = m4_tmpopen (output_diversion->divnum);
+      output_file = output_diversion->u.file;
+    }
   output_current_line = -1;
 }
 
@@ -605,10 +678,15 @@
     {
       if (diversion->size)
        output_text (diversion->u.buffer, diversion->used);
-      else if (diversion->u.file)
+      else
        {
-         rewind (diversion->u.file);
-         insert_file (diversion->u.file);
+         if (!diversion->u.file && diversion->used)
+           diversion->u.file = m4_tmpopen (diversion->divnum);
+         if (diversion->u.file)
+           {
+             rewind (diversion->u.file);
+             insert_file (diversion->u.file);
+           }
        }
 
       output_current_line = -1;
@@ -621,8 +699,19 @@
       diversion->size = 0;
       diversion->used = 0;
     }
-  else if (diversion->u.file && close_stream_temp (diversion->u.file) != 0)
-    M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
+  else
+    {
+      if (diversion->u.file)
+       {
+         FILE *file = diversion->u.file;
+         diversion->u.file = NULL;
+         diversion->used = 0;
+         if (m4_tmpclose (file) != 0)
+           M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
+       }
+      if (m4_tmpremove (diversion->divnum) != 0)
+       M4ERROR ((0, errno, "cannot clean temporary file for diversion"));
+    }
   gl_oset_remove (diversion_table, diversion);
   diversion->u.next = free_list;
   free_list = diversion;








reply via email to

[Prev in Thread] Current Thread [Next in Thread]