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