mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-09 18:04:25 +09:00
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:
committed by
Sahana Prasad
parent
3809db771d
commit
21627509f5
109
src/sftpserver.c
109
src/sftpserver.c
@@ -31,10 +31,19 @@
|
|||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#endif
|
#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 <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "libssh/libssh.h"
|
#include "libssh/libssh.h"
|
||||||
@@ -829,6 +838,7 @@ SSH_SFTP_CALLBACK(process_readlink);
|
|||||||
SSH_SFTP_CALLBACK(process_symlink);
|
SSH_SFTP_CALLBACK(process_symlink);
|
||||||
SSH_SFTP_CALLBACK(process_remove);
|
SSH_SFTP_CALLBACK(process_remove);
|
||||||
SSH_SFTP_CALLBACK(process_extended_statvfs);
|
SSH_SFTP_CALLBACK(process_extended_statvfs);
|
||||||
|
SSH_SFTP_CALLBACK(process_setstat);
|
||||||
|
|
||||||
const struct sftp_message_handler message_handlers[] = {
|
const struct sftp_message_handler message_handlers[] = {
|
||||||
{"open", NULL, SSH_FXP_OPEN, process_open},
|
{"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},
|
{"write", NULL, SSH_FXP_WRITE, process_write},
|
||||||
{"lstat", NULL, SSH_FXP_LSTAT, process_lstat},
|
{"lstat", NULL, SSH_FXP_LSTAT, process_lstat},
|
||||||
{"fstat", NULL, SSH_FXP_FSTAT, process_unsupposed},
|
{"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},
|
{"fsetstat", NULL, SSH_FXP_FSETSTAT, process_unsupposed},
|
||||||
{"opendir", NULL, SSH_FXP_OPENDIR, process_opendir},
|
{"opendir", NULL, SSH_FXP_OPENDIR, process_opendir},
|
||||||
{"readdir", NULL, SSH_FXP_READDIR, process_readdir},
|
{"readdir", NULL, SSH_FXP_READDIR, process_readdir},
|
||||||
@@ -1437,6 +1447,103 @@ process_stat(sftp_client_message client_msg)
|
|||||||
return ret;
|
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
|
static int
|
||||||
process_readlink(sftp_client_message client_msg)
|
process_readlink(sftp_client_message client_msg)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -229,8 +229,10 @@ static int session_setup(void **state)
|
|||||||
struct test_server_st *tss = *state;
|
struct test_server_st *tss = *state;
|
||||||
struct torture_state *s;
|
struct torture_state *s;
|
||||||
int verbosity = torture_libssh_verbosity();
|
int verbosity = torture_libssh_verbosity();
|
||||||
|
char template2[] = "/tmp/ssh_torture_XXXXXX";
|
||||||
char *cwd = NULL;
|
char *cwd = NULL;
|
||||||
char *tmp_dir = NULL;
|
char *tmp_dir = NULL;
|
||||||
|
char *p = NULL;
|
||||||
bool b = false;
|
bool b = false;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@@ -243,6 +245,8 @@ static int session_setup(void **state)
|
|||||||
assert_non_null(cwd);
|
assert_non_null(cwd);
|
||||||
|
|
||||||
tmp_dir = torture_make_temp_dir(template);
|
tmp_dir = torture_make_temp_dir(template);
|
||||||
|
p = mkdtemp(template2);
|
||||||
|
assert_non_null(p);
|
||||||
assert_non_null(tmp_dir);
|
assert_non_null(tmp_dir);
|
||||||
|
|
||||||
tss->cwd = cwd;
|
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));
|
s->ssh.tsftp = (struct torture_sftp*)calloc(1, sizeof(struct torture_sftp));
|
||||||
assert_non_null(s->ssh.tsftp);
|
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);
|
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||||
assert_ssh_return_code(s->ssh.session, rc);
|
assert_ssh_return_code(s->ssh.session, rc);
|
||||||
@@ -332,6 +338,7 @@ static int session_teardown(void **state)
|
|||||||
s = tss->state;
|
s = tss->state;
|
||||||
assert_non_null(s);
|
assert_non_null(s);
|
||||||
|
|
||||||
|
SAFE_FREE(s->ssh.tsftp->testdir);
|
||||||
sftp_free(s->ssh.tsftp->sftp);
|
sftp_free(s->ssh.tsftp->sftp);
|
||||||
SAFE_FREE(s->ssh.tsftp);
|
SAFE_FREE(s->ssh.tsftp);
|
||||||
|
|
||||||
@@ -973,6 +980,87 @@ static void torture_server_sftp_extended(void **state)
|
|||||||
assert_int_equal(rc, SSH_OK);
|
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 torture_run_tests(void) {
|
||||||
int rc;
|
int rc;
|
||||||
struct CMUnitTest tests[] = {
|
struct CMUnitTest tests[] = {
|
||||||
@@ -997,6 +1085,9 @@ int torture_run_tests(void) {
|
|||||||
cmocka_unit_test_setup_teardown(torture_server_sftp_extended,
|
cmocka_unit_test_setup_teardown(torture_server_sftp_extended,
|
||||||
session_setup_sftp,
|
session_setup_sftp,
|
||||||
session_teardown),
|
session_teardown),
|
||||||
|
cmocka_unit_test_setup_teardown(torture_server_sftp_setstat,
|
||||||
|
session_setup_sftp,
|
||||||
|
session_teardown),
|
||||||
};
|
};
|
||||||
|
|
||||||
ssh_init();
|
ssh_init();
|
||||||
|
|||||||
Reference in New Issue
Block a user