For an operating systems class I'm taking, I've needed to run Freebsd5.4. Since I didn't want to wrestle with trying to build git on a version of bsd released before git was released, I thought it would be neat to use git from Arch over sshfs.

At any rate I found a workaround that suggested I give sshfs the option -o workaround=rename.

It seems to make git happy, but I am rather confused as to what just happened...

All the man page says about the workaround is

fix renaming to existing file

but I am completely baffled as to what that means...

What exactly does that option do?

up vote 10 down vote accepted

sshfs uses the SSH File Transfer Protocol (SFTP). The workaround that you have enabled is working around the semantics of a rename() operation over that protocol when the "new" name already exists.

The POSIX behavior for rename() in this case is to remove the existing file and complete the rename.

In the SFTP protocol, you can rename a file with the SSH_FXP_RENAME operation; however, its behavior when the target name already exists seems to depend on the version of the protocol you are using and what flags you pass. The wikipedia page for the SFTP protocol has links to various draft RFCs for various versions of the protocol. In Draft 00 the behavior is listed as:

It is an error if there already exists a file with the name specified by newpath.

In Draft 13, the behavior is listed as

If flags does not include SSH_FXP_RENAME_OVERWRITE, and there already exists a file with the name specified by newpath, the server MUST respond with SSH_FX_FILE_ALREADY_EXISTS.

If flags includes SSH_FXP_RENAME_ATOMIC, and the destination file already exists, it is replaced in an atomic fashion. I.e., there is no observable instant in time where the name does not refer to either the old or the new file. SSH_FXP_RENAME_ATOMIC implies SSH_FXP_RENAME_OVERWRITE.

To deal with the possible failure of a rename() operation when the target name exists, sshfs provides the following workaround (if enabled):

   if (err == -EPERM && sshfs.rename_workaround) {
            size_t tolen = strlen(to);
            if (tolen + RENAME_TEMP_CHARS < PATH_MAX) {
                    int tmperr;
                    char totmp[PATH_MAX];
                    strcpy(totmp, to);
                    random_string(totmp + tolen, RENAME_TEMP_CHARS);
                    tmperr = sshfs_do_rename(to, totmp);
                    if (!tmperr) {
                            err = sshfs_do_rename(from, to);
                            if (!err)
                                    err = sshfs_unlink(totmp);
                            else
                                    sshfs_do_rename(totmp, to);
                    }
            }
    }

In this code "from" is the existing name of the file we want to rename and "to" is the new name we want. Putting some path length and error bookkeeping aside, this work around

  • Renames "to" to "totmp"
  • Renames "from" to "to"
  • Unlinks (deletes) "totmp"

This avoids the "file already exists" conflict, but also changes the semantics of the rename() operations, which is why you wouldn't want to do it by default.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.