1 /*
2 * Copyright (C) 2020 Microsoft Corporation
3 *
4 * Author: Alan Jowett
5 *
6 * This file is part of ocserv.
7 *
8 * ocserv is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
20 */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <fcntl.h>
29 #include <limits.h>
30 #include <errno.h>
31 #include <talloc.h>
32 #include <sys/stat.h>
33 #include <common/common.h>
34
35 #include <ccan/htable/htable.h>
36 #include <ccan/hash/hash.h>
37
38 #include <snapshot.h>
39
40 #define ERRSTR "error: "
41 #define WARNSTR "warning: "
42 #define NOTESTR "note: "
43
44 typedef struct snapshot_t {
45 struct htable ht;
46 void *pool;
47 const char *tmp_filename_template;
48 } snapshot_t;
49
50 typedef struct snapshot_entry_t {
51 uint32_t fd;
52 const char name[];
53 } snapshot_entry_t;
54
55 typedef struct htable_iter snapshot_iter_t;
56
snapshot_hash_filename(const char * file_name)57 static size_t snapshot_hash_filename(const char *file_name)
58 {
59 return hash64(file_name, strlen(file_name), 0);
60 }
61
snapshot_rehash(const void * elem,void * priv)62 static size_t snapshot_rehash(const void *elem, void *priv)
63 {
64 snapshot_entry_t *entry = (snapshot_entry_t *) elem;
65 return snapshot_hash_filename(entry->name);
66 }
67
snapshot_find(struct snapshot_t * snapshot,const char * filename)68 static snapshot_entry_t *snapshot_find(struct snapshot_t *snapshot,
69 const char *filename)
70 {
71 struct htable_iter iter;
72 size_t hash = snapshot_hash_filename(filename);
73 snapshot_entry_t *entry = htable_firstval(&snapshot->ht, &iter, hash);
74 while (entry != NULL) {
75 if (strcmp(entry->name, filename) == 0) {
76 break;
77 }
78 entry = htable_nextval(&snapshot->ht, &iter, hash);
79 }
80 return entry;
81 }
82
snapshot_file_name_from_fd(int fd,char * file_name,size_t file_name_length)83 static int snapshot_file_name_from_fd(int fd, char *file_name,
84 size_t file_name_length)
85 {
86 int ret = snprintf(file_name, file_name_length, "/proc/self/fd/%d", fd);
87 if (ret >= file_name_length) {
88 return -1;
89 } else {
90 return 0;
91 }
92 }
93
snapshot_add_entry(snapshot_t * snapshot,const char * filename,int fd)94 static int snapshot_add_entry(snapshot_t * snapshot, const char *filename,
95 int fd)
96 {
97 int retval = -1;
98 snapshot_entry_t *entry = NULL;
99 size_t file_name_length = strlen(filename) + 1;
100 entry =
101 (snapshot_entry_t *) talloc_zero_array(snapshot->pool, char,
102 sizeof(uint32_t) +
103 file_name_length);
104 if (entry == NULL)
105 goto cleanup;
106
107 entry->fd = fd;
108 strlcpy((char *)entry->name, filename, file_name_length);
109
110 if (!htable_add
111 (&snapshot->ht, snapshot_hash_filename(entry->name), entry))
112 goto cleanup;
113
114 entry = NULL;
115 retval = 0;
116 cleanup:
117 if (entry)
118 talloc_free(entry);
119
120 return retval;
121 }
122
talloc_clear_htable(snapshot_t * s)123 static int talloc_clear_htable(snapshot_t *s)
124 {
125 htable_clear(&s->ht);
126 return 0;
127 }
128
snapshot_init(void * pool,struct snapshot_t ** snapshot,const char * prefix)129 int snapshot_init(void *pool, struct snapshot_t **snapshot, const char *prefix)
130 {
131 snapshot_t *new_snapshot = NULL;
132 size_t tmp_filename_template_length = strlen(prefix) + 7;
133
134 new_snapshot = talloc_zero(pool, snapshot_t);
135 if (new_snapshot == NULL)
136 goto cleanup;
137
138 new_snapshot->pool = pool;
139
140 new_snapshot->tmp_filename_template =
141 talloc_array(pool, char, tmp_filename_template_length);
142
143 if (snprintf
144 ((char *)new_snapshot->tmp_filename_template,
145 tmp_filename_template_length, "%sXXXXXX",
146 prefix) >= tmp_filename_template_length)
147 goto cleanup;
148
149 htable_init(&new_snapshot->ht, snapshot_rehash, new_snapshot);
150 talloc_set_destructor(new_snapshot, talloc_clear_htable);
151
152 *snapshot = new_snapshot;
153 new_snapshot = NULL;
154 cleanup:
155 if (new_snapshot != NULL) {
156 if (new_snapshot->tmp_filename_template != NULL)
157 talloc_free((char *)new_snapshot->
158 tmp_filename_template);
159 talloc_free(new_snapshot);
160 }
161
162 if ((*snapshot) != NULL) {
163 return 0;
164 } else {
165 return -1;
166 }
167 }
168
snapshot_terminate(struct snapshot_t * snapshot)169 void snapshot_terminate(struct snapshot_t *snapshot)
170 {
171 struct htable_iter iter;
172 snapshot_entry_t *entry = htable_first(&snapshot->ht, &iter);
173 while (entry != NULL) {
174 htable_delval(&snapshot->ht, &iter);
175 close(entry->fd);
176 talloc_free(entry);
177 entry = htable_next(&snapshot->ht, &iter);
178 }
179 }
180
snapshot_create(struct snapshot_t * snapshot,const char * filename)181 int snapshot_create(struct snapshot_t *snapshot, const char *filename)
182 {
183 int ret = -1;
184 char buffer[4096];
185 char tmp_file_name[_POSIX_PATH_MAX];
186 int fd_in = -1;
187 int fd_out = -1;
188 snapshot_entry_t *entry = NULL;
189
190 if (filename == NULL)
191 return 0;
192
193 strlcpy(tmp_file_name, snapshot->tmp_filename_template,
194 _POSIX_PATH_MAX);
195
196 fd_in = open(filename, O_RDONLY);
197 if (fd_in == -1) {
198 fprintf(stderr, ERRSTR "cannot open file %s\n", filename);
199 goto cleanup;
200 }
201
202 umask(006);
203 fd_out = mkstemp(tmp_file_name);
204 if (fd_out == -1) {
205 int err = errno;
206 fprintf(stderr, ERRSTR "cannot create temp file '%s' : %s\n",
207 tmp_file_name, strerror(err));
208 goto cleanup;
209 }
210 // After opening the output file, unlink it to make it anonymous
211 unlink(tmp_file_name);
212
213 for (;;) {
214 int byteRead = read(fd_in, buffer, sizeof(buffer));
215 int bytesWritten;
216 if (byteRead == 0) {
217 break;
218 } else if (byteRead == -1) {
219 int err = errno;
220 fprintf(stderr, ERRSTR " reading %s failed %s\n",
221 filename, strerror(err));
222 goto cleanup;
223 } else {
224 bytesWritten = write(fd_out, buffer, byteRead);
225 if (bytesWritten != byteRead) {
226 int err = errno;
227 fprintf(stderr,
228 ERRSTR " writing %s failed %s\n",
229 tmp_file_name, strerror(err));
230 goto cleanup;
231 }
232 }
233 }
234
235 lseek(fd_out, 0, SEEK_SET);
236
237 close(fd_in);
238 fd_in = -1;
239
240 entry = snapshot_find(snapshot, filename);
241 if (entry != NULL) {
242 close(entry->fd);
243 entry->fd = fd_out;
244 } else {
245 if (snapshot_add_entry(snapshot, filename, fd_out) != 0)
246 goto cleanup;
247 }
248
249 fd_out = -1;
250 ret = 0;
251 entry = NULL;
252
253 cleanup:
254 if (fd_in != -1)
255 close(fd_in);
256
257 if (fd_out != -1)
258 close(fd_out);
259
260 return ret;
261 }
262
snapshot_first(struct snapshot_t * snapshot,struct htable_iter * iter,int * fd,const char ** file_name)263 int snapshot_first(struct snapshot_t *snapshot, struct htable_iter *iter,
264 int *fd, const char **file_name)
265 {
266 snapshot_entry_t *entry = htable_first(&snapshot->ht, iter);
267 if (entry == NULL) {
268 return -1;
269 } else {
270 *fd = entry->fd;
271 *file_name = entry->name;
272 return 0;
273 }
274 }
275
snapshot_next(struct snapshot_t * snapshot,struct htable_iter * iter,int * fd,const char ** file_name)276 int snapshot_next(struct snapshot_t *snapshot, struct htable_iter *iter,
277 int *fd, const char **file_name)
278 {
279 snapshot_entry_t *entry = htable_next(&snapshot->ht, iter);
280 if (entry == NULL) {
281 return -1;
282 } else {
283 *fd = entry->fd;
284 *file_name = entry->name;
285 return 0;
286 }
287 }
288
snapshot_restore_entry(struct snapshot_t * snapshot,int fd,const char * file_name)289 int snapshot_restore_entry(struct snapshot_t *snapshot, int fd,
290 const char *file_name)
291 {
292 int ret = snapshot_add_entry(snapshot, file_name, fd);
293 if (ret < 0)
294 return ret;
295
296 return 0;
297 }
298
snapshot_entry_count(struct snapshot_t * snapshot)299 size_t snapshot_entry_count(struct snapshot_t * snapshot)
300 {
301 struct htable_iter iter;
302 size_t count = 0;
303 snapshot_entry_t *entry = htable_first(&snapshot->ht, &iter);
304
305 while (entry != NULL) {
306 entry = htable_next(&snapshot->ht, &iter);
307 count++;
308 }
309
310 return count;
311 }
312
snapshot_lookup_filename(struct snapshot_t * snapshot,const char * file_name,char ** snapshot_file_name)313 int snapshot_lookup_filename(struct snapshot_t *snapshot, const char *file_name,
314 char **snapshot_file_name)
315 {
316 int ret = -1;
317 char fd_path[128];
318 char *new_file_name = NULL;
319 snapshot_entry_t *entry = snapshot_find(snapshot, file_name);
320 if (entry == NULL)
321 goto cleanup;
322
323 if (snapshot_file_name_from_fd(entry->fd, fd_path, sizeof(fd_path)) < 0)
324 goto cleanup;
325
326 new_file_name = talloc_strdup(snapshot->pool, fd_path);
327 if (new_file_name == NULL)
328 goto cleanup;
329
330 *snapshot_file_name = new_file_name;
331 new_file_name = NULL;
332
333 ret = 0;
334
335 cleanup:
336 if (new_file_name != NULL)
337 talloc_free(new_file_name);
338
339 return ret;
340 }
341