1*edf85781SEd Maste /* $OpenBSD: sftp-glob.c,v 1.33 2023/09/10 23:12:32 djm Exp $ */
21e8db6e2SBrian Feldman /*
3efcad6b7SDag-Erling Smørgrav * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
41e8db6e2SBrian Feldman *
5efcad6b7SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any
6efcad6b7SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above
7efcad6b7SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies.
81e8db6e2SBrian Feldman *
9efcad6b7SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10efcad6b7SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11efcad6b7SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12efcad6b7SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13efcad6b7SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14efcad6b7SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15efcad6b7SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
161e8db6e2SBrian Feldman */
171e8db6e2SBrian Feldman
181e8db6e2SBrian Feldman #include "includes.h"
191e8db6e2SBrian Feldman
20761efaa7SDag-Erling Smørgrav #include <sys/types.h>
21761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_STAT_H
22761efaa7SDag-Erling Smørgrav # include <sys/stat.h>
23761efaa7SDag-Erling Smørgrav #endif
24761efaa7SDag-Erling Smørgrav
25761efaa7SDag-Erling Smørgrav #include <dirent.h>
26f7167e0eSDag-Erling Smørgrav #include <stdlib.h>
27761efaa7SDag-Erling Smørgrav #include <string.h>
2819261079SEd Maste #include <stdarg.h>
29761efaa7SDag-Erling Smørgrav
301e8db6e2SBrian Feldman #include "xmalloc.h"
311e8db6e2SBrian Feldman #include "sftp.h"
321e8db6e2SBrian Feldman #include "sftp-common.h"
331e8db6e2SBrian Feldman #include "sftp-client.h"
34efcad6b7SDag-Erling Smørgrav
35*edf85781SEd Maste int sftp_glob(struct sftp_conn *, const char *, int,
36efcad6b7SDag-Erling Smørgrav int (*)(const char *, int), glob_t *);
371e8db6e2SBrian Feldman
381e8db6e2SBrian Feldman struct SFTP_OPENDIR {
391e8db6e2SBrian Feldman SFTP_DIRENT **dir;
401e8db6e2SBrian Feldman int offset;
411e8db6e2SBrian Feldman };
421e8db6e2SBrian Feldman
431e8db6e2SBrian Feldman static struct {
44ae1f160dSDag-Erling Smørgrav struct sftp_conn *conn;
451e8db6e2SBrian Feldman } cur;
461e8db6e2SBrian Feldman
47ae1f160dSDag-Erling Smørgrav static void *
fudge_opendir(const char * path)48ae1f160dSDag-Erling Smørgrav fudge_opendir(const char *path)
491e8db6e2SBrian Feldman {
501e8db6e2SBrian Feldman struct SFTP_OPENDIR *r;
511e8db6e2SBrian Feldman
520a37d4a3SXin LI r = xcalloc(1, sizeof(*r));
531e8db6e2SBrian Feldman
54*edf85781SEd Maste if (sftp_readdir(cur.conn, path, &r->dir)) {
55e4a9863fSDag-Erling Smørgrav free(r);
561e8db6e2SBrian Feldman return(NULL);
574b17dab0SDag-Erling Smørgrav }
581e8db6e2SBrian Feldman
591e8db6e2SBrian Feldman r->offset = 0;
601e8db6e2SBrian Feldman
611e8db6e2SBrian Feldman return((void *)r);
621e8db6e2SBrian Feldman }
631e8db6e2SBrian Feldman
64ae1f160dSDag-Erling Smørgrav static struct dirent *
fudge_readdir(struct SFTP_OPENDIR * od)65ae1f160dSDag-Erling Smørgrav fudge_readdir(struct SFTP_OPENDIR *od)
661e8db6e2SBrian Feldman {
6783d2307dSDag-Erling Smørgrav /* Solaris needs sizeof(dirent) + path length (see below) */
6883d2307dSDag-Erling Smørgrav static char buf[sizeof(struct dirent) + MAXPATHLEN];
6983d2307dSDag-Erling Smørgrav struct dirent *ret = (struct dirent *)buf;
7083d2307dSDag-Erling Smørgrav #ifdef __GNU_LIBRARY__
7183d2307dSDag-Erling Smørgrav static int inum = 1;
7283d2307dSDag-Erling Smørgrav #endif /* __GNU_LIBRARY__ */
731e8db6e2SBrian Feldman
741e8db6e2SBrian Feldman if (od->dir[od->offset] == NULL)
751e8db6e2SBrian Feldman return(NULL);
761e8db6e2SBrian Feldman
7783d2307dSDag-Erling Smørgrav memset(buf, 0, sizeof(buf));
781e8db6e2SBrian Feldman
7983d2307dSDag-Erling Smørgrav /*
8083d2307dSDag-Erling Smørgrav * Solaris defines dirent->d_name as a one byte array and expects
8183d2307dSDag-Erling Smørgrav * you to hack around it.
8283d2307dSDag-Erling Smørgrav */
8383d2307dSDag-Erling Smørgrav #ifdef BROKEN_ONE_BYTE_DIRENT_D_NAME
8483d2307dSDag-Erling Smørgrav strlcpy(ret->d_name, od->dir[od->offset++]->filename, MAXPATHLEN);
8583d2307dSDag-Erling Smørgrav #else
8683d2307dSDag-Erling Smørgrav strlcpy(ret->d_name, od->dir[od->offset++]->filename,
8783d2307dSDag-Erling Smørgrav sizeof(ret->d_name));
8883d2307dSDag-Erling Smørgrav #endif
8983d2307dSDag-Erling Smørgrav #ifdef __GNU_LIBRARY__
9083d2307dSDag-Erling Smørgrav /*
9183d2307dSDag-Erling Smørgrav * Idiot glibc uses extensions to struct dirent for readdir with
9283d2307dSDag-Erling Smørgrav * ALTDIRFUNCs. Not that this is documented anywhere but the
9383d2307dSDag-Erling Smørgrav * source... Fake an inode number to appease it.
9483d2307dSDag-Erling Smørgrav */
9583d2307dSDag-Erling Smørgrav ret->d_ino = inum++;
9683d2307dSDag-Erling Smørgrav if (!inum)
9783d2307dSDag-Erling Smørgrav inum = 1;
9883d2307dSDag-Erling Smørgrav #endif /* __GNU_LIBRARY__ */
9983d2307dSDag-Erling Smørgrav
10083d2307dSDag-Erling Smørgrav return(ret);
1011e8db6e2SBrian Feldman }
1021e8db6e2SBrian Feldman
103ae1f160dSDag-Erling Smørgrav static void
fudge_closedir(struct SFTP_OPENDIR * od)104ae1f160dSDag-Erling Smørgrav fudge_closedir(struct SFTP_OPENDIR *od)
1051e8db6e2SBrian Feldman {
106*edf85781SEd Maste sftp_free_dirents(od->dir);
107e4a9863fSDag-Erling Smørgrav free(od);
1081e8db6e2SBrian Feldman }
1091e8db6e2SBrian Feldman
110ae1f160dSDag-Erling Smørgrav static int
fudge_lstat(const char * path,struct stat * st)111ae1f160dSDag-Erling Smørgrav fudge_lstat(const char *path, struct stat *st)
1121e8db6e2SBrian Feldman {
113*edf85781SEd Maste Attrib a;
1141e8db6e2SBrian Feldman
115*edf85781SEd Maste if (sftp_lstat(cur.conn, path, 1, &a) != 0)
116*edf85781SEd Maste return -1;
1171e8db6e2SBrian Feldman
118*edf85781SEd Maste attrib_to_stat(&a, st);
1191e8db6e2SBrian Feldman
120*edf85781SEd Maste return 0;
1211e8db6e2SBrian Feldman }
1221e8db6e2SBrian Feldman
123ae1f160dSDag-Erling Smørgrav static int
fudge_stat(const char * path,struct stat * st)124ae1f160dSDag-Erling Smørgrav fudge_stat(const char *path, struct stat *st)
1251e8db6e2SBrian Feldman {
126*edf85781SEd Maste Attrib a;
1271e8db6e2SBrian Feldman
128*edf85781SEd Maste if (sftp_stat(cur.conn, path, 1, &a) != 0)
129*edf85781SEd Maste return -1;
1301e8db6e2SBrian Feldman
131*edf85781SEd Maste attrib_to_stat(&a, st);
1321e8db6e2SBrian Feldman
1331e8db6e2SBrian Feldman return(0);
1341e8db6e2SBrian Feldman }
1351e8db6e2SBrian Feldman
1361e8db6e2SBrian Feldman int
sftp_glob(struct sftp_conn * conn,const char * pattern,int flags,int (* errfunc)(const char *,int),glob_t * pglob)137*edf85781SEd Maste sftp_glob(struct sftp_conn *conn, const char *pattern, int flags,
1381e8db6e2SBrian Feldman int (*errfunc)(const char *, int), glob_t *pglob)
1391e8db6e2SBrian Feldman {
140f374ba41SEd Maste int r;
141f374ba41SEd Maste size_t l;
142f374ba41SEd Maste char *s;
143f374ba41SEd Maste struct stat sb;
144f374ba41SEd Maste
145ae1f160dSDag-Erling Smørgrav pglob->gl_opendir = fudge_opendir;
146ae1f160dSDag-Erling Smørgrav pglob->gl_readdir = (struct dirent *(*)(void *))fudge_readdir;
147ae1f160dSDag-Erling Smørgrav pglob->gl_closedir = (void (*)(void *))fudge_closedir;
1481e8db6e2SBrian Feldman pglob->gl_lstat = fudge_lstat;
1491e8db6e2SBrian Feldman pglob->gl_stat = fudge_stat;
1501e8db6e2SBrian Feldman
1511e8db6e2SBrian Feldman memset(&cur, 0, sizeof(cur));
152ae1f160dSDag-Erling Smørgrav cur.conn = conn;
1531e8db6e2SBrian Feldman
154f374ba41SEd Maste if ((r = glob(pattern, flags | GLOB_ALTDIRFUNC, errfunc, pglob)) != 0)
155f374ba41SEd Maste return r;
156f374ba41SEd Maste /*
157f374ba41SEd Maste * When both GLOB_NOCHECK and GLOB_MARK are active, a single gl_pathv
158f374ba41SEd Maste * entry has been returned and that entry has not already been marked,
159f374ba41SEd Maste * then check whether it needs a '/' appended as a directory mark.
160f374ba41SEd Maste *
161f374ba41SEd Maste * This ensures that a NOCHECK result is annotated as a directory.
162f374ba41SEd Maste * The glob(3) spec doesn't promise to mark NOCHECK entries, but doing
163f374ba41SEd Maste * it simplifies our callers (sftp/scp) considerably.
164f374ba41SEd Maste *
165f374ba41SEd Maste * XXX doesn't try to handle gl_offs.
166f374ba41SEd Maste */
167f374ba41SEd Maste if ((flags & (GLOB_NOCHECK|GLOB_MARK)) == (GLOB_NOCHECK|GLOB_MARK) &&
168f374ba41SEd Maste pglob->gl_matchc == 0 && pglob->gl_offs == 0 &&
169f374ba41SEd Maste pglob->gl_pathc == 1 && (s = pglob->gl_pathv[0]) != NULL &&
170f374ba41SEd Maste (l = strlen(s)) > 0 && s[l-1] != '/') {
171f374ba41SEd Maste if (fudge_stat(s, &sb) == 0 && S_ISDIR(sb.st_mode)) {
172f374ba41SEd Maste /* NOCHECK on a directory; annotate */
173f374ba41SEd Maste if ((s = realloc(s, l + 2)) != NULL) {
174f374ba41SEd Maste memcpy(s + l, "/", 2);
175f374ba41SEd Maste pglob->gl_pathv[0] = s;
176f374ba41SEd Maste }
177f374ba41SEd Maste }
178f374ba41SEd Maste }
179f374ba41SEd Maste return 0;
1801e8db6e2SBrian Feldman }
181