/src/lvm2/libdm/libdm-file.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
3 | | * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. |
4 | | * |
5 | | * This file is part of the device-mapper userspace tools. |
6 | | * |
7 | | * This copyrighted material is made available to anyone wishing to use, |
8 | | * modify, copy, or redistribute it subject to the terms and conditions |
9 | | * of the GNU Lesser General Public License v.2.1. |
10 | | * |
11 | | * You should have received a copy of the GNU Lesser General Public License |
12 | | * along with this program; if not, write to the Free Software Foundation, |
13 | | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
14 | | */ |
15 | | |
16 | | #include "libdm/misc/dmlib.h" |
17 | | |
18 | | #include <sys/file.h> |
19 | | #include <fcntl.h> |
20 | | #include <dirent.h> |
21 | | |
22 | | static int _is_dir(const char *path) |
23 | 0 | { |
24 | 0 | struct stat st; |
25 | |
|
26 | 0 | if (stat(path, &st) < 0) { |
27 | 0 | log_sys_error("stat", path); |
28 | 0 | return 0; |
29 | 0 | } |
30 | | |
31 | 0 | if (!S_ISDIR(st.st_mode)) { |
32 | 0 | log_error("Existing path %s is not " |
33 | 0 | "a directory.", path); |
34 | 0 | return 0; |
35 | 0 | } |
36 | | |
37 | 0 | return 1; |
38 | 0 | } |
39 | | |
40 | | static int _create_dir_recursive(const char *dir) |
41 | 0 | { |
42 | 0 | char *orig, *s; |
43 | 0 | int rc, r = 0; |
44 | |
|
45 | 0 | log_verbose("Creating directory \"%s\"", dir); |
46 | | /* Create parent directories */ |
47 | 0 | orig = s = dm_strdup(dir); |
48 | 0 | if (!s) { |
49 | 0 | log_error("Failed to duplicate directory name."); |
50 | 0 | return 0; |
51 | 0 | } |
52 | | |
53 | 0 | while ((s = strchr(s, '/')) != NULL) { |
54 | 0 | *s = '\0'; |
55 | 0 | if (*orig) { |
56 | 0 | rc = mkdir(orig, 0777); |
57 | 0 | if (rc < 0) { |
58 | 0 | if (errno == EEXIST) { |
59 | 0 | if (!_is_dir(orig)) |
60 | 0 | goto_out; |
61 | 0 | } else { |
62 | 0 | if (errno != EROFS) |
63 | 0 | log_sys_error("mkdir", orig); |
64 | 0 | goto out; |
65 | 0 | } |
66 | 0 | } |
67 | 0 | } |
68 | 0 | *s++ = '/'; |
69 | 0 | } |
70 | | |
71 | | /* Create final directory */ |
72 | 0 | rc = mkdir(dir, 0777); |
73 | 0 | if (rc < 0) { |
74 | 0 | if (errno == EEXIST) { |
75 | 0 | if (!_is_dir(dir)) |
76 | 0 | goto_out; |
77 | 0 | } else { |
78 | 0 | if (errno != EROFS) |
79 | 0 | log_sys_error("mkdir", orig); |
80 | 0 | goto out; |
81 | 0 | } |
82 | 0 | } |
83 | | |
84 | 0 | r = 1; |
85 | 0 | out: |
86 | 0 | dm_free(orig); |
87 | 0 | return r; |
88 | 0 | } |
89 | | |
90 | | int dm_create_dir(const char *dir) |
91 | 0 | { |
92 | 0 | struct stat info; |
93 | |
|
94 | 0 | if (!*dir) |
95 | 0 | return 1; |
96 | | |
97 | 0 | if (stat(dir, &info) == 0 && S_ISDIR(info.st_mode)) |
98 | 0 | return 1; |
99 | | |
100 | 0 | if (!_create_dir_recursive(dir)) |
101 | 0 | return_0; |
102 | | |
103 | 0 | return 1; |
104 | 0 | } |
105 | | |
106 | | int dm_is_empty_dir(const char *dir) |
107 | 0 | { |
108 | 0 | struct dirent *dirent; |
109 | 0 | DIR *d; |
110 | |
|
111 | 0 | if (!(d = opendir(dir))) { |
112 | 0 | log_sys_error("opendir", dir); |
113 | 0 | return 0; |
114 | 0 | } |
115 | | |
116 | 0 | while ((dirent = readdir(d))) |
117 | 0 | if (strcmp(dirent->d_name, ".") && strcmp(dirent->d_name, "..")) |
118 | 0 | break; |
119 | |
|
120 | 0 | if (closedir(d)) |
121 | 0 | log_sys_debug("closedir", dir); |
122 | |
|
123 | 0 | return dirent ? 0 : 1; |
124 | 0 | } |
125 | | |
126 | | int dm_fclose(FILE *stream) |
127 | 0 | { |
128 | 0 | int prev_fail = ferror(stream); |
129 | 0 | int fclose_fail = fclose(stream); |
130 | | |
131 | | /* If there was a previous failure, but fclose succeeded, |
132 | | clear errno, since ferror does not set it, and its value |
133 | | may be unrelated to the ferror-reported failure. */ |
134 | 0 | if (prev_fail && !fclose_fail) |
135 | 0 | errno = 0; |
136 | |
|
137 | 0 | return prev_fail || fclose_fail ? EOF : 0; |
138 | 0 | } |
139 | | |
140 | | int dm_create_lockfile(const char *lockfile) |
141 | 0 | { |
142 | 0 | int fd, value; |
143 | 0 | size_t bufferlen; |
144 | 0 | ssize_t write_out; |
145 | 0 | struct flock lock; |
146 | 0 | char buffer[50]; |
147 | 0 | int retries = 0; |
148 | |
|
149 | 0 | if ((fd = open(lockfile, O_CREAT | O_WRONLY, |
150 | 0 | (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) < 0) { |
151 | 0 | log_error("Cannot open lockfile [%s], error was [%s]", |
152 | 0 | lockfile, strerror(errno)); |
153 | 0 | return 0; |
154 | 0 | } |
155 | | |
156 | 0 | lock.l_type = F_WRLCK; |
157 | 0 | lock.l_start = 0; |
158 | 0 | lock.l_whence = SEEK_SET; |
159 | 0 | lock.l_len = 0; |
160 | 0 | retry_fcntl: |
161 | 0 | if (fcntl(fd, F_SETLK, &lock) < 0) { |
162 | 0 | switch (errno) { |
163 | 0 | case EINTR: |
164 | 0 | goto retry_fcntl; |
165 | 0 | case EACCES: |
166 | 0 | case EAGAIN: |
167 | 0 | if (retries == 20) { |
168 | 0 | log_error("Cannot lock lockfile [%s], error was [%s]", |
169 | 0 | lockfile, strerror(errno)); |
170 | 0 | break; |
171 | 0 | } else { |
172 | 0 | ++ retries; |
173 | 0 | usleep(1000); |
174 | 0 | goto retry_fcntl; |
175 | 0 | } |
176 | 0 | default: |
177 | 0 | log_error("process is already running"); |
178 | 0 | } |
179 | | |
180 | 0 | goto fail_close; |
181 | 0 | } |
182 | | |
183 | 0 | if (ftruncate(fd, 0) < 0) { |
184 | 0 | log_error("Cannot truncate pidfile [%s], error was [%s]", |
185 | 0 | lockfile, strerror(errno)); |
186 | |
|
187 | 0 | goto fail_close_unlink; |
188 | 0 | } |
189 | | |
190 | 0 | snprintf(buffer, sizeof(buffer), "%u\n", getpid()); |
191 | |
|
192 | 0 | bufferlen = strlen(buffer); |
193 | 0 | write_out = write(fd, buffer, bufferlen); |
194 | |
|
195 | 0 | if ((write_out < 0) || (write_out == 0 && errno)) { |
196 | 0 | log_error("Cannot write pid to pidfile [%s], error was [%s]", |
197 | 0 | lockfile, strerror(errno)); |
198 | |
|
199 | 0 | goto fail_close_unlink; |
200 | 0 | } |
201 | | |
202 | 0 | if ((write_out == 0) || ((size_t)write_out < bufferlen)) { |
203 | 0 | log_error("Cannot write pid to pidfile [%s], shortwrite of" |
204 | 0 | "[%" PRIsize_t "] bytes, expected [%" PRIsize_t "]\n", |
205 | 0 | lockfile, write_out, bufferlen); |
206 | |
|
207 | 0 | goto fail_close_unlink; |
208 | 0 | } |
209 | | |
210 | 0 | if ((value = fcntl(fd, F_GETFD, 0)) < 0) { |
211 | 0 | log_error("Cannot get close-on-exec flag from pidfile [%s], " |
212 | 0 | "error was [%s]", lockfile, strerror(errno)); |
213 | |
|
214 | 0 | goto fail_close_unlink; |
215 | 0 | } |
216 | 0 | value |= FD_CLOEXEC; |
217 | 0 | if (fcntl(fd, F_SETFD, value) < 0) { |
218 | 0 | log_error("Cannot set close-on-exec flag from pidfile [%s], " |
219 | 0 | "error was [%s]", lockfile, strerror(errno)); |
220 | |
|
221 | 0 | goto fail_close_unlink; |
222 | 0 | } |
223 | | |
224 | | /* coverity[leaked_handle] intentional leak of fd handle here */ |
225 | 0 | return 1; |
226 | | |
227 | 0 | fail_close_unlink: |
228 | 0 | if (unlink(lockfile)) |
229 | 0 | log_sys_debug("unlink", lockfile); |
230 | 0 | fail_close: |
231 | 0 | if (close(fd)) |
232 | 0 | log_sys_debug("close", lockfile); |
233 | |
|
234 | 0 | return 0; |
235 | 0 | } |
236 | | |
237 | | int dm_daemon_is_running(const char* lockfile) |
238 | 0 | { |
239 | 0 | int fd; |
240 | 0 | struct flock lock; |
241 | |
|
242 | 0 | if((fd = open(lockfile, O_RDONLY)) < 0) |
243 | 0 | return 0; |
244 | | |
245 | 0 | lock.l_type = F_WRLCK; |
246 | 0 | lock.l_start = 0; |
247 | 0 | lock.l_whence = SEEK_SET; |
248 | 0 | lock.l_len = 0; |
249 | 0 | if (fcntl(fd, F_GETLK, &lock) < 0) { |
250 | 0 | log_error("Cannot check lock status of lockfile [%s], error was [%s]", |
251 | 0 | lockfile, strerror(errno)); |
252 | 0 | if (close(fd)) |
253 | 0 | stack; |
254 | 0 | return 0; |
255 | 0 | } |
256 | | |
257 | 0 | if (close(fd)) |
258 | 0 | stack; |
259 | |
|
260 | 0 | return (lock.l_type == F_UNLCK) ? 0 : 1; |
261 | 0 | } |