#define _GNU_SOURCE
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include "os.h"
#include "file.h"
#include "out.h"
#define MAX_SIZE_LENGTH 64
static int
util_tmpfile_mkstemp(const char *dir, const char *templ)
{
ASSERTeq(templ[0], '/');
int oerrno;
int fd = -1;
char *fullname = alloca(strlen(dir) + strlen(templ) + 1);
(void) strcpy(fullname, dir);
(void) strcat(fullname, templ);
sigset_t set, oldset;
sigfillset(&set);
(void) sigprocmask(SIG_BLOCK, &set, &oldset);
mode_t prev_umask = umask(S_IRWXG | S_IRWXO);
fd = os_mkstemp(fullname);
umask(prev_umask);
if (fd < 0) {
ERR("!mkstemp");
goto err;
}
(void) os_unlink(fullname);
(void) sigprocmask(SIG_SETMASK, &oldset, NULL);
LOG(3, "unlinked file is \"%s\"", fullname);
return fd;
err:
oerrno = errno;
(void) sigprocmask(SIG_SETMASK, &oldset, NULL);
if (fd != -1)
(void) os_close(fd);
errno = oerrno;
return -1;
}
int
util_tmpfile(const char *dir, const char *templ, int flags)
{
LOG(3, "dir \"%s\" template \"%s\" flags %x", dir, templ, flags);
ASSERT(flags == 0 || flags == O_EXCL);
#ifdef O_TMPFILE
int fd = open(dir, O_TMPFILE | O_RDWR | flags, S_IRUSR | S_IWUSR);
if (fd >= 0)
return fd;
if (errno != EOPNOTSUPP) {
ERR("!open");
return -1;
}
#endif
return util_tmpfile_mkstemp(dir, templ);
}
int
util_is_absolute_path(const char *path)
{
LOG(3, "path: %s", path);
if (path[0] == OS_DIR_SEPARATOR)
return 1;
else
return 0;
}
int
util_file_mkdir(const char *path, mode_t mode)
{
LOG(3, "path: %s mode: %o", path, mode);
return mkdir(path, mode);
}
int
util_file_dir_open(struct dir_handle *handle, const char *path)
{
LOG(3, "handle: %p path: %s", handle, path);
handle->dirp = opendir(path);
return handle->dirp == NULL;
}
int
util_file_dir_next(struct dir_handle *handle, struct file_info *info)
{
LOG(3, "handle: %p info: %p", handle, info);
struct dirent *d = readdir(handle->dirp);
if (d == NULL)
return 1;
info->filename[NAME_MAX] = '\0';
strncpy(info->filename, d->d_name, NAME_MAX + 1);
if (info->filename[NAME_MAX] != '\0')
return -1;
info->is_dir = d->d_type == DT_DIR;
return 0;
}
int
util_file_dir_close(struct dir_handle *handle)
{
LOG(3, "path: %p", handle);
return closedir(handle->dirp);
}
int
util_file_dir_remove(const char *path)
{
LOG(3, "path: %s", path);
return rmdir(path);
}
static size_t
device_dax_alignment(const char *path)
{
LOG(3, "path \"%s\"", path);
os_stat_t st;
int olderrno;
if (os_stat(path, &st) < 0) {
ERR("!stat \"%s\"", path);
return 0;
}
char spath[PATH_MAX];
snprintf(spath, PATH_MAX, "/sys/dev/char/%d:%d/device/align",
major(st.st_rdev), minor(st.st_rdev));
LOG(4, "device align path \"%s\"", spath);
int fd = os_open(spath, O_RDONLY);
if (fd < 0) {
ERR("!open \"%s\"", spath);
return 0;
}
size_t size = 0;
char sizebuf[MAX_SIZE_LENGTH + 1];
ssize_t nread;
if ((nread = read(fd, sizebuf, MAX_SIZE_LENGTH)) < 0) {
ERR("!read");
goto out;
}
sizebuf[nread] = 0;
char *endptr;
olderrno = errno;
errno = 0;
size = strtoull(sizebuf, &endptr, 10);
if (endptr == sizebuf || *endptr != '\n' ||
(size == ULLONG_MAX && errno == ERANGE)) {
ERR("invalid device alignment %s", sizebuf);
size = 0;
goto out;
}
if ((size & (size - 1)) != 0) {
size = strtoull(sizebuf, &endptr, 16);
if (endptr == sizebuf || *endptr != '\n' ||
(size == ULLONG_MAX && errno == ERANGE)) {
ERR("invalid device alignment %s", sizebuf);
size = 0;
goto out;
}
}
errno = olderrno;
out:
olderrno = errno;
(void) os_close(fd);
errno = olderrno;
LOG(4, "device alignment %zu", size);
return size;
}
size_t
util_file_device_dax_alignment(const char *path)
{
LOG(3, "path \"%s\"", path);
return device_dax_alignment(path);
}