Don't assume the current directory is writable 0.5.6
authorJ. Ali Harlow <ali@juiblex.co.uk>
Tue, 27 Mar 2012 20:32:46 +0000 (21:32 +0100)
committerJ. Ali Harlow <ali@juiblex.co.uk>
Tue, 27 Mar 2012 20:32:46 +0000 (21:32 +0100)
librazor/atomic-emulate.c

index a467626..1ba8f96 100644 (file)
@@ -77,13 +77,6 @@ RAZOR_EXPORT struct razor_atomic *razor_atomic_open(const char *description)
 
        atomic = zalloc(sizeof *atomic);
 
-       atomic->toplevel = strdup(".atomic-XXXXXX");
-       if (!mkdtemp(atomic->toplevel)) {
-               free(atomic->toplevel);
-               free(atomic);
-               return NULL;
-       }
-
        atomic->description = strdup(description);
 
        return atomic;
@@ -126,12 +119,107 @@ RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic)
        free(atomic);
 }
 
+/*
+ * We need a toplevel directory in which to hold temporary files
+ * before they are committed. Since we can generally assume that
+ * we have write permissions anywhere on the disk in question,
+ * the best location is in the relevant root directory. The most
+ * common case where this assumption fails is when testing, when
+ * the current directory is a good choice.
+ * It might be even better to find a mount point above path instead
+ * but this is hard to do, and probably not worth the effort.
+ */
+
+static int
+razor_atomic_set_toplevel_from_path(struct razor_atomic *atomic,
+                                   const char *path)
+{
+       if (razor_atomic_in_error_state(atomic))
+               return -1;
+
+       if (atomic->toplevel)
+               return 0;
+
+#ifdef MSWIN_API
+       if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
+           && strchr(path+3,'\\')) {
+               /* We have a UNC path: \\servername\sharename... */
+               const char *sharename, *root;
+               int disklen;
+
+               sharename = strchr(path+3,'\\')+1;
+               root = strchr(sharename,'\\');
+               if (root)
+                   disklen = root - path;
+               else
+                   disklen = strlen(path);
+
+               atomic->toplevel =
+                 malloc(disklen + strlen("\\atomic-XXXXXX") + 1);
+               memcpy(atomic->toplevel, path, disklen);
+               strcpy(atomic->toplevel + disklen, "\\atomic-XXXXXX");
+       } else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
+                   path[1]==':') {
+               atomic->toplevel = strdup("X:\\atomic-XXXXXX");
+               *atomic->toplevel = *path;
+       } else {
+               DWORD n;
+               wchar_t *buf;
+               char *dir;
+
+               n = GetCurrentDirectoryW(0, NULL);
+               buf = malloc(n * sizeof(wchar_t));
+
+               if (GetCurrentDirectoryW(n, buf)) {
+                       dir = razor_utf16_to_utf8(buf, n - 1);
+                       razor_atomic_set_toplevel_from_path(atomic, dir);
+
+                       free(dir);
+                       free(buf);
+                       return;
+               } else
+                       atomic->toplevel = strdup("C:\\atomic-XXXXXX");
+
+               free(buf);
+       }
+#else
+       atomic->toplevel = strdup("/.atomic-XXXXXX");
+#endif
+
+       if (!mkdtemp(atomic->toplevel)) {
+               int err = errno;
+
+#ifdef EACCES
+               if (err == EACCES) {
+                       char *s = strdup("atomic-XXXXXX");
+
+                       if (mkdtemp(s)) {
+                               free(atomic->toplevel);
+                               atomic->toplevel = s;
+                               return 0;
+                       } else
+                               free(s);
+               }
+#endif
+
+               atomic->error = razor_error_new_str(atomic->toplevel,
+                                                   strerror(err));
+
+               free(atomic->toplevel);
+               atomic->toplevel = NULL;
+       }
+
+       return !atomic->toplevel;
+}
+
 RAZOR_EXPORT int
 razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
                       const char *path)
 {
        struct atomic_action *a;
 
+       razor_atomic_set_toplevel_from_path(atomic, *root ? root : path);
+
        if (razor_atomic_in_error_state(atomic))
                return -1;
 
@@ -148,6 +236,8 @@ razor_atomic_remove(struct razor_atomic *atomic, const char *path)
 {
        struct atomic_action *a;
 
+       razor_atomic_set_toplevel_from_path(atomic, path);
+
        if (razor_atomic_in_error_state(atomic))
                return -1;
 
@@ -164,6 +254,8 @@ razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
 {
        struct atomic_action *a;
 
+       razor_atomic_set_toplevel_from_path(atomic, newpath);
+
        if (razor_atomic_in_error_state(atomic))
                return -1;
 
@@ -181,6 +273,8 @@ razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
 {
        struct atomic_action *a;
 
+       razor_atomic_set_toplevel_from_path(atomic, dirname);
+
        if (razor_atomic_in_error_state(atomic))
                return -1;
 
@@ -198,6 +292,8 @@ razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
 {
 #if HAVE_SYMLINK
        struct atomic_action *a;
+
+       razor_atomic_set_toplevel_from_path(atomic, path);
 #endif
 
        if (razor_atomic_in_error_state(atomic))
@@ -227,6 +323,8 @@ razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
        struct atomic_action *a;
        char *tmpnam;
 
+       razor_atomic_set_toplevel_from_path(atomic, filename);
+
        if (razor_atomic_in_error_state(atomic))
                return -1;