From 622806d5cf18c9eba6438789ba552cee8f3ab712 Mon Sep 17 00:00:00 2001 From: Andre Miranda Date: Fri, 14 Aug 2020 15:52:03 +0100 Subject: [PATCH] glocalfileinfo: Add support for optional/required fields in stat bufs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a step towards supporting `statx()`, which allows the set of fields it returns to be specified by the caller. Currently, the existing `stat()` and `fstat()` calls continue to be made, and there are no behavioural changes — but the new wrapper functions will be extended in future. Helps: #1970 --- gio/glocalfile.c | 10 ++- gio/glocalfileinfo.c | 37 +++++------ gio/glocalfileinfo.h | 116 +++++++++++++++++++++++++++++++++++ gio/glocalfileoutputstream.c | 33 +++++----- 4 files changed, 154 insertions(+), 42 deletions(-) diff --git a/gio/glocalfile.c b/gio/glocalfile.c index 3d3f5735f..f92e6ed20 100644 --- a/gio/glocalfile.c +++ b/gio/glocalfile.c @@ -1352,11 +1352,7 @@ g_local_file_read (GFile *file, return NULL; } -#ifdef G_OS_WIN32 - ret = GLIB_PRIVATE_CALL (g_win32_fstat) (fd, &buf); -#else - ret = fstat (fd, &buf); -#endif + ret = g_local_file_fstat (fd, G_LOCAL_FILE_STAT_FIELD_TYPE, G_LOCAL_FILE_STAT_FIELD_ALL, &buf); if (ret == 0 && S_ISDIR (_g_stat_mode (&buf))) { @@ -2706,7 +2702,9 @@ g_local_file_measure_size_of_file (gint parent_fd, return FALSE; #if defined (AT_FDCWD) - if (fstatat (parent_fd, name->data, &buf, AT_SYMLINK_NOFOLLOW) != 0) + if (g_local_file_fstatat (parent_fd, name->data, AT_SYMLINK_NOFOLLOW, + G_LOCAL_FILE_STAT_FIELD_BASIC_STATS, G_LOCAL_FILE_STAT_FIELD_ALL, + &buf) != 0) { int errsv = errno; return g_local_file_measure_size_error (state->flags, errsv, name, error); diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c index 1d1f91c8a..0115a385a 100644 --- a/gio/glocalfileinfo.c +++ b/gio/glocalfileinfo.c @@ -123,6 +123,8 @@ _g_local_file_info_create_etag (GLocalFileStat *statbuf) { glong sec, usec; + g_return_val_if_fail (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_MTIME), NULL); + #if defined (G_OS_WIN32) sec = statbuf->st_mtim.tv_sec; usec = statbuf->st_mtim.tv_nsec / 1000; @@ -1773,11 +1775,7 @@ _g_local_file_info_get (const char *basename, { GFileInfo *info; GLocalFileStat statbuf; -#ifdef S_ISLNK - struct stat statbuf2; -#elif defined (G_OS_WIN32) - GWin32PrivateStat statbuf2; -#endif + GLocalFileStat statbuf2; int res; gboolean stat_ok; gboolean is_symlink, symlink_broken; @@ -1799,11 +1797,10 @@ _g_local_file_info_get (const char *basename, return info; } -#ifndef G_OS_WIN32 - res = g_lstat (path, &statbuf); -#else - res = GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (path, &statbuf); -#endif + res = g_local_file_lstat (path, + G_LOCAL_FILE_STAT_FIELD_BASIC_STATS, + G_LOCAL_FILE_STAT_FIELD_ALL, + &statbuf); if (res == -1) { @@ -1850,11 +1847,10 @@ _g_local_file_info_get (const char *basename, /* Unless NOFOLLOW was set we default to following symlinks */ if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)) { -#ifndef G_OS_WIN32 - res = stat (path, &statbuf2); -#else - res = GLIB_PRIVATE_CALL (g_win32_stat_utf8) (path, &statbuf2); -#endif + res = g_local_file_stat (path, + G_LOCAL_FILE_STAT_FIELD_BASIC_STATS, + G_LOCAL_FILE_STAT_FIELD_ALL, + &statbuf2); /* Report broken links as symlinks */ if (res != -1) @@ -2073,14 +2069,11 @@ _g_local_file_info_get_from_fd (int fd, GLocalFileStat stat_buf; GFileAttributeMatcher *matcher; GFileInfo *info; - -#ifdef G_OS_WIN32 -#define FSTAT GLIB_PRIVATE_CALL (g_win32_fstat) -#else -#define FSTAT fstat -#endif - if (FSTAT (fd, &stat_buf) == -1) + if (g_local_file_fstat (fd, + G_LOCAL_FILE_STAT_FIELD_BASIC_STATS, + G_LOCAL_FILE_STAT_FIELD_ALL, + &stat_buf) == -1) { int errsv = errno; diff --git a/gio/glocalfileinfo.h b/gio/glocalfileinfo.h index f3dba9b06..9d744d6d4 100644 --- a/gio/glocalfileinfo.h +++ b/gio/glocalfileinfo.h @@ -21,8 +21,11 @@ #ifndef __G_LOCAL_FILE_INFO_H__ #define __G_LOCAL_FILE_INFO_H__ +#include #include #include +#include +#include #include #include #include @@ -48,6 +51,119 @@ typedef struct #define GLocalFileStat struct stat #endif +/* If the system doesn’t have statx() support, emulate it using traditional + * stat(). It supports fields %G_LOCAL_FILE_STAT_FIELD_BASIC_STATS only, and + * always returns all of them. */ +typedef enum +{ + G_LOCAL_FILE_STAT_FIELD_TYPE = (1 << 0), + G_LOCAL_FILE_STAT_FIELD_MODE = (1 << 1), + G_LOCAL_FILE_STAT_FIELD_NLINK = (1 << 2), + G_LOCAL_FILE_STAT_FIELD_UID = (1 << 3), + G_LOCAL_FILE_STAT_FIELD_GID = (1 << 4), + G_LOCAL_FILE_STAT_FIELD_ATIME = (1 << 5), + G_LOCAL_FILE_STAT_FIELD_MTIME = (1 << 6), + G_LOCAL_FILE_STAT_FIELD_CTIME = (1 << 7), + G_LOCAL_FILE_STAT_FIELD_INO = (1 << 8), + G_LOCAL_FILE_STAT_FIELD_SIZE = (1 << 9), + G_LOCAL_FILE_STAT_FIELD_BLOCKS = (1 << 10), + G_LOCAL_FILE_STAT_FIELD_BTIME = (1 << 11), +} GLocalFileStatField; + +#define G_LOCAL_FILE_STAT_FIELD_BASIC_STATS \ + (G_LOCAL_FILE_STAT_FIELD_TYPE | G_LOCAL_FILE_STAT_FIELD_MODE | \ + G_LOCAL_FILE_STAT_FIELD_NLINK | G_LOCAL_FILE_STAT_FIELD_UID | \ + G_LOCAL_FILE_STAT_FIELD_GID | G_LOCAL_FILE_STAT_FIELD_ATIME | \ + G_LOCAL_FILE_STAT_FIELD_MTIME | G_LOCAL_FILE_STAT_FIELD_CTIME | \ + G_LOCAL_FILE_STAT_FIELD_INO | G_LOCAL_FILE_STAT_FIELD_SIZE | \ + G_LOCAL_FILE_STAT_FIELD_BLOCKS) +#define G_LOCAL_FILE_STAT_FIELD_ALL (G_LOCAL_FILE_STAT_FIELD_BASIC_STATS | G_LOCAL_FILE_STAT_FIELD_BTIME) + +static inline int +g_local_file_fstat (int fd, + GLocalFileStatField mask, + GLocalFileStatField mask_required, + GLocalFileStat *stat_buf) +{ + if ((G_LOCAL_FILE_STAT_FIELD_BASIC_STATS & (mask_required & mask)) != (mask_required & mask)) + { + /* Only G_LOCAL_FILE_STAT_FIELD_BASIC_STATS are supported. */ + errno = ERANGE; + return -1; + } + +#ifdef G_OS_WIN32 + return GLIB_PRIVATE_CALL (g_win32_fstat) (fd, stat_buf); +#else + return fstat (fd, stat_buf); +#endif +} + +static inline int +g_local_file_fstatat (int fd, + const char *path, + int flags, + GLocalFileStatField mask, + GLocalFileStatField mask_required, + GLocalFileStat *stat_buf) +{ + if ((G_LOCAL_FILE_STAT_FIELD_BASIC_STATS & (mask_required & mask)) != (mask_required & mask)) + { + /* Only G_LOCAL_FILE_STAT_FIELD_BASIC_STATS are supported. */ + errno = ERANGE; + return -1; + } + +#ifdef G_OS_WIN32 + /* Currently not supported on Windows */ + errno = ENOSYS; + return -1; +#else + return fstatat (fd, path, stat_buf, flags); +#endif +} + +static inline int +g_local_file_lstat (const char *path, + GLocalFileStatField mask, + GLocalFileStatField mask_required, + GLocalFileStat *stat_buf) +{ + if ((G_LOCAL_FILE_STAT_FIELD_BASIC_STATS & (mask_required & mask)) != (mask_required & mask)) + { + /* Only G_LOCAL_FILE_STAT_FIELD_BASIC_STATS are supported. */ + errno = ERANGE; + return -1; + } + +#ifdef G_OS_WIN32 + return GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (path, stat_buf); +#else + return g_lstat (path, stat_buf); +#endif +} + +static inline int +g_local_file_stat (const char *path, + GLocalFileStatField mask, + GLocalFileStatField mask_required, + GLocalFileStat *stat_buf) +{ + if ((G_LOCAL_FILE_STAT_FIELD_BASIC_STATS & (mask_required & mask)) != (mask_required & mask)) + { + /* Only G_LOCAL_FILE_STAT_FIELD_BASIC_STATS are supported. */ + errno = ERANGE; + return -1; + } + +#ifdef G_OS_WIN32 + return GLIB_PRIVATE_CALL (g_win32_stat_utf8) (path, stat_buf); +#else + return stat (path, stat_buf); +#endif +} + +inline static gboolean _g_stat_has_field (const GLocalFileStat *buf, GLocalFileStatField field) { return (G_LOCAL_FILE_STAT_FIELD_BASIC_STATS & field) == field; } #ifndef G_OS_WIN32 inline static mode_t _g_stat_mode (const GLocalFileStat *buf) { return buf->st_mode; } diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c index 2057e3d3a..aa018a062 100644 --- a/gio/glocalfileoutputstream.c +++ b/gio/glocalfileoutputstream.c @@ -424,7 +424,7 @@ _g_local_file_output_stream_really_close (GLocalFileOutputStream *file, #ifndef G_OS_WIN32 /* Already did the fstat() and close() above on Win32 */ - if (fstat (file->priv->fd, &final_stat) == 0) + if (g_local_file_fstat (file->priv->fd, G_LOCAL_FILE_STAT_FIELD_MTIME, G_LOCAL_FILE_STAT_FIELD_ALL, &final_stat) == 0) file->priv->etag = _g_local_file_info_create_etag (&final_stat); if (!g_close (file->priv->fd, NULL)) @@ -891,12 +891,15 @@ handle_overwrite_open (const char *filename, g_free (display_name); return -1; } - -#ifdef G_OS_WIN32 - res = GLIB_PRIVATE_CALL (g_win32_fstat) (fd, &original_stat); -#else - res = fstat (fd, &original_stat); -#endif + + res = g_local_file_fstat (fd, + G_LOCAL_FILE_STAT_FIELD_TYPE | + G_LOCAL_FILE_STAT_FIELD_MODE | + G_LOCAL_FILE_STAT_FIELD_UID | + G_LOCAL_FILE_STAT_FIELD_GID | + G_LOCAL_FILE_STAT_FIELD_MTIME | + G_LOCAL_FILE_STAT_FIELD_NLINK, + G_LOCAL_FILE_STAT_FIELD_ALL, &original_stat); errsv = errno; if (res != 0) @@ -986,11 +989,13 @@ handle_overwrite_open (const char *filename, GLocalFileStat tmp_statbuf; int tres; -#ifdef G_OS_WIN32 - tres = GLIB_PRIVATE_CALL (g_win32_fstat) (tmpfd, &tmp_statbuf); -#else - tres = fstat (tmpfd, &tmp_statbuf); -#endif + tres = g_local_file_fstat (tmpfd, + G_LOCAL_FILE_STAT_FIELD_TYPE | + G_LOCAL_FILE_STAT_FIELD_MODE | + G_LOCAL_FILE_STAT_FIELD_UID | + G_LOCAL_FILE_STAT_FIELD_GID, + G_LOCAL_FILE_STAT_FIELD_ALL, &tmp_statbuf); + /* Check that we really needed to change something */ if (tres != 0 || _g_stat_uid (&original_stat) != _g_stat_uid (&tmp_statbuf) || @@ -1014,7 +1019,7 @@ handle_overwrite_open (const char *filename, if (create_backup) { #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD) - struct stat tmp_statbuf; + GLocalFileStat tmp_statbuf; #endif char *backup_filename; int bfd; @@ -1050,7 +1055,7 @@ handle_overwrite_open (const char *filename, * bits for the group same as the protection bits for * others. */ #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD) - if (fstat (bfd, &tmp_statbuf) != 0) + if (g_local_file_fstat (bfd, G_LOCAL_FILE_STAT_FIELD_GID, G_LOCAL_FILE_STAT_FIELD_ALL, &tmp_statbuf) != 0) { g_set_error_literal (error, G_IO_ERROR,