1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * rmtree.c |
4 | * |
5 | * Portions Copyright (c) 1996-2019, 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 | #ifndef FRONTEND |
24 | #define pg_log_warning(...) elog(WARNING, __VA_ARGS__) |
25 | #else |
26 | #include "common/logging.h" |
27 | #endif |
28 | |
29 | |
30 | /* |
31 | * rmtree |
32 | * |
33 | * Delete a directory tree recursively. |
34 | * Assumes path points to a valid directory. |
35 | * Deletes everything under path. |
36 | * If rmtopdir is true deletes the directory too. |
37 | * Returns true if successful, false if there was any problem. |
38 | * (The details of the problem are reported already, so caller |
39 | * doesn't really have to say anything more, but most do.) |
40 | */ |
41 | bool |
42 | rmtree(const char *path, bool rmtopdir) |
43 | { |
44 | bool result = true; |
45 | 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); |
55 | |
56 | if (filenames == NULL) |
57 | return false; |
58 | |
59 | /* now we have the names we can start removing things */ |
60 | for (filename = filenames; *filename; filename++) |
61 | { |
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 | } |
83 | 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 |
96 | { |
97 | if (unlink(pathbuf) != 0) |
98 | { |
99 | if (errno != ENOENT) |
100 | { |
101 | pg_log_warning("could not remove file or directory \"%s\": %m" , |
102 | pathbuf); |
103 | result = false; |
104 | } |
105 | } |
106 | } |
107 | } |
108 | |
109 | if (rmtopdir) |
110 | { |
111 | if (rmdir(path) != 0) |
112 | { |
113 | pg_log_warning("could not remove file or directory \"%s\": %m" , |
114 | path); |
115 | result = false; |
116 | } |
117 | } |
118 | |
119 | pgfnames_cleanup(filenames); |
120 | |
121 | return result; |
122 | } |
123 | |