aboutsummaryrefslogtreecommitdiffstats
path: root/wsutil/tempfile.c
blob: de5314843ba2e14642e5f2f46d773b7152dbc224 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/* tempfile.c
 * Routines to create temporary files
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include <string.h>
#include <stdlib.h>
#include <errno.h>

#ifdef _WIN32
#include <windows.h>
#include <process.h>    /* For getpid() */
#endif

#include "tempfile.h"
#include <wsutil/file_util.h>

#ifndef __set_errno
#define __set_errno(x) errno=(x)
#endif

#define INITIAL_PATH_SIZE   128
#define TMP_FILE_SUFFIX     "XXXXXX"

#ifndef HAVE_MKSTEMPS
/* Generate a unique temporary file name from TEMPLATE.
   The last six characters before the suffix length of TEMPLATE
   must be TMP_FILE_SUFFIX; they are replaced with a string that
   makes the filename unique.
   Returns a file descriptor open on the file for reading and writing.  */
static int
mkstemps(char *path_template, int suffixlen)
{
  static const char letters[]
    = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  char uniqueness[6];
  size_t len;
  size_t i;

  len = strlen (path_template);
  if (len < 6 || strncmp (&path_template[len - 6 - suffixlen], TMP_FILE_SUFFIX, 6))
    {
      __set_errno (EINVAL);
      return -1;
    }

  if (g_snprintf (uniqueness, 6, "%.5u",
                  (unsigned int) ws_getpid () % 100000) != 5)
    /* Inconceivable lossage.  */
    return -1;

  memcpy(&path_template[len - 5 - suffixlen], uniqueness, 5);

  for (i = 0; i < sizeof (letters); ++i)
    {
      int fd;

      path_template[len - 6 - suffixlen] = letters[i];

      fd = ws_open (path_template, O_RDWR|O_BINARY|O_CREAT|O_EXCL, 0600);
      if (fd >= 0)
        return fd;
    }

  /* We return the null string if we can't find a unique file name.  */

  path_template[0] = '\0';
  return -1;
}

#endif /* HAVE_MKSTEMPS */

/*
 * Construct and return the path name of a file in the
 * appropriate temporary file directory.
 */
char *get_tempfile_path(const char *filename)
{
  return g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", g_get_tmp_dir(), filename);
}

#define MAX_TEMPFILES   3

/**
 * Create a tempfile with the given prefix (e.g. "wireshark").
 *
 * @param namebuf If not NULL, receives the full path of the temp file.
 *                Should NOT be freed.
 * @param pfx A prefix for the temporary file.
 * @param sfx [in] A file extension for the temporary file. NULL can be passed
 *                 if no file extension is needed
 * @return The file descriptor of the new tempfile, from mkstemps().
 */
int
create_tempfile(char **namebuf, const char *pfx, const char *sfx)
{
  static struct _tf {
    char *path;
    size_t len;
  } tf[MAX_TEMPFILES];
  static int idx;

  const char *tmp_dir;
  int old_umask;
  int fd;
  time_t current_time;
  struct tm *tm;
  char timestr[14 + 1];
  gchar *tmp_file;
  gchar *safe_pfx;
  gchar sep[2] = {0, 0};

  /* The characters in "delimiters" come from:
   * http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx.
   * Add to the list as necessary for other OS's.
   */
  const gchar *delimiters = "<>:\"/\\|?*"
    "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
    "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
    "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";

  /* Sanitize the pfx to resolve bug 7877 */
  safe_pfx = g_strdup(pfx);
  safe_pfx = g_strdelimit(safe_pfx, delimiters, '-');

  idx = (idx + 1) % MAX_TEMPFILES;

  /*
   * Allocate the buffer if it's not already allocated.
   */
  if (tf[idx].path == NULL) {
    tf[idx].len = INITIAL_PATH_SIZE;
    tf[idx].path = (char *)g_malloc(tf[idx].len);
  }

  tmp_dir = g_get_tmp_dir();

#ifdef _WIN32
  _tzset();
#endif
  current_time = time(NULL);
  tm = localtime(&current_time);
  if (tm != NULL)
    strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", tm);
  else
    g_strlcpy(timestr, "196912312359", sizeof(timestr)); /* second before the Epoch */
  sep[0] = G_DIR_SEPARATOR;
  tmp_file = g_strconcat(tmp_dir, sep, safe_pfx, "_", timestr, "_", TMP_FILE_SUFFIX, sfx, NULL);
  g_free(safe_pfx);
  if (strlen(tmp_file) > tf[idx].len) {
    tf[idx].len = strlen(tmp_file) + 1;
    tf[idx].path = (char *)g_realloc(tf[idx].path, tf[idx].len);
  }
  g_strlcpy(tf[idx].path, tmp_file, tf[idx].len);
  g_free(tmp_file);

  if (namebuf) {
    *namebuf = tf[idx].path;
  }
  /* The Single UNIX Specification doesn't say that "mkstemps()"
     creates the temporary file with mode rw-------, so we
     won't assume that all UNIXes will do so; instead, we set
     the umask to 0077 to take away all group and other
     permissions, attempt to create the file, and then put
     the umask back. */
  old_umask = ws_umask(0077);
  fd = mkstemps(tf[idx].path, sfx ? (int) strlen(sfx) : 0);
  ws_umask(old_umask);
  return fd;
}

/*
 * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
 *
 * Local Variables:
 * c-basic-offset: 2
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * ex: set shiftwidth=2 tabstop=8 expandtab:
 * :indentSize=2:tabSize=8:noTabs=true:
 */