diff options
-rw-r--r-- | include/libssh/sftp.h | 45 | ||||
-rw-r--r-- | libssh/sftp.c | 249 |
2 files changed, 291 insertions, 3 deletions
diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h index bcdf9d8..347661d 100644 --- a/include/libssh/sftp.h +++ b/include/libssh/sftp.h @@ -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 /** @@ -677,6 +691,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. * * @param sftp The sftp session handle. @@ -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 } ; diff --git a/libssh/sftp.c b/libssh/sftp.c index d8c4690..2595dba 100644 --- a/libssh/sftp.c +++ b/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; |