cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

unix_dir.c
Go to the documentation of this file.
1 /*
2  Copyright 2020 Northern.tech AS
3 
4  This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the
8  Free Software Foundation; version 3.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 
19  To the extent this program is licensed as part of the Enterprise
20  versions of CFEngine, the applicable Commercial Open Source License
21  (COSL) may apply to this file if you as a licensee so wish it. See
22  included file COSL.txt.
23 */
24 
25 #include <platform.h>
26 #include <dir.h>
27 #include <dir_priv.h>
28 #include <file_lib.h>
29 #include <alloc.h>
30 
31 
32 struct Dir_
33 {
34  DIR *dirh;
35  struct dirent *entrybuf;
36 };
37 
38 static size_t GetNameMax(DIR *dirp);
39 static size_t GetDirentBufferSize(size_t path_len);
40 
41 Dir *DirOpen(const char *dirname)
42 {
43  Dir *ret = xcalloc(1, sizeof(Dir));
44  int safe_fd;
45 
46  safe_fd = safe_open(dirname, O_RDONLY);
47  if (safe_fd < 0)
48  {
49  free(ret);
50  return NULL;
51  }
52 
53  ret->dirh = opendir(dirname);
54  if (ret->dirh == NULL)
55  {
56  close(safe_fd);
57  free(ret);
58  return NULL;
59  }
60 
61  struct stat safe_stat, dir_stat;
62  bool stat_failed = fstat(safe_fd, &safe_stat) < 0 || fstat(dirfd(ret->dirh), &dir_stat) < 0;
63  close(safe_fd);
64  if (stat_failed)
65  {
66  closedir(ret->dirh);
67  free(ret);
68  return NULL;
69  }
70 
71  // Make sure we opened the same file descriptor as safe_open did.
72  if (safe_stat.st_dev != dir_stat.st_dev || safe_stat.st_ino != dir_stat.st_ino)
73  {
74  closedir(ret->dirh);
75  free(ret);
76  errno = EACCES;
77  return NULL;
78  }
79 
80  size_t dirent_buf_size = GetDirentBufferSize(GetNameMax(ret->dirh));
81 
82  ret->entrybuf = xcalloc(1, dirent_buf_size);
83 
84  return ret;
85 }
86 
87 /*
88  * Returns NULL on EOF or error.
89  *
90  * Sets errno to 0 for EOF and non-0 for error.
91  */
92 const struct dirent *DirRead(Dir *dir)
93 {
94  assert(dir != NULL);
95  errno = 0;
96 #ifdef __linux__
97  return readdir((DIR *) dir->dirh); // Sets errno for error
98 #else // "exotics" use readdir_r
99 
100  struct dirent *ret;
101  int err = readdir_r((DIR *) dir->dirh, dir->entrybuf, &ret);
102 
103  if (err != 0)
104  {
105  errno = err;
106  return NULL;
107  }
108 
109  if (ret == NULL)
110  {
111  return NULL;
112  }
113 
114  return ret;
115 #endif
116 }
117 
118 void DirClose(Dir *dir)
119 {
120  closedir((DIR *) dir->dirh);
121  free(dir->entrybuf);
122  free(dir);
123 }
124 
125 /*
126  * Taken from http://womble.decadent.org.uk/readdir_r-advisory.html
127  *
128  * Issued by Ben Hutchings <ben@decadent.org.uk>, 2005-11-02.
129  *
130  * Licence
131  *
132  * Permission is hereby granted, free of charge, to any person obtaining a copy
133  * of this software and associated documentation files (the "Software"), to deal
134  * in the Software without restriction, including without limitation the rights
135  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
136  * copies of the Software, and to permit persons to whom the Software is
137  * furnished to do so, subject to the following condition:
138  *
139  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
140  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
141  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
142  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
143  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
144  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
145  * SOFTWARE.
146  */
147 
148 /*
149  * Calculate the required buffer size (in bytes) for directory entries read from
150  * the given directory handle. Return -1 if this this cannot be done.
151  *
152  * This code does not trust values of NAME_MAX that are less than 255, since
153  * some systems (including at least HP-UX) incorrectly define it to be a smaller
154  * value.
155  *
156  * If you use autoconf, include fpathconf and dirfd in your AC_CHECK_FUNCS list.
157  * Otherwise use some other method to detect and use them where available.
158  */
159 
160 #if defined(HAVE_FPATHCONF) && defined(_PC_NAME_MAX)
161 
162 static size_t GetNameMax(DIR *dirp)
163 {
164  long name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
165 
166  if (name_max != -1)
167  {
168  return name_max;
169  }
170 
171 # if defined(NAME_MAX)
172  return (NAME_MAX > 255) ? NAME_MAX : 255;
173 # else
174  return (size_t) (-1);
175 # endif
176 }
177 
178 #else /* FPATHCONF && _PC_NAME_MAX */
179 
180 # if defined(NAME_MAX)
181 static size_t GetNameMax(DIR *dirp)
182 {
183  return (NAME_MAX > 255) ? NAME_MAX : 255;
184 }
185 # else
186 # error "buffer size for readdir_r cannot be determined"
187 # endif
188 
189 #endif /* FPATHCONF && _PC_NAME_MAX */
190 
191 /*
192  * Returns size of memory enough to hold path name_len bytes long.
193  */
194 static size_t GetDirentBufferSize(size_t name_len)
195 {
196  size_t name_end = (size_t) offsetof(struct dirent, d_name) + name_len + 1;
197 
198  return MAX(name_end, sizeof(struct dirent));
199 }
200 
201 struct dirent *AllocateDirentForFilename(const char *filename)
202 {
203  int length = strlen(filename);
204  struct dirent *entry = xcalloc(1, GetDirentBufferSize(length));
205 
206  // d_name is fixed length, but we have allocated extra space using xcalloc
207  // cast is to silence the compiler warning which checks length of d_name:
208  memcpy((char *)entry->d_name, filename, length + 1);
209  return entry;
210 }
void * xcalloc(size_t nmemb, size_t size)
Definition: alloc-mini.c:51
void free(void *)
int dirfd(DIR *dirp)
int safe_open(const char *pathname, int flags)
Definition: file_lib.c:516
int errno
#define NULL
Definition: getopt1.c:56
#define dirent
Definition: platform.h:160
#define MAX(x, y)
Definition: snprintf.c:500
Definition: unix_dir.c:33
DIR * dirh
Definition: unix_dir.c:34
struct direct * entrybuf
Definition: unix_dir.c:35
static size_t GetDirentBufferSize(size_t path_len)
Definition: unix_dir.c:194
static size_t GetNameMax(DIR *dirp)
void DirClose(Dir *dir)
Definition: unix_dir.c:118
const struct direct * DirRead(Dir *dir)
Definition: unix_dir.c:92
struct direct * AllocateDirentForFilename(const char *filename)
Definition: unix_dir.c:201
Dir * DirOpen(const char *dirname)
Definition: unix_dir.c:41