uint32_t sftp_v6_link(struct sftpjob *job) { char *oldpath, *newlinkpath; uint8_t symbolic; struct stat sb; /* See also comment in v3.c for SSH_FXP_SYMLINK */ if(readonly) return SSH_FX_PERMISSION_DENIED; pcheck(sftp_parse_path(job, &newlinkpath)); pcheck(sftp_parse_path(job, &oldpath)); /* aka existing-path/target-paths */ pcheck(sftp_parse_uint8(job, &symbolic)); D(("sftp_link %s %s [%s]", oldpath, newlinkpath, symbolic ? "symbolic" : "hard")); if((symbolic ? symlink : link)(oldpath, newlinkpath) < 0) { switch(errno) { case EPERM: if(!symbolic && stat(oldpath, &sb) >= 0 && S_ISDIR(sb.st_mode)) /* Can't hard-link directories */ return SSH_FX_FILE_IS_A_DIRECTORY; else /* e.g. Linux returns EPERM for symlink or link on a FAT32 fs */ return SSH_FX_OP_UNSUPPORTED; break; default: return HANDLER_ERRNO; } } else return SSH_FX_OK; }
void queue_serializable_job(struct sftpjob *job) { uint8_t type; uint32_t id; uint64_t offset, len64; uint32_t len; struct handleid hid; unsigned handleflags; struct sqnode *q; job->ptr = job->data; job->left = job->len; if(!sftp_parse_uint8(job, &type) && (type == SSH_FXP_READ || type == SSH_FXP_WRITE) && sftp_parse_uint32(job, &id) == SSH_FX_OK && sftp_parse_handle(job, &hid) == SSH_FX_OK && sftp_parse_uint64(job, &offset) == SSH_FX_OK && sftp_parse_uint32(job, &len) == SSH_FX_OK) { /* This is a well-formed read or write operation */ len64 = len; handleflags = sftp_handle_flags(&hid); } else { /* Anything else has dummy values */ memset(&hid, 0, sizeof hid); offset = 0; len64 = ~(uint64_t)0; handleflags = 0; } ferrcheck(pthread_mutex_lock(&sq_mutex)); q = xmalloc(sizeof *q); q->older = newest; q->job = job; q->type = type; q->hid = hid; q->handleflags = handleflags; q->offset = offset; q->len = len64; newest = q; ferrcheck(pthread_mutex_unlock(&sq_mutex)); }
uint32_t sftp_v456_parseattrs(struct sftpjob *job, struct sftpattr *attrs) { uint32_t n, rc; memset(attrs, 0, sizeof *attrs); if((rc = sftp_parse_uint32(job, &attrs->valid)) != SSH_FX_OK) return rc; if((attrs->valid & protocol->attrmask) != attrs->valid) { D(("received attrs %#x but protocol %d only supports %#x", attrs->valid, protocol->version, protocol->attrmask)); attrs->valid = 0; return SSH_FX_BAD_MESSAGE; } if((rc = sftp_parse_uint8(job, &attrs->type)) != SSH_FX_OK) return rc; if(attrs->valid & SSH_FILEXFER_ATTR_SIZE) if((rc = sftp_parse_uint64(job, &attrs->size)) != SSH_FX_OK) return rc; if(attrs->valid & SSH_FILEXFER_ATTR_OWNERGROUP) { if((rc = sftp_parse_path(job, &attrs->owner)) != SSH_FX_OK) return rc; if((rc = sftp_parse_path(job, &attrs->group)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_PERMISSIONS) { if((rc = sftp_parse_uint32(job, &attrs->permissions)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_ACCESSTIME) { if((rc = sftp_parse_uint64(job, (uint64_t *)&attrs->atime.seconds)) != SSH_FX_OK) return rc; if(attrs->valid & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) if((rc = sftp_parse_uint32(job, &attrs->atime.nanoseconds)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_CREATETIME) { if((rc = sftp_parse_uint64(job, (uint64_t *)&attrs->createtime.seconds)) != SSH_FX_OK) return rc; if(attrs->valid & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) if((rc = sftp_parse_uint32(job, &attrs->createtime.nanoseconds)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_MODIFYTIME) { if((rc = sftp_parse_uint64(job, (uint64_t *)&attrs->mtime.seconds)) != SSH_FX_OK) return rc; if(attrs->valid & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) if((rc = sftp_parse_uint32(job, &attrs->mtime.nanoseconds)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_CTIME) { if((rc = sftp_parse_uint64(job, (uint64_t *)&attrs->ctime.seconds)) != SSH_FX_OK) return rc; if(attrs->valid & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) if((rc = sftp_parse_uint32(job, &attrs->ctime.nanoseconds)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_ACL) { if((rc = sftp_parse_string(job, &attrs->acl, 0)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_BITS) { if((rc = sftp_parse_uint32(job, &attrs->attrib_bits)) != SSH_FX_OK) return rc; if(protocol->version >= 6) { if((rc = sftp_parse_uint32(job, &attrs->attrib_bits_valid)) != SSH_FX_OK) return rc; } else attrs->attrib_bits_valid = 0x7ff; /* -05 s5.8 */ } if(attrs->valid & SSH_FILEXFER_ATTR_TEXT_HINT) { if((rc = sftp_parse_uint8(job, &attrs->text_hint)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_MIME_TYPE) { if((rc = sftp_parse_string(job, &attrs->mime_type, 0)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_LINK_COUNT) { if((rc = sftp_parse_uint32(job, &attrs->link_count)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_UNTRANSLATED_NAME) { if((rc = sftp_parse_string(job, &attrs->mime_type, 0)) != SSH_FX_OK) return rc; } if(attrs->valid & SSH_FILEXFER_ATTR_EXTENDED) { if((rc = sftp_parse_uint32(job, &n)) != SSH_FX_OK) return rc; while(n-- > 0) { if((rc = sftp_parse_string(job, 0, 0)) != SSH_FX_OK) return rc; if((rc = sftp_parse_string(job, 0, 0)) != SSH_FX_OK) return rc; } } return SSH_FX_OK; }
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; }