2 This file is part of GNUnet.
3 (C) 2001--2012 Christian Grothoff (and other contributing authors)
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.
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.
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.
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
29 #include "gnunet_common.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_disk_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_strings_lib.h"
34 #include "gnunet_crypto_lib.h"
37 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
39 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
41 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
44 * Block size for IO for copying files.
46 #define COPY_BLK_SIZE 65536
50 #if defined(LINUX) || defined(CYGWIN) || defined(GNU)
53 #if defined(SOMEBSD) || defined(DARWIN)
54 #include <sys/param.h>
55 #include <sys/mount.h>
58 #include <sys/types.h>
59 #include <sys/statvfs.h>
64 ULONG PipeSerialNumber;
66 #define _IFMT 0170000 /* type of file */
67 #define _IFLNK 0120000 /* symbolic link */
68 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
70 #error PORT-ME: need to port statfs (how much space is left on the drive?)
76 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
80 #include <sys/statvfs.h>
85 * Handle used to manage a pipe.
87 struct GNUNET_DISK_PipeHandle
90 * File descriptors for the pipe.
91 * One or both of them could be NULL.
93 struct GNUNET_DISK_FileHandle *fd[2];
98 * Closure for the recursion to determine the file size
101 struct GetFileSizeData
104 * Set to the total file size.
109 * GNUNET_YES if symbolic links should be included.
111 int include_sym_links;
114 * GNUNET_YES if mode is file-only (return total == -1 for directories).
116 int single_file_mode;
122 * Translate GNUnet-internal permission bitmap to UNIX file
123 * access permission bitmap.
125 * @param perm file permissions, GNUnet style
126 * @return file permissions, UNIX style
129 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
134 if (perm & GNUNET_DISK_PERM_USER_READ)
136 if (perm & GNUNET_DISK_PERM_USER_WRITE)
138 if (perm & GNUNET_DISK_PERM_USER_EXEC)
140 if (perm & GNUNET_DISK_PERM_GROUP_READ)
142 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
144 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
146 if (perm & GNUNET_DISK_PERM_OTHER_READ)
148 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
150 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
159 * Iterate over all files in the given directory and
160 * accumulate their size.
162 * @param cls closure of type "struct GetFileSizeData"
163 * @param fn current filename we are looking at
164 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
167 getSizeRec (void *cls, const char *fn)
169 struct GetFileSizeData *gfsd = cls;
178 if (0 != STAT64 (fn, &buf))
180 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
181 return GNUNET_SYSERR;
184 if (0 != STAT (fn, &buf))
186 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
187 return GNUNET_SYSERR;
190 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
193 return GNUNET_SYSERR;
195 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
196 gfsd->total += buf.st_size;
197 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
198 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
200 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
201 return GNUNET_SYSERR;
208 * Checks whether a handle is invalid
210 * @param h handle to check
211 * @return GNUNET_YES if invalid, GNUNET_NO if valid
214 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
217 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
219 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
224 * Get the size of an open file.
226 * @param fh open file handle
227 * @param size where to write size of the file
228 * @return GNUNET_OK on success, GNUNET_SYSERR on error
231 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
237 b = GetFileSizeEx (fh->h, &li);
240 SetErrnoFromWinError (GetLastError ());
241 return GNUNET_SYSERR;
243 *size = (OFF_T) li.QuadPart;
247 if (0 != FSTAT (fh->fd, &sbuf))
248 return GNUNET_SYSERR;
249 *size = sbuf.st_size;
256 * Move the read/write pointer in a file
258 * @param h handle of an open file
259 * @param offset position to move to
260 * @param whence specification to which position the offset parameter relates to
261 * @return the new position on success, GNUNET_SYSERR otherwise
264 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
265 enum GNUNET_DISK_Seek whence)
270 return GNUNET_SYSERR;
275 LARGE_INTEGER new_pos;
278 static DWORD t[] = { FILE_BEGIN, FILE_CURRENT, FILE_END };
279 li.QuadPart = offset;
281 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
284 SetErrnoFromWinError (GetLastError ());
285 return GNUNET_SYSERR;
287 return (OFF_T) new_pos.QuadPart;
289 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
291 return lseek (h->fd, offset, t[whence]);
297 * Get the size of the file (or directory) of the given file (in
300 * @param filename name of the file or directory
301 * @param size set to the size of the file (or,
302 * in the case of directories, the sum
303 * of all sizes of files in the directory)
304 * @param includeSymLinks should symbolic links be
306 * @param singleFileMode GNUNET_YES to only get size of one file
307 * and return GNUNET_SYSERR for directories.
308 * @return GNUNET_SYSERR on error, GNUNET_OK on success
311 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
312 int includeSymLinks, int singleFileMode)
314 struct GetFileSizeData gfsd;
317 GNUNET_assert (size != NULL);
319 gfsd.include_sym_links = includeSymLinks;
320 gfsd.single_file_mode = singleFileMode;
321 ret = getSizeRec (&gfsd, filename);
328 * Obtain some unique identifiers for the given file
329 * that can be used to identify it in the local system.
330 * This function is used between GNUnet processes to
331 * quickly check if two files with the same absolute path
332 * are actually identical. The two processes represent
333 * the same peer but may communicate over the network
334 * (and the file may be on an NFS volume). This function
335 * may not be supported on all operating systems.
337 * @param filename name of the file
338 * @param dev set to the device ID
339 * @param ino set to the inode ID
340 * @return GNUNET_OK on success
343 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
350 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
352 *dev = (uint64_t) fbuf.f_fsid;
353 *ino = (uint64_t) sbuf.st_ino;
360 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
362 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
363 ((uint64_t) fbuf.f_fsid.val[1]);
364 *ino = (uint64_t) sbuf.st_ino;
368 // FIXME NILS: test this
369 struct GNUNET_DISK_FileHandle *fh;
370 BY_HANDLE_FILE_INFORMATION info;
373 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
375 return GNUNET_SYSERR;
376 succ = GetFileInformationByHandle (fh->h, &info);
377 GNUNET_DISK_file_close (fh);
380 *dev = info.dwVolumeSerialNumber;
381 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
385 return GNUNET_SYSERR;
388 return GNUNET_SYSERR;
393 * Create the name for a temporary file or directory from a template.
395 * @param t template (without XXXXX or "/tmp/")
396 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
399 mktemp_name (const char *t)
405 if ((t[0] != '/') && (t[0] != '\\')
407 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
411 /* FIXME: This uses system codepage on W32, not UTF-8 */
412 tmpdir = getenv ("TMPDIR");
414 tmpdir = getenv ("TMP");
416 tmpdir = getenv ("TEMP");
419 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
423 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
426 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
427 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
450 tfn = GNUNET_strdup (fn);
451 random_fn = _mktemp (tfn);
452 if (NULL == random_fn)
457 /* FIXME: assume fn to be UTF-8-encoded and do the right thing */
458 if (0 == CreateDirectoryA (tfn, NULL))
460 DWORD error = GetLastError ();
462 if (ERROR_ALREADY_EXISTS == error)
474 * Create an (empty) temporary directory on disk. If the given name is not
475 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
476 * 6 random characters will be appended to the name to create a unique
479 * @param t component to use for the name;
480 * does NOT contain "XXXXXX" or "/tmp/".
481 * @return NULL on error, otherwise name of fresh
482 * file on disk in directory for temporary files
485 GNUNET_DISK_mkdtemp (const char *t)
489 fn = mktemp_name (t);
490 if (fn != mkdtemp (fn))
492 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
501 * Move a file out of the way (create a backup) by
502 * renaming it to "orig.NUM~" where NUM is the smallest
503 * number that is not used yet.
505 * @param fil name of the file to back up
508 GNUNET_DISK_file_backup (const char *fil)
514 slen = strlen (fil) + 20;
515 target = GNUNET_malloc (slen);
519 GNUNET_snprintf (target, slen,
523 } while (0 == access (target, F_OK));
524 if (0 != rename (fil, target))
525 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
528 GNUNET_free (target);
533 * Create an (empty) temporary file on disk. If the given name is not
534 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
535 * 6 random characters will be appended to the name to create a unique
538 * @param t component to use for the name;
539 * does NOT contain "XXXXXX" or "/tmp/".
540 * @return NULL on error, otherwise name of fresh
541 * file on disk in directory for temporary files
544 GNUNET_DISK_mktemp (const char *t)
549 fn = mktemp_name (t);
550 if (-1 == (fd = mkstemp (fn)))
552 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
557 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
563 * Test if "fil" is a directory and listable. Optionally, also check if the
564 * directory is readable. Will not print an error message if the directory does
565 * not exist. Will log errors if GNUNET_SYSERR is returned (i.e., a file exists
566 * with the same name).
568 * @param fil filename to test
569 * @param is_readable GNUNET_YES to additionally check if "fil" is readable;
570 * GNUNET_NO to disable this check
571 * @return GNUNET_YES if yes, GNUNET_NO if not; GNUNET_SYSERR if it
572 * does not exist or stat'ed
575 GNUNET_DISK_directory_test (const char *fil, int is_readable)
577 struct stat filestat;
580 ret = STAT (fil, &filestat);
584 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
585 return GNUNET_SYSERR;
587 if (!S_ISDIR (filestat.st_mode))
589 LOG (GNUNET_ERROR_TYPE_WARNING,
590 "A file already exits with the same name %s\n", fil);
593 if (GNUNET_YES == is_readable)
594 ret = ACCESS (fil, R_OK | X_OK);
596 ret = ACCESS (fil, X_OK);
599 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
607 * Check that fil corresponds to a filename
608 * (of a file that exists and that is not a directory).
610 * @param fil filename to check
611 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
612 * else (will print an error message in that case, too).
615 GNUNET_DISK_file_test (const char *fil)
617 struct stat filestat;
621 rdir = GNUNET_STRINGS_filename_expand (fil);
623 return GNUNET_SYSERR;
625 ret = STAT (rdir, &filestat);
630 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
632 return GNUNET_SYSERR;
637 if (!S_ISREG (filestat.st_mode))
642 if (ACCESS (rdir, F_OK) < 0)
644 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
646 return GNUNET_SYSERR;
654 * Implementation of "mkdir -p"
655 * @param dir the directory to create
656 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
659 GNUNET_DISK_directory_create (const char *dir)
667 rdir = GNUNET_STRINGS_filename_expand (dir);
669 return GNUNET_SYSERR;
673 pos = 1; /* skip heading '/' */
675 /* Local or Network path? */
676 if (strncmp (rdir, "\\\\", 2) == 0)
681 if (rdir[pos] == '\\')
691 pos = 3; /* strlen("C:\\") */
694 /* Check which low level directories already exist */
696 rdir[len] = DIR_SEPARATOR;
699 if (DIR_SEPARATOR == rdir[pos2])
702 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
703 if (GNUNET_NO == ret)
706 return GNUNET_SYSERR;
708 rdir[pos2] = DIR_SEPARATOR;
709 if (GNUNET_YES == ret)
720 /* Start creating directories */
723 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
726 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
727 if (GNUNET_NO == ret)
730 return GNUNET_SYSERR;
732 if (GNUNET_SYSERR == ret)
735 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
737 wchar_t wrdir[MAX_PATH + 1];
738 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
739 ret = !CreateDirectoryW (wrdir, NULL);
743 if ((ret != 0) && (errno != EEXIST))
745 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
747 return GNUNET_SYSERR;
750 rdir[pos] = DIR_SEPARATOR;
760 * Create the directory structure for storing
763 * @param filename name of a file in the directory
764 * @returns GNUNET_OK on success,
765 * GNUNET_SYSERR on failure,
766 * GNUNET_NO if the directory
767 * exists but is not writeable for us
770 GNUNET_DISK_directory_create_for_file (const char *filename)
776 rdir = GNUNET_STRINGS_filename_expand (filename);
778 return GNUNET_SYSERR;
780 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
783 ret = GNUNET_DISK_directory_create (rdir);
784 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
792 * Read the contents of a binary file into a buffer.
793 * @param h handle to an open file
794 * @param result the buffer to write the result to
795 * @param len the maximum number of bytes to read
796 * @return the number of bytes read on success, GNUNET_SYSERR on failure
799 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
805 return GNUNET_SYSERR;
811 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
813 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
815 SetErrnoFromWinError (GetLastError ());
816 return GNUNET_SYSERR;
821 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
823 if (GetLastError () != ERROR_IO_PENDING)
825 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
826 SetErrnoFromWinError (GetLastError ());
827 return GNUNET_SYSERR;
829 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
830 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
832 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from pipe\n", bytesRead);
836 return read (h->fd, result, len);
842 * Read the contents of a binary file into a buffer.
843 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
844 * when no data can be read).
846 * @param h handle to an open file
847 * @param result the buffer to write the result to
848 * @param len the maximum number of bytes to read
849 * @return the number of bytes read on success, GNUNET_SYSERR on failure
852 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
859 return GNUNET_SYSERR;
865 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
867 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
869 SetErrnoFromWinError (GetLastError ());
870 return GNUNET_SYSERR;
875 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
877 if (GetLastError () != ERROR_IO_PENDING)
879 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
880 SetErrnoFromWinError (GetLastError ());
881 return GNUNET_SYSERR;
885 LOG (GNUNET_ERROR_TYPE_DEBUG,
886 "ReadFile() queued a read, cancelling\n");
889 return GNUNET_SYSERR;
892 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
899 /* set to non-blocking, read, then set back */
900 flags = fcntl (h->fd, F_GETFL);
901 if (0 == (flags & O_NONBLOCK))
902 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
903 ret = read (h->fd, result, len);
904 if (0 == (flags & O_NONBLOCK))
907 (void) fcntl (h->fd, F_SETFL, flags);
916 * Read the contents of a binary file into a buffer.
918 * @param fn file name
919 * @param result the buffer to write the result to
920 * @param len the maximum number of bytes to read
921 * @return number of bytes read, GNUNET_SYSERR on failure
924 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
926 struct GNUNET_DISK_FileHandle *fh;
929 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
931 return GNUNET_SYSERR;
932 ret = GNUNET_DISK_file_read (fh, result, len);
933 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
940 * Write a buffer to a file.
941 * @param h handle to open file
942 * @param buffer the data to write
943 * @param n number of bytes to write
944 * @return number of bytes written on success, GNUNET_SYSERR on error
947 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
948 const void *buffer, size_t n)
953 return GNUNET_SYSERR;
959 if (h->type != GNUNET_DISK_HANLDE_TYPE_PIPE)
961 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
963 SetErrnoFromWinError (GetLastError ());
964 return GNUNET_SYSERR;
969 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
970 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
972 if (GetLastError () != ERROR_IO_PENDING)
974 SetErrnoFromWinError (GetLastError ());
975 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
977 return GNUNET_SYSERR;
979 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
980 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
982 SetErrnoFromWinError (GetLastError ());
983 LOG (GNUNET_ERROR_TYPE_DEBUG,
984 "Error getting overlapped result while writing to pipe: %u\n",
986 return GNUNET_SYSERR;
992 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
994 LOG (GNUNET_ERROR_TYPE_DEBUG,
995 "Error getting control overlapped result while writing to pipe: %u\n",
1000 LOG (GNUNET_ERROR_TYPE_DEBUG,
1001 "Wrote %u bytes (ovr says %u), picking the greatest\n",
1005 if (bytesWritten == 0)
1009 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
1011 return GNUNET_SYSERR;
1014 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1016 return bytesWritten;
1018 return write (h->fd, buffer, n);
1024 * Write a buffer to a file, blocking, if necessary.
1025 * @param h handle to open file
1026 * @param buffer the data to write
1027 * @param n number of bytes to write
1028 * @return number of bytes written on success, GNUNET_SYSERR on error
1031 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
1032 const void *buffer, size_t n)
1037 return GNUNET_SYSERR;
1042 /* We do a non-overlapped write, which is as blocking as it gets */
1043 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
1044 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1046 SetErrnoFromWinError (GetLastError ());
1047 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1049 return GNUNET_SYSERR;
1051 if (bytesWritten == 0 && n > 0)
1053 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
1054 WaitForSingleObject (h->h, INFINITE);
1055 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
1057 SetErrnoFromWinError (GetLastError ());
1058 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
1060 return GNUNET_SYSERR;
1063 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1064 return bytesWritten;
1069 /* set to blocking, write, then set back */
1070 flags = fcntl (h->fd, F_GETFL);
1071 if (0 != (flags & O_NONBLOCK))
1072 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1073 ret = write (h->fd, buffer, n);
1074 if (0 == (flags & O_NONBLOCK))
1075 (void) fcntl (h->fd, F_SETFL, flags);
1082 * Write a buffer to a file. If the file is longer than the
1083 * number of bytes that will be written, it will be truncated.
1085 * @param fn file name
1086 * @param buffer the data to write
1087 * @param n number of bytes to write
1088 * @param mode file permissions
1089 * @return number of bytes written on success, GNUNET_SYSERR on error
1092 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1093 enum GNUNET_DISK_AccessPermissions mode)
1095 struct GNUNET_DISK_FileHandle *fh;
1098 fh = GNUNET_DISK_file_open (fn,
1099 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1100 | GNUNET_DISK_OPEN_CREATE, mode);
1102 return GNUNET_SYSERR;
1103 ret = GNUNET_DISK_file_write (fh, buffer, n);
1104 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1110 * Scan a directory for files.
1112 * @param dirName the name of the directory
1113 * @param callback the method to call for each file,
1114 * can be NULL, in that case, we only count
1115 * @param callback_cls closure for callback
1116 * @return the number of files found, GNUNET_SYSERR on error or
1117 * ieration aborted by callback returning GNUNET_SYSERR
1120 GNUNET_DISK_directory_scan (const char *dirName,
1121 GNUNET_FileNameCallback callback,
1125 struct dirent *finfo;
1131 unsigned int name_len;
1132 unsigned int n_size;
1134 GNUNET_assert (dirName != NULL);
1135 dname = GNUNET_STRINGS_filename_expand (dirName);
1137 return GNUNET_SYSERR;
1138 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1139 dname[strlen (dname) - 1] = '\0';
1140 if (0 != STAT (dname, &istat))
1142 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1143 GNUNET_free (dname);
1144 return GNUNET_SYSERR;
1146 if (!S_ISDIR (istat.st_mode))
1148 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1150 GNUNET_free (dname);
1151 return GNUNET_SYSERR;
1154 dinfo = OPENDIR (dname);
1155 if ((errno == EACCES) || (dinfo == NULL))
1157 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1160 GNUNET_free (dname);
1161 return GNUNET_SYSERR;
1164 n_size = strlen (dname) + name_len + 2;
1165 name = GNUNET_malloc (n_size);
1166 while ((finfo = READDIR (dinfo)) != NULL)
1168 if ((0 == strcmp (finfo->d_name, ".")) ||
1169 (0 == strcmp (finfo->d_name, "..")))
1171 if (callback != NULL)
1173 if (name_len < strlen (finfo->d_name))
1176 name_len = strlen (finfo->d_name);
1177 n_size = strlen (dname) + name_len + 2;
1178 name = GNUNET_malloc (n_size);
1180 /* dname can end in "/" only if dname == "/";
1181 * if dname does not end in "/", we need to add
1182 * a "/" (otherwise, we must not!) */
1183 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1184 (strcmp (dname, DIR_SEPARATOR_STR) ==
1185 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1186 ret = callback (callback_cls, name);
1187 if (GNUNET_OK != ret)
1191 GNUNET_free (dname);
1192 if (GNUNET_NO == ret)
1194 return GNUNET_SYSERR;
1201 GNUNET_free (dname);
1207 * Opaque handle used for iterating over a directory.
1209 struct GNUNET_DISK_DirectoryIterator
1213 * Function to call on directory entries.
1215 GNUNET_DISK_DirectoryIteratorCallback callback;
1218 * Closure for callback.
1223 * Reference to directory.
1233 * Next filename to process.
1240 enum GNUNET_SCHEDULER_Priority priority;
1246 * Task used by the directory iterator.
1249 directory_iterator_task (void *cls,
1250 const struct GNUNET_SCHEDULER_TaskContext *tc)
1252 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1255 name = iter->next_name;
1256 GNUNET_assert (name != NULL);
1257 iter->next_name = NULL;
1258 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1264 * This function must be called during the DiskIteratorCallback
1265 * (exactly once) to schedule the task to process the next
1266 * filename in the directory (if there is one).
1268 * @param iter opaque handle for the iterator
1269 * @param can set to GNUNET_YES to terminate the iteration early
1270 * @return GNUNET_YES if iteration will continue,
1271 * GNUNET_NO if this was the last entry (and iteration is complete),
1272 * GNUNET_SYSERR if abort was YES
1275 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1278 struct dirent *finfo;
1280 GNUNET_assert (iter->next_name == NULL);
1281 if (can == GNUNET_YES)
1283 CLOSEDIR (iter->directory);
1284 GNUNET_free (iter->dirname);
1286 return GNUNET_SYSERR;
1288 while (NULL != (finfo = READDIR (iter->directory)))
1290 if ((0 == strcmp (finfo->d_name, ".")) ||
1291 (0 == strcmp (finfo->d_name, "..")))
1293 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1294 DIR_SEPARATOR_STR, finfo->d_name);
1299 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1302 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1309 * Scan a directory for files using the scheduler to run a task for
1310 * each entry. The name of the directory must be expanded first (!).
1311 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1312 * may provide a simpler API.
1314 * @param prio priority to use
1315 * @param dirName the name of the directory
1316 * @param callback the method to call for each file
1317 * @param callback_cls closure for callback
1318 * @return GNUNET_YES if directory is not empty and 'callback'
1319 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1322 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1323 const char *dirName,
1324 GNUNET_DISK_DirectoryIteratorCallback
1325 callback, void *callback_cls)
1327 struct GNUNET_DISK_DirectoryIterator *di;
1329 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1330 di->callback = callback;
1331 di->callback_cls = callback_cls;
1332 di->directory = OPENDIR (dirName);
1333 if (di->directory == NULL)
1336 callback (callback_cls, NULL, NULL, NULL);
1337 return GNUNET_SYSERR;
1339 di->dirname = GNUNET_strdup (dirName);
1340 di->priority = prio;
1341 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1346 * Function that removes the given directory by calling
1347 * "GNUNET_DISK_directory_remove".
1349 * @param unused not used
1350 * @param fn directory to remove
1354 remove_helper (void *unused, const char *fn)
1356 (void) GNUNET_DISK_directory_remove (fn);
1362 * Remove all files in a directory (rm -rf). Call with
1366 * @param filename the file to remove
1367 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1370 GNUNET_DISK_directory_remove (const char *filename)
1374 if (0 != LSTAT (filename, &istat))
1375 return GNUNET_NO; /* file may not exist... */
1376 (void) CHMOD (filename, S_IWUSR | S_IRUSR | S_IXUSR);
1377 if (UNLINK (filename) == 0)
1379 if ((errno != EISDIR) &&
1380 /* EISDIR is not sufficient in all cases, e.g.
1381 * sticky /tmp directory may result in EPERM on BSD.
1382 * So we also explicitly check "isDirectory" */
1383 (GNUNET_YES != GNUNET_DISK_directory_test (filename, GNUNET_YES)))
1385 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1386 return GNUNET_SYSERR;
1388 if (GNUNET_SYSERR ==
1389 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1390 return GNUNET_SYSERR;
1391 if (0 != RMDIR (filename))
1393 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1394 return GNUNET_SYSERR;
1403 * @param src file to copy
1404 * @param dst destination file name
1405 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1408 GNUNET_DISK_file_copy (const char *src, const char *dst)
1414 struct GNUNET_DISK_FileHandle *in;
1415 struct GNUNET_DISK_FileHandle *out;
1417 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1418 return GNUNET_SYSERR;
1420 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1421 GNUNET_DISK_PERM_NONE);
1423 return GNUNET_SYSERR;
1425 GNUNET_DISK_file_open (dst,
1426 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1427 GNUNET_DISK_OPEN_FAILIFEXISTS,
1428 GNUNET_DISK_PERM_USER_READ |
1429 GNUNET_DISK_PERM_USER_WRITE |
1430 GNUNET_DISK_PERM_GROUP_READ |
1431 GNUNET_DISK_PERM_GROUP_WRITE);
1434 GNUNET_DISK_file_close (in);
1435 return GNUNET_SYSERR;
1437 buf = GNUNET_malloc (COPY_BLK_SIZE);
1440 len = COPY_BLK_SIZE;
1441 if (len > size - pos)
1443 if (len != GNUNET_DISK_file_read (in, buf, len))
1445 if (len != GNUNET_DISK_file_write (out, buf, len))
1450 GNUNET_DISK_file_close (in);
1451 GNUNET_DISK_file_close (out);
1455 GNUNET_DISK_file_close (in);
1456 GNUNET_DISK_file_close (out);
1457 return GNUNET_SYSERR;
1462 * @brief Removes special characters as ':' from a filename.
1463 * @param fn the filename to canonicalize
1466 GNUNET_DISK_filename_canonicalize (char *fn)
1476 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1477 c == '<' || c == '>' || c == '|')
1489 * @brief Change owner of a file
1491 * @param filename name of file to change the owner of
1492 * @param user name of the new owner
1493 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1496 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1501 pws = getpwnam (user);
1504 LOG (GNUNET_ERROR_TYPE_ERROR,
1505 _("Cannot obtain information about user `%s': %s\n"), user,
1507 return GNUNET_SYSERR;
1509 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1510 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1517 * Lock a part of a file
1518 * @param fh file handle
1519 * @param lockStart absolute position from where to lock
1520 * @param lockEnd absolute position until where to lock
1521 * @param excl GNUNET_YES for an exclusive lock
1522 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1525 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1526 OFF_T lockEnd, int excl)
1531 return GNUNET_SYSERR;
1537 memset (&fl, 0, sizeof (struct flock));
1538 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1539 fl.l_whence = SEEK_SET;
1540 fl.l_start = lockStart;
1543 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1546 OFF_T diff = lockEnd - lockStart;
1547 DWORD diff_low, diff_high;
1548 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1549 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1551 memset (&o, 0, sizeof (OVERLAPPED));
1552 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1553 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1556 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1557 0, diff_low, diff_high, &o))
1559 SetErrnoFromWinError (GetLastError ());
1560 return GNUNET_SYSERR;
1569 * Unlock a part of a file
1570 * @param fh file handle
1571 * @param unlockStart absolute position from where to unlock
1572 * @param unlockEnd absolute position until where to unlock
1573 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1576 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1582 return GNUNET_SYSERR;
1588 memset (&fl, 0, sizeof (struct flock));
1589 fl.l_type = F_UNLCK;
1590 fl.l_whence = SEEK_SET;
1591 fl.l_start = unlockStart;
1592 fl.l_len = unlockEnd;
1594 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1597 OFF_T diff = unlockEnd - unlockStart;
1598 DWORD diff_low, diff_high;
1599 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1600 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1602 memset (&o, 0, sizeof (OVERLAPPED));
1603 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1604 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1606 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1608 SetErrnoFromWinError (GetLastError ());
1609 return GNUNET_SYSERR;
1618 * Open a file. Note that the access permissions will only be
1619 * used if a new file is created and if the underlying operating
1620 * system supports the given permissions.
1622 * @param fn file name to be opened
1623 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1624 * @param perm permissions for the newly created file, use
1625 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1626 * call (because of flags)
1627 * @return IO handle on success, NULL on error
1629 struct GNUNET_DISK_FileHandle *
1630 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1631 enum GNUNET_DISK_AccessPermissions perm)
1634 struct GNUNET_DISK_FileHandle *ret;
1640 wchar_t wexpfn[MAX_PATH + 1];
1647 expfn = GNUNET_STRINGS_filename_expand (fn);
1652 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1653 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1654 else if (flags & GNUNET_DISK_OPEN_READ)
1656 else if (flags & GNUNET_DISK_OPEN_WRITE)
1661 GNUNET_free (expfn);
1664 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1665 oflags |= (O_CREAT | O_EXCL);
1666 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1668 if (flags & GNUNET_DISK_OPEN_APPEND)
1670 if (flags & GNUNET_DISK_OPEN_CREATE)
1672 (void) GNUNET_DISK_directory_create_for_file (expfn);
1674 mode = translate_unix_perms (perm);
1677 fd = open (expfn, oflags
1681 | O_LARGEFILE, mode);
1684 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1685 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1687 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1688 GNUNET_free (expfn);
1695 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1696 access = FILE_READ_DATA | FILE_WRITE_DATA;
1697 else if (flags & GNUNET_DISK_OPEN_READ)
1698 access = FILE_READ_DATA;
1699 else if (flags & GNUNET_DISK_OPEN_WRITE)
1700 access = FILE_WRITE_DATA;
1702 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1706 else if (flags & GNUNET_DISK_OPEN_CREATE)
1708 (void) GNUNET_DISK_directory_create_for_file (expfn);
1709 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1710 disp = CREATE_ALWAYS;
1714 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1716 disp = TRUNCATE_EXISTING;
1720 disp = OPEN_EXISTING;
1723 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1724 h = CreateFileW (wexpfn, access,
1725 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1726 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1728 h = INVALID_HANDLE_VALUE;
1729 if (h == INVALID_HANDLE_VALUE)
1732 SetErrnoFromWinError (GetLastError ());
1734 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_INFO, "open", expfn);
1735 GNUNET_free (expfn);
1740 if (flags & GNUNET_DISK_OPEN_APPEND)
1741 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1743 SetErrnoFromWinError (GetLastError ());
1744 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1746 GNUNET_free (expfn);
1751 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1754 ret->type = GNUNET_DISK_HANLDE_TYPE_FILE;
1758 GNUNET_free (expfn);
1764 * Close an open file
1765 * @param h file handle
1766 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1769 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1775 return GNUNET_SYSERR;
1781 if (!CloseHandle (h->h))
1783 SetErrnoFromWinError (GetLastError ());
1784 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1785 ret = GNUNET_SYSERR;
1787 if (h->oOverlapRead)
1789 if (!CloseHandle (h->oOverlapRead->hEvent))
1791 SetErrnoFromWinError (GetLastError ());
1792 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1793 ret = GNUNET_SYSERR;
1795 GNUNET_free (h->oOverlapRead);
1797 if (h->oOverlapWrite)
1799 if (!CloseHandle (h->oOverlapWrite->hEvent))
1801 SetErrnoFromWinError (GetLastError ());
1802 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1803 ret = GNUNET_SYSERR;
1805 GNUNET_free (h->oOverlapWrite);
1808 if (close (h->fd) != 0)
1810 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1811 ret = GNUNET_SYSERR;
1820 * Get a GNUnet file handle from a W32 handle.
1822 * @param handle native handle
1823 * @return GNUnet file handle corresponding to the W32 handle
1825 struct GNUNET_DISK_FileHandle *
1826 GNUNET_DISK_get_handle_from_w32_handle (HANDLE osfh)
1828 struct GNUNET_DISK_FileHandle *fh;
1831 enum GNUNET_FILE_Type ftype;
1833 dwret = GetFileType (osfh);
1836 case FILE_TYPE_DISK:
1837 ftype = GNUNET_DISK_HANLDE_TYPE_FILE;
1839 case FILE_TYPE_PIPE:
1840 ftype = GNUNET_DISK_HANLDE_TYPE_PIPE;
1846 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1850 if (ftype == GNUNET_DISK_HANLDE_TYPE_PIPE)
1853 * Note that we can't make it overlapped if it isn't already.
1854 * (ReOpenFile() is only available in 2003/Vista).
1855 * The process that opened this file in the first place (usually a parent
1856 * process, if this is stdin/stdout/stderr) must make it overlapped,
1857 * otherwise we're screwed, as selecting on non-overlapped handle
1860 fh->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
1861 fh->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
1862 fh->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1863 fh->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
1871 * Get a handle from a native integer FD.
1873 * @param fno native integer file descriptor
1874 * @return file handle corresponding to the descriptor, NULL on error
1876 struct GNUNET_DISK_FileHandle *
1877 GNUNET_DISK_get_handle_from_int_fd (int fno)
1879 struct GNUNET_DISK_FileHandle *fh;
1881 if ( (((off_t) -1) == lseek (fno, 0, SEEK_CUR)) &&
1883 return NULL; /* invalid FD */
1886 fh = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1892 osfh = _get_osfhandle (fno);
1893 if (INVALID_HANDLE_VALUE == (HANDLE) osfh)
1896 fh = GNUNET_DISK_get_handle_from_w32_handle ((HANDLE) osfh);
1904 * Get a handle from a native streaming FD.
1906 * @param fd native streaming file descriptor
1907 * @return file handle corresponding to the descriptor
1909 struct GNUNET_DISK_FileHandle *
1910 GNUNET_DISK_get_handle_from_native (FILE *fd)
1918 return GNUNET_DISK_get_handle_from_int_fd (fno);
1923 * Construct full path to a file inside of the private
1924 * directory used by GNUnet. Also creates the corresponding
1925 * directory. If the resulting name is supposed to be
1926 * a directory, end the last argument in '/' (or pass
1927 * DIR_SEPARATOR_STR as the last argument before NULL).
1929 * @param cfg configuration to use (determines HOME)
1930 * @param serviceName name of the service
1931 * @param ... is NULL-terminated list of
1932 * path components to append to the
1933 * private directory name.
1934 * @return the constructed filename
1937 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1938 const char *serviceName, ...)
1944 unsigned int needed;
1947 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1951 LOG (GNUNET_ERROR_TYPE_WARNING,
1952 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1956 needed = strlen (pfx) + 2;
1957 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1959 va_start (ap, serviceName);
1962 c = va_arg (ap, const char *);
1966 needed += strlen (c);
1967 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1971 ret = GNUNET_malloc (needed);
1974 va_start (ap, serviceName);
1977 c = va_arg (ap, const char *);
1981 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1982 strcat (ret, DIR_SEPARATOR_STR);
1986 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1987 (void) GNUNET_DISK_directory_create_for_file (ret);
1989 (void) GNUNET_DISK_directory_create (ret);
1995 * Handle for a memory-mapping operation.
1997 struct GNUNET_DISK_MapHandle
2000 * Address where the map is in memory.
2006 * Underlying OS handle.
2011 * Number of bytes mapped.
2019 #define MAP_FAILED ((void *) -1)
2023 * Map a file into memory
2025 * @param h open file handle
2026 * @param m handle to the new mapping
2027 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
2028 * @param len size of the mapping
2029 * @return pointer to the mapped memory region, NULL on failure
2032 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
2033 struct GNUNET_DISK_MapHandle **m,
2034 enum GNUNET_DISK_MapType access, size_t len)
2043 DWORD mapAccess, protect;
2045 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
2046 (access & GNUNET_DISK_MAP_TYPE_WRITE))
2048 protect = PAGE_READWRITE;
2049 mapAccess = FILE_MAP_ALL_ACCESS;
2051 else if (access & GNUNET_DISK_MAP_TYPE_READ)
2053 protect = PAGE_READONLY;
2054 mapAccess = FILE_MAP_READ;
2056 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2058 protect = PAGE_READWRITE;
2059 mapAccess = FILE_MAP_WRITE;
2067 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2068 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
2069 if ((*m)->h == INVALID_HANDLE_VALUE)
2071 SetErrnoFromWinError (GetLastError ());
2076 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
2079 SetErrnoFromWinError (GetLastError ());
2080 CloseHandle ((*m)->h);
2089 if (access & GNUNET_DISK_MAP_TYPE_READ)
2091 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
2093 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
2094 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
2095 GNUNET_assert (NULL != (*m)->addr);
2096 if (MAP_FAILED == (*m)->addr)
2108 * @param h mapping handle
2109 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2112 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
2119 return GNUNET_SYSERR;
2123 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
2124 if (ret != GNUNET_OK)
2125 SetErrnoFromWinError (GetLastError ());
2126 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
2128 ret = GNUNET_SYSERR;
2129 SetErrnoFromWinError (GetLastError ());
2132 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
2140 * Write file changes to disk
2141 * @param h handle to an open file
2142 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2145 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
2150 return GNUNET_SYSERR;
2156 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
2157 if (ret != GNUNET_OK)
2158 SetErrnoFromWinError (GetLastError ());
2160 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
2161 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2163 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
2169 /* Copyright Bob Byrnes <byrnes <at> curl.com>
2170 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
2172 /* Create a pipe, and return handles to the read and write ends,
2173 just like CreatePipe, but ensure that the write end permits
2174 FILE_READ_ATTRIBUTES access, on later versions of win32 where
2175 this is supported. This access is needed by NtQueryInformationFile,
2176 which is used to implement select and nonblocking writes.
2177 Note that the return value is either NO_ERROR or GetLastError,
2178 unlike CreatePipe, which returns a bool for success or failure. */
2180 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
2181 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
2182 DWORD dwReadMode, DWORD dwWriteMode)
2184 /* Default to error. */
2185 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
2190 /* Ensure that there is enough pipe buffer space for atomic writes. */
2191 if (psize < PIPE_BUF)
2194 char pipename[MAX_PATH];
2196 /* Retry CreateNamedPipe as long as the pipe name is in use.
2197 * Retrying will probably never be necessary, but we want
2198 * to be as robust as possible. */
2201 static volatile LONG pipe_unique_id;
2203 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2204 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2205 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2207 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2208 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2209 * access, on versions of win32 earlier than WinXP SP2.
2210 * CreatePipe also stupidly creates a full duplex pipe, which is
2211 * a waste, since only a single direction is actually used.
2212 * It's important to only allow a single instance, to ensure that
2213 * the pipe was not created earlier by some other process, even if
2214 * the pid has been reused. */
2215 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2216 psize, /* output buffer size */
2217 psize, /* input buffer size */
2218 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2220 if (read_pipe != INVALID_HANDLE_VALUE)
2222 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2226 DWORD err = GetLastError ();
2230 case ERROR_PIPE_BUSY:
2231 /* The pipe is already open with compatible parameters.
2232 * Pick a new name and retry. */
2233 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2235 case ERROR_ACCESS_DENIED:
2236 /* The pipe is already open with incompatible parameters.
2237 * Pick a new name and retry. */
2238 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2240 case ERROR_CALL_NOT_IMPLEMENTED:
2241 /* We are on an older Win9x platform without named pipes.
2242 * Return an anonymous pipe as the best approximation. */
2243 LOG (GNUNET_ERROR_TYPE_DEBUG,
2244 "CreateNamedPipe not implemented, resorting to "
2245 "CreatePipe: size = %lu\n", psize);
2246 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2248 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p, write handle = %p\n",
2253 err = GetLastError ();
2254 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2257 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2262 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2264 /* Open the named pipe for writing.
2265 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2266 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2267 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2268 0); /* handle to template file */
2270 if (write_pipe == INVALID_HANDLE_VALUE)
2273 DWORD err = GetLastError ();
2275 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2276 CloseHandle (read_pipe);
2279 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2281 *read_pipe_ptr = read_pipe;
2282 *write_pipe_ptr = write_pipe;
2289 * Creates an interprocess channel
2291 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2292 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2293 * @param inherit_read inherit the parent processes stdin (only for windows)
2294 * @param inherit_write inherit the parent processes stdout (only for windows)
2295 * @return handle to the new pipe, NULL on error
2297 struct GNUNET_DISK_PipeHandle *
2298 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2309 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2313 return GNUNET_DISK_pipe_from_fd (blocking_read,
2317 struct GNUNET_DISK_PipeHandle *p;
2322 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
2323 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2324 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2326 /* All pipes are overlapped. If you want them to block - just
2327 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2328 * NOTE: calling with NULL overlapped pointer works only
2329 * for pipes, and doesn't seem to be a documented feature.
2330 * It will NOT work for files, because overlapped files need
2331 * to read offsets from the overlapped structure, regardless.
2332 * Pipes are not seekable, and need no offsets, which is
2333 * probably why it works for them.
2336 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2337 FILE_FLAG_OVERLAPPED,
2338 FILE_FLAG_OVERLAPPED);
2341 SetErrnoFromWinError (GetLastError ());
2343 GNUNET_free (p->fd[0]);
2344 GNUNET_free (p->fd[1]);
2349 if (!DuplicateHandle
2350 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2351 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2353 SetErrnoFromWinError (GetLastError ());
2355 CloseHandle (p->fd[0]->h);
2356 CloseHandle (p->fd[1]->h);
2357 GNUNET_free (p->fd[0]);
2358 GNUNET_free (p->fd[1]);
2363 CloseHandle (p->fd[0]->h);
2364 p->fd[0]->h = tmp_handle;
2366 if (!DuplicateHandle
2367 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2368 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2370 SetErrnoFromWinError (GetLastError ());
2372 CloseHandle (p->fd[0]->h);
2373 CloseHandle (p->fd[1]->h);
2374 GNUNET_free (p->fd[0]);
2375 GNUNET_free (p->fd[1]);
2380 CloseHandle (p->fd[1]->h);
2381 p->fd[1]->h = tmp_handle;
2383 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2384 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2386 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2387 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2388 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2389 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2391 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2392 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2394 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2395 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2403 * Creates a pipe object from a couple of file descriptors.
2404 * Useful for wrapping existing pipe FDs.
2406 * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
2407 * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
2408 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2410 * @return handle to the new pipe, NULL on error
2412 struct GNUNET_DISK_PipeHandle *
2413 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2415 struct GNUNET_DISK_PipeHandle *p;
2417 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle));
2422 int eno = 0; /* make gcc happy */
2427 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2428 p->fd[0]->fd = fd[0];
2431 flags = fcntl (fd[0], F_GETFL);
2432 flags |= O_NONBLOCK;
2433 if (0 > fcntl (fd[0], F_SETFL, flags))
2439 flags = fcntl (fd[0], F_GETFD);
2440 flags |= FD_CLOEXEC;
2441 if (0 > fcntl (fd[0], F_SETFD, flags))
2450 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2451 p->fd[1]->fd = fd[1];
2452 if (!blocking_write)
2454 flags = fcntl (fd[1], F_GETFL);
2455 flags |= O_NONBLOCK;
2456 if (0 > fcntl (fd[1], F_SETFL, flags))
2462 flags = fcntl (fd[1], F_GETFD);
2463 flags |= FD_CLOEXEC;
2464 if (0 > fcntl (fd[1], F_SETFD, flags))
2473 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2474 if (p->fd[0]->fd >= 0)
2475 GNUNET_break (0 == close (p->fd[0]->fd));
2476 if (p->fd[1]->fd >= 0)
2477 GNUNET_break (0 == close (p->fd[1]->fd));
2478 GNUNET_free_non_null (p->fd[0]);
2479 GNUNET_free_non_null (p->fd[1]);
2487 p->fd[0] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2488 p->fd[0]->h = (HANDLE) _get_osfhandle (fd[0]);
2489 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2491 p->fd[0]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2492 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2493 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2494 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2495 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2499 GNUNET_free (p->fd[0]);
2505 p->fd[1] = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
2506 p->fd[1]->h = (HANDLE) _get_osfhandle (fd[1]);
2507 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2509 p->fd[1]->type = GNUNET_DISK_HANLDE_TYPE_PIPE;
2510 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2511 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2512 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2513 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2517 GNUNET_free (p->fd[1]);
2528 * Closes an interprocess channel
2530 * @param p pipe to close
2531 * @param end which end of the pipe to close
2532 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2535 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2536 enum GNUNET_DISK_PipeEnd end)
2538 int ret = GNUNET_OK;
2540 if (end == GNUNET_DISK_PIPE_END_READ)
2544 ret = GNUNET_DISK_file_close (p->fd[0]);
2548 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2552 ret = GNUNET_DISK_file_close (p->fd[1]);
2561 * Detaches one of the ends from the pipe.
2562 * Detached end is a fully-functional FileHandle, it will
2563 * not be affected by anything you do with the pipe afterwards.
2564 * Each end of a pipe can only be detched from it once (i.e.
2565 * it is not duplicated).
2567 * @param p pipe to detach an end from
2568 * @param end which end of the pipe to detach
2569 * @return Detached end on success, NULL on failure
2570 * (or if that end is not present or is closed).
2572 struct GNUNET_DISK_FileHandle *
2573 GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
2574 enum GNUNET_DISK_PipeEnd end)
2576 struct GNUNET_DISK_FileHandle *ret = NULL;
2578 if (end == GNUNET_DISK_PIPE_END_READ)
2586 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2600 * Closes an interprocess channel
2602 * @param p pipe to close
2603 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2606 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2608 int ret = GNUNET_OK;
2611 int write_end_close;
2612 int read_end_close_errno;
2613 int write_end_close_errno;
2615 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
2616 read_end_close_errno = errno;
2617 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
2618 write_end_close_errno = errno;
2621 if (GNUNET_OK != read_end_close)
2623 errno = read_end_close_errno;
2624 ret = read_end_close;
2626 else if (GNUNET_OK != write_end_close)
2628 errno = write_end_close_errno;
2629 ret = write_end_close;
2637 * Get the handle to a particular pipe end
2640 * @param n end to access
2641 * @return handle for the respective end
2643 const struct GNUNET_DISK_FileHandle *
2644 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2645 enum GNUNET_DISK_PipeEnd n)
2649 case GNUNET_DISK_PIPE_END_READ:
2650 case GNUNET_DISK_PIPE_END_WRITE:
2660 * Retrieve OS file handle
2662 * @param fh GNUnet file descriptor
2663 * @param dst destination buffer
2664 * @param dst_len length of dst
2665 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2668 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2669 void *dst, size_t dst_len)
2672 if (dst_len < sizeof (HANDLE))
2673 return GNUNET_SYSERR;
2674 *((HANDLE *) dst) = fh->h;
2676 if (dst_len < sizeof (int))
2677 return GNUNET_SYSERR;
2678 *((int *) dst) = fh->fd;