1.1 --- a/librazor/atomic-emulate.c Tue Mar 27 21:32:46 2012 +0100
1.2 +++ b/librazor/atomic-emulate.c Tue Sep 09 15:04:24 2014 +0100
1.3 @@ -1,5 +1,5 @@
1.4 /*
1.5 - * Copyright (C) 2012 J. Ali Harlow <ali@juiblex.co.uk>
1.6 + * Copyright (C) 2012, 2014 J. Ali Harlow <ali@juiblex.co.uk>
1.7 *
1.8 * This program is free software; you can redistribute it and/or modify
1.9 * it under the terms of the GNU General Public License as published by
1.10 @@ -29,6 +29,7 @@
1.11 #include <fcntl.h>
1.12 #include <dirent.h>
1.13 #include <errno.h>
1.14 +#include <unistd.h>
1.15 #include "razor-internal.h"
1.16
1.17 /*
1.18 @@ -119,21 +120,70 @@
1.19 free(atomic);
1.20 }
1.21
1.22 +#ifndef MSWIN_API
1.23 +static char *absolute_path(const char *path)
1.24 +{
1.25 + int len;
1.26 + char *result, *subpath, *p, *s, *t;
1.27 +
1.28 + result = realpath(path, NULL);
1.29 +
1.30 + if (!result && errno == ENOENT) {
1.31 + p = strdup(path);
1.32 + s = strrchr(p, '/');
1.33 +
1.34 + while (s) {
1.35 + if (s == p) {
1.36 + result = strdup("/");
1.37 + break;
1.38 + }
1.39 +
1.40 + *s = '\0';
1.41 + subpath = realpath(p, NULL);
1.42 +
1.43 + if (subpath) {
1.44 + *s = '/';
1.45 + len = strlen(subpath);
1.46 + result = malloc(len + strlen(s) + 1);
1.47 + memcpy(result, subpath, len);
1.48 + strcpy(result + len, s);
1.49 + break;
1.50 + } else if (errno != ENOENT)
1.51 + break;
1.52 +
1.53 + t = strrchr(p, '/');
1.54 + *s = '/';
1.55 + s = t;
1.56 + }
1.57 +
1.58 + if (!s)
1.59 + result = realpath(".", NULL);
1.60 +
1.61 + free(p);
1.62 + }
1.63 +
1.64 + return result;
1.65 +}
1.66 +#endif
1.67 +
1.68 /*
1.69 * We need a toplevel directory in which to hold temporary files
1.70 * before they are committed. Since we can generally assume that
1.71 - * we have write permissions anywhere on the disk in question,
1.72 - * the best location is in the relevant root directory. The most
1.73 - * common case where this assumption fails is when testing, when
1.74 - * the current directory is a good choice.
1.75 - * It might be even better to find a mount point above path instead
1.76 - * but this is hard to do, and probably not worth the effort.
1.77 + * we have write permissions anywhere on the filesystem in
1.78 + * question, the best location is at the relevant mount point.
1.79 + * The most common case where this assumption fails is when
1.80 + * testing, when the current directory is a good choice.
1.81 */
1.82
1.83 static int
1.84 razor_atomic_set_toplevel_from_path(struct razor_atomic *atomic,
1.85 const char *path)
1.86 {
1.87 +#ifndef MSWIN_API
1.88 + dev_t filesystem;
1.89 + struct stat buf;
1.90 +#endif
1.91 +
1.92 if (razor_atomic_in_error_state(atomic))
1.93 return -1;
1.94
1.95 @@ -183,7 +233,66 @@
1.96 free(buf);
1.97 }
1.98 #else
1.99 - atomic->toplevel = strdup("/.atomic-XXXXXX");
1.100 + {
1.101 + /*
1.102 + * Find the mount point (assuming we can write to the
1.103 + * whole filesystem). Otherwise stop at the first
1.104 + * unwritable directory and take one step back.
1.105 + */
1.106 + char *s, *abspath, saved;
1.107 + int len;
1.108 +
1.109 + abspath = absolute_path(path);
1.110 + if (!abspath) {
1.111 + atomic->error = razor_error_new_str(path,
1.112 + strerror(errno));
1.113 + return -1;
1.114 + }
1.115 +
1.116 + if (stat(abspath, &buf) < 0) {
1.117 + atomic->error = razor_error_new_str(abspath,
1.118 + strerror(errno));
1.119 + free(abspath);
1.120 + return -1;
1.121 + }
1.122 + filesystem = buf.st_dev;
1.123 +
1.124 + len = strlen(abspath);
1.125 + while(len > 1 && (s = strrchr(abspath, '/'))) {
1.126 + if (s == abspath) {
1.127 + saved = s[1];
1.128 + s[1] = '\0';
1.129 + len = s + 1 - abspath;
1.130 + } else {
1.131 + s[0] = '\0';
1.132 + len = s - abspath;
1.133 + }
1.134 +
1.135 + if (stat(abspath, &buf) < 0) {
1.136 + atomic->error =
1.137 + razor_error_new_str(abspath, strerror(errno));
1.138 + free(abspath);
1.139 + return -1;
1.140 + }
1.141 +
1.142 + if (buf.st_dev != filesystem || access(abspath, W_OK)) {
1.143 + if (s == abspath)
1.144 + s[1] = saved;
1.145 + else
1.146 + s[0] = '/';
1.147 + len = strlen(abspath);
1.148 + break;
1.149 + }
1.150 + }
1.151 +
1.152 + if (len == 1)
1.153 + len = 0; /* Avoid an unslightly double slash. */
1.154 + atomic->toplevel = malloc(len + strlen("/.atomic-XXXXXX") + 1);
1.155 + memcpy(atomic->toplevel, abspath, len);
1.156 + strcpy(atomic->toplevel + len, "/.atomic-XXXXXX");
1.157 +
1.158 + free(abspath);
1.159 + }
1.160 #endif
1.161
1.162 if (!mkdtemp(atomic->toplevel)) {
1.163 @@ -193,6 +302,24 @@
1.164 if (err == EACCES) {
1.165 char *s = strdup("atomic-XXXXXX");
1.166
1.167 +#ifndef MSWIN_API
1.168 + if (stat(".", &buf) < 0) {
1.169 + atomic->error =
1.170 + razor_error_new_str(".", strerror(errno));
1.171 + free(s);
1.172 + free(atomic->toplevel);
1.173 + atomic->toplevel = NULL;
1.174 + return -1;
1.175 + }
1.176 + if (buf.st_dev != filesystem)
1.177 + /*
1.178 + * Don't use a different filesystem. It will
1.179 + * only fail later on (in rename) and cause
1.180 + * an unhelpful error message (EXDEV).
1.181 + */
1.182 + free(s);
1.183 + else
1.184 +#endif
1.185 if (mkdtemp(s)) {
1.186 free(atomic->toplevel);
1.187 atomic->toplevel = s;