aboutsummaryrefslogtreecommitdiff
path: root/src/util/disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/disk.c')
-rw-r--r--src/util/disk.c954
1 files changed, 954 insertions, 0 deletions
diff --git a/src/util/disk.c b/src/util/disk.c
new file mode 100644
index 000000000..f0fe9341b
--- /dev/null
+++ b/src/util/disk.c
@@ -0,0 +1,954 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/disk.c
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_directories.h"
30#include "gnunet_disk_lib.h"
31#include "gnunet_scheduler_lib.h"
32#include "gnunet_strings_lib.h"
33
34
35#if LINUX || CYGWIN
36#include <sys/vfs.h>
37#else
38#ifdef SOMEBSD
39#include <sys/param.h>
40#include <sys/mount.h>
41#else
42#ifdef OSX
43#include <sys/param.h>
44#include <sys/mount.h>
45#else
46#ifdef SOLARIS
47#include <sys/types.h>
48#include <sys/statvfs.h>
49#else
50#ifdef MINGW
51#define _IFMT 0170000 /* type of file */
52#define _IFLNK 0120000 /* symbolic link */
53#define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
54#else
55#error PORT-ME: need to port statfs (how much space is left on the drive?)
56#endif
57#endif
58#endif
59#endif
60#endif
61
62#ifndef SOMEBSD
63#ifndef WINDOWS
64#ifndef OSX
65#include <wordexp.h>
66#endif
67#endif
68#endif
69
70typedef struct
71{
72 unsigned long long total;
73 int include_sym_links;
74} GetFileSizeData;
75
76static int
77getSizeRec (void *ptr, const char *fn)
78{
79 GetFileSizeData *gfsd = ptr;
80#ifdef HAVE_STAT64
81 struct stat64 buf;
82#else
83 struct stat buf;
84#endif
85
86#ifdef HAVE_STAT64
87 if (0 != STAT64 (fn, &buf))
88 {
89 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
90 return GNUNET_SYSERR;
91 }
92#else
93 if (0 != STAT (fn, &buf))
94 {
95 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
96 return GNUNET_SYSERR;
97 }
98#endif
99 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
100 gfsd->total += buf.st_size;
101 if ((S_ISDIR (buf.st_mode)) &&
102 (0 == ACCESS (fn, X_OK)) &&
103 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
104 {
105 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
106 return GNUNET_SYSERR;
107 }
108 return GNUNET_OK;
109}
110
111/**
112 * Get the size of the file (or directory)
113 * of the given file (in bytes).
114 *
115 * @return GNUNET_SYSERR on error, GNUNET_OK on success
116 */
117int
118GNUNET_DISK_file_size (const char *filename,
119 unsigned long long *size, int includeSymLinks)
120{
121 GetFileSizeData gfsd;
122 int ret;
123
124 GNUNET_assert (size != NULL);
125 gfsd.total = 0;
126 gfsd.include_sym_links = includeSymLinks;
127 ret = getSizeRec (&gfsd, filename);
128 *size = gfsd.total;
129 return ret;
130}
131
132/**
133 * Get the number of blocks that are left on the partition that
134 * contains the given file (for normal users).
135 *
136 * @param part a file on the partition to check
137 * @return -1 on errors, otherwise the number of free blocks
138 */
139long
140GNUNET_DISK_get_blocks_available (const char *part)
141{
142#ifdef SOLARIS
143 struct statvfs buf;
144
145 if (0 != statvfs (part, &buf))
146 {
147 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
148 return -1;
149 }
150 return buf.f_bavail;
151#elif MINGW
152 DWORD dwDummy;
153 DWORD dwBlocks;
154 char szDrive[4];
155
156 memcpy (szDrive, part, 3);
157 szDrive[3] = 0;
158 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
159 {
160 GNUNET_GE_LOG (GNUNET_ERROR_TYPE_WARNING,
161 _("`%s' failed for drive `%s': %u\n"),
162 "GetDiskFreeSpace", szDrive, GetLastError ());
163
164 return -1;
165 }
166 return dwBlocks;
167#else
168 struct statfs s;
169 if (0 != statfs (part, &s))
170 {
171 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
172 return -1;
173 }
174 return s.f_bavail;
175#endif
176}
177
178/**
179 * Test if fil is a directory.
180 *
181 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
182 * does not exist
183 */
184int
185GNUNET_DISK_directory_test (const char *fil)
186{
187 struct stat filestat;
188 int ret;
189
190 ret = STAT (fil, &filestat);
191 if (ret != 0)
192 {
193 if (errno != ENOENT)
194 {
195 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
196 return GNUNET_SYSERR;
197 }
198 return GNUNET_NO;
199 }
200 if (!S_ISDIR (filestat.st_mode))
201 return GNUNET_NO;
202 if (ACCESS (fil, R_OK | X_OK) < 0)
203 {
204 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
205 return GNUNET_SYSERR;
206 }
207 return GNUNET_YES;
208}
209
210/**
211 * Check that fil corresponds to a filename
212 * (of a file that exists and that is not a directory).
213 * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
214 * else (will print an error message in that case, too).
215 */
216int
217GNUNET_DISK_file_test (const char *fil)
218{
219 struct stat filestat;
220 int ret;
221 char *rdir;
222
223 rdir = GNUNET_STRINGS_filename_expand (fil);
224 if (rdir == NULL)
225 return GNUNET_SYSERR;
226
227 ret = STAT (rdir, &filestat);
228 if (ret != 0)
229 {
230 if (errno != ENOENT)
231 {
232 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
233 GNUNET_free (rdir);
234 return GNUNET_SYSERR;
235 }
236 GNUNET_free (rdir);
237 return GNUNET_NO;
238 }
239 if (!S_ISREG (filestat.st_mode))
240 {
241 GNUNET_free (rdir);
242 return GNUNET_NO;
243 }
244 if (ACCESS (rdir, R_OK) < 0)
245 {
246 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
247 GNUNET_free (rdir);
248 return GNUNET_SYSERR;
249 }
250 GNUNET_free (rdir);
251 return GNUNET_YES;
252}
253
254/**
255 * Implementation of "mkdir -p"
256 * @param dir the directory to create
257 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
258 */
259int
260GNUNET_DISK_directory_create (const char *dir)
261{
262 char *rdir;
263 int len;
264 int pos;
265 int ret = GNUNET_OK;
266
267 rdir = GNUNET_STRINGS_filename_expand (dir);
268 if (rdir == NULL)
269 return GNUNET_SYSERR;
270
271 len = strlen (rdir);
272#ifndef MINGW
273 pos = 1; /* skip heading '/' */
274#else
275 /* Local or Network path? */
276 if (strncmp (rdir, "\\\\", 2) == 0)
277 {
278 pos = 2;
279 while (rdir[pos])
280 {
281 if (rdir[pos] == '\\')
282 {
283 pos++;
284 break;
285 }
286 pos++;
287 }
288 }
289 else
290 {
291 pos = 3; /* strlen("C:\\") */
292 }
293#endif
294 while (pos <= len)
295 {
296 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
297 {
298 rdir[pos] = '\0';
299 ret = GNUNET_DISK_directory_test (rdir);
300 if (ret == GNUNET_SYSERR)
301 {
302 GNUNET_free (rdir);
303 return GNUNET_SYSERR;
304 }
305 if (ret == GNUNET_NO)
306 {
307#ifndef MINGW
308 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
309#else
310 ret = mkdir (rdir);
311#endif
312 if ((ret != 0) && (errno != EEXIST))
313 {
314 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
315 rdir);
316 GNUNET_free (rdir);
317 return GNUNET_SYSERR;
318 }
319 }
320 rdir[pos] = DIR_SEPARATOR;
321 }
322 pos++;
323 }
324 GNUNET_free (rdir);
325 return GNUNET_OK;
326}
327
328
329/**
330 * Create the directory structure for storing
331 * a file.
332 *
333 * @param filename name of a file in the directory
334 * @returns GNUNET_OK on success,
335 * GNUNET_SYSERR on failure,
336 * GNUNET_NO if the directory
337 * exists but is not writeable for us
338 */
339int
340GNUNET_DISK_directory_create_for_file (const char *dir)
341{
342 char *rdir;
343 int len;
344 int ret;
345
346 rdir = GNUNET_STRINGS_filename_expand (dir);
347 if (rdir == NULL)
348 return GNUNET_SYSERR;
349 len = strlen (rdir);
350 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
351 len--;
352 rdir[len] = '\0';
353 ret = GNUNET_DISK_directory_create (rdir);
354 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
355 ret = GNUNET_NO;
356 GNUNET_free (rdir);
357 return ret;
358}
359
360/**
361 * Read the contents of a binary file into a buffer.
362 * @param fileName the name of the file, not freed,
363 * must already be expanded!
364 * @param len the maximum number of bytes to read
365 * @param result the buffer to write the result to
366 * @return the number of bytes read on success, -1 on failure
367 */
368int
369GNUNET_DISK_file_read (const char *fileName, int len, void *result)
370{
371 /* open file, must exist, open read only */
372 int handle;
373 int size;
374
375 GNUNET_assert (fileName != NULL);
376 GNUNET_assert (len > 0);
377 if (len == 0)
378 return 0;
379 GNUNET_assert (result != NULL);
380 handle = GNUNET_DISK_file_open (fileName, O_RDONLY, S_IRUSR);
381 if (handle < 0)
382 return -1;
383 size = READ (handle, result, len);
384 GNUNET_DISK_file_close (fileName, handle);
385 return size;
386}
387
388
389/**
390 * Convert string to value ('755' for chmod-call)
391 */
392static int
393atoo (const char *s)
394{
395 int n = 0;
396
397 while (('0' <= *s) && (*s < '8'))
398 {
399 n <<= 3;
400 n += *s++ - '0';
401 }
402 return n;
403}
404
405/**
406 * Write a buffer to a file.
407 * @param fileName the name of the file, NOT freed!
408 * @param buffer the data to write
409 * @param n number of bytes to write
410 * @param mode permissions to set on the file
411 * @return GNUNET_OK on success, GNUNET_SYSERR on error
412 */
413int
414GNUNET_DISK_file_write (const char *fileName,
415 const void *buffer, unsigned int n, const char *mode)
416{
417 int handle;
418 char *fn;
419
420 /* open file, open with 600, create if not
421 present, otherwise overwrite */
422 GNUNET_assert (fileName != NULL);
423 fn = GNUNET_STRINGS_filename_expand (fileName);
424 handle = GNUNET_DISK_file_open (fn, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
425 if (handle == -1)
426 {
427 GNUNET_free (fn);
428 return GNUNET_SYSERR;
429 }
430 GNUNET_assert ((n == 0) || (buffer != NULL));
431 /* write the buffer take length from the beginning */
432 if (n != WRITE (handle, buffer, n))
433 {
434 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
435 GNUNET_DISK_file_close (fn, handle);
436 GNUNET_free (fn);
437 return GNUNET_SYSERR;
438 }
439 GNUNET_DISK_file_close (fn, handle);
440 if (0 != CHMOD (fn, atoo (mode)))
441 {
442 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn);
443 }
444 GNUNET_free (fn);
445 return GNUNET_OK;
446}
447
448/**
449 * Scan a directory for files. The name of the directory
450 * must be expanded first (!).
451 * @param dirName the name of the directory
452 * @param callback the method to call for each file,
453 * can be NULL, in that case, we only count
454 * @param data argument to pass to callback
455 * @return the number of files found, GNUNET_SYSERR on error or
456 * ieration aborted by callback returning GNUNET_SYSERR
457 */
458int
459GNUNET_DISK_directory_scan (const char *dirName,
460 GNUNET_FileNameCallback callback, void *data)
461{
462 DIR *dinfo;
463 struct dirent *finfo;
464 struct stat istat;
465 int count = 0;
466 char *name;
467 char *dname;
468 unsigned int name_len;
469 unsigned int n_size;
470
471 GNUNET_assert (dirName != NULL);
472 dname = GNUNET_STRINGS_filename_expand (dirName);
473 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
474 dname[strlen (dname) - 1] = '\0';
475 if (0 != STAT (dname, &istat))
476 {
477 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
478 GNUNET_free (dname);
479 return GNUNET_SYSERR;
480 }
481 if (!S_ISDIR (istat.st_mode))
482 {
483 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
484 _("Expected `%s' to be a directory!\n"), dirName);
485 GNUNET_free (dname);
486 return GNUNET_SYSERR;
487 }
488 errno = 0;
489 dinfo = OPENDIR (dname);
490 if ((errno == EACCES) || (dinfo == NULL))
491 {
492 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
493 if (dinfo != NULL)
494 closedir (dinfo);
495 GNUNET_free (dname);
496 return GNUNET_SYSERR;
497 }
498 name_len = 256;
499 n_size = strlen (dname) + name_len + 2;
500 name = GNUNET_malloc (n_size);
501 while ((finfo = readdir (dinfo)) != NULL)
502 {
503 if ((0 == strcmp (finfo->d_name, ".")) ||
504 (0 == strcmp (finfo->d_name, "..")))
505 continue;
506 if (callback != NULL)
507 {
508 if (name_len < strlen (finfo->d_name))
509 {
510 GNUNET_free (name);
511 name_len = strlen (finfo->d_name);
512 n_size = strlen (dname) + name_len + 2;
513 name = GNUNET_malloc (n_size);
514 }
515 /* dname can end in "/" only if dname == "/";
516 if dname does not end in "/", we need to add
517 a "/" (otherwise, we must not!) */
518 GNUNET_snprintf (name,
519 n_size,
520 "%s%s%s",
521 dname,
522 (strcmp (dname, DIR_SEPARATOR_STR) ==
523 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
524 if (GNUNET_OK != callback (data, name))
525 {
526 closedir (dinfo);
527 GNUNET_free (name);
528 GNUNET_free (dname);
529 return GNUNET_SYSERR;
530 }
531 }
532 count++;
533 }
534 closedir (dinfo);
535 GNUNET_free (name);
536 GNUNET_free (dname);
537 return count;
538}
539
540
541/**
542 * Opaque handle used for iterating over a directory.
543 */
544struct GNUNET_DISK_DirectoryIterator
545{
546 /**
547 * Our scheduler.
548 */
549 struct GNUNET_SCHEDULER_Handle *sched;
550
551 /**
552 * Function to call on directory entries.
553 */
554 GNUNET_DISK_DirectoryIteratorCallback callback;
555
556 /**
557 * Closure for callback.
558 */
559 void *callback_cls;
560
561 /**
562 * Reference to directory.
563 */
564 DIR *directory;
565
566 /**
567 * Directory name.
568 */
569 char *dirname;
570
571 /**
572 * Next filename to process.
573 */
574 char *next_name;
575
576 /**
577 * Our priority.
578 */
579 enum GNUNET_SCHEDULER_Priority priority;
580
581};
582
583
584/**
585 * Task used by the directory iterator.
586 */
587static void
588directory_iterator_task (void *cls,
589 const struct GNUNET_SCHEDULER_TaskContext *tc)
590{
591 struct GNUNET_DISK_DirectoryIterator *iter = cls;
592 char *name;
593
594 name = iter->next_name;
595 GNUNET_assert (name != NULL);
596 iter->next_name = NULL;
597 iter->callback (iter->callback_cls, iter, name, iter->dirname);
598 GNUNET_free (name);
599}
600
601
602/**
603 * This function must be called during the DiskIteratorCallback
604 * (exactly once) to schedule the task to process the next
605 * filename in the directory (if there is one).
606 *
607 * @param iter opaque handle for the iterator
608 * @param can set to GNUNET_YES to terminate the iteration early
609 * @return GNUNET_YES if iteration will continue,
610 * GNUNET_NO if this was the last entry (and iteration is complete),
611 * GNUNET_SYSERR if abort was YES
612 */
613int
614GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
615 *iter, int can)
616{
617 struct dirent *finfo;
618
619 GNUNET_assert (iter->next_name == NULL);
620 if (can == GNUNET_YES)
621 {
622 closedir (iter->directory);
623 GNUNET_free (iter->dirname);
624 GNUNET_free (iter);
625 return GNUNET_SYSERR;
626 }
627 while (NULL != (finfo = readdir (iter->directory)))
628 {
629 if ((0 == strcmp (finfo->d_name, ".")) ||
630 (0 == strcmp (finfo->d_name, "..")))
631 continue;
632 GNUNET_asprintf (&iter->next_name,
633 "%s%s%s",
634 iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
635 break;
636 }
637 if (finfo == NULL)
638 {
639 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
640 return GNUNET_NO;
641 }
642 GNUNET_SCHEDULER_add_after (iter->sched,
643 GNUNET_YES,
644 iter->priority,
645 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
646 &directory_iterator_task, iter);
647 return GNUNET_YES;
648}
649
650
651/**
652 * Scan a directory for files using the scheduler to run a task for
653 * each entry. The name of the directory must be expanded first (!).
654 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
655 * may provide a simpler API.
656 *
657 * @param sched scheduler to use
658 * @param prio priority to use
659 * @param dirName the name of the directory
660 * @param callback the method to call for each file
661 * @param callback_cls closure for callback
662 */
663void
664GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
665 enum GNUNET_SCHEDULER_Priority prio,
666 const char *dirName,
667 GNUNET_DISK_DirectoryIteratorCallback
668 callback, void *callback_cls)
669{
670 struct GNUNET_DISK_DirectoryIterator *di;
671
672 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
673 di->sched = sched;
674 di->callback = callback;
675 di->callback_cls = callback_cls;
676 di->directory = OPENDIR (dirName);
677 di->dirname = GNUNET_strdup (dirName);
678 di->priority = prio;
679 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
680}
681
682
683static int
684remove_helper (void *unused, const char *fn)
685{
686 GNUNET_DISK_directory_remove (fn);
687 return GNUNET_OK;
688}
689
690/**
691 * Remove all files in a directory (rm -rf). Call with
692 * caution.
693 *
694 *
695 * @param fileName the file to remove
696 * @return GNUNET_OK on success, GNUNET_SYSERR on error
697 */
698int
699GNUNET_DISK_directory_remove (const char *fileName)
700{
701 struct stat istat;
702
703 if (0 != LSTAT (fileName, &istat))
704 return GNUNET_NO; /* file may not exist... */
705 if (UNLINK (fileName) == 0)
706 return GNUNET_OK;
707 if ((errno != EISDIR) &&
708 /* EISDIR is not sufficient in all cases, e.g.
709 sticky /tmp directory may result in EPERM on BSD.
710 So we also explicitly check "isDirectory" */
711 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
712 {
713 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
714 return GNUNET_SYSERR;
715 }
716 if (GNUNET_SYSERR ==
717 GNUNET_DISK_directory_scan (fileName, remove_helper, NULL))
718 return GNUNET_SYSERR;
719 if (0 != RMDIR (fileName))
720 {
721 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
722 return GNUNET_SYSERR;
723 }
724 return GNUNET_OK;
725}
726
727void
728GNUNET_DISK_file_close (const char *filename, int fd)
729{
730 if (0 != CLOSE (fd))
731 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", filename);
732}
733
734int
735GNUNET_DISK_file_open (const char *filename, int oflag, ...)
736{
737 char *fn;
738 int mode;
739 int ret;
740#ifdef MINGW
741 char szFile[_MAX_PATH + 1];
742 long lRet;
743
744 if ((lRet = plibc_conv_to_win_path (filename, szFile)) != ERROR_SUCCESS)
745 {
746 errno = ENOENT;
747 SetLastError (lRet);
748 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
749 "plibc_conv_to_win_path", filename);
750 return -1;
751 }
752 fn = GNUNET_strdup (szFile);
753#else
754 fn = GNUNET_STRINGS_filename_expand (filename);
755#endif
756 if (oflag & O_CREAT)
757 {
758 va_list arg;
759 va_start (arg, oflag);
760 mode = va_arg (arg, int);
761 va_end (arg);
762 }
763 else
764 {
765 mode = 0;
766 }
767#ifdef MINGW
768 /* set binary mode */
769 oflag |= O_BINARY;
770#endif
771 ret = OPEN (fn, oflag, mode);
772 if (ret == -1)
773 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", fn);
774 GNUNET_free (fn);
775 return ret;
776}
777
778#define COPY_BLK_SIZE 65536
779
780/**
781 * Copy a file.
782 * @return GNUNET_OK on success, GNUNET_SYSERR on error
783 */
784int
785GNUNET_DISK_file_copy (const char *src, const char *dst)
786{
787 char *buf;
788 unsigned long long pos;
789 unsigned long long size;
790 unsigned long long len;
791 int in;
792 int out;
793
794 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
795 return GNUNET_SYSERR;
796 pos = 0;
797 in = GNUNET_DISK_file_open (src, O_RDONLY | O_LARGEFILE);
798 if (in == -1)
799 return GNUNET_SYSERR;
800 out = GNUNET_DISK_file_open (dst,
801 O_LARGEFILE | O_WRONLY | O_CREAT | O_EXCL,
802 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
803 if (out == -1)
804 {
805 GNUNET_DISK_file_close (src, in);
806 return GNUNET_SYSERR;
807 }
808 buf = GNUNET_malloc (COPY_BLK_SIZE);
809 while (pos < size)
810 {
811 len = COPY_BLK_SIZE;
812 if (len > size - pos)
813 len = size - pos;
814 if (len != READ (in, buf, len))
815 goto FAIL;
816 if (len != WRITE (out, buf, len))
817 goto FAIL;
818 pos += len;
819 }
820 GNUNET_free (buf);
821 GNUNET_DISK_file_close (src, in);
822 GNUNET_DISK_file_close (dst, out);
823 return GNUNET_OK;
824FAIL:
825 GNUNET_free (buf);
826 GNUNET_DISK_file_close (src, in);
827 GNUNET_DISK_file_close (dst, out);
828 return GNUNET_SYSERR;
829}
830
831
832/**
833 * @brief Removes special characters as ':' from a filename.
834 * @param fn the filename to canonicalize
835 */
836void
837GNUNET_DISK_filename_canonicalize (char *fn)
838{
839 char *idx;
840 char c;
841
842 idx = fn;
843 while (*idx)
844 {
845 c = *idx;
846
847 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
848 c == '"' || c == '<' || c == '>' || c == '|')
849 {
850 *idx = '_';
851 }
852
853 idx++;
854 }
855}
856
857
858
859/**
860 * @brief Change owner of a file
861 */
862int
863GNUNET_DISK_file_change_owner (const char *filename, const char *user)
864{
865#ifndef MINGW
866 struct passwd *pws;
867
868 pws = getpwnam (user);
869 if (pws == NULL)
870 {
871 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
872 _("Cannot obtain information about user `%s': %s\n"),
873 user, STRERROR (errno));
874 return GNUNET_SYSERR;
875 }
876 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
877 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
878#endif
879 return GNUNET_OK;
880}
881
882
883/**
884 * Construct full path to a file inside of the private
885 * directory used by GNUnet. Also creates the corresponding
886 * directory. If the resulting name is supposed to be
887 * a directory, end the last argument in '/' (or pass
888 * DIR_SEPARATOR_STR as the last argument before NULL).
889 *
890 * @param serviceName name of the service
891 * @param varargs is NULL-terminated list of
892 * path components to append to the
893 * private directory name.
894 * @return the constructed filename
895 */
896char *
897GNUNET_DISK_get_home_filename (struct GNUNET_CONFIGURATION_Handle *cfg,
898 const char *serviceName, ...)
899{
900 const char *c;
901 char *pfx;
902 char *ret;
903 va_list ap;
904 unsigned int needed;
905
906 if (GNUNET_OK !=
907 GNUNET_CONFIGURATION_get_value_filename (cfg,
908 serviceName, "HOME", &pfx))
909 return NULL;
910 if (pfx == NULL)
911 {
912 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
913 _("No `%s' specified for service `%s' in configuration.\n"),
914 "HOME", serviceName);
915 return NULL;
916 }
917 needed = strlen (pfx) + 2;
918 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
919 needed++;
920 va_start (ap, serviceName);
921 while (1)
922 {
923 c = va_arg (ap, const char *);
924 if (c == NULL)
925 break;
926 needed += strlen (c);
927 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
928 needed++;
929 }
930 va_end (ap);
931 ret = GNUNET_malloc (needed);
932 strcpy (ret, pfx);
933 GNUNET_free (pfx);
934 va_start (ap, serviceName);
935 while (1)
936 {
937 c = va_arg (ap, const char *);
938 if (c == NULL)
939 break;
940 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
941 strcat (ret, DIR_SEPARATOR_STR);
942 strcat (ret, c);
943 }
944 va_end (ap);
945 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
946 GNUNET_DISK_directory_create_for_file (ret);
947 else
948 GNUNET_DISK_directory_create (ret);
949 return ret;
950}
951
952
953
954/* end of disk.c */