From 3fa8f1a22ad120322bbc68823ca9759d8939b131 Mon Sep 17 00:00:00 2001
From: Tavian Barnes <tavianator@tavianator.com>
Date: Tue, 16 Jan 2024 11:06:28 -0500
Subject: stat: Expose bfs_stat{,x}_convert()

---
 src/stat.c | 183 +++++++++++++++++++++++++++++--------------------------------
 src/stat.h |  41 +++++++++++---
 2 files changed, 121 insertions(+), 103 deletions(-)

(limited to 'src')

diff --git a/src/stat.c b/src/stat.c
index 91aa092..8da1466 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -13,20 +13,12 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#if defined(STATX_BASIC_STATS) && (!__ANDROID__ || __ANDROID_API__ >= 30)
-#  define BFS_HAS_LIBC_STATX true
-#elif __linux__
+#if BFS_USE_STATX && !BFS_HAS_LIBC_STATX
 #  include <linux/stat.h>
 #  include <sys/syscall.h>
 #  include <unistd.h>
 #endif
 
-#ifndef BFS_USE_STATX
-#  if BFS_HAS_LIBC_STATX || defined(SYS_statx)
-#    define BFS_USE_STATX true
-#  endif
-#endif
-
 const char *bfs_stat_field_name(enum bfs_stat_field field) {
 	switch (field) {
 	case BFS_STAT_DEV:
@@ -65,56 +57,53 @@ const char *bfs_stat_field_name(enum bfs_stat_field field) {
 	return "???";
 }
 
-/**
- * Convert a struct stat to a struct bfs_stat.
- */
-static void bfs_stat_convert(const struct stat *statbuf, struct bfs_stat *buf) {
-	buf->mask = 0;
+void bfs_stat_convert(struct bfs_stat *dest, const struct stat *src) {
+	dest->mask = 0;
 
-	buf->dev = statbuf->st_dev;
-	buf->mask |= BFS_STAT_DEV;
+	dest->dev = src->st_dev;
+	dest->mask |= BFS_STAT_DEV;
 
-	buf->ino = statbuf->st_ino;
-	buf->mask |= BFS_STAT_INO;
+	dest->ino = src->st_ino;
+	dest->mask |= BFS_STAT_INO;
 
-	buf->mode = statbuf->st_mode;
-	buf->mask |= BFS_STAT_TYPE | BFS_STAT_MODE;
+	dest->mode = src->st_mode;
+	dest->mask |= BFS_STAT_TYPE | BFS_STAT_MODE;
 
-	buf->nlink = statbuf->st_nlink;
-	buf->mask |= BFS_STAT_NLINK;
+	dest->nlink = src->st_nlink;
+	dest->mask |= BFS_STAT_NLINK;
 
-	buf->gid = statbuf->st_gid;
-	buf->mask |= BFS_STAT_GID;
+	dest->gid = src->st_gid;
+	dest->mask |= BFS_STAT_GID;
 
-	buf->uid = statbuf->st_uid;
-	buf->mask |= BFS_STAT_UID;
+	dest->uid = src->st_uid;
+	dest->mask |= BFS_STAT_UID;
 
-	buf->size = statbuf->st_size;
-	buf->mask |= BFS_STAT_SIZE;
+	dest->size = src->st_size;
+	dest->mask |= BFS_STAT_SIZE;
 
-	buf->blocks = statbuf->st_blocks;
-	buf->mask |= BFS_STAT_BLOCKS;
+	dest->blocks = src->st_blocks;
+	dest->mask |= BFS_STAT_BLOCKS;
 
-	buf->rdev = statbuf->st_rdev;
-	buf->mask |= BFS_STAT_RDEV;
+	dest->rdev = src->st_rdev;
+	dest->mask |= BFS_STAT_RDEV;
 
 #if BSD
-	buf->attrs = statbuf->st_flags;
-	buf->mask |= BFS_STAT_ATTRS;
+	dest->attrs = src->st_flags;
+	dest->mask |= BFS_STAT_ATTRS;
 #endif
 
-	buf->atime = statbuf->st_atim;
-	buf->mask |= BFS_STAT_ATIME;
+	dest->atime = src->st_atim;
+	dest->mask |= BFS_STAT_ATIME;
 
-	buf->ctime = statbuf->st_ctim;
-	buf->mask |= BFS_STAT_CTIME;
+	dest->ctime = src->st_ctim;
+	dest->mask |= BFS_STAT_CTIME;
 
-	buf->mtime = statbuf->st_mtim;
-	buf->mask |= BFS_STAT_MTIME;
+	dest->mtime = src->st_mtim;
+	dest->mask |= BFS_STAT_MTIME;
 
 #if __APPLE__ || __FreeBSD__ || __NetBSD__
-	buf->btime = statbuf->st_birthtim;
-	buf->mask |= BFS_STAT_BTIME;
+	dest->btime = src->st_birthtim;
+	dest->mask |= BFS_STAT_BTIME;
 #endif
 }
 
@@ -125,7 +114,7 @@ static int bfs_stat_impl(int at_fd, const char *at_path, int at_flags, struct bf
 	struct stat statbuf;
 	int ret = fstatat(at_fd, at_path, &statbuf, at_flags);
 	if (ret == 0) {
-		bfs_stat_convert(&statbuf, buf);
+		bfs_stat_convert(buf, &statbuf);
 	}
 	return ret;
 }
@@ -150,82 +139,86 @@ static int bfs_statx(int at_fd, const char *at_path, int at_flags, unsigned int
 	return ret;
 }
 
-/**
- * bfs_stat() implementation backed by statx().
- */
-static int bfs_statx_impl(int at_fd, const char *at_path, int at_flags, struct bfs_stat *buf) {
-	unsigned int mask = STATX_BASIC_STATS | STATX_BTIME;
-	struct statx xbuf;
-	int ret = bfs_statx(at_fd, at_path, at_flags, mask, &xbuf);
-	if (ret != 0) {
-		return ret;
-	}
-
+int bfs_statx_convert(struct bfs_stat *dest, const struct statx *src) {
 	// Callers shouldn't have to check anything except the times
 	const unsigned int guaranteed = STATX_BASIC_STATS & ~(STATX_ATIME | STATX_CTIME | STATX_MTIME);
-	if ((xbuf.stx_mask & guaranteed) != guaranteed) {
+	if ((src->stx_mask & guaranteed) != guaranteed) {
 		errno = ENOTSUP;
 		return -1;
 	}
 
-	buf->mask = 0;
+	dest->mask = 0;
 
-	buf->dev = xmakedev(xbuf.stx_dev_major, xbuf.stx_dev_minor);
-	buf->mask |= BFS_STAT_DEV;
+	dest->dev = xmakedev(src->stx_dev_major, src->stx_dev_minor);
+	dest->mask |= BFS_STAT_DEV;
 
-	buf->ino = xbuf.stx_ino;
-	buf->mask |= BFS_STAT_INO;
+	dest->ino = src->stx_ino;
+	dest->mask |= BFS_STAT_INO;
 
-	buf->mode = xbuf.stx_mode;
-	buf->mask |= BFS_STAT_TYPE;
-	buf->mask |= BFS_STAT_MODE;
+	dest->mode = src->stx_mode;
+	dest->mask |= BFS_STAT_TYPE;
+	dest->mask |= BFS_STAT_MODE;
 
-	buf->nlink = xbuf.stx_nlink;
-	buf->mask |= BFS_STAT_NLINK;
+	dest->nlink = src->stx_nlink;
+	dest->mask |= BFS_STAT_NLINK;
 
-	buf->gid = xbuf.stx_gid;
-	buf->mask |= BFS_STAT_GID;
+	dest->gid = src->stx_gid;
+	dest->mask |= BFS_STAT_GID;
 
-	buf->uid = xbuf.stx_uid;
-	buf->mask |= BFS_STAT_UID;
+	dest->uid = src->stx_uid;
+	dest->mask |= BFS_STAT_UID;
 
-	buf->size = xbuf.stx_size;
-	buf->mask |= BFS_STAT_SIZE;
+	dest->size = src->stx_size;
+	dest->mask |= BFS_STAT_SIZE;
 
-	buf->blocks = xbuf.stx_blocks;
-	buf->mask |= BFS_STAT_BLOCKS;
+	dest->blocks = src->stx_blocks;
+	dest->mask |= BFS_STAT_BLOCKS;
 
-	buf->rdev = xmakedev(xbuf.stx_rdev_major, xbuf.stx_rdev_minor);
-	buf->mask |= BFS_STAT_RDEV;
+	dest->rdev = xmakedev(src->stx_rdev_major, src->stx_rdev_minor);
+	dest->mask |= BFS_STAT_RDEV;
 
-	buf->attrs = xbuf.stx_attributes;
-	buf->mask |= BFS_STAT_ATTRS;
+	dest->attrs = src->stx_attributes;
+	dest->mask |= BFS_STAT_ATTRS;
 
-	if (xbuf.stx_mask & STATX_ATIME) {
-		buf->atime.tv_sec = xbuf.stx_atime.tv_sec;
-		buf->atime.tv_nsec = xbuf.stx_atime.tv_nsec;
-		buf->mask |= BFS_STAT_ATIME;
+	if (src->stx_mask & STATX_ATIME) {
+		dest->atime.tv_sec = src->stx_atime.tv_sec;
+		dest->atime.tv_nsec = src->stx_atime.tv_nsec;
+		dest->mask |= BFS_STAT_ATIME;
 	}
 
-	if (xbuf.stx_mask & STATX_BTIME) {
-		buf->btime.tv_sec = xbuf.stx_btime.tv_sec;
-		buf->btime.tv_nsec = xbuf.stx_btime.tv_nsec;
-		buf->mask |= BFS_STAT_BTIME;
+	if (src->stx_mask & STATX_BTIME) {
+		dest->btime.tv_sec = src->stx_btime.tv_sec;
+		dest->btime.tv_nsec = src->stx_btime.tv_nsec;
+		dest->mask |= BFS_STAT_BTIME;
 	}
 
-	if (xbuf.stx_mask & STATX_CTIME) {
-		buf->ctime.tv_sec = xbuf.stx_ctime.tv_sec;
-		buf->ctime.tv_nsec = xbuf.stx_ctime.tv_nsec;
-		buf->mask |= BFS_STAT_CTIME;
+	if (src->stx_mask & STATX_CTIME) {
+		dest->ctime.tv_sec = src->stx_ctime.tv_sec;
+		dest->ctime.tv_nsec = src->stx_ctime.tv_nsec;
+		dest->mask |= BFS_STAT_CTIME;
 	}
 
-	if (xbuf.stx_mask & STATX_MTIME) {
-		buf->mtime.tv_sec = xbuf.stx_mtime.tv_sec;
-		buf->mtime.tv_nsec = xbuf.stx_mtime.tv_nsec;
-		buf->mask |= BFS_STAT_MTIME;
+	if (src->stx_mask & STATX_MTIME) {
+		dest->mtime.tv_sec = src->stx_mtime.tv_sec;
+		dest->mtime.tv_nsec = src->stx_mtime.tv_nsec;
+		dest->mask |= BFS_STAT_MTIME;
 	}
 
-	return ret;
+	return 0;
+}
+
+/**
+ * bfs_stat() implementation backed by statx().
+ */
+static int bfs_statx_impl(int at_fd, const char *at_path, int at_flags, struct bfs_stat *buf) {
+	unsigned int mask = STATX_BASIC_STATS | STATX_BTIME;
+	struct statx xbuf;
+	int ret = bfs_statx(at_fd, at_path, at_flags, mask, &xbuf);
+	if (ret != 0) {
+		return ret;
+	}
+
+	return bfs_statx_convert(buf, &xbuf);
 }
 
 #endif // BFS_USE_STATX
@@ -306,7 +299,7 @@ int bfs_stat(int at_fd, const char *at_path, enum bfs_stat_flags flags, struct b
 
 	struct stat statbuf;
 	if (fstat(at_fd, &statbuf) == 0) {
-		bfs_stat_convert(&statbuf, buf);
+		bfs_stat_convert(buf, &statbuf);
 		return 0;
 	} else {
 		return -1;
diff --git a/src/stat.h b/src/stat.h
index e08dd4d..68a5c13 100644
--- a/src/stat.h
+++ b/src/stat.h
@@ -13,13 +13,34 @@
 #define BFS_STAT_H
 
 #include "config.h"
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <time.h>
 
+#if defined(STATX_BASIC_STATS) && (!__ANDROID__ || __ANDROID_API__ >= 30)
+#  define BFS_HAS_LIBC_STATX true
+#elif __linux__
+#  include <linux/stat.h>
+#endif
+
+#ifndef BFS_USE_STATX
+#  ifdef STATX_BASIC_STATS
+#    define BFS_USE_STATX true
+#  endif
+#endif
+
 #if BFS_USE_SYS_PARAM_H
 #  include <sys/param.h>
 #endif
 
+#ifdef DEV_BSIZE
+#  define BFS_STAT_BLKSIZE DEV_BSIZE
+#elif defined(S_BLKSIZE)
+#  define BFS_STAT_BLKSIZE S_BLKSIZE
+#else
+#  define BFS_STAT_BLKSIZE 512
+#endif
+
 /**
  * bfs_stat field bitmask.
  */
@@ -60,14 +81,6 @@ enum bfs_stat_flags {
 	BFS_STAT_NOSYNC = 1 << 2,
 };
 
-#ifdef DEV_BSIZE
-#  define BFS_STAT_BLKSIZE DEV_BSIZE
-#elif defined(S_BLKSIZE)
-#  define BFS_STAT_BLKSIZE S_BLKSIZE
-#else
-#  define BFS_STAT_BLKSIZE 512
-#endif
-
 /**
  * Facade over struct stat.
  */
@@ -124,6 +137,18 @@ struct bfs_stat {
  */
 int bfs_stat(int at_fd, const char *at_path, enum bfs_stat_flags flags, struct bfs_stat *buf);
 
+/**
+ * Convert struct stat to struct bfs_stat.
+ */
+void bfs_stat_convert(struct bfs_stat *dest, const struct stat *src);
+
+#if BFS_USE_STATX
+/**
+ * Convert struct statx to struct bfs_stat.
+ */
+int bfs_statx_convert(struct bfs_stat *dest, const struct statx *src);
+#endif
+
 /**
  * Get a particular time field from a bfs_stat() buffer.
  */
-- 
cgit v1.2.3