1
0
mirror of https://git.libssh.org/projects/libssh.git synced 2025-08-08 19:02:06 +03:00

sftp_aio.dox: Change the sftp aio tutorial to incorporate capping

A section has been added to explain the capping applied by the
sftp aio API. Also the example codes have been changed such that
they expect sftp_aio_begin_*() functions to return an ssize_t
indicating the number of bytes it requested the server to
read/write.

Signed-off-by: Eshan Kelkar <eshankelkar@galorithm.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
Eshan Kelkar
2023-12-30 14:02:11 +05:30
committed by Jakub Jelen
parent 9857a5ef59
commit d7f7c952f2

View File

@@ -73,18 +73,20 @@ on an sftp file using the sftp aio API.
@code @code
ssize_t read_chunk(sftp_file file, void *buf, size_t to_read) ssize_t read_chunk(sftp_file file, void *buf, size_t to_read)
{ {
ssize_t bytes_read; ssize_t bytes_requested, bytes_read;
int rc;
// Variable to store an sftp aio handle // Variable to store an sftp aio handle
sftp_aio aio = NULL; sftp_aio aio = NULL;
// Send a read request to the sftp server // Send a read request to the sftp server
rc = sftp_aio_begin_read(file, to_read, &aio); bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
if (rc == SSH_ERROR) { if (bytes_requested == SSH_ERROR) {
// handle error // handle error
} }
// Here its possible that (bytes_requested < to_read) as specified in
// the function documentation of sftp_aio_begin_read()
// Wait for the response of the read request corresponding to the // Wait for the response of the read request corresponding to the
// sftp aio handle stored in the aio variable. // sftp aio handle stored in the aio variable.
bytes_read = sftp_aio_wait_read(&aio, buf, to_read); bytes_read = sftp_aio_wait_read(&aio, buf, to_read);
@@ -113,18 +115,20 @@ sftp file using the sftp aio API.
@code @code
ssize_t write_chunk(sftp_file file, void *buf, size_t to_write) ssize_t write_chunk(sftp_file file, void *buf, size_t to_write)
{ {
ssize_t bytes_written; ssize_t bytes_requested, bytes_written;
int rc;
// Variable to store an sftp aio handle // Variable to store an sftp aio handle
sftp_aio aio = NULL; sftp_aio aio = NULL;
// Send a write request to the sftp server // Send a write request to the sftp server
rc = sftp_aio_begin_write(file, buf, to_write, &aio); bytes_requested = sftp_aio_begin_write(file, buf, to_write, &aio);
if (rc == SSH_ERROR) { if (bytes_requested == SSH_ERROR) {
// handle error // handle error
} }
// Here its possible that (bytes_requested < to_write) as specified in
// the function documentation of sftp_aio_begin_write()
// Wait for the response of the write request corresponding to // Wait for the response of the write request corresponding to
// the sftp aio handle stored in the aio variable. // the sftp aio handle stored in the aio variable.
bytes_written = sftp_aio_wait_write(&aio); bytes_written = sftp_aio_wait_write(&aio);
@@ -218,10 +222,60 @@ can be found at https://gitlab.com/libssh/libssh-mirror/-/tree/master.
- libssh sftp ft API code for performing a remote to local transfer - libssh sftp ft API code for performing a remote to local transfer
(download). [See src/sftp_ft.c] (download). [See src/sftp_ft.c]
@subsection sftp_aio_cap Capping applied by the sftp aio API
Before the code examples for uploads and downloads, its important
to know about the capping applied by the sftp aio API.
sftp_aio_begin_read() caps the number of bytes the caller can request
to read from the remote file. That cap is the value of the max_read_length
field of the sftp_limits_t returned by sftp_limits(). Say that cap is LIM
and the caller passes x as the number of bytes to read to
sftp_aio_begin_read(), then (assuming no error occurs) :
- if x <= LIM, then sftp_aio_begin_read() will request the server
to read x bytes from the remote file, and will return x.
- if x > LIM, then sftp_aio_begin_read() will request the server
to read LIM bytes from the remote file and will return LIM.
Hence to request server to read x bytes (> LIM), the caller would have
to call sftp_aio_begin_read() multiple times, typically in a loop and
break out of the loop when the summation of return values of the multiple
sftp_aio_begin_read() calls becomes equal to x.
For the sake of simplicity, the code example for download in the upcoming
section would always ask sftp_aio_begin_read() to read x <= LIM bytes,
so that its return value is guaranteed to be x, unless an error occurs.
Similarly, sftp_aio_begin_write() caps the number of bytes the caller
can request to write to the remote file. That cap is the value of
max_write_length field of the sftp_limits_t returned by sftp_limits().
Say that cap is LIM and the caller passes x as the number of bytes to
write to sftp_aio_begin_write(), then (assuming no error occurs) :
- if x <= LIM, then sftp_aio_begin_write() will request the server
to write x bytes to the remote file, and will return x.
- if x > LIM, then sftp_aio_begin_write() will request the server
to write LIM bytes to the remote file and will return LIM.
Hence to request server to write x bytes (> LIM), the caller would have
to call sftp_aio_begin_write() multiple times, typically in a loop and
break out of the loop when the summation of return values of the multiple
sftp_aio_begin_write() calls becomes equal to x.
For the sake of simplicity, the code example for upload in the upcoming
section would always ask sftp_aio_begin_write() to write x <= LIM bytes,
so that its return value is guaranteed to be x, unless an error occurs.
@subsection sftp_aio_download_example Performing a download using the sftp aio API @subsection sftp_aio_download_example Performing a download using the sftp aio API
Terminologies used in the following code snippets : Terminologies used in the following code snippets :
- sftp : The sftp_session opened using sftp_new() and initialised using
sftp_init()
- file : The sftp file handle of the remote file to download data - file : The sftp file handle of the remote file to download data
from. (See sftp_open()) from. (See sftp_open())
@@ -238,14 +292,18 @@ requested don't exceed the size of the file to download.
@code @code
sftp_aio aio = NULL; sftp_aio aio = NULL;
// Using a chunk size of 16 KB // Chunk size to use for the transfer
size_t chunk_size = 16 * 1024; size_t chunk_size;
// For the limits structure that would be used
// by the code to set the chunk size
sftp_limits_t lim = NULL;
// Max number of requests to keep outstanding at a time // Max number of requests to keep outstanding at a time
size_t in_flight_requests = 5; size_t in_flight_requests = 5;
// Number of bytes for which requests have been sent // Number of bytes for which requests have been sent
size_t bytes_requested = 0; size_t total_bytes_requested = 0;
// Number of bytes which have been downloaded // Number of bytes which have been downloaded
size_t bytes_downloaded = 0; size_t bytes_downloaded = 0;
@@ -253,6 +311,25 @@ size_t bytes_downloaded = 0;
// Buffer to use for the download // Buffer to use for the download
char *buffer = NULL; char *buffer = NULL;
// Helper variables
size_t to_read;
ssize_t bytes_requested;
// Get the sftp limits
lim = sftp_limits(sftp);
if (lim == NULL) {
// handle error
}
// Set the chunk size for download = the max limit for reading
// The reason for this has been given in the "Capping applied by
// the sftp aio API" section (Its to make the code simpler)
//
// Assigning a size_t type variable a uint64_t type value here,
// theoretically could cause an overflow, but practically
// max_read_length would never exceed SIZE_MAX so its okay.
chunk_size = lim->max_read_length;
buffer = malloc(chunk_size); buffer = malloc(chunk_size);
if (buffer == NULL) { if (buffer == NULL) {
// handle error // handle error
@@ -265,20 +342,26 @@ if (buffer == NULL) {
// handles. // handles.
for (i = 0; for (i = 0;
i < in_flight_requests && bytes_requested < file_size; i < in_flight_requests && total_bytes_requested < file_size;
++i) { ++i) {
to_read = file_size - bytes_requested; to_read = file_size - total_bytes_requested;
if (to_read > chunk_size) { if (to_read > chunk_size) {
to_read = chunk_size; to_read = chunk_size;
} }
// Issue a read request // Issue a read request
rc = sftp_aio_begin_read(file, to_read, &aio); bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
if (rc == SSH_ERROR) { if (bytes_requested == SSH_ERROR) {
// handle error // handle error
} }
bytes_requested += to_read; if ((size_t)bytes_requested < to_read) {
// Should not happen for this code, as the to_read is <=
// max limit for reading (chunk size), so there is no reason
// for sftp_aio_begin_read() to return a lesser value.
}
total_bytes_requested += (size_t)bytes_requested;
// Pseudo code // Pseudo code
ENQUEUE aio in the queue; ENQUEUE aio in the queue;
@@ -292,7 +375,7 @@ issued outstanding request.
On getting that response, we issue another read request if there are On getting that response, we issue another read request if there are
still some bytes in the sftp file (to download) for which we haven't sent the still some bytes in the sftp file (to download) for which we haven't sent the
read request. (This happens when bytes_requested < file_size) read request. (This happens when total_bytes_requested < file_size)
This issuing of another read request (under a condition) is done to This issuing of another read request (under a condition) is done to
keep the number of outstanding requests equal to the value of the keep the number of outstanding requests equal to the value of the
@@ -314,36 +397,45 @@ while (the queue is not empty) {
bytes_downloaded += bytes_read; bytes_downloaded += bytes_read;
if (bytes_read != chunk_size && bytes_downloaded != file_size) { if (bytes_read != chunk_size && bytes_downloaded != file_size) {
// A short read encountered on the remote file before reaching EOF, // A short read encountered on the remote file before reaching EOF,
// handle it. // short read before reaching EOF should never happen for the sftp aio
// API which respects the max limit for reading. This probably
// indicates a bad server.
} }
// Pseudo code // Pseudo code
WRITE bytes_read bytes from the buffer into the local file WRITE bytes_read bytes from the buffer into the local file
in which downloaded data is to be stored ; in which downloaded data is to be stored ;
if (bytes_requested == file_size) { if (total_bytes_requested == file_size) {
// no need to issue more read requests // no need to issue more read requests
continue; continue;
} }
// else issue a read request // else issue a read request
to_read = file_size - bytes_requested; to_read = file_size - total_bytes_requested;
if (to_read > chunk_size) { if (to_read > chunk_size) {
to_read = chunk_size; to_read = chunk_size;
} }
rc = sftp_aio_begin_read(file, to_read, &aio); bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
if (rc == SSH_ERROR) { if (bytes_requested == SSH_ERROR) {
// handle error // handle error
} }
bytes_requested += to_read; if ((size_t)bytes_requested < to_read) {
// Should not happen for this code, as the to_read is <=
// max limit for reading (chunk size), so there is no reason
// for sftp_aio_begin_read() to return a lesser value.
}
total_bytes_requested += bytes_requested;
// Pseudo code // Pseudo code
ENQUEUE aio in the queue; ENQUEUE aio in the queue;
} }
free(buffer); free(buffer);
sftp_limits_free(lim);
... // Code to destroy the queue which was used to store the sftp aio ... // Code to destroy the queue which was used to store the sftp aio
// handles. // handles.
@@ -356,6 +448,9 @@ would've been complete (assuming no error occurs).
Terminologies used in the following code snippets : Terminologies used in the following code snippets :
- sftp : The sftp_session opened using sftp_new() and initialised using
sftp_init()
- file : The sftp file handle of the remote file in which uploaded data - file : The sftp file handle of the remote file in which uploaded data
is to be stored. (See sftp_open()) is to be stored. (See sftp_open())
@@ -372,18 +467,41 @@ requested to write don't exceed the size of the file to upload.
@code @code
sftp_aio aio = NULL; sftp_aio aio = NULL;
// Using a chunk size of 16 KB // The chunk size to use for the transfer
size_t chunk_size = 16 * 1024; size_t chunk_size;
// For the limits structure that would be used by
// the code to set the chunk size
sftp_limits_t lim = NULL;
// Max number of requests to keep outstanding at a time // Max number of requests to keep outstanding at a time
size_t in_flight_requests = 5; size_t in_flight_requests = 5;
// Number of bytes for which write requests have been sent // Total number of bytes for which write requests have been sent
size_t bytes_requested = 0; size_t total_bytes_requested = 0;
// Buffer to use for the upload // Buffer to use for the upload
char *buffer = NULL; char *buffer = NULL;
// Helper variables
size_t to_write;
ssize_t bytes_requested;
// Get the sftp limits
lim = sftp_limits(sftp);
if (lim == NULL) {
// handle error
}
// Set the chunk size for upload = the max limit for writing.
// The reason for this has been given in the "Capping applied by
// the sftp aio API" section (Its to make the code simpler)
//
// Assigning a size_t type variable a uint64_t type value here,
// theoretically could cause an overflow, but practically
// max_write_length would never exceed SIZE_MAX so its okay.
chunk_size = lim->max_write_length;
buffer = malloc(chunk_size); buffer = malloc(chunk_size);
if (buffer == NULL) { if (buffer == NULL) {
// handle error // handle error
@@ -397,9 +515,9 @@ if (buffer == NULL) {
// handles. // handles.
for (i = 0; for (i = 0;
i < in_flight_requests && bytes_requested < file_size; i < in_flight_requests && total_bytes_requested < file_size;
++i) { ++i) {
to_write = file_size - bytes_requested; to_write = file_size - total_bytes_requested;
if (to_write > chunk_size) { if (to_write > chunk_size) {
to_write = chunk_size; to_write = chunk_size;
} }
@@ -407,12 +525,18 @@ for (i = 0;
// Pseudo code // Pseudo code
READ to_write bytes from the local file (to upload) into the buffer; READ to_write bytes from the local file (to upload) into the buffer;
rc = sftp_aio_begin_write(file, buffer, to_write, &aio); bytes_requested = sftp_aio_begin_write(file, buffer, to_write, &aio);
if (rc == SSH_ERROR) { if (bytes_requested == SSH_ERROR) {
// handle error // handle error
} }
bytes_requested += to_write; if ((size_t)bytes_requested < to_write) {
// Should not happen for this code, as the to_write is <=
// max limit for writing (chunk size), so there is no reason
// for sftp_aio_begin_write() to return a lesser value.
}
total_bytes_requested += (size_t)bytes_requested;
// Pseudo code // Pseudo code
ENQUEUE aio in the queue; ENQUEUE aio in the queue;
@@ -426,7 +550,7 @@ issued outstanding request.
On getting that response, we issue another write request if there are On getting that response, we issue another write request if there are
still some bytes in the local file (to upload) for which we haven't sent still some bytes in the local file (to upload) for which we haven't sent
the write request. (This happens when bytes_requested < file_size) the write request. (This happens when total_bytes_requested < file_size)
This issuing of another write request (under a condition) is done to This issuing of another write request (under a condition) is done to
keep the number of outstanding requests equal to the value of the keep the number of outstanding requests equal to the value of the
@@ -448,13 +572,13 @@ while (the queue is not empty) {
// sftp_aio_wait_write() won't report a short write, so no need // sftp_aio_wait_write() won't report a short write, so no need
// to check for a short write here. // to check for a short write here.
if (bytes_requested == file_size) { if (total_bytes_requested == file_size) {
// no need to issue more write requests // no need to issue more write requests
continue; continue;
} }
// else issue a write request // else issue a write request
to_write = file_size - bytes_requested; to_write = file_size - total_bytes_requested;
if (to_write > chunk_size) { if (to_write > chunk_size) {
to_write = chunk_size; to_write = chunk_size;
} }
@@ -462,12 +586,18 @@ while (the queue is not empty) {
// Pseudo code // Pseudo code
READ to_write bytes from the local file (to upload) into a buffer; READ to_write bytes from the local file (to upload) into a buffer;
rc = sftp_aio_begin_write(file, buffer, to_write, &aio); bytes_requested = sftp_aio_begin_write(file, buffer, to_write, &aio);
if (rc == SSH_ERROR) { if (bytes_requested == SSH_ERROR) {
// handle error // handle error
} }
bytes_requested += to_write; if ((size_t)bytes_requested < to_write) {
// Should not happen for this code, as the to_write is <=
// max limit for writing (chunk size), so there is no reason
// for sftp_aio_begin_write() to return a lesser value.
}
total_bytes_requested += (size_t)bytes_requested;
// Pseudo code // Pseudo code
ENQUEUE aio in the queue; ENQUEUE aio in the queue;