1 /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "str.h"
6 #include "hex-binary.h"
7 #include "hostpid.h"
8 #include "file-lock.h"
9 #include "eacces-error.h"
10 #include "write-full.h"
11 #include "safe-mkstemp.h"
12 #include "nfs-workarounds.h"
13 #include "file-dotlock.h"
14 #include "sleep.h"
15 
16 #include <stdio.h>
17 #include <signal.h>
18 #include <time.h>
19 #include <utime.h>
20 #include <sys/stat.h>
21 
22 #define DEFAULT_LOCK_SUFFIX ".lock"
23 
24 /* 0.1 .. 0.2msec */
25 #define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)i_rand() % 100000)
26 /* Maximum 3 second wait between dotlock checks */
27 #define LOCK_MAX_WAIT_USECS (1000000 * 3)
28 
29 /* If the dotlock is newer than this, don't verify that the PID it contains
30    is valid (since it most likely is). */
31 #define STALE_PID_CHECK_SECS 2
32 
33 /* Maximum difference between current time and create file's ctime before
34    logging a warning. Should be less than a second in normal operation. */
35 #define MAX_TIME_DIFF 30
36 /* NFS may return a cached mtime in stat(). A later non-cached stat() may
37    return a slightly different mtime. Allow the difference to be this much
38    and still consider it to be the same mtime. */
39 #define FILE_DOTLOCK_MAX_STAT_MTIME_DIFF 1
40 
41 struct dotlock {
42 	struct dotlock_settings settings;
43 
44 	dev_t dev;
45 	ino_t ino;
46 	time_t mtime;
47 
48 	char *path;
49 	char *lock_path;
50 	int fd;
51 
52 	time_t lock_time;
53 };
54 
55 struct file_change_info {
56 	dev_t dev;
57 	ino_t ino;
58 	off_t size;
59 	time_t ctime, mtime;
60 };
61 
62 struct lock_info {
63 	const struct dotlock_settings *set;
64 	const char *path, *lock_path, *temp_path;
65 	int fd;
66 
67 	struct file_change_info lock_info;
68 	struct file_change_info file_info;
69 
70 	time_t last_pid_check;
71 	time_t last_change;
72 	unsigned int wait_usecs;
73 
74 	bool have_pid:1;
75 	bool pid_read:1;
76 	bool use_io_notify:1;
77 	bool lock_stated:1;
78 };
79 
80 static struct dotlock *
file_dotlock_alloc(const struct dotlock_settings * settings,const char * path)81 file_dotlock_alloc(const struct dotlock_settings *settings, const char *path)
82 {
83 	struct dotlock *dotlock;
84 
85 	dotlock = i_new(struct dotlock, 1);
86 	dotlock->settings = *settings;
87 	if (dotlock->settings.lock_suffix == NULL)
88 		dotlock->settings.lock_suffix = DEFAULT_LOCK_SUFFIX;
89 	dotlock->path = i_strdup(path);
90 	dotlock->fd = -1;
91 
92 	return dotlock;
93 }
94 
read_local_pid(const char * lock_path)95 static pid_t read_local_pid(const char *lock_path)
96 {
97 	char buf[512], *host;
98 	int fd;
99 	ssize_t ret;
100 	pid_t pid;
101 
102 	fd = open(lock_path, O_RDONLY);
103 	if (fd == -1)
104 		return -1; /* ignore the actual error */
105 
106 	/* read line */
107 	ret = read(fd, buf, sizeof(buf)-1);
108 	i_close_fd(&fd);
109 	if (ret <= 0)
110 		return -1;
111 
112 	/* fix the string */
113 	if (buf[ret-1] == '\n')
114 		ret--;
115 	buf[ret] = '\0';
116 
117 	/* it should contain pid:host */
118 	host = strchr(buf, ':');
119 	if (host == NULL)
120 		return -1;
121 	*host++ = '\0';
122 
123 	/* host must be ours */
124 	if (strcmp(host, my_hostname) != 0)
125 		return -1;
126 
127 	if (str_to_pid(buf, &pid) < 0)
128 		return -1;
129 	if (pid <= 0)
130 		return -1;
131 	return pid;
132 }
133 
134 static bool
update_change_info(const struct stat * st,struct file_change_info * change,time_t * last_change_r,time_t now,bool check_ctime)135 update_change_info(const struct stat *st, struct file_change_info *change,
136 		   time_t *last_change_r, time_t now, bool check_ctime)
137 {
138 	/* ctime is checked only if we're not doing NFS attribute cache
139 	   flushes. it changes them. */
140 	if (change->ino != st->st_ino || !CMP_DEV_T(change->dev, st->st_dev) ||
141 	    (change->ctime != st->st_ctime && check_ctime) ||
142 	    change->mtime != st->st_mtime || change->size != st->st_size) {
143 		time_t change_time = now;
144 
145 		if (change->ctime == 0) {
146 			/* First check, set last_change to file's change time.
147 			   Use mtime instead if it's higher, but only if it's
148 			   not higher than current time, because the mtime
149 			   can also be used for keeping metadata. */
150 			change_time = st->st_mtime <= now &&
151 				(st->st_mtime > st->st_ctime || !check_ctime) ?
152 				st->st_mtime : st->st_ctime;
153 		}
154 		if (*last_change_r < change_time)
155 			*last_change_r = change_time;
156 		change->ino = st->st_ino;
157 		change->dev = st->st_dev;
158 		change->ctime = st->st_ctime;
159 		change->mtime = st->st_mtime;
160 		change->size = st->st_size;
161 		return TRUE;
162 	}
163 	return FALSE;
164 }
165 
update_lock_info(time_t now,struct lock_info * lock_info,bool * changed_r)166 static int update_lock_info(time_t now, struct lock_info *lock_info,
167 			    bool *changed_r)
168 {
169 	struct stat st;
170 
171 	/* don't waste time flushing attribute cache the first time we're here.
172 	   if it's stale we'll get back here soon. */
173 	if (lock_info->set->nfs_flush && lock_info->lock_stated) {
174 		nfs_flush_file_handle_cache(lock_info->lock_path);
175 		nfs_flush_attr_cache_unlocked(lock_info->lock_path);
176 	}
177 
178 	lock_info->lock_stated = TRUE;
179 	if (nfs_safe_lstat(lock_info->lock_path, &st) < 0) {
180 		if (errno != ENOENT) {
181 			i_error("lstat(%s) failed: %m", lock_info->lock_path);
182 			return -1;
183 		}
184 		return 1;
185 	}
186 
187 	*changed_r = update_change_info(&st, &lock_info->lock_info,
188 					&lock_info->last_change, now,
189 					!lock_info->set->nfs_flush);
190 	return 0;
191 }
192 
dotlock_override(struct lock_info * lock_info)193 static int dotlock_override(struct lock_info *lock_info)
194 {
195 	if (i_unlink_if_exists(lock_info->lock_path) < 0)
196 		return -1;
197 
198 	/* make sure we sleep for a while after overriding the lock file.
199 	   otherwise another process might try to override it at the same time
200 	   and unlink our newly created dotlock. */
201 	if (lock_info->use_io_notify)
202 		i_sleep_usecs(LOCK_RANDOM_USLEEP_TIME);
203 	return 0;
204 }
205 
check_lock(time_t now,struct lock_info * lock_info)206 static int check_lock(time_t now, struct lock_info *lock_info)
207 {
208 	time_t stale_timeout = lock_info->set->stale_timeout;
209 	pid_t pid = -1;
210 	bool changed;
211 	int ret;
212 
213 	if ((ret = update_lock_info(now, lock_info, &changed)) != 0)
214 		return ret;
215 	if (changed || !lock_info->pid_read) {
216 		/* either our first check or someone else got the lock file.
217 		   if the dotlock was created only a couple of seconds ago,
218 		   don't bother to read its PID. */
219 		if (lock_info->lock_info.mtime >= now - STALE_PID_CHECK_SECS)
220 			lock_info->pid_read = FALSE;
221 		else {
222 			pid = read_local_pid(lock_info->lock_path);
223 			lock_info->pid_read = TRUE;
224 		}
225 		lock_info->have_pid = pid != -1;
226 	} else if (!lock_info->have_pid) {
227 		/* no pid checking */
228 	} else {
229 		if (lock_info->last_pid_check == now) {
230 			/* we just checked the pid */
231 			return 0;
232 		}
233 
234 		/* re-read the pid. even if all times and inodes are the same,
235 		   the PID in the file might have changed if lock files were
236 		   rapidly being recreated. */
237 		pid = read_local_pid(lock_info->lock_path);
238 		lock_info->have_pid = pid != -1;
239 	}
240 
241 	if (lock_info->have_pid) {
242 		/* we've local PID. Check if it exists. */
243 		if (kill(pid, 0) == 0 || errno != ESRCH) {
244 			if (pid != getpid()) {
245 				/* process exists, don't override */
246 				return 0;
247 			}
248 			/* it's us. either we're locking it again, or it's a
249 			   stale lock file with same pid than us. either way,
250 			   recreate it.. */
251 		}
252 
253 		/* doesn't exist - now check again if the dotlock was just
254 		   deleted or replaced */
255 		if ((ret = update_lock_info(now, lock_info, &changed)) != 0)
256 			return ret;
257 
258 		if (!changed) {
259 			/* still there, go ahead and override it */
260 			return dotlock_override(lock_info);
261 		}
262 		return 1;
263 	}
264 
265 	if (stale_timeout == 0) {
266 		/* no change checking */
267 		return 0;
268 	}
269 
270 	if (now > lock_info->last_change + stale_timeout) {
271 		struct stat st;
272 
273 		/* possibly stale lock file. check also the timestamp of the
274 		   file we're protecting. */
275 		if (lock_info->set->nfs_flush) {
276 			nfs_flush_file_handle_cache(lock_info->path);
277 			nfs_flush_attr_cache_maybe_locked(lock_info->path);
278 		}
279 		if (nfs_safe_stat(lock_info->path, &st) < 0) {
280 			if (errno == ENOENT) {
281 				/* file doesn't exist. treat it as if
282 				   it hasn't changed */
283 			} else {
284 				i_error("stat(%s) failed: %m", lock_info->path);
285 				return -1;
286 			}
287 		} else {
288 			(void)update_change_info(&st, &lock_info->file_info,
289 						 &lock_info->last_change, now,
290 						 !lock_info->set->nfs_flush);
291 		}
292 	}
293 
294 	if (now > lock_info->last_change + stale_timeout) {
295 		/* no changes for a while, assume stale lock */
296 		return dotlock_override(lock_info);
297 	}
298 
299 	return 0;
300 }
301 
file_write_pid(int fd,const char * path,bool nfs_flush)302 static int file_write_pid(int fd, const char *path, bool nfs_flush)
303 {
304 	const char *str;
305 
306 	/* write our pid and host, if possible */
307 	str = t_strdup_printf("%s:%s", my_pid, my_hostname);
308 	if (write_full(fd, str, strlen(str)) < 0 ||
309 	    (nfs_flush && fdatasync(fd) < 0)) {
310 		/* failed, leave it empty then */
311 		if (ftruncate(fd, 0) < 0) {
312 			i_error("ftruncate(%s) failed: %m", path);
313 			return -1;
314 		}
315 	}
316 	return 0;
317 }
318 
try_create_lock_hardlink(struct lock_info * lock_info,bool write_pid,string_t * tmp_path,time_t now)319 static int try_create_lock_hardlink(struct lock_info *lock_info, bool write_pid,
320 				    string_t *tmp_path, time_t now)
321 {
322 	const char *temp_prefix = lock_info->set->temp_prefix;
323 	const char *p;
324 	mode_t old_mask;
325 	struct stat st;
326 
327 	if (lock_info->temp_path == NULL) {
328 		/* we'll need our temp file first. */
329 		i_assert(lock_info->fd == -1);
330 
331 		p = strrchr(lock_info->lock_path, '/');
332 
333 		str_truncate(tmp_path, 0);
334 		if (temp_prefix != NULL) {
335 			if (*temp_prefix != '/' && p != NULL) {
336 				/* add directory */
337 				str_append_data(tmp_path, lock_info->lock_path,
338 						p - lock_info->lock_path);
339 				str_append_c(tmp_path, '/');
340 			}
341 			str_append(tmp_path, temp_prefix);
342 		} else {
343 			if (p != NULL) {
344 				/* add directory */
345 				str_append_data(tmp_path, lock_info->lock_path,
346 						p - lock_info->lock_path);
347 				str_append_c(tmp_path, '/');
348 			}
349 			str_printfa(tmp_path, ".temp.%s.%s.",
350 				    my_hostname, my_pid);
351 		}
352 
353 		old_mask = umask(0666);
354 		lock_info->fd = safe_mkstemp(tmp_path, 0666 ^ old_mask,
355 					     (uid_t)-1, (gid_t)-1);
356 		umask(old_mask);
357 		if (lock_info->fd == -1)
358 			return -1;
359 
360 		if (write_pid) {
361 			if (file_write_pid(lock_info->fd,
362 					   str_c(tmp_path),
363 					   lock_info->set->nfs_flush) < 0) {
364 				i_close_fd(&lock_info->fd);
365 				return -1;
366 			}
367 		}
368 
369                 lock_info->temp_path = str_c(tmp_path);
370 	} else if (fstat(lock_info->fd, &st) < 0) {
371 		i_error("fstat(%s) failed: %m", lock_info->temp_path);
372 		return -1;
373 	} else if (st.st_ctime < now) {
374 		/* we've been waiting for a while.
375 		   refresh the file's timestamp. */
376 		if (utime(lock_info->temp_path, NULL) < 0)
377 			i_error("utime(%s) failed: %m", lock_info->temp_path);
378 	}
379 
380 	if (nfs_safe_link(lock_info->temp_path,
381 			  lock_info->lock_path, TRUE) < 0) {
382 		if (errno == EEXIST)
383 			return 0;
384 
385 		if (errno != EACCES) {
386 			i_error("link(%s, %s) failed: %m",
387 				lock_info->temp_path, lock_info->lock_path);
388 		}
389 		return -1;
390 	}
391 
392 	if (i_unlink(lock_info->temp_path) < 0) {
393 		/* non-fatal, continue */
394 	}
395 	lock_info->temp_path = NULL;
396 	return 1;
397 }
398 
try_create_lock_excl(struct lock_info * lock_info,bool write_pid)399 static int try_create_lock_excl(struct lock_info *lock_info, bool write_pid)
400 {
401 	int fd;
402 
403 	fd = open(lock_info->lock_path, O_RDWR | O_EXCL | O_CREAT, 0666);
404 	if (fd == -1) {
405 		if (errno == EEXIST)
406 			return 0;
407 
408 		if (errno != ENOENT && errno != EACCES)
409 			i_error("open(%s) failed: %m", lock_info->lock_path);
410 		return -1;
411 	}
412 
413 	if (write_pid) {
414 		if (file_write_pid(fd, lock_info->lock_path,
415 				   lock_info->set->nfs_flush) < 0) {
416 			i_close_fd(&fd);
417 			return -1;
418 		}
419 	}
420 
421 	lock_info->fd = fd;
422 	return 1;
423 }
424 
dotlock_wait_end(struct ioloop * ioloop)425 static void dotlock_wait_end(struct ioloop *ioloop)
426 {
427 	io_loop_stop(ioloop);
428 }
429 
dotlock_wait(struct lock_info * lock_info)430 static void dotlock_wait(struct lock_info *lock_info)
431 {
432 	struct ioloop *ioloop;
433 	struct io *io;
434 	struct timeout *to;
435 
436 	if (!lock_info->use_io_notify) {
437 		i_sleep_usecs(lock_info->wait_usecs);
438 		return;
439 	}
440 
441 	ioloop = io_loop_create();
442 	switch (io_add_notify(lock_info->lock_path, dotlock_wait_end,
443 			      ioloop, &io)) {
444 	case IO_NOTIFY_ADDED:
445 		break;
446 	case IO_NOTIFY_NOTFOUND:
447 		/* the lock file doesn't exist anymore, don't sleep */
448 		io_loop_destroy(&ioloop);
449 		return;
450 	case IO_NOTIFY_NOSUPPORT:
451 		/* listening for files not supported */
452 		io_loop_destroy(&ioloop);
453 		lock_info->use_io_notify = FALSE;
454 		i_sleep_usecs(LOCK_RANDOM_USLEEP_TIME);
455 		return;
456 	}
457 	/* timeout after a random time even when using notify, since it
458 	   doesn't work reliably with e.g. NFS. */
459 	to = timeout_add(lock_info->wait_usecs/1000,
460 			 dotlock_wait_end, ioloop);
461 	io_loop_run(ioloop);
462 	io_remove(&io);
463 	timeout_remove(&to);
464 	io_loop_destroy(&ioloop);
465 }
466 
467 static int
dotlock_create(struct dotlock * dotlock,enum dotlock_create_flags flags,bool write_pid,const char ** lock_path_r)468 dotlock_create(struct dotlock *dotlock, enum dotlock_create_flags flags,
469 	       bool write_pid, const char **lock_path_r)
470 {
471 	const struct dotlock_settings *set = &dotlock->settings;
472 	const char *lock_path;
473 	struct lock_info lock_info;
474 	struct stat st;
475 	unsigned int stale_notify_threshold;
476 	unsigned int change_secs, wait_left;
477 	time_t now, max_wait_time, last_notify;
478 	time_t prev_last_change = 0, prev_wait_update = 0;
479 	string_t *tmp_path;
480 	int ret;
481 	bool do_wait;
482 
483 	now = time(NULL);
484 
485 	lock_path = *lock_path_r =
486 		t_strconcat(dotlock->path, set->lock_suffix, NULL);
487 	stale_notify_threshold = set->stale_timeout / 2;
488 	max_wait_time = (flags & DOTLOCK_CREATE_FLAG_NONBLOCK) != 0 ? 0 :
489 		now + set->timeout;
490 	tmp_path = t_str_new(256);
491 
492 	i_zero(&lock_info);
493 	lock_info.path = dotlock->path;
494 	lock_info.set = set;
495 	lock_info.lock_path = lock_path;
496 	lock_info.fd = -1;
497 	lock_info.use_io_notify = set->use_io_notify;
498 
499 	last_notify = 0; do_wait = FALSE;
500 
501 	file_lock_wait_start();
502 	do {
503 		if (do_wait) {
504 			if (prev_last_change != lock_info.last_change) {
505 				/* dotlock changed since last check,
506 				   reset the wait time */
507 				lock_info.wait_usecs = LOCK_RANDOM_USLEEP_TIME;
508 				prev_last_change = lock_info.last_change;
509 				prev_wait_update = now;
510 			} else if (prev_wait_update != now &&
511 				   lock_info.wait_usecs < LOCK_MAX_WAIT_USECS) {
512 				/* we've been waiting for a while now, increase
513 				   the wait time to avoid wasting CPU */
514 				prev_wait_update = now;
515 				lock_info.wait_usecs += lock_info.wait_usecs/2;
516 			}
517 			dotlock_wait(&lock_info);
518 			now = time(NULL);
519 		}
520 
521 		ret = check_lock(now, &lock_info);
522 		if (ret < 0)
523 			break;
524 
525 		if (ret == 1) {
526 			if ((flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0)
527 				break;
528 
529 			ret = set->use_excl_lock ?
530 				try_create_lock_excl(&lock_info, write_pid) :
531 				try_create_lock_hardlink(&lock_info, write_pid,
532 							 tmp_path, now);
533 			if (ret != 0) {
534 				/* if we succeeded, get the current time once
535 				   more in case disk I/O usage was really high
536 				   and it took a long time to create the lock */
537 				now = time(NULL);
538 				break;
539 			}
540 		}
541 
542 		if (last_notify != now && set->callback != NULL &&
543 		    now < max_wait_time) {
544 			last_notify = now;
545 			change_secs = now - lock_info.last_change;
546 			wait_left = max_wait_time - now;
547 
548 			if (change_secs >= stale_notify_threshold &&
549 			    change_secs <= wait_left) {
550 				unsigned int secs_left =
551 					set->stale_timeout < change_secs ?
552 					0 : set->stale_timeout - change_secs;
553 				if (!set->callback(secs_left, TRUE,
554 						   set->context)) {
555 					/* we don't want to override */
556 					lock_info.last_change = now;
557 				}
558 			} else if (wait_left > 0) {
559 				(void)set->callback(wait_left, FALSE,
560 						    set->context);
561 			}
562 		}
563 
564 		do_wait = TRUE;
565 		now = time(NULL);
566 	} while (now < max_wait_time);
567 	file_lock_wait_end(dotlock->path);
568 
569 	if (ret > 0) {
570 		i_assert(lock_info.fd != -1);
571 		if (fstat(lock_info.fd, &st) < 0) {
572 			i_error("fstat(%s) failed: %m", lock_path);
573 			ret = -1;
574 		} else {
575 			/* successful dotlock creation */
576 			dotlock->dev = st.st_dev;
577 			dotlock->ino = st.st_ino;
578 
579 			dotlock->fd = lock_info.fd;
580                         dotlock->lock_time = now;
581 			lock_info.fd = -1;
582 
583 			if (st.st_ctime + MAX_TIME_DIFF < now ||
584 			    st.st_ctime - MAX_TIME_DIFF > now) {
585 				i_warning("Created dotlock file's timestamp is "
586 					  "different than current time "
587 					  "(%s vs %s): %s", dec2str(st.st_ctime),
588 					  dec2str(now), dotlock->path);
589 			}
590 		}
591 	}
592 
593 	if (lock_info.fd != -1) {
594 		int old_errno = errno;
595 
596 		if (close(lock_info.fd) < 0)
597 			i_error("close(%s) failed: %m", lock_path);
598 		errno = old_errno;
599 	}
600 	if (lock_info.temp_path != NULL)
601 		i_unlink(lock_info.temp_path);
602 
603 	if (ret == 0)
604 		errno = EAGAIN;
605 	return ret;
606 }
607 
file_dotlock_free(struct dotlock ** _dotlock)608 static void file_dotlock_free(struct dotlock **_dotlock)
609 {
610 	struct dotlock *dotlock = *_dotlock;
611 	int old_errno;
612 
613 	*_dotlock = NULL;
614 
615 	if (dotlock->fd != -1) {
616 		old_errno = errno;
617 		if (close(dotlock->fd) < 0)
618 			i_error("close(%s) failed: %m", dotlock->path);
619 		dotlock->fd = -1;
620 		errno = old_errno;
621 	}
622 
623 	i_free(dotlock->path);
624 	i_free(dotlock->lock_path);
625 	i_free(dotlock);
626 }
627 
file_dotlock_create_real(struct dotlock * dotlock,enum dotlock_create_flags flags)628 static int file_dotlock_create_real(struct dotlock *dotlock,
629 				    enum dotlock_create_flags flags)
630 {
631 	const char *lock_path;
632 	struct stat st;
633 	int fd, ret;
634 
635 	ret = dotlock_create(dotlock, flags, TRUE, &lock_path);
636 	if (ret <= 0 || (flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0)
637 		return ret;
638 
639 	fd = dotlock->fd;
640 	dotlock->fd = -1;
641 
642 	if (close(fd) < 0) {
643 		i_error("close(%s) failed: %m", lock_path);
644 		return -1;
645 	}
646 
647 	/* With NFS the writes may have been flushed only when closing the
648 	   file. Get the mtime again after that to avoid "dotlock was modified"
649 	   errors. */
650 	if (lstat(lock_path, &st) < 0) {
651 		if (errno != ENOENT)
652 			i_error("stat(%s) failed: %m", lock_path);
653 		else {
654 			i_error("dotlock %s was immediately deleted under us",
655 				lock_path);
656 		}
657 		return -1;
658 	}
659 	/* extra sanity check won't hurt.. */
660 	if (st.st_dev != dotlock->dev || st.st_ino != dotlock->ino) {
661 		errno = ENOENT;
662 		i_error("dotlock %s was immediately recreated under us",
663 			lock_path);
664 		return -1;
665 	}
666 	dotlock->mtime = st.st_mtime;
667 	return 1;
668 }
669 
file_dotlock_create(const struct dotlock_settings * set,const char * path,enum dotlock_create_flags flags,struct dotlock ** dotlock_r)670 int file_dotlock_create(const struct dotlock_settings *set, const char *path,
671 			enum dotlock_create_flags flags,
672 			struct dotlock **dotlock_r)
673 {
674 	struct dotlock *dotlock;
675 	int ret;
676 
677 	dotlock = file_dotlock_alloc(set, path);
678 	T_BEGIN {
679 		ret = file_dotlock_create_real(dotlock, flags);
680 	} T_END;
681 	if (ret <= 0 || (flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0)
682 		file_dotlock_free(&dotlock);
683 
684 	*dotlock_r = dotlock;
685 	return ret;
686 }
687 
dotlock_replaced_warning(struct dotlock * dotlock,bool deleted)688 static void dotlock_replaced_warning(struct dotlock *dotlock, bool deleted)
689 {
690 	const char *lock_path;
691 	time_t now = time(NULL);
692 
693 	lock_path = file_dotlock_get_lock_path(dotlock);
694 	if (dotlock->mtime == dotlock->lock_time) {
695 		i_warning("Our dotlock file %s was %s "
696 			  "(locked %d secs ago, touched %d secs ago)",
697 			  lock_path, deleted ? "deleted" : "overridden",
698 			  (int)(now - dotlock->lock_time),
699 			  (int)(now - dotlock->mtime));
700 	} else {
701 		i_warning("Our dotlock file %s was %s "
702 			  "(kept it %d secs)", lock_path,
703 			  deleted ? "deleted" : "overridden",
704 			  (int)(now - dotlock->lock_time));
705 	}
706 }
707 
file_dotlock_has_mtime_changed(time_t t1,time_t t2)708 static bool file_dotlock_has_mtime_changed(time_t t1, time_t t2)
709 {
710 	time_t diff;
711 
712 	if (t1 == t2)
713 		return FALSE;
714 
715 	/* with NFS t1 may have been looked up from local cache.
716 	   allow it to be a little bit different. */
717 	diff = t1 > t2 ? t1-t2 : t2-t1;
718 	return diff > FILE_DOTLOCK_MAX_STAT_MTIME_DIFF;
719 }
720 
file_dotlock_delete(struct dotlock ** dotlock_p)721 int file_dotlock_delete(struct dotlock **dotlock_p)
722 {
723 	struct dotlock *dotlock;
724 	const char *lock_path;
725         struct stat st;
726 	int ret;
727 
728 	dotlock = *dotlock_p;
729 	*dotlock_p = NULL;
730 
731 	lock_path = file_dotlock_get_lock_path(dotlock);
732 	if (nfs_safe_lstat(lock_path, &st) < 0) {
733 		if (errno == ENOENT) {
734 			dotlock_replaced_warning(dotlock, TRUE);
735 			file_dotlock_free(&dotlock);
736 			return 0;
737 		}
738 
739 		i_error("lstat(%s) failed: %m", lock_path);
740 		file_dotlock_free(&dotlock);
741 		return -1;
742 	}
743 
744 	if (dotlock->ino != st.st_ino ||
745 	    !CMP_DEV_T(dotlock->dev, st.st_dev)) {
746 		dotlock_replaced_warning(dotlock, FALSE);
747 		errno = EEXIST;
748 		file_dotlock_free(&dotlock);
749 		return 0;
750 	}
751 
752 	if (file_dotlock_has_mtime_changed(dotlock->mtime, st.st_mtime) &&
753 	    dotlock->fd == -1) {
754 		i_warning("Our dotlock file %s was modified (%s vs %s), "
755 			  "assuming it wasn't overridden (kept it %d secs)",
756 			  lock_path,
757 			  dec2str(dotlock->mtime), dec2str(st.st_mtime),
758 			  (int)(time(NULL) - dotlock->lock_time));
759 	}
760 
761 	if ((ret = i_unlink_if_exists(lock_path)) == 0)
762 		dotlock_replaced_warning(dotlock, TRUE);
763 	file_dotlock_free(&dotlock);
764 	return ret;
765 }
766 
file_dotlock_open(const struct dotlock_settings * set,const char * path,enum dotlock_create_flags flags,struct dotlock ** dotlock_r)767 int file_dotlock_open(const struct dotlock_settings *set, const char *path,
768 		      enum dotlock_create_flags flags,
769 		      struct dotlock **dotlock_r)
770 {
771 	struct dotlock *dotlock;
772 	int ret;
773 
774 	dotlock = file_dotlock_alloc(set, path);
775 	T_BEGIN {
776 		const char *lock_path;
777 
778 		ret = dotlock_create(dotlock, flags, FALSE, &lock_path);
779 	} T_END;
780 
781 	if (ret <= 0) {
782 		file_dotlock_free(&dotlock);
783 		*dotlock_r = NULL;
784 		return -1;
785 	}
786 
787 	*dotlock_r = dotlock;
788 	return dotlock->fd;
789 }
790 
791 static int ATTR_NULL(7)
file_dotlock_open_mode_full(const struct dotlock_settings * set,const char * path,enum dotlock_create_flags flags,mode_t mode,uid_t uid,gid_t gid,const char * gid_origin,struct dotlock ** dotlock_r)792 file_dotlock_open_mode_full(const struct dotlock_settings *set, const char *path,
793 			    enum dotlock_create_flags flags,
794 			    mode_t mode, uid_t uid, gid_t gid,
795 			    const char *gid_origin, struct dotlock **dotlock_r)
796 {
797 	struct dotlock *dotlock;
798 	mode_t old_mask;
799 	int fd;
800 
801 	old_mask = umask(0666 ^ mode);
802 	fd = file_dotlock_open(set, path, flags, &dotlock);
803 	umask(old_mask);
804 
805 	if (fd != -1 && (uid != (uid_t)-1 || gid != (gid_t)-1)) {
806 		if (fchown(fd, uid, gid) < 0) {
807 			if (errno == EPERM && uid == (uid_t)-1) {
808 				i_error("%s", eperm_error_get_chgrp("fchown",
809 					file_dotlock_get_lock_path(dotlock),
810 					gid, gid_origin));
811 			} else {
812 				i_error("fchown(%s, %ld, %ld) failed: %m",
813 					file_dotlock_get_lock_path(dotlock),
814 					(long)uid, (long)gid);
815 			}
816 			file_dotlock_delete(&dotlock);
817 			return -1;
818 		}
819 	}
820 	*dotlock_r = dotlock;
821 	return fd;
822 }
823 
file_dotlock_open_mode(const struct dotlock_settings * set,const char * path,enum dotlock_create_flags flags,mode_t mode,uid_t uid,gid_t gid,struct dotlock ** dotlock_r)824 int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path,
825 			   enum dotlock_create_flags flags,
826 			   mode_t mode, uid_t uid, gid_t gid,
827 			   struct dotlock **dotlock_r)
828 {
829 	return file_dotlock_open_mode_full(set, path, flags, mode, uid, gid,
830 					   NULL, dotlock_r);
831 }
832 
file_dotlock_open_group(const struct dotlock_settings * set,const char * path,enum dotlock_create_flags flags,mode_t mode,gid_t gid,const char * gid_origin,struct dotlock ** dotlock_r)833 int file_dotlock_open_group(const struct dotlock_settings *set, const char *path,
834 			    enum dotlock_create_flags flags,
835 			    mode_t mode, gid_t gid, const char *gid_origin,
836 			    struct dotlock **dotlock_r)
837 {
838 	return file_dotlock_open_mode_full(set, path, flags, mode, (uid_t)-1,
839 					   gid, gid_origin, dotlock_r);
840 }
841 
file_dotlock_replace(struct dotlock ** dotlock_p,enum dotlock_replace_flags flags)842 int file_dotlock_replace(struct dotlock **dotlock_p,
843 			 enum dotlock_replace_flags flags)
844 {
845 	struct dotlock *dotlock;
846 	const char *lock_path;
847 	bool is_locked;
848 
849 	dotlock = *dotlock_p;
850 	*dotlock_p = NULL;
851 
852 	is_locked = (flags & DOTLOCK_REPLACE_FLAG_VERIFY_OWNER) == 0 ? TRUE :
853 		file_dotlock_is_locked(dotlock);
854 
855 	if ((flags & DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) != 0)
856 		dotlock->fd = -1;
857 
858 	if (!is_locked) {
859 		dotlock_replaced_warning(dotlock, FALSE);
860 		errno = EEXIST;
861 		file_dotlock_free(&dotlock);
862 		return 0;
863 	}
864 
865 	lock_path = file_dotlock_get_lock_path(dotlock);
866 	if (rename(lock_path, dotlock->path) < 0) {
867 		i_error("rename(%s, %s) failed: %m", lock_path, dotlock->path);
868 		if (errno == ENOENT)
869 			dotlock_replaced_warning(dotlock, TRUE);
870 		file_dotlock_free(&dotlock);
871 		return -1;
872 	}
873 	file_dotlock_free(&dotlock);
874 	return 1;
875 }
876 
file_dotlock_touch(struct dotlock * dotlock)877 int file_dotlock_touch(struct dotlock *dotlock)
878 {
879 	time_t now = time(NULL);
880 	struct utimbuf buf;
881 	int ret = 0;
882 
883 	if (dotlock->mtime == now)
884 		return 0;
885 
886 	dotlock->mtime = now;
887 	buf.actime = buf.modtime = now;
888 
889 	T_BEGIN {
890 		const char *lock_path = file_dotlock_get_lock_path(dotlock);
891 		if (utime(lock_path, &buf) < 0) {
892 			i_error("utime(%s) failed: %m", lock_path);
893 			ret = -1;
894 		}
895 	} T_END;
896 	return ret;
897 }
898 
file_dotlock_is_locked(struct dotlock * dotlock)899 bool file_dotlock_is_locked(struct dotlock *dotlock)
900 {
901 	struct stat st, st2;
902 	const char *lock_path;
903 
904 	lock_path = file_dotlock_get_lock_path(dotlock);
905 	if (fstat(dotlock->fd, &st) < 0) {
906 		i_error("fstat(%s) failed: %m", lock_path);
907 		return FALSE;
908 	}
909 
910 	if (nfs_safe_lstat(lock_path, &st2) < 0) {
911 		i_error("lstat(%s) failed: %m", lock_path);
912 		return FALSE;
913 	}
914 	return st.st_ino == st2.st_ino && CMP_DEV_T(st.st_dev, st2.st_dev);
915 }
916 
file_dotlock_get_lock_path(struct dotlock * dotlock)917 const char *file_dotlock_get_lock_path(struct dotlock *dotlock)
918 {
919 	if (dotlock->lock_path == NULL) {
920 		dotlock->lock_path =
921 			i_strconcat(dotlock->path,
922 				    dotlock->settings.lock_suffix, NULL);
923 	}
924 	return dotlock->lock_path;
925 }
926