1 /* $OpenBSD: sftp-glob.c,v 1.33 2023/09/10 23:12:32 djm Exp $ */ 2 /* 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "includes.h" 19 20 #include <sys/types.h> 21 #ifdef HAVE_SYS_STAT_H 22 # include <sys/stat.h> 23 #endif 24 25 #include <dirent.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <stdarg.h> 29 30 #include "xmalloc.h" 31 #include "sftp.h" 32 #include "sftp-common.h" 33 #include "sftp-client.h" 34 35 int sftp_glob(struct sftp_conn *, const char *, int, 36 int (*)(const char *, int), glob_t *); 37 38 struct SFTP_OPENDIR { 39 SFTP_DIRENT **dir; 40 int offset; 41 }; 42 43 static struct { 44 struct sftp_conn *conn; 45 } cur; 46 47 static void * 48 fudge_opendir(const char *path) 49 { 50 struct SFTP_OPENDIR *r; 51 52 r = xcalloc(1, sizeof(*r)); 53 54 if (sftp_readdir(cur.conn, path, &r->dir)) { 55 free(r); 56 return(NULL); 57 } 58 59 r->offset = 0; 60 61 return((void *)r); 62 } 63 64 static struct dirent * 65 fudge_readdir(struct SFTP_OPENDIR *od) 66 { 67 /* Solaris needs sizeof(dirent) + path length (see below) */ 68 static char buf[sizeof(struct dirent) + MAXPATHLEN]; 69 struct dirent *ret = (struct dirent *)buf; 70 #ifdef __GNU_LIBRARY__ 71 static int inum = 1; 72 #endif /* __GNU_LIBRARY__ */ 73 74 if (od->dir[od->offset] == NULL) 75 return(NULL); 76 77 memset(buf, 0, sizeof(buf)); 78 79 /* 80 * Solaris defines dirent->d_name as a one byte array and expects 81 * you to hack around it. 82 */ 83 #ifdef BROKEN_ONE_BYTE_DIRENT_D_NAME 84 strlcpy(ret->d_name, od->dir[od->offset++]->filename, MAXPATHLEN); 85 #else 86 strlcpy(ret->d_name, od->dir[od->offset++]->filename, 87 sizeof(ret->d_name)); 88 #endif 89 #ifdef __GNU_LIBRARY__ 90 /* 91 * Idiot glibc uses extensions to struct dirent for readdir with 92 * ALTDIRFUNCs. Not that this is documented anywhere but the 93 * source... Fake an inode number to appease it. 94 */ 95 ret->d_ino = inum++; 96 if (!inum) 97 inum = 1; 98 #endif /* __GNU_LIBRARY__ */ 99 100 return(ret); 101 } 102 103 static void 104 fudge_closedir(struct SFTP_OPENDIR *od) 105 { 106 sftp_free_dirents(od->dir); 107 free(od); 108 } 109 110 static int 111 fudge_lstat(const char *path, struct stat *st) 112 { 113 Attrib a; 114 115 if (sftp_lstat(cur.conn, path, 1, &a) != 0) 116 return -1; 117 118 attrib_to_stat(&a, st); 119 120 return 0; 121 } 122 123 static int 124 fudge_stat(const char *path, struct stat *st) 125 { 126 Attrib a; 127 128 if (sftp_stat(cur.conn, path, 1, &a) != 0) 129 return -1; 130 131 attrib_to_stat(&a, st); 132 133 return(0); 134 } 135 136 int 137 sftp_glob(struct sftp_conn *conn, const char *pattern, int flags, 138 int (*errfunc)(const char *, int), glob_t *pglob) 139 { 140 int r; 141 size_t l; 142 char *s; 143 struct stat sb; 144 145 pglob->gl_opendir = fudge_opendir; 146 pglob->gl_readdir = (struct dirent *(*)(void *))fudge_readdir; 147 pglob->gl_closedir = (void (*)(void *))fudge_closedir; 148 pglob->gl_lstat = fudge_lstat; 149 pglob->gl_stat = fudge_stat; 150 151 memset(&cur, 0, sizeof(cur)); 152 cur.conn = conn; 153 154 if ((r = glob(pattern, flags | GLOB_ALTDIRFUNC, errfunc, pglob)) != 0) 155 return r; 156 /* 157 * When both GLOB_NOCHECK and GLOB_MARK are active, a single gl_pathv 158 * entry has been returned and that entry has not already been marked, 159 * then check whether it needs a '/' appended as a directory mark. 160 * 161 * This ensures that a NOCHECK result is annotated as a directory. 162 * The glob(3) spec doesn't promise to mark NOCHECK entries, but doing 163 * it simplifies our callers (sftp/scp) considerably. 164 * 165 * XXX doesn't try to handle gl_offs. 166 */ 167 if ((flags & (GLOB_NOCHECK|GLOB_MARK)) == (GLOB_NOCHECK|GLOB_MARK) && 168 pglob->gl_matchc == 0 && pglob->gl_offs == 0 && 169 pglob->gl_pathc == 1 && (s = pglob->gl_pathv[0]) != NULL && 170 (l = strlen(s)) > 0 && s[l-1] != '/') { 171 if (fudge_stat(s, &sb) == 0 && S_ISDIR(sb.st_mode)) { 172 /* NOCHECK on a directory; annotate */ 173 if ((s = realloc(s, l + 2)) != NULL) { 174 memcpy(s + l, "/", 2); 175 pglob->gl_pathv[0] = s; 176 } 177 } 178 } 179 return 0; 180 } 181