<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Petar G. Petrov</title>
    <description>The latest articles on Forem by Petar G. Petrov (@petarov).</description>
    <link>https://forem.com/petarov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1033%2Fbdf15acb-b1d4-43ae-a309-1a005e2b3df0.png</url>
      <title>Forem: Petar G. Petrov</title>
      <link>https://forem.com/petarov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/petarov"/>
    <language>en</language>
    <item>
      <title>Java and Native Code in OpenJDK 13</title>
      <dc:creator>Petar G. Petrov</dc:creator>
      <pubDate>Tue, 11 Jun 2019 18:46:22 +0000</pubDate>
      <link>https://forem.com/petarov/java-and-native-code-in-openjdk-13-18em</link>
      <guid>https://forem.com/petarov/java-and-native-code-in-openjdk-13-18em</guid>
      <description>&lt;h1&gt;
  
  
  Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Intro&lt;/li&gt;
&lt;li&gt;
Hands-on

&lt;ul&gt;
&lt;li&gt;The libCurl App&lt;/li&gt;
&lt;li&gt;The libSSH2 App&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Final Thoughts&lt;/li&gt;

&lt;li&gt;References&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Intro
&lt;/h1&gt;

&lt;p&gt;Last week I watched a pretty cool GOTO Conference talk [1] with Mikael Vidstedt, director of Software Engineering at Oracle, where he presents many of the upcoming and currently being worked on Java features. One of those, &lt;strong&gt;Project Panama&lt;/strong&gt;, stood out as particularly interesting to me.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1136248991254028289-119" src="https://platform.twitter.com/embed/Tweet.html?id=1136248991254028289"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1136248991254028289-119');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1136248991254028289&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: Project Panama is still in its early stages of development, so the contents of this article may not reflect its current state.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Project Panama, available in early-access JDK 13 builds, is meant as a bridge between Java and native code. How is this useful? There are certain use cases where a piece of functionality is available as a library written in C or C++ and the requirement is to integrate it into a Java application. The current solution is to use the Java Native Interface (JNI) [2] which could require a vast amount of work and is also quite error-prone. You need to have a good grasp of the native library's insides and then write all the required implementations to bridge the Java interface with the native code. From my experience, calling native library functions that may change at some later point in time and also managing heap memory allocations could be a real challenge with JNI. To quote Mikael as he says it on the video:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How many people here have written JNI or, you know, done JNI? We're so sorry for you!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is where Panama comes into play. It provides new tools and API to simplify the bridging between Java and native code. It basically boils down to the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate Java bindings (interfaces) from existing C header file(s) using the &lt;strong&gt;jextract&lt;/strong&gt; tool.&lt;/li&gt;
&lt;li&gt;Invoke C functions via the jextracted Java interface using the &lt;strong&gt;java.foreign&lt;/strong&gt; API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This allows you to concentrate on writing the core logic of your application, rather than fiddling with glue code and integration details.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Hands-on
&lt;/h1&gt;

&lt;p&gt;Project Panama's documentation pages [3] already provide a solid number of examples to start with. I'll just take a quick peek at how to bridge and run a &lt;em&gt;libCurl&lt;/em&gt; Java app and then I'd like to present a more detailed example - a simple SSH client that I wrote based on &lt;em&gt;libSSH2&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;I'm running these examples on a macOS, but with a few tweaks you should also be able to run them on a Linux installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;The libCurl App
&lt;/h2&gt;

&lt;p&gt;How to download a web page using the native Curl library's implementation? Well, first we need to get and extract a Panama &lt;a href="http://jdk.java.net/panama/" rel="noopener noreferrer"&gt;OpenJDK build&lt;/a&gt; archive.&lt;/p&gt;

&lt;p&gt;Let's open up a shell and set the &lt;code&gt;JAVA_HOME&lt;/code&gt; environment variable to where the OpenJDK build archive is extracted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JAVA_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jdk-13.jdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we need to generate the Java interfaces, the glue code that will bind Java code to the native library. This will produce a &lt;code&gt;curl.jar&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$JAVA_HOME&lt;/span&gt;/bin/jextract &lt;span class="nt"&gt;-t&lt;/span&gt; org.unix &lt;span class="nt"&gt;-L&lt;/span&gt; /usr/lib &lt;span class="nt"&gt;-lcurl&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--record-library-path&lt;/span&gt; /usr/include/curl/curl.h &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; curl.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When we inspect the JAR file, we can see all the Curl API calls, as well as dependency bindings. The Curl API is available through the new &lt;strong&gt;java.foreign&lt;/strong&gt; Java API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F5j56upyw7yz643tiwefx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F5j56upyw7yz643tiwefx.png" alt="curl.jar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now for a quick example. Here's a Java piece of code that fetches a web page and displays its contents on screen.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.lang.invoke.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.foreign.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.foreign.memory.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.unix.curl.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.unix.curl_h&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;curl_h&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;easy_h&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Scope&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curl_h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; 
           &lt;span class="n"&gt;curl_global_init&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CURL_GLOBAL_DEFAULT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
           &lt;span class="nc"&gt;Pointer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;curl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curl_easy_init&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
           &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isNull&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
               &lt;span class="nc"&gt;Pointer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Byte&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allocateCString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]);&lt;/span&gt;
               &lt;span class="n"&gt;curl_easy_setopt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_URL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
               &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curl_easy_perform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;CURLE_OK&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                 &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error fetching from: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" ERR: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;Pointer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curl_easy_strerror&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
                 &lt;span class="n"&gt;curl_easy_cleanup&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
               &lt;span class="o"&gt;}&lt;/span&gt;
           &lt;span class="o"&gt;}&lt;/span&gt;
           &lt;span class="n"&gt;curl_global_cleanup&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A couple of things to point out here. Notice how we cannot directly pass a Java &lt;code&gt;String&lt;/code&gt; to the &lt;code&gt;curl_easy_setopt()&lt;/code&gt; call. This call accepts a memory address pointer as the &lt;code&gt;url&lt;/code&gt; parameter, so we first need to do a dynamic memory allocation operation using the &lt;code&gt;Scope&lt;/code&gt; and pass a &lt;code&gt;Pointer&lt;/code&gt; interface instance instead. As you may find in the Panama tech docs [5], the &lt;code&gt;Pointer&lt;/code&gt; interface helps a lot when it comes to complex C-alike pointer operations like pointer arithmetic, casts, memory dereference, etc. The &lt;code&gt;Scope&lt;/code&gt; manages the runtime life-cycle of dynamic allocated memory.&lt;/p&gt;

&lt;p&gt;Alright, now armed with this knowledge, can you extend this code to write the contents of a Curl fetched web page to a file stream?&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;The libSSH2 App
&lt;/h2&gt;

&lt;p&gt;Here's a more complete example application that utilizes &lt;em&gt;libSSH2&lt;/em&gt; [4] to implement a simple SSH client. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/petarov" rel="noopener noreferrer"&gt;
        petarov
      &lt;/a&gt; / &lt;a href="https://github.com/petarov/java-panama-ssh2" rel="noopener noreferrer"&gt;
        java-panama-ssh2
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Java SSH client using libssh2 through project Panama
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;java-panama-ssh2&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A simple Java SSH2 client using the native &lt;a href="https://www.libssh2.org" rel="nofollow noopener noreferrer"&gt;libssh2&lt;/a&gt; library and JDK 13 &lt;a href="https://openjdk.java.net/projects/panama/" rel="nofollow noopener noreferrer"&gt;Project Panama
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;NOTE: This is an experimental project. Stability is not guaranteed.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Install&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The client has been tested on macOS and Linux. It should also be possible to run it on Windows, however, no work has been done in that direction. PRs are welcome!&lt;/p&gt;
&lt;p&gt;Install &lt;code&gt;libssh2&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;macOS - &lt;code&gt;brew install libssh2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CentOS - &lt;code&gt;yum install libssh2.x86_64 libssh2-devel.x86_64&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Get the latest Panama &lt;a href="http://jdk.java.net/panama/" rel="nofollow noopener noreferrer"&gt;JDK build&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Open a console shell and configure the &lt;code&gt;JAVA_HOME&lt;/code&gt; var to point to JDK 13.&lt;/p&gt;
&lt;p&gt;Generate the required Java interfaces from the native libssh2 headers:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;./gen_bindings.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;To compile and run use:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;./run.sh [-p|-k] hostname port username [path to ssh keys]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-p&lt;/code&gt; uses a password login. You'll be prompted to enter your password.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-k&lt;/code&gt; uses a public key login. You'll need to specify the path to your keys, e.g., &lt;code&gt;~/.ssh&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;License&lt;/h2&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/petarov/java-panama-ssh2" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;If you'd like to have a go and adjust it to run on Windows, I'll greatly appreciate a PR.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Final Thoughts
&lt;/h1&gt;

&lt;p&gt;A few points from my side that I learned or have been thinking about while working with Panama.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Scope&lt;/code&gt; is pretty powerful. You can allocate callback pointers, structs, arrays. The concept of layouts and layout types deserves more time for me to fully explore and grasp.&lt;/li&gt;
&lt;li&gt;It comes as no surprise that writing Java code using a native library is more extensive and requires extra care, especially when it comes to not forgetting to invoke cleanup API calls that the library requires.&lt;/li&gt;
&lt;li&gt;I/O in most native libraries requires a file descriptor, which isn't easy to get in Java [6]. This, however, is not directly related to the &lt;strong&gt;java.foreign&lt;/strong&gt; API.&lt;/li&gt;
&lt;li&gt;Some libraries define C++ style function prototypes without argument as opposed to C-style &lt;code&gt;Void&lt;/code&gt; argument types. The Foreign Docs [3] have an example about this case when using the TensorFlow C API.&lt;/li&gt;
&lt;li&gt;I haven't explored if it would be possible to use Panama with Go or Rust created native libraries. That would be pretty cool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading!🍻&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;References
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=vJrHHe3IbQs&amp;amp;t=2172" rel="noopener noreferrer"&gt;GOTO 2019 • Project Panama part&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Java_Native_Interface" rel="noopener noreferrer"&gt;Java Native Interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hg.openjdk.java.net/panama/dev/raw-file/4810a7de75cb/doc/panama_foreign.html" rel="noopener noreferrer"&gt;Panama Foreign Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.libssh2.org" rel="noopener noreferrer"&gt;libSSH2&lt;/a&gt; - a client-side C library implementing the SSH2 protocol&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cr.openjdk.java.net/~mcimadamore/panama/panama-binder-v3.html" rel="noopener noreferrer"&gt;Panama Binder Docs v3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/q/11455803/10364676" rel="noopener noreferrer"&gt;Most efficient way to pass Java socket file descriptor to C binary file
&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>java</category>
      <category>ssh</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Help Me Delete Your Tweets!</title>
      <dc:creator>Petar G. Petrov</dc:creator>
      <pubDate>Sat, 16 Mar 2019 20:39:30 +0000</pubDate>
      <link>https://forem.com/petarov/help-me-delete-your-tweets-3a6</link>
      <guid>https://forem.com/petarov/help-me-delete-your-tweets-3a6</guid>
      <description>&lt;p&gt;Ok, I do apologize for the clickbaity title and to make up for it here's what this is all about in brief.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt;, I'm looking for hackers to help me improve a Python script.&lt;/p&gt;

&lt;p&gt;A couple of years ago I wanted to have all my tweets and likes deleted, but still keep them somewhere for private purposes, mainly for history reference and personal archive. Because I'm not really that active on Twitter, deleting my account would probably have made more sense. But hey! I still wanted to put my uninformed opinion out there from time to time.&lt;/p&gt;

&lt;p&gt;Long story short, I hacked this small Python command line tool that consumes the Twitter API. It finds all tweets and/or likes after a specified date-time point, puts them in an ePub book and then (optionally) removes them from Twitter. Because it's just a command line thing, it's also simple to have it run in a cron job as well. So far so good, it seems to work alright, however, there're some limitations. I couldn't figure out how to save tweet images with &lt;a href="http://www.tweepy.org/" rel="noopener noreferrer"&gt;Tweepy&lt;/a&gt; and the ePub E-book generation could use more options like front and back cover images, table of contents, layout options, etc.&lt;/p&gt;

&lt;p&gt;So I guess this is a call for contributions. If you're looking for some open source project to contribute to and this looks fun to you, you're more than welcome to join in. I'm actually not a Python developer, but a Python hacker, so this is more like a &lt;em&gt;hack-it-for-fun-but-still-make-it-useful&lt;/em&gt; thing here &lt;/p&gt;

&lt;p&gt;Here's the GitHub repo and thanks for reading!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/petarov" rel="noopener noreferrer"&gt;
        petarov
      &lt;/a&gt; / &lt;a href="https://github.com/petarov/shut-up-bird" rel="noopener noreferrer"&gt;
        shut-up-bird
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🐦 Put your tweets/likes in an EPUB and delete them like a boss
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Shut Up Bird&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;⚠️ NO LONGER MAINTAINED 😔&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Archives your tweets in an &lt;a href="https://en.wikipedia.org/wiki/EPUB" rel="nofollow noopener noreferrer"&gt;EPUB&lt;/a&gt; book and then optionally deletes them.&lt;/p&gt;
&lt;p&gt;Things you could do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get rid of your old &lt;em&gt;tweets&lt;/em&gt; or &lt;em&gt;likes&lt;/em&gt; but still have them nicely organized somewhere.&lt;/li&gt;
&lt;li&gt;An annual archive of your twitter activty.&lt;/li&gt;
&lt;li&gt;Setup a cron job to regularly clean up your status.&lt;/li&gt;
&lt;li&gt;Archive someone else's tweets for your own viewing pleasure.&lt;/li&gt;
&lt;li&gt;Read your own tweets in your favourite e-book reader app and cry.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Installation&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Requires Python &lt;code&gt;2.7&lt;/code&gt; or &lt;code&gt;3.x&lt;/code&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Packages&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;To install on &lt;strong&gt;ArchLinux&lt;/strong&gt; from &lt;a href="https://aur.archlinux.org/packages/shut-up-bird" rel="nofollow noopener noreferrer"&gt;AUR&lt;/a&gt; run:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;yaourt -S shut-up-bird
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Manual&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Run &lt;code&gt;make&lt;/code&gt; or &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Setup&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;Create a new &lt;a href="https://apps.twitter.com/" rel="nofollow noopener noreferrer"&gt;Twitter application&lt;/a&gt;. The name shouldn't matter.&lt;/p&gt;
&lt;p&gt;Open the app's &lt;code&gt;Permissions&lt;/code&gt; page and make sure &lt;code&gt;Read and Write&lt;/code&gt; is selected, otherwise &lt;code&gt;shut-up-bird&lt;/code&gt; will not be able to delete anything.&lt;/p&gt;
&lt;p&gt;Run without any parameters to initialize:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;$ python shut-up-bird.py
Please provide your Twitter&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/petarov/shut-up-bird" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>opensource</category>
      <category>showdev</category>
      <category>python</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Home Storage with Raspberry Pi and Samba</title>
      <dc:creator>Petar G. Petrov</dc:creator>
      <pubDate>Fri, 02 Nov 2018 20:53:25 +0000</pubDate>
      <link>https://forem.com/petarov/shared-home-storage-with-raspberry-pi-and-samba-38d9</link>
      <guid>https://forem.com/petarov/shared-home-storage-with-raspberry-pi-and-samba-38d9</guid>
      <description>&lt;h1&gt;
  
  
  Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Goal&lt;/li&gt;
&lt;li&gt;
Raspberry Pi

&lt;ul&gt;
&lt;li&gt;Remove X11&lt;/li&gt;
&lt;li&gt;Storage User&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Samba Server

&lt;ul&gt;
&lt;li&gt;Installation&lt;/li&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Clinets

&lt;ul&gt;
&lt;li&gt;
Linux Client

&lt;ul&gt;
&lt;li&gt;View Shares&lt;/li&gt;
&lt;li&gt;fstab Mount&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Android Client&lt;/li&gt;

&lt;li&gt;iOS Client&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

References
&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Goal
&lt;/h1&gt;

&lt;p&gt;The idea here is pretty simple. Setup a central storage on a Raspberry Pi [1] device, so we could exchange files at home without using &lt;em&gt;the cloud&lt;/em&gt; or &lt;em&gt;Skype&lt;/em&gt;, or any other messaging service. The whole thing wasn't so hard to setup and I do recommend it to everyone that can spare some time and effort into doing this.&lt;/p&gt;

&lt;p&gt;Things I wanted to have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Central storage to place and exchange files.&lt;/li&gt;
&lt;li&gt;Accessible from desktop and mobile devices.&lt;/li&gt;
&lt;li&gt;Available only on the internal network.&lt;/li&gt;
&lt;li&gt;User/Password protection.&lt;/li&gt;
&lt;li&gt;A reason to finally do something with that RPi I got.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwt0jlcnphgtryvx6ho8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwt0jlcnphgtryvx6ho8.png" alt="Samba share scheme" width="633" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Initially I wanted to install a dedicated cloud or storage solution on the RPi device, however, I found out it was much more simple and intuitive to configure a Samba server instead.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Raspberry Pi
&lt;/h1&gt;

&lt;p&gt;Get a Raspbian Lite [2] installation and use &lt;code&gt;dd&lt;/code&gt; to copy the image to your SD Card. Check the official installation guidelines [3].&lt;/p&gt;

&lt;p&gt;On Linux the following should suffice:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dd bs=4M if=2018-10-09-raspbian-stretch.img of=/dev/sd? conv=fsync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Careful with the &lt;code&gt;sd?&lt;/code&gt; part! Make sure that's your SD card device.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Remove X11 (Optional)
&lt;/h2&gt;

&lt;p&gt;EDIT: If you download Raspbian Lite, you don't need to do anything here.&lt;/p&gt;

&lt;p&gt;This might be a good idea, if you'd like to use Raspbian only as a server without a desktop environment. You'd also probably free up to 500MB SD card space.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get remove --purge x11-common
sudo apt-get autoremove
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Storage User
&lt;/h2&gt;

&lt;p&gt;Add a new user with the name &lt;code&gt;storeuser&lt;/code&gt;. All shared files and folders will be stored in the home directory of this user.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo adduser storeuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It might also be a good idea to disable shell login for this guy. In the end we only want to have this user as a basis for our Samba share configuration.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo usermod -s /usr/sbin/nologin storeuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A new home folder &lt;code&gt;/home/storeuser&lt;/code&gt; will get created. Next, create a &lt;code&gt;share&lt;/code&gt; folder under which the shared files and folders will be saved.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo cd /home/storeuser &amp;amp;&amp;amp; mkdir share
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Adjust the read/write permissions to the share folder:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chown -R storeuser /home/storeuser/share
sudo chgrp -R storeuser /home/storeuser/share
sudo chmod -R 1770 /home/storeuser/share
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;About the &lt;code&gt;1770&lt;/code&gt; permissions. We set the sticky bit [4] to &lt;code&gt;1&lt;/code&gt; to allow only the owner of this folder &lt;code&gt;storeuser&lt;/code&gt; to edit or delete files.&lt;/p&gt;

&lt;p&gt;Create a sample file, e.g., a README, just to have at least one file in the share folder for tests.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo touch /home/storeuser/share/README
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Samba Server
&lt;/h1&gt;

&lt;p&gt;There could be a number of ways to setup a Samba share. For the purpose of my needs I chose a single user password protected share mechanism, but you can extend this to use groups and more complex authentication schemes.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Installation
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install samba samba-common-bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Configuration
&lt;/h2&gt;

&lt;p&gt;Edit the &lt;code&gt;smb.conf&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nano /etc/samba/smb.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That's right! I used &lt;code&gt;nano&lt;/code&gt;. I still can't exit &lt;code&gt;vim&lt;/code&gt; on the other terminal. Deal with it. 😊 &lt;/p&gt;

&lt;p&gt;To be on the safe side add your network to the allowed hosts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;global]

hosts allow &lt;span class="o"&gt;=&lt;/span&gt; 192.168.1. 127.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm using a single subnet &lt;code&gt;192.168.1.0/24&lt;/code&gt; network at home. Adjust this depending on your network configuration.&lt;/p&gt;

&lt;p&gt;Add a &lt;code&gt;workgroup&lt;/code&gt; name and enable Windows Internet Name Serving:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;global]

hosts allow &lt;span class="o"&gt;=&lt;/span&gt; 192.168.1. 127.
workgroup &lt;span class="o"&gt;=&lt;/span&gt; homeworld.net
wins support &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now configure the share. Add the following at the end of the &lt;code&gt;smb.conf&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;mystorage]
   &lt;span class="nv"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; my home storage
   &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/storeuser/share
   &lt;span class="nv"&gt;browseable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Yes
   &lt;span class="nv"&gt;writeable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Yes
   only &lt;span class="nv"&gt;guest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no
   create &lt;span class="nv"&gt;mask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1770
   directory &lt;span class="nv"&gt;mask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1770
   &lt;span class="nv"&gt;public&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;path&lt;/code&gt; specifies a directory to which the user of the service is to be given access. &lt;code&gt;browseable&lt;/code&gt; controls whether this share is seen in the list of available shares in a net view and in the browse list.&lt;/p&gt;

&lt;p&gt;Add a new &lt;code&gt;storeuser&lt;/code&gt; password to the local Samba &lt;code&gt;smbpasswd&lt;/code&gt; file. This user must already exist on the system. We'll use the &lt;code&gt;storeuser&lt;/code&gt; that we created earlier.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;smbpasswd -a storeuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is exactly the user and password that clients will use to access the Samba share.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Clients
&lt;/h1&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Linux Client
&lt;/h2&gt;

&lt;p&gt;I'm using Arch Linux quite often at home, so the description below mostly concerns that case. However, this should not differ too much on other Linux distributions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt;View Shares
&lt;/h3&gt;

&lt;p&gt;We could first have a look at the available shares in the network by using &lt;code&gt;smbtree&lt;/code&gt;. Just like that, for fun and glory. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;smbtree -U storeuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When prompted, give the password of &lt;code&gt;storeuser&lt;/code&gt; that you set earlier using &lt;code&gt;smbpasswd&lt;/code&gt;. The result should look similar to this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MYDOMAIN.TLD
    \\RASPBIAN              my raspbian server
        \\RASPBIAN\storeuser        Home Directories
        \\RASPBIAN\IPC$             IPC Service
        \\RASPBIAN\mystorage        my home storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints a tree with all the known domains, the servers in those domains and the shares available on those servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt;fstab Mount
&lt;/h3&gt;

&lt;p&gt;Create a target mount folder.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mkdir /mnt/storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Setup an auto mount by adding the share to &lt;code&gt;/etc/fstab&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//RASPBIAN/mystorage /mnt/storage cifs username=storeuser,password=&amp;lt;password&amp;gt;,comment=noauto,x-systemd.automount,_netdev,uid=&amp;lt;your linux user&amp;gt;,gid=&amp;lt;your linux user group&amp;gt; 0 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you're not using &lt;code&gt;systemd&lt;/code&gt; (for which I don't blame you 😉), remove the &lt;code&gt;noauto,x-systemd.automount&lt;/code&gt; part.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ok, what the heck is this &lt;code&gt;_netdev&lt;/code&gt; trickery?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Basically, this means that we'd like to defer mounting the share until the network interface is up [5]. No point in trying to mount if connection's not been setup yet.&lt;/p&gt;

&lt;p&gt;Let's mount the share explicitly by running:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mount -a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should see the sample &lt;code&gt;README&lt;/code&gt; file we created earlier in &lt;code&gt;/mnt/storage&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Android Client
&lt;/h2&gt;

&lt;p&gt;There're many Android tools to choose from. Pretty much everything that supports SMB shares should work. In the past I was using ES File Explorer [6], but I found the ads too obstructive, so I switched to File Manager [7]&lt;/p&gt;

&lt;p&gt;Here's a quick setup overview of ES File Explorer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select &lt;code&gt;Network&lt;/code&gt; / &lt;code&gt;LAN&lt;/code&gt; from the app menu and then add a new connection with &lt;code&gt;New&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Type in the server name or IP address of your RPi device.&lt;/li&gt;
&lt;li&gt;Type in the &lt;code&gt;storeuser&lt;/code&gt; username and password.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwwk5gw1du1fost97yaal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwwk5gw1du1fost97yaal.png" alt="ES File Explorer" width="620" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should now be able to access your Samba share with Read/Write permissions.&lt;/p&gt;

&lt;p&gt;Setting up File Manager is pretty easy as well. Just use &lt;code&gt;Network place&lt;/code&gt; from the menu to select your share and type in the credentials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fumg5yaupl2f7mf7t3zdx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fumg5yaupl2f7mf7t3zdx.png" alt="File Manager" width="540" height="960"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;iOS Client
&lt;/h2&gt;

&lt;p&gt;I'm using File Explorer Free [7] to access Samba shares from iOS devices. The free version is limited to just one share, which is good enough for me.&lt;/p&gt;

&lt;p&gt;Configuration is quite easy. Add a new &lt;code&gt;Linux/Unix&lt;/code&gt; share and configure the sever name or IP address of your RPi device.&lt;/p&gt;

&lt;p&gt;Type in the &lt;code&gt;storeuser&lt;/code&gt; username and password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0fnrtv4ty6kgfkmu6w0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0fnrtv4ty6kgfkmu6w0.jpg" alt="File Explorer Free" width="320" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should now be able to access your Samba share with Read/Write permissions.&lt;/p&gt;

&lt;p&gt;Do you know any other good tools? Free or paid. Drop a comment below.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;References
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.raspberrypi.org" rel="noopener noreferrer"&gt;raspberrypi.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.raspberrypi.org/downloads/raspbian" rel="noopener noreferrer"&gt;raspberrypi.org/downloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.raspberrypi.org/documentation/installation/installing-images/README.md" rel="noopener noreferrer"&gt;raspberrypi.org/documentation/installation/installing-images/README.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Sticky_bit" rel="noopener noreferrer"&gt;en.wikipedia.org/wiki/Sticky_bit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://codingberg.com/linux/systemd_when_to_use_netdev_mount_option" rel="noopener noreferrer"&gt;codingberg.com/linux/systemd_when_to_use_netdev_mount_option&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://play.google.com/store/apps/details?id=com.estrongs.android.pop" rel="noopener noreferrer"&gt;ES File Explorer&lt;/a&gt; for Android&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://play.google.com/store/apps/details?id=com.asus.filemanager" rel="noopener noreferrer"&gt;File Manager&lt;/a&gt; for Android&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://itunes.apple.com/us/app/fileexplorer-free/id510282524?mt=8" rel="noopener noreferrer"&gt;File Explorer Free&lt;/a&gt; for iOS&lt;/li&gt;
&lt;li&gt;Samba suite config file ref - &lt;a href="https://www.samba.org/samba/docs/man/manpages-3/smb.conf.5.html" rel="noopener noreferrer"&gt;samba.org/samba/docs/man/manpages-3/smb.conf.5.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Arch Linux bud? Samba Wiki - &lt;a href="https://wiki.archlinux.org/index.php/samba" rel="noopener noreferrer"&gt;wiki.archlinux.org/index.php/samba&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>raspberrypi</category>
      <category>linux</category>
      <category>samba</category>
      <category>shell</category>
    </item>
    <item>
      <title>Store Encrypted Files in Google Drive</title>
      <dc:creator>Petar G. Petrov</dc:creator>
      <pubDate>Wed, 17 Oct 2018 17:25:36 +0000</pubDate>
      <link>https://forem.com/petarov/store-encrypted-files-in-google-drive-3d62</link>
      <guid>https://forem.com/petarov/store-encrypted-files-in-google-drive-3d62</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.xkcd.com/1269/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimgs.xkcd.com%2Fcomics%2Fprivacy_opinions.png" alt="XKCD Opinions on Privacy"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Intro&lt;/li&gt;
&lt;li&gt;Goal&lt;/li&gt;
&lt;li&gt;
Requirements

&lt;ul&gt;
&lt;li&gt;Generate GnuPG Keys&lt;/li&gt;
&lt;li&gt;Manage GnuPG Keys&lt;/li&gt;
&lt;li&gt;Setup Sync Folder&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Usage

&lt;ul&gt;
&lt;li&gt;Linux and macOS&lt;/li&gt;
&lt;li&gt;Windows&lt;/li&gt;
&lt;li&gt;Decrypt Files&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;Annex - Metadata&lt;/li&gt;

&lt;li&gt;

References
&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Intro
&lt;/h1&gt;

&lt;p&gt;By now I reckon you already know that Google Drive offers the astounding 15 GB of storage for free [1]. Microsoft's OneDrive offers just about 5 GB [2] for free and Dropbox, one of the pioneers in the field, about 2 GB [3]. Dropbox are kind of lagging behind and out of all three Google Drive seems the clear winner here in terms of offering a greater amount of storage for no cost at all. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;(The amounts of storage described are from October 2018.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Needless to say, free online storage is a great way to store your bulk of &lt;em&gt;important&lt;/em&gt; data - documents, backups and maybe your large collection of pop music mp3s you downloaded back in the 90s. Don't look at me like that! When it comes to music preferences, we all have sinned. Now, if you are like me, keeping sensitive documents, data backups or SQL exports &lt;em&gt;unprotected&lt;/em&gt; in the cloud does not really seem much comfortable. Google do offer extended security by using 2-Step verification process [4], which makes it quite hard for your account to get compromised. We must, however, add that the human factor [5] in security cannot be discarded.&lt;/p&gt;

&lt;p&gt;Also, call me paranoid, but the following text in Google's Terms of Service [6] leaves me with a feeling of unease:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Our automated systems analyze your content (including emails) to provide you personally relevant product features, such as customized search results, tailored advertising, and spam and malware detection. This analysis occurs as the content is sent, received, and when it is stored.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;See? My paranoia is a tad more justified now. So the question is can we store our data on Google Drive in an encrypted manner - and also how hard would be to automate that.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Goal
&lt;/h1&gt;

&lt;p&gt;Alright, case in point - our goal here is to somehow establish a process for server or desktop located files to be encrypted and uploaded to a Google Drive folder. The synchronization will be &lt;strong&gt;one way only&lt;/strong&gt;, meaning files will only be uploaded from the server or desktop machine to a designated Google Drive folder. &lt;/p&gt;

&lt;p&gt;We needed a way to collect, encrypt and synchronize files. Collecting files may be realized via shell scripts like Bash on Un*x or Powershell on Windows. &lt;/p&gt;

&lt;p&gt;To encrypt our &lt;em&gt;precious&lt;/em&gt; data we'll use GnuPG [7], either version 1.4 or 2.1. It should be downloaded using your operating system's package manager, e.g., Aptitute or Brew, or alternatively from &lt;a href="https://www.gnupg.org/download/index.en.html" rel="noopener noreferrer"&gt;GnuPG's website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For synchronization, there's gdrive [8], short for Google Drive CLI Client. I dig that short name. What's up G! It's written in &lt;em&gt;Go&lt;/em&gt; and it's open source. It comes with pre-built &lt;a href="https://github.com/prasmussen/gdrive#downloads" rel="noopener noreferrer"&gt;binaries&lt;/a&gt; for an impressive range of platforms.&lt;/p&gt;

&lt;p&gt;Oh, and here's a diagram of the intended workflow that I drew earlier, to make this article seem less boring. Behold!&lt;/p&gt;

&lt;p&gt;&lt;a href="http://i.imgur.com/UUweMo1.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fi.imgur.com%2FUUweMo1.png" alt="Action Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Requirements
&lt;/h1&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Generate GnuPG Keys
&lt;/h2&gt;

&lt;p&gt;If you already have a GnuPG key pair that you want to use for encryption, you may skip this step completely. But you really shouldn't, because there're a bunch of lame jokes ahead.&lt;/p&gt;

&lt;p&gt;Now, I'm going to first start with a fair warning that dealing with GnuPG could, at times, be somewhat irritating. Cryptography is generally complex and unfortunately using GPG could be a bit cumbersome, but we are going to do a minimal set of steps, so it should all be fine. Take a deep breath. Ready?&lt;/p&gt;

&lt;p&gt;First, you need a place to store your GPG key rings. A key ring contains one or more public or private keys. By default, this would be a place in your home dir, i.e., &lt;code&gt;/home/user/.gnupg&lt;/code&gt;. I recommend using another, separate directory for this setup. This way we can only store the synchronization keys there.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd /var/local
$ mkdir mygpgkeys &amp;amp;&amp;amp; chmod 700 mygpgkeys
$ export GNUPGHOME="/var/local/mygpgkeys"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Pull the curtains down and put your dark shades on, we're generating the GnuPG key pair next.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd mygpgkeys
$ gpg --default-new-key-algo rsa4096 --gen-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;By default gpg generates 2048 bits keys, so we add an extra parameter to initiate a 4096 bit key generation. Fill in a name and an Email address. Although recommended, the Email address does not need to be a real one. You should then be asked about a password to secure your private key. Needless to say, choose a good, long password. Yeah, your cat's name doesn't count unless it's a concat of a bunch of Lovecraft's character names. It might take a while for your key to get generated depending on the available entropy on your system [9].&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gpg &lt;span class="o"&gt;(&lt;/span&gt;GnuPG&lt;span class="o"&gt;)&lt;/span&gt; 2.1.21&lt;span class="p"&gt;;&lt;/span&gt; Copyright &lt;span class="o"&gt;(&lt;/span&gt;C&lt;span class="o"&gt;)&lt;/span&gt; 2017 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: keybox &lt;span class="s1"&gt;'/var/local/mygpgkeys/pubring.kbx'&lt;/span&gt; created
Note: Use &lt;span class="s2"&gt;"gpg2 --full-generate-key"&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;a full featured key generation dialog.

GnuPG needs to construct a user ID to identify your key.

Real name: Max Mustermann
Email address: maxmustermann@example.org
You selected this USER-ID:              
    &lt;span class="s2"&gt;"Max Mustermann &amp;lt;maxmustermann@example.org&amp;gt;"&lt;/span&gt;

Change &lt;span class="o"&gt;(&lt;/span&gt;N&lt;span class="o"&gt;)&lt;/span&gt;ame, &lt;span class="o"&gt;(&lt;/span&gt;E&lt;span class="o"&gt;)&lt;/span&gt;mail, or &lt;span class="o"&gt;(&lt;/span&gt;O&lt;span class="o"&gt;)&lt;/span&gt;kay/&lt;span class="o"&gt;(&lt;/span&gt;Q&lt;span class="o"&gt;)&lt;/span&gt;uit? o
...
gpg: /var/local/mygpgkeys/trustdb.gpg: trustdb created
gpg: key 1A2B04F0E994DAF4 marked as ultimately trusted
gpg: directory &lt;span class="s1"&gt;'/var/local/mygpgkeys/openpgp-revocs.d'&lt;/span&gt; created
gpg: revocation certificate stored as &lt;span class="s1"&gt;'/var/local/mygpgkeys/openpgp-revocs.d/31208B5D45320FAE3D7E7FE21A2B04F0E994DAF4.rev'&lt;/span&gt;
public and secret key created and signed.

pub   rsa2048 2017-05-25 &lt;span class="o"&gt;[&lt;/span&gt;SC] &lt;span class="o"&gt;[&lt;/span&gt;expires: 2019-05-25]
        31208B5D45320FAE3D7E7FE21A2B04F0E994DAF4
        31208B5D45320FAE3D7E7FE21A2B04F0E994DAF4
uid                      Max Mustermann &amp;lt;maxmustermann@example.org&amp;gt;
sub   rsa2048 2017-05-25 &lt;span class="o"&gt;[&lt;/span&gt;E] &lt;span class="o"&gt;[&lt;/span&gt;expires: 2019-05-25]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GnuPG will generate a public and a secret key ring files named &lt;code&gt;pubring.gpg&lt;/code&gt; and &lt;code&gt;secring.gpg&lt;/code&gt;. (The file names may differ on macOS! Thanks Tim Cook.) You can get a glimpse on the available keys in your GPG key ring by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--list-keys&lt;/span&gt;

&lt;span class="nt"&gt;-------------------------------------------------------------------&lt;/span&gt;
pub   rsa2048 2017-05-25 &lt;span class="o"&gt;[&lt;/span&gt;SC] &lt;span class="o"&gt;[&lt;/span&gt;expires: 2019-05-25]
        31208B5D45320FAE3D7E7FE21A2B04F0E994DAF4
uid           &lt;span class="o"&gt;[&lt;/span&gt;ultimate] Max Mustermann &amp;lt;maxmustermann@example.org&amp;gt;
sub   rsa2048 2017-05-25 &lt;span class="o"&gt;[&lt;/span&gt;E] &lt;span class="o"&gt;[&lt;/span&gt;expires: 2019-05-25]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We got our key pair, so let's board the synchronization train next. Oh, and by the way, you may now take your shades off.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Manage GnuPG Keys
&lt;/h2&gt;

&lt;p&gt;I'll have to resort to bold measures for this one, so here goes - &lt;strong&gt;Do NOT lose your GPG key pair&lt;/strong&gt;! I'm sure you've thought about that already, but take good care of the key you've just generated. You won't be able to decrypt your data, if you lose it!&lt;/p&gt;

&lt;p&gt;A poor man solution could be to backup the keys on a dedicated flash drive and hide that under your pillow. If you're less of a paranoid, then you can put your keys in a certificate manager like &lt;code&gt;Kleopatra&lt;/code&gt; [10]. Thunderbird users can just install and use the &lt;code&gt;Enigmail&lt;/code&gt; [11] extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Setup Sync Folder
&lt;/h2&gt;

&lt;p&gt;The first requirement is a Google Drive folder where you'll be uploading your encrypted files to. Notice that the unique id of the folder is given in the browser url.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://i.imgur.com/kDTA2an.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fi.imgur.com%2FkDTA2an.png" alt="Sync Folder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, you need to initialize gdrive and configure a sync folder on the hard drive.&lt;/p&gt;

&lt;p&gt;To create a sync folder run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir mysyncfolder &amp;amp;&amp;amp; cd mysyncfolder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To initialize gdrive run the command below using the folder id you got from Google Drive. You'll need to manually copy and paste the authorization url in your browser and follow the steps to authorize gdrive to upload files on your behalf.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gdrive &lt;span class="nb"&gt;sync &lt;/span&gt;upload &lt;span class="nb"&gt;.&lt;/span&gt; 0BxNabiLkX8lpSGN3UVRmWEVQWWM &lt;span class="nt"&gt;-c&lt;/span&gt; /Users/max/.gdrive-test-sync

Authentication needed
Go to the following url &lt;span class="k"&gt;in &lt;/span&gt;your browser:
https://accounts.google.com/o/oauth2/auth?access_type&lt;span class="o"&gt;=&lt;/span&gt;offline&amp;amp;client_id&lt;span class="o"&gt;=&lt;/span&gt;someverylongstringeg.apps.googleusercontent.com&amp;amp;redirect_uri&lt;span class="o"&gt;=&lt;/span&gt;...

Enter verification code: 7/TWTDPBq32xYiRqqAlWp4tkcVBYgL3n5qsD_bfzXr8B0
Starting sync...
Collecting &lt;span class="nb"&gt;local &lt;/span&gt;and remote file information...
Found 0 &lt;span class="nb"&gt;local &lt;/span&gt;files and 0 remote files
Sync finished &lt;span class="k"&gt;in &lt;/span&gt;2.095311029s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the gdrive access token parameters are saved to a manually specified path, i.e., in &lt;code&gt;/Users/max/.gdrive-test-sync&lt;/code&gt;. This should normally be a location that only your user is allowed to access. &lt;em&gt;chmod 600&lt;/em&gt; anyone?&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Usage
&lt;/h1&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Linux and macOS
&lt;/h2&gt;

&lt;p&gt;Let's write a Bash script that will encrypt all files from a given folder on the hard drive and then upload them to a specified Google Drive folder. We'll call it &lt;code&gt;sync.sh&lt;/code&gt;, because I'm out of original ideas.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch sync.sh &amp;amp;&amp;amp; chmod +x sync.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Open the file in an editor and add the following. Check the comments to adjust the source and destination paths where needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nv"&gt;GPG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;which gpg&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;GDRIVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;which gdrive&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# checks if the required tools are available &lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"XY&lt;/span&gt;&lt;span class="nv"&gt;$GPG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"XY"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"GPG not found!"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"XY&lt;/span&gt;&lt;span class="nv"&gt;$GDRIVE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"XY"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gdrive not found!"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi&lt;/span&gt;


&lt;span class="c"&gt;####################################&lt;/span&gt;
&lt;span class="c"&gt;## configuraitons&lt;/span&gt;
&lt;span class="c"&gt;####################################&lt;/span&gt;

&lt;span class="c"&gt;##### !IMPORTANT! #####&lt;/span&gt;
&lt;span class="c"&gt;## Make sure to adjust the paths to your system&lt;/span&gt;
&lt;span class="c"&gt;#######################&lt;/span&gt;

&lt;span class="c"&gt;# key to encrypt with (the one we generated earlier)&lt;/span&gt;
&lt;span class="nv"&gt;GPG_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"maxmustermann@example.org"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GNUPGHOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/local/mygpgkeys"&lt;/span&gt;
&lt;span class="c"&gt;# destination folder id in gdrive&lt;/span&gt;
&lt;span class="nv"&gt;GDRIVE_DEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0BxNabiLkX8lpSGN3UVRmWEVQWWM"&lt;/span&gt;
&lt;span class="c"&gt;# local sync folder path&lt;/span&gt;
&lt;span class="nv"&gt;GDRIVE_SYNC_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/local/mysyncfolder"&lt;/span&gt;
&lt;span class="c"&gt;# gdrive config&lt;/span&gt;
&lt;span class="nv"&gt;GDRIVE_CONFIG_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/Users/max/.gdrive-test-sync"&lt;/span&gt;

&lt;span class="c"&gt;####################################&lt;/span&gt;
&lt;span class="c"&gt;## encrypt source files&lt;/span&gt;
&lt;span class="c"&gt;####################################&lt;/span&gt;
&lt;span class="nb"&gt;echo

&lt;/span&gt;&lt;span class="nv"&gt;FOLDER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/&amp;lt;PATH-TO-MY-IMPORTANT-FILES-OR-BACKUPS&amp;gt;"&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$FOLDER&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Encrypting &lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt; ..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo &lt;/span&gt;y | &lt;span class="nv"&gt;$GPG&lt;/span&gt; &lt;span class="nt"&gt;--recipient&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GPG_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt;
    &lt;span class="nv"&gt;GPG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;.gpg"&lt;/span&gt;

    &lt;span class="nv"&gt;BASE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="nv"&gt;$GPG_FILE&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Moving to &lt;/span&gt;&lt;span class="nv"&gt;$GDRIVE_SYNC_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$BASE_NAME&lt;/span&gt;&lt;span class="s2"&gt; ..."&lt;/span&gt;
    &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="nv"&gt;$GPG_FILE&lt;/span&gt; &lt;span class="nv"&gt;$GDRIVE_SYNC_DIR&lt;/span&gt;/&lt;span class="nv"&gt;$BASE_NAME&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;## ...you may add more folders here&lt;/span&gt;

&lt;span class="c"&gt;####################################&lt;/span&gt;
&lt;span class="c"&gt;## sync everything&lt;/span&gt;
&lt;span class="c"&gt;####################################&lt;/span&gt;

&lt;span class="nb"&gt;echo
echo&lt;/span&gt; &lt;span class="s2"&gt;"Sync files with Google Drive ..."&lt;/span&gt;

&lt;span class="nv"&gt;$GDRIVE&lt;/span&gt; &lt;span class="nb"&gt;sync &lt;/span&gt;upload &lt;span class="nv"&gt;$GDRIVE_SYNC_DIR&lt;/span&gt; &lt;span class="nv"&gt;$GDRIVE_DEST&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;$GDRIVE_CONFIG_PATH&lt;/span&gt;

&lt;span class="c"&gt;####################################&lt;/span&gt;
&lt;span class="c"&gt;## cleanup&lt;/span&gt;
&lt;span class="c"&gt;####################################&lt;/span&gt;

&lt;span class="nb"&gt;echo
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"XZ&lt;/span&gt;&lt;span class="nv"&gt;$GDRIVE_SYNC_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"XZ"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing encrypted files from &lt;/span&gt;&lt;span class="nv"&gt;$GDRIVE_SYNC_DIR&lt;/span&gt;&lt;span class="s2"&gt; ..."&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;$GDRIVE_SYNC_DIR&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this is a very simple script that may be further extended. For example, traversing a folder tree or producing and encrypting a &lt;code&gt;tar&lt;/code&gt; archive composed of many small sized files. I leave this up to you to adjust as needed.&lt;/p&gt;

&lt;p&gt;Alright, let's run the &lt;code&gt;sync.sh&lt;/code&gt; script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./sync.sh 

Encrypting LICENSE ...
Moving to /var/local/mysyncfolder/LICENSE.gpg ...
Encrypting Makefile ...
Moving to /var/local/mysyncfolder/Makefile.gpg ...
Encrypting MobileDevice.h ...
Moving to /var/local/mysyncfolder/MobileDevice.h.gpg ...
Encrypting README.md ...
Moving to /var/local/mysyncfolder/README.md.gpg ...

Sync files with Google Drive ...
Starting sync...
Collecting &lt;span class="nb"&gt;local &lt;/span&gt;and remote file information...
Found 4 &lt;span class="nb"&gt;local &lt;/span&gt;files and 4 remote files

4 &lt;span class="nb"&gt;local &lt;/span&gt;files has changed
&lt;span class="o"&gt;[&lt;/span&gt;0001/0006] Updating LICENSE.gpg -&amp;gt; TestSync/LICENSE.gpg
&lt;span class="o"&gt;[&lt;/span&gt;0002/0006] Updating Makefile.gpg -&amp;gt; TestSync/Makefile.gpg
&lt;span class="o"&gt;[&lt;/span&gt;0003/0006] Updating MobileDevice.h.gpg -&amp;gt; TestSync/MobileDevice.h.gpg
&lt;span class="o"&gt;[&lt;/span&gt;0004/0006] Updating README.md.gpg -&amp;gt; TestSync/README.md.gpg
Sync finished &lt;span class="k"&gt;in &lt;/span&gt;7.074881652s

Removing encrypted files from /var/local/mysyncfolder ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you now take a look in Google Drive, you'll find all the encrypted files placed in the target folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://i.imgur.com/cvgiMsv.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fi.imgur.com%2FcvgiMsv.png" alt="Synchronized files"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what if you need this done on regular basis? Did I hear you say a cron job? Here's one that invokes the &lt;code&gt;sync.sh&lt;/code&gt; script at 00:30 every day:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# m h  dom mon dow   command&lt;/span&gt;
30 0 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /var/local/sync.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/sync.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Windows
&lt;/h2&gt;

&lt;p&gt;The sync workflow and script on Windows systems is pretty much the same. Of course, you would need the Windows version of GnuPG - &lt;a href="https://www.gpg4win.org/get-gpg4win.html" rel="noopener noreferrer"&gt;Gpg4win&lt;/a&gt; and a Windows binary of the gdrive CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;@echo off
setlocal

REM &lt;span class="c"&gt;################################&lt;/span&gt;
REM &lt;span class="c"&gt;## configuraitons&lt;/span&gt;
REM &lt;span class="c"&gt;################################&lt;/span&gt;
SET &lt;span class="nv"&gt;RC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
SET &lt;span class="s2"&gt;"CURRENT_DIR=%cd%"&lt;/span&gt;

SET &lt;span class="s2"&gt;"GDRIVE=C:&lt;/span&gt;&lt;span class="se"&gt;\S&lt;/span&gt;&lt;span class="s2"&gt;YNC_TEST&lt;/span&gt;&lt;span class="se"&gt;\g&lt;/span&gt;&lt;span class="s2"&gt;drive-windows-x64.exe"&lt;/span&gt;

REM &lt;span class="c"&gt;# key to encrypt with (the one we generated earlier)&lt;/span&gt;
SET &lt;span class="s2"&gt;"GPG_KEY=maxmustermann@example.org"&lt;/span&gt;
SET &lt;span class="s2"&gt;"GNUPGHOME=C:&lt;/span&gt;&lt;span class="se"&gt;\S&lt;/span&gt;&lt;span class="s2"&gt;YNC_TEST&lt;/span&gt;&lt;span class="se"&gt;\m&lt;/span&gt;&lt;span class="s2"&gt;ygpgkeys"&lt;/span&gt;
REM &lt;span class="c"&gt;# destination folder id in gdrive&lt;/span&gt;
SET &lt;span class="s2"&gt;"GDRIVE_DEST=0BxNabiLkX8lpSGN3UVRmWEVQWWM"&lt;/span&gt;
REM &lt;span class="c"&gt;# local sync folder path&lt;/span&gt;
SET &lt;span class="s2"&gt;"GDRIVE_SYNC_DIR=C:&lt;/span&gt;&lt;span class="se"&gt;\S&lt;/span&gt;&lt;span class="s2"&gt;YNC_TEST&lt;/span&gt;&lt;span class="se"&gt;\m&lt;/span&gt;&lt;span class="s2"&gt;ysyncfolder"&lt;/span&gt;
REM &lt;span class="c"&gt;# gdrive config&lt;/span&gt;
SET &lt;span class="s2"&gt;"GDRIVE_CONFIG_PATH=C:&lt;/span&gt;&lt;span class="se"&gt;\U&lt;/span&gt;&lt;span class="s2"&gt;sers&lt;/span&gt;&lt;span class="se"&gt;\&amp;lt;&lt;/span&gt;&lt;span class="s2"&gt;MY_USERNAME&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\A&lt;/span&gt;&lt;span class="s2"&gt;ppData&lt;/span&gt;&lt;span class="se"&gt;\L&lt;/span&gt;&lt;span class="s2"&gt;ocal&lt;/span&gt;&lt;span class="se"&gt;\g&lt;/span&gt;&lt;span class="s2"&gt;drive-test"&lt;/span&gt;

REM &lt;span class="c"&gt;####################################&lt;/span&gt;
REM &lt;span class="c"&gt;## encrypt source files&lt;/span&gt;
REM &lt;span class="c"&gt;####################################&lt;/span&gt;

SET &lt;span class="s2"&gt;"FOLDER=C:&lt;/span&gt;&lt;span class="se"&gt;\S&lt;/span&gt;&lt;span class="s2"&gt;YNC_TEST&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;ourcefiles"&lt;/span&gt;

PUSHD
CD %FOLDER%

&lt;span class="k"&gt;for&lt;/span&gt; %%f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt; 
    &lt;span class="nb"&gt;echo &lt;/span&gt;Encrypting %%f ...
    gpg &lt;span class="nt"&gt;--recipient&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%GPG_KEY%"&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; %%f
    &lt;span class="k"&gt;if &lt;/span&gt;errorlevel 1 &lt;span class="o"&gt;(&lt;/span&gt;
        goto error_encrypt
    &lt;span class="o"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;echo &lt;/span&gt;Moving %%f.gpg to %GDRIVE_SYNC_DIR% ...
    move %%f.gpg %GDRIVE_SYNC_DIR%
&lt;span class="o"&gt;)&lt;/span&gt;
POPD


REM &lt;span class="c"&gt;####################################&lt;/span&gt;
REM &lt;span class="c"&gt;## sync everything&lt;/span&gt;
REM &lt;span class="c"&gt;####################################&lt;/span&gt;

&lt;span class="nb"&gt;echo &lt;/span&gt;Sync files with Google Drive ...

PUSHD
CD %GDRIVE_SYNC_DIR%
%GDRIVE% &lt;span class="nb"&gt;sync &lt;/span&gt;upload %GDRIVE_SYNC_DIR% %GDRIVE_DEST% &lt;span class="nt"&gt;-c&lt;/span&gt; %GDRIVE_CONFIG_PATH%
POPD

REM &lt;span class="c"&gt;####################################&lt;/span&gt;
REM &lt;span class="c"&gt;## cleanup&lt;/span&gt;
REM &lt;span class="c"&gt;####################################&lt;/span&gt;
:cleanup

PUSHD
&lt;span class="nb"&gt;cd&lt;/span&gt; %GDRIVE_SYNC_DIR%
&lt;span class="nb"&gt;echo &lt;/span&gt;Removing encrypted files from %GDRIVE_SYNC_DIR% ...
del /F /Q &lt;span class="k"&gt;*&lt;/span&gt;
POPD

goto end

:error_encrypt
&lt;span class="nb"&gt;echo &lt;/span&gt;Error encrypting file.
goto end

:end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the &lt;code&gt;sync.cmd&lt;/code&gt; script would produce the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; sync.cmd
Encrypting LICENSE ...
Moving LICENSE.gpg to C:&lt;span class="se"&gt;\S&lt;/span&gt;YNC_TEST&lt;span class="se"&gt;\m&lt;/span&gt;ysyncfolder ...
        1 file&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; moved.
Encrypting Makefile ...
Moving Makefile.gpg to C:&lt;span class="se"&gt;\S&lt;/span&gt;YNC_TEST&lt;span class="se"&gt;\m&lt;/span&gt;ysyncfolder ...
        1 file&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; moved.
Encrypting MobileDevice.h ...
Moving MobileDevice.h.gpg to C:&lt;span class="se"&gt;\S&lt;/span&gt;YNC_TEST&lt;span class="se"&gt;\m&lt;/span&gt;ysyncfolder ...
        1 file&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; moved.
Encrypting README.md ...
Moving README.md.gpg to C:&lt;span class="se"&gt;\S&lt;/span&gt;YNC_TEST&lt;span class="se"&gt;\m&lt;/span&gt;ysyncfolder ...
        1 file&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; moved.
Sync files with Google Drive ...
Starting sync...
Collecting &lt;span class="nb"&gt;local &lt;/span&gt;and remote file information...
Found 4 &lt;span class="nb"&gt;local &lt;/span&gt;files and 6 remote files

4 &lt;span class="nb"&gt;local &lt;/span&gt;files has changed
&lt;span class="o"&gt;[&lt;/span&gt;0001/0004] Updating LICENSE.gpg -&amp;gt; TestSync&lt;span class="se"&gt;\L&lt;/span&gt;ICENSE.gpg
&lt;span class="o"&gt;[&lt;/span&gt;0002/0004] Updating Makefile.gpg -&amp;gt; TestSync&lt;span class="se"&gt;\M&lt;/span&gt;akefile.gpg
&lt;span class="o"&gt;[&lt;/span&gt;0003/0004] Updating MobileDevice.h.gpg -&amp;gt; TestSync&lt;span class="se"&gt;\M&lt;/span&gt;obileDevice.h.gpg
&lt;span class="o"&gt;[&lt;/span&gt;0004/0004] Updating README.md.gpg -&amp;gt; TestSync&lt;span class="se"&gt;\R&lt;/span&gt;EADME.md.gpg
Sync finished &lt;span class="k"&gt;in &lt;/span&gt;4.2520023s
Removing encrypted files from C:&lt;span class="se"&gt;\S&lt;/span&gt;YNC_TEST&lt;span class="se"&gt;\m&lt;/span&gt;ysyncfolder ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the Windows Task Scheduler [12] to run the &lt;code&gt;sync.cmd&lt;/code&gt; script at a desired time or interval.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Decrypt Files
&lt;/h2&gt;

&lt;p&gt;So, the encrypt and sync workflow is ready and this is nice and all, but how does one get back their content in case they need it? Downloading the files from Google Drive is not an issue, but how about decrypting the content? Remember my all 90s mp3 file collection? Those are an important legacy I'd like my grandchildren to have.&lt;/p&gt;

&lt;p&gt;Here is a simple bash script the decrypts all encrypted &lt;code&gt;gpg&lt;/code&gt; files in a directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No input folder specified!"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;GPG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;which gpg&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"XY&lt;/span&gt;&lt;span class="nv"&gt;$GPG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"XY"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"GPG not found!"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;FOLDER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$FOLDER&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.gpg&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Decrypt &lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt; ..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;your-gpg-key-password&amp;gt;"&lt;/span&gt; | gpg &lt;span class="nt"&gt;--passphrase-fd&lt;/span&gt; 0 &lt;span class="nt"&gt;--batch&lt;/span&gt; &lt;span class="nt"&gt;--yes&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simply pass the directory path where all encrypted files reside and run the script to get all of them decrypted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-gdrive-downloaded-files &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./decrypt &lt;span class="nb"&gt;.&lt;/span&gt;

Decrypt ./LICENSE.gpg ...
gpg: encrypted with 2048-bit RSA key, ID E31A85E6729D5490, created 2017-05-25
    &lt;span class="s2"&gt;"Max Mustermann &amp;lt;maxmustermann@example.org&amp;gt;"&lt;/span&gt;
Decrypt ./Makefile.gpg ...
gpg: encrypted with 2048-bit RSA key, ID E31A85E6729D5490, created 2017-05-25
    &lt;span class="s2"&gt;"Max Mustermann &amp;lt;maxmustermann@example.org&amp;gt;"&lt;/span&gt;
Decrypt ./MobileDevice.h.gpg ...
gpg: encrypted with 2048-bit RSA key, ID E31A85E6729D5490, created 2017-05-25
    &lt;span class="s2"&gt;"Max Mustermann &amp;lt;maxmustermann@example.org&amp;gt;"&lt;/span&gt;
Decrypt ./README.md.gpg ...
gpg: encrypted with 2048-bit RSA key, ID E31A85E6729D5490, created 2017-05-25
    &lt;span class="s2"&gt;"Max Mustermann &amp;lt;maxmustermann@example.org&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, that's it. You got all your data encrypted. Sundar Pichai will never smile again.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Conclusion
&lt;/h1&gt;

&lt;p&gt;In the end, what's all this good for? (I probably should have put that question at the beginning of the article.)&lt;/p&gt;

&lt;p&gt;I personally use it to keep server backups safely stored in &lt;em&gt;the cloud&lt;/em&gt;. Mostly binary files, PDFs, archives, etc. It probably doesn't make sense to store frequently changing documents like text or Word files using this method, although there's nothing stopping you.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Annex - Metadata
&lt;/h1&gt;

&lt;p&gt;One topic that I did not consider when I was writing this article is Metadata [13]. You may have noticed that the original filenames in the scripts above are always preserved when uploaded to Google Drive. The name and size of a file may be enough information for complex algorithms to still extract quite a lot of meaningful info about what the purpose and contents of that file may be in your user context. A simple counter mechanism could be used to produce a hash value of each filename, thus at least preventing filename metadata extraction.&lt;/p&gt;

&lt;p&gt;Here is a modification of the encryption script for Linux/macOS that hashes the filenames using arbitrary salt value and produces a CSV file index of all encrypted files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# a registry of hashed files&lt;/span&gt;
&lt;span class="nv"&gt;INDEX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/local/gdrive_index.csv"&lt;/span&gt;
&lt;span class="nv"&gt;SALT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"someveryveryveryveryveryveryverylongstring"&lt;/span&gt;

&lt;span class="c"&gt;## reset index contents&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"FILENAME;HASHED NAME"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$INDEX&lt;/span&gt;

&lt;span class="c"&gt;####################################&lt;/span&gt;
&lt;span class="c"&gt;## Encrypt source files&lt;/span&gt;
&lt;span class="c"&gt;####################################&lt;/span&gt;
&lt;span class="nb"&gt;echo

&lt;/span&gt;&lt;span class="nv"&gt;FOLDER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/&amp;lt;PATH-TO-MY-IMPORTANT-FILES-OR-BACKUPS&amp;gt;"&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$FOLDER&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Encrypting &lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt; ..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo &lt;/span&gt;y | &lt;span class="nv"&gt;$GPG&lt;/span&gt; &lt;span class="nt"&gt;--recipient&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GPG_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt;

    &lt;span class="nv"&gt;BASE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;HASHED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$BASE_NAME&lt;/span&gt;.&lt;span class="nv"&gt;$SALT&lt;/span&gt; | openssl dgst &lt;span class="nt"&gt;-sha256&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE_NAME&lt;/span&gt;&lt;span class="s2"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$HASHED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$INDEX&lt;/span&gt;

    &lt;span class="nv"&gt;GPG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;.gpg"&lt;/span&gt;
    &lt;span class="nv"&gt;HASHED_GPG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HASHED&lt;/span&gt;&lt;span class="s2"&gt;.gpg"&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Moving to &lt;/span&gt;&lt;span class="nv"&gt;$GDRIVE_SYNC_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$HASHED_GPG_FILE&lt;/span&gt;&lt;span class="s2"&gt; ..."&lt;/span&gt;
    &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="nv"&gt;$GPG_FILE&lt;/span&gt; &lt;span class="nv"&gt;$GDRIVE_SYNC_DIR&lt;/span&gt;/&lt;span class="nv"&gt;$HASHED_GPG_FILE&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;References
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;GDrive Pricing Guide - &lt;a href="https://www.google.com/drive/using-drive" rel="noopener noreferrer"&gt;google.com/drive/pricing/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Microsoft OneDrive Plans - &lt;a href="https://onedrive.live.com/about/en-us/plans" rel="noopener noreferrer"&gt;onedrive.live.com/about/en-us/plans&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How much does Dropbox cost? - &lt;a href="https://www.dropbox.com/help/billing/cost" rel="noopener noreferrer"&gt;dropbox.com/help/billing/cost&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google's 2-Step Verification - &lt;a href="https://www.google.com/landing/2step" rel="noopener noreferrer"&gt;google.com/landing/2step&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The human factor is key to good security - &lt;a href="http://www.computerweekly.com/opinion/The-human-factor-is-key-to-good-security" rel="noopener noreferrer"&gt;computerweekly.com/opinion/The-human-factor-is-key-to-good-security&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google Terms of Service - &lt;a href="https://www.google.com/intl/en/policies/terms" rel="noopener noreferrer"&gt;google.com/intl/en/policies/terms&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;What’s GnuPG? - &lt;a href="https://gnupg.org/faq/gnupg-faq.html#whats_gnupg" rel="noopener noreferrer"&gt;gnupg.org/faq/gnupg-faq.html#whats_gnupg&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google Drive CLI Client - &lt;a href="https://github.com/prasmussen/gdrive" rel="noopener noreferrer"&gt;github.com/prasmussen/gdrive&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GPG does not have enough entropy - &lt;a href="https://serverfault.com/q/214605" rel="noopener noreferrer"&gt;serverfault.com/q/214605&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Kleopatra - &lt;a href="https://www.openpgp.org/software/kleopatra/" rel="noopener noreferrer"&gt;openpgp.org/software/kleopatra&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Enigmail - &lt;a href="https://www.enigmail.net/index.php/en/" rel="noopener noreferrer"&gt;enigmail.net/index.php/en&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Schedule a Task - &lt;a href="https://technet.microsoft.com/en-us/library/cc748993(v=ws.11).aspx" rel="noopener noreferrer"&gt;technet.microsoft.com/en-us/library/cc748993(v=ws.11).aspx&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Metadata - &lt;a href="https://en.wikipedia.org/wiki/Metadata" rel="noopener noreferrer"&gt;en.wikipedia.org/wiki/Metadata&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>privacy</category>
      <category>security</category>
      <category>backup</category>
      <category>cryptography</category>
    </item>
    <item>
      <title>SSH File System</title>
      <dc:creator>Petar G. Petrov</dc:creator>
      <pubDate>Tue, 09 Oct 2018 19:16:24 +0000</pubDate>
      <link>https://forem.com/petarov/ssh-file-system-43p1</link>
      <guid>https://forem.com/petarov/ssh-file-system-43p1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Dig deep!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://xkcd.com/1360" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5a9i9s7rqyc4168qdwb6.png" alt="XKCD Documents" width="381" height="773"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Get this article read to you.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="120" src="https://api.parler.io/ss/player?url=https://www.parler.io/audio/4622312860/3248c94d87d858e69a8a669e9acb19d9b380dc85.5a352dde-baa3-4ca4-b1a6-4f303847b5bc.mp3"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Goal&lt;/li&gt;
&lt;li&gt;The SSH File System&lt;/li&gt;
&lt;li&gt;User Access Workflow&lt;/li&gt;
&lt;li&gt;
Setup

&lt;ul&gt;
&lt;li&gt;Debian Linux&lt;/li&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Goal
&lt;/h1&gt;

&lt;p&gt;A couple of weeks ago I found myself setting up an e-books server that was supposed to store my ever growing &lt;em&gt;"Books I'd like to but I'll rarely find the time to read"&lt;/em&gt; collection. That imposed an interesting network problem I had to solve. I had a large external drive connected to another machine that I wanted to utilize, however, setting up a Samba server wasn't something I wanted to spend several cups of coffee on and NFS wasn't an option for me either. It occurred to me that I should be able to achieve my goal using SSH.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;The SSH File System
&lt;/h1&gt;

&lt;p&gt;First, I must say, I just love this idea. Using SSH as means to provide seamless file I/O operations and access control via asymmetric crypto is something I'll always consider from now on. The tool behind this is &lt;a href="https://github.com/libfuse/sshfs" rel="noopener noreferrer"&gt;sshfs&lt;/a&gt;. It's being frequently &lt;a href="https://github.com/libfuse/sshfs/blob/master/ChangeLog.rst" rel="noopener noreferrer"&gt;updated&lt;/a&gt; and one may be sure to find it in their package manager.&lt;/p&gt;

&lt;p&gt;However, there are a couple of things to consider.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It's expected that SSH transfers will be slower due to crypto (and of course network) operations, although &lt;strong&gt;sshfs&lt;/strong&gt; does use &lt;a href="https://github.com/libfuse/sshfs/blob/master/cache.c" rel="noopener noreferrer"&gt;caching&lt;/a&gt; and multi-threading to address this. In any case, this does not seem to be an issue for me in a local network over a wired ethernet connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sshfs&lt;/strong&gt; leads to some very interesting user management cases, but this may quickly get too complex to handle. I'd say that using this within a complex access permissions scenario could be painful.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;User Access Workflow
&lt;/h1&gt;

&lt;p&gt;Here's a simplified workflow on what happens behind the scenes.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://i.imgur.com/7YPQPBA.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb8jko36v0z1zxeh5tcbf.png" alt="SSHFS User Access" width="593" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;alice&lt;/strong&gt; on &lt;strong&gt;Server A&lt;/strong&gt; needs to access &lt;strong&gt;Server B&lt;/strong&gt;'s storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bob&lt;/strong&gt; has full access to &lt;strong&gt;Server B&lt;/strong&gt;'s storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;alice&lt;/strong&gt; generates an ssh key pair and uses that to establish an SSH connection and impersonate &lt;em&gt;Bob&lt;/em&gt; on &lt;strong&gt;Server B&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;alice&lt;/strong&gt; creates a mount point on &lt;strong&gt;Server A&lt;/strong&gt; and can induce file read/write operations on Storage via the established SSH channel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Setup
&lt;/h1&gt;

&lt;p&gt;I have prepared two setup use cases - one based on Linux that I'm currently using and another one for macOS, which I mostly did for research purposes. It is most certainly possible to use &lt;strong&gt;sshfs&lt;/strong&gt; on &lt;strong&gt;Windows&lt;/strong&gt;, however, I have not done any setup research in that direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Debian Linux
&lt;/h2&gt;

&lt;p&gt;Install &lt;strong&gt;sshfs&lt;/strong&gt; on &lt;strong&gt;Server A&lt;/strong&gt; via aptitude:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install sshfs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install FUSE (Filesystem in Userspace) via aptitude:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install fuse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You would need to load the &lt;code&gt;fuse&lt;/code&gt; module via:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;modprobe fuse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Check if the module is loaded via:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lsmod | grep fuse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;ssh-keygen&lt;/code&gt; on &lt;strong&gt;Server A&lt;/strong&gt; to generate a key pair.&lt;/p&gt;

&lt;p&gt;Copy the public key in &lt;code&gt;$HOME/.ssh/id_rsa.pub&lt;/code&gt; from &lt;strong&gt;Server A&lt;/strong&gt; and paste it to &lt;code&gt;$HOME/.ssh/authorized_keys&lt;/code&gt; on &lt;strong&gt;Server B&lt;/strong&gt;. Or just use &lt;code&gt;scp&lt;/code&gt; to copy the file over.&lt;/p&gt;

&lt;p&gt;Create a target mount point and mount it to &lt;strong&gt;Server A&lt;/strong&gt;, e.g.,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir $HOME/mountpoint
sshfs bob@server-b:/var/storage $HOME/mountpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You now have access to &lt;strong&gt;Server B&lt;/strong&gt;'s storage space. To unmount run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fusermount -u $HOME/mountpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Mount via systemd.service
&lt;/h3&gt;

&lt;p&gt;It'd be great to have this run as a service, so if &lt;strong&gt;Server A&lt;/strong&gt; reboots, the mount point gets setup automatically. For systems using &lt;strong&gt;systemd&lt;/strong&gt; we can do that the following way:&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;mount&lt;/code&gt; file in your &lt;code&gt;$HOME&lt;/code&gt; (or any other accessible) directory.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nv"&gt;CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;MNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/mountpoint"&lt;/span&gt;
&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/storage/"&lt;/span&gt;

start&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;exec &lt;/span&gt;sshfs bob@server-b:&lt;span class="nv"&gt;$TARGET&lt;/span&gt; &lt;span class="nv"&gt;$MNT&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

stop&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  fusermount &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nv"&gt;$MNT&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="k"&gt;in
  &lt;/span&gt;start|stop&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a &lt;code&gt;root&lt;/code&gt; user, create a &lt;code&gt;bob-mount.service&lt;/code&gt; file in &lt;code&gt;/lib/systemd/system&lt;/code&gt; and add the following inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Server B Mount Service
After=network.target

[Service]
User=alice
Group=alice
Type=forking
ExecStart=/home/alice/mount start
ExecStop=/home/alice/mount stop

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start and stop the service via:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl start bob-mount
systemctl stop bob-mount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Enable the service to run at boot time:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl enable bob-mount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;macOS
&lt;/h2&gt;

&lt;p&gt;Installation on macOS is pretty similar. Use &lt;strong&gt;brew&lt;/strong&gt; to install the following:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install Caskroom/cask/osxfuse
brew install Caskroom/cask/sshfs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should see &lt;code&gt;FUSE for macOS&lt;/code&gt; in your &lt;strong&gt;System Preferences&lt;/strong&gt; afterwards. To mount a share run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir $HOME/mountpoint
sshfs bob@server-b:/var/storage $HOME/mountpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To unmount just run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unmount $HOME/mountpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;An easy way to mount after reboot is to create a mount script and put it in the &lt;code&gt;crontab&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>ssh</category>
      <category>linux</category>
      <category>bash</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
