support for setstat on server

Signed-off-by: Abdelrahman Youssef <abdelrahmanyossef12@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
Abdelrahman Youssef
2024-04-11 13:30:33 +02:00
committed by Sahana Prasad
parent 3809db771d
commit 21627509f5
2 changed files with 199 additions and 1 deletions

View File

@@ -31,10 +31,19 @@
#include <dirent.h>
#include <sys/statvfs.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif /* HAVE_SYS_TIME_H */
#ifdef HAVE_SYS_UTIME_H
#include <sys/utime.h>
#endif /* HAVE_SYS_UTIME_H */
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include "libssh/libssh.h"
@@ -829,6 +838,7 @@ SSH_SFTP_CALLBACK(process_readlink);
SSH_SFTP_CALLBACK(process_symlink);
SSH_SFTP_CALLBACK(process_remove);
SSH_SFTP_CALLBACK(process_extended_statvfs);
SSH_SFTP_CALLBACK(process_setstat);
const struct sftp_message_handler message_handlers[] = {
{"open", NULL, SSH_FXP_OPEN, process_open},
@@ -837,7 +847,7 @@ const struct sftp_message_handler message_handlers[] = {
{"write", NULL, SSH_FXP_WRITE, process_write},
{"lstat", NULL, SSH_FXP_LSTAT, process_lstat},
{"fstat", NULL, SSH_FXP_FSTAT, process_unsupposed},
{"setstat", NULL, SSH_FXP_SETSTAT, process_unsupposed},
{"setstat", NULL, SSH_FXP_SETSTAT, process_setstat},
{"fsetstat", NULL, SSH_FXP_FSETSTAT, process_unsupposed},
{"opendir", NULL, SSH_FXP_OPENDIR, process_opendir},
{"readdir", NULL, SSH_FXP_READDIR, process_readdir},
@@ -1437,6 +1447,103 @@ process_stat(sftp_client_message client_msg)
return ret;
}
static int
process_setstat(sftp_client_message client_msg)
{
int rv;
int ret = SSH_OK;
int status = SSH_FX_OK;
uint32_t msg_flags = client_msg->attr->flags;
const char *filename = sftp_client_message_get_filename(client_msg);
SSH_LOG(SSH_LOG_PROTOCOL, "Processing setstat %s", filename);
if (filename == NULL) {
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
return SSH_ERROR;
}
if (msg_flags & SSH_FILEXFER_ATTR_SIZE) {
rv = truncate(filename, client_msg->attr->size);
if (rv < 0) {
int saved_errno = errno;
SSH_LOG(SSH_LOG_PROTOCOL,
"changing size failed: %s",
strerror(saved_errno));
status = unix_errno_to_ssh_stat(saved_errno);
sftp_reply_status(client_msg, status, NULL);
return rv;
}
}
if (msg_flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
rv = chmod(filename, client_msg->attr->permissions);
if (rv < 0) {
int saved_errno = errno;
SSH_LOG(SSH_LOG_PROTOCOL,
"chmod failed: %s",
strerror(saved_errno));
status = unix_errno_to_ssh_stat(saved_errno);
sftp_reply_status(client_msg, status, NULL);
return rv;
}
}
if (msg_flags & SSH_FILEXFER_ATTR_UIDGID) {
rv = chown(filename, client_msg->attr->uid, client_msg->attr->gid);
if (rv < 0) {
int saved_errno = errno;
SSH_LOG(SSH_LOG_PROTOCOL,
"chwon failed: %s",
strerror(saved_errno));
status = unix_errno_to_ssh_stat(saved_errno);
sftp_reply_status(client_msg, status, NULL);
return rv;
}
}
if (msg_flags & SSH_FILEXFER_ATTR_ACMODTIME) {
#ifdef HAVE_SYS_TIME_H
struct timeval tv[2];
tv[0].tv_sec = client_msg->attr->atime;
tv[0].tv_usec = 0;
tv[1].tv_sec = client_msg->attr->mtime;
tv[1].tv_usec = 0;
rv = utimes(filename, tv);
if (rv < 0) {
int saved_errno = errno;
SSH_LOG(SSH_LOG_PROTOCOL,
"utimes failed: %s",
strerror(saved_errno));
status = unix_errno_to_ssh_stat(saved_errno);
sftp_reply_status(client_msg, status, NULL);
return rv;
}
#else
struct _utimbuf tf;
tf.actime = client_msg->attr->atime;
tf.modtime = client_msg->attr->mtime;
rv = _utime(filename, &tf);
if (rv < 0) {
int saved_errno = errno;
SSH_LOG(SSH_LOG_PROTOCOL,
"utimes failed: %s",
strerror(saved_errno));
status = unix_errno_to_ssh_stat(saved_errno);
sftp_reply_status(client_msg, status, NULL);
return rv;
}
#endif
}
sftp_reply_status(client_msg, status, NULL);
return ret;
}
static int
process_readlink(sftp_client_message client_msg)
{

View File

@@ -229,8 +229,10 @@ static int session_setup(void **state)
struct test_server_st *tss = *state;
struct torture_state *s;
int verbosity = torture_libssh_verbosity();
char template2[] = "/tmp/ssh_torture_XXXXXX";
char *cwd = NULL;
char *tmp_dir = NULL;
char *p = NULL;
bool b = false;
int rc;
@@ -243,6 +245,8 @@ static int session_setup(void **state)
assert_non_null(cwd);
tmp_dir = torture_make_temp_dir(template);
p = mkdtemp(template2);
assert_non_null(p);
assert_non_null(tmp_dir);
tss->cwd = cwd;
@@ -256,6 +260,8 @@ static int session_setup(void **state)
s->ssh.tsftp = (struct torture_sftp*)calloc(1, sizeof(struct torture_sftp));
assert_non_null(s->ssh.tsftp);
s->ssh.tsftp->testdir = strdup(p);
assert_non_null(s->ssh.tsftp->testdir);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
assert_ssh_return_code(s->ssh.session, rc);
@@ -332,6 +338,7 @@ static int session_teardown(void **state)
s = tss->state;
assert_non_null(s);
SAFE_FREE(s->ssh.tsftp->testdir);
sftp_free(s->ssh.tsftp->sftp);
SAFE_FREE(s->ssh.tsftp);
@@ -973,6 +980,87 @@ static void torture_server_sftp_extended(void **state)
assert_int_equal(rc, SSH_OK);
}
static void
torture_server_sftp_setstat(void **state)
{
char name[128] = {0};
char data[10] = "0123456789";
int rc;
size_t len;
int atime = 10676, mtime = 13467;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP;
struct passwd *pwd = NULL;
struct test_server_st *tss = *state;
struct torture_state *s = NULL;
struct torture_sftp *tsftp = NULL;
struct sftp_attributes_struct attr;
sftp_attributes tmp_attr = NULL;
sftp_session sftp = NULL;
ssh_session session = NULL;
sftp_file new_file = NULL;
pwd = getpwnam("alice");
assert_non_null(pwd);
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
session = s->ssh.session;
assert_non_null(session);
tsftp = s->ssh.tsftp;
assert_non_null(tsftp);
sftp = tsftp->sftp;
assert_non_null(sftp);
assert_non_null(tsftp->testdir);
snprintf(name, sizeof(name), "%s/server_setstat_test", tsftp->testdir);
new_file = sftp_open(sftp, name, O_WRONLY | O_CREAT, 0700);
assert_non_null(new_file);
len = sftp_write(new_file, data, sizeof(data));
assert_int_equal(len, sizeof(data));
rc = sftp_close(new_file);
assert_int_equal(rc, SSH_OK);
ZERO_STRUCT(attr);
attr.flags = SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS |
SSH_FILEXFER_ATTR_UIDGID | SSH_FILEXFER_ATTR_ACMODTIME;
attr.size = len;
attr.uid = pwd->pw_uid;
attr.gid = pwd->pw_gid;
attr.permissions = mode;
attr.atime = atime;
attr.mtime = mtime;
rc = sftp_setstat(sftp, name, &attr);
assert_int_equal(rc, SSH_OK);
assert_int_equal(rc, SSH_OK);
tmp_attr = sftp_stat(sftp, name);
assert_non_null(tmp_attr);
assert_int_equal(tmp_attr->uid, pwd->pw_uid);
assert_int_equal(tmp_attr->gid, pwd->pw_gid);
assert_int_equal(len, tmp_attr->size);
assert_int_equal(tmp_attr->permissions & ACCESSPERMS, mode);
assert_int_equal(tmp_attr->mtime, mtime);
assert_int_equal(tmp_attr->atime, atime);
/*negative tests*/
rc = sftp_setstat(sftp, "not existing", &attr);
assert_int_equal(rc, SSH_ERROR);
sftp_unlink(sftp, name);
sftp_attributes_free(tmp_attr);
}
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
@@ -997,6 +1085,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_server_sftp_extended,
session_setup_sftp,
session_teardown),
cmocka_unit_test_setup_teardown(torture_server_sftp_setstat,
session_setup_sftp,
session_teardown),
};
ssh_init();