lockfile.c 5.6 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
			signal(SIGPIPE, remove_lock_file_on_signal);
144 145
			atexit(remove_lock_file);
		}
146
		lk->owner = getpid();
147
		if (!lk->on_list) {
148 149
			lk->next = lock_file_list;
			lock_file_list = lk;
150 151
			lk->on_list = 1;
		}
152 153 154
		if (adjust_shared_perm(lk->filename))
			return error("cannot fix permission bits on %s",
				     lk->filename);
155
	}
156 157
	else
		lk->filename[0] = 0;
158
	return lk->fd;
159 160
}

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

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

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

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

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

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

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

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

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