diff --git a/src/sftp/sftp_attr.c b/src/sftp/sftp_attr.c new file mode 100644 index 0000000000000000000000000000000000000000..ee3da031e0e10fb6fa0bb4023ae2eb4065873b2c --- /dev/null +++ b/src/sftp/sftp_attr.c @@ -0,0 +1,283 @@ +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <time.h> +#include "sftp.h" + +struct sftp_file_attributes { + uint32_t flags; + uint64_t size; + uint32_t uid; + uint32_t gid; + uint32_t perm; + uint32_t atime; + uint32_t mtime; + uint32_t ext_count; + struct sftp_extended_file_attribute ext[]; +}; + +sftp_file_attr_t +sftp_fattr_alloc(void) +{ + sftp_file_attr_t ret = calloc(1, sizeof(struct sftp_file_attributes)); + return ret; +} + +void +sftp_fattr_free(sftp_file_attr_t fattr) +{ + uint32_t i; + + for (i = 0; i < fattr->ext_count; i++) { + free_sftp_str(fattr->ext[i].type); + free_sftp_str(fattr->ext[i].data); + } + free(fattr); +} + +void +sftp_fattr_set_size(sftp_file_attr_t fattr, uint64_t sz) +{ + assert(fattr); + fattr->size = sz; + fattr->flags |= SSH_FILEXFER_ATTR_SIZE; +} + +bool +sftp_fattr_get_size(sftp_file_attr_t fattr, uint64_t *sz) +{ + assert(fattr); + if (fattr->flags & SSH_FILEXFER_ATTR_SIZE) { + if (sz) + *sz = fattr->size; + return true; + } + return false; +} + +void +sftp_fattr_set_uid_gid(sftp_file_attr_t fattr, uint32_t uid, uint32_t gid) +{ + assert(fattr); + fattr->uid = uid; + fattr->gid = gid; + fattr->flags |= SSH_FILEXFER_ATTR_SIZE; +} + +bool +sftp_fattr_get_uid(sftp_file_attr_t fattr, uint32_t *uid) +{ + assert(fattr); + if (fattr->flags & SSH_FILEXFER_ATTR_UIDGID) { + if (uid) + *uid = fattr->uid; + return true; + } + return false; +} + +bool +sftp_fattr_get_gid(sftp_file_attr_t fattr, uint32_t *gid) +{ + assert(fattr); + if (fattr->flags & SSH_FILEXFER_ATTR_UIDGID) { + if (gid) + *gid = fattr->gid; + return true; + } + return false; +} + +void +sftp_fattr_set_permissions(sftp_file_attr_t fattr, uint64_t perm) +{ + assert(fattr); + fattr->perm = perm; + fattr->flags |= SSH_FILEXFER_ATTR_PERMISSIONS; +} + +bool +sftp_fattr_get_permissions(sftp_file_attr_t fattr, uint64_t *perm) +{ + assert(fattr); + if (fattr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) { + if (perm) + *perm = fattr->perm; + return true; + } + return false; +} + +void +sftp_fattr_set_times(sftp_file_attr_t fattr, uint32_t atime, uint32_t mtime) +{ + assert(fattr); + fattr->atime = atime; + fattr->mtime = mtime; + fattr->flags |= SSH_FILEXFER_ATTR_ACMODTIME; +} + +bool +sftp_fattr_get_atime(sftp_file_attr_t fattr, uint32_t *atime) +{ + assert(fattr); + if (fattr->flags & SSH_FILEXFER_ATTR_ACMODTIME) { + if (atime) + *atime = fattr->atime; + return true; + } + return false; +} + +bool +sftp_fattr_get_mtime(sftp_file_attr_t fattr, uint32_t *mtime) +{ + assert(fattr); + if (fattr->flags & SSH_FILEXFER_ATTR_ACMODTIME) { + if (mtime) + *mtime = fattr->mtime; + return true; + } + return false; +} + +static bool +grow_ext(sftp_file_attr_t *fattr) +{ + size_t newsz = offsetof(struct sftp_file_attributes, ext); + newsz += sizeof((*fattr)->ext[0]) * ((*fattr)->ext_count + 1); + sftp_file_attr_t new_attr = realloc(*fattr, newsz); + if (new_attr == NULL) + return false; + *fattr = new_attr; + return true; +} + +bool +sftp_fattr_add_ext(sftp_file_attr_t *fattr, sftp_str_t type, sftp_str_t data) +{ + assert(fattr); + assert(*fattr); + if (fattr == NULL) + return false; + if (*fattr == NULL) + return false; + assert(type != NULL); + assert(data != NULL); + if (type == NULL || data == NULL) + return false; + sftp_str_t newtype = sftp_memdup(type->c_str, type->len); + if (newtype == NULL) + return false; + sftp_str_t newdata = sftp_memdup(data->c_str, data->len); + if (newdata == NULL) { + free_sftp_str(newtype); + return false; + } + if (!grow_ext(fattr)) { + free_sftp_str(newtype); + free_sftp_str(newdata); + return false; + } + (*fattr)->ext[(*fattr)->ext_count].type = newtype; + (*fattr)->ext[(*fattr)->ext_count].data = newdata; + (*fattr)->ext_count += 1; + (*fattr)->flags |= SSH_FILEXFER_ATTR_EXTENDED; + return true; +} + +sftp_str_t +sftp_fattr_get_ext_type(sftp_file_attr_t fattr, uint32_t index) +{ + assert(fattr); + if (index >= fattr->ext_count) + return NULL; + return fattr->ext[index].type; +} + +sftp_str_t +sftp_fattr_get_ext_data(sftp_file_attr_t fattr, uint32_t index) +{ + assert(fattr); + if (index >= fattr->ext_count) + return NULL; + return fattr->ext[index].data; +} + +uint32_t +sftp_fattr_get_ext_count(sftp_file_attr_t fattr) +{ + assert(fattr); + return fattr->ext_count; +} + +static uint32_t +get_size(sftp_file_attr_t fattr) +{ + assert(fattr); + if (fattr == NULL) + return 0; + uint32_t ret = sizeof(uint32_t); + if (fattr->flags & SSH_FILEXFER_ATTR_SIZE) + ret += sizeof(uint64_t); + if (fattr->flags & SSH_FILEXFER_ATTR_UIDGID) + ret += sizeof(uint32_t) * 2; + if (fattr->flags & SSH_FILEXFER_ATTR_EXTENDED) { + ret += sizeof(uint32_t); + for (uint32_t i = 0; i < fattr->ext_count; i++) { + ret += sizeof(uint32_t); + ret += fattr->ext[i].type->len; + ret += sizeof(uint32_t); + ret += fattr->ext[i].data->len; + } + } + + return ret; +} + +bool +sftp_appendfattr(sftp_tx_pkt_t *pktp, sftp_file_attr_t fattr) +{ + uint32_t sz = get_size(fattr); + uint32_t oldused = (*pktp)->used; + if (sz == 0) + return false; + if (!sftp_append32(pktp, fattr->flags)) + return false; + if (fattr->flags & SSH_FILEXFER_ATTR_SIZE) { + if (!sftp_append64(pktp, fattr->size)) + goto fail; + } + if (fattr->flags & SSH_FILEXFER_ATTR_UIDGID) { + if (!sftp_append32(pktp, fattr->uid)) + goto fail; + if (!sftp_append32(pktp, fattr->gid)) + goto fail; + } + if (fattr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) { + if (!sftp_append32(pktp, fattr->perm)) + goto fail; + } + if (fattr->flags & SSH_FILEXFER_ATTR_ACMODTIME) { + if (!sftp_append32(pktp, fattr->atime)) + goto fail; + if (!sftp_append32(pktp, fattr->mtime)) + goto fail; + } + if (fattr->flags & SSH_FILEXFER_ATTR_EXTENDED) { + if (!sftp_append32(pktp, fattr->ext_count)) + goto fail; + for (uint32_t i = 0; i < fattr->ext_count; i++) { + if (!sftp_appendstring(pktp, fattr->ext[i].type)) + goto fail; + if (!sftp_appendstring(pktp, fattr->ext[i].data)) + goto fail; + } + } + + return true; +fail: + (*pktp)->used = oldused; + return false; +}