void sftp_v456_sendattrs(struct sftpjob *job, const struct sftpattr *attrs) { const uint32_t valid = attrs->valid & protocol->attrmask; sftp_send_uint32(job->worker, valid); sftp_send_uint8(job->worker, attrs->type); if(valid & SSH_FILEXFER_ATTR_SIZE) sftp_send_uint64(job->worker, attrs->size); if(valid & SSH_FILEXFER_ATTR_OWNERGROUP) { sftp_send_path(job, job->worker, attrs->owner); sftp_send_path(job, job->worker, attrs->group); } if(valid & SSH_FILEXFER_ATTR_PERMISSIONS) sftp_send_uint32(job->worker, attrs->permissions); /* lftp 3.1.3 expects subsecond time fields even if the corresonding time is * absent. It sends no identifying information so we cannot enable a * workaround for this bizarre bug. lftp 3.5.9 gets this right as does * WinSCP. */ if(valid & SSH_FILEXFER_ATTR_ACCESSTIME) { sftp_send_uint64(job->worker, attrs->atime.seconds); if(valid & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) sftp_send_uint32(job->worker, attrs->atime.nanoseconds); } if(valid & SSH_FILEXFER_ATTR_CREATETIME) { sftp_send_uint64(job->worker, attrs->createtime.seconds); if(valid & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) sftp_send_uint32(job->worker, attrs->createtime.nanoseconds); } if(valid & SSH_FILEXFER_ATTR_MODIFYTIME) { sftp_send_uint64(job->worker, attrs->mtime.seconds); if(valid & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) sftp_send_uint32(job->worker, attrs->mtime.nanoseconds); } if(valid & SSH_FILEXFER_ATTR_CTIME) { sftp_send_uint64(job->worker, attrs->ctime.seconds); if(valid & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) sftp_send_uint32(job->worker, attrs->ctime.nanoseconds); } if(valid & SSH_FILEXFER_ATTR_ACL) { sftp_send_string(job->worker, attrs->acl); } if(valid & SSH_FILEXFER_ATTR_BITS) { sftp_send_uint32(job->worker, attrs->attrib_bits); if(protocol->version >= 6) sftp_send_uint32(job->worker, attrs->attrib_bits_valid); } if(valid & SSH_FILEXFER_ATTR_TEXT_HINT) { sftp_send_uint8(job->worker, attrs->text_hint); } if(valid & SSH_FILEXFER_ATTR_MIME_TYPE) { sftp_send_string(job->worker, attrs->mime_type); } if(valid & SSH_FILEXFER_ATTR_LINK_COUNT) { sftp_send_uint32(job->worker, attrs->link_count); } /* We don't implement untranslated-name yet */ }
/* Send a filename list as found in an SSH_FXP_NAME response. The response * header and so on must be generated by the caller. */ void sftp_v456_sendnames(struct sftpjob *job, int nnames, const struct sftpattr *names) { /* We'd like to know what year we're in for dates in longname */ sftp_send_uint32(job->worker, nnames); while(nnames > 0) { sftp_send_path(job, job->worker, names->name); protocol->sendattrs(job, names); ++names; --nnames; } }
/* Command code for the various _*STAT calls. rc is the return value * from *stat() and SB is the buffer. */ static uint32_t sftp_v456_stat_core(struct sftpjob *job, int rc, const struct stat *sb, const char *path) { struct sftpattr attrs; uint32_t flags; if(!rc) { pcheck(sftp_parse_uint32(job, &flags)); sftp_stat_to_attrs(job->a, sb, &attrs, flags, path); sftp_send_begin(job->worker); sftp_send_uint8(job->worker, SSH_FXP_ATTRS); sftp_send_uint32(job->worker, job->id); protocol->sendattrs(job, &attrs); sftp_send_end(job->worker); return HANDLER_RESPONDED; } else return HANDLER_ERRNO; }
void sftp_send_begin(struct worker *w) { w->bufused = 0; sftp_send_uint32(w, 0); /* placeholder for length */ }
uint32_t sftp_v6_realpath(struct sftpjob *job) { char *path, *compose, *resolvedpath; uint8_t control_byte = SSH_FXP_REALPATH_NO_CHECK; unsigned rpflags = 0; struct stat sb; struct sftpattr attrs; pcheck(sftp_parse_path(job, &path)); if(job->left) { pcheck(sftp_parse_uint8(job, &control_byte)); while(job->left) { pcheck(sftp_parse_path(job, &compose)); if(compose[0] == '/') path = compose; else { char *newpath = sftp_alloc(job->a, strlen(path) + strlen(compose) + 2); strcpy(newpath, path); strcat(newpath, "/"); strcat(newpath, compose); path = newpath; } } } D(("sftp_v6_realpath %s %#x", path, control_byte)); switch(control_byte) { case SSH_FXP_REALPATH_NO_CHECK: /* Don't follow links and don't fail if the path doesn't exist */ rpflags = 0; break; case SSH_FXP_REALPATH_STAT_IF: /* Follow links but don't fail if the path doesn't exist */ rpflags = RP_READLINK; break; case SSH_FXP_REALPATH_STAT_ALWAYS: /* Follow links and fail if the path doesn't exist */ rpflags = RP_READLINK|RP_MUST_EXIST; break; default: return SSH_FX_BAD_MESSAGE; } if(!(resolvedpath = sftp_find_realpath(job->a, path, rpflags))) return HANDLER_ERRNO; D(("...real path is %s", resolvedpath)); switch(control_byte) { case SSH_FXP_REALPATH_NO_CHECK: /* Don't stat, send dummy attributes */ memset(&attrs, 0, sizeof attrs); attrs.name = resolvedpath; break; case SSH_FXP_REALPATH_STAT_IF: /* stat as hard as we can but accept failure if it's just not there */ if(stat(resolvedpath, &sb) >= 0 || lstat(resolvedpath, &sb) >= 0) sftp_stat_to_attrs(job->a, &sb, &attrs, 0xFFFFFFFF, resolvedpath); else { memset(&attrs, 0, sizeof attrs); attrs.name = resolvedpath; } break; case SSH_FXP_REALPATH_STAT_ALWAYS: /* stat and error on failure */ if(stat(resolvedpath, &sb) >= 0 || lstat(resolvedpath, &sb) >= 0) sftp_stat_to_attrs(job->a, &sb, &attrs, 0xFFFFFFFF, resolvedpath); else /* Can only happen if path is deleted between realpath call and stat */ return HANDLER_ERRNO; break; } sftp_send_begin(job->worker); sftp_send_uint8(job->worker, SSH_FXP_NAME); sftp_send_uint32(job->worker, job->id); protocol->sendnames(job, 1, &attrs); sftp_send_end(job->worker); return HANDLER_RESPONDED; }