lockfile.c 5.6 KB
Newer Older
1 2 3 4
/*
 * Copyright (c) 2005, Junio C Hamano
 */
#include "cache.h"
5
#include "sigchain.h"
6 7

static struct lock_file *lock_file_list;
8
static const char *alternate_index_output;
9 10 11

static void remove_lock_file(void)
{
12 13
	pid_t me = getpid();

14
	while (lock_file_list) {
15
		if (lock_file_list->owner == me &&
16
		    lock_file_list->filename[0]) {
17 18
			if (lock_file_list->fd >= 0)
				close(lock_file_list->fd);
19
			unlink(lock_file_list->filename);
20
		}
21 22 23 24 25 26 27
		lock_file_list = lock_file_list->next;
	}
}

static void remove_lock_file_on_signal(int signo)
{
	remove_lock_file();
28
	sigchain_pop(signo);
29 30 31
	raise(signo);
}

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
/*
 * p = absolute or relative path name
 *
 * Return a pointer into p showing the beginning of the last path name
 * element.  If p is empty or the root directory ("/"), just return p.
 */
static char *last_path_elm(char *p)
{
	/* r starts pointing to null at the end of the string */
	char *r = strchr(p, '\0');

	if (r == p)
		return p; /* just return empty string */

	r--; /* back up to last non-null character */

	/* back up past trailing slashes, if any */
	while (r > p && *r == '/')
		r--;

	/*
	 * then go backwards until I hit a slash, or the beginning of
	 * the string
	 */
	while (r > p && *(r-1) != '/')
		r--;
	return r;
}


/* We allow "recursive" symbolic links. Only within reason, though */
#define MAXDEPTH 5

/*
 * p = path that may be a symlink
 * s = full size of p
 *
 * If p is a symlink, attempt to overwrite p with a path to the real
 * file or directory (which may or may not exist), following a chain of
 * symlinks if necessary.  Otherwise, leave p unmodified.
 *
 * This is a best-effort routine.  If an error occurs, p will either be
 * left unmodified or will name a different symlink in a symlink chain
 * that started with p's initial contents.
 *
 * Always returns p.
 */

static char *resolve_symlink(char *p, size_t s)
{
	int depth = MAXDEPTH;

	while (depth--) {
		char link[PATH_MAX];
		int link_len = readlink(p, link, sizeof(link));
		if (link_len < 0) {
			/* not a symlink anymore */
			return p;
		}
		else if (link_len < sizeof(link))
			/* readlink() never null-terminates */
			link[link_len] = '\0';
		else {
			warning("%s: symlink too long", p);
			return p;
		}

99
		if (is_absolute_path(link)) {
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
			/* absolute path simply replaces p */
			if (link_len < s)
				strcpy(p, link);
			else {
				warning("%s: symlink too long", p);
				return p;
			}
		} else {
			/*
			 * link is a relative path, so I must replace the
			 * last element of p with it.
			 */
			char *r = (char*)last_path_elm(p);
			if (r - p + link_len < s)
				strcpy(r, link);
			else {
				warning("%s: symlink too long", p);
				return p;
			}
		}
	}
	return p;
}


125
static int lock_file(struct lock_file *lk, const char *path, int flags)
126
{
127 128
	if (strlen(path) >= sizeof(lk->filename))
		return -1;
129 130 131 132 133
	strcpy(lk->filename, path);
	/*
	 * subtract 5 from size to make sure there's room for adding
	 * ".lock" for the lock file name
	 */
134 135
	if (!(flags & LOCK_NODEREF))
		resolve_symlink(lk->filename, sizeof(lk->filename)-5);
136
	strcat(lk->filename, ".lock");
137 138
	lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
	if (0 <= lk->fd) {
139
		if (!lock_file_list) {
140 141 142 143 144
			sigchain_push(SIGINT, remove_lock_file_on_signal);
			sigchain_push(SIGHUP, remove_lock_file_on_signal);
			sigchain_push(SIGTERM, remove_lock_file_on_signal);
			sigchain_push(SIGQUIT, remove_lock_file_on_signal);
			sigchain_push(SIGPIPE, remove_lock_file_on_signal);
145 146
			atexit(remove_lock_file);
		}
147
		lk->owner = getpid();
148
		if (!lk->on_list) {
149 150
			lk->next = lock_file_list;
			lock_file_list = lk;
151 152
			lk->on_list = 1;
		}
153 154 155
		if (adjust_shared_perm(lk->filename))
			return error("cannot fix permission bits on %s",
				     lk->filename);
156
	}
157 158
	else
		lk->filename[0] = 0;
159
	return lk->fd;
160 161
}

162
int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
163
{
164 165
	int fd = lock_file(lk, path, flags);
	if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
166
		die("unable to create '%s.lock': %s", path, strerror(errno));
167 168 169
	return fd;
}

170
int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
171 172 173
{
	int fd, orig_fd;

174
	fd = lock_file(lk, path, flags);
175
	if (fd < 0) {
176
		if (flags & LOCK_DIE_ON_ERROR)
177 178 179 180 181 182 183
			die("unable to create '%s.lock': %s", path, strerror(errno));
		return fd;
	}

	orig_fd = open(path, O_RDONLY);
	if (orig_fd < 0) {
		if (errno != ENOENT) {
184
			if (flags & LOCK_DIE_ON_ERROR)
185 186 187 188 189
				die("cannot open '%s' for copying", path);
			close(fd);
			return error("cannot open '%s' for copying", path);
		}
	} else if (copy_fd(orig_fd, fd)) {
190
		if (flags & LOCK_DIE_ON_ERROR)
191 192 193 194 195 196 197
			exit(128);
		close(fd);
		return -1;
	}
	return fd;
}

198 199 200 201 202 203 204
int close_lock_file(struct lock_file *lk)
{
	int fd = lk->fd;
	lk->fd = -1;
	return close(fd);
}

205 206 207
int commit_lock_file(struct lock_file *lk)
{
	char result_file[PATH_MAX];
208 209 210
	size_t i;
	if (lk->fd >= 0 && close_lock_file(lk))
		return -1;
211 212 213
	strcpy(result_file, lk->filename);
	i = strlen(result_file) - 5; /* .lock */
	result_file[i] = 0;
214 215
	if (rename(lk->filename, result_file))
		return -1;
216
	lk->filename[0] = 0;
217
	return 0;
218 219
}

220 221
int hold_locked_index(struct lock_file *lk, int die_on_error)
{
222 223 224 225
	return hold_lock_file_for_update(lk, get_index_file(),
					 die_on_error
					 ? LOCK_DIE_ON_ERROR
					 : 0);
226 227
}

228 229 230 231 232
void set_alternate_index_output(const char *name)
{
	alternate_index_output = name;
}

233 234
int commit_locked_index(struct lock_file *lk)
{
235
	if (alternate_index_output) {
236 237 238 239
		if (lk->fd >= 0 && close_lock_file(lk))
			return -1;
		if (rename(lk->filename, alternate_index_output))
			return -1;
240
		lk->filename[0] = 0;
241
		return 0;
242 243 244 245 246
	}
	else
		return commit_lock_file(lk);
}

247 248
void rollback_lock_file(struct lock_file *lk)
{
249
	if (lk->filename[0]) {
250 251
		if (lk->fd >= 0)
			close(lk->fd);
252
		unlink(lk->filename);
253
	}
254 255
	lk->filename[0] = 0;
}