summaryrefslogtreecommitdiffstats
path: root/common/sysutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/sysutils.c')
-rw-r--r--common/sysutils.c1951
1 files changed, 1951 insertions, 0 deletions
diff --git a/common/sysutils.c b/common/sysutils.c
new file mode 100644
index 0000000..5f54ae1
--- /dev/null
+++ b/common/sysutils.c
@@ -0,0 +1,1951 @@
+/* sysutils.c - system helpers
+ * Copyright (C) 1991-2001, 2003-2004,
+ * 2006-2008 Free Software Foundation, Inc.
+ * Copyright (C) 2013-2016 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either
+ *
+ * - the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * or
+ *
+ * - the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * This file 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */
+# undef HAVE_NPTH
+# undef USE_NPTH
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif
+#include <unistd.h>
+#include <errno.h>
+#ifdef HAVE_STAT
+# include <sys/stat.h>
+#endif
+#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2
+# include <asm/sysinfo.h>
+# include <asm/unistd.h>
+#endif
+#include <time.h>
+#ifdef HAVE_SETRLIMIT
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+#ifdef HAVE_W32_SYSTEM
+# if WINVER < 0x0500
+# define WINVER 0x0500 /* Required for AllowSetForegroundWindow. */
+# endif
+# ifdef HAVE_WINSOCK2_H
+# include <winsock2.h>
+# endif
+# include <windows.h>
+#else /*!HAVE_W32_SYSTEM*/
+# include <sys/socket.h>
+# include <sys/un.h>
+#endif
+#ifdef HAVE_INOTIFY_INIT
+# include <sys/inotify.h>
+#endif /*HAVE_INOTIFY_INIT*/
+#ifdef HAVE_NPTH
+# include <npth.h>
+#endif
+#include <fcntl.h>
+#include <dirent.h>
+
+#include <assuan.h>
+
+#include "util.h"
+#include "i18n.h"
+
+#include "sysutils.h"
+
+#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
+
+
+/* The object used with our opendir functions. We need to define our
+ * own so that we can properly handle Unicode on Windows. */
+struct gnupg_dir_s
+{
+#ifdef HAVE_W32_SYSTEM
+ _WDIR *dir; /* The system's DIR pointer. */
+#else
+ DIR *dir; /* The system's DIR pointer. */
+#endif
+ struct gnupg_dirent_s dirent; /* The current dirent. */
+ size_t namesize; /* If not 0 the allocated size of dirent.d_name. */
+ char name[256]; /* Only used if NAMESIZE is 0. */
+};
+
+
+/* Flag to tell whether special file names are enabled. See gpg.c for
+ * an explanation of these file names. */
+static int allow_special_filenames;
+
+
+static GPGRT_INLINE gpg_error_t
+my_error_from_syserror (void)
+{
+ return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+}
+
+static GPGRT_INLINE gpg_error_t
+my_error (int e)
+{
+ return gpg_err_make (default_errsource, (e));
+}
+
+
+
+#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2
+#warning using trap_unaligned
+static int
+setsysinfo(unsigned long op, void *buffer, unsigned long size,
+ int *start, void *arg, unsigned long flag)
+{
+ return syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag);
+}
+
+void
+trap_unaligned(void)
+{
+ unsigned int buf[2];
+
+ buf[0] = SSIN_UACPROC;
+ buf[1] = UAC_SIGBUS | UAC_NOPRINT;
+ setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0);
+}
+#else
+void
+trap_unaligned(void)
+{ /* dummy */
+}
+#endif
+
+
+int
+disable_core_dumps (void)
+{
+#ifdef HAVE_DOSISH_SYSTEM
+ return 0;
+#else
+# ifdef HAVE_SETRLIMIT
+ struct rlimit limit;
+
+ /* We only set the current limit unless we were not able to
+ retrieve the old value. */
+ if (getrlimit (RLIMIT_CORE, &limit))
+ limit.rlim_max = 0;
+ limit.rlim_cur = 0;
+ if( !setrlimit (RLIMIT_CORE, &limit) )
+ return 0;
+ if( errno != EINVAL && errno != ENOSYS )
+ log_fatal (_("can't disable core dumps: %s\n"), strerror(errno) );
+#endif
+ return 1;
+#endif
+}
+
+int
+enable_core_dumps (void)
+{
+#ifdef HAVE_DOSISH_SYSTEM
+ return 0;
+#else
+# ifdef HAVE_SETRLIMIT
+ struct rlimit limit;
+
+ if (getrlimit (RLIMIT_CORE, &limit))
+ return 1;
+ limit.rlim_cur = limit.rlim_max;
+ setrlimit (RLIMIT_CORE, &limit);
+ return 1; /* We always return true because this function is
+ merely a debugging aid. */
+# endif
+ return 1;
+#endif
+}
+
+#ifdef HAVE_W32_SYSTEM
+static int
+any8bitchar (const char *string)
+{
+ if (string)
+ for ( ; *string; string++)
+ if ((*string & 0x80))
+ return 1;
+ return 0;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/* Helper for gnupg_w32_set_errno. */
+#ifdef HAVE_W32_SYSTEM
+static int
+map_w32_to_errno (DWORD w32_err)
+{
+ switch (w32_err)
+ {
+ case 0:
+ return 0;
+
+ case ERROR_FILE_NOT_FOUND:
+ return ENOENT;
+
+ case ERROR_PATH_NOT_FOUND:
+ return ENOENT;
+
+ case ERROR_ACCESS_DENIED:
+ return EPERM; /* ReactOS uses EACCES ("Permission denied") and
+ * is likely right because they used an
+ * undocumented function to associate the error
+ * codes. However we have always used EPERM
+ * ("Operation not permitted", e.g. function is
+ * required to be called by root) and we better
+ * stick to that to avoid surprising bugs. */
+
+ case ERROR_INVALID_HANDLE:
+ return EBADF;
+
+ case ERROR_INVALID_BLOCK:
+ return ENOMEM;
+
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return ENOMEM;
+
+ case ERROR_NO_DATA:
+ return EPIPE;
+
+ case ERROR_ALREADY_EXISTS:
+ return EEXIST;
+
+ /* This mapping has been taken from reactOS. */
+ case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
+ case ERROR_ARENA_TRASHED: return ENOMEM;
+ case ERROR_BAD_ENVIRONMENT: return E2BIG;
+ case ERROR_BAD_FORMAT: return ENOEXEC;
+ case ERROR_INVALID_DRIVE: return ENOENT;
+ case ERROR_CURRENT_DIRECTORY: return EACCES;
+ case ERROR_NOT_SAME_DEVICE: return EXDEV;
+ case ERROR_NO_MORE_FILES: return ENOENT;
+ case ERROR_WRITE_PROTECT: return EACCES;
+ case ERROR_BAD_UNIT: return EACCES;
+ case ERROR_NOT_READY: return EACCES;
+ case ERROR_BAD_COMMAND: return EACCES;
+ case ERROR_CRC: return EACCES;
+ case ERROR_BAD_LENGTH: return EACCES;
+ case ERROR_SEEK: return EACCES;
+ case ERROR_NOT_DOS_DISK: return EACCES;
+ case ERROR_SECTOR_NOT_FOUND: return EACCES;
+ case ERROR_OUT_OF_PAPER: return EACCES;
+ case ERROR_WRITE_FAULT: return EACCES;
+ case ERROR_READ_FAULT: return EACCES;
+ case ERROR_GEN_FAILURE: return EACCES;
+ case ERROR_SHARING_VIOLATION: return EACCES;
+ case ERROR_LOCK_VIOLATION: return EACCES;
+ case ERROR_WRONG_DISK: return EACCES;
+ case ERROR_SHARING_BUFFER_EXCEEDED: return EACCES;
+ case ERROR_BAD_NETPATH: return ENOENT;
+ case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
+ case ERROR_BAD_NET_NAME: return ENOENT;
+ case ERROR_FILE_EXISTS: return EEXIST;
+ case ERROR_CANNOT_MAKE: return EACCES;
+ case ERROR_FAIL_I24: return EACCES;
+ case ERROR_NO_PROC_SLOTS: return EAGAIN;
+ case ERROR_DRIVE_LOCKED: return EACCES;
+ case ERROR_BROKEN_PIPE: return EPIPE;
+ case ERROR_DISK_FULL: return ENOSPC;
+ case ERROR_INVALID_TARGET_HANDLE: return EBADF;
+ case ERROR_WAIT_NO_CHILDREN: return ECHILD;
+ case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
+ case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
+ case ERROR_SEEK_ON_DEVICE: return EACCES;
+ case ERROR_DIR_NOT_EMPTY: return ENOTEMPTY;
+ case ERROR_NOT_LOCKED: return EACCES;
+ case ERROR_BAD_PATHNAME: return ENOENT;
+ case ERROR_MAX_THRDS_REACHED: return EAGAIN;
+ case ERROR_LOCK_FAILED: return EACCES;
+ case ERROR_INVALID_STARTING_CODESEG: return ENOEXEC;
+ case ERROR_INVALID_STACKSEG: return ENOEXEC;
+ case ERROR_INVALID_MODULETYPE: return ENOEXEC;
+ case ERROR_INVALID_EXE_SIGNATURE: return ENOEXEC;
+ case ERROR_EXE_MARKED_INVALID: return ENOEXEC;
+ case ERROR_BAD_EXE_FORMAT: return ENOEXEC;
+ case ERROR_ITERATED_DATA_EXCEEDS_64k: return ENOEXEC;
+ case ERROR_INVALID_MINALLOCSIZE: return ENOEXEC;
+ case ERROR_DYNLINK_FROM_INVALID_RING: return ENOEXEC;
+ case ERROR_IOPL_NOT_ENABLED: return ENOEXEC;
+ case ERROR_INVALID_SEGDPL: return ENOEXEC;
+ case ERROR_AUTODATASEG_EXCEEDS_64k: return ENOEXEC;
+ case ERROR_RING2SEG_MUST_BE_MOVABLE: return ENOEXEC;
+ case ERROR_RELOC_CHAIN_XEEDS_SEGLIM: return ENOEXEC;
+ case ERROR_INFLOOP_IN_RELOC_CHAIN: return ENOEXEC;
+ case ERROR_FILENAME_EXCED_RANGE: return ENOENT;
+ case ERROR_NESTING_NOT_ALLOWED: return EAGAIN;
+ case ERROR_NOT_ENOUGH_QUOTA: return ENOMEM;
+
+ default:
+ return EIO;
+ }
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/* Set ERRNO from the Windows error. EC may be -1 to use the last error. */
+#ifdef HAVE_W32_SYSTEM
+void
+gnupg_w32_set_errno (int ec)
+{
+ /* FIXME: Replace by gpgrt_w32_set_errno. */
+ if (ec == -1)
+ ec = GetLastError ();
+ _set_errno (map_w32_to_errno (ec));
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+
+/* Allow the use of special "-&nnn" style file names. */
+void
+enable_special_filenames (void)
+{
+ allow_special_filenames = 1;
+}
+
+
+/* Return a string which is used as a kind of process ID. */
+const byte *
+get_session_marker (size_t *rlen)
+{
+ static byte marker[SIZEOF_UNSIGNED_LONG*2];
+ static int initialized;
+
+ if (!initialized)
+ {
+ gcry_create_nonce (marker, sizeof marker);
+ initialized = 1;
+ }
+ *rlen = sizeof (marker);
+ return marker;
+}
+
+/* Return a random number in an unsigned int. */
+unsigned int
+get_uint_nonce (void)
+{
+ unsigned int value;
+
+ gcry_create_nonce (&value, sizeof value);
+ return value;
+}
+
+
+
+#if 0 /* not yet needed - Note that this will require inclusion of
+ cmacros.am in Makefile.am */
+int
+check_permissions(const char *path,int extension,int checkonly)
+{
+#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM)
+ char *tmppath;
+ struct stat statbuf;
+ int ret=1;
+ int isdir=0;
+
+ if(opt.no_perm_warn)
+ return 0;
+
+ if(extension && path[0]!=DIRSEP_C)
+ {
+ if(strchr(path,DIRSEP_C))
+ tmppath=make_filename(path,NULL);
+ else
+ tmppath=make_filename(GNUPG_LIBDIR,path,NULL);
+ }
+ else
+ tmppath=m_strdup(path);
+
+ /* It's okay if the file doesn't exist */
+ if(stat(tmppath,&statbuf)!=0)
+ {
+ ret=0;
+ goto end;
+ }
+
+ isdir=S_ISDIR(statbuf.st_mode);
+
+ /* Per-user files must be owned by the user. Extensions must be
+ owned by the user or root. */
+ if((!extension && statbuf.st_uid != getuid()) ||
+ (extension && statbuf.st_uid!=0 && statbuf.st_uid!=getuid()))
+ {
+ if(!checkonly)
+ log_info(_("Warning: unsafe ownership on %s \"%s\"\n"),
+ isdir?"directory":extension?"extension":"file",path);
+ goto end;
+ }
+
+ /* This works for both directories and files - basically, we don't
+ care what the owner permissions are, so long as the group and
+ other permissions are 0 for per-user files, and non-writable for
+ extensions. */
+ if((extension && (statbuf.st_mode & (S_IWGRP|S_IWOTH)) !=0) ||
+ (!extension && (statbuf.st_mode & (S_IRWXG|S_IRWXO)) != 0))
+ {
+ char *dir;
+
+ /* However, if the directory the directory/file is in is owned
+ by the user and is 700, then this is not a problem.
+ Theoretically, we could walk this test up to the root
+ directory /, but for the sake of sanity, I'm stopping at one
+ level down. */
+
+ dir= make_dirname (tmppath);
+ if(stat(dir,&statbuf)==0 && statbuf.st_uid==getuid() &&
+ S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
+ {
+ xfree (dir);
+ ret=0;
+ goto end;
+ }
+
+ m_free(dir);
+
+ if(!checkonly)
+ log_info(_("Warning: unsafe permissions on %s \"%s\"\n"),
+ isdir?"directory":extension?"extension":"file",path);
+ goto end;
+ }
+
+ ret=0;
+
+ end:
+ m_free(tmppath);
+
+ return ret;
+
+#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */
+
+ return 0;
+}
+#endif
+
+
+/* Wrapper around the usual sleep function. This one won't wake up
+ before the sleep time has really elapsed. When build with Pth it
+ merely calls pth_sleep and thus suspends only the current
+ thread. */
+void
+gnupg_sleep (unsigned int seconds)
+{
+#ifdef USE_NPTH
+ npth_sleep (seconds);
+#else
+ /* Fixme: make sure that a sleep won't wake up to early. */
+# ifdef HAVE_W32_SYSTEM
+ Sleep (seconds*1000);
+# else
+ sleep (seconds);
+# endif
+#endif
+}
+
+
+/* Wrapper around the platforms usleep function. This one won't wake
+ * up before the sleep time has really elapsed. When build with nPth
+ * it merely calls npth_usleep and thus suspends only the current
+ * thread. */
+void
+gnupg_usleep (unsigned int usecs)
+{
+#if defined(USE_NPTH)
+
+ npth_usleep (usecs);
+
+#elif defined(HAVE_W32_SYSTEM)
+
+ Sleep ((usecs + 999) / 1000);
+
+#elif defined(HAVE_NANOSLEEP)
+
+ if (usecs)
+ {
+ struct timespec req;
+ struct timespec rem;
+
+ req.tv_sec = usecs / 1000000;
+ req.tv_nsec = (usecs % 1000000) * 1000;
+ while (nanosleep (&req, &rem) < 0 && errno == EINTR)
+ req = rem;
+ }
+
+#else /*Standard Unix*/
+
+ if (usecs)
+ {
+ struct timeval tv;
+
+ tv.tv_sec = usecs / 1000000;
+ tv.tv_usec = usecs % 1000000;
+ select (0, NULL, NULL, NULL, &tv);
+ }
+
+#endif
+}
+
+
+/* This function is a NOP for POSIX systems but required under Windows
+ as the file handles as returned by OS calls (like CreateFile) are
+ different from the libc file descriptors (like open). This function
+ translates system file handles to libc file handles. FOR_WRITE
+ gives the direction of the handle. */
+int
+translate_sys2libc_fd (gnupg_fd_t fd, int for_write)
+{
+#if defined(HAVE_W32CE_SYSTEM)
+ (void)for_write;
+ return (int) fd;
+#elif defined(HAVE_W32_SYSTEM)
+ int x;
+
+ if (fd == GNUPG_INVALID_FD)
+ return -1;
+
+ /* Note that _open_osfhandle is currently defined to take and return
+ a long. */
+ x = _open_osfhandle ((long)fd, for_write ? 1 : 0);
+ if (x == -1)
+ log_error ("failed to translate osfhandle %p\n", (void *) fd);
+ return x;
+#else /*!HAVE_W32_SYSTEM */
+ (void)for_write;
+ return fd;
+#endif
+}
+
+/* This is the same as translate_sys2libc_fd but takes an integer
+ which is assumed to be such an system handle. On WindowsCE the
+ passed FD is a rendezvous ID and the function finishes the pipe
+ creation. */
+int
+translate_sys2libc_fd_int (int fd, int for_write)
+{
+#if HAVE_W32CE_SYSTEM
+ fd = (int) _assuan_w32ce_finish_pipe (fd, for_write);
+ return translate_sys2libc_fd ((void*)fd, for_write);
+#elif HAVE_W32_SYSTEM
+ if (fd <= 2)
+ return fd; /* Do not do this for error, stdin, stdout, stderr. */
+
+ return translate_sys2libc_fd ((void*)fd, for_write);
+#else
+ (void)for_write;
+ return fd;
+#endif
+}
+
+
+/* Check whether FNAME has the form "-&nnnn", where N is a non-zero
+ * number. Returns this number or -1 if it is not the case. If the
+ * caller wants to use the file descriptor for writing FOR_WRITE shall
+ * be set to 1. If NOTRANSLATE is set the Windows specific mapping is
+ * not done. */
+int
+check_special_filename (const char *fname, int for_write, int notranslate)
+{
+ if (allow_special_filenames
+ && fname && *fname == '-' && fname[1] == '&')
+ {
+ int i;
+
+ fname += 2;
+ for (i=0; digitp (fname+i); i++ )
+ ;
+ if (!fname[i])
+ return notranslate? atoi (fname)
+ /**/ : translate_sys2libc_fd_int (atoi (fname), for_write);
+ }
+ return -1;
+}
+
+
+/* Replacement for tmpfile(). This is required because the tmpfile
+ function of Windows' runtime library is broken, insecure, ignores
+ TMPDIR and so on. In addition we create a file with an inheritable
+ handle. */
+FILE *
+gnupg_tmpfile (void)
+{
+#ifdef HAVE_W32_SYSTEM
+ int attempts, n;
+#ifdef HAVE_W32CE_SYSTEM
+ wchar_t buffer[MAX_PATH+7+12+1];
+# define mystrlen(a) wcslen (a)
+ wchar_t *name, *p;
+#else
+ char buffer[MAX_PATH+7+12+1];
+# define mystrlen(a) strlen (a)
+ char *name, *p;
+#endif
+ HANDLE file;
+ int pid = GetCurrentProcessId ();
+ unsigned int value;
+ int i;
+ SECURITY_ATTRIBUTES sec_attr;
+
+ memset (&sec_attr, 0, sizeof sec_attr );
+ sec_attr.nLength = sizeof sec_attr;
+ sec_attr.bInheritHandle = TRUE;
+
+ n = GetTempPath (MAX_PATH+1, buffer);
+ if (!n || n > MAX_PATH || mystrlen (buffer) > MAX_PATH)
+ {
+ gpg_err_set_errno (ENOENT);
+ return NULL;
+ }
+ p = buffer + mystrlen (buffer);
+#ifdef HAVE_W32CE_SYSTEM
+ wcscpy (p, L"_gnupg");
+ p += 7;
+#else
+ p = stpcpy (p, "_gnupg");
+#endif
+ /* We try to create the directory but don't care about an error as
+ it may already exist and the CreateFile would throw an error
+ anyway. */
+ CreateDirectory (buffer, NULL);
+ *p++ = '\\';
+ name = p;
+ for (attempts=0; attempts < 10; attempts++)
+ {
+ p = name;
+ value = (GetTickCount () ^ ((pid<<16) & 0xffff0000));
+ for (i=0; i < 8; i++)
+ {
+ *p++ = tohex (((value >> 28) & 0x0f));
+ value <<= 4;
+ }
+#ifdef HAVE_W32CE_SYSTEM
+ wcscpy (p, L".tmp");
+#else
+ strcpy (p, ".tmp");
+#endif
+ file = CreateFile (buffer,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ &sec_attr,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
+ NULL);
+ if (file != INVALID_HANDLE_VALUE)
+ {
+ FILE *fp;
+#ifdef HAVE_W32CE_SYSTEM
+ int fd = (int)file;
+ fp = _wfdopen (fd, L"w+b");
+#else
+ int fd = _open_osfhandle ((long)file, 0);
+ if (fd == -1)
+ {
+ CloseHandle (file);
+ return NULL;
+ }
+ fp = fdopen (fd, "w+b");
+#endif
+ if (!fp)
+ {
+ int save = errno;
+ close (fd);
+ gpg_err_set_errno (save);
+ return NULL;
+ }
+ return fp;
+ }
+ Sleep (1); /* One ms as this is the granularity of GetTickCount. */
+ }
+ gpg_err_set_errno (ENOENT);
+ return NULL;
+#undef mystrlen
+#else /*!HAVE_W32_SYSTEM*/
+ return tmpfile ();
+#endif /*!HAVE_W32_SYSTEM*/
+}
+
+
+/* Make sure that the standard file descriptors are opened. Obviously
+ some folks close them before an exec and the next file we open will
+ get one of them assigned and thus any output (i.e. diagnostics) end
+ up in that file (e.g. the trustdb). Not actually a gpg problem as
+ this will happen with almost all utilities when called in a wrong
+ way. However we try to minimize the damage here and raise
+ awareness of the problem.
+
+ Must be called before we open any files! */
+void
+gnupg_reopen_std (const char *pgmname)
+{
+#ifdef F_GETFD
+ int did_stdin = 0;
+ int did_stdout = 0;
+ int did_stderr = 0;
+ FILE *complain;
+
+ if (fcntl (STDIN_FILENO, F_GETFD) == -1 && errno ==EBADF)
+ {
+ if (open ("/dev/null",O_RDONLY) == STDIN_FILENO)
+ did_stdin = 1;
+ else
+ did_stdin = 2;
+ }
+
+ if (fcntl (STDOUT_FILENO, F_GETFD) == -1 && errno == EBADF)
+ {
+ if (open ("/dev/null",O_WRONLY) == STDOUT_FILENO)
+ did_stdout = 1;
+ else
+ did_stdout = 2;
+ }
+
+ if (fcntl (STDERR_FILENO, F_GETFD)==-1 && errno==EBADF)
+ {
+ if (open ("/dev/null", O_WRONLY) == STDERR_FILENO)
+ did_stderr = 1;
+ else
+ did_stderr = 2;
+ }
+
+ /* It's hard to log this sort of thing since the filehandle we would
+ complain to may be closed... */
+ if (!did_stderr)
+ complain = stderr;
+ else if (!did_stdout)
+ complain = stdout;
+ else
+ complain = NULL;
+
+ if (complain)
+ {
+ if (did_stdin == 1)
+ fprintf (complain, "%s: WARNING: standard input reopened\n", pgmname);
+ if (did_stdout == 1)
+ fprintf (complain, "%s: WARNING: standard output reopened\n", pgmname);
+ if (did_stderr == 1)
+ fprintf (complain, "%s: WARNING: standard error reopened\n", pgmname);
+
+ if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2)
+ fprintf(complain,"%s: fatal: unable to reopen standard input,"
+ " output, or error\n", pgmname);
+ }
+
+ if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2)
+ exit (3);
+#else /* !F_GETFD */
+ (void)pgmname;
+#endif
+}
+
+
+/* Hack required for Windows. */
+void
+gnupg_allow_set_foregound_window (pid_t pid)
+{
+ if (!pid)
+ log_info ("%s called with invalid pid %lu\n",
+ "gnupg_allow_set_foregound_window", (unsigned long)pid);
+#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM)
+ else if (!AllowSetForegroundWindow ((pid_t)pid == (pid_t)(-1)?ASFW_ANY:pid))
+ {
+ char *flags = getenv ("GNUPG_EXEC_DEBUG_FLAGS");
+ if (flags && (atoi (flags) & 2))
+ log_info ("AllowSetForegroundWindow(%lu) failed: %s\n",
+ (unsigned long)pid, w32_strerror (-1));
+ }
+#endif
+}
+
+int
+gnupg_remove (const char *fname)
+{
+#ifdef HAVE_W32_SYSTEM
+ int rc;
+ wchar_t *wfname;
+
+ wfname = utf8_to_wchar (fname);
+ if (!wfname)
+ rc = 0;
+ else
+ {
+ rc = DeleteFileW (wfname);
+ if (!rc)
+ gnupg_w32_set_errno (-1);
+ xfree (wfname);
+ }
+ if (!rc)
+ return -1;
+ return 0;
+#else
+ return remove (fname);
+#endif
+}
+
+
+/* Helper for gnupg_rename_file. */
+#ifdef HAVE_W32_SYSTEM
+static int
+w32_rename (const char *oldname, const char *newname)
+{
+ if (any8bitchar (oldname) || any8bitchar (newname))
+ {
+ wchar_t *woldname, *wnewname;
+ int ret;
+
+ woldname = utf8_to_wchar (oldname);
+ if (!woldname)
+ return -1;
+ wnewname = utf8_to_wchar (newname);
+ if (!wnewname)
+ {
+ xfree (wnewname);
+ return -1;
+ }
+ ret = _wrename (woldname, wnewname);
+ xfree (wnewname);
+ xfree (woldname);
+ return ret;
+ }
+ else
+ return rename (oldname, newname);
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/* Wrapper for rename(2) to handle Windows peculiarities. If
+ * BLOCK_SIGNALS is not NULL and points to a variable set to true, all
+ * signals will be blocked by calling gnupg_block_all_signals; the
+ * caller needs to call gnupg_unblock_all_signals if that variable is
+ * still set to true on return. */
+gpg_error_t
+gnupg_rename_file (const char *oldname, const char *newname, int *block_signals)
+{
+ gpg_error_t err = 0;
+
+ if (block_signals && *block_signals)
+ gnupg_block_all_signals ();
+
+#ifdef HAVE_DOSISH_SYSTEM
+ {
+ int wtime = 0;
+
+ gnupg_remove (newname);
+ again:
+ if (w32_rename (oldname, newname))
+ {
+ if (GetLastError () == ERROR_SHARING_VIOLATION)
+ {
+ /* Another process has the file open. We do not use a
+ * lock for read but instead we wait until the other
+ * process has closed the file. This may take long but
+ * that would also be the case with a dotlock approach for
+ * read and write. Note that we don't need this on Unix
+ * due to the inode concept.
+ *
+ * So let's wait until the rename has worked. The retry
+ * intervals are 50, 100, 200, 400, 800, 50ms, ... */
+ if (!wtime || wtime >= 800)
+ wtime = 50;
+ else
+ wtime *= 2;
+
+ if (wtime >= 800)
+ log_info (_("waiting for file '%s' to become accessible ...\n"),
+ oldname);
+
+ Sleep (wtime);
+ goto again;
+ }
+ err = my_error_from_syserror ();
+ }
+ }
+#else /* Unix */
+ {
+#ifdef __riscos__
+ gnupg_remove (newname);
+#endif
+ if (rename (oldname, newname) )
+ err = my_error_from_syserror ();
+ }
+#endif /* Unix */
+
+ if (block_signals && *block_signals && err)
+ {
+ gnupg_unblock_all_signals ();
+ *block_signals = 0;
+ }
+
+ if (err)
+ log_error (_("renaming '%s' to '%s' failed: %s\n"),
+ oldname, newname, gpg_strerror (err));
+ return err;
+}
+
+
+#ifndef HAVE_W32_SYSTEM
+static mode_t
+modestr_to_mode (const char *modestr, mode_t oldmode)
+{
+ static struct {
+ char letter;
+ mode_t value;
+ } table[] = { { '-', 0 },
+ { 'r', S_IRUSR }, { 'w', S_IWUSR }, { 'x', S_IXUSR },
+ { 'r', S_IRGRP }, { 'w', S_IWGRP }, { 'x', S_IXGRP },
+ { 'r', S_IROTH }, { 'w', S_IWOTH }, { 'x', S_IXOTH } };
+ int idx;
+ mode_t mode = 0;
+
+ /* For now we only support a string as used by ls(1) and no octal
+ * numbers. The first character must be a dash. */
+ for (idx=0; idx < 10 && *modestr; idx++, modestr++)
+ {
+ if (*modestr == table[idx].letter)
+ mode |= table[idx].value;
+ else if (*modestr == '.')
+ {
+ if (!idx)
+ ; /* Skip the dummy. */
+ else if ((oldmode & table[idx].value))
+ mode |= (oldmode & table[idx].value);
+ else
+ mode &= ~(oldmode & table[idx].value);
+ }
+ else if (*modestr != '-')
+ break;
+ }
+
+
+ return mode;
+}
+#endif
+
+
+/* A wrapper around mkdir which takes a string for the mode argument.
+ This makes it easier to handle the mode argument which is not
+ defined on all systems. The format of the modestring is
+
+ "-rwxrwxrwx"
+
+ '-' is a don't care or not set. 'r', 'w', 'x' are read allowed,
+ write allowed, execution allowed with the first group for the user,
+ the second for the group and the third for all others. If the
+ string is shorter than above the missing mode characters are meant
+ to be not set. */
+int
+gnupg_mkdir (const char *name, const char *modestr)
+{
+#if GPG_ERROR_VERSION_NUMBER < 0x011c00 /* 1.28 */
+ #ifdef HAVE_W32CE_SYSTEM
+ wchar_t *wname;
+ (void)modestr;
+
+ wname = utf8_to_wchar (name);
+ if (!wname)
+ return -1;
+ if (!CreateDirectoryW (wname, NULL))
+ {
+ xfree (wname);
+ return -1; /* ERRNO is automagically provided by gpg-error.h. */
+ }
+ xfree (wname);
+ return 0;
+ #elif MKDIR_TAKES_ONE_ARG
+ (void)modestr;
+ /* Note: In the case of W32 we better use CreateDirectory and try to
+ set appropriate permissions. However using mkdir is easier
+ because this sets ERRNO. */
+ return mkdir (name);
+ #else
+ return mkdir (name, modestr_to_mode (modestr, 0));
+ #endif
+#else
+ /* Note that gpgrt_mkdir also sets ERRNO in addition to returing an
+ * gpg-error style error code. */
+ return gpgrt_mkdir (name, modestr);
+#endif
+}
+
+
+/* A simple wrapper around chdir. NAME is expected to be utf8
+ * encoded. */
+int
+gnupg_chdir (const char *name)
+{
+#if GPG_ERROR_VERSION_NUMBER < 0x011c00 /* 1.28 */
+ return chdir (name);
+#else /* Use the improved version from libgpg_error. */
+ /* Note that gpgrt_chdir also sets ERRNO in addition to returning a
+ * gpg-error style error code. */
+ return gpgrt_chdir (name);
+#endif
+}
+
+
+/* A wrapper around rmdir. NAME is expected to be utf8 encoded. */
+int
+gnupg_rmdir (const char *name)
+{
+#ifdef HAVE_W32_SYSTEM
+ int rc;
+ wchar_t *wfname;
+
+ wfname = utf8_to_wchar (name);
+ if (!wfname)
+ rc = 0;
+ else
+ {
+ rc = RemoveDirectoryW (wfname);
+ if (!rc)
+ gnupg_w32_set_errno (-1);
+ xfree (wfname);
+ }
+ if (!rc)
+ return -1;
+ return 0;
+#else
+ return rmdir (name);
+#endif
+}
+
+
+/* A wrapper around chmod which takes a string for the mode argument.
+ This makes it easier to handle the mode argument which is not
+ defined on all systems. The format of the modestring is the same
+ as for gnupg_mkdir with extra feature that a '.' keeps the original
+ mode bit. */
+int
+gnupg_chmod (const char *name, const char *modestr)
+{
+#ifdef HAVE_W32_SYSTEM
+ (void)name;
+ (void)modestr;
+ return 0;
+#else
+ mode_t oldmode;
+ if (strchr (modestr, '.'))
+ {
+ /* Get the old mode so that a '.' can copy that bit. */
+ struct stat st;
+
+ if (stat (name, &st))
+ return -1;
+ oldmode = st.st_mode;
+ }
+ else
+ oldmode = 0;
+ return chmod (name, modestr_to_mode (modestr, oldmode));
+#endif
+}
+
+
+/* Our version of mkdtemp. The API is identical to POSIX.1-2008
+ version. We do not use a system provided mkdtemp because we have a
+ good RNG instantly available and this way we don't have diverging
+ versions. */
+char *
+gnupg_mkdtemp (char *tmpl)
+{
+ /* A lower bound on the number of temporary files to attempt to
+ generate. The maximum total number of temporary file names that
+ can exist for a given template is 62**6 (5*36**3 for Windows).
+ It should never be necessary to try all these combinations.
+ Instead if a reasonable number of names is tried (we define
+ reasonable as 62**3 or 5*36**3) fail to give the system
+ administrator the chance to remove the problems. */
+#ifdef HAVE_W32_SYSTEM
+ static const char letters[] =
+ "abcdefghijklmnopqrstuvwxyz0123456789";
+# define NUMBER_OF_LETTERS 36
+# define ATTEMPTS_MIN (5 * 36 * 36 * 36)
+#else
+ static const char letters[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+# define NUMBER_OF_LETTERS 62
+# define ATTEMPTS_MIN (62 * 62 * 62)
+#endif
+ int len;
+ char *XXXXXX;
+ uint64_t value;
+ unsigned int count;
+ int save_errno = errno;
+ /* The number of times to attempt to generate a temporary file. To
+ conform to POSIX, this must be no smaller than TMP_MAX. */
+#if ATTEMPTS_MIN < TMP_MAX
+ unsigned int attempts = TMP_MAX;
+#else
+ unsigned int attempts = ATTEMPTS_MIN;
+#endif
+
+ len = strlen (tmpl);
+ if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
+ {
+ gpg_err_set_errno (EINVAL);
+ return NULL;
+ }
+
+ /* This is where the Xs start. */
+ XXXXXX = &tmpl[len - 6];
+
+ /* Get a random start value. */
+ gcry_create_nonce (&value, sizeof value);
+
+ /* Loop until a directory was created. */
+ for (count = 0; count < attempts; value += 7777, ++count)
+ {
+ uint64_t v = value;
+
+ /* Fill in the random bits. */
+ XXXXXX[0] = letters[v % NUMBER_OF_LETTERS];
+ v /= NUMBER_OF_LETTERS;
+ XXXXXX[1] = letters[v % NUMBER_OF_LETTERS];
+ v /= NUMBER_OF_LETTERS;
+ XXXXXX[2] = letters[v % NUMBER_OF_LETTERS];
+ v /= NUMBER_OF_LETTERS;
+ XXXXXX[3] = letters[v % NUMBER_OF_LETTERS];
+ v /= NUMBER_OF_LETTERS;
+ XXXXXX[4] = letters[v % NUMBER_OF_LETTERS];
+ v /= NUMBER_OF_LETTERS;
+ XXXXXX[5] = letters[v % NUMBER_OF_LETTERS];
+
+ if (!gnupg_mkdir (tmpl, "-rwx"))
+ {
+ gpg_err_set_errno (save_errno);
+ return tmpl;
+ }
+ if (errno != EEXIST)
+ return NULL;
+ }
+
+ /* We got out of the loop because we ran out of combinations to try. */
+ gpg_err_set_errno (EEXIST);
+ return NULL;
+}
+
+
+int
+gnupg_setenv (const char *name, const char *value, int overwrite)
+{
+#ifdef HAVE_W32CE_SYSTEM
+ (void)name;
+ (void)value;
+ (void)overwrite;
+ return 0;
+#else /*!W32CE*/
+# ifdef HAVE_W32_SYSTEM
+ /* Windows maintains (at least) two sets of environment variables.
+ One set can be accessed by GetEnvironmentVariable and
+ SetEnvironmentVariable. This set is inherited by the children.
+ The other set is maintained in the C runtime, and is accessed
+ using getenv and putenv. We try to keep them in sync by
+ modifying both sets. */
+ {
+ int exists;
+ char tmpbuf[10];
+ exists = GetEnvironmentVariable (name, tmpbuf, sizeof tmpbuf);
+
+ if ((! exists || overwrite) && !SetEnvironmentVariable (name, value))
+ {
+ gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */
+ return -1;
+ }
+ }
+# endif /*W32*/
+
+# ifdef HAVE_SETENV
+ return setenv (name, value, overwrite);
+# else /*!HAVE_SETENV*/
+ if (! getenv (name) || overwrite)
+ {
+ char *buf;
+
+ (void)overwrite;
+ if (!name || !value)
+ {
+ gpg_err_set_errno (EINVAL);
+ return -1;
+ }
+ buf = strconcat (name, "=", value, NULL);
+ if (!buf)
+ return -1;
+# if __GNUC__
+# warning no setenv - using putenv but leaking memory.
+# endif
+ return putenv (buf);
+ }
+ return 0;
+# endif /*!HAVE_SETENV*/
+#endif /*!W32CE*/
+}
+
+
+int
+gnupg_unsetenv (const char *name)
+{
+#ifdef HAVE_W32CE_SYSTEM
+ (void)name;
+ return 0;
+#else /*!W32CE*/
+# ifdef HAVE_W32_SYSTEM
+ /* Windows maintains (at least) two sets of environment variables.
+ One set can be accessed by GetEnvironmentVariable and
+ SetEnvironmentVariable. This set is inherited by the children.
+ The other set is maintained in the C runtime, and is accessed
+ using getenv and putenv. We try to keep them in sync by
+ modifying both sets. */
+ if (!SetEnvironmentVariable (name, NULL))
+ {
+ gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */
+ return -1;
+ }
+# endif /*W32*/
+
+# ifdef HAVE_UNSETENV
+ return unsetenv (name);
+# else /*!HAVE_UNSETENV*/
+ {
+ char *buf;
+
+ if (!name)
+ {
+ gpg_err_set_errno (EINVAL);
+ return -1;
+ }
+ buf = xtrystrdup (name);
+ if (!buf)
+ return -1;
+# if __GNUC__
+# warning no unsetenv - trying putenv but leaking memory.
+# endif
+ return putenv (buf);
+ }
+# endif /*!HAVE_UNSETENV*/
+#endif /*!W32CE*/
+}
+
+
+/* Return the current working directory as a malloced string. Return
+ NULL and sets ERRNO on error. */
+char *
+gnupg_getcwd (void)
+{
+#if GPGRT_VERSION_NUMBER < 0x012800 /* 1.40 */
+# ifdef HAVE_W32_SYSTEM
+ wchar_t wbuffer[MAX_PATH + sizeof(wchar_t)];
+ DWORD wlen;
+ char *buf, *p;
+
+ wlen = GetCurrentDirectoryW (MAX_PATH, wbuffer);
+ if (!wlen)
+ {
+ gpg_err_set_errno (EINVAL);
+ return NULL;
+
+ }
+ else if (wlen > MAX_PATH)
+ {
+ gpg_err_set_errno (ENAMETOOLONG);
+ return NULL;
+ }
+ buf = wchar_to_utf8 (wbuffer);
+ if (buf)
+ {
+ for (p=buf; *p; p++)
+ if (*p == '\\')
+ *p = '/';
+ }
+ return buf;
+
+# else /*Unix*/
+ char *buffer;
+ size_t size = 100;
+
+ for (;;)
+ {
+ buffer = xtrymalloc (size+1);
+ if (!buffer)
+ return NULL;
+ if (getcwd (buffer, size) == buffer)
+ return buffer;
+ xfree (buffer);
+ if (errno != ERANGE)
+ return NULL;
+ size *= 2;
+ }
+# endif /*Unix*/
+#else
+ return gpgrt_getcwd ();
+#endif
+}
+
+
+/* A simple wrapper around access. NAME is expected to be utf8
+ * encoded. This function returns an error code and sets ERRNO. */
+gpg_err_code_t
+gnupg_access (const char *name, int mode)
+{
+#if GPGRT_VERSION_NUMBER < 0x012800 /* 1.40 */
+# ifdef HAVE_W32_SYSTEM
+ wchar_t *wfname;
+ gpg_err_code_t ec;
+
+ wfname = utf8_to_wchar (name);
+ if (!wfname)
+ ec = gpg_err_code_from_syserror ();
+ else
+ {
+ ec = _waccess (wfname, mode)? gpg_err_code_from_syserror () : 0;
+ xfree (wfname);
+ }
+ return ec;
+# else
+ return access (name, mode)? gpg_err_code_from_syserror () : 0;
+# endif
+#else /* gpgrt 1.40 or newer. */
+ return gpgrt_access (name, mode);
+#endif
+}
+
+
+/* A wrapper around stat to handle Unicode file names under Windows. */
+#ifdef HAVE_STAT
+int
+gnupg_stat (const char *name, struct stat *statbuf)
+{
+# ifdef HAVE_W32_SYSTEM
+ if (any8bitchar (name))
+ {
+ wchar_t *wname;
+ struct _stat32 st32;
+ int ret;
+
+ wname = utf8_to_wchar (name);
+ if (!wname)
+ return -1;
+ ret = _wstat (wname, &st32);
+ xfree (wname);
+ if (!ret)
+ {
+ statbuf->st_dev = st32.st_dev;
+ statbuf->st_ino = st32.st_ino;
+ statbuf->st_mode = st32.st_mode;
+ statbuf->st_nlink = st32.st_nlink;
+ statbuf->st_uid = st32.st_uid;
+ statbuf->st_gid = st32.st_gid;
+ statbuf->st_rdev = st32.st_rdev;
+ statbuf->st_size = st32.st_size;
+ statbuf->st_atime = st32.st_atime;
+ statbuf->st_mtime = st32.st_mtime;
+ statbuf->st_ctime = st32.st_ctime;
+ }
+ return ret;
+ }
+ else
+ return stat (name, statbuf);
+# else
+ return stat (name, statbuf);
+# endif
+}
+#endif /*HAVE_STAT*/
+
+
+/* Wrapper around fopen for the cases where we have not yet switched
+ * to es_fopen. Note that for convenience the prototype is in util.h */
+FILE *
+gnupg_fopen (const char *fname, const char *mode)
+{
+#ifdef HAVE_W32_SYSTEM
+ if (any8bitchar (fname))
+ {
+ wchar_t *wfname;
+ const wchar_t *wmode;
+ wchar_t *wmodebuf = NULL;
+ FILE *ret;
+
+ wfname = utf8_to_wchar (fname);
+ if (!wfname)
+ return NULL;
+ if (!strcmp (mode, "r"))
+ wmode = L"r";
+ else if (!strcmp (mode, "rb"))
+ wmode = L"rb";
+ else if (!strcmp (mode, "w"))
+ wmode = L"w";
+ else if (!strcmp (mode, "wb"))
+ wmode = L"wb";
+ else
+ {
+ wmodebuf = utf8_to_wchar (mode);
+ if (!wmodebuf)
+ {
+ xfree (wfname);
+ return NULL;
+ }
+ wmode = wmodebuf;
+ }
+ ret = _wfopen (wfname, wmode);
+ xfree (wfname);
+ xfree (wmodebuf);
+ return ret;
+ }
+ else
+ return fopen (fname, mode);
+
+#else /*Unix*/
+ return fopen (fname, mode);
+#endif /*Unix*/
+}
+
+
+
+/* A wrapper around open to handle Unicode file names under Windows. */
+int
+gnupg_open (const char *name, int flags, unsigned int mode)
+{
+#ifdef HAVE_W32_SYSTEM
+ if (any8bitchar (name))
+ {
+ wchar_t *wname;
+ int ret;
+
+ wname = utf8_to_wchar (name);
+ if (!wname)
+ return -1;
+ ret = _wopen (wname, flags, mode);
+ xfree (wname);
+ return ret;
+ }
+ else
+ return open (name, flags, mode);
+#else
+ return open (name, flags, mode);
+#endif
+}
+
+
+/* A wrapper around opendir to handle Unicode file names under
+ * Windows. This assumes the mingw toolchain. */
+gnupg_dir_t
+gnupg_opendir (const char *name)
+{
+#ifdef HAVE_W32_SYSTEM
+ _WDIR *dir;
+ wchar_t *wname;
+#else
+ DIR *dir;
+#endif
+ gnupg_dir_t gdir;
+
+#ifdef HAVE_W32_SYSTEM
+ /* Note: See gpgtar-create for an alternative implementation which
+ * could be used here to avoid a mingw dependency. */
+ wname = utf8_to_wchar (name);
+ if (!wname)
+ return NULL;
+ dir = _wopendir (wname);
+ xfree (wname);
+#else
+ dir = opendir (name);
+#endif
+
+ if (!dir)
+ return NULL;
+
+ gdir = xtrymalloc (sizeof *gdir);
+ if (!gdir)
+ {
+ int save_errno = errno;
+#ifdef HAVE_W32_SYSTEM
+ _wclosedir (dir);
+#else
+ closedir (dir);
+#endif
+ gpg_err_set_errno (save_errno);
+ return NULL;
+ }
+ gdir->dir = dir;
+ gdir->namesize = 0;
+ gdir->dirent.d_name = gdir->name;
+
+ return gdir;
+}
+
+
+gnupg_dirent_t
+gnupg_readdir (gnupg_dir_t gdir)
+{
+#ifdef HAVE_W32_SYSTEM
+ char *namebuffer = NULL;
+ struct _wdirent *de;
+#else
+ struct dirent *de;
+#endif
+ size_t n;
+ gnupg_dirent_t gde;
+ const char *name;
+
+ if (!gdir)
+ {
+ gpg_err_set_errno (EINVAL);
+ return 0;
+ }
+
+#ifdef HAVE_W32_SYSTEM
+ de = _wreaddir (gdir->dir);
+ if (!de)
+ return NULL;
+ namebuffer = wchar_to_utf8 (de->d_name);
+ if (!namebuffer)
+ return NULL;
+ name = namebuffer;
+#else
+ de = readdir (gdir->dir);
+ if (!de)
+ return NULL;
+ name = de->d_name;
+#endif
+
+ gde = &gdir->dirent;
+ n = strlen (name);
+ if (gdir->namesize)
+ {
+ /* Use allocated buffer. */
+ if (n+1 >= gdir->namesize || !gde->d_name)
+ {
+ gdir->namesize = n + 256;
+ xfree (gde->d_name);
+ gde->d_name = xtrymalloc (gdir->namesize);
+ if (!gde->d_name)
+ return NULL; /* ERRNO is already set. */
+ }
+ strcpy (gde->d_name, name);
+ }
+ else if (n+1 >= sizeof (gdir->name))
+ {
+ /* Switch to allocated buffer. */
+ gdir->namesize = n + 256;
+ gde->d_name = xtrymalloc (gdir->namesize);
+ if (!gde->d_name)
+ return NULL; /* ERRNO is already set. */
+ strcpy (gde->d_name, name);
+ }
+ else
+ {
+ /* Use static buffer. */
+ gde->d_name = gdir->name;
+ strcpy (gde->d_name, name);
+ }
+
+#ifdef HAVE_W32_SYSTEM
+ xfree (namebuffer);
+#endif
+
+ return gde;
+}
+
+
+int
+gnupg_closedir (gnupg_dir_t gdir)
+{
+#ifdef HAVE_W32_SYSTEM
+ _WDIR *dir;
+#else
+ DIR *dir;
+#endif
+
+ if (!gdir)
+ return 0;
+ dir = gdir->dir;
+ if (gdir->namesize)
+ xfree (gdir->dirent.d_name);
+ xfree (gdir);
+
+#ifdef HAVE_W32_SYSTEM
+ return _wclosedir (dir);
+#else
+ return closedir (dir);
+#endif
+}
+
+
+
+#ifdef HAVE_W32CE_SYSTEM
+/* There is a isatty function declaration in cegcc but it does not
+ make sense, thus we redefine it. */
+int
+_gnupg_isatty (int fd)
+{
+ (void)fd;
+ return 0;
+}
+#endif
+
+
+#ifdef HAVE_W32CE_SYSTEM
+/* Replacement for getenv which takes care of the our use of getenv.
+ The code is not thread safe but we expect it to work in all cases
+ because it is called for the first time early enough. */
+char *
+_gnupg_getenv (const char *name)
+{
+ static int initialized;
+ static char *assuan_debug;
+
+ if (!initialized)
+ {
+ assuan_debug = read_w32_registry_string (NULL,
+ "\\Software\\GNU\\libassuan",
+ "debug");
+ initialized = 1;
+ }
+
+ if (!strcmp (name, "ASSUAN_DEBUG"))
+ return assuan_debug;
+ else
+ return NULL;
+}
+
+#endif /*HAVE_W32CE_SYSTEM*/
+
+
+#ifdef HAVE_W32_SYSTEM
+/* Return the user's security identifier from the current process. */
+PSID
+w32_get_user_sid (void)
+{
+ int okay = 0;
+ HANDLE proc = NULL;
+ HANDLE token = NULL;
+ TOKEN_USER *user = NULL;
+ PSID sid = NULL;
+ DWORD tokenlen, sidlen;
+
+ proc = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
+ if (!proc)
+ goto leave;
+
+ if (!OpenProcessToken (proc, TOKEN_QUERY, &token))
+ goto leave;
+
+ if (!GetTokenInformation (token, TokenUser, NULL, 0, &tokenlen)
+ && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ goto leave;
+
+ user = xtrymalloc (tokenlen);
+ if (!user)
+ goto leave;
+
+ if (!GetTokenInformation (token, TokenUser, user, tokenlen, &tokenlen))
+ goto leave;
+ if (!IsValidSid (user->User.Sid))
+ goto leave;
+ sidlen = GetLengthSid (user->User.Sid);
+ sid = xtrymalloc (sidlen);
+ if (!sid)
+ goto leave;
+ if (!CopySid (sidlen, sid, user->User.Sid))
+ goto leave;
+ okay = 1;
+
+ leave:
+ xfree (user);
+ if (token)
+ CloseHandle (token);
+ if (proc)
+ CloseHandle (proc);
+
+ if (!okay)
+ {
+ xfree (sid);
+ sid = NULL;
+ }
+ return sid;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+
+/* Support for inotify under Linux. */
+
+/* Store a new inotify file handle for FNAME at R_FD or return an
+ * error code. This file descriptor watch the removal of FNAME. */
+gpg_error_t
+gnupg_inotify_watch_delete_self (int *r_fd, const char *fname)
+{
+#if HAVE_INOTIFY_INIT
+ gpg_error_t err;
+ int fd;
+
+ *r_fd = -1;
+
+ if (!fname)
+ return my_error (GPG_ERR_INV_VALUE);
+
+ fd = inotify_init ();
+ if (fd == -1)
+ return my_error_from_syserror ();
+
+ if (inotify_add_watch (fd, fname, IN_DELETE_SELF) == -1)
+ {
+ err = my_error_from_syserror ();
+ close (fd);
+ return err;
+ }
+
+ *r_fd = fd;
+ return 0;
+#else /*!HAVE_INOTIFY_INIT*/
+
+ (void)fname;
+ *r_fd = -1;
+ return my_error (GPG_ERR_NOT_SUPPORTED);
+
+#endif /*!HAVE_INOTIFY_INIT*/
+}
+
+
+/* Store a new inotify file handle for SOCKET_NAME at R_FD or return
+ * an error code. */
+gpg_error_t
+gnupg_inotify_watch_socket (int *r_fd, const char *socket_name)
+{
+#if HAVE_INOTIFY_INIT
+ gpg_error_t err;
+ char *fname;
+ int fd;
+ char *p;
+
+ *r_fd = -1;
+
+ if (!socket_name)
+ return my_error (GPG_ERR_INV_VALUE);
+
+ fname = xtrystrdup (socket_name);
+ if (!fname)
+ return my_error_from_syserror ();
+
+ fd = inotify_init ();
+ if (fd == -1)
+ {
+ err = my_error_from_syserror ();
+ xfree (fname);
+ return err;
+ }
+
+ /* We need to watch the directory for the file because there won't
+ * be an IN_DELETE_SELF for a socket file. To handle a removal of
+ * the directory we also watch the directory itself. */
+ p = strrchr (fname, '/');
+ if (p)
+ *p = 0;
+ if (inotify_add_watch (fd, fname,
+ (IN_DELETE|IN_DELETE_SELF|IN_EXCL_UNLINK)) == -1)
+ {
+ err = my_error_from_syserror ();
+ close (fd);
+ xfree (fname);
+ return err;
+ }
+
+ xfree (fname);
+
+ *r_fd = fd;
+ return 0;
+#else /*!HAVE_INOTIFY_INIT*/
+
+ (void)socket_name;
+ *r_fd = -1;
+ return my_error (GPG_ERR_NOT_SUPPORTED);
+
+#endif /*!HAVE_INOTIFY_INIT*/
+}
+
+
+/* Read an inotify event and return true if it matches NAME or if it
+ * sees an IN_DELETE_SELF event for the directory of NAME. */
+int
+gnupg_inotify_has_name (int fd, const char *name)
+{
+#if USE_NPTH && HAVE_INOTIFY_INIT
+#define BUFSIZE_FOR_INOTIFY (sizeof (struct inotify_event) + 255 + 1)
+ union {
+ struct inotify_event ev;
+ char _buf[sizeof (struct inotify_event) + 255 + 1];
+ } buf;
+ struct inotify_event *evp;
+ int n;
+
+ n = npth_read (fd, &buf, sizeof buf);
+ /* log_debug ("notify read: n=%d\n", n); */
+ evp = &buf.ev;
+ while (n >= sizeof (struct inotify_event))
+ {
+ /* log_debug (" mask=%x len=%u name=(%s)\n", */
+ /* evp->mask, (unsigned int)evp->len, evp->len? evp->name:""); */
+ if ((evp->mask & IN_UNMOUNT))
+ {
+ /* log_debug (" found (dir unmounted)\n"); */
+ return 3; /* Directory was unmounted. */
+ }
+ if ((evp->mask & IN_DELETE_SELF))
+ {
+ /* log_debug (" found (dir removed)\n"); */
+ return 2; /* Directory was removed. */
+ }
+ if ((evp->mask & IN_DELETE))
+ {
+ if (evp->len >= strlen (name) && !strcmp (evp->name, name))
+ {
+ /* log_debug (" found (file removed)\n"); */
+ return 1; /* File was removed. */
+ }
+ }
+ n -= sizeof (*evp) + evp->len;
+ evp = (struct inotify_event *)(void *)
+ ((char *)evp + sizeof (*evp) + evp->len);
+ }
+
+#else /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/
+
+ (void)fd;
+ (void)name;
+
+#endif /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/
+
+ return 0; /* Not found. */
+}
+
+
+/* Return a malloc'ed string that is the path to the passed
+ * unix-domain socket (or return NULL if this is not a valid
+ * unix-domain socket). We use a plain int here because it is only
+ * used on Linux.
+ *
+ * FIXME: This function needs to be moved to libassuan. */
+#ifndef HAVE_W32_SYSTEM
+char *
+gnupg_get_socket_name (int fd)
+{
+ struct sockaddr_un un;
+ socklen_t len = sizeof(un);
+ char *name = NULL;
+
+ if (getsockname (fd, (struct sockaddr*)&un, &len) != 0)
+ log_error ("could not getsockname(%d): %s\n", fd,
+ gpg_strerror (my_error_from_syserror ()));
+ else if (un.sun_family != AF_UNIX)
+ log_error ("file descriptor %d is not a unix-domain socket\n", fd);
+ else if (len <= offsetof (struct sockaddr_un, sun_path))
+ log_error ("socket name not present for file descriptor %d\n", fd);
+ else if (len > sizeof(un))
+ log_error ("socket name for file descriptor %d was truncated "
+ "(passed %zu bytes, wanted %u)\n", fd, sizeof(un), len);
+ else
+ {
+ size_t namelen = len - offsetof (struct sockaddr_un, sun_path);
+
+ /* log_debug ("file descriptor %d has path %s (%zu octets)\n", fd, */
+ /* un.sun_path, namelen); */
+ name = xtrymalloc (namelen + 1);
+ if (!name)
+ log_error ("failed to allocate memory for name of fd %d: %s\n",
+ fd, gpg_strerror (my_error_from_syserror ()));
+ else
+ {
+ memcpy (name, un.sun_path, namelen);
+ name[namelen] = 0;
+ }
+ }
+
+ return name;
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+/* Check whether FD is valid. */
+int
+gnupg_fd_valid (int fd)
+{
+ int d = dup (fd);
+ if (d < 0)
+ return 0;
+ close (d);
+ return 1;
+}
+
+
+/* Return a malloced copy of the current user's account name; this may
+ * return NULL on memory failure. Note that this should eventually be
+ * replaced by a gpgrt function. */
+char *
+gnupg_getusername (void)
+{
+ char *result = NULL;
+
+#ifdef HAVE_W32_SYSTEM
+ wchar_t wtmp[1];
+ wchar_t *wbuf;
+ DWORD wsize = 1;
+
+ GetUserNameW (wtmp, &wsize);
+ wbuf = xtrymalloc (wsize * sizeof *wbuf);
+ if (!wbuf)
+ {
+ gpg_err_set_errno (ENOMEM);
+ return NULL;
+ }
+ if (!GetUserNameW (wbuf, &wsize))
+ {
+ gpg_err_set_errno (EINVAL);
+ xfree (wbuf);
+ return NULL;
+ }
+ result= wchar_to_utf8 (wbuf);
+ xfree (wbuf);
+
+#else /* !HAVE_W32_SYSTEM */
+
+# if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
+ struct passwd *pwd;
+
+ pwd = getpwuid (getuid());
+ if (pwd)
+ result = xtrystrdup (pwd->pw_name);
+
+# endif /*HAVE_PWD_H*/
+
+#endif /* !HAVE_W32_SYSTEM */
+
+ return result;
+}