From 2e4fb373fccee2e5a296d484189169914f6e07d8 Mon Sep 17 00:00:00 2001 From: djm Date: Tue, 13 Mar 2001 23:27:09 +0000 Subject: [PATCH] - djm@cvs.openbsd.org 2001/03/13 22:42:54 [sftp-client.c sftp-client.h sftp-glob.c sftp-glob.h sftp-int.c] sftp client filename globbing for get, put, ch{mod,grp,own}. ok markus@ --- ChangeLog | 3 + Makefile.in | 4 +- sftp-client.c | 53 +++++++++++++++-- sftp-client.h | 16 ++++- sftp-glob.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++ sftp-glob.h | 32 ++++++++++ sftp-int.c | 133 ++++++++++++++++++++++++++++++----------- 7 files changed, 359 insertions(+), 42 deletions(-) create mode 100644 sftp-glob.c create mode 100644 sftp-glob.h diff --git a/ChangeLog b/ChangeLog index 1390c885..22ef4ac4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,9 @@ - markus@cvs.openbsd.org 2001/03/13 17:34:42 [auth-options.c] missing xfree, deny key on parse error; ok stevesk@ + - djm@cvs.openbsd.org 2001/03/13 22:42:54 + [sftp-client.c sftp-client.h sftp-glob.c sftp-glob.h sftp-int.c] + sftp client filename globbing for get, put, ch{mod,grp,own}. ok markus@ 20010313 - OpenBSD CVS Sync diff --git a/Makefile.in b/Makefile.in index 86cda697..4f6825f9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -116,8 +116,8 @@ ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a log.o ssh-keyscan.o sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o log.o sftp-server.o $(LD) -o $@ sftp-server.o sftp-common.o log.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) -sftp$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-client.o sftp-int.o sftp-common.o - $(LD) -o $@ sftp.o sftp-client.o sftp-common.o sftp-int.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) +sftp$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-client.o sftp-int.o sftp-common.o sftp-glob.o + $(LD) -o $@ sftp.o sftp-client.o sftp-common.o sftp-int.o sftp-glob.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) # test driver for the loginrec code - not built by default logintest: logintest.o $(LIBCOMPAT) libssh.a log.o loginrec.o diff --git a/sftp-client.c b/sftp-client.c index d1e4ebac..9f77d366 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -29,7 +29,7 @@ /* XXX: copy between two remote sites */ #include "includes.h" -RCSID("$OpenBSD: sftp-client.c,v 1.11 2001/03/07 10:11:22 djm Exp $"); +RCSID("$OpenBSD: sftp-client.c,v 1.12 2001/03/13 22:42:54 djm Exp $"); #include "ssh.h" #include "buffer.h" @@ -275,11 +275,13 @@ do_close(int fd_in, int fd_out, char *handle, u_int handle_len) return(status); } + int -do_ls(int fd_in, int fd_out, char *path) +do_lsreaddir(int fd_in, int fd_out, char *path, int printflag, + SFTP_DIRENT ***dir) { Buffer msg; - u_int type, id, handle_len, i, expected_id; + u_int type, id, handle_len, i, expected_id, ents; char *handle; id = msg_id++; @@ -296,6 +298,13 @@ do_ls(int fd_in, int fd_out, char *path) if (handle == NULL) return(-1); + if (dir) { + ents = 0; + *dir = xmalloc(sizeof(**dir)); + (*dir)[0] = NULL; + } + + for(;;) { int count; @@ -350,7 +359,18 @@ do_ls(int fd_in, int fd_out, char *path) longname = buffer_get_string(&msg, NULL); a = decode_attrib(&msg); - printf("%s\n", longname); + if (printflag) + printf("%s\n", longname); + + if (dir) { + *dir = xrealloc(*dir, sizeof(**dir) * + (ents + 2)); + (*dir)[ents] = xmalloc(sizeof(***dir)); + (*dir)[ents]->filename = xstrdup(filename); + (*dir)[ents]->longname = xstrdup(longname); + memcpy(&(*dir)[ents]->a, a, sizeof(*a)); + (*dir)[++ents] = NULL; + } xfree(filename); xfree(longname); @@ -364,6 +384,30 @@ do_ls(int fd_in, int fd_out, char *path) return(0); } +int +do_ls(int fd_in, int fd_out, char *path) +{ + return(do_lsreaddir(fd_in, fd_out, path, 1, NULL)); +} + +int +do_readdir(int fd_in, int fd_out, char *path, SFTP_DIRENT ***dir) +{ + return(do_lsreaddir(fd_in, fd_out, path, 0, dir)); +} + +void free_sftp_dirents(SFTP_DIRENT **s) +{ + int i; + + for(i = 0; s[i]; i++) { + xfree(s[i]->filename); + xfree(s[i]->longname); + xfree(s[i]); + } + xfree(s); +} + int do_rm(int fd_in, int fd_out, char *path) { @@ -875,3 +919,4 @@ done: buffer_free(&msg); return status; } + diff --git a/sftp-client.h b/sftp-client.h index e836c0d6..e7ba02ad 100644 --- a/sftp-client.h +++ b/sftp-client.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.h,v 1.2 2001/03/07 10:11:23 djm Exp $ */ +/* $OpenBSD: sftp-client.h,v 1.3 2001/03/13 22:42:54 djm Exp $ */ /* * Copyright (c) 2001 Damien Miller. All rights reserved. @@ -26,6 +26,14 @@ /* Client side of SSH2 filexfer protocol */ +typedef struct SFTP_DIRENT SFTP_DIRENT; + +struct SFTP_DIRENT { + char *filename; + char *longname; + Attrib a; +}; + /* * Initialiase a SSH filexfer connection. Returns -1 on error or * protocol version on success. @@ -38,6 +46,12 @@ int do_close(int fd_in, int fd_out, char *handle, u_int handle_len); /* List contents of directory 'path' to stdout */ int do_ls(int fd_in, int fd_out, char *path); +/* Read contents of 'path' to NULL-terminated array 'dir' */ +int do_readdir(int fd_in, int fd_out, char *path, SFTP_DIRENT ***dir); + +/* Frees a NULL-terminated array of SFTP_DIRENTs (eg. from do_readdir) */ +void free_sftp_dirents(SFTP_DIRENT **s); + /* Delete file 'path' */ int do_rm(int fd_in, int fd_out, char *path); diff --git a/sftp-glob.c b/sftp-glob.c new file mode 100644 index 00000000..17f46a15 --- /dev/null +++ b/sftp-glob.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2001 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$OpenBSD: sftp-glob.c,v 1.1 2001/03/13 22:42:54 djm Exp $"); + +#include + +#include "ssh.h" +#include "buffer.h" +#include "bufaux.h" +#include "getput.h" +#include "xmalloc.h" +#include "log.h" +#include "atomicio.h" +#include "pathnames.h" + +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" +#include "sftp-glob.h" + +struct SFTP_OPENDIR { + SFTP_DIRENT **dir; + int offset; +}; + +static struct { + int fd_in; + int fd_out; +} cur; + +void *fudge_opendir(const char *path) +{ + struct SFTP_OPENDIR *r; + + r = xmalloc(sizeof(*r)); + + if (do_readdir(cur.fd_in, cur.fd_out, (char*)path, &r->dir)) + return(NULL); + + r->offset = 0; + + return((void*)r); +} + +struct dirent *fudge_readdir(struct SFTP_OPENDIR *od) +{ + static struct dirent ret; +#ifdef __GNU_LIBRARY__ + static int inum = 1; +#endif /* __GNU_LIBRARY__ */ + + if (od->dir[od->offset] == NULL) + return(NULL); + + memset(&ret, 0, sizeof(ret)); + strlcpy(ret.d_name, od->dir[od->offset++]->filename, + sizeof(ret.d_name)); + +#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); +} + +void fudge_closedir(struct SFTP_OPENDIR *od) +{ + free_sftp_dirents(od->dir); + free(od); +} + +void attrib_to_stat(Attrib *a, struct stat *st) +{ + memset(st, 0, sizeof(*st)); + + if (a->flags & SSH2_FILEXFER_ATTR_SIZE) + st->st_size = a->size; + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + st->st_uid = a->uid; + st->st_gid = a->gid; + } + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + st->st_mode = a->perm; + if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + st->st_atime = a->atime; + st->st_mtime = a->mtime; + } +} + +int fudge_lstat(const char *path, struct stat *st) +{ + Attrib *a; + + if (!(a = do_lstat(cur.fd_in, cur.fd_out, (char*)path))) + return(-1); + + attrib_to_stat(a, st); + + return(0); +} + +int fudge_stat(const char *path, struct stat *st) +{ + Attrib *a; + + if (!(a = do_stat(cur.fd_in, cur.fd_out, (char*)path))) + return(-1); + + attrib_to_stat(a, st); + + return(0); +} + +int +remote_glob(int fd_in, int fd_out, const char *pattern, int flags, + const int (*errfunc)(const char *, int), glob_t *pglob) +{ + pglob->gl_opendir = (void*)fudge_opendir; + pglob->gl_readdir = (void*)fudge_readdir; + pglob->gl_closedir = (void*)fudge_closedir; + pglob->gl_lstat = fudge_lstat; + pglob->gl_stat = fudge_stat; + + memset(&cur, 0, sizeof(cur)); + cur.fd_in = fd_in; + cur.fd_out = fd_out; + + return(glob(pattern, flags | GLOB_ALTDIRFUNC, (void*)errfunc, + pglob)); +} diff --git a/sftp-glob.h b/sftp-glob.h new file mode 100644 index 00000000..9e75168a --- /dev/null +++ b/sftp-glob.h @@ -0,0 +1,32 @@ +/* $OpenBSD: sftp-glob.h,v 1.1 2001/03/13 22:42:54 djm Exp $ */ + +/* + * Copyright (c) 2001 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Remote sftp filename globbing */ + +int +remote_glob(int fd_in, int fd_out, const char *pattern, int flags, + const int (*errfunc)(const char *, int), glob_t *pglob); + diff --git a/sftp-int.c b/sftp-int.c index 6f5b3677..d350e398 100644 --- a/sftp-int.c +++ b/sftp-int.c @@ -22,13 +22,13 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* XXX: finish implementation of all commands */ -/* XXX: do fnmatch() instead of using raw pathname */ /* XXX: globbed ls */ /* XXX: recursive operations */ #include "includes.h" -RCSID("$OpenBSD: sftp-int.c,v 1.26 2001/03/07 10:11:23 djm Exp $"); +RCSID("$OpenBSD: sftp-int.c,v 1.27 2001/03/13 22:42:54 djm Exp $"); + +#include #include "buffer.h" #include "xmalloc.h" @@ -37,6 +37,7 @@ RCSID("$OpenBSD: sftp-int.c,v 1.26 2001/03/07 10:11:23 djm Exp $"); #include "sftp.h" #include "sftp-common.h" +#include "sftp-glob.h" #include "sftp-client.h" #include "sftp-int.h" @@ -283,8 +284,6 @@ infer_path(const char *p, char **ifp) { char *cp; - debug("XXX: P = \"%s\"", p); - cp = strrchr(p, '/'); if (cp == NULL) { *ifp = xstrdup(p); @@ -360,9 +359,6 @@ parse_args(const char **cpp, int *pflag, unsigned long *n_arg, /* Try to get second pathname (optional) */ if (get_pathname(&cp, path2)) return(-1); - /* Otherwise try to guess it from first path */ - if (*path2 == NULL && infer_path(*path1, path2)) - return(-1); break; case I_RENAME: case I_SYMLINK: @@ -451,11 +447,12 @@ int parse_dispatch_command(int in, int out, const char *cmd, char **pwd) { char *path1, *path2, *tmp; - int pflag, cmdnum; + int pflag, cmdnum, i; unsigned long n_arg; Attrib a, *aa; char path_buf[MAXPATHLEN]; int err = 0; + glob_t g; path1 = path2 = NULL; cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2); @@ -465,14 +462,63 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) case -1: break; case I_GET: - path1 = make_absolute(path1, *pwd); - err = do_download(in, out, path1, path2, pflag); + memset(&g, 0, sizeof(g)); + if (!remote_glob(in, out, path1, 0, NULL, &g)) { + if (path2) { + /* XXX: target should be directory */ + error("You cannot specify a target when " + "downloading multiple files"); + err = -1; + break; + } + for(i = 0; g.gl_pathv[i]; i++) { + if (!infer_path(g.gl_pathv[i], &path2)) { + printf("Fetching %s\n", g.gl_pathv[i]); + if (do_download(in, out, g.gl_pathv[i], + path2, pflag) == -1) + err = -1; + free(path2); + path2 = NULL; + } else + err = -1; + } + } else { + if (!path2 && infer_path(path1, &path2)) { + err = -1; + break; + } + err = do_download(in, out, path1, path2, pflag); + } break; case I_PUT: - path2 = make_absolute(path2, *pwd); - err = do_upload(in, out, path1, path2, pflag); - break; - case I_RENAME: + if (!glob(path1, 0, NULL, &g)) { + if (path2) { + error("You cannot specify a target when " + "uploading multiple files"); + err = -1; + break; + } + for(i = 0; g.gl_pathv[i]; i++) { + if (!infer_path(g.gl_pathv[i], &path2)) { + path2 = make_absolute(path2, *pwd); + printf("Uploading %s\n", g.gl_pathv[i]); + if (do_upload(in, out, g.gl_pathv[i], + path2, pflag) == -1) + err = -1; + free(path2); + path2 = NULL; + } else + err = -1; + } + } else { + if (!path2 && infer_path(path1, &path2)) { + err = -1; + break; + } + err = do_upload(in, out, path1, path2, pflag); + } + break; + case I_RENAME: path1 = make_absolute(path1, *pwd); path2 = make_absolute(path2, *pwd); err = do_rename(in, out, path1, path2); @@ -489,7 +535,12 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) break; case I_RM: path1 = make_absolute(path1, *pwd); - err = do_rm(in, out, path1); + remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); + for(i = 0; g.gl_pathv[i]; i++) { + printf("Removing %s\n", g.gl_pathv[i]); + if (do_rm(in, out, g.gl_pathv[i]) == -1) + err = -1; + } break; case I_MKDIR: path1 = make_absolute(path1, *pwd); @@ -577,33 +628,45 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = n_arg; - do_setstat(in, out, path1, &a); + remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); + for(i = 0; g.gl_pathv[i]; i++) { + printf("Changing mode on %s\n", g.gl_pathv[i]); + do_setstat(in, out, g.gl_pathv[i], &a); + } break; case I_CHOWN: path1 = make_absolute(path1, *pwd); - if (!(aa = do_stat(in, out, path1))) - break; - if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { - error("Can't get current ownership of " - "remote file \"%s\"", path1); - break; + remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); + for(i = 0; g.gl_pathv[i]; i++) { + if (!(aa = do_stat(in, out, g.gl_pathv[i]))) + continue; + if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { + error("Can't get current ownership of " + "remote file \"%s\"", g.gl_pathv[i]); + continue; + } + printf("Changing owner on %s\n", g.gl_pathv[i]); + aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; + aa->uid = n_arg; + do_setstat(in, out, g.gl_pathv[i], aa); } - aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; - aa->uid = n_arg; - do_setstat(in, out, path1, aa); break; case I_CHGRP: path1 = make_absolute(path1, *pwd); - if (!(aa = do_stat(in, out, path1))) - break; - if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { - error("Can't get current ownership of " - "remote file \"%s\"", path1); - break; + remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); + for(i = 0; g.gl_pathv[i]; i++) { + if (!(aa = do_stat(in, out, g.gl_pathv[i]))) + continue; + if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { + error("Can't get current ownership of " + "remote file \"%s\"", g.gl_pathv[i]); + continue; + } + printf("Changing group on %s\n", g.gl_pathv[i]); + aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; + aa->gid = n_arg; + do_setstat(in, out, g.gl_pathv[i], aa); } - aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; - aa->gid = n_arg; - do_setstat(in, out, path1, aa); break; case I_PWD: printf("Remote working directory: %s\n", *pwd); -- 2.45.2