Coverage Report

Created: 2025-07-03 06:49

/src/postgres/src/common/rmtree.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * rmtree.c
4
 *
5
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
6
 * Portions Copyright (c) 1994, Regents of the University of California
7
 *
8
 * IDENTIFICATION
9
 *    src/common/rmtree.c
10
 *
11
 *-------------------------------------------------------------------------
12
 */
13
14
#ifndef FRONTEND
15
#include "postgres.h"
16
#else
17
#include "postgres_fe.h"
18
#endif
19
20
#include <unistd.h>
21
#include <sys/stat.h>
22
23
#include "common/file_utils.h"
24
25
#ifndef FRONTEND
26
#include "storage/fd.h"
27
0
#define pg_log_warning(...) elog(WARNING, __VA_ARGS__)
28
0
#define LOG_LEVEL WARNING
29
0
#define OPENDIR(x) AllocateDir(x)
30
0
#define CLOSEDIR(x) FreeDir(x)
31
#else
32
#include "common/logging.h"
33
#define LOG_LEVEL PG_LOG_WARNING
34
#define OPENDIR(x) opendir(x)
35
#define CLOSEDIR(x) closedir(x)
36
#endif
37
38
/*
39
 *  rmtree
40
 *
41
 *  Delete a directory tree recursively.
42
 *  Assumes path points to a valid directory.
43
 *  Deletes everything under path.
44
 *  If rmtopdir is true deletes the directory too.
45
 *  Returns true if successful, false if there was any problem.
46
 *  (The details of the problem are reported already, so caller
47
 *  doesn't really have to say anything more, but most do.)
48
 */
49
bool
50
rmtree(const char *path, bool rmtopdir)
51
0
{
52
0
  char    pathbuf[MAXPGPATH];
53
0
  DIR      *dir;
54
0
  struct dirent *de;
55
0
  bool    result = true;
56
0
  size_t    dirnames_size = 0;
57
0
  size_t    dirnames_capacity = 8;
58
0
  char    **dirnames;
59
60
0
  dir = OPENDIR(path);
61
0
  if (dir == NULL)
62
0
  {
63
0
    pg_log_warning("could not open directory \"%s\": %m", path);
64
0
    return false;
65
0
  }
66
67
0
  dirnames = (char **) palloc(sizeof(char *) * dirnames_capacity);
68
69
0
  while (errno = 0, (de = readdir(dir)))
70
0
  {
71
0
    if (strcmp(de->d_name, ".") == 0 ||
72
0
      strcmp(de->d_name, "..") == 0)
73
0
      continue;
74
0
    snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, de->d_name);
75
0
    switch (get_dirent_type(pathbuf, de, false, LOG_LEVEL))
76
0
    {
77
0
      case PGFILETYPE_ERROR:
78
        /* already logged, press on */
79
0
        break;
80
0
      case PGFILETYPE_DIR:
81
82
        /*
83
         * Defer recursion until after we've closed this directory, to
84
         * avoid using more than one file descriptor at a time.
85
         */
86
0
        if (dirnames_size == dirnames_capacity)
87
0
        {
88
0
          dirnames = repalloc(dirnames,
89
0
                    sizeof(char *) * dirnames_capacity * 2);
90
0
          dirnames_capacity *= 2;
91
0
        }
92
0
        dirnames[dirnames_size++] = pstrdup(pathbuf);
93
0
        break;
94
0
      default:
95
0
        if (unlink(pathbuf) != 0 && errno != ENOENT)
96
0
        {
97
0
          pg_log_warning("could not remove file \"%s\": %m", pathbuf);
98
0
          result = false;
99
0
        }
100
0
        break;
101
0
    }
102
0
  }
103
104
0
  if (errno != 0)
105
0
  {
106
0
    pg_log_warning("could not read directory \"%s\": %m", path);
107
0
    result = false;
108
0
  }
109
110
0
  CLOSEDIR(dir);
111
112
  /* Now recurse into the subdirectories we found. */
113
0
  for (size_t i = 0; i < dirnames_size; ++i)
114
0
  {
115
0
    if (!rmtree(dirnames[i], true))
116
0
      result = false;
117
0
    pfree(dirnames[i]);
118
0
  }
119
120
0
  if (rmtopdir)
121
0
  {
122
0
    if (rmdir(path) != 0)
123
0
    {
124
0
      pg_log_warning("could not remove directory \"%s\": %m", path);
125
0
      result = false;
126
0
    }
127
0
  }
128
129
0
  pfree(dirnames);
130
131
0
  return result;
132
0
}