diff options
Diffstat (limited to 'src/util/disk.c')
-rw-r--r-- | src/util/disk.c | 1688 |
1 files changed, 0 insertions, 1688 deletions
diff --git a/src/util/disk.c b/src/util/disk.c deleted file mode 100644 index 2efb52d46..000000000 --- a/src/util/disk.c +++ /dev/null | |||
@@ -1,1688 +0,0 @@ | |||
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 | #include "platform.h" | ||
27 | #include "disk.h" | ||
28 | #include "gnunet_strings_lib.h" | ||
29 | #include "gnunet_disk_lib.h" | ||
30 | |||
31 | #define LOG(kind, ...) GNUNET_log_from (kind, "util-disk", __VA_ARGS__) | ||
32 | |||
33 | #define LOG_STRERROR(kind, syscall) \ | ||
34 | GNUNET_log_from_strerror (kind, "util-disk", syscall) | ||
35 | |||
36 | #define LOG_STRERROR_FILE(kind, syscall, filename) \ | ||
37 | GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename) | ||
38 | |||
39 | /** | ||
40 | * Block size for IO for copying files. | ||
41 | */ | ||
42 | #define COPY_BLK_SIZE 65536 | ||
43 | |||
44 | #include <sys/types.h> | ||
45 | #if HAVE_SYS_VFS_H | ||
46 | #include <sys/vfs.h> | ||
47 | #endif | ||
48 | #if HAVE_SYS_PARAM_H | ||
49 | #include <sys/param.h> | ||
50 | #endif | ||
51 | #if HAVE_SYS_MOUNT_H | ||
52 | #include <sys/mount.h> | ||
53 | #endif | ||
54 | #if HAVE_SYS_STATVFS_H | ||
55 | #include <sys/statvfs.h> | ||
56 | #endif | ||
57 | |||
58 | #ifndef S_ISLNK | ||
59 | #define _IFMT 0170000 /* type of file */ | ||
60 | #define _IFLNK 0120000 /* symbolic link */ | ||
61 | #define S_ISLNK(m) (((m) & _IFMT) == _IFLNK) | ||
62 | #endif | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Handle used to manage a pipe. | ||
67 | */ | ||
68 | struct GNUNET_DISK_PipeHandle | ||
69 | { | ||
70 | /** | ||
71 | * File descriptors for the pipe. | ||
72 | * One or both of them could be NULL. | ||
73 | */ | ||
74 | struct GNUNET_DISK_FileHandle *fd[2]; | ||
75 | }; | ||
76 | |||
77 | |||
78 | /** | ||
79 | * Closure for the recursion to determine the file size | ||
80 | * of a directory. | ||
81 | */ | ||
82 | struct GetFileSizeData | ||
83 | { | ||
84 | /** | ||
85 | * Set to the total file size. | ||
86 | */ | ||
87 | uint64_t total; | ||
88 | |||
89 | /** | ||
90 | * GNUNET_YES if symbolic links should be included. | ||
91 | */ | ||
92 | int include_sym_links; | ||
93 | |||
94 | /** | ||
95 | * #GNUNET_YES if mode is file-only (return total == -1 for directories). | ||
96 | */ | ||
97 | int single_file_mode; | ||
98 | }; | ||
99 | |||
100 | |||
101 | /** | ||
102 | * Translate GNUnet-internal permission bitmap to UNIX file | ||
103 | * access permission bitmap. | ||
104 | * | ||
105 | * @param perm file permissions, GNUnet style | ||
106 | * @return file permissions, UNIX style | ||
107 | */ | ||
108 | static int | ||
109 | translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm) | ||
110 | { | ||
111 | int mode; | ||
112 | |||
113 | mode = 0; | ||
114 | if (perm & GNUNET_DISK_PERM_USER_READ) | ||
115 | mode |= S_IRUSR; | ||
116 | if (perm & GNUNET_DISK_PERM_USER_WRITE) | ||
117 | mode |= S_IWUSR; | ||
118 | if (perm & GNUNET_DISK_PERM_USER_EXEC) | ||
119 | mode |= S_IXUSR; | ||
120 | if (perm & GNUNET_DISK_PERM_GROUP_READ) | ||
121 | mode |= S_IRGRP; | ||
122 | if (perm & GNUNET_DISK_PERM_GROUP_WRITE) | ||
123 | mode |= S_IWGRP; | ||
124 | if (perm & GNUNET_DISK_PERM_GROUP_EXEC) | ||
125 | mode |= S_IXGRP; | ||
126 | if (perm & GNUNET_DISK_PERM_OTHER_READ) | ||
127 | mode |= S_IROTH; | ||
128 | if (perm & GNUNET_DISK_PERM_OTHER_WRITE) | ||
129 | mode |= S_IWOTH; | ||
130 | if (perm & GNUNET_DISK_PERM_OTHER_EXEC) | ||
131 | mode |= S_IXOTH; | ||
132 | |||
133 | return mode; | ||
134 | } | ||
135 | |||
136 | |||
137 | /** | ||
138 | * Iterate over all files in the given directory and | ||
139 | * accumulate their size. | ||
140 | * | ||
141 | * @param cls closure of type `struct GetFileSizeData` | ||
142 | * @param fn current filename we are looking at | ||
143 | * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK | ||
144 | */ | ||
145 | static enum GNUNET_GenericReturnValue | ||
146 | get_size_rec (void *cls, const char *fn) | ||
147 | { | ||
148 | struct GetFileSizeData *gfsd = cls; | ||
149 | |||
150 | #if defined(HAVE_STAT64) && \ | ||
151 | ! (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) | ||
152 | struct stat64 buf; | ||
153 | |||
154 | if (0 != stat64 (fn, &buf)) | ||
155 | { | ||
156 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn); | ||
157 | return GNUNET_SYSERR; | ||
158 | } | ||
159 | #else | ||
160 | struct stat buf; | ||
161 | |||
162 | if (0 != stat (fn, &buf)) | ||
163 | { | ||
164 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn); | ||
165 | return GNUNET_SYSERR; | ||
166 | } | ||
167 | #endif | ||
168 | if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES)) | ||
169 | { | ||
170 | errno = EISDIR; | ||
171 | return GNUNET_SYSERR; | ||
172 | } | ||
173 | if ((! S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)) | ||
174 | gfsd->total += buf.st_size; | ||
175 | if ((S_ISDIR (buf.st_mode)) && (0 == access (fn, X_OK)) && | ||
176 | ((! S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))) | ||
177 | { | ||
178 | if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &get_size_rec, gfsd)) | ||
179 | return GNUNET_SYSERR; | ||
180 | } | ||
181 | return GNUNET_OK; | ||
182 | } | ||
183 | |||
184 | |||
185 | enum GNUNET_GenericReturnValue | ||
186 | GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h) | ||
187 | { | ||
188 | return ((! h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO; | ||
189 | } | ||
190 | |||
191 | |||
192 | enum GNUNET_GenericReturnValue | ||
193 | GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh, | ||
194 | off_t *size) | ||
195 | { | ||
196 | struct stat sbuf; | ||
197 | |||
198 | if (0 != fstat (fh->fd, &sbuf)) | ||
199 | return GNUNET_SYSERR; | ||
200 | *size = sbuf.st_size; | ||
201 | return GNUNET_OK; | ||
202 | } | ||
203 | |||
204 | |||
205 | off_t | ||
206 | GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h, | ||
207 | off_t offset, | ||
208 | enum GNUNET_DISK_Seek whence) | ||
209 | { | ||
210 | static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END }; | ||
211 | |||
212 | if (h == NULL) | ||
213 | { | ||
214 | errno = EINVAL; | ||
215 | return GNUNET_SYSERR; | ||
216 | } | ||
217 | return lseek (h->fd, offset, t[whence]); | ||
218 | } | ||
219 | |||
220 | |||
221 | enum GNUNET_GenericReturnValue | ||
222 | GNUNET_DISK_file_size (const char *filename, | ||
223 | uint64_t *size, | ||
224 | int include_symbolic_links, | ||
225 | int single_file_mode) | ||
226 | { | ||
227 | struct GetFileSizeData gfsd; | ||
228 | enum GNUNET_GenericReturnValue ret; | ||
229 | |||
230 | GNUNET_assert (size != NULL); | ||
231 | gfsd.total = 0; | ||
232 | gfsd.include_sym_links = include_symbolic_links; | ||
233 | gfsd.single_file_mode = single_file_mode; | ||
234 | ret = get_size_rec (&gfsd, filename); | ||
235 | *size = gfsd.total; | ||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | |||
240 | enum GNUNET_GenericReturnValue | ||
241 | GNUNET_DISK_file_get_identifiers (const char *filename, | ||
242 | uint64_t *dev, | ||
243 | uint64_t *ino) | ||
244 | { | ||
245 | #if HAVE_STAT | ||
246 | { | ||
247 | struct stat sbuf; | ||
248 | |||
249 | if (0 != stat (filename, &sbuf)) | ||
250 | { | ||
251 | return GNUNET_SYSERR; | ||
252 | } | ||
253 | *ino = (uint64_t) sbuf.st_ino; | ||
254 | } | ||
255 | #else | ||
256 | *ino = 0; | ||
257 | #endif | ||
258 | #if HAVE_STATVFS | ||
259 | { | ||
260 | struct statvfs fbuf; | ||
261 | |||
262 | if (0 != statvfs (filename, &fbuf)) | ||
263 | { | ||
264 | return GNUNET_SYSERR; | ||
265 | } | ||
266 | *dev = (uint64_t) fbuf.f_fsid; | ||
267 | } | ||
268 | #elif HAVE_STATFS | ||
269 | { | ||
270 | struct statfs fbuf; | ||
271 | |||
272 | if (0 != statfs (filename, &fbuf)) | ||
273 | { | ||
274 | return GNUNET_SYSERR; | ||
275 | } | ||
276 | *dev = | ||
277 | ((uint64_t) fbuf.f_fsid.val[0]) << 32 || ((uint64_t) fbuf.f_fsid.val[1]); | ||
278 | } | ||
279 | #else | ||
280 | *dev = 0; | ||
281 | #endif | ||
282 | return GNUNET_OK; | ||
283 | } | ||
284 | |||
285 | |||
286 | /** | ||
287 | * Create the name for a temporary file or directory from a template. | ||
288 | * | ||
289 | * @param t template (without XXXXX or "/tmp/") | ||
290 | * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error | ||
291 | */ | ||
292 | static char * | ||
293 | mktemp_name (const char *t) | ||
294 | { | ||
295 | const char *tmpdir; | ||
296 | char *tmpl; | ||
297 | char *fn; | ||
298 | |||
299 | if ((t[0] != '/') && (t[0] != '\\')) | ||
300 | { | ||
301 | /* FIXME: This uses system codepage on W32, not UTF-8 */ | ||
302 | tmpdir = getenv ("TMPDIR"); | ||
303 | if (NULL == tmpdir) | ||
304 | tmpdir = getenv ("TMP"); | ||
305 | if (NULL == tmpdir) | ||
306 | tmpdir = getenv ("TEMP"); | ||
307 | if (NULL == tmpdir) | ||
308 | tmpdir = "/tmp"; | ||
309 | GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX"); | ||
310 | } | ||
311 | else | ||
312 | { | ||
313 | GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX"); | ||
314 | } | ||
315 | fn = tmpl; | ||
316 | return fn; | ||
317 | } | ||
318 | |||
319 | |||
320 | void | ||
321 | GNUNET_DISK_fix_permissions (const char *fn, | ||
322 | int require_uid_match, | ||
323 | int require_gid_match) | ||
324 | { | ||
325 | mode_t mode; | ||
326 | |||
327 | if (GNUNET_YES == require_uid_match) | ||
328 | mode = S_IRUSR | S_IWUSR | S_IXUSR; | ||
329 | else if (GNUNET_YES == require_gid_match) | ||
330 | mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP; | ||
331 | else | ||
332 | mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | ||
333 | | S_IWOTH | S_IXOTH; | ||
334 | if (0 != chmod (fn, mode)) | ||
335 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn); | ||
336 | } | ||
337 | |||
338 | |||
339 | char * | ||
340 | GNUNET_DISK_mkdtemp (const char *t) | ||
341 | { | ||
342 | char *fn; | ||
343 | mode_t omask; | ||
344 | |||
345 | omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH); | ||
346 | fn = mktemp_name (t); | ||
347 | if (fn != mkdtemp (fn)) | ||
348 | { | ||
349 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn); | ||
350 | GNUNET_free (fn); | ||
351 | umask (omask); | ||
352 | return NULL; | ||
353 | } | ||
354 | umask (omask); | ||
355 | return fn; | ||
356 | } | ||
357 | |||
358 | |||
359 | void | ||
360 | GNUNET_DISK_file_backup (const char *fil) | ||
361 | { | ||
362 | size_t slen; | ||
363 | char *target; | ||
364 | unsigned int num; | ||
365 | |||
366 | slen = strlen (fil) + 20; | ||
367 | target = GNUNET_malloc (slen); | ||
368 | num = 0; | ||
369 | do | ||
370 | { | ||
371 | GNUNET_snprintf (target, slen, "%s.%u~", fil, num++); | ||
372 | } | ||
373 | while (0 == access (target, F_OK)); | ||
374 | if (0 != rename (fil, target)) | ||
375 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "rename", fil); | ||
376 | GNUNET_free (target); | ||
377 | } | ||
378 | |||
379 | |||
380 | char * | ||
381 | GNUNET_DISK_mktemp (const char *t) | ||
382 | { | ||
383 | int fd; | ||
384 | char *fn; | ||
385 | mode_t omask; | ||
386 | |||
387 | omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH); | ||
388 | fn = mktemp_name (t); | ||
389 | if (-1 == (fd = mkstemp (fn))) | ||
390 | { | ||
391 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn); | ||
392 | GNUNET_free (fn); | ||
393 | umask (omask); | ||
394 | return NULL; | ||
395 | } | ||
396 | umask (omask); | ||
397 | if (0 != close (fd)) | ||
398 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn); | ||
399 | return fn; | ||
400 | } | ||
401 | |||
402 | |||
403 | enum GNUNET_GenericReturnValue | ||
404 | GNUNET_DISK_directory_test (const char *fil, int is_readable) | ||
405 | { | ||
406 | struct stat filestat; | ||
407 | int ret; | ||
408 | |||
409 | ret = stat (fil, &filestat); | ||
410 | if (ret != 0) | ||
411 | { | ||
412 | if (errno != ENOENT) | ||
413 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil); | ||
414 | return GNUNET_SYSERR; | ||
415 | } | ||
416 | if (! S_ISDIR (filestat.st_mode)) | ||
417 | { | ||
418 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
419 | "A file already exits with the same name %s\n", | ||
420 | fil); | ||
421 | return GNUNET_NO; | ||
422 | } | ||
423 | if (GNUNET_YES == is_readable) | ||
424 | ret = access (fil, R_OK | X_OK); | ||
425 | else | ||
426 | ret = access (fil, X_OK); | ||
427 | if (ret < 0) | ||
428 | { | ||
429 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil); | ||
430 | return GNUNET_NO; | ||
431 | } | ||
432 | return GNUNET_YES; | ||
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 | * Check for a simple wildcard match. | ||
907 | * Only asterisks are allowed. | ||
908 | * Asterisks match everything, including slashes. | ||
909 | * | ||
910 | * @param pattern pattern with wildcards | ||
911 | * @param str string to match against | ||
912 | * @returns true on match, false otherwise | ||
913 | */ | ||
914 | static bool | ||
915 | glob_match (const char *pattern, const char *str) | ||
916 | { | ||
917 | /* Position in the input string */ | ||
918 | const char *str_pos = str; | ||
919 | /* Position in the pattern */ | ||
920 | const char *pat_pos = pattern; | ||
921 | /* Backtrack position in string */ | ||
922 | const char *str_bt = NULL; | ||
923 | /* Backtrack position in pattern */ | ||
924 | const char *pat_bt = NULL; | ||
925 | |||
926 | for (;;) | ||
927 | { | ||
928 | if (*pat_pos == '*') | ||
929 | { | ||
930 | str_bt = str_pos; | ||
931 | pat_bt = pat_pos++; | ||
932 | } | ||
933 | else if (*pat_pos == *str_pos) | ||
934 | { | ||
935 | if ('\0' == *pat_pos) | ||
936 | return true; | ||
937 | str_pos++; | ||
938 | pat_pos++; | ||
939 | } | ||
940 | else | ||
941 | { | ||
942 | if (NULL == str_bt) | ||
943 | return false; | ||
944 | /* Backtrack to match one more | ||
945 | character as part of the asterisk. */ | ||
946 | str_pos = str_bt + 1; | ||
947 | if ('\0' == *str_pos) | ||
948 | return false; | ||
949 | pat_pos = pat_bt; | ||
950 | } | ||
951 | } | ||
952 | } | ||
953 | |||
954 | struct GlobClosure | ||
955 | { | ||
956 | const char *glob; | ||
957 | GNUNET_FileNameCallback cb; | ||
958 | void *cls; | ||
959 | |||
960 | /** | ||
961 | * Number of files that actually matched the glob pattern. | ||
962 | */ | ||
963 | int nres; | ||
964 | }; | ||
965 | |||
966 | /** | ||
967 | * Function called with a filename. | ||
968 | * | ||
969 | * @param cls closure | ||
970 | * @param filename complete filename (absolute path) | ||
971 | * @return #GNUNET_OK to continue to iterate, | ||
972 | * #GNUNET_NO to stop iteration with no error, | ||
973 | * #GNUNET_SYSERR to abort iteration with error! | ||
974 | */ | ||
975 | static enum GNUNET_GenericReturnValue | ||
976 | glob_cb (void *cls, | ||
977 | const char *filename) | ||
978 | { | ||
979 | struct GlobClosure *gc = cls; | ||
980 | const char *fn; | ||
981 | |||
982 | fn = strrchr (filename, DIR_SEPARATOR); | ||
983 | fn = (NULL == fn) ? filename : (fn + 1); | ||
984 | |||
985 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
986 | "checking glob '%s' against '%s'\n", | ||
987 | gc->glob, | ||
988 | fn); | ||
989 | |||
990 | if (glob_match (gc->glob, fn)) | ||
991 | { | ||
992 | enum GNUNET_GenericReturnValue cbret; | ||
993 | |||
994 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
995 | "found glob match '%s'\n", | ||
996 | filename); | ||
997 | gc->nres++; | ||
998 | cbret = gc->cb (gc->cls, filename); | ||
999 | if (GNUNET_OK != cbret) | ||
1000 | return cbret; | ||
1001 | } | ||
1002 | return GNUNET_OK; | ||
1003 | } | ||
1004 | |||
1005 | |||
1006 | int | ||
1007 | GNUNET_DISK_glob (const char *glob_pattern, | ||
1008 | GNUNET_FileNameCallback callback, | ||
1009 | void *callback_cls) | ||
1010 | { | ||
1011 | char *mypat = GNUNET_strdup (glob_pattern); | ||
1012 | char *sep; | ||
1013 | int ret; | ||
1014 | |||
1015 | if ( (NULL != strrchr (glob_pattern, '+')) || | ||
1016 | (NULL != strrchr (glob_pattern, '[')) || | ||
1017 | (NULL != strrchr (glob_pattern, '+')) || | ||
1018 | (NULL != strrchr (glob_pattern, '~')) ) | ||
1019 | { | ||
1020 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
1021 | "unsupported glob pattern: '%s'\n", | ||
1022 | glob_pattern); | ||
1023 | GNUNET_free (mypat); | ||
1024 | return -1; | ||
1025 | } | ||
1026 | |||
1027 | sep = strrchr (mypat, DIR_SEPARATOR); | ||
1028 | if (NULL == sep) | ||
1029 | { | ||
1030 | GNUNET_free (mypat); | ||
1031 | return -1; | ||
1032 | } | ||
1033 | |||
1034 | *sep = '\0'; | ||
1035 | |||
1036 | if (NULL != strchr (mypat, '*')) | ||
1037 | { | ||
1038 | GNUNET_free (mypat); | ||
1039 | GNUNET_break (0); | ||
1040 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
1041 | "glob pattern may only contain '*' in the final path component\n"); | ||
1042 | return -1; | ||
1043 | } | ||
1044 | |||
1045 | { | ||
1046 | struct GlobClosure gc = { | ||
1047 | .glob = sep + 1, | ||
1048 | .cb = callback, | ||
1049 | .cls = callback_cls, | ||
1050 | .nres = 0, | ||
1051 | }; | ||
1052 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1053 | "scanning directory '%s' for glob matches on '%s'\n", | ||
1054 | mypat, | ||
1055 | gc.glob); | ||
1056 | ret = GNUNET_DISK_directory_scan (mypat, | ||
1057 | glob_cb, | ||
1058 | &gc | ||
1059 | ); | ||
1060 | GNUNET_free (mypat); | ||
1061 | return (ret < 0) ? ret : gc.nres; | ||
1062 | } | ||
1063 | } | ||
1064 | |||
1065 | |||
1066 | /** | ||
1067 | * Function that removes the given directory by calling | ||
1068 | * #GNUNET_DISK_directory_remove(). | ||
1069 | * | ||
1070 | * @param unused not used | ||
1071 | * @param fn directory to remove | ||
1072 | * @return #GNUNET_OK | ||
1073 | */ | ||
1074 | static enum GNUNET_GenericReturnValue | ||
1075 | remove_helper (void *unused, | ||
1076 | const char *fn) | ||
1077 | { | ||
1078 | (void) unused; | ||
1079 | (void) GNUNET_DISK_directory_remove (fn); | ||
1080 | return GNUNET_OK; | ||
1081 | } | ||
1082 | |||
1083 | |||
1084 | enum GNUNET_GenericReturnValue | ||
1085 | GNUNET_DISK_directory_remove (const char *filename) | ||
1086 | { | ||
1087 | struct stat istat; | ||
1088 | |||
1089 | if (NULL == filename) | ||
1090 | { | ||
1091 | GNUNET_break (0); | ||
1092 | return GNUNET_SYSERR; | ||
1093 | } | ||
1094 | if (0 != lstat (filename, &istat)) | ||
1095 | return GNUNET_NO; /* file may not exist... */ | ||
1096 | (void) chmod (filename, | ||
1097 | S_IWUSR | S_IRUSR | S_IXUSR); | ||
1098 | if (0 == unlink (filename)) | ||
1099 | return GNUNET_OK; | ||
1100 | if ( (errno != EISDIR) && | ||
1101 | /* EISDIR is not sufficient in all cases, e.g. | ||
1102 | * sticky /tmp directory may result in EPERM on BSD. | ||
1103 | * So we also explicitly check "isDirectory" */ | ||
1104 | (GNUNET_YES != | ||
1105 | GNUNET_DISK_directory_test (filename, | ||
1106 | GNUNET_YES)) ) | ||
1107 | { | ||
1108 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename); | ||
1109 | return GNUNET_SYSERR; | ||
1110 | } | ||
1111 | if (GNUNET_SYSERR == | ||
1112 | GNUNET_DISK_directory_scan (filename, &remove_helper, NULL)) | ||
1113 | return GNUNET_SYSERR; | ||
1114 | if (0 != rmdir (filename)) | ||
1115 | { | ||
1116 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename); | ||
1117 | return GNUNET_SYSERR; | ||
1118 | } | ||
1119 | return GNUNET_OK; | ||
1120 | } | ||
1121 | |||
1122 | |||
1123 | enum GNUNET_GenericReturnValue | ||
1124 | GNUNET_DISK_file_copy (const char *src, | ||
1125 | const char *dst) | ||
1126 | { | ||
1127 | char *buf; | ||
1128 | uint64_t pos; | ||
1129 | uint64_t size; | ||
1130 | size_t len; | ||
1131 | ssize_t sret; | ||
1132 | struct GNUNET_DISK_FileHandle *in; | ||
1133 | struct GNUNET_DISK_FileHandle *out; | ||
1134 | |||
1135 | if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES)) | ||
1136 | { | ||
1137 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "stat", src); | ||
1138 | return GNUNET_SYSERR; | ||
1139 | } | ||
1140 | pos = 0; | ||
1141 | in = | ||
1142 | GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE); | ||
1143 | if (! in) | ||
1144 | { | ||
1145 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", src); | ||
1146 | return GNUNET_SYSERR; | ||
1147 | } | ||
1148 | out = | ||
1149 | GNUNET_DISK_file_open (dst, | ||
1150 | GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE | ||
1151 | | GNUNET_DISK_OPEN_FAILIFEXISTS, | ||
1152 | GNUNET_DISK_PERM_USER_READ | ||
1153 | | GNUNET_DISK_PERM_USER_WRITE | ||
1154 | | GNUNET_DISK_PERM_GROUP_READ | ||
1155 | | GNUNET_DISK_PERM_GROUP_WRITE); | ||
1156 | if (! out) | ||
1157 | { | ||
1158 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", dst); | ||
1159 | GNUNET_DISK_file_close (in); | ||
1160 | return GNUNET_SYSERR; | ||
1161 | } | ||
1162 | buf = GNUNET_malloc (COPY_BLK_SIZE); | ||
1163 | while (pos < size) | ||
1164 | { | ||
1165 | len = COPY_BLK_SIZE; | ||
1166 | if (len > size - pos) | ||
1167 | len = size - pos; | ||
1168 | sret = GNUNET_DISK_file_read (in, buf, len); | ||
1169 | if ((sret < 0) || (len != (size_t) sret)) | ||
1170 | goto FAIL; | ||
1171 | sret = GNUNET_DISK_file_write (out, buf, len); | ||
1172 | if ((sret < 0) || (len != (size_t) sret)) | ||
1173 | goto FAIL; | ||
1174 | pos += len; | ||
1175 | } | ||
1176 | GNUNET_free (buf); | ||
1177 | GNUNET_DISK_file_close (in); | ||
1178 | GNUNET_DISK_file_close (out); | ||
1179 | return GNUNET_OK; | ||
1180 | FAIL: | ||
1181 | GNUNET_free (buf); | ||
1182 | GNUNET_DISK_file_close (in); | ||
1183 | GNUNET_DISK_file_close (out); | ||
1184 | return GNUNET_SYSERR; | ||
1185 | } | ||
1186 | |||
1187 | |||
1188 | void | ||
1189 | GNUNET_DISK_filename_canonicalize (char *fn) | ||
1190 | { | ||
1191 | char *idx; | ||
1192 | char c; | ||
1193 | |||
1194 | for (idx = fn; *idx; idx++) | ||
1195 | { | ||
1196 | c = *idx; | ||
1197 | |||
1198 | if ((c == '/') || (c == '\\') || (c == ':') || (c == '*') || (c == '?') || | ||
1199 | (c == | ||
1200 | '"') | ||
1201 | || | ||
1202 | (c == '<') || (c == '>') || (c == '|') ) | ||
1203 | { | ||
1204 | *idx = '_'; | ||
1205 | } | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | |||
1210 | enum GNUNET_GenericReturnValue | ||
1211 | GNUNET_DISK_file_change_owner (const char *filename, | ||
1212 | const char *user) | ||
1213 | { | ||
1214 | struct passwd *pws; | ||
1215 | |||
1216 | pws = getpwnam (user); | ||
1217 | if (NULL == pws) | ||
1218 | { | ||
1219 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
1220 | _ ("Cannot obtain information about user `%s': %s\n"), | ||
1221 | user, | ||
1222 | strerror (errno)); | ||
1223 | return GNUNET_SYSERR; | ||
1224 | } | ||
1225 | if (0 != chown (filename, pws->pw_uid, pws->pw_gid)) | ||
1226 | { | ||
1227 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename); | ||
1228 | return GNUNET_SYSERR; | ||
1229 | } | ||
1230 | return GNUNET_OK; | ||
1231 | } | ||
1232 | |||
1233 | |||
1234 | struct GNUNET_DISK_FileHandle * | ||
1235 | GNUNET_DISK_file_open (const char *fn, | ||
1236 | enum GNUNET_DISK_OpenFlags flags, | ||
1237 | enum GNUNET_DISK_AccessPermissions perm) | ||
1238 | { | ||
1239 | char *expfn; | ||
1240 | struct GNUNET_DISK_FileHandle *ret; | ||
1241 | |||
1242 | int oflags; | ||
1243 | int mode; | ||
1244 | int fd; | ||
1245 | |||
1246 | expfn = GNUNET_STRINGS_filename_expand (fn); | ||
1247 | if (NULL == expfn) | ||
1248 | return NULL; | ||
1249 | |||
1250 | mode = 0; | ||
1251 | if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE)) | ||
1252 | oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */ | ||
1253 | else if (flags & GNUNET_DISK_OPEN_READ) | ||
1254 | oflags = O_RDONLY; | ||
1255 | else if (flags & GNUNET_DISK_OPEN_WRITE) | ||
1256 | oflags = O_WRONLY; | ||
1257 | else | ||
1258 | { | ||
1259 | GNUNET_break (0); | ||
1260 | GNUNET_free (expfn); | ||
1261 | return NULL; | ||
1262 | } | ||
1263 | if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS) | ||
1264 | oflags |= (O_CREAT | O_EXCL); | ||
1265 | if (flags & GNUNET_DISK_OPEN_TRUNCATE) | ||
1266 | oflags |= O_TRUNC; | ||
1267 | if (flags & GNUNET_DISK_OPEN_APPEND) | ||
1268 | oflags |= O_APPEND; | ||
1269 | if (GNUNET_NO == GNUNET_DISK_file_test (fn)) | ||
1270 | { | ||
1271 | if (flags & GNUNET_DISK_OPEN_CREATE) | ||
1272 | { | ||
1273 | (void) GNUNET_DISK_directory_create_for_file (expfn); | ||
1274 | oflags |= O_CREAT; | ||
1275 | mode = translate_unix_perms (perm); | ||
1276 | } | ||
1277 | } | ||
1278 | |||
1279 | fd = open (expfn, | ||
1280 | oflags | ||
1281 | #if O_CLOEXEC | ||
1282 | | O_CLOEXEC | ||
1283 | #endif | ||
1284 | | O_LARGEFILE, | ||
1285 | mode); | ||
1286 | if (fd == -1) | ||
1287 | { | ||
1288 | if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)) | ||
1289 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn); | ||
1290 | else | ||
1291 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn); | ||
1292 | GNUNET_free (expfn); | ||
1293 | return NULL; | ||
1294 | } | ||
1295 | |||
1296 | ret = GNUNET_new (struct GNUNET_DISK_FileHandle); | ||
1297 | |||
1298 | ret->fd = fd; | ||
1299 | |||
1300 | GNUNET_free (expfn); | ||
1301 | return ret; | ||
1302 | } | ||
1303 | |||
1304 | |||
1305 | enum GNUNET_GenericReturnValue | ||
1306 | GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h) | ||
1307 | { | ||
1308 | enum GNUNET_GenericReturnValue ret; | ||
1309 | |||
1310 | if (NULL == h) | ||
1311 | { | ||
1312 | errno = EINVAL; | ||
1313 | return GNUNET_SYSERR; | ||
1314 | } | ||
1315 | |||
1316 | ret = GNUNET_OK; | ||
1317 | if (0 != close (h->fd)) | ||
1318 | { | ||
1319 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close"); | ||
1320 | ret = GNUNET_SYSERR; | ||
1321 | } | ||
1322 | GNUNET_free (h); | ||
1323 | return ret; | ||
1324 | } | ||
1325 | |||
1326 | |||
1327 | struct GNUNET_DISK_FileHandle * | ||
1328 | GNUNET_DISK_get_handle_from_int_fd (int fno) | ||
1329 | { | ||
1330 | struct GNUNET_DISK_FileHandle *fh; | ||
1331 | |||
1332 | if ((((off_t) -1) == lseek (fno, 0, SEEK_CUR)) && (EBADF == errno)) | ||
1333 | return NULL; /* invalid FD */ | ||
1334 | |||
1335 | fh = GNUNET_new (struct GNUNET_DISK_FileHandle); | ||
1336 | |||
1337 | fh->fd = fno; | ||
1338 | |||
1339 | return fh; | ||
1340 | } | ||
1341 | |||
1342 | |||
1343 | struct GNUNET_DISK_FileHandle * | ||
1344 | GNUNET_DISK_get_handle_from_native (FILE *fd) | ||
1345 | { | ||
1346 | int fno; | ||
1347 | |||
1348 | fno = fileno (fd); | ||
1349 | if (-1 == fno) | ||
1350 | return NULL; | ||
1351 | return GNUNET_DISK_get_handle_from_int_fd (fno); | ||
1352 | } | ||
1353 | |||
1354 | |||
1355 | /** | ||
1356 | * Handle for a memory-mapping operation. | ||
1357 | */ | ||
1358 | struct GNUNET_DISK_MapHandle | ||
1359 | { | ||
1360 | /** | ||
1361 | * Address where the map is in memory. | ||
1362 | */ | ||
1363 | void *addr; | ||
1364 | |||
1365 | /** | ||
1366 | * Number of bytes mapped. | ||
1367 | */ | ||
1368 | size_t len; | ||
1369 | }; | ||
1370 | |||
1371 | |||
1372 | #ifndef MAP_FAILED | ||
1373 | #define MAP_FAILED ((void *) -1) | ||
1374 | #endif | ||
1375 | |||
1376 | |||
1377 | void * | ||
1378 | GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h, | ||
1379 | struct GNUNET_DISK_MapHandle **m, | ||
1380 | enum GNUNET_DISK_MapType access, | ||
1381 | size_t len) | ||
1382 | { | ||
1383 | int prot; | ||
1384 | |||
1385 | if (NULL == h) | ||
1386 | { | ||
1387 | errno = EINVAL; | ||
1388 | return NULL; | ||
1389 | } | ||
1390 | prot = 0; | ||
1391 | if (access & GNUNET_DISK_MAP_TYPE_READ) | ||
1392 | prot = PROT_READ; | ||
1393 | if (access & GNUNET_DISK_MAP_TYPE_WRITE) | ||
1394 | prot |= PROT_WRITE; | ||
1395 | *m = GNUNET_new (struct GNUNET_DISK_MapHandle); | ||
1396 | (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0); | ||
1397 | GNUNET_assert (NULL != (*m)->addr); | ||
1398 | if (MAP_FAILED == (*m)->addr) | ||
1399 | { | ||
1400 | GNUNET_free (*m); | ||
1401 | return NULL; | ||
1402 | } | ||
1403 | (*m)->len = len; | ||
1404 | return (*m)->addr; | ||
1405 | } | ||
1406 | |||
1407 | |||
1408 | enum GNUNET_GenericReturnValue | ||
1409 | GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h) | ||
1410 | { | ||
1411 | enum GNUNET_GenericReturnValue ret; | ||
1412 | |||
1413 | if (NULL == h) | ||
1414 | { | ||
1415 | errno = EINVAL; | ||
1416 | return GNUNET_SYSERR; | ||
1417 | } | ||
1418 | ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR; | ||
1419 | GNUNET_free (h); | ||
1420 | return ret; | ||
1421 | } | ||
1422 | |||
1423 | |||
1424 | enum GNUNET_GenericReturnValue | ||
1425 | GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h) | ||
1426 | { | ||
1427 | if (h == NULL) | ||
1428 | { | ||
1429 | errno = EINVAL; | ||
1430 | return GNUNET_SYSERR; | ||
1431 | } | ||
1432 | |||
1433 | #if ! defined(__linux__) || ! defined(GNU) | ||
1434 | return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK; | ||
1435 | #else | ||
1436 | return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK; | ||
1437 | #endif | ||
1438 | } | ||
1439 | |||
1440 | |||
1441 | struct GNUNET_DISK_PipeHandle * | ||
1442 | GNUNET_DISK_pipe (enum GNUNET_DISK_PipeFlags pf) | ||
1443 | { | ||
1444 | int fd[2]; | ||
1445 | |||
1446 | if (-1 == pipe (fd)) | ||
1447 | { | ||
1448 | int eno = errno; | ||
1449 | |||
1450 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe"); | ||
1451 | errno = eno; | ||
1452 | return NULL; | ||
1453 | } | ||
1454 | return GNUNET_DISK_pipe_from_fd (pf, fd); | ||
1455 | } | ||
1456 | |||
1457 | |||
1458 | struct GNUNET_DISK_PipeHandle * | ||
1459 | GNUNET_DISK_pipe_from_fd (enum GNUNET_DISK_PipeFlags pf, | ||
1460 | int fd[2]) | ||
1461 | { | ||
1462 | struct GNUNET_DISK_PipeHandle *p; | ||
1463 | int ret = 0; | ||
1464 | int flags; | ||
1465 | int eno = 0; /* make gcc happy */ | ||
1466 | |||
1467 | p = GNUNET_new (struct GNUNET_DISK_PipeHandle); | ||
1468 | if (fd[0] >= 0) | ||
1469 | { | ||
1470 | p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle); | ||
1471 | p->fd[0]->fd = fd[0]; | ||
1472 | if (0 == (GNUNET_DISK_PF_BLOCKING_READ & pf)) | ||
1473 | { | ||
1474 | flags = fcntl (fd[0], F_GETFL); | ||
1475 | flags |= O_NONBLOCK; | ||
1476 | if (0 > fcntl (fd[0], F_SETFL, flags)) | ||
1477 | { | ||
1478 | ret = -1; | ||
1479 | eno = errno; | ||
1480 | } | ||
1481 | } | ||
1482 | flags = fcntl (fd[0], F_GETFD); | ||
1483 | flags |= FD_CLOEXEC; | ||
1484 | if (0 > fcntl (fd[0], F_SETFD, flags)) | ||
1485 | { | ||
1486 | ret = -1; | ||
1487 | eno = errno; | ||
1488 | } | ||
1489 | } | ||
1490 | |||
1491 | if (fd[1] >= 0) | ||
1492 | { | ||
1493 | p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle); | ||
1494 | p->fd[1]->fd = fd[1]; | ||
1495 | if (0 == (GNUNET_DISK_PF_BLOCKING_WRITE & pf)) | ||
1496 | { | ||
1497 | flags = fcntl (fd[1], F_GETFL); | ||
1498 | flags |= O_NONBLOCK; | ||
1499 | if (0 > fcntl (fd[1], F_SETFL, flags)) | ||
1500 | { | ||
1501 | ret = -1; | ||
1502 | eno = errno; | ||
1503 | } | ||
1504 | } | ||
1505 | flags = fcntl (fd[1], F_GETFD); | ||
1506 | flags |= FD_CLOEXEC; | ||
1507 | if (0 > fcntl (fd[1], F_SETFD, flags)) | ||
1508 | { | ||
1509 | ret = -1; | ||
1510 | eno = errno; | ||
1511 | } | ||
1512 | } | ||
1513 | if (ret == -1) | ||
1514 | { | ||
1515 | errno = eno; | ||
1516 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl"); | ||
1517 | if (p->fd[0]->fd >= 0) | ||
1518 | GNUNET_break (0 == close (p->fd[0]->fd)); | ||
1519 | if (p->fd[1]->fd >= 0) | ||
1520 | GNUNET_break (0 == close (p->fd[1]->fd)); | ||
1521 | GNUNET_free (p->fd[0]); | ||
1522 | GNUNET_free (p->fd[1]); | ||
1523 | GNUNET_free (p); | ||
1524 | errno = eno; | ||
1525 | return NULL; | ||
1526 | } | ||
1527 | return p; | ||
1528 | } | ||
1529 | |||
1530 | |||
1531 | enum GNUNET_GenericReturnValue | ||
1532 | GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p, | ||
1533 | enum GNUNET_DISK_PipeEnd end) | ||
1534 | { | ||
1535 | enum GNUNET_GenericReturnValue ret = GNUNET_OK; | ||
1536 | |||
1537 | if (end == GNUNET_DISK_PIPE_END_READ) | ||
1538 | { | ||
1539 | if (p->fd[0]) | ||
1540 | { | ||
1541 | ret = GNUNET_DISK_file_close (p->fd[0]); | ||
1542 | p->fd[0] = NULL; | ||
1543 | } | ||
1544 | } | ||
1545 | else if (end == GNUNET_DISK_PIPE_END_WRITE) | ||
1546 | { | ||
1547 | if (p->fd[1]) | ||
1548 | { | ||
1549 | ret = GNUNET_DISK_file_close (p->fd[1]); | ||
1550 | p->fd[1] = NULL; | ||
1551 | } | ||
1552 | } | ||
1553 | return ret; | ||
1554 | } | ||
1555 | |||
1556 | |||
1557 | struct GNUNET_DISK_FileHandle * | ||
1558 | GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p, | ||
1559 | enum GNUNET_DISK_PipeEnd end) | ||
1560 | { | ||
1561 | struct GNUNET_DISK_FileHandle *ret = NULL; | ||
1562 | |||
1563 | if (end == GNUNET_DISK_PIPE_END_READ) | ||
1564 | { | ||
1565 | if (p->fd[0]) | ||
1566 | { | ||
1567 | ret = p->fd[0]; | ||
1568 | p->fd[0] = NULL; | ||
1569 | } | ||
1570 | } | ||
1571 | else if (end == GNUNET_DISK_PIPE_END_WRITE) | ||
1572 | { | ||
1573 | if (p->fd[1]) | ||
1574 | { | ||
1575 | ret = p->fd[1]; | ||
1576 | p->fd[1] = NULL; | ||
1577 | } | ||
1578 | } | ||
1579 | |||
1580 | return ret; | ||
1581 | } | ||
1582 | |||
1583 | |||
1584 | enum GNUNET_GenericReturnValue | ||
1585 | GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p) | ||
1586 | { | ||
1587 | int ret = GNUNET_OK; | ||
1588 | |||
1589 | int read_end_close; | ||
1590 | int write_end_close; | ||
1591 | int read_end_close_errno; | ||
1592 | int write_end_close_errno; | ||
1593 | |||
1594 | read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ); | ||
1595 | read_end_close_errno = errno; | ||
1596 | write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE); | ||
1597 | write_end_close_errno = errno; | ||
1598 | GNUNET_free (p); | ||
1599 | |||
1600 | if (GNUNET_OK != read_end_close) | ||
1601 | { | ||
1602 | errno = read_end_close_errno; | ||
1603 | ret = read_end_close; | ||
1604 | } | ||
1605 | else if (GNUNET_OK != write_end_close) | ||
1606 | { | ||
1607 | errno = write_end_close_errno; | ||
1608 | ret = write_end_close; | ||
1609 | } | ||
1610 | |||
1611 | return ret; | ||
1612 | } | ||
1613 | |||
1614 | |||
1615 | const struct GNUNET_DISK_FileHandle * | ||
1616 | GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p, | ||
1617 | enum GNUNET_DISK_PipeEnd n) | ||
1618 | { | ||
1619 | switch (n) | ||
1620 | { | ||
1621 | case GNUNET_DISK_PIPE_END_READ: | ||
1622 | case GNUNET_DISK_PIPE_END_WRITE: | ||
1623 | return p->fd[n]; | ||
1624 | |||
1625 | default: | ||
1626 | GNUNET_break (0); | ||
1627 | return NULL; | ||
1628 | } | ||
1629 | } | ||
1630 | |||
1631 | |||
1632 | enum GNUNET_GenericReturnValue | ||
1633 | GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh, | ||
1634 | void *dst, | ||
1635 | size_t dst_len) | ||
1636 | { | ||
1637 | if (NULL == fh) | ||
1638 | return GNUNET_SYSERR; | ||
1639 | if (dst_len < sizeof(int)) | ||
1640 | return GNUNET_SYSERR; | ||
1641 | *((int *) dst) = fh->fd; | ||
1642 | return GNUNET_OK; | ||
1643 | } | ||
1644 | |||
1645 | |||
1646 | /** | ||
1647 | * Helper function for #GNUNET_DISK_purge_cfg_dir. | ||
1648 | * | ||
1649 | * @param cls a `const char *` with the option to purge | ||
1650 | * @param cfg our configuration | ||
1651 | * @return #GNUNET_OK on success | ||
1652 | */ | ||
1653 | static enum GNUNET_GenericReturnValue | ||
1654 | purge_cfg_dir (void *cls, | ||
1655 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
1656 | { | ||
1657 | const char *option = cls; | ||
1658 | char *tmpname; | ||
1659 | |||
1660 | if (GNUNET_OK != | ||
1661 | GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", option, &tmpname)) | ||
1662 | { | ||
1663 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "PATHS", option); | ||
1664 | return GNUNET_NO; | ||
1665 | } | ||
1666 | if (GNUNET_SYSERR == GNUNET_DISK_directory_remove (tmpname)) | ||
1667 | { | ||
1668 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "remove", tmpname); | ||
1669 | GNUNET_free (tmpname); | ||
1670 | return GNUNET_OK; | ||
1671 | } | ||
1672 | GNUNET_free (tmpname); | ||
1673 | return GNUNET_OK; | ||
1674 | } | ||
1675 | |||
1676 | |||
1677 | void | ||
1678 | GNUNET_DISK_purge_cfg_dir (const char *cfg_filename, | ||
1679 | const char *option) | ||
1680 | { | ||
1681 | GNUNET_break (GNUNET_OK == | ||
1682 | GNUNET_CONFIGURATION_parse_and_run (cfg_filename, | ||
1683 | &purge_cfg_dir, | ||
1684 | (void *) option)); | ||
1685 | } | ||
1686 | |||
1687 | |||
1688 | /* end of disk.c */ | ||