/* * * dirmod.c * directory handling functions * * Copyright (C) 2ndQuadrant, 2010-2016 * * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "postgres_fe.h" /* Don't modify declarations in system headers */ #include #include #include /* * pgfnames * * return a list of the names of objects in the argument directory. Caller * must call pgfnames_cleanup later to free the memory allocated by this * function. */ char ** pgfnames(const char *path) { DIR *dir; struct dirent *file; char **filenames; int numnames = 0; int fnsize = 200; /* enough for many small dbs */ dir = opendir(path); if (dir == NULL) { return NULL; } filenames = (char **) palloc(fnsize * sizeof(char *)); while (errno = 0, (file = readdir(dir)) != NULL) { if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0) { if (numnames + 1 >= fnsize) { fnsize *= 2; filenames = (char **) repalloc(filenames, fnsize * sizeof(char *)); } filenames[numnames++] = pstrdup(file->d_name); } } if (errno) { fprintf(stderr, _("could not read directory \"%s\": %s\n"), path, strerror(errno)); } filenames[numnames] = NULL; if (closedir(dir)) { fprintf(stderr, _("could not close directory \"%s\": %s\n"), path, strerror(errno)); } return filenames; } /* * pgfnames_cleanup * * deallocate memory used for filenames */ void pgfnames_cleanup(char **filenames) { char **fn; for (fn = filenames; *fn; fn++) pfree(*fn); pfree(filenames); } /* * rmtree * * Delete a directory tree recursively. * Assumes path points to a valid directory. * Deletes everything under path. * If rmtopdir is true deletes the directory too. * Returns true if successful, false if there was any problem. * (The details of the problem are reported already, so caller * doesn't really have to say anything more, but most do.) */ bool rmtree(const char *path, bool rmtopdir) { bool result = true; char pathbuf[MAXPGPATH]; char **filenames; char **filename; struct stat statbuf; /* * we copy all the names out of the directory before we start modifying * it. */ filenames = pgfnames(path); if (filenames == NULL) return false; /* now we have the names we can start removing things */ for (filename = filenames; *filename; filename++) { snprintf(pathbuf, MAXPGPATH, "%s/%s", path, *filename); /* * It's ok if the file is not there anymore; we were just about to * delete it anyway. * * This is not an academic possibility. One scenario where this * happens is when bgwriter has a pending unlink request for a file in * a database that's being dropped. In dropdb(), we call * ForgetDatabaseFsyncRequests() to flush out any such pending unlink * requests, but because that's asynchronous, it's not guaranteed that * the bgwriter receives the message in time. */ if (lstat(pathbuf, &statbuf) != 0) { if (errno != ENOENT) { result = false; } continue; } if (S_ISDIR(statbuf.st_mode)) { /* call ourselves recursively for a directory */ if (!rmtree(pathbuf, true)) { /* we already reported the error */ result = false; } } else { if (unlink(pathbuf) != 0) { if (errno != ENOENT) { result = false; } } } } if (rmtopdir) { if (rmdir(path) != 0) { result = false; } } pgfnames_cleanup(filenames); return result; }