disk.c 41 KB

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