Re-write remove_old_tsversions and greatest_version

Stop deliberately breaking const rules by copying file->name into
dirbuf and truncating it there.  Handle files located in the root
directory properly. Use unlinkat() from POSIX 200809.

(cherry picked from commit 9fcd42c672)
This commit is contained in:
Mark Andrews
2023-04-14 13:53:41 +10:00
committed by Matthijs Mekking
parent 1670a738bd
commit fa35d059da

View File

@@ -20,9 +20,11 @@
#include <stdlib.h>
#include <sys/types.h> /* dev_t FreeBSD 2.1 */
#include <time.h>
#include <unistd.h>
#include <isc/atomic.h>
#include <isc/dir.h>
#include <isc/errno.h>
#include <isc/file.h>
#include <isc/log.h>
#include <isc/magic.h>
@@ -1019,25 +1021,37 @@ sync_highest_level(isc_log_t *lctx, isc_logconfig_t *lcfg) {
static isc_result_t
greatest_version(isc_logfile_t *file, int versions, int *greatestp) {
char *bname, *digit_end;
const char *dirname;
char *digit_end;
char dirbuf[PATH_MAX + 1];
const char *bname;
const char *dirname = ".";
int version, greatest = -1;
size_t bnamelen;
isc_dir_t dir;
isc_result_t result;
char sep = '/';
size_t bnamelen;
/*
* It is safe to DE_CONST the file.name because it was copied
* with isc_mem_strdup().
*/
bname = strrchr(file->name, sep);
bname = strrchr(file->name, '/');
if (bname != NULL) {
*bname++ = '\0';
dirname = file->name;
/*
* Copy the complete file name to dirbuf.
*/
size_t len = strlcpy(dirbuf, file->name, sizeof(dirbuf));
if (len >= sizeof(dirbuf)) {
result = ISC_R_NOSPACE;
syslog(LOG_ERR, "unable to remove log files: %s",
isc_result_totext(result));
return (result);
}
/*
* Truncate after trailing '/' so the code works for
* files in the root directory.
*/
bname++;
dirbuf[bname - file->name] = '\0';
dirname = dirbuf;
} else {
DE_CONST(file->name, bname);
dirname = ".";
bname = file->name;
}
bnamelen = strlen(bname);
@@ -1048,7 +1062,7 @@ greatest_version(isc_logfile_t *file, int versions, int *greatestp) {
* Return if the directory open failed.
*/
if (result != ISC_R_SUCCESS) {
goto out;
return (result);
}
while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
@@ -1062,26 +1076,23 @@ greatest_version(isc_logfile_t *file, int versions, int *greatestp) {
* Remove any backup files that exceed versions.
*/
if (*digit_end == '\0' && version >= versions) {
char rmfile[PATH_MAX + 1];
int n = snprintf(rmfile, sizeof(rmfile),
"%s/%s", dirname,
dir.entry.name);
if (n >= (int)sizeof(rmfile) || n < 0) {
result = ISC_R_NOSPACE;
syslog(LOG_ERR,
"unable to remove log files: %s",
isc_result_totext(result));
break;
}
result = isc_file_remove(rmfile);
if (result != ISC_R_SUCCESS &&
result != ISC_R_NOTFOUND)
{
syslog(LOG_ERR,
"unable to remove log file "
"'%s': %s",
rmfile,
isc_result_totext(result));
int n = unlinkat(dirfd(dir.handle),
dir.entry.name, 0);
if (n < 0) {
result = isc_errno_toresult(errno);
if (result != ISC_R_SUCCESS &&
result != ISC_R_FILENOTFOUND)
{
syslog(LOG_ERR,
"unable to remove log "
"file '%s%s': %s",
bname == file->name
? ""
: dirname,
dir.entry.name,
isc_result_totext(
result));
}
}
} else if (*digit_end == '\0' && version > greatest) {
greatest = version;
@@ -1091,16 +1102,7 @@ greatest_version(isc_logfile_t *file, int versions, int *greatestp) {
isc_dir_close(&dir);
*greatestp = greatest;
result = ISC_R_SUCCESS;
out:
/*
* Replace the file separator if it was taken out.
*/
if (bname != file->name) {
*(bname - 1) = sep;
}
return (result);
return (ISC_R_SUCCESS);
}
static void
@@ -1120,7 +1122,8 @@ insert_sort(int64_t to_keep[], int64_t versions, int64_t version) {
}
static int64_t
last_to_keep(int64_t versions, isc_dir_t *dirp, char *bname, size_t bnamelen) {
last_to_keep(int64_t versions, isc_dir_t *dirp, const char *bname,
size_t bnamelen) {
int64_t to_keep[ISC_LOG_MAX_VERSIONS] = { 0 };
int64_t version = 0;
@@ -1163,25 +1166,37 @@ last_to_keep(int64_t versions, isc_dir_t *dirp, char *bname, size_t bnamelen) {
static isc_result_t
remove_old_tsversions(isc_logfile_t *file, int versions) {
isc_result_t result;
char *bname = NULL, *digit_end = NULL;
const char *dirname = NULL;
char *digit_end;
char dirbuf[PATH_MAX + 1];
const char *bname;
const char *dirname = ".";
int64_t version, last = INT64_MAX;
size_t bnamelen;
isc_dir_t dir;
char sep = '/';
isc_result_t result;
size_t bnamelen;
/*
* It is safe to DE_CONST the file.name because it was copied
* with isc_mem_strdup().
*/
bname = strrchr(file->name, sep);
bname = strrchr(file->name, '/');
if (bname != NULL) {
*bname++ = '\0';
dirname = file->name;
/*
* Copy the complete file name to dirbuf.
*/
size_t len = strlcpy(dirbuf, file->name, sizeof(dirbuf));
if (len >= sizeof(dirbuf)) {
result = ISC_R_NOSPACE;
syslog(LOG_ERR, "unable to remove log files: %s",
isc_result_totext(result));
return (result);
}
/*
* Truncate after trailing '/' so the code works for
* files in the root directory.
*/
bname++;
dirbuf[bname - file->name] = '\0';
dirname = dirbuf;
} else {
DE_CONST(file->name, bname);
dirname = ".";
bname = file->name;
}
bnamelen = strlen(bname);
@@ -1192,61 +1207,45 @@ remove_old_tsversions(isc_logfile_t *file, int versions) {
* Return if the directory open failed.
*/
if (result != ISC_R_SUCCESS) {
goto out;
return (result);
}
last = last_to_keep(versions, &dir, bname, bnamelen);
/*
* Then we remove all files that we don't want to_keep
*/
while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
if (dir.entry.length > bnamelen &&
strncmp(dir.entry.name, bname, bnamelen) == 0 &&
dir.entry.name[bnamelen] == '.')
{
char *ename = &dir.entry.name[bnamelen + 1];
version = strtoull(ename, &digit_end, 10);
version = strtoull(&dir.entry.name[bnamelen + 1],
&digit_end, 10);
/*
* Remove any backup files that exceed versions.
*/
if (*digit_end == '\0' && version < last) {
char rmfile[PATH_MAX + 1];
int n = snprintf(rmfile, sizeof(rmfile),
"%s/%s", dirname,
dir.entry.name);
if (n >= (int)sizeof(rmfile) || n < 0) {
result = ISC_R_NOSPACE;
syslog(LOG_ERR,
"unable to remove log files: %s",
isc_result_totext(result));
break;
}
result = isc_file_remove(rmfile);
if (result != ISC_R_SUCCESS &&
result != ISC_R_NOTFOUND)
{
syslog(LOG_ERR,
"unable to remove log file "
"'%s': %s",
rmfile,
isc_result_totext(result));
int n = unlinkat(dirfd(dir.handle),
dir.entry.name, 0);
if (n < 0) {
result = isc_errno_toresult(errno);
if (result != ISC_R_SUCCESS &&
result != ISC_R_FILENOTFOUND)
{
syslog(LOG_ERR,
"unable to remove log "
"file '%s%s': %s",
bname == file->name
? ""
: dirname,
dir.entry.name,
isc_result_totext(
result));
}
}
}
}
}
isc_dir_close(&dir);
result = ISC_R_SUCCESS;
out:
/*
* Replace the file separator if it was taken out.
*/
if (bname != file->name) {
*(bname - 1) = sep;
}
return (result);
return (ISC_R_SUCCESS);
}
static isc_result_t