diff -r 275a4428c13b -r 4277359896dc librazor/atomic-emulate.c --- a/librazor/atomic-emulate.c Tue Mar 27 21:32:46 2012 +0100 +++ b/librazor/atomic-emulate.c Tue Sep 09 15:04:24 2014 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 J. Ali Harlow + * Copyright (C) 2012, 2014 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,6 +29,7 @@ #include #include #include +#include #include "razor-internal.h" /* @@ -119,21 +120,70 @@ free(atomic); } +#ifndef MSWIN_API +static char *absolute_path(const char *path) +{ + int len; + char *result, *subpath, *p, *s, *t; + + result = realpath(path, NULL); + + if (!result && errno == ENOENT) { + p = strdup(path); + s = strrchr(p, '/'); + + while (s) { + if (s == p) { + result = strdup("/"); + break; + } + + *s = '\0'; + subpath = realpath(p, NULL); + + if (subpath) { + *s = '/'; + len = strlen(subpath); + result = malloc(len + strlen(s) + 1); + memcpy(result, subpath, len); + strcpy(result + len, s); + break; + } else if (errno != ENOENT) + break; + + t = strrchr(p, '/'); + *s = '/'; + s = t; + } + + if (!s) + result = realpath(".", NULL); + + free(p); + } + + return result; +} +#endif + /* * 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. + * we have write permissions anywhere on the filesystem in + * question, the best location is at the relevant mount point. + * The most common case where this assumption fails is when + * testing, when the current directory is a good choice. */ static int razor_atomic_set_toplevel_from_path(struct razor_atomic *atomic, const char *path) { +#ifndef MSWIN_API + dev_t filesystem; + struct stat buf; +#endif + if (razor_atomic_in_error_state(atomic)) return -1; @@ -183,7 +233,66 @@ free(buf); } #else - atomic->toplevel = strdup("/.atomic-XXXXXX"); + { + /* + * Find the mount point (assuming we can write to the + * whole filesystem). Otherwise stop at the first + * unwritable directory and take one step back. + */ + char *s, *abspath, saved; + int len; + + abspath = absolute_path(path); + if (!abspath) { + atomic->error = razor_error_new_str(path, + strerror(errno)); + return -1; + } + + if (stat(abspath, &buf) < 0) { + atomic->error = razor_error_new_str(abspath, + strerror(errno)); + free(abspath); + return -1; + } + filesystem = buf.st_dev; + + len = strlen(abspath); + while(len > 1 && (s = strrchr(abspath, '/'))) { + if (s == abspath) { + saved = s[1]; + s[1] = '\0'; + len = s + 1 - abspath; + } else { + s[0] = '\0'; + len = s - abspath; + } + + if (stat(abspath, &buf) < 0) { + atomic->error = + razor_error_new_str(abspath, strerror(errno)); + free(abspath); + return -1; + } + + if (buf.st_dev != filesystem || access(abspath, W_OK)) { + if (s == abspath) + s[1] = saved; + else + s[0] = '/'; + len = strlen(abspath); + break; + } + } + + if (len == 1) + len = 0; /* Avoid an unslightly double slash. */ + atomic->toplevel = malloc(len + strlen("/.atomic-XXXXXX") + 1); + memcpy(atomic->toplevel, abspath, len); + strcpy(atomic->toplevel + len, "/.atomic-XXXXXX"); + + free(abspath); + } #endif if (!mkdtemp(atomic->toplevel)) { @@ -193,6 +302,24 @@ if (err == EACCES) { char *s = strdup("atomic-XXXXXX"); +#ifndef MSWIN_API + if (stat(".", &buf) < 0) { + atomic->error = + razor_error_new_str(".", strerror(errno)); + free(s); + free(atomic->toplevel); + atomic->toplevel = NULL; + return -1; + } + if (buf.st_dev != filesystem) + /* + * Don't use a different filesystem. It will + * only fail later on (in rename) and cause + * an unhelpful error message (EXDEV). + */ + free(s); + else +#endif if (mkdtemp(s)) { free(atomic->toplevel); atomic->toplevel = s;