|
20 | 20 | #include <unistd.h>
|
21 | 21 | #include <sys/stat.h>
|
22 | 22 |
|
| 23 | +#include "common/file_utils.h" |
| 24 | + |
23 | 25 | #ifndef FRONTEND
|
| 26 | +#include "storage/fd.h" |
24 | 27 | #define pg_log_warning(...) elog(WARNING, __VA_ARGS__)
|
| 28 | +#define LOG_LEVEL WARNING |
| 29 | +#define OPENDIR(x) AllocateDir(x) |
| 30 | +#define CLOSEDIR(x) FreeDir(x) |
25 | 31 | #else
|
26 | 32 | #include "common/logging.h"
|
| 33 | +#define LOG_LEVEL PG_LOG_WARNING |
| 34 | +#define OPENDIR(x) opendir(x) |
| 35 | +#define CLOSEDIR(x) closedir(x) |
27 | 36 | #endif
|
28 | 37 |
|
29 |
| - |
30 | 38 | /*
|
31 | 39 | * rmtree
|
32 | 40 | *
|
|
41 | 49 | bool
|
42 | 50 | rmtree(const char *path, bool rmtopdir)
|
43 | 51 | {
|
44 |
| - bool result = true; |
45 | 52 | char pathbuf[MAXPGPATH];
|
46 |
| - char **filenames; |
47 |
| - char **filename; |
48 |
| - struct stat statbuf; |
49 |
| - |
50 |
| - /* |
51 |
| - * we copy all the names out of the directory before we start modifying |
52 |
| - * it. |
53 |
| - */ |
54 |
| - filenames = pgfnames(path); |
| 53 | + DIR *dir; |
| 54 | + struct dirent *de; |
| 55 | + bool result = true; |
| 56 | + size_t dirnames_size = 0; |
| 57 | + size_t dirnames_capacity = 8; |
| 58 | + char **dirnames = palloc(sizeof(char *) * dirnames_capacity); |
55 | 59 |
|
56 |
| - if (filenames == NULL) |
| 60 | + dir = OPENDIR(path); |
| 61 | + if (dir == NULL) |
| 62 | + { |
| 63 | + pg_log_warning("could not open directory \"%s\": %m", path); |
57 | 64 | return false;
|
| 65 | + } |
58 | 66 |
|
59 |
| - /* now we have the names we can start removing things */ |
60 |
| - for (filename = filenames; *filename; filename++) |
| 67 | + while (errno = 0, (de = readdir(dir))) |
61 | 68 | {
|
62 |
| - snprintf(pathbuf, MAXPGPATH, "%s/%s", path, *filename); |
63 |
| - |
64 |
| - /* |
65 |
| - * It's ok if the file is not there anymore; we were just about to |
66 |
| - * delete it anyway. |
67 |
| - * |
68 |
| - * This is not an academic possibility. One scenario where this |
69 |
| - * happens is when bgwriter has a pending unlink request for a file in |
70 |
| - * a database that's being dropped. In dropdb(), we call |
71 |
| - * ForgetDatabaseSyncRequests() to flush out any such pending unlink |
72 |
| - * requests, but because that's asynchronous, it's not guaranteed that |
73 |
| - * the bgwriter receives the message in time. |
74 |
| - */ |
75 |
| - if (lstat(pathbuf, &statbuf) != 0) |
76 |
| - { |
77 |
| - if (errno != ENOENT) |
78 |
| - { |
79 |
| - pg_log_warning("could not stat file or directory \"%s\": %m", |
80 |
| - pathbuf); |
81 |
| - result = false; |
82 |
| - } |
| 69 | + if (strcmp(de->d_name, ".") == 0 || |
| 70 | + strcmp(de->d_name, "..") == 0) |
83 | 71 | continue;
|
84 |
| - } |
85 |
| - |
86 |
| - if (S_ISDIR(statbuf.st_mode)) |
87 |
| - { |
88 |
| - /* call ourselves recursively for a directory */ |
89 |
| - if (!rmtree(pathbuf, true)) |
90 |
| - { |
91 |
| - /* we already reported the error */ |
92 |
| - result = false; |
93 |
| - } |
94 |
| - } |
95 |
| - else |
| 72 | + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, de->d_name); |
| 73 | + switch (get_dirent_type(pathbuf, de, false, LOG_LEVEL)) |
96 | 74 | {
|
97 |
| - if (unlink(pathbuf) != 0) |
98 |
| - { |
99 |
| - if (errno != ENOENT) |
| 75 | + case PGFILETYPE_ERROR: |
| 76 | + /* already logged, press on */ |
| 77 | + break; |
| 78 | + case PGFILETYPE_DIR: |
| 79 | + |
| 80 | + /* |
| 81 | + * Defer recursion until after we've closed this directory, to |
| 82 | + * avoid using more than one file descriptor at a time. |
| 83 | + */ |
| 84 | + if (dirnames_size == dirnames_capacity) |
| 85 | + { |
| 86 | + dirnames = repalloc(dirnames, |
| 87 | + sizeof(char *) * dirnames_capacity * 2); |
| 88 | + dirnames_capacity *= 2; |
| 89 | + } |
| 90 | + dirnames[dirnames_size++] = pstrdup(pathbuf); |
| 91 | + break; |
| 92 | + default: |
| 93 | + if (unlink(pathbuf) != 0 && errno != ENOENT) |
100 | 94 | {
|
101 |
| - pg_log_warning("could not remove file or directory \"%s\": %m", |
102 |
| - pathbuf); |
| 95 | + pg_log_warning("could not unlink file \"%s\": %m", pathbuf); |
103 | 96 | result = false;
|
104 | 97 | }
|
105 |
| - } |
| 98 | + break; |
106 | 99 | }
|
107 | 100 | }
|
108 | 101 |
|
| 102 | + if (errno != 0) |
| 103 | + { |
| 104 | + pg_log_warning("could not read directory \"%s\": %m", path); |
| 105 | + result = false; |
| 106 | + } |
| 107 | + |
| 108 | + CLOSEDIR(dir); |
| 109 | + |
| 110 | + /* Now recurse into the subdirectories we found. */ |
| 111 | + for (size_t i = 0; i < dirnames_size; ++i) |
| 112 | + { |
| 113 | + if (!rmtree(dirnames[i], true)) |
| 114 | + result = false; |
| 115 | + pfree(dirnames[i]); |
| 116 | + } |
| 117 | + |
109 | 118 | if (rmtopdir)
|
110 | 119 | {
|
111 | 120 | if (rmdir(path) != 0)
|
112 | 121 | {
|
113 |
| - pg_log_warning("could not remove file or directory \"%s\": %m", |
114 |
| - path); |
| 122 | + pg_log_warning("could not remove directory \"%s\": %m", path); |
115 | 123 | result = false;
|
116 | 124 | }
|
117 | 125 | }
|
118 | 126 |
|
119 |
| - pgfnames_cleanup(filenames); |
| 127 | + pfree(dirnames); |
120 | 128 |
|
121 | 129 | return result;
|
122 | 130 | }
|
0 commit comments