diff options
Diffstat (limited to 'sftp-glob.c')
-rw-r--r-- | sftp-glob.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/sftp-glob.c b/sftp-glob.c new file mode 100644 index 0000000..1b82759 --- /dev/null +++ b/sftp-glob.c @@ -0,0 +1,180 @@ +/* $OpenBSD: sftp-glob.c,v 1.33 2023/09/10 23:12:32 djm Exp $ */ +/* + * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif + +#include <dirent.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include "xmalloc.h" +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" + +int sftp_glob(struct sftp_conn *, const char *, int, + int (*)(const char *, int), glob_t *); + +struct SFTP_OPENDIR { + SFTP_DIRENT **dir; + int offset; +}; + +static struct { + struct sftp_conn *conn; +} cur; + +static void * +fudge_opendir(const char *path) +{ + struct SFTP_OPENDIR *r; + + r = xcalloc(1, sizeof(*r)); + + if (sftp_readdir(cur.conn, path, &r->dir)) { + free(r); + return(NULL); + } + + r->offset = 0; + + return((void *)r); +} + +static struct dirent * +fudge_readdir(struct SFTP_OPENDIR *od) +{ + /* Solaris needs sizeof(dirent) + path length (see below) */ + static char buf[sizeof(struct dirent) + MAXPATHLEN]; + struct dirent *ret = (struct dirent *)buf; +#ifdef __GNU_LIBRARY__ + static int inum = 1; +#endif /* __GNU_LIBRARY__ */ + + if (od->dir[od->offset] == NULL) + return(NULL); + + memset(buf, 0, sizeof(buf)); + + /* + * Solaris defines dirent->d_name as a one byte array and expects + * you to hack around it. + */ +#ifdef BROKEN_ONE_BYTE_DIRENT_D_NAME + strlcpy(ret->d_name, od->dir[od->offset++]->filename, MAXPATHLEN); +#else + strlcpy(ret->d_name, od->dir[od->offset++]->filename, + sizeof(ret->d_name)); +#endif +#ifdef __GNU_LIBRARY__ + /* + * Idiot glibc uses extensions to struct dirent for readdir with + * ALTDIRFUNCs. Not that this is documented anywhere but the + * source... Fake an inode number to appease it. + */ + ret->d_ino = inum++; + if (!inum) + inum = 1; +#endif /* __GNU_LIBRARY__ */ + + return(ret); +} + +static void +fudge_closedir(struct SFTP_OPENDIR *od) +{ + sftp_free_dirents(od->dir); + free(od); +} + +static int +fudge_lstat(const char *path, struct stat *st) +{ + Attrib a; + + if (sftp_lstat(cur.conn, path, 1, &a) != 0) + return -1; + + attrib_to_stat(&a, st); + + return 0; +} + +static int +fudge_stat(const char *path, struct stat *st) +{ + Attrib a; + + if (sftp_stat(cur.conn, path, 1, &a) != 0) + return -1; + + attrib_to_stat(&a, st); + + return(0); +} + +int +sftp_glob(struct sftp_conn *conn, const char *pattern, int flags, + int (*errfunc)(const char *, int), glob_t *pglob) +{ + int r; + size_t l; + char *s; + struct stat sb; + + pglob->gl_opendir = fudge_opendir; + pglob->gl_readdir = (struct dirent *(*)(void *))fudge_readdir; + pglob->gl_closedir = (void (*)(void *))fudge_closedir; + pglob->gl_lstat = fudge_lstat; + pglob->gl_stat = fudge_stat; + + memset(&cur, 0, sizeof(cur)); + cur.conn = conn; + + if ((r = glob(pattern, flags | GLOB_ALTDIRFUNC, errfunc, pglob)) != 0) + return r; + /* + * When both GLOB_NOCHECK and GLOB_MARK are active, a single gl_pathv + * entry has been returned and that entry has not already been marked, + * then check whether it needs a '/' appended as a directory mark. + * + * This ensures that a NOCHECK result is annotated as a directory. + * The glob(3) spec doesn't promise to mark NOCHECK entries, but doing + * it simplifies our callers (sftp/scp) considerably. + * + * XXX doesn't try to handle gl_offs. + */ + if ((flags & (GLOB_NOCHECK|GLOB_MARK)) == (GLOB_NOCHECK|GLOB_MARK) && + pglob->gl_matchc == 0 && pglob->gl_offs == 0 && + pglob->gl_pathc == 1 && (s = pglob->gl_pathv[0]) != NULL && + (l = strlen(s)) > 0 && s[l-1] != '/') { + if (fudge_stat(s, &sb) == 0 && S_ISDIR(sb.st_mode)) { + /* NOCHECK on a directory; annotate */ + if ((s = realloc(s, l + 2)) != NULL) { + memcpy(s + l, "/", 2); + pglob->gl_pathv[0] = s; + } + } + } + return 0; +} |