util: add functions for common file operations

Add a function to open a file for reading, writing, or appending.
In uppercase modes errors are handled as fatal, i.e. the caller doesn't
need to check for NULL. To avoid string manipulations in the callers,
the function accepts an optional directory and suffix. New files are
created with specified permissions, which will be needed for saving
keys. The O_EXCL flag is used in the writing mode to make sure a new
file is created (on filesystems that support it).

Also, add a function to rename a temporary file by changing its suffix,
and a function to remove a file.

All functions log all errors, at least as debug messages.
This commit is contained in:
Miroslav Lichvar 2019-10-22 18:06:15 +02:00
parent 88f846f656
commit 7a4c396bba
2 changed files with 165 additions and 0 deletions

146
util.c
View file

@ -1089,6 +1089,152 @@ UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid)
/* ================================================== */
static int
join_path(const char *basedir, const char *name, const char *suffix,
char *buffer, size_t length, LOG_Severity severity)
{
const char *sep;
if (!basedir) {
basedir = "";
sep = "";
} else {
sep = "/";
}
if (!suffix)
suffix = "";
if (snprintf(buffer, length, "%s%s%s%s", basedir, sep, name, suffix) >= length) {
LOG(severity, "File path %s%s%s%s too long", basedir, sep, name, suffix);
return 0;
}
return 1;
}
/* ================================================== */
FILE *
UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
char mode, mode_t perm)
{
const char *file_mode;
char path[PATH_MAX];
LOG_Severity severity;
int fd, flags;
FILE *file;
severity = mode >= 'A' && mode <= 'Z' ? LOGS_FATAL : LOGS_ERR;
if (!join_path(basedir, name, suffix, path, sizeof (path), severity))
return NULL;
switch (mode) {
case 'r':
case 'R':
flags = O_RDONLY;
file_mode = "r";
if (severity != LOGS_FATAL)
severity = LOGS_DEBUG;
break;
case 'w':
case 'W':
flags = O_WRONLY | O_CREAT | O_EXCL;
file_mode = "w";
break;
case 'a':
case 'A':
flags = O_WRONLY | O_CREAT | O_APPEND;
file_mode = "a";
break;
default:
assert(0);
return NULL;
}
try_again:
fd = open(path, flags, perm);
if (fd < 0) {
if (errno == EEXIST) {
if (unlink(path) < 0) {
LOG(severity, "Could not remove %s : %s", path, strerror(errno));
return NULL;
}
DEBUG_LOG("Removed %s", path);
goto try_again;
}
LOG(severity, "Could not open %s : %s", path, strerror(errno));
return NULL;
}
UTI_FdSetCloexec(fd);
file = fdopen(fd, file_mode);
if (!file) {
LOG(severity, "Could not open %s : %s", path, strerror(errno));
close(fd);
return NULL;
}
DEBUG_LOG("Opened %s fd=%d mode=%c", path, fd, mode);
return file;
}
/* ================================================== */
int
UTI_RenameTempFile(const char *basedir, const char *name,
const char *old_suffix, const char *new_suffix)
{
char old_path[PATH_MAX], new_path[PATH_MAX];
if (!join_path(basedir, name, old_suffix, old_path, sizeof (old_path), LOGS_ERR))
return 0;
if (!join_path(basedir, name, new_suffix, new_path, sizeof (new_path), LOGS_ERR))
goto error;
if (rename(old_path, new_path) < 0) {
LOG(LOGS_ERR, "Could not replace %s with %s : %s", new_path, old_path, strerror(errno));
goto error;
}
DEBUG_LOG("Renamed %s to %s", old_path, new_path);
return 1;
error:
if (unlink(old_path) < 0)
LOG(LOGS_ERR, "Could not remove %s : %s", old_path, strerror(errno));
return 0;
}
/* ================================================== */
int
UTI_RemoveFile(const char *basedir, const char *name, const char *suffix)
{
char path[PATH_MAX];
if (!join_path(basedir, name, suffix, path, sizeof (path), LOGS_ERR))
return 0;
if (unlink(path) < 0) {
LOG(errno != ENOENT ? LOGS_ERR : LOGS_DEBUG,
"Could not remove %s : %s", path, strerror(errno));
return 0;
}
DEBUG_LOG("Removed %s", path);
return 1;
}
/* ================================================== */
void
UTI_DropRoot(uid_t uid, gid_t gid)
{

19
util.h
View file

@ -173,6 +173,25 @@ extern int UTI_CreateDirAndParents(const char *path, mode_t mode, uid_t uid, gid
permissions and its uid/gid must match the specified values. */
extern int UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid);
/* Open a file. The full path of the file is constructed from the basedir
(may be NULL), '/' (if basedir is not NULL), name, and suffix (may be NULL).
Created files have specified permissions (umasked). Returns NULL on error.
The following modes are supported (if the mode is an uppercase character,
errors are fatal):
r/R - open an existing file for reading
w/W - open a new file for writing (remove existing file)
a/A - open an existing file for appending (create if does not exist) */
extern FILE *UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
char mode, mode_t perm);
/* Rename a temporary file by changing its suffix. The paths are constructed as
in UTI_OpenFile(). If the renaming fails, the file will be removed. */
extern int UTI_RenameTempFile(const char *basedir, const char *name,
const char *old_suffix, const char *new_suffix);
/* Remove a file. The path is constructed as in UTI_OpenFile(). */
extern int UTI_RemoveFile(const char *basedir, const char *name, const char *suffix);
/* Set process user/group IDs and drop supplementary groups */
extern void UTI_DropRoot(uid_t uid, gid_t gid);