Mercurial > dovecot > core-2.2
annotate src/lib/file-create-locked.c @ 23007:36e01285b5b8
lib: buffer - Improve header comment for buffer_insert() and buffer_delete().
author | Stephan Bosch <stephan.bosch@dovecot.fi> |
---|---|
date | Mon, 18 Mar 2019 00:52:37 +0100 |
parents | cb108f786fb4 |
children |
rev | line source |
---|---|
22713
cb108f786fb4
Updated copyright notices to include the year 2018.
Stephan Bosch <stephan.bosch@dovecot.fi>
parents:
22434
diff
changeset
|
1 /* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ |
18740 | 2 |
3 #include "lib.h" | |
4 #include "str.h" | |
5 #include "safe-mkstemp.h" | |
22272
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
6 #include "mkdir-parents.h" |
18740 | 7 #include "file-lock.h" |
8 #include "file-create-locked.h" | |
9 | |
10 #include <unistd.h> | |
11 #include <fcntl.h> | |
12 #include <sys/stat.h> | |
13 | |
22272
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
14 /* Try mkdir() + lock creation multiple times. This allows the lock file |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
15 creation to work even while the directory is simultaneously being |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
16 rmdir()ed. */ |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
17 #define MAX_MKDIR_COUNT 10 |
18740 | 18 #define MAX_RETRY_COUNT 1000 |
19 | |
20 static int | |
21 try_lock_existing(int fd, const char *path, | |
22 const struct file_create_settings *set, | |
23 struct file_lock **lock_r, const char **error_r) | |
24 { | |
25 struct stat st1, st2; | |
18743
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
26 int ret; |
18740 | 27 |
28 if (fstat(fd, &st1) < 0) { | |
29 *error_r = t_strdup_printf("fstat(%s) failed: %m", path); | |
30 return -1; | |
31 } | |
18744
57f8c2a6209b
lib: file_create_locked() lock method is now configurable
Timo Sirainen <tss@iki.fi>
parents:
18743
diff
changeset
|
32 if (file_wait_lock_error(fd, path, F_WRLCK, set->lock_method, |
18740 | 33 set->lock_timeout_secs, lock_r, error_r) <= 0) |
34 return -1; | |
35 if (stat(path, &st2) == 0) { | |
18743
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
36 ret = st1.st_ino == st2.st_ino && |
18740 | 37 CMP_DEV_T(st1.st_dev, st2.st_dev) ? 1 : 0; |
38 } else if (errno == ENOENT) { | |
18743
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
39 ret = 0; |
18740 | 40 } else { |
41 *error_r = t_strdup_printf("stat(%s) failed: %m", path); | |
18743
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
42 ret = -1; |
18740 | 43 } |
18743
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
44 if (ret <= 0) { |
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
45 /* the fd is closed next - no need to unlock */ |
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
46 file_lock_free(lock_r); |
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
47 } |
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
48 return ret; |
18740 | 49 } |
50 | |
51 static int | |
22272
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
52 try_mkdir(const char *path, const struct file_create_settings *set, |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
53 const char **error_r) |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
54 { |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
55 uid_t uid = set->mkdir_uid != 0 ? set->mkdir_uid : (uid_t)-1; |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
56 gid_t gid = set->mkdir_gid != 0 ? set->mkdir_gid : (gid_t)-1; |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
57 const char *p = strrchr(path, '/'); |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
58 if (p == NULL) |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
59 return 0; |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
60 |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
61 const char *dir = t_strdup_until(path, p); |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
62 int ret; |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
63 if (uid != (uid_t)-1) |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
64 ret = mkdir_parents_chown(dir, set->mkdir_mode, uid, gid); |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
65 else { |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
66 ret = mkdir_parents_chgrp(dir, set->mkdir_mode, |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
67 gid, set->gid_origin); |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
68 } |
22434
339d2a356620
lib: file_create_locked() - Treat mkdir() EEXIST error as success
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22272
diff
changeset
|
69 if (ret < 0 && errno != EEXIST) { |
22272
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
70 *error_r = t_strdup_printf("mkdir_parents(%s) failed: %m", dir); |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
71 return -1; |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
72 } |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
73 return 1; |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
74 } |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
75 |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
76 static int |
18740 | 77 try_create_new(const char *path, const struct file_create_settings *set, |
78 int *fd_r, struct file_lock **lock_r, const char **error_r) | |
79 { | |
80 string_t *temp_path = t_str_new(128); | |
22272
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
81 int fd, orig_errno, ret = 1; |
18740 | 82 int mode = set->mode != 0 ? set->mode : 0600; |
83 uid_t uid = set->uid != 0 ? set->uid : (uid_t)-1; | |
84 uid_t gid = set->gid != 0 ? set->gid : (gid_t)-1; | |
85 | |
86 str_append(temp_path, path); | |
22272
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
87 for (unsigned int i = 0; ret > 0; i++) { |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
88 if (uid != (uid_t)-1) |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
89 fd = safe_mkstemp(temp_path, mode, uid, gid); |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
90 else |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
91 fd = safe_mkstemp_group(temp_path, mode, gid, set->gid_origin); |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
92 if (fd != -1 || errno != ENOENT || set->mkdir_mode == 0 || |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
93 i >= MAX_MKDIR_COUNT) |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
94 break; |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
95 |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
96 int orig_errno = errno; |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
97 if ((ret = try_mkdir(path, set, error_r)) < 0) |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
98 return -1; |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
99 errno = orig_errno; |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
100 } |
18740 | 101 if (fd == -1) { |
102 *error_r = t_strdup_printf("safe_mkstemp(%s) failed: %m", path); | |
103 return -1; | |
104 } | |
22272
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
105 |
651fa80715a3
lib: file_create_locked() - Add settings to mkdir() missing parent directories
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
22268
diff
changeset
|
106 ret = -1; |
18740 | 107 if (file_try_lock_error(fd, str_c(temp_path), F_WRLCK, |
18744
57f8c2a6209b
lib: file_create_locked() lock method is now configurable
Timo Sirainen <tss@iki.fi>
parents:
18743
diff
changeset
|
108 set->lock_method, lock_r, error_r) <= 0) { |
18740 | 109 } else if (link(str_c(temp_path), path) < 0) { |
110 if (errno == EEXIST) { | |
111 /* just created by somebody else */ | |
112 ret = 0; | |
113 } else if (errno == ENOENT) { | |
20379
53ae51fb323e
lib: file_create_locked() - Unexpectedly deleted temp file is error.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
19552
diff
changeset
|
114 /* nobody should be deleting the temp file unless the |
53ae51fb323e
lib: file_create_locked() - Unexpectedly deleted temp file is error.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
19552
diff
changeset
|
115 entire directory is deleted. */ |
53ae51fb323e
lib: file_create_locked() - Unexpectedly deleted temp file is error.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
19552
diff
changeset
|
116 *error_r = t_strdup_printf( |
53ae51fb323e
lib: file_create_locked() - Unexpectedly deleted temp file is error.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
19552
diff
changeset
|
117 "Temporary file %s was unexpectedly deleted", |
53ae51fb323e
lib: file_create_locked() - Unexpectedly deleted temp file is error.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
19552
diff
changeset
|
118 str_c(temp_path)); |
18740 | 119 } else { |
120 *error_r = t_strdup_printf("link(%s, %s) failed: %m", | |
121 str_c(temp_path), path); | |
122 } | |
19145
9ce9bd7fba83
lib: file_create_locked() may have leaked memory on some race conditions.
Timo Sirainen <tss@iki.fi>
parents:
19136
diff
changeset
|
123 file_lock_free(lock_r); |
18740 | 124 } else { |
22268
382ee00c9669
lib: file_create_locked() - update lock path after link()
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21390
diff
changeset
|
125 file_lock_set_path(*lock_r, path); |
19136
fefaa6d09a81
Replaced unlink() calls with i_unlink*() wherever possible.
Timo Sirainen <tss@iki.fi>
parents:
18744
diff
changeset
|
126 i_unlink_if_exists(str_c(temp_path)); |
18740 | 127 *fd_r = fd; |
128 return 1; | |
129 } | |
130 orig_errno = errno; | |
131 i_close_fd(&fd); | |
19136
fefaa6d09a81
Replaced unlink() calls with i_unlink*() wherever possible.
Timo Sirainen <tss@iki.fi>
parents:
18744
diff
changeset
|
132 i_unlink_if_exists(str_c(temp_path)); |
18740 | 133 errno = orig_errno; |
134 return ret; | |
135 } | |
136 | |
137 int file_create_locked(const char *path, const struct file_create_settings *set, | |
138 struct file_lock **lock_r, bool *created_r, | |
139 const char **error_r) | |
140 { | |
141 unsigned int i; | |
142 int fd, ret; | |
143 | |
144 for (i = 0; i < MAX_RETRY_COUNT; i++) { | |
145 fd = open(path, O_RDWR); | |
146 if (fd != -1) { | |
147 ret = try_lock_existing(fd, path, set, lock_r, error_r); | |
148 if (ret > 0) { | |
149 /* successfully locked an existing file */ | |
150 *created_r = FALSE; | |
151 return fd; | |
152 } | |
18743
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
153 i_close_fd(&fd); |
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
154 if (ret < 0) |
1d088dc567bd
lib: file_create_locked() was leaking fds and locks.
Timo Sirainen <tss@iki.fi>
parents:
18742
diff
changeset
|
155 return -1; |
18740 | 156 } else if (errno != ENOENT) { |
157 *error_r = t_strdup_printf("open(%s) failed: %m", path); | |
158 return -1; | |
159 } else { | |
160 /* try to create the file */ | |
161 ret = try_create_new(path, set, &fd, lock_r, error_r); | |
162 if (ret < 0) | |
163 return -1; | |
164 if (ret > 0) { | |
165 /* successfully created a new locked file */ | |
166 *created_r = TRUE; | |
167 return fd; | |
168 } | |
169 /* the file was just created - try again opening and | |
170 locking it */ | |
171 } | |
172 } | |
173 *error_r = t_strdup_printf("Creating a locked file %s keeps failing", path); | |
174 errno = EINVAL; | |
175 return -1; | |
176 } |