Blame doc/sftp.dox

Packit 6c0a39
/**
Packit 6c0a39
@page libssh_tutor_sftp Chapter 5: The SFTP subsystem
Packit 6c0a39
@section sftp_subsystem The SFTP subsystem
Packit 6c0a39
Packit 6c0a39
SFTP stands for "Secure File Transfer Protocol". It enables you to safely
Packit 6c0a39
transfer files between the local and the remote computer. It reminds a lot
Packit 6c0a39
of the old FTP protocol.
Packit 6c0a39
Packit 6c0a39
SFTP is a rich protocol. It lets you do over the network almost everything
Packit 6c0a39
that you can do with local files:
Packit 6c0a39
  - send files
Packit 6c0a39
  - modify only a portion of a file
Packit 6c0a39
  - receive files
Packit 6c0a39
  - receive only a portion of a file
Packit 6c0a39
  - get file owner and group
Packit 6c0a39
  - get file permissions
Packit 6c0a39
  - set file owner and group
Packit 6c0a39
  - set file permissions
Packit 6c0a39
  - remove files
Packit 6c0a39
  - rename files
Packit 6c0a39
  - create a directory
Packit 6c0a39
  - remove a directory
Packit 6c0a39
  - retrieve the list of files in a directory
Packit 6c0a39
  - get the target of a symbolic link
Packit 6c0a39
  - create symbolic links
Packit 6c0a39
  - get information about mounted filesystems.
Packit 6c0a39
Packit 6c0a39
The current implemented version of the SFTP protocol is version 3. All functions
Packit 6c0a39
aren't implemented yet, but the most important are.
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
@subsection sftp_session Opening and closing a SFTP session
Packit 6c0a39
Packit 6c0a39
Unlike with remote shells and remote commands, when you use the SFTP subsystem,
Packit 6c0a39
you don't handle directly the SSH channels. Instead, you open a "SFTP session".
Packit 6c0a39
Packit 6c0a39
The function sftp_new() creates a new SFTP session. The function sftp_init()
Packit 6c0a39
initializes it. The function sftp_free() deletes it.
Packit 6c0a39
Packit 6c0a39
As you see, all the SFTP-related functions start with the "sftp_" prefix
Packit 6c0a39
instead of the usual "ssh_" prefix.
Packit 6c0a39
Packit 6c0a39
The example below shows how to use these functions:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
#include <libssh/sftp.h>
Packit 6c0a39
Packit 6c0a39
int sftp_helloworld(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
  sftp_session sftp;
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  sftp = sftp_new(session);
Packit 6c0a39
  if (sftp == NULL)
Packit 6c0a39
  {
Packit 6c0a39
    fprintf(stderr, "Error allocating SFTP session: %s\n",
Packit 6c0a39
            ssh_get_error(session));
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = sftp_init(sftp);
Packit 6c0a39
  if (rc != SSH_OK)
Packit 6c0a39
  {
Packit 6c0a39
    fprintf(stderr, "Error initializing SFTP session: code %d.\n",
Packit 6c0a39
            sftp_get_error(sftp));
Packit 6c0a39
    sftp_free(sftp);
Packit 6c0a39
    return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  ...
Packit 6c0a39
Packit 6c0a39
  sftp_free(sftp);
Packit 6c0a39
  return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
@subsection sftp_errors Analyzing SFTP errors
Packit 6c0a39
Packit 6c0a39
In case of a problem, the function sftp_get_error() returns a SFTP-specific
Packit 6c0a39
error number, in addition to the regular SSH error number returned by
Packit 6c0a39
ssh_get_error_number().
Packit 6c0a39
Packit 6c0a39
Possible errors are:
Packit 6c0a39
  - SSH_FX_OK: no error
Packit 6c0a39
  - SSH_FX_EOF: end-of-file encountered
Packit 6c0a39
  - SSH_FX_NO_SUCH_FILE: file does not exist
Packit 6c0a39
  - SSH_FX_PERMISSION_DENIED: permission denied
Packit 6c0a39
  - SSH_FX_FAILURE: generic failure
Packit 6c0a39
  - SSH_FX_BAD_MESSAGE: garbage received from server
Packit 6c0a39
  - SSH_FX_NO_CONNECTION: no connection has been set up
Packit 6c0a39
  - SSH_FX_CONNECTION_LOST: there was a connection, but we lost it
Packit 6c0a39
  - SSH_FX_OP_UNSUPPORTED: operation not supported by libssh yet
Packit 6c0a39
  - SSH_FX_INVALID_HANDLE: invalid file handle
Packit 6c0a39
  - SSH_FX_NO_SUCH_PATH: no such file or directory path exists
Packit 6c0a39
  - SSH_FX_FILE_ALREADY_EXISTS: an attempt to create an already existing file or directory has been made
Packit 6c0a39
  - SSH_FX_WRITE_PROTECT: write-protected filesystem
Packit 6c0a39
  - SSH_FX_NO_MEDIA: no media was in remote drive
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
@subsection sftp_mkdir Creating a directory
Packit 6c0a39
Packit 6c0a39
The function sftp_mkdir() takes the "SFTP session" we just created as
Packit 6c0a39
its first argument. It also needs the name of the file to create, and the
Packit 6c0a39
desired permissions. The permissions are the same as for the usual mkdir()
Packit 6c0a39
function. To get a comprehensive list of the available permissions, use the
Packit 6c0a39
"man 2 stat" command. The desired permissions are combined with the remote
Packit 6c0a39
user's mask to determine the effective permissions.
Packit 6c0a39
Packit 6c0a39
The code below creates a directory named "helloworld" in the current directory that
Packit 6c0a39
can be read and written only by its owner:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
#include <libssh/sftp.h>
Packit 6c0a39
#include <sys/stat.h>
Packit 6c0a39
Packit 6c0a39
int sftp_helloworld(ssh_session session, sftp_session sftp)
Packit 6c0a39
{
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  rc = sftp_mkdir(sftp, "helloworld", S_IRWXU);
Packit 6c0a39
  if (rc != SSH_OK)
Packit 6c0a39
  {
Packit 6c0a39
    if (sftp_get_error(sftp) != SSH_FX_FILE_ALREADY_EXISTS)
Packit 6c0a39
    {
Packit 6c0a39
      fprintf(stderr, "Can't create directory: %s\n",
Packit 6c0a39
              ssh_get_error(session));
Packit 6c0a39
        return rc;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  ...
Packit 6c0a39
Packit 6c0a39
  return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
Unlike its equivalent in the SCP subsystem, this function does NOT change the
Packit 6c0a39
current directory to the newly created subdirectory.
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
@subsection sftp_write Copying a file to the remote computer
Packit 6c0a39
Packit 6c0a39
You handle the contents of a remote file just like you would do with a
Packit 6c0a39
local file: you open the file in a given mode, move the file pointer in it,
Packit 6c0a39
read or write data, and close the file.
Packit 6c0a39
Packit 6c0a39
The sftp_open() function is very similar to the regular open() function,
Packit 6c0a39
excepted that it returns a file handle of type sftp_file. This file handle
Packit 6c0a39
is then used by the other file manipulation functions and remains valid
Packit 6c0a39
until you close the remote file with sftp_close().
Packit 6c0a39
Packit 6c0a39
The example below creates a new file named "helloworld.txt" in the
Packit 6c0a39
newly created "helloworld" directory. If the file already exists, it will
Packit 6c0a39
be truncated. It then writes the famous "Hello, World!" sentence to the
Packit 6c0a39
file, followed by a new line character. Finally, the file is closed:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
#include <libssh/sftp.h>
Packit 6c0a39
#include <sys/stat.h>
Packit 6c0a39
#include <fcntl.h>
Packit 6c0a39
Packit 6c0a39
int sftp_helloworld(ssh_session session, sftp_session sftp)
Packit 6c0a39
{
Packit 6c0a39
  int access_type = O_WRONLY | O_CREAT | O_TRUNC;
Packit 6c0a39
  sftp_file file;
Packit 6c0a39
  const char *helloworld = "Hello, World!\n";
Packit 6c0a39
  int length = strlen(helloworld);
Packit 6c0a39
  int rc, nwritten;
Packit 6c0a39
Packit 6c0a39
  ...
Packit 6c0a39
Packit 6c0a39
  file = sftp_open(sftp, "helloworld/helloworld.txt",
Packit 6c0a39
                   access_type, S_IRWXU);
Packit 6c0a39
  if (file == NULL)
Packit 6c0a39
  {
Packit 6c0a39
    fprintf(stderr, "Can't open file for writing: %s\n",
Packit 6c0a39
            ssh_get_error(session));
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  nwritten = sftp_write(file, helloworld, length);
Packit 6c0a39
  if (nwritten != length)
Packit 6c0a39
  {
Packit 6c0a39
    fprintf(stderr, "Can't write data to file: %s\n",
Packit 6c0a39
            ssh_get_error(session));
Packit 6c0a39
    sftp_close(file);
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = sftp_close(file);
Packit 6c0a39
  if (rc != SSH_OK)
Packit 6c0a39
  {
Packit 6c0a39
    fprintf(stderr, "Can't close the written file: %s\n",
Packit 6c0a39
            ssh_get_error(session));
Packit 6c0a39
    return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
Packit 6c0a39
@subsection sftp_read Reading a file from the remote computer
Packit 6c0a39
Packit 6c0a39
The nice thing with reading a file over the network through SFTP is that it
Packit 6c0a39
can be done both in a synchronous way or an asynchronous way. If you read the file
Packit 6c0a39
asynchronously, your program can do something else while it waits for the
Packit 6c0a39
results to come.
Packit 6c0a39
Packit 6c0a39
Synchronous read is done with sftp_read().
Packit 6c0a39
Packit 6c0a39
Files are normally transferred in chunks. A good chunk size is 16 KB. The following
Packit 6c0a39
example transfers the remote file "/etc/profile" in 16 KB chunks. For each chunk we
Packit 6c0a39
request, sftp_read blocks till the data has been received:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
// Good chunk size
Packit 6c0a39
#define MAX_XFER_BUF_SIZE 16384
Packit 6c0a39
Packit 6c0a39
int sftp_read_sync(ssh_session session, sftp_session sftp)
Packit 6c0a39
{
Packit 6c0a39
  int access_type;
Packit 6c0a39
  sftp_file file;
Packit 6c0a39
  char buffer[MAX_XFER_BUF_SIZE];
Packit 6c0a39
  int nbytes, nwritten, rc;
Packit 6c0a39
  int fd;
Packit 6c0a39
Packit 6c0a39
  access_type = O_RDONLY;
Packit 6c0a39
  file = sftp_open(sftp, "/etc/profile",
Packit 6c0a39
                   access_type, 0);
Packit 6c0a39
  if (file == NULL) {
Packit 6c0a39
      fprintf(stderr, "Can't open file for reading: %s\n",
Packit 6c0a39
              ssh_get_error(session));
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  fd = open("/path/to/profile", O_CREAT);
Packit 6c0a39
  if (fd < 0) {
Packit 6c0a39
      fprintf(stderr, "Can't open file for writing: %s\n",
Packit 6c0a39
              strerror(errno));
Packit 6c0a39
      return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  for (;;) {
Packit 6c0a39
      nbytes = sftp_read(file, buffer, sizeof(buffer));
Packit 6c0a39
      if (nbytes == 0) {
Packit 6c0a39
          break; // EOF
Packit 6c0a39
      } else if (nbytes < 0) {
Packit 6c0a39
          fprintf(stderr, "Error while reading file: %s\n",
Packit 6c0a39
                  ssh_get_error(session));
Packit 6c0a39
          sftp_close(file);
Packit 6c0a39
          return SSH_ERROR;
Packit 6c0a39
      }
Packit 6c0a39
Packit 6c0a39
      nwritten = write(fd, buffer, nbytes);
Packit 6c0a39
      if (nwritten != nbytes) {
Packit 6c0a39
          fprintf(stderr, "Error writing: %s\n",
Packit 6c0a39
                  strerror(errno));
Packit 6c0a39
          sftp_close(file);
Packit 6c0a39
          return SSH_ERROR;
Packit 6c0a39
      }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = sftp_close(file);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
      fprintf(stderr, "Can't close the read file: %s\n",
Packit 6c0a39
              ssh_get_error(session));
Packit 6c0a39
      return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
Asynchronous read is done in two steps, first sftp_async_read_begin(), which
Packit 6c0a39
returns a "request handle", and then sftp_async_read(), which uses that request handle.
Packit 6c0a39
If the file has been opened in nonblocking mode, then sftp_async_read()
Packit 6c0a39
might return SSH_AGAIN, which means that the request hasn't completed yet
Packit 6c0a39
and that the function should be called again later on. Otherwise,
Packit 6c0a39
sftp_async_read() waits for the data to come. To open a file in nonblocking mode,
Packit 6c0a39
call sftp_file_set_nonblocking() right after you opened it. Default is blocking mode.
Packit 6c0a39
Packit 6c0a39
The example below reads a very big file in asynchronous, nonblocking, mode. Each
Packit 6c0a39
time the data is not ready yet, a counter is incremented.
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
// Good chunk size
Packit 6c0a39
#define MAX_XFER_BUF_SIZE 16384
Packit 6c0a39
Packit 6c0a39
int sftp_read_async(ssh_session session, sftp_session sftp)
Packit 6c0a39
{
Packit 6c0a39
  int access_type;
Packit 6c0a39
  sftp_file file;
Packit 6c0a39
  char buffer[MAX_XFER_BUF_SIZE];
Packit 6c0a39
  int async_request;
Packit 6c0a39
  int nbytes;
Packit 6c0a39
  long counter;
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  access_type = O_RDONLY;
Packit 6c0a39
  file = sftp_open(sftp, "some_very_big_file",
Packit 6c0a39
                   access_type, 0);
Packit 6c0a39
  if (file == NULL) {
Packit 6c0a39
    fprintf(stderr, "Can't open file for reading: %s\n",
Packit 6c0a39
                     ssh_get_error(session));
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
  sftp_file_set_nonblocking(file);
Packit 6c0a39
Packit 6c0a39
  async_request = sftp_async_read_begin(file, sizeof(buffer));
Packit 6c0a39
  counter = 0L;
Packit 6c0a39
  usleep(10000);
Packit 6c0a39
  if (async_request >= 0) {
Packit 6c0a39
    nbytes = sftp_async_read(file, buffer, sizeof(buffer),
Packit 6c0a39
                             async_request);
Packit 6c0a39
  } else {
Packit 6c0a39
      nbytes = -1;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  while (nbytes > 0 || nbytes == SSH_AGAIN) {
Packit 6c0a39
    if (nbytes > 0) {
Packit 6c0a39
      write(1, buffer, nbytes);
Packit 6c0a39
      async_request = sftp_async_read_begin(file, sizeof(buffer));
Packit 6c0a39
    } else {
Packit 6c0a39
        counter++;
Packit 6c0a39
    }
Packit 6c0a39
    usleep(10000);
Packit 6c0a39
Packit 6c0a39
    if (async_request >= 0) {
Packit 6c0a39
      nbytes = sftp_async_read(file, buffer, sizeof(buffer),
Packit 6c0a39
                               async_request);
Packit 6c0a39
    } else {
Packit 6c0a39
        nbytes = -1;
Packit 6c0a39
    }
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (nbytes < 0) {
Packit 6c0a39
    fprintf(stderr, "Error while reading file: %s\n",
Packit 6c0a39
            ssh_get_error(session));
Packit 6c0a39
    sftp_close(file);
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  printf("The counter has reached value: %ld\n", counter);
Packit 6c0a39
Packit 6c0a39
  rc = sftp_close(file);
Packit 6c0a39
  if (rc != SSH_OK) {
Packit 6c0a39
    fprintf(stderr, "Can't close the read file: %s\n",
Packit 6c0a39
            ssh_get_error(session));
Packit 6c0a39
    return rc;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
@subsection sftp_ls Listing the contents of a directory
Packit 6c0a39
Packit 6c0a39
The functions sftp_opendir(), sftp_readdir(), sftp_dir_eof(),
Packit 6c0a39
and sftp_closedir() enable to list the contents of a directory.
Packit 6c0a39
They use a new handle_type, "sftp_dir", which gives access to the
Packit 6c0a39
directory being read.
Packit 6c0a39
Packit 6c0a39
In addition, sftp_readdir() returns a "sftp_attributes" which is a pointer
Packit 6c0a39
to a structure with information about a directory entry:
Packit 6c0a39
  - name: the name of the file or directory
Packit 6c0a39
  - size: its size in bytes
Packit 6c0a39
  - etc.
Packit 6c0a39
Packit 6c0a39
sftp_readdir() might return NULL under two conditions:
Packit 6c0a39
  - when the end of the directory has been met
Packit 6c0a39
  - when an error occurred
Packit 6c0a39
Packit 6c0a39
To tell the difference, call sftp_dir_eof().
Packit 6c0a39
Packit 6c0a39
The attributes must be freed with sftp_attributes_free() when no longer
Packit 6c0a39
needed.
Packit 6c0a39
Packit 6c0a39
The following example reads the contents of some remote directory:
Packit 6c0a39
Packit 6c0a39
@code
Packit 6c0a39
int sftp_list_dir(ssh_session session, sftp_session sftp)
Packit 6c0a39
{
Packit 6c0a39
  sftp_dir dir;
Packit 6c0a39
  sftp_attributes attributes;
Packit 6c0a39
  int rc;
Packit 6c0a39
Packit 6c0a39
  dir = sftp_opendir(sftp, "/var/log");
Packit 6c0a39
  if (!dir)
Packit 6c0a39
  {
Packit 6c0a39
    fprintf(stderr, "Directory not opened: %s\n",
Packit 6c0a39
            ssh_get_error(session));
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  printf("Name                       Size Perms    Owner\tGroup\n");
Packit 6c0a39
Packit 6c0a39
  while ((attributes = sftp_readdir(sftp, dir)) != NULL)
Packit 6c0a39
  {
Packit 6c0a39
    printf("%-20s %10llu %.8o %s(%d)\t%s(%d)\n",
Packit 6c0a39
     attributes->name,
Packit 6c0a39
     (long long unsigned int) attributes->size,
Packit 6c0a39
     attributes->permissions,
Packit 6c0a39
     attributes->owner,
Packit 6c0a39
     attributes->uid,
Packit 6c0a39
     attributes->group,
Packit 6c0a39
     attributes->gid);
Packit 6c0a39
Packit 6c0a39
     sftp_attributes_free(attributes);
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  if (!sftp_dir_eof(dir))
Packit 6c0a39
  {
Packit 6c0a39
    fprintf(stderr, "Can't list directory: %s\n",
Packit 6c0a39
            ssh_get_error(session));
Packit 6c0a39
    sftp_closedir(dir);
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  rc = sftp_closedir(dir);
Packit 6c0a39
  if (rc != SSH_OK)
Packit 6c0a39
  {
Packit 6c0a39
    fprintf(stderr, "Can't close directory: %s\n",
Packit 6c0a39
            ssh_get_error(session));
Packit 6c0a39
    return rc;
Packit 6c0a39
  }
Packit 6c0a39
}
Packit 6c0a39
@endcode
Packit 6c0a39
Packit 6c0a39
*/