mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-09-01 06:21:56 +03:00
Add support for OpenSSH's statvfs and fstatvfs calls.
This commit is contained in:
@@ -174,6 +174,20 @@ typedef struct sftp_attributes{
|
||||
ssh_string extended_data;
|
||||
} SFTP_ATTRIBUTES;
|
||||
|
||||
typedef struct sftp_statvfs_struct {
|
||||
uint64_t f_bsize; /* file system block size */
|
||||
uint64_t f_frsize; /* fundamental fs block size */
|
||||
uint64_t f_blocks; /* number of blocks (unit f_frsize) */
|
||||
uint64_t f_bfree; /* free blocks in file system */
|
||||
uint64_t f_bavail; /* free blocks for non-root */
|
||||
uint64_t f_files; /* total file inodes */
|
||||
uint64_t f_ffree; /* free file inodes */
|
||||
uint64_t f_favail; /* free file inodes for to non-root */
|
||||
uint64_t f_fsid; /* file system id */
|
||||
uint64_t f_flag; /* bit mask of f_flag values */
|
||||
uint64_t f_namemax; /* maximum filename length */
|
||||
} SFTP_STATVFS;
|
||||
|
||||
#define LIBSFTP_VERSION 3
|
||||
|
||||
/**
|
||||
@@ -676,6 +690,33 @@ LIBSSH_API int sftp_symlink(SFTP_SESSION *sftp, const char *target, const char *
|
||||
*/
|
||||
LIBSSH_API char *sftp_readlink(SFTP_SESSION *sftp, const char *path);
|
||||
|
||||
/**
|
||||
* @brief Get information about a mounted file system.
|
||||
*
|
||||
* @param sftp The sftp session handle.
|
||||
*
|
||||
* @param path The pathname of any file within the mounted file system.
|
||||
*
|
||||
* @return A statvfs structure or NULL on error.
|
||||
*/
|
||||
LIBSSH_API SFTP_STATVFS *sftp_statvfs(SFTP_SESSION *sftp, const char *path);
|
||||
|
||||
/**
|
||||
* @brief Get information about a mounted file system.
|
||||
*
|
||||
* @param file An opened file.
|
||||
*
|
||||
* @return A statvfs structure or NULL on error.
|
||||
*/
|
||||
LIBSSH_API SFTP_STATVFS *sftp_fstatvfs(SFTP_FILE *file);
|
||||
|
||||
/**
|
||||
* @brief Free the memory of an allocated statvfs.
|
||||
*
|
||||
* @param statvfs The statvfs to free.
|
||||
*/
|
||||
LIBSSH_API void sftp_statvfs_free(SFTP_STATVFS *statvfs);
|
||||
|
||||
/**
|
||||
* @brief Canonicalize a sftp path.
|
||||
*
|
||||
@@ -846,7 +887,9 @@ void sftp_handle_remove(SFTP_SESSION *sftp, void *handle);
|
||||
#define SFTP_READLINK SSH_FXP_READLINK
|
||||
#define SFTP_SYMLINK SSH_FXP_SYMLINK
|
||||
|
||||
|
||||
/* openssh flags */
|
||||
#define SSH_FXE_STATVFS_ST_RDONLY 0x1 /* read-only */
|
||||
#define SSH_FXE_STATVFS_ST_NOSUID 0x2 /* no setuid */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} ;
|
||||
|
249
libssh/sftp.c
249
libssh/sftp.c
@@ -396,8 +396,8 @@ static SFTP_MESSAGE *sftp_get_message(SFTP_PACKET *packet) {
|
||||
|
||||
if ((packet->type != SSH_FXP_STATUS) && (packet->type!=SSH_FXP_HANDLE) &&
|
||||
(packet->type != SSH_FXP_DATA) && (packet->type != SSH_FXP_ATTRS) &&
|
||||
(packet->type != SSH_FXP_NAME)) {
|
||||
ssh_set_error(packet->sftp->session, SSH_FATAL,
|
||||
(packet->type != SSH_FXP_NAME) && (packet->type != SSH_FXP_EXTENDED_REPLY)) {
|
||||
ssh_set_error(packet->sftp->session, SSH_FATAL,
|
||||
"Unknown packet type %d", packet->type);
|
||||
sftp_message_free(msg);
|
||||
sftp_leave_function();
|
||||
@@ -2523,6 +2523,251 @@ char *sftp_readlink(SFTP_SESSION *sftp, const char *path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static SFTP_STATVFS *sftp_parse_statvfs(SFTP_SESSION *sftp, ssh_buffer buf) {
|
||||
SFTP_STATVFS *statvfs;
|
||||
uint64_t tmp;
|
||||
int ok = 0;
|
||||
|
||||
statvfs = malloc(sizeof(SFTP_STATVFS));
|
||||
if (statvfs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ZERO_STRUCTP(statvfs);
|
||||
|
||||
/* try .. catch */
|
||||
do {
|
||||
/* file system block size */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_bsize = ntohll(tmp);
|
||||
|
||||
/* fundamental fs block size */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_frsize = ntohll(tmp);
|
||||
|
||||
/* number of blocks (unit f_frsize) */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_blocks = ntohll(tmp);
|
||||
|
||||
/* free blocks in file system */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_bfree = ntohll(tmp);
|
||||
|
||||
/* free blocks for non-root */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_bavail = ntohll(tmp);
|
||||
|
||||
/* total file inodes */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_files = ntohll(tmp);
|
||||
|
||||
/* free file inodes */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_ffree = ntohll(tmp);
|
||||
|
||||
/* free file inodes for to non-root */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_favail = ntohll(tmp);
|
||||
|
||||
/* file system id */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_fsid = ntohll(tmp);
|
||||
|
||||
/* bit mask of f_flag values */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_flag = ntohll(tmp);
|
||||
|
||||
/* maximum filename length */
|
||||
if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
|
||||
break;
|
||||
}
|
||||
statvfs->f_namemax = ntohll(tmp);
|
||||
|
||||
ok = 1;
|
||||
} while(0);
|
||||
|
||||
if (!ok) {
|
||||
SAFE_FREE(statvfs);
|
||||
ssh_set_error(sftp->session, SSH_FATAL, "Invalid statvfs structure");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return statvfs;
|
||||
}
|
||||
|
||||
SFTP_STATVFS *sftp_statvfs(SFTP_SESSION *sftp, const char *path) {
|
||||
STATUS_MESSAGE *status = NULL;
|
||||
SFTP_MESSAGE *msg = NULL;
|
||||
ssh_string pathstr;
|
||||
ssh_string statvfs;
|
||||
ssh_buffer buffer;
|
||||
uint32_t id;
|
||||
|
||||
if (sftp == NULL || path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer = buffer_new();
|
||||
if (buffer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
statvfs = string_from_char("statvfs@openssh.com");
|
||||
if (statvfs == NULL) {
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pathstr = string_from_char(path);
|
||||
if (pathstr == NULL) {
|
||||
buffer_free(buffer);
|
||||
string_free(statvfs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id = sftp_get_new_id(sftp);
|
||||
if (buffer_add_u32(buffer, id) < 0 ||
|
||||
buffer_add_ssh_string(buffer, statvfs) < 0 ||
|
||||
buffer_add_ssh_string(buffer, pathstr) < 0 ||
|
||||
sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer) < 0) {
|
||||
buffer_free(buffer);
|
||||
string_free(statvfs);
|
||||
string_free(pathstr);
|
||||
return NULL;
|
||||
}
|
||||
buffer_free(buffer);
|
||||
string_free(statvfs);
|
||||
string_free(pathstr);
|
||||
|
||||
while (msg == NULL) {
|
||||
if (sftp_read_and_dispatch(sftp) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
msg = sftp_dequeue(sftp, id);
|
||||
}
|
||||
|
||||
if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) {
|
||||
SFTP_STATVFS *statvfs = sftp_parse_statvfs(sftp, msg->payload);
|
||||
sftp_message_free(msg);
|
||||
if (statvfs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return statvfs;
|
||||
} else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
|
||||
status = parse_status_msg(msg);
|
||||
sftp_message_free(msg);
|
||||
if (status == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
|
||||
"SFTP server: %s", status->errormsg);
|
||||
status_msg_free(status);
|
||||
} else { /* this shouldn't happen */
|
||||
ssh_set_error(sftp->session, SSH_FATAL,
|
||||
"Received message %d when attempting to get statvfs", msg->packet_type);
|
||||
sftp_message_free(msg);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SFTP_STATVFS *sftp_fstatvfs(SFTP_FILE *file) {
|
||||
STATUS_MESSAGE *status = NULL;
|
||||
SFTP_MESSAGE *msg = NULL;
|
||||
SFTP_SESSION *sftp;
|
||||
ssh_string fstatvfs;
|
||||
ssh_buffer buffer;
|
||||
uint32_t id;
|
||||
|
||||
if (sftp == NULL || file == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
sftp = file->sftp;
|
||||
|
||||
buffer = buffer_new();
|
||||
if (buffer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fstatvfs = string_from_char("fstatvfs@openssh.com");
|
||||
if (fstatvfs == NULL) {
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id = sftp_get_new_id(sftp);
|
||||
if (buffer_add_u32(buffer, id) < 0 ||
|
||||
buffer_add_ssh_string(buffer, fstatvfs) < 0 ||
|
||||
buffer_add_ssh_string(buffer, file->handle) < 0 ||
|
||||
sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer) < 0) {
|
||||
buffer_free(buffer);
|
||||
string_free(fstatvfs);
|
||||
return NULL;
|
||||
}
|
||||
buffer_free(buffer);
|
||||
string_free(fstatvfs);
|
||||
|
||||
while (msg == NULL) {
|
||||
if (sftp_read_and_dispatch(sftp) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
msg = sftp_dequeue(sftp, id);
|
||||
}
|
||||
|
||||
if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) {
|
||||
SFTP_STATVFS *statvfs = sftp_parse_statvfs(sftp, msg->payload);
|
||||
sftp_message_free(msg);
|
||||
if (statvfs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return statvfs;
|
||||
} else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
|
||||
status = parse_status_msg(msg);
|
||||
sftp_message_free(msg);
|
||||
if (status == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
|
||||
"SFTP server: %s", status->errormsg);
|
||||
status_msg_free(status);
|
||||
} else { /* this shouldn't happen */
|
||||
ssh_set_error(sftp->session, SSH_FATAL,
|
||||
"Received message %d when attempting to set stats", msg->packet_type);
|
||||
sftp_message_free(msg);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void sftp_statvfs_free(SFTP_STATVFS *statvfs) {
|
||||
if (statvfs == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
SAFE_FREE(statvfs);
|
||||
}
|
||||
|
||||
/* another code written by Nick */
|
||||
char *sftp_canonicalize_path(SFTP_SESSION *sftp, const char *path) {
|
||||
STATUS_MESSAGE *status = NULL;
|
||||
|
Reference in New Issue
Block a user