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

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

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

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

static void remove_lock_file_on_signal(int signo)
{
	remove_lock_file();
27
	signal(signo, SIG_DFL);
28 29 30
	raise(signo);
}

31 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
/*
 * 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;
		}

98
		if (is_absolute_path(link)) {
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
			/* 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;
}


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

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

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

172
	fd = lock_file(lk, path, flags);
173
	if (fd < 0) {
174
		if (flags & LOCK_DIE_ON_ERROR)
175 176 177 178 179 180 181
			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) {
182
			if (flags & LOCK_DIE_ON_ERROR)
183 184 185 186 187
				die("cannot open '%s' for copying", path);
			close(fd);
			return error("cannot open '%s' for copying", path);
		}
	} else if (copy_fd(orig_fd, fd)) {
188
		if (flags & LOCK_DIE_ON_ERROR)
189 190 191 192 193 194 195
			exit(128);
		close(fd);
		return -1;
	}
	return fd;
}

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

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

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

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

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

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