From 7e22e08e2ab09cd3c2317f5e80e8ee47d46eff4b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 21 Oct 2020 16:59:38 +0200 Subject: [PATCH] common: New functions gnupg_opendir et al. * common/sysutils.h (struct gnupg_dirent_s): New. * common/sysutils.c: Include dirent.h. (struct gnupg_dir_s): New. (gnupg_opendir, gnupg_readdir, gnupg_closedir): New. Change all callers of opendir, readdir, and closedir to use these functions. -- GnuPG-bug-id: 5098 --- agent/command.c | 11 ++- common/sysutils.c | 162 +++++++++++++++++++++++++++++++++++++++++ common/sysutils.h | 18 +++++ dirmngr/certcache.c | 10 +-- dirmngr/crlcache.c | 22 +++--- dirmngr/dirmngr.c | 2 +- tools/gpg-pair-tool.c | 11 ++- tools/gpg-wks-server.c | 22 +++--- tools/gpgtar-create.c | 2 + 9 files changed, 217 insertions(+), 43 deletions(-) diff --git a/agent/command.c b/agent/command.c index 160979dae..e21dcdac4 100644 --- a/agent/command.c +++ b/agent/command.c @@ -1370,7 +1370,7 @@ cmd_keyinfo (assuan_context_t ctx, char *line) ctrl_t ctrl = assuan_get_pointer (ctx); int err; unsigned char grip[20]; - DIR *dir = NULL; + gnupg_dir_t dir = NULL; int list_mode; int opt_data, opt_ssh_fpr, opt_with_ssh; ssh_control_file_t cf = NULL; @@ -1454,7 +1454,7 @@ cmd_keyinfo (assuan_context_t ctx, char *line) else if (list_mode) { char *dirname; - struct dirent *dir_entry; + gnupg_dirent_t dir_entry; if (ctrl->restricted) return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); @@ -1466,7 +1466,7 @@ cmd_keyinfo (assuan_context_t ctx, char *line) err = gpg_error_from_syserror (); goto leave; } - dir = opendir (dirname); + dir = gnupg_opendir (dirname); if (!dir) { err = gpg_error_from_syserror (); @@ -1475,7 +1475,7 @@ cmd_keyinfo (assuan_context_t ctx, char *line) } xfree (dirname); - while ( (dir_entry = readdir (dir)) ) + while ( (dir_entry = gnupg_readdir (dir)) ) { if (strlen (dir_entry->d_name) != 44 || strcmp (dir_entry->d_name + 40, ".key")) @@ -1536,8 +1536,7 @@ cmd_keyinfo (assuan_context_t ctx, char *line) leave: ssh_close_control_file (cf); - if (dir) - closedir (dir); + gnupg_closedir (dir); if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND) leave_cmd (ctx, err); return err; diff --git a/common/sysutils.c b/common/sysutils.c index 9926c0e52..25de374a3 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -79,6 +79,7 @@ # include #endif #include +#include #include @@ -89,6 +90,22 @@ #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; @@ -1176,6 +1193,151 @@ gnupg_open (const char *name, int flags, unsigned int mode) } +/* 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 +} + + /* Try to set an envvar. Print only a notice on error. */ #ifndef HAVE_W32_SYSTEM static void diff --git a/common/sysutils.h b/common/sysutils.h index eee4cf5b6..13e781587 100644 --- a/common/sysutils.h +++ b/common/sysutils.h @@ -50,6 +50,19 @@ typedef int gnupg_fd_t; # include #endif +struct gnupg_dir_s; +typedef struct gnupg_dir_s *gnupg_dir_t; +struct gnupg_dirent_s +{ + /* We don't have a d_ino because that can't be used on Windows + * anyway. D_NAME is a pointer into the gnupg_dir_s which has a + * static buffer or allocates sufficient space as needed. This is + * only valid after gnupg_readdir. */ + char *d_name; +}; +typedef struct gnupg_dirent_s *gnupg_dirent_t; + + void trap_unaligned (void); int disable_core_dumps (void); int enable_core_dumps (void); @@ -81,6 +94,11 @@ gpg_err_code_t gnupg_access (const char *name, int mode); int gnupg_stat (const char *name, struct stat *statbuf); #endif /*HAVE_STAT*/ int gnupg_open (const char *name, int flags, unsigned int mode); + +gnupg_dir_t gnupg_opendir (const char *name); +gnupg_dirent_t gnupg_readdir (gnupg_dir_t gdir); +int gnupg_closedir (gnupg_dir_t gdir); + gpg_error_t gnupg_chuid (const char *user, int silent); char *gnupg_get_socket_name (int fd); int gnupg_fd_valid (int fd); diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c index 04da510a0..9ca6069a2 100644 --- a/dirmngr/certcache.c +++ b/dirmngr/certcache.c @@ -364,8 +364,8 @@ static gpg_error_t load_certs_from_dir (const char *dirname, unsigned int trustclass) { gpg_error_t err; - DIR *dir; - struct dirent *ep; + gnupg_dir_t dir; + gnupg_dirent_t ep; char *p; size_t n; estream_t fp; @@ -373,13 +373,13 @@ load_certs_from_dir (const char *dirname, unsigned int trustclass) ksba_cert_t cert; char *fname = NULL; - dir = opendir (dirname); + dir = gnupg_opendir (dirname); if (!dir) { return 0; /* We do not consider this a severe error. */ } - while ( (ep=readdir (dir)) ) + while ( (ep = gnupg_readdir (dir)) ) { p = ep->d_name; if (*p == '.' || !*p) @@ -447,7 +447,7 @@ load_certs_from_dir (const char *dirname, unsigned int trustclass) } xfree (fname); - closedir (dir); + gnupg_closedir (dir); return 0; } diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c index 88b07cd7a..9d18b721f 100644 --- a/dirmngr/crlcache.c +++ b/dirmngr/crlcache.c @@ -101,10 +101,6 @@ #ifndef HAVE_W32_SYSTEM #include #endif -#ifdef MKDIR_TAKES_ONE_ARG -#undef mkdir -#define mkdir(a,b) mkdir(a) -#endif #include "dirmngr.h" #include "validate.h" @@ -206,15 +202,15 @@ get_current_cache (void) static int create_directory_if_needed (const char *name) { - DIR *dir; + gnupg_dir_t dir; char *fname; fname = make_filename (opt.homedir_cache, name, NULL); - dir = opendir (fname); + dir = gnupg_opendir (fname); if (!dir) { log_info (_("creating directory '%s'\n"), fname); - if (mkdir (fname, S_IRUSR|S_IWUSR|S_IXUSR) ) + if (gnupg_mkdir (fname, "-rwx")) { int save_errno = errno; log_error (_("error creating directory '%s': %s\n"), @@ -225,7 +221,7 @@ create_directory_if_needed (const char *name) } } else - closedir (dir); + gnupg_closedir (dir); xfree (fname); return 0; } @@ -237,8 +233,8 @@ static int cleanup_cache_dir (int force) { char *dname = make_filename (opt.homedir_cache, DBDIR_D, NULL); - DIR *dir; - struct dirent *de; + gnupg_dir_t dir; + gnupg_dirent_t de; int problem = 0; if (!force) @@ -251,7 +247,7 @@ cleanup_cache_dir (int force) } } - dir = opendir (dname); + dir = gnupg_opendir (dname); if (!dir) { log_error (_("error reading directory '%s': %s\n"), @@ -260,7 +256,7 @@ cleanup_cache_dir (int force) return -1; } - while ((de = readdir (dir))) + while ((de = gnupg_readdir (dir))) { if (strcmp (de->d_name, "." ) && strcmp (de->d_name, "..")) { @@ -289,7 +285,7 @@ cleanup_cache_dir (int force) } } xfree (dname); - closedir (dir); + gnupg_closedir (dir); return problem; } diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index 838ab9487..06bb4bae6 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -1305,7 +1305,7 @@ main (int argc, char **argv) { log_error (_("error binding socket to '%s': %s\n"), serv_addr.sun_path, - gpg_strerror (gpg_error_from_errno (errno))); + gpg_strerror (gpg_error_from_syserror ())); assuan_sock_close (fd); dirmngr_exit (1); } diff --git a/tools/gpg-pair-tool.c b/tools/gpg-pair-tool.c index 4a9a6f56c..4054f5d19 100644 --- a/tools/gpg-pair-tool.c +++ b/tools/gpg-pair-tool.c @@ -1701,8 +1701,8 @@ expire_old_states (void) { gpg_error_t err = 0; const char *dirname; - DIR *dir = NULL; - struct dirent *dir_entry; + gnupg_dir_t dir = NULL; + gnupg_dirent_t dir_entry; char *fname = NULL; estream_t fp = NULL; nvc_t nvc = NULL; @@ -1712,14 +1712,14 @@ expire_old_states (void) unsigned long now = gnupg_get_time (); dirname = get_pairing_statedir (); - dir = opendir (dirname); + dir = gnupg_opendir (dirname); if (!dir) { err = gpg_error_from_syserror (); goto leave; } - while ((dir_entry = readdir (dir))) + while ((dir_entry = gnupg_readdir (dir))) { if (strlen (dir_entry->d_name) != 16+4 || strcmp (dir_entry->d_name + 16, ".pa1")) @@ -1781,8 +1781,7 @@ expire_old_states (void) if (err) log_error ("expiring old states in '%s' failed: %s\n", dirname, gpg_strerror (err)); - if (dir) - closedir (dir); + gnupg_closedir (dir); es_fclose (fp); xfree (fname); } diff --git a/tools/gpg-wks-server.c b/tools/gpg-wks-server.c index 0cfe47fba..937479260 100644 --- a/tools/gpg-wks-server.c +++ b/tools/gpg-wks-server.c @@ -1613,22 +1613,22 @@ static gpg_error_t get_domain_list (strlist_t *r_list) { gpg_error_t err; - DIR *dir = NULL; + gnupg_dir_t dir = NULL; char *fname = NULL; - struct dirent *dentry; + gnupg_dirent_t dentry; struct stat sb; strlist_t list = NULL; *r_list = NULL; - dir = opendir (opt.directory); + dir = gnupg_opendir (opt.directory); if (!dir) { err = gpg_error_from_syserror (); goto leave; } - while ((dentry = readdir (dir))) + while ((dentry = gnupg_readdir (dir))) { if (*dentry->d_name == '.') continue; @@ -1668,8 +1668,7 @@ get_domain_list (strlist_t *r_list) leave: free_strlist (list); - if (dir) - closedir (dir); + gnupg_closedir (dir); xfree (fname); return err; } @@ -1682,8 +1681,8 @@ expire_one_domain (const char *top_dirname, const char *domain) gpg_error_t err; char *dirname; char *fname = NULL; - DIR *dir = NULL; - struct dirent *dentry; + gnupg_dir_t dir = NULL; + gnupg_dirent_t dentry; struct stat sb; time_t now = gnupg_get_time (); @@ -1696,7 +1695,7 @@ expire_one_domain (const char *top_dirname, const char *domain) goto leave; } - dir = opendir (dirname); + dir = gnupg_opendir (dirname); if (!dir) { err = gpg_error_from_syserror (); @@ -1705,7 +1704,7 @@ expire_one_domain (const char *top_dirname, const char *domain) goto leave; } - while ((dentry = readdir (dir))) + while ((dentry = gnupg_readdir (dir))) { if (*dentry->d_name == '.') continue; @@ -1754,8 +1753,7 @@ expire_one_domain (const char *top_dirname, const char *domain) err = 0; leave: - if (dir) - closedir (dir); + gnupg_closedir (dir); xfree (dirname); xfree (fname); return err; diff --git a/tools/gpgtar-create.c b/tools/gpgtar-create.c index d13b1dc7e..6114d68ff 100644 --- a/tools/gpgtar-create.c +++ b/tools/gpgtar-create.c @@ -301,6 +301,8 @@ scan_directory (const char *dname, scanctrl_t scanctrl) gpg_error_t err = 0; #ifdef HAVE_W32_SYSTEM + /* Note that we introduced gnupg_opendir only after we had deployed + * this code and thus we don't change it for now. */ WIN32_FIND_DATAW fi; HANDLE hd = INVALID_HANDLE_VALUE; char *p;