2 This file is part of GNUnet.
3 (C) 2001, 2002, 2005, 2006, 2009 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)
43 #define DEBUG_NPIPE GNUNET_EXTRA_LOGGING
45 #define DEBUG_PIPE GNUNET_EXTRA_LOGGING
48 * Block size for IO for copying files.
50 #define COPY_BLK_SIZE 65536
54 #if defined(LINUX) || defined(CYGWIN)
57 #if defined(SOMEBSD) || defined(DARWIN)
58 #include <sys/param.h>
59 #include <sys/mount.h>
62 #include <sys/types.h>
63 #include <sys/statvfs.h>
68 ULONG PipeSerialNumber;
70 #define _IFMT 0170000 /* type of file */
71 #define _IFLNK 0120000 /* symbolic link */
72 #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
74 #error PORT-ME: need to port statfs (how much space is left on the drive?)
80 #if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
84 #include <sys/statvfs.h>
89 * Handle used to manage a pipe.
91 struct GNUNET_DISK_PipeHandle
94 * File descriptors for the pipe.
96 struct GNUNET_DISK_FileHandle *fd[2];
101 * Closure for the recursion to determine the file size
104 struct GetFileSizeData
107 * Set to the total file size.
112 * GNUNET_YES if symbolic links should be included.
114 int include_sym_links;
119 translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
124 if (perm & GNUNET_DISK_PERM_USER_READ)
126 if (perm & GNUNET_DISK_PERM_USER_WRITE)
128 if (perm & GNUNET_DISK_PERM_USER_EXEC)
130 if (perm & GNUNET_DISK_PERM_GROUP_READ)
132 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
134 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
136 if (perm & GNUNET_DISK_PERM_OTHER_READ)
138 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
140 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
148 * Iterate over all files in the given directory and
149 * accumulate their size.
151 * @param cls closure of type "struct GetFileSizeData"
152 * @param fn current filename we are looking at
153 * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
156 getSizeRec (void *cls, const char *fn)
158 struct GetFileSizeData *gfsd = cls;
167 if (0 != STAT64 (fn, &buf))
169 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
170 return GNUNET_SYSERR;
173 if (0 != STAT (fn, &buf))
175 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
176 return GNUNET_SYSERR;
179 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
180 gfsd->total += buf.st_size;
181 if ((S_ISDIR (buf.st_mode)) && (0 == ACCESS (fn, X_OK)) &&
182 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
184 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
185 return GNUNET_SYSERR;
192 * Checks whether a handle is invalid
194 * @param h handle to check
195 * @return GNUNET_YES if invalid, GNUNET_NO if valid
198 GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
201 return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
203 return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
208 * Get the size of an open file.
210 * @param fh open file handle
211 * @param size where to write size of the file
212 * @return GNUNET_OK on success, GNUNET_SYSERR on error
215 GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
221 b = GetFileSizeEx (fh->h, &li);
224 SetErrnoFromWinError (GetLastError ());
225 return GNUNET_SYSERR;
227 *size = (OFF_T) li.QuadPart;
231 if (0 != FSTAT (fh->fd, &sbuf))
232 return GNUNET_SYSERR;
233 *size = sbuf.st_size;
240 * Move the read/write pointer in a file
242 * @param h handle of an open file
243 * @param offset position to move to
244 * @param whence specification to which position the offset parameter relates to
245 * @return the new position on success, GNUNET_SYSERR otherwise
248 GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
249 enum GNUNET_DISK_Seek whence)
254 return GNUNET_SYSERR;
258 LARGE_INTEGER li, new_pos;
261 static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
262 [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
264 li.QuadPart = offset;
266 b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
269 SetErrnoFromWinError (GetLastError ());
270 return GNUNET_SYSERR;
272 return (OFF_T) new_pos.QuadPart;
274 static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
275 [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
278 return lseek (h->fd, offset, t[whence]);
284 * Get the size of the file (or directory) of the given file (in
287 * @param filename name of the file or directory
288 * @param size set to the size of the file (or,
289 * in the case of directories, the sum
290 * of all sizes of files in the directory)
291 * @param includeSymLinks should symbolic links be
293 * @return GNUNET_SYSERR on error, GNUNET_OK on success
296 GNUNET_DISK_file_size (const char *filename, uint64_t * size,
299 struct GetFileSizeData gfsd;
302 GNUNET_assert (size != NULL);
304 gfsd.include_sym_links = includeSymLinks;
305 ret = getSizeRec (&gfsd, filename);
312 * Obtain some unique identifiers for the given file
313 * that can be used to identify it in the local system.
314 * This function is used between GNUnet processes to
315 * quickly check if two files with the same absolute path
316 * are actually identical. The two processes represent
317 * the same peer but may communicate over the network
318 * (and the file may be on an NFS volume). This function
319 * may not be supported on all operating systems.
321 * @param filename name of the file
322 * @param dev set to the device ID
323 * @param ino set to the inode ID
324 * @return GNUNET_OK on success
327 GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
334 if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
336 *dev = (uint64_t) fbuf.f_fsid;
337 *ino = (uint64_t) sbuf.st_ino;
344 if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
346 *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
347 ((uint64_t) fbuf.f_fsid.val[1]);
348 *ino = (uint64_t) sbuf.st_ino;
352 // FIXME NILS: test this
353 struct GNUNET_DISK_FileHandle *fh;
354 BY_HANDLE_FILE_INFORMATION info;
357 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
359 return GNUNET_SYSERR;
360 succ = GetFileInformationByHandle (fh->h, &info);
361 GNUNET_DISK_file_close (fh);
364 *dev = info.dwVolumeSerialNumber;
365 *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
369 return GNUNET_SYSERR;
372 return GNUNET_SYSERR;
377 * Create an (empty) temporary file on disk. If the given name is not
378 * an absolute path, the current 'TMPDIR' will be prepended. In any case,
379 * 6 random characters will be appended to the name to create a unique
382 * @param t component to use for the name;
383 * does NOT contain "XXXXXX" or "/tmp/".
384 * @return NULL on error, otherwise name of fresh
385 * file on disk in directory for temporary files
388 GNUNET_DISK_mktemp (const char *t)
395 if ((t[0] != '/') && (t[0] != '\\')
397 && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
401 /* FIXME: This uses system codepage on W32, not UTF-8 */
402 tmpdir = getenv ("TMPDIR");
403 tmpdir = tmpdir ? tmpdir : "/tmp";
404 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
408 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
411 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
412 if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
422 /* FIXME: why is this not MKSTEMP()? This function is implemented in plibc.
423 * CG: really? If I put MKSTEMP here, I get a compilation error...
424 * It will assume that fn is UTF-8-encoded, if compiled with UTF-8 support.
429 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
434 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
440 * Get the number of blocks that are left on the partition that
441 * contains the given file (for normal users).
443 * @param part a file on the partition to check
444 * @return -1 on errors, otherwise the number of free blocks
447 GNUNET_DISK_get_blocks_available (const char *part)
452 if (0 != statvfs (part, &buf))
454 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
462 wchar_t wpath[MAX_PATH + 1];
465 path = GNUNET_STRINGS_filename_expand (part);
468 /* "part" was in UTF-8, and so is "path" */
469 if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
475 wcsncpy (szDrive, wpath, 3);
478 if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
480 LOG (GNUNET_ERROR_TYPE_WARNING, _("`%s' failed for drive `%S': %u\n"),
481 "GetDiskFreeSpace", szDrive, GetLastError ());
489 if (0 != statfs (part, &s))
491 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
500 * Test if "fil" is a directory.
501 * Will not print an error message if the directory
502 * does not exist. Will log errors if GNUNET_SYSERR is
503 * returned (i.e., a file exists with the same name).
505 * @param fil filename to test
506 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
510 GNUNET_DISK_directory_test (const char *fil)
512 struct stat filestat;
515 ret = STAT (fil, &filestat);
520 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
521 return GNUNET_SYSERR;
525 if (!S_ISDIR (filestat.st_mode))
527 if (ACCESS (fil, R_OK | X_OK) < 0)
529 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
530 return GNUNET_SYSERR;
537 * Check that fil corresponds to a filename
538 * (of a file that exists and that is not a directory).
540 * @param fil filename to check
541 * @return GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
542 * else (will print an error message in that case, too).
545 GNUNET_DISK_file_test (const char *fil)
547 struct stat filestat;
551 rdir = GNUNET_STRINGS_filename_expand (fil);
553 return GNUNET_SYSERR;
555 ret = STAT (rdir, &filestat);
560 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
562 return GNUNET_SYSERR;
567 if (!S_ISREG (filestat.st_mode))
572 if (ACCESS (rdir, R_OK) < 0)
574 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
576 return GNUNET_SYSERR;
584 * Implementation of "mkdir -p"
585 * @param dir the directory to create
586 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
589 GNUNET_DISK_directory_create (const char *dir)
596 rdir = GNUNET_STRINGS_filename_expand (dir);
598 return GNUNET_SYSERR;
602 pos = 1; /* skip heading '/' */
604 /* Local or Network path? */
605 if (strncmp (rdir, "\\\\", 2) == 0)
610 if (rdir[pos] == '\\')
620 pos = 3; /* strlen("C:\\") */
625 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
628 ret = GNUNET_DISK_directory_test (rdir);
629 if (ret == GNUNET_SYSERR)
632 return GNUNET_SYSERR;
634 if (ret == GNUNET_NO)
637 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
639 wchar_t wrdir[MAX_PATH + 1];
640 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
641 ret = !CreateDirectoryW (wrdir, NULL);
645 if ((ret != 0) && (errno != EEXIST))
647 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
649 return GNUNET_SYSERR;
652 rdir[pos] = DIR_SEPARATOR;
662 * Create the directory structure for storing
665 * @param filename name of a file in the directory
666 * @returns GNUNET_OK on success,
667 * GNUNET_SYSERR on failure,
668 * GNUNET_NO if the directory
669 * exists but is not writeable for us
672 GNUNET_DISK_directory_create_for_file (const char *filename)
678 rdir = GNUNET_STRINGS_filename_expand (filename);
680 return GNUNET_SYSERR;
682 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
685 ret = GNUNET_DISK_directory_create (rdir);
686 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
694 * Read the contents of a binary file into a buffer.
695 * @param h handle to an open file
696 * @param result the buffer to write the result to
697 * @param len the maximum number of bytes to read
698 * @return the number of bytes read on success, GNUNET_SYSERR on failure
701 GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
707 return GNUNET_SYSERR;
713 if (h->type != GNUNET_PIPE)
715 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
717 SetErrnoFromWinError (GetLastError ());
718 return GNUNET_SYSERR;
724 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to read\n");
726 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
728 if (GetLastError () != ERROR_IO_PENDING)
731 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
733 SetErrnoFromWinError (GetLastError ());
734 return GNUNET_SYSERR;
737 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
739 GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
742 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
747 return read (h->fd, result, len);
753 * Read the contents of a binary file into a buffer.
754 * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
755 * when no data can be read).
757 * @param h handle to an open file
758 * @param result the buffer to write the result to
759 * @param len the maximum number of bytes to read
760 * @return the number of bytes read on success, GNUNET_SYSERR on failure
763 GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
764 void *result, size_t len)
769 return GNUNET_SYSERR;
775 if (h->type != GNUNET_PIPE)
777 if (!ReadFile (h->h, result, len, &bytesRead, NULL))
779 SetErrnoFromWinError (GetLastError ());
780 return GNUNET_SYSERR;
786 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe, trying to read\n");
788 if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
790 if (GetLastError () != ERROR_IO_PENDING)
793 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
795 SetErrnoFromWinError (GetLastError ());
796 return GNUNET_SYSERR;
801 LOG (GNUNET_ERROR_TYPE_DEBUG,
802 "ReadFile() queued a read, cancelling\n");
806 return GNUNET_SYSERR;
810 LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
818 /* set to non-blocking, read, then set back */
819 flags = fcntl (h->fd, F_GETFL);
820 if (0 == (flags & O_NONBLOCK))
821 fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
822 ret = read (h->fd, result, len);
823 if (0 == (flags & O_NONBLOCK))
824 fcntl (h->fd, F_SETFL, flags);
831 * Read the contents of a binary file into a buffer.
833 * @param fn file name
834 * @param result the buffer to write the result to
835 * @param len the maximum number of bytes to read
836 * @return number of bytes read, GNUNET_SYSERR on failure
839 GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
841 struct GNUNET_DISK_FileHandle *fh;
844 fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
846 return GNUNET_SYSERR;
847 ret = GNUNET_DISK_file_read (fh, result, len);
848 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
855 * Write a buffer to a file.
856 * @param h handle to open file
857 * @param buffer the data to write
858 * @param n number of bytes to write
859 * @return number of bytes written on success, GNUNET_SYSERR on error
862 GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
863 const void *buffer, size_t n)
868 return GNUNET_SYSERR;
874 if (h->type != GNUNET_PIPE)
876 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
878 SetErrnoFromWinError (GetLastError ());
879 return GNUNET_SYSERR;
885 LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
887 if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
889 if (GetLastError () != ERROR_IO_PENDING)
891 SetErrnoFromWinError (GetLastError ());
893 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
896 return GNUNET_SYSERR;
899 LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
901 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
903 SetErrnoFromWinError (GetLastError ());
905 LOG (GNUNET_ERROR_TYPE_DEBUG,
906 "Error getting overlapped result while writing to pipe: %u\n",
909 return GNUNET_SYSERR;
915 if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
918 LOG (GNUNET_ERROR_TYPE_DEBUG,
919 "Error getting control overlapped result while writing to pipe: %u\n",
926 LOG (GNUNET_ERROR_TYPE_DEBUG,
927 "Wrote %u bytes (ovr says %u), picking the greatest\n",
932 if (bytesWritten == 0)
937 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
940 return GNUNET_SYSERR;
944 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
949 return write (h->fd, buffer, n);
955 * Write a buffer to a file, blocking, if necessary.
956 * @param h handle to open file
957 * @param buffer the data to write
958 * @param n number of bytes to write
959 * @return number of bytes written on success, GNUNET_SYSERR on error
962 GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
963 const void *buffer, size_t n)
968 return GNUNET_SYSERR;
973 /* We do a non-overlapped write, which is as blocking as it gets */
975 LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
977 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
979 SetErrnoFromWinError (GetLastError ());
981 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
984 return GNUNET_SYSERR;
986 if (bytesWritten == 0 && n > 0)
989 LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
991 WaitForSingleObject (h->h, INFINITE);
992 if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
994 SetErrnoFromWinError (GetLastError ());
996 LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
999 return GNUNET_SYSERR;
1003 LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
1005 return bytesWritten;
1010 /* set to blocking, write, then set back */
1011 flags = fcntl (h->fd, F_GETFL);
1012 if (0 != (flags & O_NONBLOCK))
1013 fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
1014 ret = write (h->fd, buffer, n);
1015 if (0 == (flags & O_NONBLOCK))
1016 fcntl (h->fd, F_SETFL, flags);
1023 * Write a buffer to a file. If the file is longer than the
1024 * number of bytes that will be written, it will be truncated.
1026 * @param fn file name
1027 * @param buffer the data to write
1028 * @param n number of bytes to write
1029 * @param mode file permissions
1030 * @return number of bytes written on success, GNUNET_SYSERR on error
1033 GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
1034 enum GNUNET_DISK_AccessPermissions mode)
1036 struct GNUNET_DISK_FileHandle *fh;
1039 fh = GNUNET_DISK_file_open (fn,
1040 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
1041 | GNUNET_DISK_OPEN_CREATE, mode);
1043 return GNUNET_SYSERR;
1044 ret = GNUNET_DISK_file_write (fh, buffer, n);
1045 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1051 * Scan a directory for files.
1053 * @param dirName the name of the directory
1054 * @param callback the method to call for each file,
1055 * can be NULL, in that case, we only count
1056 * @param callback_cls closure for callback
1057 * @return the number of files found, GNUNET_SYSERR on error or
1058 * ieration aborted by callback returning GNUNET_SYSERR
1061 GNUNET_DISK_directory_scan (const char *dirName,
1062 GNUNET_FileNameCallback callback,
1066 struct dirent *finfo;
1071 unsigned int name_len;
1072 unsigned int n_size;
1074 GNUNET_assert (dirName != NULL);
1075 dname = GNUNET_STRINGS_filename_expand (dirName);
1077 return GNUNET_SYSERR;
1078 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
1079 dname[strlen (dname) - 1] = '\0';
1080 if (0 != STAT (dname, &istat))
1082 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
1083 GNUNET_free (dname);
1084 return GNUNET_SYSERR;
1086 if (!S_ISDIR (istat.st_mode))
1088 LOG (GNUNET_ERROR_TYPE_WARNING, _("Expected `%s' to be a directory!\n"),
1090 GNUNET_free (dname);
1091 return GNUNET_SYSERR;
1094 dinfo = OPENDIR (dname);
1095 if ((errno == EACCES) || (dinfo == NULL))
1097 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
1100 GNUNET_free (dname);
1101 return GNUNET_SYSERR;
1104 n_size = strlen (dname) + name_len + 2;
1105 name = GNUNET_malloc (n_size);
1106 while ((finfo = READDIR (dinfo)) != NULL)
1108 if ((0 == strcmp (finfo->d_name, ".")) ||
1109 (0 == strcmp (finfo->d_name, "..")))
1111 if (callback != NULL)
1113 if (name_len < strlen (finfo->d_name))
1116 name_len = strlen (finfo->d_name);
1117 n_size = strlen (dname) + name_len + 2;
1118 name = GNUNET_malloc (n_size);
1120 /* dname can end in "/" only if dname == "/";
1121 * if dname does not end in "/", we need to add
1122 * a "/" (otherwise, we must not!) */
1123 GNUNET_snprintf (name, n_size, "%s%s%s", dname,
1124 (strcmp (dname, DIR_SEPARATOR_STR) ==
1125 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
1126 if (GNUNET_OK != callback (callback_cls, name))
1130 GNUNET_free (dname);
1131 return GNUNET_SYSERR;
1138 GNUNET_free (dname);
1144 * Opaque handle used for iterating over a directory.
1146 struct GNUNET_DISK_DirectoryIterator
1150 * Function to call on directory entries.
1152 GNUNET_DISK_DirectoryIteratorCallback callback;
1155 * Closure for callback.
1160 * Reference to directory.
1170 * Next filename to process.
1177 enum GNUNET_SCHEDULER_Priority priority;
1183 * Task used by the directory iterator.
1186 directory_iterator_task (void *cls,
1187 const struct GNUNET_SCHEDULER_TaskContext *tc)
1189 struct GNUNET_DISK_DirectoryIterator *iter = cls;
1192 name = iter->next_name;
1193 GNUNET_assert (name != NULL);
1194 iter->next_name = NULL;
1195 iter->callback (iter->callback_cls, iter, name, iter->dirname);
1201 * This function must be called during the DiskIteratorCallback
1202 * (exactly once) to schedule the task to process the next
1203 * filename in the directory (if there is one).
1205 * @param iter opaque handle for the iterator
1206 * @param can set to GNUNET_YES to terminate the iteration early
1207 * @return GNUNET_YES if iteration will continue,
1208 * GNUNET_NO if this was the last entry (and iteration is complete),
1209 * GNUNET_SYSERR if abort was YES
1212 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
1215 struct dirent *finfo;
1217 GNUNET_assert (iter->next_name == NULL);
1218 if (can == GNUNET_YES)
1220 CLOSEDIR (iter->directory);
1221 GNUNET_free (iter->dirname);
1223 return GNUNET_SYSERR;
1225 while (NULL != (finfo = READDIR (iter->directory)))
1227 if ((0 == strcmp (finfo->d_name, ".")) ||
1228 (0 == strcmp (finfo->d_name, "..")))
1230 GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
1231 DIR_SEPARATOR_STR, finfo->d_name);
1236 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
1239 GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
1246 * Scan a directory for files using the scheduler to run a task for
1247 * each entry. The name of the directory must be expanded first (!).
1248 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
1249 * may provide a simpler API.
1251 * @param prio priority to use
1252 * @param dirName the name of the directory
1253 * @param callback the method to call for each file
1254 * @param callback_cls closure for callback
1255 * @return GNUNET_YES if directory is not empty and @callback
1256 * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
1259 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
1260 const char *dirName,
1261 GNUNET_DISK_DirectoryIteratorCallback
1262 callback, void *callback_cls)
1264 struct GNUNET_DISK_DirectoryIterator *di;
1266 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
1267 di->callback = callback;
1268 di->callback_cls = callback_cls;
1269 di->directory = OPENDIR (dirName);
1270 if (di->directory == NULL)
1273 callback (callback_cls, NULL, NULL, NULL);
1274 return GNUNET_SYSERR;
1276 di->dirname = GNUNET_strdup (dirName);
1277 di->priority = prio;
1278 return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
1283 * Function that removes the given directory by calling
1284 * "GNUNET_DISK_directory_remove".
1286 * @param unused not used
1287 * @param fn directory to remove
1291 remove_helper (void *unused, const char *fn)
1293 (void) GNUNET_DISK_directory_remove (fn);
1299 * Remove all files in a directory (rm -rf). Call with
1303 * @param fileName the file to remove
1304 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1307 GNUNET_DISK_directory_remove (const char *fileName)
1311 if (0 != LSTAT (fileName, &istat))
1312 return GNUNET_NO; /* file may not exist... */
1313 CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
1314 if (UNLINK (fileName) == 0)
1316 if ((errno != EISDIR) &&
1317 /* EISDIR is not sufficient in all cases, e.g.
1318 * sticky /tmp directory may result in EPERM on BSD.
1319 * So we also explicitly check "isDirectory" */
1320 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
1322 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1323 return GNUNET_SYSERR;
1325 if (GNUNET_SYSERR ==
1326 GNUNET_DISK_directory_scan (fileName, &remove_helper, NULL))
1327 return GNUNET_SYSERR;
1328 if (0 != RMDIR (fileName))
1330 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
1331 return GNUNET_SYSERR;
1340 * @param src file to copy
1341 * @param dst destination file name
1342 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1345 GNUNET_DISK_file_copy (const char *src, const char *dst)
1351 struct GNUNET_DISK_FileHandle *in;
1352 struct GNUNET_DISK_FileHandle *out;
1354 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
1355 return GNUNET_SYSERR;
1357 in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
1358 GNUNET_DISK_PERM_NONE);
1360 return GNUNET_SYSERR;
1362 GNUNET_DISK_file_open (dst,
1363 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
1364 GNUNET_DISK_OPEN_FAILIFEXISTS,
1365 GNUNET_DISK_PERM_USER_READ |
1366 GNUNET_DISK_PERM_USER_WRITE |
1367 GNUNET_DISK_PERM_GROUP_READ |
1368 GNUNET_DISK_PERM_GROUP_WRITE);
1371 GNUNET_DISK_file_close (in);
1372 return GNUNET_SYSERR;
1374 buf = GNUNET_malloc (COPY_BLK_SIZE);
1377 len = COPY_BLK_SIZE;
1378 if (len > size - pos)
1380 if (len != GNUNET_DISK_file_read (in, buf, len))
1382 if (len != GNUNET_DISK_file_write (out, buf, len))
1387 GNUNET_DISK_file_close (in);
1388 GNUNET_DISK_file_close (out);
1392 GNUNET_DISK_file_close (in);
1393 GNUNET_DISK_file_close (out);
1394 return GNUNET_SYSERR;
1399 * @brief Removes special characters as ':' from a filename.
1400 * @param fn the filename to canonicalize
1403 GNUNET_DISK_filename_canonicalize (char *fn)
1413 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' ||
1414 c == '<' || c == '>' || c == '|')
1426 * @brief Change owner of a file
1428 * @param filename name of file to change the owner of
1429 * @param user name of the new owner
1430 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1433 GNUNET_DISK_file_change_owner (const char *filename, const char *user)
1438 pws = getpwnam (user);
1441 LOG (GNUNET_ERROR_TYPE_ERROR,
1442 _("Cannot obtain information about user `%s': %s\n"), user,
1444 return GNUNET_SYSERR;
1446 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1447 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1454 * Lock a part of a file
1455 * @param fh file handle
1456 * @param lockStart absolute position from where to lock
1457 * @param lockEnd absolute position until where to lock
1458 * @param excl GNUNET_YES for an exclusive lock
1459 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1462 GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
1463 OFF_T lockEnd, int excl)
1468 return GNUNET_SYSERR;
1474 memset (&fl, 0, sizeof (struct flock));
1475 fl.l_type = excl ? F_WRLCK : F_RDLCK;
1476 fl.l_whence = SEEK_SET;
1477 fl.l_start = lockStart;
1480 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1483 OFF_T diff = lockEnd - lockStart;
1484 DWORD diff_low, diff_high;
1485 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1486 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1488 memset (&o, 0, sizeof (OVERLAPPED));
1489 o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
1490 o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1493 (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
1494 0, diff_low, diff_high, &o))
1496 SetErrnoFromWinError (GetLastError ());
1497 return GNUNET_SYSERR;
1506 * Unlock a part of a file
1507 * @param fh file handle
1508 * @param unlockStart absolute position from where to unlock
1509 * @param unlockEnd absolute position until where to unlock
1510 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1513 GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
1519 return GNUNET_SYSERR;
1525 memset (&fl, 0, sizeof (struct flock));
1526 fl.l_type = F_UNLCK;
1527 fl.l_whence = SEEK_SET;
1528 fl.l_start = unlockStart;
1529 fl.l_len = unlockEnd;
1531 return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
1534 OFF_T diff = unlockEnd - unlockStart;
1535 DWORD diff_low, diff_high;
1536 diff_low = (DWORD) (diff & 0xFFFFFFFF);
1537 diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1539 memset (&o, 0, sizeof (OVERLAPPED));
1540 o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
1541 o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
1543 if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
1545 SetErrnoFromWinError (GetLastError ());
1546 return GNUNET_SYSERR;
1555 * Open a file. Note that the access permissions will only be
1556 * used if a new file is created and if the underlying operating
1557 * system supports the given permissions.
1559 * @param fn file name to be opened
1560 * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
1561 * @param perm permissions for the newly created file, use
1562 * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
1563 * call (because of flags)
1564 * @return IO handle on success, NULL on error
1566 struct GNUNET_DISK_FileHandle *
1567 GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
1568 enum GNUNET_DISK_AccessPermissions perm)
1571 struct GNUNET_DISK_FileHandle *ret;
1577 wchar_t wexpfn[MAX_PATH + 1];
1584 expfn = GNUNET_STRINGS_filename_expand (fn);
1589 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1590 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1591 else if (flags & GNUNET_DISK_OPEN_READ)
1593 else if (flags & GNUNET_DISK_OPEN_WRITE)
1598 GNUNET_free (expfn);
1601 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1602 oflags |= (O_CREAT | O_EXCL);
1603 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1605 if (flags & GNUNET_DISK_OPEN_APPEND)
1607 if (flags & GNUNET_DISK_OPEN_CREATE)
1609 (void) GNUNET_DISK_directory_create_for_file (expfn);
1611 mode = translate_unix_perms (perm);
1614 fd = open (expfn, oflags | O_LARGEFILE, mode);
1617 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1618 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1620 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1621 GNUNET_free (expfn);
1628 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1629 access = FILE_READ_DATA | FILE_WRITE_DATA;
1630 else if (flags & GNUNET_DISK_OPEN_READ)
1631 access = FILE_READ_DATA;
1632 else if (flags & GNUNET_DISK_OPEN_WRITE)
1633 access = FILE_WRITE_DATA;
1635 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1639 else if (flags & GNUNET_DISK_OPEN_CREATE)
1641 (void) GNUNET_DISK_directory_create_for_file (expfn);
1642 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1643 disp = CREATE_ALWAYS;
1647 else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1649 disp = TRUNCATE_EXISTING;
1653 disp = OPEN_EXISTING;
1656 if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
1657 h = CreateFileW (wexpfn, access,
1658 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1659 disp, FILE_ATTRIBUTE_NORMAL, NULL);
1661 h = INVALID_HANDLE_VALUE;
1662 if (h == INVALID_HANDLE_VALUE)
1664 SetErrnoFromWinError (GetLastError ());
1665 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1666 GNUNET_free (expfn);
1670 if (flags & GNUNET_DISK_OPEN_APPEND)
1671 if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
1673 SetErrnoFromWinError (GetLastError ());
1674 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
1676 GNUNET_free (expfn);
1681 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
1684 ret->type = GNUNET_DISK_FILE;
1688 GNUNET_free (expfn);
1694 * Close an open file
1695 * @param h file handle
1696 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1699 GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1704 return GNUNET_SYSERR;
1708 if (!CloseHandle (h->h))
1710 SetErrnoFromWinError (GetLastError ());
1711 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1712 GNUNET_free (h->oOverlapRead);
1713 GNUNET_free (h->oOverlapWrite);
1715 return GNUNET_SYSERR;
1718 if (close (h->fd) != 0)
1720 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1722 return GNUNET_SYSERR;
1731 * Construct full path to a file inside of the private
1732 * directory used by GNUnet. Also creates the corresponding
1733 * directory. If the resulting name is supposed to be
1734 * a directory, end the last argument in '/' (or pass
1735 * DIR_SEPARATOR_STR as the last argument before NULL).
1737 * @param cfg configuration to use (determines HOME)
1738 * @param serviceName name of the service
1739 * @param ... is NULL-terminated list of
1740 * path components to append to the
1741 * private directory name.
1742 * @return the constructed filename
1745 GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
1746 const char *serviceName, ...)
1752 unsigned int needed;
1755 GNUNET_CONFIGURATION_get_value_filename (cfg, serviceName, "HOME", &pfx))
1759 LOG (GNUNET_ERROR_TYPE_WARNING,
1760 _("No `%s' specified for service `%s' in configuration.\n"), "HOME",
1764 needed = strlen (pfx) + 2;
1765 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
1767 va_start (ap, serviceName);
1770 c = va_arg (ap, const char *);
1774 needed += strlen (c);
1775 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1779 ret = GNUNET_malloc (needed);
1782 va_start (ap, serviceName);
1785 c = va_arg (ap, const char *);
1789 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
1790 strcat (ret, DIR_SEPARATOR_STR);
1794 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
1795 (void) GNUNET_DISK_directory_create_for_file (ret);
1797 (void) GNUNET_DISK_directory_create (ret);
1803 * Handle for a memory-mapping operation.
1805 struct GNUNET_DISK_MapHandle
1808 * Address where the map is in memory.
1814 * Underlying OS handle.
1819 * Number of bytes mapped.
1827 #define MAP_FAILED ((void *) -1)
1831 * Map a file into memory
1833 * @param h open file handle
1834 * @param m handle to the new mapping
1835 * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
1836 * @param len size of the mapping
1837 * @return pointer to the mapped memory region, NULL on failure
1840 GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1841 struct GNUNET_DISK_MapHandle **m,
1842 enum GNUNET_DISK_MapType access, size_t len)
1851 DWORD mapAccess, protect;
1853 if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
1854 (access & GNUNET_DISK_MAP_TYPE_WRITE))
1856 protect = PAGE_READWRITE;
1857 mapAccess = FILE_MAP_ALL_ACCESS;
1859 else if (access & GNUNET_DISK_MAP_TYPE_READ)
1861 protect = PAGE_READONLY;
1862 mapAccess = FILE_MAP_READ;
1864 else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1866 protect = PAGE_READWRITE;
1867 mapAccess = FILE_MAP_WRITE;
1875 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1876 (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
1877 if ((*m)->h == INVALID_HANDLE_VALUE)
1879 SetErrnoFromWinError (GetLastError ());
1884 (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
1887 SetErrnoFromWinError (GetLastError ());
1888 CloseHandle ((*m)->h);
1897 if (access & GNUNET_DISK_MAP_TYPE_READ)
1899 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1901 *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
1902 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1903 GNUNET_assert (NULL != (*m)->addr);
1904 if (MAP_FAILED == (*m)->addr)
1916 * @param h mapping handle
1917 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1920 GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1927 return GNUNET_SYSERR;
1931 ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
1932 if (ret != GNUNET_OK)
1933 SetErrnoFromWinError (GetLastError ());
1934 if (!CloseHandle (h->h) && (ret == GNUNET_OK))
1936 ret = GNUNET_SYSERR;
1937 SetErrnoFromWinError (GetLastError ());
1940 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1948 * Write file changes to disk
1949 * @param h handle to an open file
1950 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1953 GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1958 return GNUNET_SYSERR;
1964 ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
1965 if (ret != GNUNET_OK)
1966 SetErrnoFromWinError (GetLastError ());
1968 #elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
1969 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1971 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1976 /* Copyright Bob Byrnes <byrnes <at> curl.com>
1977 http://permalink.gmane.org/gmane.os.cygwin.patches/2121
1979 /* Create a pipe, and return handles to the read and write ends,
1980 just like CreatePipe, but ensure that the write end permits
1981 FILE_READ_ATTRIBUTES access, on later versions of win32 where
1982 this is supported. This access is needed by NtQueryInformationFile,
1983 which is used to implement select and nonblocking writes.
1984 Note that the return value is either NO_ERROR or GetLastError,
1985 unlike CreatePipe, which returns a bool for success or failure. */
1987 create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
1988 LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
1989 DWORD dwReadMode, DWORD dwWriteMode)
1991 /* Default to error. */
1992 *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
1994 HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
1996 /* Ensure that there is enough pipe buffer space for atomic writes. */
1997 if (psize < PIPE_BUF)
2000 char pipename[MAX_PATH];
2002 /* Retry CreateNamedPipe as long as the pipe name is in use.
2003 * Retrying will probably never be necessary, but we want
2004 * to be as robust as possible. */
2007 static volatile LONG pipe_unique_id;
2009 snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
2010 getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
2012 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
2015 /* Use CreateNamedPipe instead of CreatePipe, because the latter
2016 * returns a write handle that does not permit FILE_READ_ATTRIBUTES
2017 * access, on versions of win32 earlier than WinXP SP2.
2018 * CreatePipe also stupidly creates a full duplex pipe, which is
2019 * a waste, since only a single direction is actually used.
2020 * It's important to only allow a single instance, to ensure that
2021 * the pipe was not created earlier by some other process, even if
2022 * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
2023 * because that is only available for Win2k SP2 and WinXP. */
2024 read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
2025 psize, /* output buffer size */
2026 psize, /* input buffer size */
2027 NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
2029 if (read_pipe != INVALID_HANDLE_VALUE)
2032 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
2037 DWORD err = GetLastError ();
2041 case ERROR_PIPE_BUSY:
2042 /* The pipe is already open with compatible parameters.
2043 * Pick a new name and retry. */
2045 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
2048 case ERROR_ACCESS_DENIED:
2049 /* The pipe is already open with incompatible parameters.
2050 * Pick a new name and retry. */
2052 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
2055 case ERROR_CALL_NOT_IMPLEMENTED:
2056 /* We are on an older Win9x platform without named pipes.
2057 * Return an anonymous pipe as the best approximation. */
2059 LOG (GNUNET_ERROR_TYPE_DEBUG,
2060 "CreateNamedPipe not implemented, resorting to "
2061 "CreatePipe: size = %lu\n", psize);
2063 if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
2066 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
2068 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
2073 err = GetLastError ();
2074 LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
2077 LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
2083 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
2086 /* Open the named pipe for writing.
2087 * Be sure to permit FILE_READ_ATTRIBUTES access. */
2088 write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
2089 sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
2090 0); /* handle to template file */
2092 if (write_pipe == INVALID_HANDLE_VALUE)
2095 DWORD err = GetLastError ();
2098 LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
2100 CloseHandle (read_pipe);
2104 LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
2107 *read_pipe_ptr = read_pipe;
2108 *write_pipe_ptr = write_pipe;
2114 * Creates an interprocess channel
2116 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
2117 * @param inherit_read inherit the parent processes stdin (only for windows)
2118 * @param inherit_write inherit the parent processes stdout (only for windows)
2120 * @return handle to the new pipe, NULL on error
2122 struct GNUNET_DISK_PipeHandle *
2123 GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
2125 struct GNUNET_DISK_PipeHandle *p;
2126 struct GNUNET_DISK_FileHandle *fds;
2128 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2129 2 * sizeof (struct GNUNET_DISK_FileHandle));
2130 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2143 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
2148 p->fd[0]->fd = fd[0];
2149 p->fd[1]->fd = fd[1];
2151 flags = fcntl (fd[0], F_GETFL);
2153 flags |= O_NONBLOCK;
2154 if (0 > fcntl (fd[0], F_SETFL, flags))
2156 flags = fcntl (fd[0], F_GETFD);
2157 flags |= FD_CLOEXEC;
2158 if (0 > fcntl (fd[0], F_SETFD, flags))
2161 flags = fcntl (fd[1], F_GETFL);
2162 if (!blocking_write)
2163 flags |= O_NONBLOCK;
2164 if (0 > fcntl (fd[1], F_SETFL, flags))
2166 flags = fcntl (fd[1], F_GETFD);
2167 flags |= FD_CLOEXEC;
2168 if (0 > fcntl (fd[1], F_SETFD, flags))
2173 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2174 GNUNET_break (0 == close (p->fd[0]->fd));
2175 GNUNET_break (0 == close (p->fd[1]->fd));
2184 /* All pipes are overlapped. If you want them to block - just
2185 * call WriteFile() and ReadFile() with NULL overlapped pointer.
2188 create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
2189 FILE_FLAG_OVERLAPPED,
2190 FILE_FLAG_OVERLAPPED);
2194 SetErrnoFromWinError (GetLastError ());
2197 if (!DuplicateHandle
2198 (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
2199 inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2201 SetErrnoFromWinError (GetLastError ());
2202 CloseHandle (p->fd[0]->h);
2203 CloseHandle (p->fd[1]->h);
2207 CloseHandle (p->fd[0]->h);
2208 p->fd[0]->h = tmp_handle;
2210 if (!DuplicateHandle
2211 (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
2212 inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
2214 SetErrnoFromWinError (GetLastError ());
2215 CloseHandle (p->fd[0]->h);
2216 CloseHandle (p->fd[1]->h);
2220 CloseHandle (p->fd[1]->h);
2221 p->fd[1]->h = tmp_handle;
2223 p->fd[0]->type = GNUNET_PIPE;
2224 p->fd[1]->type = GNUNET_PIPE;
2226 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2227 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2228 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2229 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2231 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2232 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2234 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2235 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2242 * Creates a pipe object from a couple of file descriptors.
2243 * Useful for wrapping existing pipe FDs.
2245 * @param blocking creates an asynchronous pipe if set to GNUNET_NO
2246 * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
2248 * @return handle to the new pipe, NULL on error
2250 struct GNUNET_DISK_PipeHandle *
2251 GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
2253 struct GNUNET_DISK_PipeHandle *p;
2254 struct GNUNET_DISK_FileHandle *fds;
2256 p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
2257 2 * sizeof (struct GNUNET_DISK_FileHandle));
2258 fds = (struct GNUNET_DISK_FileHandle *) &p[1];
2266 p->fd[0]->fd = fd[0];
2267 p->fd[1]->fd = fd[1];
2271 flags = fcntl (fd[0], F_GETFL);
2273 flags |= O_NONBLOCK;
2274 if (0 > fcntl (fd[0], F_SETFL, flags))
2276 flags = fcntl (fd[0], F_GETFD);
2277 flags |= FD_CLOEXEC;
2278 if (0 > fcntl (fd[0], F_SETFD, flags))
2284 flags = fcntl (fd[1], F_GETFL);
2285 if (!blocking_write)
2286 flags |= O_NONBLOCK;
2287 if (0 > fcntl (fd[1], F_SETFL, flags))
2289 flags = fcntl (fd[1], F_GETFD);
2290 flags |= FD_CLOEXEC;
2291 if (0 > fcntl (fd[1], F_SETFD, flags))
2297 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
2298 if (p->fd[0]->fd >= 0)
2299 GNUNET_break (0 == close (p->fd[0]->fd));
2300 if (p->fd[1]->fd >= 0)
2301 GNUNET_break (0 == close (p->fd[1]->fd));
2310 p->fd[0]->h = _get_osfhandle (fd[0]);
2312 p->fd[0]->h = INVALID_HANDLE_VALUE;
2314 p->fd[1]->h = _get_osfhandle (fd[1]);
2316 p->fd[1]->h = INVALID_HANDLE_VALUE;
2318 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2320 p->fd[0]->type = GNUNET_PIPE;
2321 p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2322 p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2323 p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2324 p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2327 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2329 p->fd[1]->type = GNUNET_PIPE;
2330 p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2331 p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2332 p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2333 p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2341 * Closes an interprocess channel
2343 * @param p pipe to close
2344 * @param end which end of the pipe to close
2345 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2348 GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
2349 enum GNUNET_DISK_PipeEnd end)
2351 int ret = GNUNET_OK;
2355 if (end == GNUNET_DISK_PIPE_END_READ)
2357 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2359 if (!CloseHandle (p->fd[0]->h))
2361 SetErrnoFromWinError (GetLastError ());
2362 ret = GNUNET_SYSERR;
2364 GNUNET_free (p->fd[0]->oOverlapRead);
2365 GNUNET_free (p->fd[0]->oOverlapWrite);
2366 p->fd[0]->h = INVALID_HANDLE_VALUE;
2369 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2371 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2373 if (!CloseHandle (p->fd[1]->h))
2375 SetErrnoFromWinError (GetLastError ());
2376 ret = GNUNET_SYSERR;
2378 GNUNET_free (p->fd[1]->oOverlapRead);
2379 GNUNET_free (p->fd[1]->oOverlapWrite);
2380 p->fd[1]->h = INVALID_HANDLE_VALUE;
2386 if (end == GNUNET_DISK_PIPE_END_READ)
2388 if (0 != close (p->fd[0]->fd))
2390 ret = GNUNET_SYSERR;
2395 else if (end == GNUNET_DISK_PIPE_END_WRITE)
2397 if (0 != close (p->fd[1]->fd))
2399 ret = GNUNET_SYSERR;
2410 * Closes an interprocess channel
2412 * @param p pipe to close
2413 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2416 GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
2418 int ret = GNUNET_OK;
2422 if (p->fd[0]->h != INVALID_HANDLE_VALUE)
2424 if (!CloseHandle (p->fd[0]->h))
2426 SetErrnoFromWinError (GetLastError ());
2427 ret = GNUNET_SYSERR;
2429 GNUNET_free (p->fd[0]->oOverlapRead);
2430 GNUNET_free (p->fd[0]->oOverlapWrite);
2432 if (p->fd[1]->h != INVALID_HANDLE_VALUE)
2434 if (!CloseHandle (p->fd[1]->h))
2436 SetErrnoFromWinError (GetLastError ());
2437 ret = GNUNET_SYSERR;
2439 GNUNET_free (p->fd[1]->oOverlapRead);
2440 GNUNET_free (p->fd[1]->oOverlapWrite);
2445 if (p->fd[0]->fd != -1)
2447 if (0 != close (p->fd[0]->fd))
2449 ret = GNUNET_SYSERR;
2454 if (p->fd[1]->fd != -1)
2456 if (0 != close (p->fd[1]->fd))
2458 ret = GNUNET_SYSERR;
2470 * Creates a named pipe/FIFO and opens it
2471 * @param fn pointer to the name of the named pipe or to NULL
2472 * @param flags open flags
2473 * @param perm access permissions
2474 * @return pipe handle on success, NULL on error
2476 struct GNUNET_DISK_FileHandle *
2477 GNUNET_DISK_npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
2478 enum GNUNET_DISK_AccessPermissions perm)
2481 struct GNUNET_DISK_FileHandle *ret;
2487 if (flags & GNUNET_DISK_OPEN_READWRITE)
2488 openMode = PIPE_ACCESS_DUPLEX;
2489 else if (flags & GNUNET_DISK_OPEN_READ)
2490 openMode = PIPE_ACCESS_INBOUND;
2491 else if (flags & GNUNET_DISK_OPEN_WRITE)
2492 openMode = PIPE_ACCESS_OUTBOUND;
2494 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
2495 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
2504 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
2506 LOG (GNUNET_ERROR_TYPE_DEBUG,
2507 "Trying to create an instance of named pipe `%s'\n", name);
2509 /* 1) This might work just fine with UTF-8 strings as it is.
2510 * 2) This is only used by GNUnet itself, and only with latin names.
2512 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
2513 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2518 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
2519 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2522 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
2525 h = CreateNamedPipe (*fn,
2526 openMode | FILE_FLAG_OVERLAPPED |
2527 FILE_FLAG_FIRST_PIPE_INSTANCE,
2528 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
2531 error_code = GetLastError ();
2534 /* don't re-set name to NULL yet */
2535 if (h == INVALID_HANDLE_VALUE)
2537 SetErrnoFromWinError (error_code);
2539 LOG (GNUNET_ERROR_TYPE_DEBUG,
2540 "Pipe creation have failed because of %d, errno is %d\n", error_code,
2546 LOG (GNUNET_ERROR_TYPE_DEBUG,
2547 "Pipe was to be unique, considering re-creation\n");
2551 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
2556 LOG (GNUNET_ERROR_TYPE_DEBUG,
2557 "Pipe name was not unique, trying again\n");
2567 ret = GNUNET_malloc (sizeof (*ret));
2569 ret->type = GNUNET_PIPE;
2571 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2572 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2574 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2575 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2581 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
2583 if (mkdtemp (dir) == NULL)
2585 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
2588 GNUNET_asprintf (fn, "%s/child-control", dir);
2591 if (mkfifo (*fn, translate_unix_perms (perm)) == -1)
2593 if ((errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)))
2597 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2598 return GNUNET_DISK_file_open (*fn, flags, perm);
2604 * Opens already existing named pipe/FIFO
2606 * @param fn name of an existing named pipe
2607 * @param flags open flags
2608 * @param perm access permissions
2609 * @return pipe handle on success, NULL on error
2611 struct GNUNET_DISK_FileHandle *
2612 GNUNET_DISK_npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
2613 enum GNUNET_DISK_AccessPermissions perm)
2616 struct GNUNET_DISK_FileHandle *ret;
2621 if (flags & GNUNET_DISK_OPEN_READWRITE)
2622 openMode = GENERIC_WRITE | GENERIC_READ;
2623 else if (flags & GNUNET_DISK_OPEN_READ)
2624 openMode = GENERIC_READ;
2625 else if (flags & GNUNET_DISK_OPEN_WRITE)
2626 openMode = GENERIC_WRITE;
2628 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
2629 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
2630 if (h == INVALID_HANDLE_VALUE)
2632 SetErrnoFromWinError (GetLastError ());
2636 ret = GNUNET_malloc (sizeof (*ret));
2638 ret->type = GNUNET_PIPE;
2639 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
2640 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
2641 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2642 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
2646 flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
2647 return GNUNET_DISK_file_open (fn, flags, perm);
2652 * Closes a named pipe/FIFO
2653 * @param pipe named pipe
2654 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2657 GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
2660 return close (pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
2664 ret = CloseHandle (pipe->h);
2667 SetErrnoFromWinError (GetLastError ());
2668 return GNUNET_SYSERR;
2677 * Get the handle to a particular pipe end
2680 * @param n end to access
2681 * @return handle for the respective end
2683 const struct GNUNET_DISK_FileHandle *
2684 GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
2685 enum GNUNET_DISK_PipeEnd n)
2689 case GNUNET_DISK_PIPE_END_READ:
2690 case GNUNET_DISK_PIPE_END_WRITE:
2700 * Retrieve OS file handle
2702 * @param fh GNUnet file descriptor
2703 * @param dst destination buffer
2704 * @param dst_len length of dst
2705 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
2708 GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
2709 void *dst, size_t dst_len)
2712 if (dst_len < sizeof (HANDLE))
2713 return GNUNET_SYSERR;
2714 *((HANDLE *) dst) = fh->h;
2716 if (dst_len < sizeof (int))
2717 return GNUNET_SYSERR;
2718 *((int *) dst) = fh->fd;