]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: add support for a "lsetstat@openssh.com" extension. This
authordjm@openbsd.org <djm@openbsd.org>
Wed, 16 Jan 2019 23:22:10 +0000 (23:22 +0000)
committerDamien Miller <djm@mindrot.org>
Thu, 17 Jan 2019 00:08:13 +0000 (11:08 +1100)
replicates the functionality of the existing SSH2_FXP_SETSTAT operation but
does not follow symlinks. Based on a patch from Bert Haverkamp in bz#2067 but
with more attribute modifications supported.

ok markus@ dtucker@

OpenBSD-Commit-ID: f7234f6e90db19655d55d936a115ee4ccb6aaf80

sftp-server.c

index de9ad3d3bbd38742336671d8c561e7934ed00f93..19a132bd91ce44e8e715045fa151401936c8ea97 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-server.c,v 1.113 2019/01/01 23:10:53 djm Exp $ */
+/* $OpenBSD: sftp-server.c,v 1.114 2019/01/16 23:22:10 djm Exp $ */
 /*
  * Copyright (c) 2000-2004 Markus Friedl.  All rights reserved.
  *
@@ -107,6 +107,7 @@ static void process_extended_statvfs(u_int32_t id);
 static void process_extended_fstatvfs(u_int32_t id);
 static void process_extended_hardlink(u_int32_t id);
 static void process_extended_fsync(u_int32_t id);
+static void process_extended_lsetstat(u_int32_t id);
 static void process_extended(u_int32_t id);
 
 struct sftp_handler {
@@ -148,6 +149,7 @@ static const struct sftp_handler extended_handlers[] = {
        { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 },
        { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 },
        { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 },
+       { "lsetstat", "lsetstat@openssh.com", 0, process_extended_lsetstat, 1 },
        { NULL, NULL, 0, NULL, 0 }
 };
 
@@ -666,6 +668,8 @@ process_init(void)
            (r = sshbuf_put_cstring(msg, "1")) != 0 || /* version */
            /* fsync extension */
            (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 ||
+           (r = sshbuf_put_cstring(msg, "1")) != 0 || /* version */
+           (r = sshbuf_put_cstring(msg, "lsetstat@openssh.com")) != 0 ||
            (r = sshbuf_put_cstring(msg, "1")) != 0) /* version */
                fatal("%s: buffer error: %s", __func__, ssh_err(r));
        send_msg(msg);
@@ -889,6 +893,18 @@ attrib_to_tv(const Attrib *a)
        return tv;
 }
 
+static struct timespec *
+attrib_to_ts(const Attrib *a)
+{
+       static struct timespec ts[2];
+
+       ts[0].tv_sec = a->atime;
+       ts[0].tv_nsec = 0;
+       ts[1].tv_sec = a->mtime;
+       ts[1].tv_nsec = 0;
+       return ts;
+}
+
 static void
 process_setstat(u_int32_t id)
 {
@@ -1369,6 +1385,55 @@ process_extended_fsync(u_int32_t id)
        send_status(id, status);
 }
 
+static void
+process_extended_lsetstat(u_int32_t id)
+{
+       Attrib a;
+       char *name;
+       int r, status = SSH2_FX_OK;
+
+       if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
+           (r = decode_attrib(iqueue, &a)) != 0)
+               fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
+       debug("request %u: lsetstat name \"%s\"", id, name);
+       if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
+               /* nonsensical for links */
+               status = SSH2_FX_BAD_MESSAGE;
+               goto out;
+       }
+       if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
+               logit("set \"%s\" mode %04o", name, a.perm);
+               r = fchmodat(AT_FDCWD, name,
+                   a.perm & 07777, AT_SYMLINK_NOFOLLOW);
+               if (r == -1)
+                       status = errno_to_portable(errno);
+       }
+       if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
+               char buf[64];
+               time_t t = a.mtime;
+
+               strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
+                   localtime(&t));
+               logit("set \"%s\" modtime %s", name, buf);
+               r = utimensat(AT_FDCWD, name,
+                   attrib_to_ts(&a), AT_SYMLINK_NOFOLLOW);
+               if (r == -1)
+                       status = errno_to_portable(errno);
+       }
+       if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
+               logit("set \"%s\" owner %lu group %lu", name,
+                   (u_long)a.uid, (u_long)a.gid);
+               r = fchownat(AT_FDCWD, name, a.uid, a.gid,
+                   AT_SYMLINK_NOFOLLOW);
+               if (r == -1)
+                       status = errno_to_portable(errno);
+       }
+ out:
+       send_status(id, status);
+       free(name);
+}
+
 static void
 process_extended(u_int32_t id)
 {