diff options
Diffstat (limited to 'src/lib/util/disk.c')
-rw-r--r-- | src/lib/util/disk.c | 1689 |
1 files changed, 1689 insertions, 0 deletions
diff --git a/src/lib/util/disk.c b/src/lib/util/disk.c new file mode 100644 index 000000000..567c2b5bc --- /dev/null +++ b/src/lib/util/disk.c | |||
@@ -0,0 +1,1689 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001--2013, 2016, 2018 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file util/disk.c | ||
22 | * @brief disk IO convenience methods | ||
23 | * @author Christian Grothoff | ||
24 | * @author Nils Durner | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | |||
30 | #define LOG(kind, ...) GNUNET_log_from (kind, "util-disk", __VA_ARGS__) | ||
31 | |||
32 | #define LOG_STRERROR(kind, syscall) \ | ||
33 | GNUNET_log_from_strerror (kind, "util-disk", syscall) | ||
34 | |||
35 | #define LOG_STRERROR_FILE(kind, syscall, filename) \ | ||
36 | GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename) | ||
37 | |||
38 | /** | ||
39 | * Block size for IO for copying files. | ||
40 | */ | ||
41 | #define COPY_BLK_SIZE 65536 | ||
42 | |||
43 | #include <sys/types.h> | ||
44 | #if HAVE_SYS_VFS_H | ||
45 | #include <sys/vfs.h> | ||
46 | #endif | ||
47 | #if HAVE_SYS_PARAM_H | ||
48 | #include <sys/param.h> | ||
49 | #endif | ||
50 | #if HAVE_SYS_MOUNT_H | ||
51 | #include <sys/mount.h> | ||
52 | #endif | ||
53 | #if HAVE_SYS_STATVFS_H | ||
54 | #include <sys/statvfs.h> | ||
55 | #endif | ||
56 | |||
57 | #ifndef S_ISLNK | ||
58 | #define _IFMT 0170000 /* type of file */ | ||
59 | #define _IFLNK 0120000 /* symbolic link */ | ||
60 | #define S_ISLNK(m) (((m) & _IFMT) == _IFLNK) | ||
61 | #endif | ||
62 | |||
63 | |||
64 | /** | ||
65 | * Handle used to manage a pipe. | ||
66 | */ | ||
67 | struct GNUNET_DISK_PipeHandle | ||
68 | { | ||
69 | /** | ||
70 | * File descriptors for the pipe. | ||
71 | * One or both of them could be NULL. | ||
72 | */ | ||
73 | struct GNUNET_DISK_FileHandle *fd[2]; | ||
74 | }; | ||
75 | |||
76 | |||
77 | /** | ||
78 | * Closure for the recursion to determine the file size | ||
79 | * of a directory. | ||
80 | */ | ||
81 | struct GetFileSizeData | ||
82 | { | ||
83 | /** | ||
84 | * Set to the total file size. | ||
85 | */ | ||
86 | uint64_t total; | ||
87 | |||
88 | /** | ||
89 | * GNUNET_YES if symbolic links should be included. | ||
90 | */ | ||
91 | int include_sym_links; | ||
92 | |||
93 | /** | ||
94 | * #GNUNET_YES if mode is file-only (return total == -1 for directories). | ||
95 | */ | ||
96 | int single_file_mode; | ||
97 | }; | ||
98 | |||
99 | |||
100 | /** | ||
101 | * Translate GNUnet-internal permission bitmap to UNIX file | ||
102 | * access permission bitmap. | ||
103 | * | ||
104 | * @param perm file permissions, GNUnet style | ||
105 | * @return file permissions, UNIX style | ||
106 | */ | ||
107 | static int | ||
108 | translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm) | ||
109 | { | ||
110 | int mode; | ||
111 | |||
112 | mode = 0; | ||
113 | if (perm & GNUNET_DISK_PERM_USER_READ) | ||
114 | mode |= S_IRUSR; | ||
115 | if (perm & GNUNET_DISK_PERM_USER_WRITE) | ||
116 | mode |= S_IWUSR; | ||
117 | if (perm & GNUNET_DISK_PERM_USER_EXEC) | ||
118 | mode |= S_IXUSR; | ||
119 | if (perm & GNUNET_DISK_PERM_GROUP_READ) | ||
120 | mode |= S_IRGRP; | ||
121 | if (perm & GNUNET_DISK_PERM_GROUP_WRITE) | ||
122 | mode |= S_IWGRP; | ||
123 | if (perm & GNUNET_DISK_PERM_GROUP_EXEC) | ||
124 | mode |= S_IXGRP; | ||
125 | if (perm & GNUNET_DISK_PERM_OTHER_READ) | ||
126 | mode |= S_IROTH; | ||
127 | if (perm & GNUNET_DISK_PERM_OTHER_WRITE) | ||
128 | mode |= S_IWOTH; | ||
129 | if (perm & GNUNET_DISK_PERM_OTHER_EXEC) | ||
130 | mode |= S_IXOTH; | ||
131 | |||
132 | return mode; | ||
133 | } | ||
134 | |||
135 | |||
136 | /** | ||
137 | * Iterate over all files in the given directory and | ||
138 | * accumulate their size. | ||
139 | * | ||
140 | * @param cls closure of type `struct GetFileSizeData` | ||
141 | * @param fn current filename we are looking at | ||
142 | * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK | ||
143 | */ | ||
144 | static enum GNUNET_GenericReturnValue | ||
145 | get_size_rec (void *cls, const char *fn) | ||
146 | { | ||
147 | struct GetFileSizeData *gfsd = cls; | ||
148 | |||
149 | #if defined(HAVE_STAT64) && \ | ||
150 | ! (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) | ||
151 | struct stat64 buf; | ||
152 | |||
153 | if (0 != stat64 (fn, &buf)) | ||
154 | { | ||
155 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn); | ||
156 | return GNUNET_SYSERR; | ||
157 | } | ||
158 | #else | ||
159 | struct stat buf; | ||
160 | |||
161 | if (0 != stat (fn, &buf)) | ||
162 | { | ||
163 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn); | ||
164 | return GNUNET_SYSERR; | ||
165 | } | ||
166 | #endif | ||
167 | if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES)) | ||
168 | { | ||
169 | errno = EISDIR; | ||
170 | return GNUNET_SYSERR; | ||
171 | } | ||
172 | if ((! S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)) | ||
173 | gfsd->total += buf.st_size; | ||
174 | if ((S_ISDIR (buf.st_mode)) && (0 == access (fn, X_OK)) && | ||
175 | ((! S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))) | ||
176 | { | ||
177 | if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &get_size_rec, gfsd)) | ||
178 | return GNUNET_SYSERR; | ||
179 | } | ||
180 | return GNUNET_OK; | ||
181 | } | ||
182 | |||
183 | |||
184 | enum GNUNET_GenericReturnValue | ||
185 | GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h) | ||
186 | { | ||
187 | return ((! h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO; | ||
188 | } | ||
189 | |||
190 | |||
191 | enum GNUNET_GenericReturnValue | ||
192 | GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh, | ||
193 | off_t *size) | ||
194 | { | ||
195 | struct stat sbuf; | ||
196 | |||
197 | if (0 != fstat (fh->fd, &sbuf)) | ||
198 | return GNUNET_SYSERR; | ||
199 | *size = sbuf.st_size; | ||
200 | return GNUNET_OK; | ||
201 | } | ||
202 | |||
203 | |||
204 | off_t | ||
205 | GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h, | ||
206 | off_t offset, | ||
207 | enum GNUNET_DISK_Seek whence) | ||
208 | { | ||
209 | static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END }; | ||
210 | |||
211 | if (h == NULL) | ||
212 | { | ||
213 | errno = EINVAL; | ||
214 | return GNUNET_SYSERR; | ||
215 | } | ||
216 | return lseek (h->fd, offset, t[whence]); | ||
217 | } | ||
218 | |||
219 | |||
220 | enum GNUNET_GenericReturnValue | ||
221 | GNUNET_DISK_file_size (const char *filename, | ||
222 | uint64_t *size, | ||
223 | int include_symbolic_links, | ||
224 | int single_file_mode) | ||
225 | { | ||
226 | struct GetFileSizeData gfsd; | ||
227 | enum GNUNET_GenericReturnValue ret; | ||
228 | |||
229 | GNUNET_assert (size != NULL); | ||
230 | gfsd.total = 0; | ||
231 | gfsd.include_sym_links = include_symbolic_links; | ||
232 | gfsd.single_file_mode = single_file_mode; | ||
233 | ret = get_size_rec (&gfsd, filename); | ||
234 | *size = gfsd.total; | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | |||
239 | enum GNUNET_GenericReturnValue | ||
240 | GNUNET_DISK_file_get_identifiers (const char *filename, | ||
241 | uint64_t *dev, | ||
242 | uint64_t *ino) | ||
243 | { | ||
244 | #if HAVE_STAT | ||
245 | { | ||
246 | struct stat sbuf; | ||
247 | |||
248 | if (0 != stat (filename, &sbuf)) | ||
249 | { | ||
250 | return GNUNET_SYSERR; | ||
251 | } | ||
252 | *ino = (uint64_t) sbuf.st_ino; | ||
253 | } | ||
254 | #else | ||
255 | *ino = 0; | ||
256 | #endif | ||
257 | #if HAVE_STATVFS | ||
258 | { | ||
259 | struct statvfs fbuf; | ||
260 | |||
261 | if (0 != statvfs (filename, &fbuf)) | ||
262 | { | ||
263 | return GNUNET_SYSERR; | ||
264 | } | ||
265 | *dev = (uint64_t) fbuf.f_fsid; | ||
266 | } | ||
267 | #elif HAVE_STATFS | ||
268 | { | ||
269 | struct statfs fbuf; | ||
270 | |||
271 | if (0 != statfs (filename, &fbuf)) | ||
272 | { | ||
273 | return GNUNET_SYSERR; | ||
274 | } | ||
275 | *dev = | ||
276 | ((uint64_t) fbuf.f_fsid.val[0]) << 32 || ((uint64_t) fbuf.f_fsid.val[1]); | ||
277 | } | ||
278 | #else | ||
279 | *dev = 0; | ||
280 | #endif | ||
281 | return GNUNET_OK; | ||
282 | } | ||
283 | |||
284 | |||
285 | /** | ||
286 | * Create the name for a temporary file or directory from a template. | ||
287 | * | ||
288 | * @param t template (without XXXXX or "/tmp/") | ||
289 | * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error | ||
290 | */ | ||
291 | static char * | ||
292 | mktemp_name (const char *t) | ||
293 | { | ||
294 | const char *tmpdir; | ||
295 | char *tmpl; | ||
296 | char *fn; | ||
297 | |||
298 | if ((t[0] != '/') && (t[0] != '\\')) | ||
299 | { | ||
300 | /* FIXME: This uses system codepage on W32, not UTF-8 */ | ||
301 | tmpdir = getenv ("TMPDIR"); | ||
302 | if (NULL == tmpdir) | ||
303 | tmpdir = getenv ("TMP"); | ||
304 | if (NULL == tmpdir) | ||
305 | tmpdir = getenv ("TEMP"); | ||
306 | if (NULL == tmpdir) | ||
307 | tmpdir = "/tmp"; | ||
308 | GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX"); | ||
309 | } | ||
310 | else | ||
311 | { | ||
312 | GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX"); | ||
313 | } | ||
314 | fn = tmpl; | ||
315 | return fn; | ||
316 | } | ||
317 | |||
318 | |||
319 | void | ||
320 | GNUNET_DISK_fix_permissions (const char *fn, | ||
321 | int require_uid_match, | ||
322 | int require_gid_match) | ||
323 | { | ||
324 | mode_t mode; | ||
325 | |||
326 | if (GNUNET_YES == require_uid_match) | ||
327 | mode = S_IRUSR | S_IWUSR | S_IXUSR; | ||
328 | else if (GNUNET_YES == require_gid_match) | ||
329 | mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP; | ||
330 | else | ||
331 | mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | ||
332 | | S_IWOTH | S_IXOTH; | ||
333 | if (0 != chmod (fn, mode)) | ||
334 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn); | ||
335 | } | ||
336 | |||
337 | |||
338 | char * | ||
339 | GNUNET_DISK_mkdtemp (const char *t) | ||
340 | { | ||
341 | char *fn; | ||
342 | mode_t omask; | ||
343 | |||
344 | omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH); | ||
345 | fn = mktemp_name (t); | ||
346 | if (fn != mkdtemp (fn)) | ||
347 | { | ||
348 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn); | ||
349 | GNUNET_free (fn); | ||
350 | umask (omask); | ||
351 | return NULL; | ||
352 | } | ||
353 | umask (omask); | ||
354 | return fn; | ||
355 | } | ||
356 | |||
357 | |||
358 | void | ||
359 | GNUNET_DISK_file_backup (const char *fil) | ||
360 | { | ||
361 | size_t slen; | ||
362 | char *target; | ||
363 | unsigned int num; | ||
364 | |||
365 | slen = strlen (fil) + 20; | ||
366 | target = GNUNET_malloc (slen); | ||
367 | num = 0; | ||
368 | do | ||
369 | { | ||
370 | GNUNET_snprintf (target, slen, "%s.%u~", fil, num++); | ||
371 | } | ||
372 | while (0 == access (target, F_OK)); | ||
373 | if (0 != rename (fil, target)) | ||
374 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "rename", fil); | ||
375 | GNUNET_free (target); | ||
376 | } | ||
377 | |||
378 | |||
379 | char * | ||
380 | GNUNET_DISK_mktemp (const char *t) | ||
381 | { | ||
382 | int fd; | ||
383 | char *fn; | ||
384 | mode_t omask; | ||
385 | |||
386 | omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH); | ||
387 | fn = mktemp_name (t); | ||
388 | if (-1 == (fd = mkstemp (fn))) | ||
389 | { | ||
390 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn); | ||
391 | GNUNET_free (fn); | ||
392 | umask (omask); | ||
393 | return NULL; | ||
394 | } | ||
395 | umask (omask); | ||
396 | if (0 != close (fd)) | ||
397 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn); | ||
398 | return fn; | ||
399 | } | ||
400 | |||
401 | |||
402 | enum GNUNET_GenericReturnValue | ||
403 | GNUNET_DISK_directory_test (const char *fil, int is_readable) | ||
404 | { | ||
405 | struct stat filestat; | ||
406 | int ret; | ||
407 | |||
408 | ret = stat (fil, &filestat); | ||
409 | if (ret != 0) | ||
410 | { | ||
411 | if (errno != ENOENT) | ||
412 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil); | ||
413 | return GNUNET_SYSERR; | ||
414 | } | ||
415 | if (! S_ISDIR (filestat.st_mode)) | ||
416 | { | ||
417 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
418 | "A file already exits with the same name %s\n", | ||
419 | fil); | ||
420 | return GNUNET_NO; | ||
421 | } | ||
422 | if (GNUNET_YES == is_readable) | ||
423 | ret = access (fil, R_OK | X_OK); | ||
424 | else | ||
425 | ret = access (fil, X_OK); | ||
426 | if (ret < 0) | ||
427 | { | ||
428 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil); | ||
429 | return GNUNET_NO; | ||
430 | } | ||
431 | return GNUNET_YES; | ||
432 | } | ||
433 | |||
434 | |||
435 | /** | ||
436 | * Check if fil can be accessed using amode. | ||
437 | * | ||
438 | * @param fil file to check for | ||
439 | * @param amode access mode | ||
440 | * @returns GNUnet error code | ||
441 | */ | ||
442 | static enum GNUNET_GenericReturnValue | ||
443 | file_test_internal (const char *fil, int amode) | ||
444 | { | ||
445 | struct stat filestat; | ||
446 | int ret; | ||
447 | char *rdir; | ||
448 | |||
449 | rdir = GNUNET_STRINGS_filename_expand (fil); | ||
450 | if (rdir == NULL) | ||
451 | return GNUNET_SYSERR; | ||
452 | |||
453 | ret = stat (rdir, &filestat); | ||
454 | if (0 != ret) | ||
455 | { | ||
456 | if (errno != ENOENT) | ||
457 | { | ||
458 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", rdir); | ||
459 | GNUNET_free (rdir); | ||
460 | return GNUNET_SYSERR; | ||
461 | } | ||
462 | GNUNET_free (rdir); | ||
463 | return GNUNET_NO; | ||
464 | } | ||
465 | if (! S_ISREG (filestat.st_mode)) | ||
466 | { | ||
467 | GNUNET_free (rdir); | ||
468 | return GNUNET_NO; | ||
469 | } | ||
470 | if (access (rdir, amode) < 0) | ||
471 | { | ||
472 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "access", rdir); | ||
473 | GNUNET_free (rdir); | ||
474 | return GNUNET_SYSERR; | ||
475 | } | ||
476 | GNUNET_free (rdir); | ||
477 | return GNUNET_YES; | ||
478 | } | ||
479 | |||
480 | |||
481 | enum GNUNET_GenericReturnValue | ||
482 | GNUNET_DISK_file_test (const char *fil) | ||
483 | { | ||
484 | return file_test_internal (fil, F_OK); | ||
485 | } | ||
486 | |||
487 | |||
488 | enum GNUNET_GenericReturnValue | ||
489 | GNUNET_DISK_file_test_read (const char *fil) | ||
490 | { | ||
491 | return file_test_internal (fil, R_OK); | ||
492 | } | ||
493 | |||
494 | |||
495 | enum GNUNET_GenericReturnValue | ||
496 | GNUNET_DISK_directory_create (const char *dir) | ||
497 | { | ||
498 | char *rdir; | ||
499 | unsigned int len; | ||
500 | unsigned int pos; | ||
501 | unsigned int pos2; | ||
502 | int ret = GNUNET_OK; | ||
503 | |||
504 | rdir = GNUNET_STRINGS_filename_expand (dir); | ||
505 | if (rdir == NULL) | ||
506 | { | ||
507 | GNUNET_break (0); | ||
508 | return GNUNET_SYSERR; | ||
509 | } | ||
510 | |||
511 | len = strlen (rdir); | ||
512 | |||
513 | pos = 1; /* skip heading '/' */ | ||
514 | |||
515 | /* Check which low level directories already exist */ | ||
516 | pos2 = len; | ||
517 | rdir[len] = DIR_SEPARATOR; | ||
518 | while (pos <= pos2) | ||
519 | { | ||
520 | if (DIR_SEPARATOR == rdir[pos2]) | ||
521 | { | ||
522 | rdir[pos2] = '\0'; | ||
523 | ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO); | ||
524 | if (GNUNET_NO == ret) | ||
525 | { | ||
526 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
527 | "Creating directory `%s' failed", | ||
528 | rdir); | ||
529 | GNUNET_free (rdir); | ||
530 | return GNUNET_SYSERR; | ||
531 | } | ||
532 | rdir[pos2] = DIR_SEPARATOR; | ||
533 | if (GNUNET_YES == ret) | ||
534 | { | ||
535 | pos2++; | ||
536 | break; | ||
537 | } | ||
538 | } | ||
539 | pos2--; | ||
540 | } | ||
541 | rdir[len] = '\0'; | ||
542 | if (pos < pos2) | ||
543 | pos = pos2; | ||
544 | /* Start creating directories */ | ||
545 | while (pos <= len) | ||
546 | { | ||
547 | if ((rdir[pos] == DIR_SEPARATOR) || (pos == len)) | ||
548 | { | ||
549 | rdir[pos] = '\0'; | ||
550 | ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO); | ||
551 | if (GNUNET_NO == ret) | ||
552 | { | ||
553 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
554 | "Creating directory `%s' failed", | ||
555 | rdir); | ||
556 | GNUNET_free (rdir); | ||
557 | return GNUNET_SYSERR; | ||
558 | } | ||
559 | if (GNUNET_SYSERR == ret) | ||
560 | { | ||
561 | ret = mkdir (rdir, | ||
562 | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | ||
563 | | S_IXOTH); /* 755 */ | ||
564 | |||
565 | if ((ret != 0) && (errno != EEXIST)) | ||
566 | { | ||
567 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir); | ||
568 | GNUNET_free (rdir); | ||
569 | return GNUNET_SYSERR; | ||
570 | } | ||
571 | } | ||
572 | rdir[pos] = DIR_SEPARATOR; | ||
573 | } | ||
574 | pos++; | ||
575 | } | ||
576 | GNUNET_free (rdir); | ||
577 | return GNUNET_OK; | ||
578 | } | ||
579 | |||
580 | |||
581 | enum GNUNET_GenericReturnValue | ||
582 | GNUNET_DISK_directory_create_for_file (const char *filename) | ||
583 | { | ||
584 | char *rdir; | ||
585 | size_t len; | ||
586 | int eno; | ||
587 | enum GNUNET_GenericReturnValue res; | ||
588 | |||
589 | rdir = GNUNET_STRINGS_filename_expand (filename); | ||
590 | if (NULL == rdir) | ||
591 | { | ||
592 | errno = EINVAL; | ||
593 | return GNUNET_SYSERR; | ||
594 | } | ||
595 | if (0 == access (rdir, W_OK)) | ||
596 | { | ||
597 | GNUNET_free (rdir); | ||
598 | return GNUNET_OK; | ||
599 | } | ||
600 | len = strlen (rdir); | ||
601 | while ((len > 0) && (rdir[len] != DIR_SEPARATOR)) | ||
602 | len--; | ||
603 | rdir[len] = '\0'; | ||
604 | /* The empty path is invalid and in this case refers to / */ | ||
605 | if (0 == len) | ||
606 | { | ||
607 | GNUNET_free (rdir); | ||
608 | rdir = GNUNET_strdup ("/"); | ||
609 | } | ||
610 | res = GNUNET_DISK_directory_create (rdir); | ||
611 | if ( (GNUNET_OK == res) && | ||
612 | (0 != access (rdir, W_OK)) ) | ||
613 | res = GNUNET_NO; | ||
614 | eno = errno; | ||
615 | GNUNET_free (rdir); | ||
616 | errno = eno; | ||
617 | return res; | ||
618 | } | ||
619 | |||
620 | |||
621 | ssize_t | ||
622 | GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h, | ||
623 | void *result, | ||
624 | size_t len) | ||
625 | { | ||
626 | if (NULL == h) | ||
627 | { | ||
628 | errno = EINVAL; | ||
629 | return GNUNET_SYSERR; | ||
630 | } | ||
631 | return read (h->fd, result, len); | ||
632 | } | ||
633 | |||
634 | |||
635 | ssize_t | ||
636 | GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h, | ||
637 | void *result, | ||
638 | size_t len) | ||
639 | { | ||
640 | int flags; | ||
641 | ssize_t ret; | ||
642 | |||
643 | if (NULL == h) | ||
644 | { | ||
645 | errno = EINVAL; | ||
646 | return GNUNET_SYSERR; | ||
647 | } | ||
648 | /* set to non-blocking, read, then set back */ | ||
649 | flags = fcntl (h->fd, F_GETFL); | ||
650 | if (0 == (flags & O_NONBLOCK)) | ||
651 | (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK); | ||
652 | ret = read (h->fd, result, len); | ||
653 | if (0 == (flags & O_NONBLOCK)) | ||
654 | { | ||
655 | int eno = errno; | ||
656 | (void) fcntl (h->fd, F_SETFL, flags); | ||
657 | errno = eno; | ||
658 | } | ||
659 | return ret; | ||
660 | } | ||
661 | |||
662 | |||
663 | ssize_t | ||
664 | GNUNET_DISK_fn_read (const char *fn, | ||
665 | void *result, | ||
666 | size_t len) | ||
667 | { | ||
668 | struct GNUNET_DISK_FileHandle *fh; | ||
669 | ssize_t ret; | ||
670 | int eno; | ||
671 | |||
672 | fh = GNUNET_DISK_file_open (fn, | ||
673 | GNUNET_DISK_OPEN_READ, | ||
674 | GNUNET_DISK_PERM_NONE); | ||
675 | if (NULL == fh) | ||
676 | return GNUNET_SYSERR; | ||
677 | ret = GNUNET_DISK_file_read (fh, result, len); | ||
678 | eno = errno; | ||
679 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh)); | ||
680 | errno = eno; | ||
681 | return ret; | ||
682 | } | ||
683 | |||
684 | |||
685 | ssize_t | ||
686 | GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h, | ||
687 | const void *buffer, | ||
688 | size_t n) | ||
689 | { | ||
690 | if (NULL == h) | ||
691 | { | ||
692 | errno = EINVAL; | ||
693 | return GNUNET_SYSERR; | ||
694 | } | ||
695 | |||
696 | return write (h->fd, buffer, n); | ||
697 | } | ||
698 | |||
699 | |||
700 | ssize_t | ||
701 | GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle *h, | ||
702 | const void *buffer, | ||
703 | size_t n) | ||
704 | { | ||
705 | int flags; | ||
706 | ssize_t ret; | ||
707 | |||
708 | if (NULL == h) | ||
709 | { | ||
710 | errno = EINVAL; | ||
711 | return GNUNET_SYSERR; | ||
712 | } | ||
713 | /* set to blocking, write, then set back */ | ||
714 | flags = fcntl (h->fd, F_GETFL); | ||
715 | if (0 != (flags & O_NONBLOCK)) | ||
716 | (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK); | ||
717 | ret = write (h->fd, buffer, n); | ||
718 | if (0 == (flags & O_NONBLOCK)) | ||
719 | (void) fcntl (h->fd, F_SETFL, flags); | ||
720 | return ret; | ||
721 | } | ||
722 | |||
723 | |||
724 | enum GNUNET_GenericReturnValue | ||
725 | GNUNET_DISK_fn_write (const char *fn, | ||
726 | const void *buf, | ||
727 | size_t buf_size, | ||
728 | enum GNUNET_DISK_AccessPermissions mode) | ||
729 | { | ||
730 | char *tmpl; | ||
731 | int fd; | ||
732 | |||
733 | if (GNUNET_OK != | ||
734 | GNUNET_DISK_directory_create_for_file (fn)) | ||
735 | { | ||
736 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
737 | "mkstemp", | ||
738 | fn); | ||
739 | return GNUNET_SYSERR; | ||
740 | } | ||
741 | { | ||
742 | char *dname; | ||
743 | |||
744 | dname = GNUNET_strdup (fn); | ||
745 | GNUNET_asprintf (&tmpl, | ||
746 | "%s/XXXXXX", | ||
747 | dirname (dname)); | ||
748 | GNUNET_free (dname); | ||
749 | } | ||
750 | fd = mkstemp (tmpl); | ||
751 | if (-1 == fd) | ||
752 | { | ||
753 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
754 | "mkstemp", | ||
755 | tmpl); | ||
756 | GNUNET_free (tmpl); | ||
757 | return GNUNET_SYSERR; | ||
758 | } | ||
759 | |||
760 | if (0 != fchmod (fd, | ||
761 | translate_unix_perms (mode))) | ||
762 | { | ||
763 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
764 | "chmod", | ||
765 | tmpl); | ||
766 | GNUNET_assert (0 == close (fd)); | ||
767 | if (0 != unlink (tmpl)) | ||
768 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
769 | "unlink", | ||
770 | tmpl); | ||
771 | GNUNET_free (tmpl); | ||
772 | return GNUNET_SYSERR; | ||
773 | } | ||
774 | if (buf_size != | ||
775 | write (fd, | ||
776 | buf, | ||
777 | buf_size)) | ||
778 | { | ||
779 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
780 | "write", | ||
781 | tmpl); | ||
782 | GNUNET_assert (0 == close (fd)); | ||
783 | if (0 != unlink (tmpl)) | ||
784 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
785 | "unlink", | ||
786 | tmpl); | ||
787 | GNUNET_free (tmpl); | ||
788 | return GNUNET_SYSERR; | ||
789 | } | ||
790 | GNUNET_assert (0 == close (fd)); | ||
791 | |||
792 | if (0 != link (tmpl, | ||
793 | fn)) | ||
794 | { | ||
795 | if (0 != unlink (tmpl)) | ||
796 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
797 | "unlink", | ||
798 | tmpl); | ||
799 | GNUNET_free (tmpl); | ||
800 | return GNUNET_NO; | ||
801 | } | ||
802 | if (0 != unlink (tmpl)) | ||
803 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
804 | "unlink", | ||
805 | tmpl); | ||
806 | GNUNET_free (tmpl); | ||
807 | return GNUNET_OK; | ||
808 | |||
809 | |||
810 | } | ||
811 | |||
812 | |||
813 | int | ||
814 | GNUNET_DISK_directory_scan (const char *dir_name, | ||
815 | GNUNET_FileNameCallback callback, | ||
816 | void *callback_cls) | ||
817 | { | ||
818 | DIR *dinfo; | ||
819 | struct dirent *finfo; | ||
820 | struct stat istat; | ||
821 | int count = 0; | ||
822 | enum GNUNET_GenericReturnValue ret; | ||
823 | char *name; | ||
824 | char *dname; | ||
825 | unsigned int name_len; | ||
826 | unsigned int n_size; | ||
827 | |||
828 | GNUNET_assert (NULL != dir_name); | ||
829 | dname = GNUNET_STRINGS_filename_expand (dir_name); | ||
830 | if (NULL == dname) | ||
831 | return GNUNET_SYSERR; | ||
832 | while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR)) | ||
833 | dname[strlen (dname) - 1] = '\0'; | ||
834 | if (0 != stat (dname, &istat)) | ||
835 | { | ||
836 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname); | ||
837 | GNUNET_free (dname); | ||
838 | return GNUNET_SYSERR; | ||
839 | } | ||
840 | if (! S_ISDIR (istat.st_mode)) | ||
841 | { | ||
842 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
843 | _ ("Expected `%s' to be a directory!\n"), | ||
844 | dir_name); | ||
845 | GNUNET_free (dname); | ||
846 | return GNUNET_SYSERR; | ||
847 | } | ||
848 | errno = 0; | ||
849 | dinfo = opendir (dname); | ||
850 | if ((EACCES == errno) || (NULL == dinfo)) | ||
851 | { | ||
852 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname); | ||
853 | if (NULL != dinfo) | ||
854 | closedir (dinfo); | ||
855 | GNUNET_free (dname); | ||
856 | return GNUNET_SYSERR; | ||
857 | } | ||
858 | name_len = 256; | ||
859 | n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1; | ||
860 | name = GNUNET_malloc (n_size); | ||
861 | while (NULL != (finfo = readdir (dinfo))) | ||
862 | { | ||
863 | if ((0 == strcmp (finfo->d_name, ".")) || | ||
864 | (0 == strcmp (finfo->d_name, ".."))) | ||
865 | continue; | ||
866 | if (NULL != callback) | ||
867 | { | ||
868 | if (name_len < strlen (finfo->d_name)) | ||
869 | { | ||
870 | GNUNET_free (name); | ||
871 | name_len = strlen (finfo->d_name); | ||
872 | n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1; | ||
873 | name = GNUNET_malloc (n_size); | ||
874 | } | ||
875 | /* dname can end in "/" only if dname == "/"; | ||
876 | * if dname does not end in "/", we need to add | ||
877 | * a "/" (otherwise, we must not!) */ | ||
878 | GNUNET_snprintf (name, | ||
879 | n_size, | ||
880 | "%s%s%s", | ||
881 | dname, | ||
882 | (0 == strcmp (dname, DIR_SEPARATOR_STR)) | ||
883 | ? "" | ||
884 | : DIR_SEPARATOR_STR, | ||
885 | finfo->d_name); | ||
886 | ret = callback (callback_cls, name); | ||
887 | if (GNUNET_OK != ret) | ||
888 | { | ||
889 | closedir (dinfo); | ||
890 | GNUNET_free (name); | ||
891 | GNUNET_free (dname); | ||
892 | if (GNUNET_NO == ret) | ||
893 | return count; | ||
894 | return GNUNET_SYSERR; | ||
895 | } | ||
896 | } | ||
897 | count++; | ||
898 | } | ||
899 | closedir (dinfo); | ||
900 | GNUNET_free (name); | ||
901 | GNUNET_free (dname); | ||
902 | return count; | ||
903 | } | ||
904 | |||
905 | |||
906 | /** | ||
907 | * Check for a simple wildcard match. | ||
908 | * Only asterisks are allowed. | ||
909 | * Asterisks match everything, including slashes. | ||
910 | * | ||
911 | * @param pattern pattern with wildcards | ||
912 | * @param str string to match against | ||
913 | * @returns true on match, false otherwise | ||
914 | */ | ||
915 | static bool | ||
916 | glob_match (const char *pattern, const char *str) | ||
917 | { | ||
918 | /* Position in the input string */ | ||
919 | const char *str_pos = str; | ||
920 | /* Position in the pattern */ | ||
921 | const char *pat_pos = pattern; | ||
922 | /* Backtrack position in string */ | ||
923 | const char *str_bt = NULL; | ||
924 | /* Backtrack position in pattern */ | ||
925 | const char *pat_bt = NULL; | ||
926 | |||
927 | for (;;) | ||
928 | { | ||
929 | if (*pat_pos == '*') | ||
930 | { | ||
931 | str_bt = str_pos; | ||
932 | pat_bt = pat_pos++; | ||
933 | } | ||
934 | else if (*pat_pos == *str_pos) | ||
935 | { | ||
936 | if ('\0' == *pat_pos) | ||
937 | return true; | ||
938 | str_pos++; | ||
939 | pat_pos++; | ||
940 | } | ||
941 | else | ||
942 | { | ||
943 | if (NULL == str_bt) | ||
944 | return false; | ||
945 | /* Backtrack to match one more | ||
946 | character as part of the asterisk. */ | ||
947 | str_pos = str_bt + 1; | ||
948 | if ('\0' == *str_pos) | ||
949 | return false; | ||
950 | pat_pos = pat_bt; | ||
951 | } | ||
952 | } | ||
953 | } | ||
954 | |||
955 | |||
956 | struct GlobClosure | ||
957 | { | ||
958 | const char *glob; | ||
959 | GNUNET_FileNameCallback cb; | ||
960 | void *cls; | ||
961 | |||
962 | /** | ||
963 | * Number of files that actually matched the glob pattern. | ||
964 | */ | ||
965 | int nres; | ||
966 | }; | ||
967 | |||
968 | /** | ||
969 | * Function called with a filename. | ||
970 | * | ||
971 | * @param cls closure | ||
972 | * @param filename complete filename (absolute path) | ||
973 | * @return #GNUNET_OK to continue to iterate, | ||
974 | * #GNUNET_NO to stop iteration with no error, | ||
975 | * #GNUNET_SYSERR to abort iteration with error! | ||
976 | */ | ||
977 | static enum GNUNET_GenericReturnValue | ||
978 | glob_cb (void *cls, | ||
979 | const char *filename) | ||
980 | { | ||
981 | struct GlobClosure *gc = cls; | ||
982 | const char *fn; | ||
983 | |||
984 | fn = strrchr (filename, DIR_SEPARATOR); | ||
985 | fn = (NULL == fn) ? filename : (fn + 1); | ||
986 | |||
987 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
988 | "checking glob '%s' against '%s'\n", | ||
989 | gc->glob, | ||
990 | fn); | ||
991 | |||
992 | if (glob_match (gc->glob, fn)) | ||
993 | { | ||
994 | enum GNUNET_GenericReturnValue cbret; | ||
995 | |||
996 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
997 | "found glob match '%s'\n", | ||
998 | filename); | ||
999 | gc->nres++; | ||
1000 | cbret = gc->cb (gc->cls, filename); | ||
1001 | if (GNUNET_OK != cbret) | ||
1002 | return cbret; | ||
1003 | } | ||
1004 | return GNUNET_OK; | ||
1005 | } | ||
1006 | |||
1007 | |||
1008 | int | ||
1009 | GNUNET_DISK_glob (const char *glob_pattern, | ||
1010 | GNUNET_FileNameCallback callback, | ||
1011 | void *callback_cls) | ||
1012 | { | ||
1013 | char *mypat = GNUNET_strdup (glob_pattern); | ||
1014 | char *sep; | ||
1015 | int ret; | ||
1016 | |||
1017 | if ( (NULL != strrchr (glob_pattern, '+')) || | ||
1018 | (NULL != strrchr (glob_pattern, '[')) || | ||
1019 | (NULL != strrchr (glob_pattern, '+')) || | ||
1020 | (NULL != strrchr (glob_pattern, '~')) ) | ||
1021 | { | ||
1022 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
1023 | "unsupported glob pattern: '%s'\n", | ||
1024 | glob_pattern); | ||
1025 | GNUNET_free (mypat); | ||
1026 | return -1; | ||
1027 | } | ||
1028 | |||
1029 | sep = strrchr (mypat, DIR_SEPARATOR); | ||
1030 | if (NULL == sep) | ||
1031 | { | ||
1032 | GNUNET_free (mypat); | ||
1033 | return -1; | ||
1034 | } | ||
1035 | |||
1036 | *sep = '\0'; | ||
1037 | |||
1038 | if (NULL != strchr (mypat, '*')) | ||
1039 | { | ||
1040 | GNUNET_free (mypat); | ||
1041 | GNUNET_break (0); | ||
1042 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
1043 | "glob pattern may only contain '*' in the final path component\n"); | ||
1044 | return -1; | ||
1045 | } | ||
1046 | |||
1047 | { | ||
1048 | struct GlobClosure gc = { | ||
1049 | .glob = sep + 1, | ||
1050 | .cb = callback, | ||
1051 | .cls = callback_cls, | ||
1052 | .nres = 0, | ||
1053 | }; | ||
1054 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1055 | "scanning directory '%s' for glob matches on '%s'\n", | ||
1056 | mypat, | ||
1057 | gc.glob); | ||
1058 | ret = GNUNET_DISK_directory_scan (mypat, | ||
1059 | glob_cb, | ||
1060 | &gc | ||
1061 | ); | ||
1062 | GNUNET_free (mypat); | ||
1063 | return (ret < 0) ? ret : gc.nres; | ||
1064 | } | ||
1065 | } | ||
1066 | |||
1067 | |||
1068 | /** | ||
1069 | * Function that removes the given directory by calling | ||
1070 | * #GNUNET_DISK_directory_remove(). | ||
1071 | * | ||
1072 | * @param unused not used | ||
1073 | * @param fn directory to remove | ||
1074 | * @return #GNUNET_OK | ||
1075 | */ | ||
1076 | static enum GNUNET_GenericReturnValue | ||
1077 | remove_helper (void *unused, | ||
1078 | const char *fn) | ||
1079 | { | ||
1080 | (void) unused; | ||
1081 | (void) GNUNET_DISK_directory_remove (fn); | ||
1082 | return GNUNET_OK; | ||
1083 | } | ||
1084 | |||
1085 | |||
1086 | enum GNUNET_GenericReturnValue | ||
1087 | GNUNET_DISK_directory_remove (const char *filename) | ||
1088 | { | ||
1089 | struct stat istat; | ||
1090 | |||
1091 | if (NULL == filename) | ||
1092 | { | ||
1093 | GNUNET_break (0); | ||
1094 | return GNUNET_SYSERR; | ||
1095 | } | ||
1096 | if (0 != lstat (filename, &istat)) | ||
1097 | return GNUNET_NO; /* file may not exist... */ | ||
1098 | (void) chmod (filename, | ||
1099 | S_IWUSR | S_IRUSR | S_IXUSR); | ||
1100 | if (0 == unlink (filename)) | ||
1101 | return GNUNET_OK; | ||
1102 | if ( (errno != EISDIR) && | ||
1103 | /* EISDIR is not sufficient in all cases, e.g. | ||
1104 | * sticky /tmp directory may result in EPERM on BSD. | ||
1105 | * So we also explicitly check "isDirectory" */ | ||
1106 | (GNUNET_YES != | ||
1107 | GNUNET_DISK_directory_test (filename, | ||
1108 | GNUNET_YES)) ) | ||
1109 | { | ||
1110 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename); | ||
1111 | return GNUNET_SYSERR; | ||
1112 | } | ||
1113 | if (GNUNET_SYSERR == | ||
1114 | GNUNET_DISK_directory_scan (filename, &remove_helper, NULL)) | ||
1115 | return GNUNET_SYSERR; | ||
1116 | if (0 != rmdir (filename)) | ||
1117 | { | ||
1118 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename); | ||
1119 | return GNUNET_SYSERR; | ||
1120 | } | ||
1121 | return GNUNET_OK; | ||
1122 | } | ||
1123 | |||
1124 | |||
1125 | enum GNUNET_GenericReturnValue | ||
1126 | GNUNET_DISK_file_copy (const char *src, | ||
1127 | const char *dst) | ||
1128 | { | ||
1129 | char *buf; | ||
1130 | uint64_t pos; | ||
1131 | uint64_t size; | ||
1132 | size_t len; | ||
1133 | ssize_t sret; | ||
1134 | struct GNUNET_DISK_FileHandle *in; | ||
1135 | struct GNUNET_DISK_FileHandle *out; | ||
1136 | |||
1137 | if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES)) | ||
1138 | { | ||
1139 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "stat", src); | ||
1140 | return GNUNET_SYSERR; | ||
1141 | } | ||
1142 | pos = 0; | ||
1143 | in = | ||
1144 | GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE); | ||
1145 | if (! in) | ||
1146 | { | ||
1147 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", src); | ||
1148 | return GNUNET_SYSERR; | ||
1149 | } | ||
1150 | out = | ||
1151 | GNUNET_DISK_file_open (dst, | ||
1152 | GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE | ||
1153 | | GNUNET_DISK_OPEN_FAILIFEXISTS, | ||
1154 | GNUNET_DISK_PERM_USER_READ | ||
1155 | | GNUNET_DISK_PERM_USER_WRITE | ||
1156 | | GNUNET_DISK_PERM_GROUP_READ | ||
1157 | | GNUNET_DISK_PERM_GROUP_WRITE); | ||
1158 | if (! out) | ||
1159 | { | ||
1160 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", dst); | ||
1161 | GNUNET_DISK_file_close (in); | ||
1162 | return GNUNET_SYSERR; | ||
1163 | } | ||
1164 | buf = GNUNET_malloc (COPY_BLK_SIZE); | ||
1165 | while (pos < size) | ||
1166 | { | ||
1167 | len = COPY_BLK_SIZE; | ||
1168 | if (len > size - pos) | ||
1169 | len = size - pos; | ||
1170 | sret = GNUNET_DISK_file_read (in, buf, len); | ||
1171 | if ((sret < 0) || (len != (size_t) sret)) | ||
1172 | goto FAIL; | ||
1173 | sret = GNUNET_DISK_file_write (out, buf, len); | ||
1174 | if ((sret < 0) || (len != (size_t) sret)) | ||
1175 | goto FAIL; | ||
1176 | pos += len; | ||
1177 | } | ||
1178 | GNUNET_free (buf); | ||
1179 | GNUNET_DISK_file_close (in); | ||
1180 | GNUNET_DISK_file_close (out); | ||
1181 | return GNUNET_OK; | ||
1182 | FAIL: | ||
1183 | GNUNET_free (buf); | ||
1184 | GNUNET_DISK_file_close (in); | ||
1185 | GNUNET_DISK_file_close (out); | ||
1186 | return GNUNET_SYSERR; | ||
1187 | } | ||
1188 | |||
1189 | |||
1190 | void | ||
1191 | GNUNET_DISK_filename_canonicalize (char *fn) | ||
1192 | { | ||
1193 | char *idx; | ||
1194 | char c; | ||
1195 | |||
1196 | for (idx = fn; *idx; idx++) | ||
1197 | { | ||
1198 | c = *idx; | ||
1199 | |||
1200 | if ((c == '/') || (c == '\\') || (c == ':') || (c == '*') || (c == '?') || | ||
1201 | (c == | ||
1202 | '"') | ||
1203 | || | ||
1204 | (c == '<') || (c == '>') || (c == '|') ) | ||
1205 | { | ||
1206 | *idx = '_'; | ||
1207 | } | ||
1208 | } | ||
1209 | } | ||
1210 | |||
1211 | |||
1212 | enum GNUNET_GenericReturnValue | ||
1213 | GNUNET_DISK_file_change_owner (const char *filename, | ||
1214 | const char *user) | ||
1215 | { | ||
1216 | struct passwd *pws; | ||
1217 | |||
1218 | pws = getpwnam (user); | ||
1219 | if (NULL == pws) | ||
1220 | { | ||
1221 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
1222 | _ ("Cannot obtain information about user `%s': %s\n"), | ||
1223 | user, | ||
1224 | strerror (errno)); | ||
1225 | return GNUNET_SYSERR; | ||
1226 | } | ||
1227 | if (0 != chown (filename, pws->pw_uid, pws->pw_gid)) | ||
1228 | { | ||
1229 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename); | ||
1230 | return GNUNET_SYSERR; | ||
1231 | } | ||
1232 | return GNUNET_OK; | ||
1233 | } | ||
1234 | |||
1235 | |||
1236 | struct GNUNET_DISK_FileHandle * | ||
1237 | GNUNET_DISK_file_open (const char *fn, | ||
1238 | enum GNUNET_DISK_OpenFlags flags, | ||
1239 | enum GNUNET_DISK_AccessPermissions perm) | ||
1240 | { | ||
1241 | char *expfn; | ||
1242 | struct GNUNET_DISK_FileHandle *ret; | ||
1243 | |||
1244 | int oflags; | ||
1245 | int mode; | ||
1246 | int fd; | ||
1247 | |||
1248 | expfn = GNUNET_STRINGS_filename_expand (fn); | ||
1249 | if (NULL == expfn) | ||
1250 | return NULL; | ||
1251 | |||
1252 | mode = 0; | ||
1253 | if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE)) | ||
1254 | oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */ | ||
1255 | else if (flags & GNUNET_DISK_OPEN_READ) | ||
1256 | oflags = O_RDONLY; | ||
1257 | else if (flags & GNUNET_DISK_OPEN_WRITE) | ||
1258 | oflags = O_WRONLY; | ||
1259 | else | ||
1260 | { | ||
1261 | GNUNET_break (0); | ||
1262 | GNUNET_free (expfn); | ||
1263 | return NULL; | ||
1264 | } | ||
1265 | if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS) | ||
1266 | oflags |= (O_CREAT | O_EXCL); | ||
1267 | if (flags & GNUNET_DISK_OPEN_TRUNCATE) | ||
1268 | oflags |= O_TRUNC; | ||
1269 | if (flags & GNUNET_DISK_OPEN_APPEND) | ||
1270 | oflags |= O_APPEND; | ||
1271 | if (GNUNET_NO == GNUNET_DISK_file_test (fn)) | ||
1272 | { | ||
1273 | if (flags & GNUNET_DISK_OPEN_CREATE) | ||
1274 | { | ||
1275 | (void) GNUNET_DISK_directory_create_for_file (expfn); | ||
1276 | oflags |= O_CREAT; | ||
1277 | mode = translate_unix_perms (perm); | ||
1278 | } | ||
1279 | } | ||
1280 | |||
1281 | fd = open (expfn, | ||
1282 | oflags | ||
1283 | #if O_CLOEXEC | ||
1284 | | O_CLOEXEC | ||
1285 | #endif | ||
1286 | | O_LARGEFILE, | ||
1287 | mode); | ||
1288 | if (fd == -1) | ||
1289 | { | ||
1290 | if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)) | ||
1291 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn); | ||
1292 | else | ||
1293 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn); | ||
1294 | GNUNET_free (expfn); | ||
1295 | return NULL; | ||
1296 | } | ||
1297 | |||
1298 | ret = GNUNET_new (struct GNUNET_DISK_FileHandle); | ||
1299 | |||
1300 | ret->fd = fd; | ||
1301 | |||
1302 | GNUNET_free (expfn); | ||
1303 | return ret; | ||
1304 | } | ||
1305 | |||
1306 | |||
1307 | enum GNUNET_GenericReturnValue | ||
1308 | GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h) | ||
1309 | { | ||
1310 | enum GNUNET_GenericReturnValue ret; | ||
1311 | |||
1312 | if (NULL == h) | ||
1313 | { | ||
1314 | errno = EINVAL; | ||
1315 | return GNUNET_SYSERR; | ||
1316 | } | ||
1317 | |||
1318 | ret = GNUNET_OK; | ||
1319 | if (0 != close (h->fd)) | ||
1320 | { | ||
1321 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close"); | ||
1322 | ret = GNUNET_SYSERR; | ||
1323 | } | ||
1324 | GNUNET_free (h); | ||
1325 | return ret; | ||
1326 | } | ||
1327 | |||
1328 | |||
1329 | struct GNUNET_DISK_FileHandle * | ||
1330 | GNUNET_DISK_get_handle_from_int_fd (int fno) | ||
1331 | { | ||
1332 | struct GNUNET_DISK_FileHandle *fh; | ||
1333 | |||
1334 | if ((((off_t) -1) == lseek (fno, 0, SEEK_CUR)) && (EBADF == errno)) | ||
1335 | return NULL; /* invalid FD */ | ||
1336 | |||
1337 | fh = GNUNET_new (struct GNUNET_DISK_FileHandle); | ||
1338 | |||
1339 | fh->fd = fno; | ||
1340 | |||
1341 | return fh; | ||
1342 | } | ||
1343 | |||
1344 | |||
1345 | struct GNUNET_DISK_FileHandle * | ||
1346 | GNUNET_DISK_get_handle_from_native (FILE *fd) | ||
1347 | { | ||
1348 | int fno; | ||
1349 | |||
1350 | fno = fileno (fd); | ||
1351 | if (-1 == fno) | ||
1352 | return NULL; | ||
1353 | return GNUNET_DISK_get_handle_from_int_fd (fno); | ||
1354 | } | ||
1355 | |||
1356 | |||
1357 | /** | ||
1358 | * Handle for a memory-mapping operation. | ||
1359 | */ | ||
1360 | struct GNUNET_DISK_MapHandle | ||
1361 | { | ||
1362 | /** | ||
1363 | * Address where the map is in memory. | ||
1364 | */ | ||
1365 | void *addr; | ||
1366 | |||
1367 | /** | ||
1368 | * Number of bytes mapped. | ||
1369 | */ | ||
1370 | size_t len; | ||
1371 | }; | ||
1372 | |||
1373 | |||
1374 | #ifndef MAP_FAILED | ||
1375 | #define MAP_FAILED ((void *) -1) | ||
1376 | #endif | ||
1377 | |||
1378 | |||
1379 | void * | ||
1380 | GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, | ||
1381 | struct GNUNET_DISK_MapHandle **m, | ||
1382 | enum GNUNET_DISK_MapType access, | ||
1383 | size_t len) | ||
1384 | { | ||
1385 | int prot; | ||
1386 | |||
1387 | if (NULL == h) | ||
1388 | { | ||
1389 | errno = EINVAL; | ||
1390 | return NULL; | ||
1391 | } | ||
1392 | prot = 0; | ||
1393 | if (access & GNUNET_DISK_MAP_TYPE_READ) | ||
1394 | prot = PROT_READ; | ||
1395 | if (access & GNUNET_DISK_MAP_TYPE_WRITE) | ||
1396 | prot |= PROT_WRITE; | ||
1397 | *m = GNUNET_new (struct GNUNET_DISK_MapHandle); | ||
1398 | (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0); | ||
1399 | GNUNET_assert (NULL != (*m)->addr); | ||
1400 | if (MAP_FAILED == (*m)->addr) | ||
1401 | { | ||
1402 | GNUNET_free (*m); | ||
1403 | return NULL; | ||
1404 | } | ||
1405 | (*m)->len = len; | ||
1406 | return (*m)->addr; | ||
1407 | } | ||
1408 | |||
1409 | |||
1410 | enum GNUNET_GenericReturnValue | ||
1411 | GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h) | ||
1412 | { | ||
1413 | enum GNUNET_GenericReturnValue ret; | ||
1414 | |||
1415 | if (NULL == h) | ||
1416 | { | ||
1417 | errno = EINVAL; | ||
1418 | return GNUNET_SYSERR; | ||
1419 | } | ||
1420 | ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR; | ||
1421 | GNUNET_free (h); | ||
1422 | return ret; | ||
1423 | } | ||
1424 | |||
1425 | |||
1426 | enum GNUNET_GenericReturnValue | ||
1427 | GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h) | ||
1428 | { | ||
1429 | if (h == NULL) | ||
1430 | { | ||
1431 | errno = EINVAL; | ||
1432 | return GNUNET_SYSERR; | ||
1433 | } | ||
1434 | |||
1435 | #if ! defined(__linux__) || ! defined(GNU) | ||
1436 | return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK; | ||
1437 | #else | ||
1438 | return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK; | ||
1439 | #endif | ||
1440 | } | ||
1441 | |||
1442 | |||
1443 | struct GNUNET_DISK_PipeHandle * | ||
1444 | GNUNET_DISK_pipe (enum GNUNET_DISK_PipeFlags pf) | ||
1445 | { | ||
1446 | int fd[2]; | ||
1447 | |||
1448 | if (-1 == pipe (fd)) | ||
1449 | { | ||
1450 | int eno = errno; | ||
1451 | |||
1452 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe"); | ||
1453 | errno = eno; | ||
1454 | return NULL; | ||
1455 | } | ||
1456 | return GNUNET_DISK_pipe_from_fd (pf, fd); | ||
1457 | } | ||
1458 | |||
1459 | |||
1460 | struct GNUNET_DISK_PipeHandle * | ||
1461 | GNUNET_DISK_pipe_from_fd (enum GNUNET_DISK_PipeFlags pf, | ||
1462 | int fd[2]) | ||
1463 | { | ||
1464 | struct GNUNET_DISK_PipeHandle *p; | ||
1465 | int ret = 0; | ||
1466 | int flags; | ||
1467 | int eno = 0; /* make gcc happy */ | ||
1468 | |||
1469 | p = GNUNET_new (struct GNUNET_DISK_PipeHandle); | ||
1470 | if (fd[0] >= 0) | ||
1471 | { | ||
1472 | p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle); | ||
1473 | p->fd[0]->fd = fd[0]; | ||
1474 | if (0 == (GNUNET_DISK_PF_BLOCKING_READ & pf)) | ||
1475 | { | ||
1476 | flags = fcntl (fd[0], F_GETFL); | ||
1477 | flags |= O_NONBLOCK; | ||
1478 | if (0 > fcntl (fd[0], F_SETFL, flags)) | ||
1479 | { | ||
1480 | ret = -1; | ||
1481 | eno = errno; | ||
1482 | } | ||
1483 | } | ||
1484 | flags = fcntl (fd[0], F_GETFD); | ||
1485 | flags |= FD_CLOEXEC; | ||
1486 | if (0 > fcntl (fd[0], F_SETFD, flags)) | ||
1487 | { | ||
1488 | ret = -1; | ||
1489 | eno = errno; | ||
1490 | } | ||
1491 | } | ||
1492 | |||
1493 | if (fd[1] >= 0) | ||
1494 | { | ||
1495 | p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle); | ||
1496 | p->fd[1]->fd = fd[1]; | ||
1497 | if (0 == (GNUNET_DISK_PF_BLOCKING_WRITE & pf)) | ||
1498 | { | ||
1499 | flags = fcntl (fd[1], F_GETFL); | ||
1500 | flags |= O_NONBLOCK; | ||
1501 | if (0 > fcntl (fd[1], F_SETFL, flags)) | ||
1502 | { | ||
1503 | ret = -1; | ||
1504 | eno = errno; | ||
1505 | } | ||
1506 | } | ||
1507 | flags = fcntl (fd[1], F_GETFD); | ||
1508 | flags |= FD_CLOEXEC; | ||
1509 | if (0 > fcntl (fd[1], F_SETFD, flags)) | ||
1510 | { | ||
1511 | ret = -1; | ||
1512 | eno = errno; | ||
1513 | } | ||
1514 | } | ||
1515 | if (ret == -1) | ||
1516 | { | ||
1517 | errno = eno; | ||
1518 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl"); | ||
1519 | if (p->fd[0]->fd >= 0) | ||
1520 | GNUNET_break (0 == close (p->fd[0]->fd)); | ||
1521 | if (p->fd[1]->fd >= 0) | ||
1522 | GNUNET_break (0 == close (p->fd[1]->fd)); | ||
1523 | GNUNET_free (p->fd[0]); | ||
1524 | GNUNET_free (p->fd[1]); | ||
1525 | GNUNET_free (p); | ||
1526 | errno = eno; | ||
1527 | return NULL; | ||
1528 | } | ||
1529 | return p; | ||
1530 | } | ||
1531 | |||
1532 | |||
1533 | enum GNUNET_GenericReturnValue | ||
1534 | GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p, | ||
1535 | enum GNUNET_DISK_PipeEnd end) | ||
1536 | { | ||
1537 | enum GNUNET_GenericReturnValue ret = GNUNET_OK; | ||
1538 | |||
1539 | if (end == GNUNET_DISK_PIPE_END_READ) | ||
1540 | { | ||
1541 | if (p->fd[0]) | ||
1542 | { | ||
1543 | ret = GNUNET_DISK_file_close (p->fd[0]); | ||
1544 | p->fd[0] = NULL; | ||
1545 | } | ||
1546 | } | ||
1547 | else if (end == GNUNET_DISK_PIPE_END_WRITE) | ||
1548 | { | ||
1549 | if (p->fd[1]) | ||
1550 | { | ||
1551 | ret = GNUNET_DISK_file_close (p->fd[1]); | ||
1552 | p->fd[1] = NULL; | ||
1553 | } | ||
1554 | } | ||
1555 | return ret; | ||
1556 | } | ||
1557 | |||
1558 | |||
1559 | struct GNUNET_DISK_FileHandle * | ||
1560 | GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p, | ||
1561 | enum GNUNET_DISK_PipeEnd end) | ||
1562 | { | ||
1563 | struct GNUNET_DISK_FileHandle *ret = NULL; | ||
1564 | |||
1565 | if (end == GNUNET_DISK_PIPE_END_READ) | ||
1566 | { | ||
1567 | if (p->fd[0]) | ||
1568 | { | ||
1569 | ret = p->fd[0]; | ||
1570 | p->fd[0] = NULL; | ||
1571 | } | ||
1572 | } | ||
1573 | else if (end == GNUNET_DISK_PIPE_END_WRITE) | ||
1574 | { | ||
1575 | if (p->fd[1]) | ||
1576 | { | ||
1577 | ret = p->fd[1]; | ||
1578 | p->fd[1] = NULL; | ||
1579 | } | ||
1580 | } | ||
1581 | |||
1582 | return ret; | ||
1583 | } | ||
1584 | |||
1585 | |||
1586 | enum GNUNET_GenericReturnValue | ||
1587 | GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p) | ||
1588 | { | ||
1589 | enum GNUNET_GenericReturnValue ret = GNUNET_OK; | ||
1590 | enum GNUNET_GenericReturnValue read_end_close; | ||
1591 | enum GNUNET_GenericReturnValue write_end_close; | ||
1592 | int read_end_close_errno; | ||
1593 | int write_end_close_errno; | ||
1594 | |||
1595 | read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ); | ||
1596 | read_end_close_errno = errno; | ||
1597 | write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE); | ||
1598 | write_end_close_errno = errno; | ||
1599 | GNUNET_free (p); | ||
1600 | |||
1601 | if (GNUNET_OK != read_end_close) | ||
1602 | { | ||
1603 | errno = read_end_close_errno; | ||
1604 | ret = read_end_close; | ||
1605 | } | ||
1606 | else if (GNUNET_OK != write_end_close) | ||
1607 | { | ||
1608 | errno = write_end_close_errno; | ||
1609 | ret = write_end_close; | ||
1610 | } | ||
1611 | |||
1612 | return ret; | ||
1613 | } | ||
1614 | |||
1615 | |||
1616 | const struct GNUNET_DISK_FileHandle * | ||
1617 | GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p, | ||
1618 | enum GNUNET_DISK_PipeEnd n) | ||
1619 | { | ||
1620 | switch (n) | ||
1621 | { | ||
1622 | case GNUNET_DISK_PIPE_END_READ: | ||
1623 | case GNUNET_DISK_PIPE_END_WRITE: | ||
1624 | return p->fd[n]; | ||
1625 | |||
1626 | default: | ||
1627 | GNUNET_break (0); | ||
1628 | return NULL; | ||
1629 | } | ||
1630 | } | ||
1631 | |||
1632 | |||
1633 | enum GNUNET_GenericReturnValue | ||
1634 | GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh, | ||
1635 | void *dst, | ||
1636 | size_t dst_len) | ||
1637 | { | ||
1638 | if (NULL == fh) | ||
1639 | return GNUNET_SYSERR; | ||
1640 | if (dst_len < sizeof(int)) | ||
1641 | return GNUNET_SYSERR; | ||
1642 | *((int *) dst) = fh->fd; | ||
1643 | return GNUNET_OK; | ||
1644 | } | ||
1645 | |||
1646 | |||
1647 | /** | ||
1648 | * Helper function for #GNUNET_DISK_purge_cfg_dir. | ||
1649 | * | ||
1650 | * @param cls a `const char *` with the option to purge | ||
1651 | * @param cfg our configuration | ||
1652 | * @return #GNUNET_OK on success | ||
1653 | */ | ||
1654 | static enum GNUNET_GenericReturnValue | ||
1655 | purge_cfg_dir (void *cls, | ||
1656 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
1657 | { | ||
1658 | const char *option = cls; | ||
1659 | char *tmpname; | ||
1660 | |||
1661 | if (GNUNET_OK != | ||
1662 | GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", option, &tmpname)) | ||
1663 | { | ||
1664 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "PATHS", option); | ||
1665 | return GNUNET_NO; | ||
1666 | } | ||
1667 | if (GNUNET_SYSERR == GNUNET_DISK_directory_remove (tmpname)) | ||
1668 | { | ||
1669 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "remove", tmpname); | ||
1670 | GNUNET_free (tmpname); | ||
1671 | return GNUNET_OK; | ||
1672 | } | ||
1673 | GNUNET_free (tmpname); | ||
1674 | return GNUNET_OK; | ||
1675 | } | ||
1676 | |||
1677 | |||
1678 | void | ||
1679 | GNUNET_DISK_purge_cfg_dir (const char *cfg_filename, | ||
1680 | const char *option) | ||
1681 | { | ||
1682 | GNUNET_break (GNUNET_OK == | ||
1683 | GNUNET_CONFIGURATION_parse_and_run (cfg_filename, | ||
1684 | &purge_cfg_dir, | ||
1685 | (void *) option)); | ||
1686 | } | ||
1687 | |||
1688 | |||
1689 | /* end of disk.c */ | ||