diff --git a/gio/gfileinfo.h b/gio/gfileinfo.h index 073a7496b..da202e6a7 100644 --- a/gio/gfileinfo.h +++ b/gio/gfileinfo.h @@ -540,7 +540,8 @@ typedef struct _GFileInfoClass GFileInfoClass; * and contains the time since the file was created, in seconds since the UNIX * epoch. * - * This corresponds to the NTFS ctime. + * This may correspond to Linux stx_btime, FreeBSD st_birthtim, NetBSD + * st_birthtime or NTFS ctime. **/ #define G_FILE_ATTRIBUTE_TIME_CREATED "time::created" /* uint64 */ diff --git a/gio/glocalfile.c b/gio/glocalfile.c index 4d61663ba..f92e6ed20 100644 --- a/gio/glocalfile.c +++ b/gio/glocalfile.c @@ -1352,13 +1352,9 @@ 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 (buf.st_mode)) + if (ret == 0 && S_ISDIR (_g_stat_mode (&buf))) { (void) g_close (fd, NULL); g_set_io_error (error, @@ -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); @@ -2730,7 +2728,7 @@ g_local_file_measure_size_of_file (gint parent_fd, /* If not at the toplevel, check for a device boundary. */ if (state->flags & G_FILE_MEASURE_NO_XDEV) - if (state->contained_on != buf.st_dev) + if (state->contained_on != _g_stat_dev (&buf)) return TRUE; } else @@ -2738,7 +2736,7 @@ g_local_file_measure_size_of_file (gint parent_fd, /* If, however, this is the toplevel, set the device number so * that recursive invocations can compare against it. */ - state->contained_on = buf.st_dev; + state->contained_on = _g_stat_dev (&buf); } #if defined (G_OS_WIN32) @@ -2747,12 +2745,12 @@ g_local_file_measure_size_of_file (gint parent_fd, else #elif defined (HAVE_STRUCT_STAT_ST_BLOCKS) if (~state->flags & G_FILE_MEASURE_APPARENT_SIZE) - state->disk_usage += buf.st_blocks * G_GUINT64_CONSTANT (512); + state->disk_usage += _g_stat_blocks (&buf) * G_GUINT64_CONSTANT (512); else #endif - state->disk_usage += buf.st_size; + state->disk_usage += _g_stat_size (&buf); - if (S_ISDIR (buf.st_mode)) + if (S_ISDIR (_g_stat_mode (&buf))) state->num_dirs++; else state->num_files++; @@ -2787,7 +2785,7 @@ g_local_file_measure_size_of_file (gint parent_fd, } } - if (S_ISDIR (buf.st_mode)) + if (S_ISDIR (_g_stat_mode (&buf))) { int dir_fd = -1; #ifdef AT_FDCWD diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c index 4ca1ce658..a34c1b78a 100644 --- a/gio/glocalfileinfo.c +++ b/gio/glocalfileinfo.c @@ -123,15 +123,17 @@ _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; #else - sec = statbuf->st_mtime; + sec = _g_stat_mtime (statbuf); #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC) usec = statbuf->st_mtimensec / 1000; #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) - usec = statbuf->st_mtim.tv_nsec / 1000; + usec = _g_stat_mtim_nsec (statbuf) / 1000; #else usec = 0; #endif @@ -147,10 +149,10 @@ _g_local_file_info_create_file_id (GLocalFileStat *statbuf) #ifdef G_OS_WIN32 ino = statbuf->file_index; #else - ino = statbuf->st_ino; + ino = _g_stat_ino (statbuf); #endif return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT, - (guint64) statbuf->st_dev, + (guint64) _g_stat_dev (statbuf), ino); } @@ -158,7 +160,7 @@ static char * _g_local_file_info_create_fs_id (GLocalFileStat *statbuf) { return g_strdup_printf ("l%" G_GUINT64_FORMAT, - (guint64) statbuf->st_dev); + (guint64) _g_stat_dev (statbuf)); } #if defined (S_ISLNK) || defined (G_OS_WIN32) @@ -926,7 +928,7 @@ get_access_rights (GFileAttributeMatcher *attribute_matcher, { uid_t uid = geteuid (); - if (uid == statbuf->st_uid || + if (uid == _g_stat_uid (statbuf) || uid == parent_info->owner || uid == 0) writable = TRUE; @@ -959,22 +961,22 @@ set_info_from_stat (GFileInfo *info, file_type = G_FILE_TYPE_UNKNOWN; - if (S_ISREG (statbuf->st_mode)) + if (S_ISREG (_g_stat_mode (statbuf))) file_type = G_FILE_TYPE_REGULAR; - else if (S_ISDIR (statbuf->st_mode)) + else if (S_ISDIR (_g_stat_mode (statbuf))) file_type = G_FILE_TYPE_DIRECTORY; #ifndef G_OS_WIN32 - else if (S_ISCHR (statbuf->st_mode) || - S_ISBLK (statbuf->st_mode) || - S_ISFIFO (statbuf->st_mode) + else if (S_ISCHR (_g_stat_mode (statbuf)) || + S_ISBLK (_g_stat_mode (statbuf)) || + S_ISFIFO (_g_stat_mode (statbuf)) #ifdef S_ISSOCK - || S_ISSOCK (statbuf->st_mode) + || S_ISSOCK (_g_stat_mode (statbuf)) #endif ) file_type = G_FILE_TYPE_SPECIAL; #endif #ifdef S_ISLNK - else if (S_ISLNK (statbuf->st_mode)) + else if (S_ISLNK (_g_stat_mode (statbuf))) file_type = G_FILE_TYPE_SYMBOLIC_LINK; #elif defined (G_OS_WIN32) else if (statbuf->reparse_tag == IO_REPARSE_TAG_SYMLINK || @@ -983,28 +985,28 @@ set_info_from_stat (GFileInfo *info, #endif g_file_info_set_file_type (info, file_type); - g_file_info_set_size (info, statbuf->st_size); + g_file_info_set_size (info, _g_stat_size (statbuf)); - _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_DEVICE, statbuf->st_dev); - _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_NLINK, statbuf->st_nlink); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_DEVICE, _g_stat_dev (statbuf)); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_NLINK, _g_stat_nlink (statbuf)); #ifndef G_OS_WIN32 /* Pointless setting these on Windows even if they exist in the struct */ - _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_INODE, statbuf->st_ino); - _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_UID, statbuf->st_uid); - _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_GID, statbuf->st_gid); - _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_RDEV, statbuf->st_rdev); + _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_INODE, _g_stat_ino (statbuf)); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_UID, _g_stat_uid (statbuf)); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_GID, _g_stat_gid (statbuf)); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_RDEV, _g_stat_rdev (statbuf)); #endif /* Mostly pointless on Windows. * Still, it allows for S_ISREG/S_ISDIR and IWRITE (read-only) checks. */ - _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_MODE, statbuf->st_mode); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_MODE, _g_stat_mode (statbuf)); #if defined (HAVE_STRUCT_STAT_ST_BLKSIZE) - _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCK_SIZE, statbuf->st_blksize); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCK_SIZE, _g_stat_blksize (statbuf)); #endif #if defined (HAVE_STRUCT_STAT_ST_BLOCKS) - _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCKS, statbuf->st_blocks); + _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_BLOCKS, _g_stat_blocks (statbuf)); _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE, - statbuf->st_blocks * G_GUINT64_CONSTANT (512)); + _g_stat_blocks (statbuf) * G_GUINT64_CONSTANT (512)); #elif defined (G_OS_WIN32) _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_ALLOCATED_SIZE, statbuf->allocated_size); @@ -1017,18 +1019,18 @@ set_info_from_stat (GFileInfo *info, _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, statbuf->st_atim.tv_sec); _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atim.tv_nsec / 1000); #else - _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, statbuf->st_mtime); + _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, _g_stat_mtime (statbuf)); #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC) _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, statbuf->st_mtimensec / 1000); #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) - _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, statbuf->st_mtim.tv_nsec / 1000); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, _g_stat_mtim_nsec (statbuf) / 1000); #endif - _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, statbuf->st_atime); + _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, _g_stat_atime (statbuf)); #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC) _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atimensec / 1000); #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC) - _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, statbuf->st_atim.tv_nsec / 1000); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, _g_stat_atim_nsec (statbuf) / 1000); #endif #endif @@ -1038,15 +1040,21 @@ set_info_from_stat (GFileInfo *info, * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions#generic-text-routine-mappings * Thank you, Microsoft! */ - _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED, statbuf->st_ctime); + _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED, _g_stat_ctime (statbuf)); #if defined (HAVE_STRUCT_STAT_ST_CTIMENSEC) _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, statbuf->st_ctimensec / 1000); #elif defined (HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC) - _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, statbuf->st_ctim.tv_nsec / 1000); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, _g_stat_ctim_nsec (statbuf) / 1000); #endif #endif -#if defined (HAVE_STRUCT_STAT_ST_BIRTHTIME) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) +#if defined (HAVE_STATX) + if (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_BTIME)) + { + _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->stx_btime.tv_sec); + _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->stx_btime.tv_nsec / 1000); + } +#elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime); _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, statbuf->st_birthtimensec / 1000); #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC) @@ -1306,16 +1314,16 @@ get_content_type (const char *basename, if (is_symlink && (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))) return g_content_type_from_mime_type ("inode/symlink"); - else if (statbuf != NULL && S_ISDIR(statbuf->st_mode)) + else if (statbuf != NULL && S_ISDIR(_g_stat_mode (statbuf))) return g_content_type_from_mime_type ("inode/directory"); #ifndef G_OS_WIN32 - else if (statbuf != NULL && S_ISCHR(statbuf->st_mode)) + else if (statbuf != NULL && S_ISCHR(_g_stat_mode (statbuf))) return g_content_type_from_mime_type ("inode/chardevice"); - else if (statbuf != NULL && S_ISBLK(statbuf->st_mode)) + else if (statbuf != NULL && S_ISBLK(_g_stat_mode (statbuf))) return g_content_type_from_mime_type ("inode/blockdevice"); - else if (statbuf != NULL && S_ISFIFO(statbuf->st_mode)) + else if (statbuf != NULL && S_ISFIFO(_g_stat_mode (statbuf))) return g_content_type_from_mime_type ("inode/fifo"); - else if (statbuf != NULL && S_ISREG(statbuf->st_mode) && statbuf->st_size == 0) + else if (statbuf != NULL && S_ISREG(_g_stat_mode (statbuf)) && _g_stat_size (statbuf) == 0) { /* Don't sniff zero-length files in order to avoid reading files * that appear normal but are not (eg: files in /proc and /sys) @@ -1328,7 +1336,7 @@ get_content_type (const char *basename, } #endif #ifdef S_ISSOCK - else if (statbuf != NULL && S_ISSOCK(statbuf->st_mode)) + else if (statbuf != NULL && S_ISSOCK(_g_stat_mode (statbuf))) return g_content_type_from_mime_type ("inode/socket"); #endif else @@ -1773,11 +1781,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 +1803,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_BTIME, + G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME), + &statbuf); if (res == -1) { @@ -1827,12 +1830,12 @@ _g_local_file_info_get (const char *basename, stat_ok = res != -1; if (stat_ok) - device = statbuf.st_dev; + device = _g_stat_dev (&statbuf); else device = 0; #ifdef S_ISLNK - is_symlink = stat_ok && S_ISLNK (statbuf.st_mode); + is_symlink = stat_ok && S_ISLNK (_g_stat_mode (&statbuf)); #elif defined (G_OS_WIN32) /* glib already checked the FILE_ATTRIBUTE_REPARSE_POINT for us */ is_symlink = stat_ok && @@ -1850,11 +1853,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_BTIME, + G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME), + &statbuf2); /* Report broken links as symlinks */ if (res != -1) @@ -1871,7 +1873,7 @@ _g_local_file_info_get (const char *basename, set_info_from_stat (info, &statbuf, attribute_matcher); #ifdef G_OS_UNIX - if (stat_ok && _g_local_file_is_lost_found_dir (path, statbuf.st_dev)) + if (stat_ok && _g_local_file_is_lost_found_dir (path, _g_stat_dev (&statbuf))) g_file_info_set_is_hidden (info, TRUE); #endif @@ -1886,7 +1888,7 @@ _g_local_file_info_get (const char *basename, } if (basename != NULL && basename[strlen (basename) -1] == '~' && - (stat_ok && S_ISREG (statbuf.st_mode))) + (stat_ok && S_ISREG (_g_stat_mode (&statbuf)))) _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_IS_BACKUP, TRUE); #else if (statbuf.attributes & FILE_ATTRIBUTE_HIDDEN) @@ -1980,7 +1982,7 @@ _g_local_file_info_get (const char *basename, win32_get_file_user_info (path, NULL, &name, NULL); #else if (stat_ok) - name = get_username_from_uid (statbuf.st_uid); + name = get_username_from_uid (_g_stat_uid (&statbuf)); #endif if (name) _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER, name); @@ -1995,7 +1997,7 @@ _g_local_file_info_get (const char *basename, win32_get_file_user_info (path, NULL, NULL, &name); #else if (stat_ok) - name = get_realname_from_uid (statbuf.st_uid); + name = get_realname_from_uid (_g_stat_uid (&statbuf)); #endif if (name) _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_USER_REAL, name); @@ -2010,7 +2012,7 @@ _g_local_file_info_get (const char *basename, win32_get_file_user_info (path, &name, NULL, NULL); #else if (stat_ok) - name = get_groupname_from_gid (statbuf.st_gid); + name = get_groupname_from_gid (_g_stat_gid (&statbuf)); #endif if (name) _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_OWNER_GROUP, name); @@ -2019,7 +2021,7 @@ _g_local_file_info_get (const char *basename, if (stat_ok && parent_info && parent_info->device != 0 && _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT) && - (statbuf.st_dev != parent_info->device || statbuf.st_ino == parent_info->inode)) + (_g_stat_dev (&statbuf) != parent_info->device || _g_stat_ino (&statbuf) == parent_info->inode)) _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT, TRUE); if (stat_ok) @@ -2073,14 +2075,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_BTIME, + G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_BTIME), + &stat_buf) == -1) { int errsv = errno; diff --git a/gio/glocalfileinfo.h b/gio/glocalfileinfo.h index 4c58c8635..f2beb70cd 100644 --- a/gio/glocalfileinfo.h +++ b/gio/glocalfileinfo.h @@ -21,12 +21,24 @@ #ifndef __G_LOCAL_FILE_INFO_H__ #define __G_LOCAL_FILE_INFO_H__ +/* Needed for statx() */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include #include #include +#include +#include #include #include #include +#ifdef HAVE_STATX +#include +#endif + G_BEGIN_DECLS typedef struct @@ -41,6 +53,119 @@ typedef struct GDestroyNotify free_extra_data; } GLocalParentFileInfo; +#ifdef HAVE_STATX +#define GLocalFileStat struct statx + +typedef enum +{ + G_LOCAL_FILE_STAT_FIELD_TYPE = STATX_TYPE, + G_LOCAL_FILE_STAT_FIELD_MODE = STATX_MODE, + G_LOCAL_FILE_STAT_FIELD_NLINK = STATX_NLINK, + G_LOCAL_FILE_STAT_FIELD_UID = STATX_UID, + G_LOCAL_FILE_STAT_FIELD_GID = STATX_GID, + G_LOCAL_FILE_STAT_FIELD_ATIME = STATX_ATIME, + G_LOCAL_FILE_STAT_FIELD_MTIME = STATX_MTIME, + G_LOCAL_FILE_STAT_FIELD_CTIME = STATX_CTIME, + G_LOCAL_FILE_STAT_FIELD_INO = STATX_INO, + G_LOCAL_FILE_STAT_FIELD_SIZE = STATX_SIZE, + G_LOCAL_FILE_STAT_FIELD_BLOCKS = STATX_BLOCKS, + G_LOCAL_FILE_STAT_FIELD_BTIME = STATX_BTIME, +} GLocalFileStatField; + +#define G_LOCAL_FILE_STAT_FIELD_BASIC_STATS STATX_BASIC_STATS +#define G_LOCAL_FILE_STAT_FIELD_ALL STATX_ALL + +static inline int +g_local_file_statx (int dirfd, + const char *pathname, + int flags, + GLocalFileStatField mask, + GLocalFileStatField mask_required, + GLocalFileStat *stat_buf) +{ + int retval; + + /* Allow the caller to set mask_required==G_LOCAL_FILE_STAT_FIELD_ALL as a + * shortcut for saying it’s equal to @mask. */ + mask_required &= mask; + + retval = statx (dirfd, pathname, flags, mask, stat_buf); + if (retval == 0 && (stat_buf->stx_mask & mask_required) != mask_required) + { + /* Not all required fields could be returned. */ + errno = ERANGE; + return -1; + } + + return retval; +} + +static inline int +g_local_file_fstat (int fd, + GLocalFileStatField mask, + GLocalFileStatField mask_required, + GLocalFileStat *stat_buf) +{ + return g_local_file_statx (fd, "", AT_EMPTY_PATH, mask, mask_required, stat_buf); +} + +static inline int +g_local_file_fstatat (int fd, + const char *path, + int flags, + GLocalFileStatField mask, + GLocalFileStatField mask_required, + GLocalFileStat *stat_buf) +{ + return g_local_file_statx (fd, path, flags, mask, mask_required, stat_buf); +} + +static inline int +g_local_file_lstat (const char *path, + GLocalFileStatField mask, + GLocalFileStatField mask_required, + GLocalFileStat *stat_buf) +{ + return g_local_file_statx (AT_FDCWD, path, + AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | AT_STATX_SYNC_AS_STAT, + mask, mask_required, stat_buf); +} + +static inline int +g_local_file_stat (const char *path, + GLocalFileStatField mask, + GLocalFileStatField mask_required, + GLocalFileStat *stat_buf) +{ + return g_local_file_statx (AT_FDCWD, path, + AT_NO_AUTOMOUNT | AT_STATX_SYNC_AS_STAT, + mask, mask_required, stat_buf); +} + +inline static gboolean _g_stat_has_field (const GLocalFileStat *buf, GLocalFileStatField field) { return buf->stx_mask & field; } + +inline static guint16 _g_stat_mode (const GLocalFileStat *buf) { return buf->stx_mode; } +inline static guint32 _g_stat_nlink (const GLocalFileStat *buf) { return buf->stx_nlink; } +inline static dev_t _g_stat_dev (const GLocalFileStat *buf) { return makedev (buf->stx_dev_major, buf->stx_dev_minor); } +inline static guint64 _g_stat_ino (const GLocalFileStat *buf) { return buf->stx_ino; } +inline static guint64 _g_stat_size (const GLocalFileStat *buf) { return buf->stx_size; } + +inline static guint32 _g_stat_uid (const GLocalFileStat *buf) { return buf->stx_uid; } +inline static guint32 _g_stat_gid (const GLocalFileStat *buf) { return buf->stx_gid; } +inline static dev_t _g_stat_rdev (const GLocalFileStat *buf) { return makedev (buf->stx_rdev_major, buf->stx_rdev_minor); } +inline static guint32 _g_stat_blksize (const GLocalFileStat *buf) { return buf->stx_blksize; } + +inline static guint64 _g_stat_blocks (const GLocalFileStat *buf) { return buf->stx_blocks; } + +inline static gint64 _g_stat_atime (const GLocalFileStat *buf) { return buf->stx_atime.tv_sec; } +inline static gint64 _g_stat_ctime (const GLocalFileStat *buf) { return buf->stx_ctime.tv_sec; } +inline static gint64 _g_stat_mtime (const GLocalFileStat *buf) { return buf->stx_mtime.tv_sec; } +inline static guint32 _g_stat_atim_nsec (const GLocalFileStat *buf) { return buf->stx_atime.tv_nsec; } +inline static guint32 _g_stat_ctim_nsec (const GLocalFileStat *buf) { return buf->stx_ctime.tv_nsec; } +inline static guint32 _g_stat_mtim_nsec (const GLocalFileStat *buf) { return buf->stx_mtime.tv_nsec; } + +#else /* if !HAVE_STATX */ + #ifdef G_OS_WIN32 /* We want 64-bit file size, file ID and symlink support */ #define GLocalFileStat GWin32PrivateStat @@ -48,6 +173,158 @@ 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; } +inline static nlink_t _g_stat_nlink (const GLocalFileStat *buf) { return buf->st_nlink; } +#else +inline static guint16 _g_stat_mode (const GLocalFileStat *buf) { return buf->st_mode; } +inline static guint32 _g_stat_nlink (const GLocalFileStat *buf) { return buf->st_nlink; } +#endif +inline static dev_t _g_stat_dev (const GLocalFileStat *buf) { return buf->st_dev; } +inline static ino_t _g_stat_ino (const GLocalFileStat *buf) { return buf->st_ino; } +inline static off_t _g_stat_size (const GLocalFileStat *buf) { return buf->st_size; } + +#ifndef G_OS_WIN32 +inline static uid_t _g_stat_uid (const GLocalFileStat *buf) { return buf->st_uid; } +inline static gid_t _g_stat_gid (const GLocalFileStat *buf) { return buf->st_gid; } +inline static dev_t _g_stat_rdev (const GLocalFileStat *buf) { return buf->st_rdev; } +inline static blksize_t _g_stat_blksize (const GLocalFileStat *buf) { return buf->st_blksize; } +#else +inline static guint16 _g_stat_uid (const GLocalFileStat *buf) { return buf->st_uid; } +inline static guint16 _g_stat_gid (const GLocalFileStat *buf) { return buf->st_gid; } +#endif + +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS +inline static blkcnt_t _g_stat_blocks (const GLocalFileStat *buf) { return buf->st_blocks; } +#endif + +#ifndef G_OS_WIN32 +inline static time_t _g_stat_atime (const GLocalFileStat *buf) { return buf->st_atime; } +inline static time_t _g_stat_ctime (const GLocalFileStat *buf) { return buf->st_ctime; } +inline static time_t _g_stat_mtime (const GLocalFileStat *buf) { return buf->st_mtime; } +#endif +#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC +inline static guint32 _g_stat_atim_nsec (const GLocalFileStat *buf) { return buf->st_atim.tv_nsec; } +inline static guint32 _g_stat_ctim_nsec (const GLocalFileStat *buf) { return buf->st_ctim.tv_nsec; } +inline static guint32 _g_stat_mtim_nsec (const GLocalFileStat *buf) { return buf->st_mtim.tv_nsec; } +#endif + +#endif /* !HAVE_STATX */ + #define G_LOCAL_FILE_INFO_NOSTAT_ATTRIBUTES \ G_FILE_ATTRIBUTE_STANDARD_NAME "," \ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," \ diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c index 136d98241..f34c3e439 100644 --- a/gio/glocalfileoutputstream.c +++ b/gio/glocalfileoutputstream.c @@ -18,6 +18,11 @@ * Author: Alexander Larsson */ +/* Needed for the statx() calls in inline functions in glocalfileinfo.h */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include "config.h" #include @@ -424,7 +429,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 +896,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) @@ -911,9 +919,9 @@ handle_overwrite_open (const char *filename, } /* not a regular file */ - if (!S_ISREG (original_stat.st_mode)) + if (!S_ISREG (_g_stat_mode (&original_stat))) { - if (S_ISDIR (original_stat.st_mode)) + if (S_ISDIR (_g_stat_mode (&original_stat))) g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, @@ -953,7 +961,7 @@ handle_overwrite_open (const char *filename, */ if ((flags & G_FILE_CREATE_REPLACE_DESTINATION) || - (!(original_stat.st_nlink > 1) && !is_symlink)) + (!(_g_stat_nlink (&original_stat) > 1) && !is_symlink)) { char *dirname, *tmp_filename; int tmpfd; @@ -974,10 +982,10 @@ handle_overwrite_open (const char *filename, if ( ! (flags & G_FILE_CREATE_REPLACE_DESTINATION) && ( #ifdef HAVE_FCHOWN - fchown (tmpfd, original_stat.st_uid, original_stat.st_gid) == -1 || + fchown (tmpfd, _g_stat_uid (&original_stat), _g_stat_gid (&original_stat)) == -1 || #endif #ifdef HAVE_FCHMOD - fchmod (tmpfd, original_stat.st_mode & ~S_IFMT) == -1 || + fchmod (tmpfd, _g_stat_mode (&original_stat) & ~S_IFMT) == -1 || #endif 0 ) @@ -986,16 +994,18 @@ 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 || - original_stat.st_uid != tmp_statbuf.st_uid || - original_stat.st_gid != tmp_statbuf.st_gid || - original_stat.st_mode != tmp_statbuf.st_mode) + _g_stat_uid (&original_stat) != _g_stat_uid (&tmp_statbuf) || + _g_stat_gid (&original_stat) != _g_stat_gid (&tmp_statbuf) || + _g_stat_mode (&original_stat) != _g_stat_mode (&tmp_statbuf)) { g_close (tmpfd, NULL); g_unlink (tmp_filename); @@ -1014,7 +1024,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; @@ -1033,7 +1043,7 @@ handle_overwrite_open (const char *filename, bfd = g_open (backup_filename, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, - original_stat.st_mode & 0777); + _g_stat_mode (&original_stat) & 0777); if (bfd == -1) { @@ -1050,7 +1060,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, @@ -1062,12 +1072,12 @@ handle_overwrite_open (const char *filename, goto err_out; } - if ((original_stat.st_gid != tmp_statbuf.st_gid) && - fchown (bfd, (uid_t) -1, original_stat.st_gid) != 0) + if ((_g_stat_gid (&original_stat) != _g_stat_gid (&tmp_statbuf)) && + fchown (bfd, (uid_t) -1, _g_stat_gid (&original_stat)) != 0) { if (fchmod (bfd, - (original_stat.st_mode & 0707) | - ((original_stat.st_mode & 07) << 3)) != 0) + (_g_stat_mode (&original_stat) & 0707) | + ((_g_stat_mode (&original_stat) & 07) << 3)) != 0) { g_set_error_literal (error, G_IO_ERROR, diff --git a/gio/tests/thumbnail-verification.c b/gio/tests/thumbnail-verification.c index d50638fe7..f439c8b06 100644 --- a/gio/tests/thumbnail-verification.c +++ b/gio/tests/thumbnail-verification.c @@ -99,12 +99,17 @@ test_validity (void) thumbnail_path = g_test_get_filename (G_TEST_DIST, "thumbnails", tests[i].filename, NULL); file_uri = g_strconcat ("file:///tmp/", tests[i].filename, NULL); +#ifdef HAVE_STATX + stat_buf.stx_mtime.tv_sec = tests[i].mtime; + stat_buf.stx_size = tests[i].size; +#else #ifdef G_OS_WIN32 stat_buf.st_mtim.tv_sec = tests[i].mtime; #else stat_buf.st_mtime = tests[i].mtime; #endif stat_buf.st_size = tests[i].size; +#endif result = thumbnail_verify (thumbnail_path, file_uri, &stat_buf); diff --git a/gio/thumbnail-verify.c b/gio/thumbnail-verify.c index 4baeeeae8..316e8a61f 100644 --- a/gio/thumbnail-verify.c +++ b/gio/thumbnail-verify.c @@ -235,9 +235,9 @@ thumbnail_verify (const char *thumbnail_path, #ifdef G_OS_WIN32 expected_info.mtime = (guint64) file_stat_buf->st_mtim.tv_sec; #else - expected_info.mtime = (guint64) file_stat_buf->st_mtime; + expected_info.mtime = _g_stat_mtime (file_stat_buf); #endif - expected_info.size = file_stat_buf->st_size; + expected_info.size = _g_stat_size (file_stat_buf); file = g_mapped_file_new (thumbnail_path, FALSE, NULL); if (file) diff --git a/meson.build b/meson.build index 9a0293120..19ac186e6 100644 --- a/meson.build +++ b/meson.build @@ -328,6 +328,23 @@ if cc.has_header('linux/netlink.h') glib_conf.set('HAVE_NETLINK', 1) endif +# Is statx() supported? Android systems don’t reliably support it as of August 2020. +statx_code = ''' + #ifndef _GNU_SOURCE + #define _GNU_SOURCE + #endif + #include + #include + int main (void) + { + struct statx stat_buf; + return statx (AT_FDCWD, "/", AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS | STATX_BTIME, &stat_buf); + } + ''' +if host_system != 'android' and cc.compiles(statx_code, name : 'statx() test') + glib_conf.set('HAVE_STATX', 1) +endif + if glib_conf.has('HAVE_LOCALE_H') if cc.has_header_symbol('locale.h', 'LC_MESSAGES') glib_conf.set('HAVE_LC_MESSAGES', 1)