1 /*****************************************************************************
2 * Written by Chris Dunlap <cdunlap@llnl.gov>.
3 * Copyright (C) 2007-2018 Lawrence Livermore National Security, LLC.
4 * Copyright (C) 2001-2007 The Regents of the University of California.
5 * UCRL-CODE-2002-009.
6 *
7 * This file is part of ConMan: The Console Manager.
8 * For details, see <https://dun.github.io/conman/>.
9 *
10 * ConMan is free software: you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free
12 * Software Foundation, either version 3 of the License, or (at your option)
13 * any later version.
14 *
15 * ConMan is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 * for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with ConMan. If not, see <http://www.gnu.org/licenses/>.
22 *****************************************************************************
23 * Refer to "util-file.h" for documentation on public functions.
24 *****************************************************************************/
25
26
27 #if HAVE_CONFIG_H
28 # include <config.h>
29 #endif /* HAVE_CONFIG_H */
30
31 #include <assert.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <limits.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include "log.h"
39 #include "util-file.h"
40 #include "util-str.h"
41
42
43 static int get_file_lock(int fd, int cmd, int type);
44 static pid_t test_file_lock(int fd, int type);
45
46
set_fd_closed_on_exec(int fd)47 void set_fd_closed_on_exec(int fd)
48 {
49 assert(fd >= 0);
50
51 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
52 log_err(errno, "fcntl(F_SETFD) failed");
53 return;
54 }
55
56
set_fd_blocking(int fd)57 void set_fd_blocking(int fd)
58 {
59 int fval;
60
61 assert(fd >= 0);
62
63 if ((fval = fcntl(fd, F_GETFL, 0)) < 0)
64 log_err(errno, "fcntl(F_GETFL) failed");
65 if (fcntl(fd, F_SETFL, fval & (~O_NONBLOCK)) < 0)
66 log_err(errno, "fcntl(F_SETFL) failed");
67 return;
68 }
69
70
set_fd_nonblocking(int fd)71 void set_fd_nonblocking(int fd)
72 {
73 int fval;
74
75 assert(fd >= 0);
76
77 if ((fval = fcntl(fd, F_GETFL, 0)) < 0)
78 log_err(errno, "fcntl(F_GETFL) failed");
79 if (fcntl(fd, F_SETFL, fval | O_NONBLOCK) < 0)
80 log_err(errno, "fcntl(F_SETFL) failed");
81 return;
82 }
83
84
get_read_lock(int fd)85 int get_read_lock(int fd)
86 {
87 return(get_file_lock(fd, F_SETLK, F_RDLCK));
88 }
89
90
get_readw_lock(int fd)91 int get_readw_lock(int fd)
92 {
93 return(get_file_lock(fd, F_SETLKW, F_RDLCK));
94 }
95
96
get_write_lock(int fd)97 int get_write_lock(int fd)
98 {
99 return(get_file_lock(fd, F_SETLK, F_WRLCK));
100 }
101
102
get_writew_lock(int fd)103 int get_writew_lock(int fd)
104 {
105 return(get_file_lock(fd, F_SETLKW, F_WRLCK));
106 }
107
108
release_lock(int fd)109 int release_lock(int fd)
110 {
111 return(get_file_lock(fd, F_SETLK, F_UNLCK));
112 }
113
114
is_read_lock_blocked(int fd)115 pid_t is_read_lock_blocked(int fd)
116 {
117 return(test_file_lock(fd, F_RDLCK));
118 }
119
120
is_write_lock_blocked(int fd)121 pid_t is_write_lock_blocked(int fd)
122 {
123 return(test_file_lock(fd, F_WRLCK));
124 }
125
126
get_file_lock(int fd,int cmd,int type)127 static int get_file_lock(int fd, int cmd, int type)
128 {
129 struct flock lock;
130
131 assert(fd >= 0);
132
133 lock.l_type = type;
134 lock.l_start = 0;
135 lock.l_whence = SEEK_SET;
136 lock.l_len = 0;
137
138 return(fcntl(fd, cmd, &lock));
139 }
140
141
test_file_lock(int fd,int type)142 static pid_t test_file_lock(int fd, int type)
143 {
144 struct flock lock;
145
146 assert(fd >= 0);
147
148 lock.l_type = type;
149 lock.l_start = 0;
150 lock.l_whence = SEEK_SET;
151 lock.l_len = 0;
152
153 if (fcntl(fd, F_GETLK, &lock) < 0)
154 log_err(errno, "Unable to test for file lock");
155 if (lock.l_type == F_UNLCK)
156 return(0);
157 return(lock.l_pid);
158 }
159
160
read_n(int fd,void * buf,size_t n)161 ssize_t read_n(int fd, void *buf, size_t n)
162 {
163 size_t nleft;
164 ssize_t nread;
165 unsigned char *p;
166
167 p = buf;
168 nleft = n;
169 while (nleft > 0) {
170 if ((nread = read(fd, p, nleft)) < 0) {
171 if (errno == EINTR)
172 continue;
173 else
174 return(-1);
175 }
176 else if (nread == 0) { /* EOF */
177 break;
178 }
179 nleft -= nread;
180 p += nread;
181 }
182 return(n - nleft);
183 }
184
185
write_n(int fd,void * buf,size_t n)186 ssize_t write_n(int fd, void *buf, size_t n)
187 {
188 size_t nleft;
189 ssize_t nwritten;
190 unsigned char *p;
191
192 p = buf;
193 nleft = n;
194 while (nleft > 0) {
195 if ((nwritten = write(fd, p, nleft)) < 0) {
196 if (errno == EINTR)
197 continue;
198 else
199 return(-1);
200 }
201 nleft -= nwritten;
202 p += nwritten;
203 }
204 return(n);
205 }
206
207
read_line(int fd,void * buf,size_t maxlen)208 ssize_t read_line(int fd, void *buf, size_t maxlen)
209 {
210 size_t n;
211 ssize_t rv;
212 unsigned char c, *p;
213
214 if (buf == NULL) {
215 errno = EINVAL;
216 return(-1);
217 }
218 if (maxlen == 0) {
219 return(0);
220 }
221 maxlen--; /* reserve space for NUL-termination */
222 n = 0;
223 p = buf;
224 while (n < maxlen) {
225 rv = read(fd, &c, sizeof(c));
226 if (rv == 1) {
227 n++;
228 *p++ = c;
229 if (c == '\n')
230 break; /* store newline, like fgets() */
231 }
232 else if (rv == 0) {
233 if (n == 0) /* EOF, no data read */
234 return(0);
235 else /* EOF, some data read */
236 break;
237 }
238 else {
239 if (errno == EINTR)
240 continue;
241 return(-1);
242 }
243 }
244
245 *p = '\0'; /* NUL-terminate, like fgets() */
246 return((ssize_t) n);
247 }
248
249
250 char *
get_dir_name(const char * srcpath,char * dstdir,size_t dstdirlen)251 get_dir_name (const char *srcpath, char *dstdir, size_t dstdirlen)
252 {
253 const char *p;
254 size_t len;
255
256 if ((srcpath == NULL) || (dstdir == NULL)) {
257 errno = EINVAL;
258 return (NULL);
259 }
260 p = srcpath + strlen (srcpath) - 1;
261
262 /* Ignore trailing slashes except for the root slash.
263 */
264 while ((p > srcpath) && (*p == '/')) {
265 p--;
266 }
267 /* Skip over last path component.
268 */
269 while ((p >= srcpath) && (*p != '/')) {
270 p--;
271 }
272 /* Skip over adjacent slashes except for the root slash.
273 */
274 while ((p > srcpath) && (*p == '/')) {
275 p--;
276 }
277 /* A path not containing a slash shall return the dot directory.
278 */
279 if (p < srcpath) {
280 if (dstdirlen < 2) {
281 errno = ENAMETOOLONG;
282 return (NULL);
283 }
284 dstdir [0] = '.';
285 dstdir [1] = '\0';
286 }
287 /* Otherwise, copy the directory string into dstdir.
288 */
289 else {
290 /* 'p' now points at the last char to copy, so +1 to include that
291 * last char, then add the terminating null char afterwards.
292 */
293 len = p - srcpath + 1;
294 if (len >= dstdirlen) {
295 errno = ENAMETOOLONG;
296 return (NULL);
297 }
298 (void) strncpy (dstdir, srcpath, len);
299 dstdir [len] = '\0';
300 }
301 return (dstdir);
302 }
303
304
305 int
create_dirs(const char * dir_name)306 create_dirs (const char *dir_name)
307 {
308 struct stat st_buf;
309 char dir_buf [PATH_MAX];
310 char *p;
311 char *slash_ptr;
312 mode_t dir_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
313
314 if ((dir_name == NULL) || (*dir_name == '\0')) {
315 errno = EINVAL;
316 log_msg (LOG_WARNING, "No directory specified for creation");
317 return (-1);
318 }
319 /* Check if the directory already exists.
320 */
321 if (stat (dir_name, &st_buf) == 0) {
322 if (S_ISDIR (st_buf.st_mode)) {
323 return (0);
324 }
325 errno = EEXIST;
326 log_msg (LOG_WARNING, "Cannot create directory \"%s\": %s",
327 dir_name, strerror (errno));
328 return (-1);
329 }
330 /* Create copy of [dir_name] for modification.
331 */
332 if (strlcpy (dir_buf, dir_name, sizeof (dir_buf)) >= sizeof (dir_buf)) {
333 errno = ENAMETOOLONG;
334 log_msg (LOG_WARNING, "Exceeded maximum directory length of %d bytes",
335 sizeof (dir_buf) - 1);
336 return (-1);
337 }
338 /* Remove trailing slashes from the directory name
339 * (while ensuring the root slash is not removed in the process).
340 */
341 p = dir_buf + strlen (dir_buf) - 1;
342 while ((p > dir_buf) && (*p == '/')) {
343 *p-- = '\0';
344 }
345 /* The slash_ptr points to the leftmost unprocessed directory component.
346 */
347 slash_ptr = dir_buf;
348
349 /* Process directory components.
350 */
351 while (1) {
352
353 /* Skip over adjacent slashes (omitting unnecessary calls to mkdir).
354 */
355 while (*slash_ptr == '/') {
356 slash_ptr++;
357 }
358 /* Advance slash_ptr to the next directory component.
359 */
360 slash_ptr = strchr (slash_ptr, '/');
361 if (slash_ptr != NULL) {
362 *slash_ptr = '\0';
363 }
364 /* Create directory.
365 */
366 if (mkdir (dir_buf, dir_mode) < 0) {
367
368 int mkdir_errno = errno;
369
370 if ((mkdir_errno != EEXIST)
371 || (stat (dir_buf, &st_buf) < 0)
372 || (! S_ISDIR (st_buf.st_mode))) {
373 log_msg (LOG_WARNING, "Cannot create directory \"%s\": %s",
374 dir_buf, strerror (mkdir_errno));
375 return (-1);
376 }
377 }
378 if (slash_ptr == NULL) {
379 break;
380 }
381 *slash_ptr++ = '/';
382 }
383 return (0);
384 }
385