]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
- djm@cvs.openbsd.org 2001/03/13 22:42:54
authorDamien Miller <djm@mindrot.org>
Tue, 13 Mar 2001 23:27:09 +0000 (10:27 +1100)
committerDamien Miller <djm@mindrot.org>
Tue, 13 Mar 2001 23:27:09 +0000 (10:27 +1100)
    [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
Makefile.in
sftp-client.c
sftp-client.h
sftp-glob.c [new file with mode: 0644]
sftp-glob.h [new file with mode: 0644]
sftp-int.c

index 13e1f3606f7e346471945d03ff7ab66ebdf3e91e..99ed4d03d35d2742dd5dce560a46b55fde003fa1 100644 (file)
--- 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
  - Wrote replacements for strlcpy and mkdtemp
  - Released 1.0pre1
 
-$Id: ChangeLog,v 1.952 2001/03/13 23:15:20 djm Exp $
+$Id: ChangeLog,v 1.953 2001/03/13 23:27:09 djm Exp $
index bc54cb6775a1e668123b919d74053edd246df880..b25ca00cde508f4f2d63a77e9b396577a1857ada 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Makefile.in,v 1.159 2001/03/12 05:16:19 mouring Exp $
+# $Id: Makefile.in,v 1.160 2001/03/13 23:27:09 djm Exp $
 
 prefix=@prefix@
 exec_prefix=@exec_prefix@
@@ -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
index d1e4ebaccd26eb947e1931ee17d2a7d1758b3cee..9f77d366ccf9d3d698c5cfeec46c853aed4cb503 100644 (file)
@@ -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;
 }
+
index e836c0d668d4abf78d5b0641e06d348eef61c9fb..e7ba02ad615d9cf046a0eff423a293ae1661dcd4 100644 (file)
@@ -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.
 
 /* 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 (file)
index 0000000..17f46a1
--- /dev/null
@@ -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 <glob.h>
+
+#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 (file)
index 0000000..9e75168
--- /dev/null
@@ -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);
+
index 6f5b3677a3d50c7f584d8e05b7229ac3a371bde9..d350e398deb081da9b20cb7648115e861261fbc5 100644 (file)
  * 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 <glob.h>
 
 #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);