mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-14 05:16:18 +01:00
e535f22b08
They were mixing up `(optional)` and `(nullable)`, and didn’t correctly annotate the arguments as `(inout)` or `(transfer full)`. Signed-off-by: Philip Withnall <pwithnall@endlessos.org> Fixes: #2793
1949 lines
56 KiB
C
1949 lines
56 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#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 <sys/types.h>
|
||
#include <sys/stat.h>
|
||
#include <fcntl.h>
|
||
|
||
#ifdef G_OS_UNIX
|
||
#include <unistd.h>
|
||
#endif
|
||
|
||
#ifdef G_OS_WIN32
|
||
#include <windows.h>
|
||
#include <errno.h>
|
||
#include <wchar.h>
|
||
#include <direct.h>
|
||
#include <io.h>
|
||
#include <sys/utime.h>
|
||
#include <stdlib.h> /* for MB_CUR_MAX */
|
||
#else
|
||
#include <utime.h>
|
||
#include <errno.h>
|
||
#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
|
||
*/
|