utils: Use fstatat and unlinkat if supported

This commit is contained in:
John-Mark Bell 2022-11-02 14:00:14 +00:00
parent 41f0a5a36e
commit 0718e58681
3 changed files with 65 additions and 89 deletions

View File

@ -146,6 +146,15 @@ char *realpath(const char *path, char *resolved_path);
#undef HAVE_SCANDIR
#endif
#define HAVE_DIRFD
#define HAVE_UNLINKAT
#define HAVE_FSTATAT
#if (defined(_WIN32) || defined(__riscos__) || defined(__HAIKU__) || defined(__BEOS__) || defined(__amigaos4__) || defined(__AMIGA__) || defined(__MINT__))
#undef HAVE_DIRFD
#undef HAVE_UNLINKAT
#undef HAVE_FSTATAT
#endif
#define HAVE_REGEX
#if (defined(__serenity__))
#undef HAVE_REGEX

View File

@ -26,6 +26,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "desktop/gui_internal.h"
@ -318,16 +319,13 @@ nserror netsurf_mkdir_all(const char *fname)
nserror
netsurf_recursive_rm(const char *path)
{
struct dirent **listing = NULL; /* directory entry listing */
int nentries, ent;
DIR *parent;
struct dirent *entry;
nserror ret = NSERROR_OK;
struct stat ent_stat; /* stat result of leaf entry */
char *leafpath = NULL;
const char *leafname;
nentries = scandir(path, &listing, 0, alphasort);
if (nentries < 0) {
parent = opendir(path);
if (parent == NULL) {
switch (errno) {
case ENOENT:
return NSERROR_NOT_FOUND;
@ -336,26 +334,44 @@ netsurf_recursive_rm(const char *path)
}
}
for (ent = 0; ent < nentries; ent++) {
leafname = listing[ent]->d_name;
if (strcmp(leafname, ".") == 0 ||
strcmp(leafname, "..") == 0)
while ((entry = readdir(parent))) {
char *leafpath = NULL;
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0)
continue;
ret = netsurf_mkpath(&leafpath, NULL, 2, path, leafname);
if (ret != NSERROR_OK) goto out;
ret = netsurf_mkpath(&leafpath, NULL, 2, path, entry->d_name);
if (ret != NSERROR_OK)
goto out;
#if (defined(HAVE_DIRFD) && defined(HAVE_FSTATAT))
if (fstatat(dirfd(parent), entry->d_name, &ent_stat,
AT_SYMLINK_NOFOLLOW) != 0) {
#else
if (stat(leafpath, &ent_stat) != 0) {
#endif
free(leafpath);
goto out_via_errno;
}
if (S_ISDIR(ent_stat.st_mode)) {
ret = netsurf_recursive_rm(leafpath);
if (ret != NSERROR_OK) goto out;
if (ret != NSERROR_OK) {
free(leafpath);
goto out;
}
} else {
#if (defined(HAVE_DIRFD) && defined(HAVE_UNLINKAT))
if (unlinkat(dirfd(parent), entry->d_name, 0) != 0) {
#else
if (unlink(leafpath) != 0) {
#endif
free(leafpath);
goto out_via_errno;
}
}
free(leafpath);
leafpath = NULL;
}
if (rmdir(path) != 0) {
@ -373,16 +389,7 @@ out_via_errno:
ret = NSERROR_UNKNOWN;
}
out:
if (listing != NULL) {
for (ent = 0; ent < nentries; ent++) {
free(listing[ent]);
}
free(listing);
}
if (leafpath != NULL) {
free(leafpath);
}
closedir(parent);
return ret;
}

View File

@ -29,10 +29,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include "utils/dirent.h"
#include "utils/errors.h"
#include "utils/file.h"
#include "utils/filename.h"
#include "utils/log.h"
#include "utils/utils.h"
@ -55,7 +58,6 @@ static char filename_directory[256];
static struct directory *filename_create_directory(const char *prefix);
static bool filename_flush_directory(const char *folder, int depth);
static bool filename_delete_recursive(char *folder);
/**
* Request a new, unique, filename.
@ -272,6 +274,8 @@ bool filename_flush_directory(const char *folder, int depth)
}
parent = opendir(folder);
if (parent == NULL)
return false;
while ((entry = readdir(parent))) {
int written;
@ -288,7 +292,12 @@ bool filename_flush_directory(const char *folder, int depth)
child[sizeof(child) - 1] = '\0';
}
#if (defined(HAVE_DIRFD) && defined(HAVE_FSTATAT))
if (fstatat(dirfd(parent), entry->d_name, &statbuf,
AT_SYMLINK_NOFOLLOW) == -1) {
#else
if (stat(child, &statbuf) == -1) {
#endif
NSLOG(netsurf, INFO, "Unable to stat %s: %s", child,
strerror(errno));
continue;
@ -354,14 +363,20 @@ bool filename_flush_directory(const char *folder, int depth)
/* delete or recurse */
if (del) {
if (S_ISDIR(statbuf.st_mode))
filename_delete_recursive(child);
if (remove(child))
NSLOG(netsurf, INFO, "Failed to remove '%s'",
child);
else
changed = true;
if (S_ISDIR(statbuf.st_mode)) {
changed = (netsurf_recursive_rm(child) ==
NSERROR_OK);
} else {
#if (defined(HAVE_DIRFD) && defined(HAVE_UNLINKAT))
if (unlinkat(dirfd(parent), entry->d_name, 0)) {
#else
if (unlink(child)) {
#endif
NSLOG(netsurf, INFO,
"Failed to remove '%s'", child);
} else
changed = true;
}
} else {
while (filename_flush_directory(child, depth + 1));
}
@ -373,61 +388,6 @@ bool filename_flush_directory(const char *folder, int depth)
}
/**
* Recursively deletes the contents of a directory
*
* \param folder the directory to delete
* \return true on success, false otherwise
*/
bool filename_delete_recursive(char *folder)
{
DIR *parent;
struct dirent *entry;
char child[256];
struct stat statbuf;
parent = opendir(folder);
while ((entry = readdir(parent))) {
int written;
/* Ignore '.' and '..' */
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0)
continue;
written = snprintf(child, sizeof(child), "%s/%s",
folder, entry->d_name);
if (written == sizeof(child)) {
child[sizeof(child) - 1] = '\0';
}
if (stat(child, &statbuf) == -1) {
NSLOG(netsurf, INFO, "Unable to stat %s: %s", child,
strerror(errno));
continue;
}
if (S_ISDIR(statbuf.st_mode)) {
if (!filename_delete_recursive(child)) {
closedir(parent);
return false;
}
}
if (remove(child)) {
NSLOG(netsurf, INFO, "Failed to remove '%s'", child);
closedir(parent);
return false;
}
}
closedir(parent);
return true;
}
/**
* Creates a new directory.
*