| /* $OpenBSD: sftp-glob.c,v 1.31 2022/10/24 21:51:55 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 remote_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 (do_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) |
| { |
| free_sftp_dirents(od->dir); |
| free(od); |
| } |
| |
| static int |
| fudge_lstat(const char *path, struct stat *st) |
| { |
| Attrib *a; |
| |
| if (!(a = do_lstat(cur.conn, path, 1))) |
| return(-1); |
| |
| attrib_to_stat(a, st); |
| |
| return(0); |
| } |
| |
| static int |
| fudge_stat(const char *path, struct stat *st) |
| { |
| Attrib *a; |
| |
| if (!(a = do_stat(cur.conn, path, 1))) |
| return(-1); |
| |
| attrib_to_stat(a, st); |
| |
| return(0); |
| } |
| |
| int |
| remote_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; |
| } |