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