mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 12:20:42 +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 <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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user