views:

864

answers:

3

I'm uploading a file to a remote server using SCP, but what's the proper way of seeing if that file exists before doing so?

A: 

You can't do this with scp. However you can do it with sftp. It would look something like this:

require 'net/sftp'

remote_path = "/path/to/remote/file"
Net::SFTP.start('host', 'username', :password => 'password') do |sftp|
  sftp.stat!(remote_path) do |response|
    unless response.ok?
    sftp.upload!("/path/to/local/file", remote_path)
  end
end
EmFi
A: 

I do not know how to properly encapsulate this in ruby.

This is how I would do it with shell commands

system("echo \"ls \\\"#{file}\\\"\" | ssh \"#{remote}\"")
  • ssh reads it's commands from stdin
  • ssh returns the return value of the last executed commnd
  • you are executing ls <file> on the remote host
  • echo's return value say's whether the file exists and or the directory is readable

This method has some drawbacks

  • there is no difference beetween file does not exist and you are not allowed to read this directory
  • you have to properly escape file and remote or the user may call sudo rm -rf / on the remote system
johannes
A: 

Beware check-then-upload: this is not atomic and may lead to race conditions (or TOCTOU vulnerabilities).

A better way forwards might be to set the file permissions on the uploaded file so that it is not writable (400 or 444 or similar on UNIX-like systems), then any subsequent uploads to the same filename will simply fail. (You'll get "permission denied" back which may be subject to a little interpretation (eg did the upload directory permissions change?). This is one downside to this method).

How do you set permissions on the remote file? The simplest way to do that is to set them on the local file and then preserve them when you do the upload. Ruby's Net::SCP README says that it can indeed "preserve file attributes across transfers".

Martin Carpenter