librazor/atomic-emulate.c
changeset 445 aada48958b92
parent 435 275a4428c13b
child 447 0a5e583393e1
     1.1 --- a/librazor/atomic-emulate.c	Tue Mar 27 21:32:46 2012 +0100
     1.2 +++ b/librazor/atomic-emulate.c	Mon Sep 08 10:26:39 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;