/* gstdio.c - wrappers for C library functions
*
* Copyright 2004 Tor Lillqvist
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see .
*/
#include "config.h"
#include "glibconfig.h"
/* Don’t redefine (for example) g_open() to open(), since we actually want to
* define g_open() in this file and export it as a symbol. See gstdio.h. */
#define G_STDIO_WRAP_ON_UNIX
#include
#include
#include
#ifdef G_OS_UNIX
#include
#endif
#ifdef G_OS_WIN32
#include
#include
#include
#include
#include
#include
#include /* for MB_CUR_MAX */
#else
#include
#include
#endif
#include "gstdio.h"
#include "gstdioprivate.h"
#if !defined (G_OS_UNIX) && !defined (G_OS_WIN32)
#error Please port this to your operating system
#endif
#if defined (_MSC_VER) && !defined(_WIN64)
#undef _wstat
#define _wstat _wstat32
#endif
#if defined (G_OS_WIN32)
/* We can't include Windows DDK and Windows SDK simultaneously,
* so let's copy this here from MinGW-w64 DDK.
* The structure is ultimately documented here:
* https://msdn.microsoft.com/en-us/library/ff552012(v=vs.85).aspx
*/
typedef struct _REPARSE_DATA_BUFFER
{
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union
{
struct
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct
{
UCHAR DataBuffer[1];
} GenericReparseBuffer;
};
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
static int
w32_error_to_errno (DWORD error_code)
{
switch (error_code)
{
case ERROR_ACCESS_DENIED:
return EACCES;
break;
case ERROR_ALREADY_EXISTS:
case ERROR_FILE_EXISTS:
return EEXIST;
case ERROR_FILE_NOT_FOUND:
return ENOENT;
break;
case ERROR_INVALID_FUNCTION:
return EFAULT;
break;
case ERROR_INVALID_HANDLE:
return EBADF;
break;
case ERROR_INVALID_PARAMETER:
return EINVAL;
break;
case ERROR_LOCK_VIOLATION:
case ERROR_SHARING_VIOLATION:
return EACCES;
break;
case ERROR_NOT_ENOUGH_MEMORY:
case ERROR_OUTOFMEMORY:
return ENOMEM;
break;
case ERROR_NOT_SAME_DEVICE:
return EXDEV;
break;
case ERROR_PATH_NOT_FOUND:
return ENOENT; /* or ELOOP, or ENAMETOOLONG */
break;
default:
return EIO;
break;
}
}
#include "gstdio-private.c"
/* Windows implementation of fopen() does not accept modes such as
* "wb+". The 'b' needs to be appended to "w+", i.e. "w+b". Note
* that otherwise these 2 modes are supposed to be aliases, hence
* swappable at will. TODO: Is this still true?
*/
static void
_g_win32_fix_mode (wchar_t *mode)
{
wchar_t *ptr;
wchar_t temp;
ptr = wcschr (mode, L'+');
if (ptr != NULL && (ptr - mode) > 1)
{
temp = mode[1];
mode[1] = *ptr;
*ptr = temp;
}
}
/* From
* https://support.microsoft.com/en-ca/help/167296/how-to-convert-a-unix-time-t-to-a-win32-filetime-or-systemtime
* FT = UT * 10000000 + 116444736000000000.
* Therefore:
* UT = (FT - 116444736000000000) / 10000000.
* Converts FILETIME to unix epoch time in form
* of a signed 64-bit integer (can be negative).
*
* The function that does the reverse can be found in
* gio/glocalfileinfo.c.
*/
static gint64
_g_win32_filetime_to_unix_time (const FILETIME *ft,
gint32 *nsec)
{
gint64 result;
/* 1 unit of FILETIME is 100ns */
const gint64 hundreds_of_usec_per_sec = 10000000;
/* The difference between January 1, 1601 UTC (FILETIME epoch) and UNIX epoch
* in hundreds of nanoseconds.
*/
const gint64 filetime_unix_epoch_offset = 116444736000000000;
result = ((gint64) ft->dwLowDateTime) | (((gint64) ft->dwHighDateTime) << 32);
result -= filetime_unix_epoch_offset;
if (nsec)
*nsec = (result % hundreds_of_usec_per_sec) * 100;
return result / hundreds_of_usec_per_sec;
}
# ifdef _MSC_VER
# ifndef S_IXUSR
# define _S_IRUSR _S_IREAD
# define _S_IWUSR _S_IWRITE
# define _S_IXUSR _S_IEXEC
# define S_IRUSR _S_IRUSR
# define S_IWUSR _S_IWUSR
# define S_IXUSR _S_IXUSR
# define S_IRGRP (S_IRUSR >> 3)
# define S_IWGRP (S_IWUSR >> 3)
# define S_IXGRP (S_IXUSR >> 3)
# define S_IROTH (S_IRGRP >> 3)
# define S_IWOTH (S_IWGRP >> 3)
# define S_IXOTH (S_IXGRP >> 3)
# endif
# ifndef S_ISDIR
# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
# endif
# endif
/* Uses filename and BHFI to fill a stat64 structure.
* Tries to reproduce the behaviour and quirks of MS C runtime stat().
*/
static int
_g_win32_fill_statbuf_from_handle_info (const wchar_t *filename,
const wchar_t *filename_target,
const BY_HANDLE_FILE_INFORMATION *handle_info,
struct __stat64 *statbuf)
{
wchar_t drive_letter_w = 0;
size_t drive_letter_size = MB_CUR_MAX;
char *drive_letter = _alloca (drive_letter_size);
/* If filename (target or link) is absolute,
* then use the drive letter from it as-is.
*/
if (filename_target != NULL &&
filename_target[0] != L'\0' &&
filename_target[1] == L':')
drive_letter_w = filename_target[0];
else if (filename[0] != L'\0' &&
filename[1] == L':')
drive_letter_w = filename[0];
if (drive_letter_w > 0 &&
iswalpha (drive_letter_w) &&
iswascii (drive_letter_w) &&
wctomb (drive_letter, drive_letter_w) == 1)
statbuf->st_dev = toupper (drive_letter[0]) - 'A'; /* 0 means A: drive */
else
/* Otherwise use the PWD drive.
* Return value of 0 gives us 0 - 1 = -1,
* which is the "no idea" value for st_dev.
*/
statbuf->st_dev = _getdrive () - 1;
statbuf->st_rdev = statbuf->st_dev;
/* Theoretically, it's possible to set it for ext-FS. No idea how.
* Meaningless for all filesystems that Windows normally uses.
*/
statbuf->st_ino = 0;
statbuf->st_mode = 0;
if ((handle_info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
statbuf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
else
statbuf->st_mode |= S_IFREG;
/* No idea what S_IFCHR means here. */
/* S_IFIFO is not even mentioned in MSDN */
/* S_IFBLK is also not mentioned */
/* The aim here is to reproduce MS stat() behaviour,
* even if it's braindead.
*/
statbuf->st_mode |= S_IRUSR | S_IRGRP | S_IROTH;
if ((handle_info->dwFileAttributes & FILE_ATTRIBUTE_READONLY) != FILE_ATTRIBUTE_READONLY)
statbuf->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
if (!S_ISDIR (statbuf->st_mode))
{
const wchar_t *name;
const wchar_t *dot = NULL;
if (filename_target != NULL)
name = filename_target;
else
name = filename;
do
{
wchar_t *last_dot = wcschr (name, L'.');
if (last_dot == NULL)
break;
dot = last_dot;
name = &last_dot[1];
}
while (TRUE);
if ((dot != NULL &&
(wcsicmp (dot, L".exe") == 0 ||
wcsicmp (dot, L".com") == 0 ||
wcsicmp (dot, L".bat") == 0 ||
wcsicmp (dot, L".cmd") == 0)))
statbuf->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
}
statbuf->st_nlink = handle_info->nNumberOfLinks;
statbuf->st_uid = statbuf->st_gid = 0;
statbuf->st_size = (((guint64) handle_info->nFileSizeHigh) << 32) | handle_info->nFileSizeLow;
statbuf->st_ctime = _g_win32_filetime_to_unix_time (&handle_info->ftCreationTime, NULL);
statbuf->st_mtime = _g_win32_filetime_to_unix_time (&handle_info->ftLastWriteTime, NULL);
statbuf->st_atime = _g_win32_filetime_to_unix_time (&handle_info->ftLastAccessTime, NULL);
return 0;
}
/* Fills our private stat-like structure using data from
* a normal stat64 struct, BHFI, FSI and a reparse tag.
*/
static void
_g_win32_fill_privatestat (const struct __stat64 *statbuf,
const BY_HANDLE_FILE_INFORMATION *handle_info,
const FILE_STANDARD_INFO *std_info,
DWORD reparse_tag,
GWin32PrivateStat *buf)
{
gint32 nsec;
buf->st_dev = statbuf->st_dev;
buf->st_ino = statbuf->st_ino;
buf->st_mode = statbuf->st_mode;
buf->volume_serial = handle_info->dwVolumeSerialNumber;
buf->file_index = (((guint64) handle_info->nFileIndexHigh) << 32) | handle_info->nFileIndexLow;
buf->attributes = handle_info->dwFileAttributes;
buf->st_nlink = handle_info->nNumberOfLinks;
buf->st_size = (((guint64) handle_info->nFileSizeHigh) << 32) | handle_info->nFileSizeLow;
buf->allocated_size = std_info->AllocationSize.QuadPart;
buf->reparse_tag = reparse_tag;
buf->st_ctim.tv_sec = _g_win32_filetime_to_unix_time (&handle_info->ftCreationTime, &nsec);
buf->st_ctim.tv_nsec = nsec;
buf->st_mtim.tv_sec = _g_win32_filetime_to_unix_time (&handle_info->ftLastWriteTime, &nsec);
buf->st_mtim.tv_nsec = nsec;
buf->st_atim.tv_sec = _g_win32_filetime_to_unix_time (&handle_info->ftLastAccessTime, &nsec);
buf->st_atim.tv_nsec = nsec;
}
/* Read the link data from a symlink/mountpoint represented
* by the handle. Also reads reparse tag.
* @reparse_tag receives the tag. Can be %NULL if @buf or @alloc_buf
* is non-NULL.
* @buf receives the link data. Can be %NULL if reparse_tag is non-%NULL.
* Mutually-exclusive with @alloc_buf.
* @buf_size is the size of the @buf, in bytes.
* @alloc_buf points to a location where internally-allocated buffer
* pointer will be written. That buffer receives the
* link data. Mutually-exclusive with @buf.
* @terminate ensures that the buffer is NUL-terminated if
* it isn't already. Note that this can erase useful
* data if @buf is provided and @buf_size is too small.
* Specifically, with @buf_size <= 2 the buffer will
* receive an empty string, even if there is some
* data in the reparse point.
* The contents of @buf or @alloc_buf are presented as-is - could
* be non-NUL-terminated (unless @terminate is %TRUE) or even malformed.
* Returns the number of bytes (!) placed into @buf or @alloc_buf,
* including NUL-terminator (if any).
*
* Returned value of 0 means that there's no recognizable data in the
* reparse point. @alloc_buf will not be allocated in that case,
* and @buf will be left unmodified.
*
* If @buf and @alloc_buf are %NULL, returns 0 to indicate success.
* Returns -1 to indicate an error, sets errno.
*/
static int
_g_win32_readlink_handle_raw (HANDLE h,
DWORD *reparse_tag,
gunichar2 *buf,
gsize buf_size,
gunichar2 **alloc_buf,
gboolean terminate)
{
DWORD error_code;
DWORD returned_bytes = 0;
BYTE *data = NULL;
gsize to_copy;
/* This is 16k. It's impossible to make DeviceIoControl() tell us
* the required size. NtFsControlFile() does have such a feature,
* but for some reason it doesn't work with CreateFile()-returned handles.
* The only alternative is to repeatedly call DeviceIoControl()
* with bigger and bigger buffers, until it succeeds.
* We choose to sacrifice stack space for speed.
*/
BYTE max_buffer[sizeof (REPARSE_DATA_BUFFER) + MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = {0,};
DWORD max_buffer_size = sizeof (REPARSE_DATA_BUFFER) + MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
REPARSE_DATA_BUFFER *rep_buf;
g_return_val_if_fail ((buf != NULL || alloc_buf != NULL || reparse_tag != NULL) &&
(buf == NULL || alloc_buf == NULL),
-1);
if (!DeviceIoControl (h, FSCTL_GET_REPARSE_POINT, NULL, 0,
max_buffer,
max_buffer_size,
&returned_bytes, NULL))
{
error_code = GetLastError ();
errno = w32_error_to_errno (error_code);
return -1;
}
rep_buf = (REPARSE_DATA_BUFFER *) max_buffer;
if (reparse_tag != NULL)
*reparse_tag = rep_buf->ReparseTag;
if (buf == NULL && alloc_buf == NULL)
return 0;
if (rep_buf->ReparseTag == IO_REPARSE_TAG_SYMLINK)
{
data = &((BYTE *) rep_buf->SymbolicLinkReparseBuffer.PathBuffer)[rep_buf->SymbolicLinkReparseBuffer.SubstituteNameOffset];
to_copy = rep_buf->SymbolicLinkReparseBuffer.SubstituteNameLength;
}
else if (rep_buf->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
{
data = &((BYTE *) rep_buf->MountPointReparseBuffer.PathBuffer)[rep_buf->MountPointReparseBuffer.SubstituteNameOffset];
to_copy = rep_buf->MountPointReparseBuffer.SubstituteNameLength;
}
else
to_copy = 0;
return _g_win32_copy_and_maybe_terminate (data, to_copy, buf, buf_size, alloc_buf, terminate);
}
/* Read the link data from a symlink/mountpoint represented
* by the @filename.
* @filename is the name of the file.
* @reparse_tag receives the tag. Can be %NULL if @buf or @alloc_buf
* is non-%NULL.
* @buf receives the link data. Mutually-exclusive with @alloc_buf.
* @buf_size is the size of the @buf, in bytes.
* @alloc_buf points to a location where internally-allocated buffer
* pointer will be written. That buffer receives the
* link data. Mutually-exclusive with @buf.
* @terminate ensures that the buffer is NUL-terminated if
* it isn't already
* The contents of @buf or @alloc_buf are presented as-is - could
* be non-NUL-terminated (unless @terminate is TRUE) or even malformed.
* Returns the number of bytes (!) placed into @buf or @alloc_buf.
* Returned value of 0 means that there's no recognizable data in the
* reparse point. @alloc_buf will not be allocated in that case,
* and @buf will be left unmodified.
* If @buf and @alloc_buf are %NULL, returns 0 to indicate success.
* Returns -1 to indicate an error, sets errno.
*/
static int
_g_win32_readlink_utf16_raw (const gunichar2 *filename,
DWORD *reparse_tag,
gunichar2 *buf,
gsize buf_size,
gunichar2 **alloc_buf,
gboolean terminate)
{
HANDLE h;
DWORD attributes;
DWORD to_copy;
DWORD error_code;
if ((attributes = GetFileAttributesW (filename)) == 0)
{
error_code = GetLastError ();
errno = w32_error_to_errno (error_code);
return -1;
}
if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
{
errno = EINVAL;
return -1;
}
/* To read symlink target we need to open the file as a reparse
* point and use DeviceIoControl() on it.
*/
h = CreateFileW (filename,
FILE_READ_EA,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_OPEN_REPARSE_POINT
| (attributes & FILE_ATTRIBUTE_DIRECTORY ? FILE_FLAG_BACKUP_SEMANTICS : 0),
NULL);
if (h == INVALID_HANDLE_VALUE)
{
error_code = GetLastError ();
errno = w32_error_to_errno (error_code);
return -1;
}
to_copy = _g_win32_readlink_handle_raw (h, reparse_tag, buf, buf_size, alloc_buf, terminate);
CloseHandle (h);
return to_copy;
}
/* Read the link data from a symlink/mountpoint represented
* by a UTF-16 filename or a file handle.
* @filename is the name of the file. Mutually-exclusive with @file_handle.
* @file_handle is the handle of the file. Mutually-exclusive with @filename.
* @reparse_tag receives the tag. Can be %NULL if @buf or @alloc_buf
* is non-%NULL.
* @buf receives the link data. Mutually-exclusive with @alloc_buf.
* @buf_size is the size of the @buf, in bytes.
* @alloc_buf points to a location where internally-allocated buffer
* pointer will be written. That buffer receives the
* link data. Mutually-exclusive with @buf.
* @terminate ensures that the buffer is NUL-terminated if
* it isn't already
* The contents of @buf or @alloc_buf are adjusted
* (extended or nt object manager prefix is stripped),
* but otherwise they are presented as-is - could be non-NUL-terminated
* (unless @terminate is TRUE) or even malformed.
* Returns the number of bytes (!) placed into @buf or @alloc_buf.
* Returned value of 0 means that there's no recognizable data in the
* reparse point. @alloc_buf will not be allocated in that case,
* and @buf will be left unmodified.
* Returns -1 to indicate an error, sets errno.
*/
static int
_g_win32_readlink_utf16_handle (const gunichar2 *filename,
HANDLE file_handle,
DWORD *reparse_tag,
gunichar2 *buf,
gsize buf_size,
gunichar2 **alloc_buf,
gboolean terminate)
{
int result;
gsize string_size;
g_return_val_if_fail ((buf != NULL || alloc_buf != NULL || reparse_tag != NULL) &&
(filename != NULL || file_handle != NULL) &&
(buf == NULL || alloc_buf == NULL) &&
(filename == NULL || file_handle == NULL),
-1);
if (filename)
result = _g_win32_readlink_utf16_raw (filename, reparse_tag, buf, buf_size, alloc_buf, terminate);
else
result = _g_win32_readlink_handle_raw (file_handle, reparse_tag, buf, buf_size, alloc_buf, terminate);
if (result <= 0)
return result;
/* Ensure that output is a multiple of sizeof (gunichar2),
* cutting any trailing partial gunichar2, if present.
*/
result -= result % sizeof (gunichar2);
if (result <= 0)
return result;
/* DeviceIoControl () tends to return filenames as NT Object Manager
* names , i.e. "\\??\\C:\\foo\\bar".
* Remove the leading 4-byte "\\??\\" prefix, as glib (as well as many W32 API
* functions) is unprepared to deal with it. Unless it has no 'x:' drive
* letter part after the prefix, in which case we leave everything
* as-is, because the path could be "\\??\\Volume{GUID}" - stripping
* the prefix will allow it to be confused with relative links
* targeting "Volume{GUID}".
*/
string_size = result / sizeof (gunichar2);
_g_win32_strip_extended_ntobjm_prefix (buf ? buf : *alloc_buf, &string_size);
return string_size * sizeof (gunichar2);
}
/* Works like stat() or lstat(), depending on the value of @for_symlink,
* but accepts filename in UTF-16 and fills our custom stat structure.
* The @filename must not have trailing slashes.
*/
static int
_g_win32_stat_utf16_no_trailing_slashes (const gunichar2 *filename,
GWin32PrivateStat *buf,
gboolean for_symlink)
{
struct __stat64 statbuf;
BY_HANDLE_FILE_INFORMATION handle_info;
FILE_STANDARD_INFO std_info;
gboolean is_symlink = FALSE;
wchar_t *filename_target = NULL;
DWORD immediate_attributes;
DWORD open_flags;
gboolean is_directory;
DWORD reparse_tag = 0;
DWORD error_code;
BOOL succeeded_so_far;
HANDLE file_handle;
immediate_attributes = GetFileAttributesW (filename);
if (immediate_attributes == INVALID_FILE_ATTRIBUTES)
{
error_code = GetLastError ();
errno = w32_error_to_errno (error_code);
return -1;
}
is_symlink = (immediate_attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT;
is_directory = (immediate_attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
open_flags = FILE_ATTRIBUTE_NORMAL;
if (for_symlink && is_symlink)
open_flags |= FILE_FLAG_OPEN_REPARSE_POINT;
if (is_directory)
open_flags |= FILE_FLAG_BACKUP_SEMANTICS;
file_handle = CreateFileW (filename, FILE_READ_ATTRIBUTES | FILE_READ_EA,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
open_flags,
NULL);
if (file_handle == INVALID_HANDLE_VALUE)
{
error_code = GetLastError ();
errno = w32_error_to_errno (error_code);
return -1;
}
succeeded_so_far = GetFileInformationByHandle (file_handle,
&handle_info);
error_code = GetLastError ();
if (succeeded_so_far)
{
succeeded_so_far = GetFileInformationByHandleEx (file_handle,
FileStandardInfo,
&std_info,
sizeof (std_info));
error_code = GetLastError ();
}
if (!succeeded_so_far)
{
CloseHandle (file_handle);
errno = w32_error_to_errno (error_code);
return -1;
}
/* It's tempting to use GetFileInformationByHandleEx(FileAttributeTagInfo),
* but it always reports that the ReparseTag is 0.
* We already have a handle open for symlink, use that.
* For the target we have to specify a filename, and the function
* will open another handle internally.
*/
if (is_symlink &&
_g_win32_readlink_utf16_handle (for_symlink ? NULL : filename,
for_symlink ? file_handle : NULL,
&reparse_tag,
NULL, 0,
for_symlink ? NULL : &filename_target,
TRUE) < 0)
{
CloseHandle (file_handle);
return -1;
}
CloseHandle (file_handle);
_g_win32_fill_statbuf_from_handle_info (filename,
filename_target,
&handle_info,
&statbuf);
g_free (filename_target);
_g_win32_fill_privatestat (&statbuf,
&handle_info,
&std_info,
reparse_tag,
buf);
return 0;
}
/* Works like fstat(), but fills our custom stat structure. */
static int
_g_win32_stat_fd (int fd,
GWin32PrivateStat *buf)
{
HANDLE file_handle;
gboolean succeeded_so_far;
DWORD error_code;
struct __stat64 statbuf;
BY_HANDLE_FILE_INFORMATION handle_info;
FILE_STANDARD_INFO std_info;
DWORD reparse_tag = 0;
gboolean is_symlink = FALSE;
file_handle = (HANDLE) _get_osfhandle (fd);
if (file_handle == INVALID_HANDLE_VALUE)
return -1;
succeeded_so_far = GetFileInformationByHandle (file_handle,
&handle_info);
error_code = GetLastError ();
if (succeeded_so_far)
{
succeeded_so_far = GetFileInformationByHandleEx (file_handle,
FileStandardInfo,
&std_info,
sizeof (std_info));
error_code = GetLastError ();
}
if (!succeeded_so_far)
{
errno = w32_error_to_errno (error_code);
return -1;
}
is_symlink = (handle_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT;
if (is_symlink &&
_g_win32_readlink_handle_raw (file_handle, &reparse_tag, NULL, 0, NULL, FALSE) < 0)
return -1;
if (_fstat64 (fd, &statbuf) != 0)
return -1;
_g_win32_fill_privatestat (&statbuf,
&handle_info,
&std_info,
reparse_tag,
buf);
return 0;
}
/* Works like stat() or lstat(), depending on the value of @for_symlink,
* but accepts filename in UTF-8 and fills our custom stat structure.
*/
static int
_g_win32_stat_utf8 (const gchar *filename,
GWin32PrivateStat *buf,
gboolean for_symlink)
{
wchar_t *wfilename;
int result;
gsize len;
if (filename == NULL)
{
errno = EINVAL;
return -1;
}
len = strlen (filename);
while (len > 0 && G_IS_DIR_SEPARATOR (filename[len - 1]))
len--;
if (len <= 0 ||
(g_path_is_absolute (filename) && len <= (gsize) (g_path_skip_root (filename) - filename)))
len = strlen (filename);
wfilename = g_utf8_to_utf16 (filename, len, NULL, NULL, NULL);
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
result = _g_win32_stat_utf16_no_trailing_slashes (wfilename, buf, for_symlink);
g_free (wfilename);
return result;
}
/* Works like stat(), but accepts filename in UTF-8
* and fills our custom stat structure.
*/
int
g_win32_stat_utf8 (const gchar *filename,
GWin32PrivateStat *buf)
{
return _g_win32_stat_utf8 (filename, buf, FALSE);
}
/* Works like lstat(), but accepts filename in UTF-8
* and fills our custom stat structure.
*/
int
g_win32_lstat_utf8 (const gchar *filename,
GWin32PrivateStat *buf)
{
return _g_win32_stat_utf8 (filename, buf, TRUE);
}
/* Works like fstat(), but accepts filename in UTF-8
* and fills our custom stat structure.
*/
int
g_win32_fstat (int fd,
GWin32PrivateStat *buf)
{
return _g_win32_stat_fd (fd, buf);
}
/**
* g_win32_readlink_utf8:
* @filename: (type filename): a pathname in UTF-8
* @buf: (array length=buf_size) : a buffer to receive the reparse point
* target path. Mutually-exclusive
* with @alloc_buf.
* @buf_size: size of the @buf, in bytes
* @alloc_buf: points to a location where internally-allocated buffer
* pointer will be written. That buffer receives the
* link data. Mutually-exclusive with @buf.
* @terminate: ensures that the buffer is NUL-terminated if
* it isn't already. If %FALSE, the returned string
* might not be NUL-terminated (depends entirely on
* what the contents of the filesystem are).
*
* Tries to read the reparse point indicated by @filename, filling
* @buf or @alloc_buf with the path that the reparse point redirects to.
* The path will be UTF-8-encoded, and an extended path prefix
* or a NT object manager prefix will be removed from it, if
* possible, but otherwise the path is returned as-is. Specifically,
* it could be a "\\\\Volume{GUID}\\" path. It also might use
* backslashes as path separators.
*
* Returns: -1 on error (sets errno), 0 if there's no (recognizable)
* path in the reparse point (@alloc_buf will not be allocated in that case,
* and @buf will be left unmodified),
* or the number of bytes placed into @buf otherwise,
* including NUL-terminator (if present or if @terminate is TRUE).
* The buffer returned via @alloc_buf should be freed with g_free().
*
* Since: 2.60
*/
int
g_win32_readlink_utf8 (const gchar *filename,
gchar *buf,
gsize buf_size,
gchar **alloc_buf,
gboolean terminate)
{
wchar_t *wfilename;
int result;
wchar_t *buf_utf16;
glong tmp_len;
gchar *tmp;
g_return_val_if_fail ((buf != NULL || alloc_buf != NULL) &&
(buf == NULL || alloc_buf == NULL),
-1);
wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
result = _g_win32_readlink_utf16_handle (wfilename, NULL, NULL,
NULL, 0, &buf_utf16, terminate);
g_free (wfilename);
if (result <= 0)
return result;
tmp = g_utf16_to_utf8 (buf_utf16,
result / sizeof (gunichar2),
NULL,
&tmp_len,
NULL);
g_free (buf_utf16);
if (tmp == NULL)
{
errno = EINVAL;
return -1;
}
if (alloc_buf)
{
*alloc_buf = tmp;
return tmp_len;
}
if ((gsize) tmp_len > buf_size)
tmp_len = buf_size;
memcpy (buf, tmp, tmp_len);
g_free (tmp);
return tmp_len;
}
#endif
/**
* g_access:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @mode: as in access()
*
* A wrapper for the POSIX access() function. This function is used to
* test a pathname for one or several of read, write or execute
* permissions, or just existence.
*
* On Windows, the file protection mechanism is not at all POSIX-like,
* and the underlying function in the C library only checks the
* FAT-style READONLY attribute, and does not look at the ACL of a
* file at all. This function is this in practise almost useless on
* Windows. Software that needs to handle file permissions on Windows
* more exactly should use the Win32 API.
*
* See your C library manual for more details about access().
*
* Returns: zero if the pathname refers to an existing file system
* object that has all the tested permissions, or -1 otherwise
* or on error.
*
* Since: 2.8
*/
int
g_access (const gchar *filename,
int mode)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
#ifndef X_OK
#define X_OK 1
#endif
retval = _waccess (wfilename, mode & ~X_OK);
save_errno = errno;
g_free (wfilename);
errno = save_errno;
return retval;
#else
return access (filename, mode);
#endif
}
/**
* g_chmod:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @mode: as in chmod()
*
* A wrapper for the POSIX chmod() function. The chmod() function is
* used to set the permissions of a file system object.
*
* On Windows the file protection mechanism is not at all POSIX-like,
* and the underlying chmod() function in the C library just sets or
* clears the FAT-style READONLY attribute. It does not touch any
* ACL. Software that needs to manage file permissions on Windows
* exactly should use the Win32 API.
*
* See your C library manual for more details about chmod().
*
* Returns: 0 if the operation succeeded, -1 on error
*
* Since: 2.8
*/
int
g_chmod (const gchar *filename,
int mode)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
retval = _wchmod (wfilename, mode);
save_errno = errno;
g_free (wfilename);
errno = save_errno;
return retval;
#else
return chmod (filename, mode);
#endif
}
/**
* g_open:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @flags: as in open()
* @mode: as in open()
*
* A wrapper for the POSIX open() function. The open() function is
* used to convert a pathname into a file descriptor.
*
* On POSIX systems file descriptors are implemented by the operating
* system. On Windows, it's the C library that implements open() and
* file descriptors. The actual Win32 API for opening files is quite
* different, see MSDN documentation for CreateFile(). The Win32 API
* uses file handles, which are more randomish integers, not small
* integers like file descriptors.
*
* Because file descriptors are specific to the C library on Windows,
* the file descriptor returned by this function makes sense only to
* functions in the same C library. Thus if the GLib-using code uses a
* different C library than GLib does, the file descriptor returned by
* this function cannot be passed to C library functions like write()
* or read().
*
* See your C library manual for more details about open().
*
* Returns: a new file descriptor, or -1 if an error occurred.
* The return value can be used exactly like the return value
* from open().
*
* Since: 2.6
*/
int
g_open (const gchar *filename,
int flags,
int mode)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
retval = _wopen (wfilename, flags, mode);
save_errno = errno;
g_free (wfilename);
errno = save_errno;
return retval;
#else
int fd;
do
fd = open (filename, flags, mode);
while (G_UNLIKELY (fd == -1 && errno == EINTR));
return fd;
#endif
}
/**
* g_creat:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @mode: as in creat()
*
* A wrapper for the POSIX creat() function. The creat() function is
* used to convert a pathname into a file descriptor, creating a file
* if necessary.
*
* On POSIX systems file descriptors are implemented by the operating
* system. On Windows, it's the C library that implements creat() and
* file descriptors. The actual Windows API for opening files is
* different, see MSDN documentation for CreateFile(). The Win32 API
* uses file handles, which are more randomish integers, not small
* integers like file descriptors.
*
* Because file descriptors are specific to the C library on Windows,
* the file descriptor returned by this function makes sense only to
* functions in the same C library. Thus if the GLib-using code uses a
* different C library than GLib does, the file descriptor returned by
* this function cannot be passed to C library functions like write()
* or read().
*
* See your C library manual for more details about creat().
*
* Returns: a new file descriptor, or -1 if an error occurred.
* The return value can be used exactly like the return value
* from creat().
*
* Since: 2.8
*/
int
g_creat (const gchar *filename,
int mode)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
retval = _wcreat (wfilename, mode);
save_errno = errno;
g_free (wfilename);
errno = save_errno;
return retval;
#else
return creat (filename, mode);
#endif
}
/**
* g_rename:
* @oldfilename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @newfilename: (type filename): a pathname in the GLib file name encoding
*
* A wrapper for the POSIX rename() function. The rename() function
* renames a file, moving it between directories if required.
*
* See your C library manual for more details about how rename() works
* on your system. It is not possible in general on Windows to rename
* a file that is open to some process.
*
* Returns: 0 if the renaming succeeded, -1 if an error occurred
*
* Since: 2.6
*/
int
g_rename (const gchar *oldfilename,
const gchar *newfilename)
{
#ifdef G_OS_WIN32
wchar_t *woldfilename = g_utf8_to_utf16 (oldfilename, -1, NULL, NULL, NULL);
wchar_t *wnewfilename;
int retval;
int save_errno = 0;
if (woldfilename == NULL)
{
errno = EINVAL;
return -1;
}
wnewfilename = g_utf8_to_utf16 (newfilename, -1, NULL, NULL, NULL);
if (wnewfilename == NULL)
{
g_free (woldfilename);
errno = EINVAL;
return -1;
}
if (MoveFileExW (woldfilename, wnewfilename, MOVEFILE_REPLACE_EXISTING))
retval = 0;
else
{
retval = -1;
save_errno = w32_error_to_errno (GetLastError ());
}
g_free (woldfilename);
g_free (wnewfilename);
errno = save_errno;
return retval;
#else
return rename (oldfilename, newfilename);
#endif
}
/**
* g_mkdir:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @mode: permissions to use for the newly created directory
*
* A wrapper for the POSIX mkdir() function. The mkdir() function
* attempts to create a directory with the given name and permissions.
* The mode argument is ignored on Windows.
*
* See your C library manual for more details about mkdir().
*
* Returns: 0 if the directory was successfully created, -1 if an error
* occurred
*
* Since: 2.6
*/
int
g_mkdir (const gchar *filename,
int mode)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
retval = _wmkdir (wfilename);
save_errno = errno;
g_free (wfilename);
errno = save_errno;
return retval;
#else
return mkdir (filename, mode);
#endif
}
/**
* g_chdir:
* @path: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
*
* A wrapper for the POSIX chdir() function. The function changes the
* current directory of the process to @path.
*
* See your C library manual for more details about chdir().
*
* Returns: 0 on success, -1 if an error occurred.
*
* Since: 2.8
*/
int
g_chdir (const gchar *path)
{
#ifdef G_OS_WIN32
wchar_t *wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
int retval;
int save_errno;
if (wpath == NULL)
{
errno = EINVAL;
return -1;
}
retval = _wchdir (wpath);
save_errno = errno;
g_free (wpath);
errno = save_errno;
return retval;
#else
return chdir (path);
#endif
}
/**
* GStatBuf:
*
* A type corresponding to the appropriate struct type for the stat()
* system call, depending on the platform and/or compiler being used.
*
* See g_stat() for more information.
*/
/**
* g_stat:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @buf: a pointer to a stat struct, which will be filled with the file
* information
*
* A wrapper for the POSIX stat() function. The stat() function
* returns information about a file. On Windows the stat() function in
* the C library checks only the FAT-style READONLY attribute and does
* not look at the ACL at all. Thus on Windows the protection bits in
* the @st_mode field are a fabrication of little use.
*
* On Windows the Microsoft C libraries have several variants of the
* stat struct and stat() function with names like _stat(), _stat32(),
* _stat32i64() and _stat64i32(). The one used here is for 32-bit code
* the one with 32-bit size and time fields, specifically called _stat32().
*
* In Microsoft's compiler, by default struct stat means one with
* 64-bit time fields while in MinGW struct stat is the legacy one
* with 32-bit fields. To hopefully clear up this messs, the gstdio.h
* header defines a type #GStatBuf which is the appropriate struct type
* depending on the platform and/or compiler being used. On POSIX it
* is just struct stat, but note that even on POSIX platforms, stat()
* might be a macro.
*
* See your C library manual for more details about stat().
*
* Returns: 0 if the information was successfully retrieved,
* -1 if an error occurred
*
* Since: 2.6
*/
int
g_stat (const gchar *filename,
GStatBuf *buf)
{
#ifdef G_OS_WIN32
GWin32PrivateStat w32_buf;
int retval = g_win32_stat_utf8 (filename, &w32_buf);
buf->st_dev = w32_buf.st_dev;
buf->st_ino = w32_buf.st_ino;
buf->st_mode = w32_buf.st_mode;
buf->st_nlink = w32_buf.st_nlink;
buf->st_uid = w32_buf.st_uid;
buf->st_gid = w32_buf.st_gid;
buf->st_rdev = w32_buf.st_dev;
buf->st_size = w32_buf.st_size;
buf->st_atime = w32_buf.st_atim.tv_sec;
buf->st_mtime = w32_buf.st_mtim.tv_sec;
buf->st_ctime = w32_buf.st_ctim.tv_sec;
return retval;
#else
return stat (filename, buf);
#endif
}
/**
* g_lstat:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @buf: a pointer to a stat struct, which will be filled with the file
* information
*
* A wrapper for the POSIX lstat() function. The lstat() function is
* like stat() except that in the case of symbolic links, it returns
* information about the symbolic link itself and not the file that it
* refers to. If the system does not support symbolic links g_lstat()
* is identical to g_stat().
*
* See your C library manual for more details about lstat().
*
* Returns: 0 if the information was successfully retrieved,
* -1 if an error occurred
*
* Since: 2.6
*/
int
g_lstat (const gchar *filename,
GStatBuf *buf)
{
#ifdef HAVE_LSTAT
/* This can't be Win32, so don't do the widechar dance. */
return lstat (filename, buf);
#elif defined (G_OS_WIN32)
GWin32PrivateStat w32_buf;
int retval = g_win32_lstat_utf8 (filename, &w32_buf);
buf->st_dev = w32_buf.st_dev;
buf->st_ino = w32_buf.st_ino;
buf->st_mode = w32_buf.st_mode;
buf->st_nlink = w32_buf.st_nlink;
buf->st_uid = w32_buf.st_uid;
buf->st_gid = w32_buf.st_gid;
buf->st_rdev = w32_buf.st_dev;
buf->st_size = w32_buf.st_size;
buf->st_atime = w32_buf.st_atim.tv_sec;
buf->st_mtime = w32_buf.st_mtim.tv_sec;
buf->st_ctime = w32_buf.st_ctim.tv_sec;
return retval;
#else
return g_stat (filename, buf);
#endif
}
/**
* g_unlink:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
*
* A wrapper for the POSIX unlink() function. The unlink() function
* deletes a name from the filesystem. If this was the last link to the
* file and no processes have it opened, the diskspace occupied by the
* file is freed.
*
* See your C library manual for more details about unlink(). Note
* that on Windows, it is in general not possible to delete files that
* are open to some process, or mapped into memory.
*
* Returns: 0 if the name was successfully deleted, -1 if an error
* occurred
*
* Since: 2.6
*/
int
g_unlink (const gchar *filename)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
retval = _wunlink (wfilename);
save_errno = errno;
g_free (wfilename);
errno = save_errno;
return retval;
#else
return unlink (filename);
#endif
}
/**
* g_remove:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
*
* A wrapper for the POSIX remove() function. The remove() function
* deletes a name from the filesystem.
*
* See your C library manual for more details about how remove() works
* on your system. On Unix, remove() removes also directories, as it
* calls unlink() for files and rmdir() for directories. On Windows,
* although remove() in the C library only works for files, this
* function tries first remove() and then if that fails rmdir(), and
* thus works for both files and directories. Note however, that on
* Windows, it is in general not possible to remove a file that is
* open to some process, or mapped into memory.
*
* If this function fails on Windows you can't infer too much from the
* errno value. rmdir() is tried regardless of what caused remove() to
* fail. Any errno value set by remove() will be overwritten by that
* set by rmdir().
*
* Returns: 0 if the file was successfully removed, -1 if an error
* occurred
*
* Since: 2.6
*/
int
g_remove (const gchar *filename)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
retval = _wremove (wfilename);
if (retval == -1)
retval = _wrmdir (wfilename);
save_errno = errno;
g_free (wfilename);
errno = save_errno;
return retval;
#else
return remove (filename);
#endif
}
/**
* g_rmdir:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
*
* A wrapper for the POSIX rmdir() function. The rmdir() function
* deletes a directory from the filesystem.
*
* See your C library manual for more details about how rmdir() works
* on your system.
*
* Returns: 0 if the directory was successfully removed, -1 if an error
* occurred
*
* Since: 2.6
*/
int
g_rmdir (const gchar *filename)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
retval = _wrmdir (wfilename);
save_errno = errno;
g_free (wfilename);
errno = save_errno;
return retval;
#else
return rmdir (filename);
#endif
}
/**
* g_fopen:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @mode: a string describing the mode in which the file should be opened
*
* A wrapper for the stdio `fopen()` function. The `fopen()` function
* opens a file and associates a new stream with it.
*
* Because file descriptors are specific to the C library on Windows,
* and a file descriptor is part of the `FILE` struct, the `FILE*` returned
* by this function makes sense only to functions in the same C library.
* Thus if the GLib-using code uses a different C library than GLib does,
* the FILE* returned by this function cannot be passed to C library
* functions like `fprintf()` or `fread()`.
*
* See your C library manual for more details about `fopen()`.
*
* As `close()` and `fclose()` are part of the C library, this implies that it is
* currently impossible to close a file if the application C library and the C library
* used by GLib are different. Convenience functions like g_file_set_contents_full()
* avoid this problem.
*
* Returns: A `FILE*` if the file was successfully opened, or %NULL if
* an error occurred
*
* Since: 2.6
*/
FILE *
g_fopen (const gchar *filename,
const gchar *mode)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
wchar_t *wmode;
FILE *retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return NULL;
}
wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
if (wmode == NULL)
{
g_free (wfilename);
errno = EINVAL;
return NULL;
}
_g_win32_fix_mode (wmode);
retval = _wfopen (wfilename, wmode);
save_errno = errno;
g_free (wfilename);
g_free (wmode);
errno = save_errno;
return retval;
#else
return fopen (filename, mode);
#endif
}
/**
* g_freopen:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @mode: a string describing the mode in which the file should be opened
* @stream: (nullable): an existing stream which will be reused, or %NULL
*
* A wrapper for the POSIX freopen() function. The freopen() function
* opens a file and associates it with an existing stream.
*
* See your C library manual for more details about freopen().
*
* Returns: A FILE* if the file was successfully opened, or %NULL if
* an error occurred.
*
* Since: 2.6
*/
FILE *
g_freopen (const gchar *filename,
const gchar *mode,
FILE *stream)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
wchar_t *wmode;
FILE *retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return NULL;
}
wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
if (wmode == NULL)
{
g_free (wfilename);
errno = EINVAL;
return NULL;
}
_g_win32_fix_mode (wmode);
retval = _wfreopen (wfilename, wmode, stream);
save_errno = errno;
g_free (wfilename);
g_free (wmode);
errno = save_errno;
return retval;
#else
return freopen (filename, mode, stream);
#endif
}
/**
* g_fsync:
* @fd: a file descriptor
*
* A wrapper for the POSIX `fsync()` function. On Windows, `_commit()` will be
* used. On macOS, `fcntl(F_FULLFSYNC)` will be used.
* The `fsync()` function is used to synchronize a file's in-core
* state with that of the disk.
*
* This wrapper will handle retrying on `EINTR`.
*
* See the C library manual for more details about fsync().
*
* Returns: 0 on success, or -1 if an error occurred.
* The return value can be used exactly like the return value from fsync().
*
* Since: 2.64
*/
gint
g_fsync (gint fd)
{
#ifdef G_OS_WIN32
return _commit (fd);
#elif defined(HAVE_FSYNC) || defined(HAVE_FCNTL_F_FULLFSYNC)
int retval;
do
#ifdef HAVE_FCNTL_F_FULLFSYNC
retval = fcntl (fd, F_FULLFSYNC, 0);
#else
retval = fsync (fd);
#endif
while (G_UNLIKELY (retval < 0 && errno == EINTR));
return retval;
#else
return 0;
#endif
}
/**
* g_utime:
* @filename: (type filename): a pathname in the GLib file name encoding
* (UTF-8 on Windows)
* @utb: a pointer to a struct utimbuf.
*
* A wrapper for the POSIX utime() function. The utime() function
* sets the access and modification timestamps of a file.
*
* See your C library manual for more details about how utime() works
* on your system.
*
* Returns: 0 if the operation was successful, -1 if an error occurred
*
* Since: 2.18
*/
int
g_utime (const gchar *filename,
struct utimbuf *utb)
{
#ifdef G_OS_WIN32
wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int retval;
int save_errno;
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
retval = _wutime (wfilename, (struct _utimbuf*) utb);
save_errno = errno;
g_free (wfilename);
errno = save_errno;
return retval;
#else
return utime (filename, utb);
#endif
}
/**
* g_close:
* @fd: A file descriptor
* @error: a #GError
*
* This wraps the close() call. In case of error, %errno will be
* preserved, but the error will also be stored as a #GError in @error.
* In case of success, %errno is undefined.
*
* Besides using #GError, there is another major reason to prefer this
* function over the call provided by the system; on Unix, it will
* attempt to correctly handle %EINTR, which has platform-specific
* semantics.
*
* It is a bug to call this function with an invalid file descriptor.
*
* Since 2.76, this function is guaranteed to be async-signal-safe if (and only
* if) @error is %NULL and @fd is a valid open file descriptor.
*
* Returns: %TRUE on success, %FALSE if there was an error.
*
* Since: 2.36
*/
gboolean
g_close (gint fd,
GError **error)
{
int res;
/* Important: if @error is NULL, we must not do anything that is
* not async-signal-safe.
*/
res = close (fd);
if (res == -1)
{
int errsv = errno;
if (errsv == EINTR)
{
/* Just ignore EINTR for now; a retry loop is the wrong thing to do
* on Linux at least. Anyone who wants to add a conditional check
* for e.g. HP-UX is welcome to do so later...
*
* close_func_with_invalid_fds() in gspawn.c has similar logic.
*
* https://lwn.net/Articles/576478/
* http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
* https://bugzilla.gnome.org/show_bug.cgi?id=682819
* http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
* https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
*
* `close$NOCANCEL()` in gstdioprivate.h, on macOS, ensures that the fd is
* closed even if it did return EINTR.
*/
return TRUE;
}
if (error)
{
g_set_error_literal (error, G_FILE_ERROR,
g_file_error_from_errno (errsv),
g_strerror (errsv));
}
if (errsv == EBADF)
{
/* There is a bug. Fail an assertion. Note that this function is supposed to be
* async-signal-safe, but in case an assertion fails, all bets are already off. */
if (fd >= 0)
{
/* Closing an non-negative, invalid file descriptor is a bug. The bug is
* not necessarily in the caller of g_close(), but somebody else
* might have wrongly closed fd. In any case, there is a serious bug
* somewhere. */
g_critical ("g_close(fd:%d) failed with EBADF. The tracking of file descriptors got messed up", fd);
}
else
{
/* Closing a negative "file descriptor" is less problematic. It's still a nonsensical action
* from the caller. Assert against that too. */
g_critical ("g_close(fd:%d) failed with EBADF. This is not a valid file descriptor", fd);
}
}
errno = errsv;
return FALSE;
}
return TRUE;
}
/**
* g_clear_fd: (skip)
* @fd_ptr: (not optional) (inout) (transfer full): a pointer to a file descriptor
* @error: Used to return an error on failure
*
* If @fd_ptr points to a file descriptor, close it and return
* whether closing it was successful, like g_close().
* If @fd_ptr points to a negative number, return %TRUE without closing
* anything.
* In both cases, set @fd_ptr to `-1` before returning.
*
* Like g_close(), if closing the file descriptor fails, the error is
* stored in both %errno and @error. If this function succeeds,
* %errno is undefined.
*
* This function is async-signal-safe if @error is %NULL and @fd_ptr
* points to either a negative number or a valid file descriptor.
*
* It is a programming error for @fd_ptr to point to a non-negative
* number that is not a valid file descriptor.
*
* A typical use of this function is to clean up a file descriptor at
* the end of its scope, whether it has been set successfully or not:
*
* |[
* gboolean
* operate_on_fd (GError **error)
* {
* gboolean ret = FALSE;
* int fd = -1;
*
* fd = open_a_fd (error);
*
* if (fd < 0)
* goto out;
*
* if (!do_something (fd, error))
* goto out;
*
* if (!g_clear_fd (&fd, error))
* goto out;
*
* ret = TRUE;
*
* out:
* // OK to call even if fd was never opened or was already closed
* g_clear_fd (&fd, NULL);
* return ret;
* }
* ]|
*
* This function is also useful in conjunction with #g_autofd.
*
* Returns: %TRUE on success
* Since: 2.76
*/
/**
* g_autofd: (skip)
*
* Macro to add an attribute to a file descriptor variable to ensure
* automatic cleanup using g_clear_fd().
*
* This macro behaves like #g_autofree rather than g_autoptr(): it is
* an attribute supplied before the type name, rather than wrapping the
* type definition.
*
* Otherwise, this macro has similar constraints as g_autoptr(): it is
* only supported on GCC and clang, and the variable must be initialized
* (to either a valid file descriptor or a negative number).
*
* Using this macro is async-signal-safe if the constraints described above
* are met, so it can be used in a signal handler or after `fork()`.
*
* Any error from closing the file descriptor when it goes out of scope
* is ignored. Use g_clear_fd() if error-checking is required.
*
* |[
* gboolean
* operate_on_fds (GError **error)
* {
* g_autofd int fd1 = open_a_fd (..., error);
* g_autofd int fd2 = -1;
*
* // it is safe to return early here, nothing will be closed
* if (fd1 < 0)
* return FALSE;
*
* fd2 = open_a_fd (..., error);
*
* // fd1 will be closed automatically if we return here
* if (fd2 < 0)
* return FALSE;
*
* // fd1 and fd2 will be closed automatically if we return here
* if (!do_something_useful (fd1, fd2, error))
* return FALSE;
*
* // fd2 will be closed automatically if we return here
* if (!g_clear_fd (&fd1, error))
* return FALSE;
*
* // fd2 will be automatically closed here if still open
* return TRUE;
* }
* ]|
*
* Since: 2.76
*/