fix
authorMiklos Szeredi <miklos@szeredi.hu>
Mon, 17 Oct 2005 16:01:45 +0000 (16:01 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Mon, 17 Oct 2005 16:01:45 +0000 (16:01 +0000)
ChangeLog
sshfs.c

index 0439a59..0e8db4c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,9 @@
 
        * Add one more missing lock.
 
+       * Add workaround for failure to rename to an existing file.  Based
+       on patch by Michael Best
+
 2005-10-15  Miklos Szeredi <miklos@szeredi.hu>
 
        * Protect request ID allocation with mutex.  Bug report by Tvrtko
diff --git a/sshfs.c b/sshfs.c
index d69c5f0..957112a 100644 (file)
--- a/sshfs.c
+++ b/sshfs.c
 
 #define MAX_REPLY_LEN (1 << 17)
 
+#define RENAME_TEMP_CHARS 8
+
+static unsigned int randseed;
+
 static int infd;
 static int outfd;
 static int connver;
@@ -93,6 +97,7 @@ static int debug = 0;
 static int reconnect = 0;
 static int sync_write = 0;
 static int sync_read = 0;
+static int rename_workaround = 0;
 static char *base_path;
 static char *host;
 static unsigned blksize = 4096;
@@ -200,6 +205,7 @@ enum {
     SOPT_MAX_READ,
     SOPT_DEBUG,
     SOPT_RECONNECT,
+    SOPT_RENAME_FIX,
     SOPT_LAST /* Last entry in this list! */
 };
 
@@ -211,6 +217,7 @@ static struct opt sshfs_opts[] = {
     [SOPT_MAX_READ]   = { .optname = "max_read" },
     [SOPT_DEBUG]      = { .optname = "sshfs_debug" },
     [SOPT_RECONNECT]  = { .optname = "reconnect" },
+    [SOPT_RENAME_FIX] = { .optname = "rename_workaround" },
     [SOPT_LAST]       = { .optname = NULL }
 };
 
@@ -1196,7 +1203,7 @@ static int sshfs_rmdir(const char *path)
     return err;
 }
 
-static int sshfs_rename(const char *from, const char *to)
+static int sshfs_do_rename(const char *from, const char *to)
 {
     int err;
     struct buffer buf;
@@ -1208,6 +1215,38 @@ static int sshfs_rename(const char *from, const char *to)
     return err;
 }
 
+static void random_string(char *str, int length)
+{
+    int i;
+    for (i = 0; i < length; i++)
+        *str++ = (char)('0' + rand_r(&randseed) % 10);
+    *str = '\0';
+}
+
+static int sshfs_rename(const char *from, const char *to)
+{
+    int err;
+    err = sshfs_do_rename(from, to);
+    if (err == -EPERM && 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);
+            }
+        }
+    }
+    return err;
+}
+
 static int sshfs_chmod(const char *path, mode_t mode)
 {
     int err;
@@ -1692,6 +1731,7 @@ static void usage(const char *progname)
 "    -o cache=YESNO         enable caching {yes,no} (default: yes)\n"
 "    -o cache_timeout=N     sets timeout for caches in seconds (default: 20)\n"
 "    -o cache_X_timeout=N   sets timeout for {stat,dir,link} cache\n"
+"    -o rename_workaround   work around problem renaming to existing file"
 "    -o ssh_command=CMD     execute CMD instead of 'ssh'\n"
 "    -o directport=PORT     directly connect to PORT bypassing ssh\n"
 "    -o SSHOPT=VAL          ssh options (see man ssh_config)\n"
@@ -1777,6 +1817,8 @@ int main(int argc, char *argv[])
         sync_read = 1;
     if (sshfs_opts[SOPT_DEBUG].present)
         debug = 1;
+    if (sshfs_opts[SOPT_RENAME_FIX].present)
+        rename_workaround = 1;
     if (sshfs_opts[SOPT_RECONNECT].present)
         reconnect = 1;
     if (sshfs_opts[SOPT_MAX_READ].present) {
@@ -1808,6 +1850,8 @@ int main(int argc, char *argv[])
     if (res == -1)
         exit(1);
 
+    randseed = time(0);
+
     newargv[newargc++] = g_strdup_printf("-omax_read=%u", max_read);
     newargv[newargc++] = g_strdup_printf("-ofsname=sshfs#%s", fsname);
     g_free(fsname);