diff options
-rw-r--r-- | sftp-server.c | 67 |
1 files changed, 66 insertions, 1 deletions
diff --git a/sftp-server.c b/sftp-server.c index de9ad3d3b..19a132bd9 100644 --- a/sftp-server.c +++ b/sftp-server.c @@ -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) { @@ -1370,6 +1386,55 @@ process_extended_fsync(u_int32_t id) } 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) { char *request; |