Coverage Report

Created: 2026-09-06 07:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/tempfile.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2019 GraphicsMagick Group
3
%
4
% This program is covered by multiple licenses, which are described in
5
% Copyright.txt. You should have received a copy of Copyright.txt with this
6
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
7
%
8
% Temporary file allocation manager.
9
%
10
*/
11
12
#include "magick/studio.h"
13
#include "magick/error.h"
14
#include "magick/log.h"
15
#include "magick/random.h"
16
#include "magick/semaphore.h"
17
#include "magick/tempfile.h"
18
#include "magick/utility.h"
19

20
/*
21
  Define declarations.
22
*/
23
#if defined(S_IRUSR) && defined(S_IWUSR)
24
672k
#  define S_MODE     (S_IRUSR | S_IWUSR)
25
#elif defined (MSWINDOWS)
26
#  define S_MODE     (_S_IREAD | _S_IWRITE)
27
#else
28
# define S_MODE      0600
29
#endif
30
31
#if defined(MSWINDOWS)
32
# if defined(_P_tmpdir) && !defined(P_tmpdir)
33
#  define P_tmpdir _P_tmpdir
34
# endif
35
#endif
36

37
/*
38
  Type definitions
39
*/
40
typedef struct _TempfileInfo
41
{
42
  char
43
    filename[MaxTextExtent];
44
45
  struct _TempfileInfo
46
    *next;
47
} TempfileInfo;
48
49
static TempfileInfo
50
  *templist = 0;
51
52
static SemaphoreInfo
53
  *templist_semaphore = 0;
54
55
/*
56
  Add a temporary file to list
57
*/
58
static void AddTemporaryFileToList(const char *filename)
59
672k
{
60
672k
  (void) LogMagickEvent(TemporaryFileEvent,GetMagickModule(),
61
672k
    "Allocating temporary file \"%s\"",filename);
62
672k
  LockSemaphoreInfo(templist_semaphore);
63
672k
  {
64
672k
    TempfileInfo
65
672k
      *info;
66
67
672k
    info=MagickAllocateMemory(TempfileInfo *,sizeof(TempfileInfo));
68
672k
    if (info)
69
672k
      {
70
672k
        info->next=0;
71
672k
        (void) strlcpy(info->filename,filename,sizeof(info->filename));
72
672k
        if (!templist)
73
66.4k
          templist=info;
74
606k
        else
75
606k
          {
76
606k
            info->next=templist;
77
606k
            templist=info;
78
606k
          }
79
672k
      }
80
672k
  }
81
672k
  UnlockSemaphoreInfo(templist_semaphore);
82
672k
}
83
84
/*
85
  Remove a temporary file from the list.
86
  Return MagickPass if removed or MagickFail if not found
87
*/
88
static MagickPassFail RemoveTemporaryFileFromList(const char *filename)
89
347k
{
90
347k
  MagickPassFail
91
347k
    status=MagickFail;
92
93
347k
  (void) LogMagickEvent(TemporaryFileEvent,GetMagickModule(),
94
347k
    "Deallocating temporary file \"%s\"",filename);
95
96
347k
  LockSemaphoreInfo(templist_semaphore);
97
347k
  {
98
347k
    TempfileInfo
99
347k
      *current,
100
347k
      *previous=0;
101
102
52.4M
    for (current=templist; current; current=current->next)
103
52.4M
      {
104
52.4M
        if (strcmp(current->filename,filename) == 0)
105
338k
          {
106
338k
            if (previous)
107
13.9k
              previous->next=current->next;
108
324k
            else
109
324k
              templist=current->next;
110
338k
            MagickFreeMemory(current);
111
338k
            status=MagickPass;
112
338k
            break;
113
338k
          }
114
52.1M
        previous=current;
115
52.1M
      }
116
347k
  }
117
347k
  UnlockSemaphoreInfo(templist_semaphore);
118
347k
  return status;
119
347k
}
120
/*
121
  Compose a temporary file name based a template string provided by
122
  the user.  Any 'X' characters are replaced with characters randomly
123
  selected from a "safe" set of characters which are unlikely to
124
  be interpreted by a shell or program and cause confusion.
125
*/
126
static void ComposeTemporaryFileName(char *name)
127
672k
{
128
672k
  static const char SafeChars[]
129
672k
    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
130
131
672k
  char
132
672k
    *c;
133
134
672k
  assert(name != (char *) NULL);
135
136
6.05M
  for (c=name; *c; c++)
137
5.38M
    {
138
5.38M
      if (*c == 'X')
139
4.03M
        *c=SafeChars[MagickRandomInteger() % (sizeof(SafeChars)-1)];
140
5.38M
    }
141
672k
}
142
143
/*
144
  Validate a temporary directory path
145
*/
146
static MagickPassFail ValidateTemporaryFileDirectory(const char *tempdir)
147
672k
{
148
672k
  assert(tempdir != (char *) NULL);
149
150
672k
  if (tempdir[0] == '\0')
151
0
    return MagickFail;
152
672k
  if (access(tempdir,W_OK) != 0)
153
0
    return MagickFail;
154
672k
  return MagickPass;
155
672k
}
156
157

158
/*
159
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
160
%                                                                             %
161
%                                                                             %
162
%                                                                             %
163
+   A c q u i r e T e m p o r a r y F i l e N a m e                           %
164
%                                                                             %
165
%                                                                             %
166
%                                                                             %
167
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
168
%
169
%  AcquireTemporaryFileName replaces the contents of the string buffer pointed
170
%  to by filename with a unique file name.  MagickPass is returned if a file
171
%  name is allocated, or MagickFail is returned if there is a failure.
172
%  When the filename is allocated, a temporary file is created on disk with
173
%  zero length, and read/write permission by the user.
174
%  The allocated filename should be recovered via the LiberateTemporaryFile()
175
%  function once it is no longer required.
176
%
177
%  The format of the AcquireTemporaryFileName method is:
178
%
179
%      MagickPassFail AcquireTemporaryFileName(char *filename,
180
%                                              ExceptionInfo *exception)
181
%
182
%  A description of each parameter follows.
183
%
184
%   o status: MagickPass if a temporary file name is allocated and successfully
185
%             created on disk.  MagickFail if the temporary file name or file
186
%             creation fails.
187
%
188
%   o filename: Specifies a pointer to an array of characters.  The unique
189
%             file name is returned in this array.
190
%
191
*/
192
MagickExport MagickPassFail AcquireTemporaryFileName(char *filename)
193
163k
{
194
163k
  int
195
163k
    fd;
196
197
163k
  assert(filename != (char *) NULL);
198
163k
  if ((fd=AcquireTemporaryFileDescriptor(filename)) != -1)
199
163k
    {
200
163k
      (void) close(fd);
201
163k
      return (MagickPass);
202
163k
    }
203
0
  return (MagickFail);
204
163k
}
205

206
/*
207
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
208
%                                                                             %
209
%                                                                             %
210
%                                                                             %
211
+   A c q u i r e T e m p o r a r y F i l e D e s c r i p t o r               %
212
%                                                                             %
213
%                                                                             %
214
%                                                                             %
215
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
216
%
217
%  AcquireTemporaryFileDescriptor replaces the contents of the string buffer
218
%  pointed to by filename with a unique file name created on disk.  A
219
%  read/write file descriptor (from open()) is returned on success, or -1 is
220
%  returned on failure. The temporary file is created on disk with read/write
221
%  permission by the user. The allocated temporary file should be deallocated
222
%  via the LiberateTemporaryFile() function once it is no longer required.
223
%
224
%  The format of the AcquireTemporaryFileDescriptor method is:
225
%
226
%      int AcquireTemporaryFileDescriptor(char *filename)
227
%
228
%  A description of each parameter follows.
229
%
230
%   o status: The file descriptor for an open file on success, or -1
231
%             on failure.
232
%
233
%   o filename: Specifies a pointer to an array of characters with an
234
%             allocated length of at least MaxTextExtent.  The unique
235
%             file name associated with the descriptor is returned in
236
%             this array.
237
%
238
*/
239
MagickExport int AcquireTemporaryFileDescriptor(char *filename)
240
672k
{
241
672k
  static const char env_strings[][14] =
242
672k
    {
243
672k
      "MAGICK_TMPDIR"
244
672k
#if defined(POSIX)
245
672k
      ,"TMPDIR"
246
672k
#endif /* defined(POSIX) */
247
#if defined(MSWINDOWS) || defined(__CYGWIN__)
248
      ,"TMP"
249
      ,"TEMP"
250
#endif
251
672k
    };
252
253
254
672k
  char
255
672k
    tempdir[MaxTextExtent];
256
257
672k
  unsigned int
258
672k
    i;
259
260
672k
  int
261
672k
    fd=-1;
262
263
672k
  assert(filename != (char *) NULL);
264
672k
  filename[0]='\0';
265
672k
  tempdir[0]='\0';
266
267
2.01M
  for (i=0; i < ArraySize(env_strings); i++)
268
1.34M
    {
269
1.34M
      const char
270
1.34M
        *env;
271
272
1.34M
      if ((env=getenv(env_strings[i])) != NULL)
273
0
        {
274
0
          const size_t copy_len = sizeof(tempdir)-16;
275
0
          if (env_strings[i][0] == '\0')
276
0
            break;
277
0
          if (strlcpy(tempdir,env,copy_len) >= copy_len)
278
0
            tempdir[0]='\0';
279
0
          if ((tempdir[0] != '\0') &&
280
0
              !ValidateTemporaryFileDirectory(tempdir))
281
0
            tempdir[0]='\0';
282
0
          if (tempdir[0] != '\0')
283
0
            break;
284
0
        }
285
1.34M
    }
286
287
672k
#if defined(P_tmpdir)
288
672k
  if (tempdir[0] == '\0')
289
672k
    {
290
672k
      const size_t copy_len = sizeof(tempdir)-16;
291
672k
      if (strlcpy(tempdir,P_tmpdir,copy_len) >= copy_len)
292
0
        tempdir[0]='\0';
293
672k
      if ((tempdir[0] != '\0') &&
294
672k
          !ValidateTemporaryFileDirectory(tempdir))
295
0
        tempdir[0]='\0';
296
672k
    }
297
672k
#endif
298
299
672k
  if (tempdir[0] != '\0')
300
672k
    {
301
672k
      char
302
672k
        tempname[16];
303
304
672k
      int
305
672k
        tries=0;
306
307
672k
      for (tries=0; tries < 256; tries++)
308
672k
        {
309
672k
          (void) strlcpy(tempname,"gmXXXXXX",sizeof(tempname));
310
672k
          ComposeTemporaryFileName(tempname);
311
672k
          if (strlcpy(filename,tempdir,MaxTextExtent) >= MaxTextExtent)
312
0
            break;
313
672k
          if (filename[strlen(filename)-1] != DirectorySeparator[0])
314
672k
            if (strlcat(filename,DirectorySeparator,MaxTextExtent) >=
315
672k
                MaxTextExtent)
316
0
              break;
317
672k
          if (strlcat(filename,tempname,MaxTextExtent) >= MaxTextExtent)
318
0
            break;
319
672k
          fd=open(filename,O_RDWR | O_CREAT | O_BINARY | O_EXCL, S_MODE);
320
672k
          if (fd != -1)
321
672k
            {
322
672k
              AddTemporaryFileToList(filename);
323
672k
              return (fd);
324
672k
            }
325
672k
        }
326
672k
    }
327
328
0
  return fd;
329
672k
}
330

331
/*
332
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
333
%                                                                             %
334
%                                                                             %
335
%                                                                             %
336
+   A c q u i r e T e m p o r a r y F i l e S t r e a m                       %
337
%                                                                             %
338
%                                                                             %
339
%                                                                             %
340
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341
%
342
%  AcquireTemporaryFileStream replaces the contents of the string buffer
343
%  pointed to by filename with a unique file name.  A pointer to an open
344
%  stdio FILE stream is returned on success, or a null pointer is returned
345
%  on failure. The temporary file is created on disk with read/write
346
%  permission by the user. The allocated temporary file should be deallocated
347
%  via the LiberateTemporaryFile() function once it is no longer required.
348
%
349
%  The format of the AcquireTemporaryFile method is:
350
%
351
%      FILE *AcquireTemporaryFileStream(char *filename,FileIOMode mode)
352
%
353
%  A description of each parameter follows.
354
%
355
%   o status: The file descriptor for an open file on success, or -1
356
%             on failure.
357
%
358
%   o filename: Specifies a pointer to an array of characters.  The unique
359
%             file name is returned in this array.
360
%
361
%   o mode:  Set to TextFileIOMode to open the file in text mode, otherwise
362
%            the file is opened in binary mode.
363
%
364
*/
365
MagickExport FILE *AcquireTemporaryFileStream(char *filename,
366
  FileIOMode mode)
367
509k
{
368
509k
  int
369
509k
    fd;
370
371
509k
  if ((fd=AcquireTemporaryFileDescriptor(filename)) != -1)
372
509k
    {
373
509k
      if (mode == TextFileIOMode)
374
117k
        return fdopen(fd,"w+");
375
391k
      return fdopen(fd,"wb+");
376
509k
    }
377
378
0
  return (FILE *) NULL;
379
509k
}
380

381
/*
382
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
383
%                                                                             %
384
%                                                                             %
385
%                                                                             %
386
+   D e s t r o y T e m p o r a r y F i l e s                                 %
387
%                                                                             %
388
%                                                                             %
389
%                                                                             %
390
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
391
%
392
%  DestroyTemporaryFiles reclaims all currently allocated temporary files,
393
%  removing the files from the filesystem if they still exist.  Also destroys
394
%  the associated semaphore so that temporary file system can not be used
395
%  again without invoking InitializeTemporaryFiles() to re-initialize it.
396
%
397
%      void DestroyTemporaryFiles(void)
398
%
399
*/
400
MagickExport void DestroyTemporaryFiles(void)
401
0
{
402
0
  PurgeTemporaryFiles();
403
0
  DestroySemaphoreInfo(&templist_semaphore);
404
0
}
405

406
/*
407
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
408
%                                                                             %
409
%                                                                             %
410
%                                                                             %
411
+   I n i t i a l i z e T e m p o r a r y F i l e s                           %
412
%                                                                             %
413
%                                                                             %
414
%                                                                             %
415
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
416
%
417
%  Method InitializeTemporaryFiles initializes the temporary file facility
418
%
419
%  The format of the InitializeTemporaryFiles method is:
420
%
421
%      MagickPassFail InitializeTemporaryFiles(void)
422
%
423
%
424
*/
425
MagickPassFail
426
InitializeTemporaryFiles(void)
427
252
{
428
252
  assert(templist_semaphore == (SemaphoreInfo *) NULL);
429
252
  templist_semaphore=AllocateSemaphoreInfo();
430
252
  return MagickPass;
431
252
}
432

433
/*
434
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435
%                                                                             %
436
%                                                                             %
437
%                                                                             %
438
+   L i b e r a t e T e m p o r a r y F i l e                                 %
439
%                                                                             %
440
%                                                                             %
441
%                                                                             %
442
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
443
%
444
%  LiberateTemporaryFile deallocates the allocated temporary file, removing
445
%  the temporary file from the filesystem if it still exists. If the name
446
%  provided is a valid temporary file name, then the first position in the
447
%  string buffer is set to null in order to avoid accidental continued use.
448
%  If the name is not contained in the temporary file list, then it is left
449
%  unmodified.
450
%
451
%      MagickPassFail LiberateTemporaryFile(char *filename)
452
%
453
%  A description of each parameter follows.
454
%
455
%   o filename: Specifies a pointer to an array of characters representing
456
%               the temporary file to reclaim.
457
%
458
*/
459
MagickExport MagickPassFail LiberateTemporaryFile(char *filename)
460
347k
{
461
347k
  MagickPassFail
462
347k
    status = MagickFail;
463
464
347k
  if (RemoveTemporaryFileFromList(filename))
465
338k
    {
466
338k
      if ((remove(filename)) == 0)
467
338k
        status=MagickPass;
468
1
      else
469
1
        (void) LogMagickEvent(TemporaryFileEvent,GetMagickModule(),
470
1
           "Temporary file removal failed \"%s\"",filename);
471
      /* Erase name so it is not accidentally used again */
472
338k
      filename[0]='\0';
473
338k
    }
474
9.20k
  else
475
9.20k
    (void) LogMagickEvent(TemporaryFileEvent,GetMagickModule(),
476
9.20k
      "Temporary file \"%s\" to be removed not allocated!",filename);
477
347k
  return (status);
478
347k
}
479

480
/*
481
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482
%                                                                             %
483
%                                                                             %
484
%                                                                             %
485
+   P u r g e T e m p o r a r y F i l e s                                     %
486
%                                                                             %
487
%                                                                             %
488
%                                                                             %
489
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490
%
491
%  PurgeTemporaryFiles reclaims all currently allocated temporary files,
492
%  removing the files from the filesystem if they still exist.  The temporary
493
%  file allocation system remains usable after this function is called.
494
%
495
%      void PurgeTemporaryFiles(void)
496
%
497
*/
498
MagickExport void PurgeTemporaryFiles(void)
499
0
{
500
0
  TempfileInfo
501
0
    *member,
502
0
    *liberate;
503
504
0
  member=templist;
505
0
  templist=0;
506
0
  while(member)
507
0
    {
508
0
      liberate=member;
509
0
      member=member->next;
510
0
      (void) LogMagickEvent(TemporaryFileEvent,GetMagickModule(),
511
0
        "Removing leaked temporary file \"%s\"",liberate->filename);
512
0
      if ((remove(liberate->filename)) != 0)
513
0
        (void) LogMagickEvent(TemporaryFileEvent,GetMagickModule(),
514
0
          "Temporary file removal failed \"%s\"",liberate->filename);
515
0
      liberate->next=0;
516
0
      MagickFreeMemory(liberate);
517
0
    }
518
0
}
519

520
/*
521
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522
%                                                                             %
523
%                                                                             %
524
%                                                                             %
525
+   P u r g e T e m p o r a r y F i l e s A s y n c S a f e                   %
526
%                                                                             %
527
%                                                                             %
528
%                                                                             %
529
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
530
%
531
%  PurgeTemporaryFiles reclaims all currently allocated temporary files,
532
%  removing the files from the filesystem if they still exist.  This version
533
%  is similar to PurgeTemporaryFiles() except that it intentionally does
534
%  not release any memory (might use a mutex/semaphore) or invoke any
535
%  functions which are not async-safe.  The only reason to call this function
536
%  is something has gone wrong and the program needs to quit immediately.
537
%
538
%      void PurgeTemporaryFilesAsyncSafe(void)
539
%
540
*/
541
MagickExport void PurgeTemporaryFilesAsyncSafe(void)
542
0
{
543
0
  const TempfileInfo
544
0
    *member;
545
546
0
  member=templist;
547
0
  templist=0;
548
0
  while(member)
549
0
    {
550
0
      (void) unlink(member->filename);
551
0
      member=member->next;
552
0
    }
553
0
}