Line data Source code
1 : /*------------------------------------------------------------------------- 2 : * 3 : * rmtree.c 4 : * 5 : * Portions Copyright (c) 1996-2024, 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 : #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) 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 6634 : rmtree(const char *path, bool rmtopdir) 51 : { 52 : char pathbuf[MAXPGPATH]; 53 : DIR *dir; 54 : struct dirent *de; 55 6634 : bool result = true; 56 6634 : size_t dirnames_size = 0; 57 6634 : size_t dirnames_capacity = 8; 58 : char **dirnames; 59 : 60 6634 : dir = OPENDIR(path); 61 6634 : if (dir == NULL) 62 : { 63 0 : pg_log_warning("could not open directory \"%s\": %m", path); 64 0 : return false; 65 : } 66 : 67 6634 : dirnames = (char **) palloc(sizeof(char *) * dirnames_capacity); 68 : 69 295254 : while (errno = 0, (de = readdir(dir))) 70 : { 71 288620 : if (strcmp(de->d_name, ".") == 0 || 72 281986 : strcmp(de->d_name, "..") == 0) 73 13268 : continue; 74 275352 : snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, de->d_name); 75 275352 : switch (get_dirent_type(pathbuf, de, false, LOG_LEVEL)) 76 : { 77 0 : case PGFILETYPE_ERROR: 78 : /* already logged, press on */ 79 0 : break; 80 5260 : 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 5260 : if (dirnames_size == dirnames_capacity) 87 : { 88 370 : dirnames = repalloc(dirnames, 89 : sizeof(char *) * dirnames_capacity * 2); 90 370 : dirnames_capacity *= 2; 91 : } 92 5260 : dirnames[dirnames_size++] = pstrdup(pathbuf); 93 5260 : break; 94 270092 : default: 95 270092 : if (unlink(pathbuf) != 0 && errno != ENOENT) 96 : { 97 0 : pg_log_warning("could not remove file \"%s\": %m", pathbuf); 98 0 : result = false; 99 : } 100 270092 : break; 101 : } 102 : } 103 : 104 6634 : if (errno != 0) 105 : { 106 0 : pg_log_warning("could not read directory \"%s\": %m", path); 107 0 : result = false; 108 : } 109 : 110 6634 : CLOSEDIR(dir); 111 : 112 : /* Now recurse into the subdirectories we found. */ 113 11894 : for (size_t i = 0; i < dirnames_size; ++i) 114 : { 115 5260 : if (!rmtree(dirnames[i], true)) 116 0 : result = false; 117 5260 : pfree(dirnames[i]); 118 : } 119 : 120 6634 : if (rmtopdir) 121 : { 122 6634 : if (rmdir(path) != 0) 123 : { 124 0 : pg_log_warning("could not remove directory \"%s\": %m", path); 125 0 : result = false; 126 : } 127 : } 128 : 129 6634 : pfree(dirnames); 130 : 131 6634 : return result; 132 : }